gnucash master: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Sun Dec 31 11:52:12 EST 2017


Updated	 via  https://github.com/Gnucash/gnucash/commit/310442ff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ac05578b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0c6e2ebf (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5823f1b2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7feb9c65 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d06ed7c1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3d2682ac (commit)
	 via  https://github.com/Gnucash/gnucash/commit/477e71b7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/108f7fbd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7c52f9ec (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7ec281f6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7dc3995a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/01ab8899 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bde39c52 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/19e0f587 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b8a85ab8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/dc1ec686 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9f3a357a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d6bb34ef (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f25c065b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/30f7f2fc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7951d425 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/47a42207 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3c65a300 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fd9474b5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a29c2db4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bab266c3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6774f122 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3f44552e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/965685cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bd9af4ae (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4ccc965e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/34c9ba05 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/18fff963 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/437a3756 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/45a52a5d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9191df2f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2f2d1b68 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f00f7335 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/13d5570b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/660ab62d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2314a322 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/26b82b56 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8ae330c8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/44fc5b05 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bbd2df6a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/91727525 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a6a46d7c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/82f1384c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/40654cf5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/44c51f43 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a17bc85a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/aeb62724 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e6dcc0cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/93b17214 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1ea1bcb3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7a5f2ed4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/408f609a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3de3d3cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/139e2aa7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f2df1bd4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a81c3483 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/39dceb55 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/005fdb5f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7b6ac3a0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/230493f2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1ce2f3f6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/72576752 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e2912d1b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/00b2e76d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0854caba (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c26af85e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e8dc5c54 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6f87138b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2e06c8fc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/aaa23dc5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/43cbe652 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/20feefe6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3b3c0322 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ea416e16 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d9d4ffaf (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8399ee65 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/521c1624 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1be88ad1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fa0bcf10 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/db019ec5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1a886fac (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ef65f544 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b549dd68 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4bfd01e7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c7f9fb1a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/68aa61a3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8044f2b0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b6c6906b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8e4d72b5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/afc6ca07 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c4089ebc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/dd222168 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d88d503b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a5306d04 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e1ba5f32 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/070c99c1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d0c435e7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a2008c49 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b9390cea (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ff0d7cc2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/02905fe3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ee01038e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7127df58 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fe757dbe (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e5a7660a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8990553e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4187cc1c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3f03cce1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/082811b9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/809d2770 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7e8ac532 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ba2e0c5f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d93d4f68 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2f96b19c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ddfd38d8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f11eab36 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/19412de2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6e9025d2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e0300d3a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/70618035 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a80318ec (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a23438d5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/805549ba (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fbf4843f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f782be1a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3312fe2d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/29ad8ff9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4a88f05d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5b031829 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f260a01b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/55154959 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d9eebd33 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5636afc4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2cda65e0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9d7ec35c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/eb6dad86 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b3667c76 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/08aa0104 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/34e0d6cf (commit)
	 via  https://github.com/Gnucash/gnucash/commit/eb6c741b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/318f7ebc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/723b51a0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3d910ad2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a784dd57 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a28bcdf1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a847d441 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9af6f184 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/302d1e73 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/98fcf1b0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3f408a88 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b6ec61fa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/76921b5e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0186bc1c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7c57ad18 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/baad2097 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5af21dfa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5aa84e13 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ba5ca5bd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0534ba4f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8ee6783b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/43f1b2fd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/288563c2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/16714a8c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ee2f3017 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1aa3601e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/71bf7d01 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/644a0aa0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/62bbe4f9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/aff1c0c5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/61f860bc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1bb2d1dc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/caba8c43 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3a3c2cba (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3c18b806 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/16845c3a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1e31db74 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8f3e175f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/32799733 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f2c78102 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3cfa9d05 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/404bc1e3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bec1fbd1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/17d8d424 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/66da4ae3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/db079b55 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5b020215 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b94b2f8a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c3b54ab0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c1a94645 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6c11cfad (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b1becf3d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/339fbaa5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9debe91e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b8bbdb2a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a996c02e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5578da11 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4d75259c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cd4b5a31 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bf0c3853 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1435813f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cf90b8cb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/393b8a12 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4fe77a57 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/eb712dc7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3410a03b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f3bc8eea (commit)
	from  https://github.com/Gnucash/gnucash/commit/0026b108 (commit)



commit 310442ffe68d583b633d380c9b0e5d5524bf1a47
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Dec 31 08:51:21 2017 -0800

    Update ChangeLogs for the new year.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2c16d99..81226a5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -598,6 +598,7 @@ SET(gnucash_DOCS
     ChangeLog.2014
     ChangeLog.2015
     ChangeLog.2016
+    ChangeLog.2017
     DOCUMENTERS
     HACKING
     LICENSE
@@ -884,7 +885,7 @@ ENDIF()
 
 IF (BUILDING_FROM_VCS)
   ADD_CUSTOM_TARGET(ChangeLog ALL
-    COMMAND ${GIT_EXECUTABLE} log --format=\"%ad %aN %n%n%x09* %s%d%n\" --date=short --since=2016-01-01 > ${CMAKE_BINARY_DIR}/ChangeLog
+    COMMAND ${GIT_EXECUTABLE} log --format=\"%ad %aN %n%n%x09* %s%d%n\" --date=short --since=2018-01-01 > ${CMAKE_BINARY_DIR}/ChangeLog
     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
   )
   INSTALL(FILES ${CMAKE_BINARY_DIR}/ChangeLog DESTINATION ${CMAKE_INSTALL_DOCDIR})
diff --git a/ChangeLog b/ChangeLog.2017
similarity index 100%
rename from ChangeLog
rename to ChangeLog.2017
diff --git a/Makefile.am b/Makefile.am
index ce7ffae..d351b8f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,6 +19,7 @@ dist_doc_DATA = \
   AUTHORS \
   COPYING \
   ChangeLog \
+  ChangeLog.2017 \
   ChangeLog.2016 \
   ChangeLog.2015 \
   ChangeLog.2014 \
@@ -222,7 +223,7 @@ ChangeLog: NEWS
 	    --stringparam include-rev "yes" $(abs_srcdir)/macros/svn2cl.xsl - > $(abs_builddir)/$@ ; \
 	else \
 	  if test x$(VCS_TYPE) = xgit ; then \
-	    "$(GIT_CMD)" log --format="%ad %aN %n%n%x09* %s%d%n" --date=short --since=2017-01-01 > $(abs_builddir)/$@ ; \
+	    "$(GIT_CMD)" log --format="%ad %aN %n%n%x09* %s%d%n" --date=short --since=2018-01-01 > $(abs_builddir)/$@ ; \
 	  else \
 	    touch $(abs_builddir)/$@ ; \
 	    echo "Note: not in svn or git. ChangeLog not regenerated." ; \

commit ac05578ba5e7d716e367c6952f1d681f590ea3b7
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 30 15:45:22 2017 -0800

    Release GnuCash 2.7.3

diff --git a/AUTHORS b/AUTHORS
index f1dcd9a..835404e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -13,6 +13,7 @@ Frank Ellenberger: Currencies
 Rob Gowin: CMake build system
 Robert Fewell: GUI Development
 Geert Janssens: General Development and Maintenance
+Christopher Lam: Scheme Development and Maintenance
 Aaron Laws: General Development
 John Ralls: General Development and Maintenance
 Christian Stimming: Administration
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4683798..2c16d99 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,14 +13,14 @@ ENABLE_TESTING()
 # Version number of gnucash
 SET (GNUCASH_MAJOR_VERSION 2)
 SET (GNUCASH_MINOR_VERSION 7)
-SET (GNUCASH_MICRO_VERSION 2)
+SET (GNUCASH_MICRO_VERSION 3)
 SET (GNUCASH_NANO_VERSION 0)
 SET (VERSION "${GNUCASH_MAJOR_VERSION}.${GNUCASH_MINOR_VERSION}.${GNUCASH_MICRO_VERSION}")
 SET (GNUCASH_LATEST_STABLE_SERIES 2.6)
 
 SET (PACKAGE gnucash)
 SET (PACKAGE_NAME GnuCash)
-SET (PACKAGE_VERSION 2.7.2)
+SET (PACKAGE_VERSION 2.7.3)
 SET (PACKAGE_BUGREPORT gnucash-devel at gnucash.org)
 SET (PACKAGE_TARNAME ${PACKAGE})
 SET (PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
diff --git a/ChangeLog b/ChangeLog
index a7da4e7..4e18bbd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,683 @@
+2017-12-30 John Ralls 
+
+	* Merge Bob Fewell's 'gtk3-update12' into unstable. (HEAD -> unstable, origin/unstable)
+
+2017-12-30 John Ralls 
+
+	* Prevent "Save before closing" dialog from appearing at startup.
+
+2017-12-30 John Ralls 
+
+	* Fix distcheck errors.
+
+2017-12-30 John Ralls 
+
+	* Fix -Wsign-compare error.
+
+2017-12-30 John Ralls 
+
+	* Merge branch 'maint' into unstable
+
+2017-12-30 Robert Fewell 
+
+	* Fix register move to another window
+
+2017-12-30 Robert Fewell 
+
+	* Fix some transient parent warnings
+
+2017-12-30 Robert Fewell 
+
+	* Fix There is no budget icon yet so set it to the account one.
+
+2017-12-30 Robert Fewell 
+
+	* Fix error when sheet is read only.
+
+2017-12-30 Robert Fewell 
+
+	* Fix transient parent warnings for budgets
+
+2017-12-30 Robert Fewell 
+
+	* Fix Invalid cast from GncWebkitHtml to GtkWindow
+
+2017-12-30 Robert Fewell 
+
+	* Fix Test for filepath is NULL when cancel pressed for export
+
+2017-12-30 Robert Fewell 
+
+	* Fix Critical errors when SX editor loaded
+
+2017-12-30 Robert Fewell 
+
+	* Fix Transient parent warnings for SX editor
+
+2017-12-30 Robert Fewell 
+
+	* Add a left margin to report zoom to align with other options
+
+2017-12-30 Robert Fewell 
+
+	* Fix register cell height by adding 1px for cell border
+
+2017-12-30 Geert Janssens 
+
+	* A few translatable string changes in the warnings for editing reconciled splits
+
+2017-11-29 Robert Fewell 
+
+	* Bug 771667 - Change reconciled splits warning
+
+2017-12-30 Christopher Lam 
+
+	* ENH: display infobox when no accounts matched
+
+2017-12-29 John Ralls 
+
+	* Bug 616709 - Pressing delete key while editing account name offers...
+
+2017-12-29 John Ralls 
+
+	* Fix crash in customer/employee/vendor reports.
+
+2017-12-29 John Ralls 
+
+	* Merge Bob Fewell's 'gtk3-update11' into unstable.
+
+2017-12-29 Geert Janssens 
+
+	* Merge branch 'unstable-TR-plus' of https://github.com/christopherlam/gnucash into unstable
+
+2017-12-30 Christopher Lam 
+
+	* COSMETIC: amend strings in options
+
+2017-12-29 Geert Janssens 
+
+	* Update POTEFILES.in
+
+2017-12-29 Geert Janssens 
+
+	* Fix a few compile warnings-turned-errors
+
+2017-12-29 Geert Janssens 
+
+	* Merge branch 'prices-in' of https://github.com/Bob-IT/gnucash into unstable
+
+2017-12-29 Robert Fewell 
+
+	* Bug 616709 - Stop the delete button on the Account page
+
+2017-12-29 Robert Fewell 
+
+	* Set focus for invoice pages
+
+2017-12-29 Robert Fewell 
+
+	* Add function to get whether sheet is read only
+
+2017-12-29 Robert Fewell 
+
+	* Make sure the tree view is the focus on an owner page
+
+2017-12-29 Robert Fewell 
+
+	* Replace tabs with spaces for budget files
+
+2017-12-29 Robert Fewell 
+
+	* ake sure the Account tree view has focus when budgets open
+
+2017-12-29 Robert Fewell 
+
+	* Change tabs to spaces in gnc-html-webkit2.c
+
+2017-12-29 Robert Fewell 
+
+	*  Make sure the webkit widget has focus on report load
+
+2017-12-29 Robert Fewell 
+
+	* Replace tabs with spaces in a couple of source files
+
+2017-12-29 Robert Fewell 
+
+	* Make sure the sx tree view has focus on page load
+
+2017-12-29 Robert Fewell 
+
+	* Make sure the sheet has focus in a register when opened
+
+2017-12-29 Robert Fewell 
+
+	* Prevent the tab being the focus
+
+2017-12-29 Robert Fewell 
+
+	* Make sure the tree view has focus on Account tree page load
+
+2017-12-27 John Ralls 
+
+	* Fix Travis 'dangling else' warning.
+
+2017-12-26 John Ralls 
+
+	* Another attempt to make Travis's g++ happy about compiler warnings.
+
+2017-12-26 John Ralls 
+
+	* Fix CXX Flags for g++.
+
+2017-12-23 John Ralls 
+
+	* Bug 791848 - GC 2.6.x does not handle ISO dates introduced with GC 2.7.
+
+2017-12-26 John Ralls 
+
+	* Enforce -Werror on C++ files and fix resulting errors.
+
+2017-12-23 John Ralls 
+
+	* Test struct tm* returns from gnc_gmtime and gnc_localtime
+
+2017-12-23 John Ralls 
+
+	* Fix autotools test setup for test-date-utilities. (origin/maint, maint)
+
+2017-12-23 John Ralls 
+
+	* Fix picky gcc-7.2 complaint about a %d conversion not fitting in 3 bytes.
+
+2017-12-23 John Ralls 
+
+	* Bug 791848 - GC 2.6.x does not handle ISO dates introduced with GC 2.7
+
+2017-12-23 John Ralls 
+
+	* Enable reading undelimited YYYYMMDDHHMMSS time strings.
+
+2017-12-24 Christopher Lam 
+
+	* REFACTOR: gnc-numeric not available in scheme anymore
+
+2017-12-05 Christopher Lam 
+
+	* ENH: Optionally hide transactions
+
+2017-12-13 Christopher Lam 
+
+	* ENH: Add 'daily subtotal strategy
+
+2017-12-02 Christopher Lam 
+
+	* ENH: Formalise Reconciliation Report as a new menu item.
+
+2017-11-30 Christopher Lam 
+
+	* ENH: Add indenting for main data and subheadings/subtotals
+
+2017-11-30 Christopher Lam 
+
+	* ENH: Add sortkey Reconciled Status
+
+2017-11-30 Christopher Lam 
+
+	* ENH: Add debit/credit friendly names in subheading rendering
+
+2017-11-27 Christopher Lam 
+
+	* ENH: Add option to choose infobox display summary
+
+2017-12-11 Christopher Lam 
+
+	* COSMETIC: if grand-totals=#f then omit <hr>
+
+2017-11-29 Christopher Lam 
+
+	* REFACTOR: remove 'renderer-key lookup symbol, simplify custom sorter
+
+2017-11-29 Christopher Lam 
+
+	* REFACTOR: simplify num/t-num display code
+
+2017-12-11 Christopher Lam 
+
+	* REFACTOR: centralize left-cols to a vector-list
+
+2017-11-28 Christopher Lam 
+
+	* ENH: if no Display/* selected, insert empty left-column
+
+2017-11-28 Christopher Lam 
+
+	* REFACTOR: simplify render-summary
+
+2017-11-28 Christopher Lam 
+
+	* REFACTOR: simplify do-rows-with-subtotals
+
+2017-11-27 Christopher Lam 
+
+	* REFACTOR:bisect subtotal-get-info into primary/secondary
+
+2017-12-11 Christopher Lam 
+
+	* REFACTOR: start refactor subtotal
+
+2017-12-11 Christopher Lam 
+
+	* BUGFIX: Reverse sign on display only
+
+2017-12-11 Christopher Lam 
+
+	* BUGFIX: Fix incorrect N_ and _ handling
+
+2017-12-11 Christopher Lam 
+
+	* ENH: Upgrade Sign Reversal to use global preference by default
+
+2017-11-26 Christopher Lam 
+
+	* REFACTOR: move calculated-cells to allow access from add-subtotal-row
+
+2017-12-11 Christopher Lam 
+
+	* ENH: 'original currency amt' now shows dual columns
+
+2017-12-11 Christopher Lam 
+
+	* REFACTOR+ENH:Add common-currency mnemonic to header if enabled
+
+2017-12-11 Christopher Lam 
+
+	* REFACTOR: move column-uses? location
+
+2017-12-11 Christopher Lam 
+
+	* REFACTOR:Move Void-status filter to filter tab
+
+2017-11-26 Christopher Lam 
+
+	* REFACTOR:Centralise sign-reverse-list
+
+2017-12-10 Christopher Lam 
+
+	* ENH: add infobox to summarise options used
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: Centralize options
+
+2017-11-27 Christopher Lam 
+
+	* ENH: disable filter accounts selector if filter-mode=none
+
+2017-12-10 Christopher Lam 
+
+	* ENH: dual columns subtotals now in correct column
+
+2017-12-11 Christopher Lam 
+
+	* REFACTOR: Use time64 instead of timepair
+
+2017-12-10 Christopher Lam 
+
+	* COSMETIC: Move Display>Sign reversal option
+
+2017-12-10 Christopher Lam 
+
+	* ENH: Enable sign reversal for amount 'single only
+
+2017-12-10 Christopher Lam 
+
+	* ENH: "Shares" column gets number-cell styling
+
+2017-12-13 Christopher Lam 
+
+	* ENH: "Price" column gets number-cell styling.
+
+2017-12-15 Christopher Lam 
+
+	* ENH: show original currency, and enable multicolumns.
+
+2017-12-15 Christopher Lam 
+
+	* ENH: add custom sorter which can handle periodic dates
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: simplify do-rows-with-subtotals to use fewer args
+
+2017-12-15 Christopher Lam 
+
+	* REFACTOR: use scheme idioms
+
+2017-12-15 Christopher Lam 
+
+	* REFACTOR: Simplify Trans Number handling
+
+2017-12-10 Christopher Lam 
+
+	* COSMETIC:Rename subtitles -> subheadings in sorting/account display
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: initialize accounts/filter by to null list
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: rewrite renderers to lookup 'renderer-key from sortlists
+
+2017-12-10 Christopher Lam 
+
+	* ENH: Show account description in subheadings
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: move add-split-row into make-split-table
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: move *-choice-list into options-generator
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: simplify functions, reduce arguments
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: improve heading-list to handle dual headings
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: centralize BOOK-SPLIT-ACTION
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: centralize DATE-SORTING-TYPES and SUBTOTAL-ENABLED
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: centralize key-choice-list
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: centralize date-subtotal-list
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: centralize sortkey-list
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: centralize numerous used-* into column-uses? helper function
+
+2017-12-22 Christopher Lam 
+
+	* REFACTOR: combine 2 key-choice-list into 1
+
+2017-12-22 Christopher Lam 
+
+	* REFACTOR: move some funcs to refactor later
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: always run qof-query-destroyer
+
+2017-12-09 Christopher Lam 
+
+	* ENH: Optimise Transaction Matcher filter
+
+2017-12-10 Christopher Lam 
+
+	* ENH: add reconciled status filtering
+
+2017-12-10 Christopher Lam 
+
+	* ENH: Move Account matcher to Filter tab
+
+2017-12-09 Christopher Lam 
+
+	* REFACTOR: Delete unused functions
+
+2017-12-10 Christopher Lam 
+
+	* REFACTOR: rename funcs, centralize strings
+
+2017-12-10 Christopher Lam 
+
+	* ***reindent and remove trailing whitespace***
+
+2017-12-09 Christopher Lam 
+
+	* ENH: Move Transaction Matcher to new Filter tab
+
+2017-12-12 Christopher Lam 
+
+	* BUGFIX: change date-sorting-types
+
+2017-12-12 Christopher Lam 
+
+	* OBSOLETE: 'exact-time removed
+
+2017-12-21 Christopher Lam 
+
+	* options.scm: upgrade lookup-value to learn section changes
+
+2017-12-23 Geert Janssens 
+
+	* Merge branch 'fix_bayes' of https://github.com/limitedAtonement/gnucash into unstable
+
+2017-12-22 Geert Janssens 
+
+	* Remove cmake hoop to change file permissions
+
+2017-12-21 Geert Janssens 
+
+	* Replace GNC_CONFIGURE(2) with configure_file
+
+2017-12-22 John Ralls 
+
+	* Fix use of guile function introduced in 2.0.10, not available in Ubuntu14.04.
+
+2017-12-22 John Ralls 
+
+	* Don't build borrowed/gwengui-gtk3 if its provided by gwenhywfar.
+
+2017-12-21 John Ralls 
+
+	* Replace the gnc:numeric pair with normal Scheme rationals.
+
+2017-12-18 John Ralls 
+
+	* Remove SIGFIG rounding from price calculation.
+
+2017-12-21 lmat 
+
+	* Adding to version info to feature string
+
+2017-12-12 lmat 
+
+	* Correct string cache code
+
+2017-12-10 lmat 
+
+	* Rename qofinstance function
+
+2017-12-09 lmat 
+
+	* Changed bayes import map design
+
+2017-12-06 lmat 
+
+	* Code review responses
+
+2017-12-01 lmat 
+
+	* Changed some constants to constexpr
+
+2017-11-28 lmat 
+
+	* Remove unused kvp function
+
+2017-11-28 lmat 
+
+	* kvp string: allocate enough space
+
+2017-11-28 lmat 
+
+	* Correct kvp to_string typo
+
+2017-11-16 lmat 
+
+	* Keep tokens as they are, don't translate them
+
+2017-11-15 lmat 
+
+	* Corrected memory management issue
+
+2017-11-09 lmat 
+
+	* Renaming functions to get rid of temporary name
+
+2017-11-06 lmat 
+
+	* Kvp no longer parses entries looking for delimiters
+
+2017-11-03 lmat 
+
+	* Added test for and corrected get_bayes_info
+
+2017-11-02 lmat 
+
+	* Removed qof_instance_set_kvp, qof_instance_get_kvp
+
+2017-10-27 lmat 
+
+	* Fixed conversion problem
+
+2017-10-19 lmat 
+
+	* Implement flat bayes kvp
+
+2017-10-19 lmat 
+
+	* Change kvp string representation
+
+2017-10-05 lmat 
+
+	* kvp frame to template and correcting failure macro
+
+2017-08-14 lmat 
+
+	* Account.c to Account.cpp
+
+2017-12-20 Geert Janssens 
+
+	* Force build order on report system support files
+
+2017-12-19 Geert Janssens 
+
+	* Add unit test for rewritten scheme error handlers
+
+2017-12-19 Geert Janssens 
+
+	* Drop guile 1.8 support
+
+2017-12-19 Geert Janssens 
+
+	* Add support for guile 2.2
+
+2017-12-19 John Ralls 
+
+	* Merge Chris Lam's Bug 790526 fix into maint.
+
+2017-12-20 christopherlam 
+
+	* fix silly mistake (PR232)
+
+2017-12-20 christopherlam 
+
+	* fix unit test again
+
+2017-12-19 John Ralls 
+
+	* Add test-date-utilities to CMakeLists.txt and Makefile.am.
+
+2017-12-19 John Ralls 
+
+	* Fix duplicate test-case name.
+
+2017-12-20 christopherlam 
+
+	* typo
+
+2017-12-20 christopherlam 
+
+	* add unittest for bugzilla 790526
+
+2017-12-19 christopherlam 
+
+	* Fix -DWITH_SQL=OFF Build.
+
+2017-12-08 Robert Fewell 
+
+	* Change the relative path to a full one for rpath
+
+2017-12-19 John Ralls 
+
+	* Fix date offset error in datetime test.
+
+2017-12-18 John Ralls 
+
+	* Add tests for GMT and GMT+7 timezones.
+
+2017-12-18 John Ralls 
+
+	* Fix timezone constructor crash when zone file has no transitions.
+
+2017-12-17 John Ralls 
+
+	* Merge branch 'maint' into unstable (origin/master, origin/HEAD)
+
+2017-12-16 John Ralls 
+
+	* Release 2.6.19 (tag: 2.6.19)
+
+2017-12-16 John Ralls 
+
+	* Fix python tests when building from tarball.
+
+2017-12-16 John Ralls 
+
+	* Update Dutch and Serbian translations from the Translation project.
+
+2017-12-16 John Ralls 
+
+	* Merge Rob Gowin's Partial Fix for Bug 787497 into maint.
+
+2017-12-16 John Ralls 
+
+	* Add test-flat-bayes to autotools build.
+
+2017-12-16 John Ralls 
+
+	* Add minimum version to feature and fix copy-paste error in test-flat-bayes.
+
 2017-12-15 John Ralls 
 
-	* Handle mid-pacific timezones in date-sensitive tests. (HEAD -> unstable, origin/unstable)
+	* Handle mid-pacific timezones in date-sensitive tests.
 
 2017-12-15 John Ralls 
 
 	* Revert post-construction adjustment of ldt for DST.
 
+2017-12-14 Robert Fewell 
+
+	* Change the way the import settings are handled
+
 2017-12-12 John Ralls 
 
 	* Fix neutral time for consistent dates in mid-pacific time zones.
@@ -26,6 +698,10 @@
 
 	* Add infrastructure to handle preference schema migrations and use it to replace one preference
 
+2017-12-11 Robert Fewell 
+
+	* Remove surplus statement
+
 2017-12-08 John Ralls 
 
 	* Fix the Mac install_name_dir to point at CMAKE_INSTALL_FULL_LIBDIR.
@@ -34,6 +710,106 @@
 
 	* Bug 791422 - gnucash 2.7 no longer opens sqlite3...
 
+2017-12-10 Robert Fewell 
+
+	* Update file with changes for transient dialog changes
+
+2017-12-10 Robert Fewell 
+
+	* Replace magic numbers used in std::get... with values from enum
+
+2017-12-10 Robert Fewell 
+
+	* Merge branch 'prices-in' of /mygit/gnucash into prices-in
+
+2017-12-07 Robert Fewell 
+
+	* Add a test for empty values
+
+2017-12-07 Robert Fewell 
+
+	* Replace date parse function with one from gnc_datetime
+
+2017-12-04 Robert Fewell 
+
+	* Make changes for Gtk3 compatibility
+
+2017-12-01 Robert Fewell 
+
+	* Pot file changes for new files and settings rename
+
+2017-12-01 Robert Fewell 
+
+	* Rename gnc-csv-trans-settings.* to gnc-csv-import-settings.*
+
+2017-12-01 Robert Fewell 
+
+	* Reorder the create price procedure.
+
+2017-12-01 Robert Fewell 
+
+	* Add the ability to test from_commodity and to_currency being the same.
+
+2017-12-01 Robert Fewell 
+
+	* Add a test for from_commodity not being the same as to_currency
+
+2017-12-01 Robert Fewell 
+
+	* Various changes to comments in source files and displayed text.
+
+2017-12-01 Robert Fewell 
+
+	* Change the way commodity and currency combo's are shown.
+
+2017-12-01 Robert Fewell 
+
+	* Minor changes and tidy up
+
+2017-12-01 Robert Fewell 
+
+	* Change the settings file to save and load price settings.
+
+2017-12-01 Robert Fewell 
+
+	* Fix some errors in conversion of some function names
+
+2017-12-01 Robert Fewell 
+
+	* Remove duplicated function
+
+2017-12-01 Robert Fewell 
+
+	* Made changes to preset column types to align with other changes
+
+2017-12-01 Robert Fewell 
+
+	* Add option to specify Commodity from and Currency to for whole file
+
+2017-12-01 Robert Fewell 
+
+	* Some text changes
+
+2017-12-01 Robert Fewell 
+
+	* Remove not required account update
+
+2017-12-01 Robert Fewell 
+
+	* Add CSV Price importer assistant files
+
+2017-12-01 Robert Fewell 
+
+	* Rename function gnc_csv_price_col_type_strs to gnc_price_col_type_strs
+
+2017-12-01 Robert Fewell 
+
+	* Add price import files for the csv price importer
+
+2017-12-01 Robert Fewell 
+
+	* Add property files for the csv price importer
+
 2017-12-09 John Ralls 
 
 	* Test more thoroughly gnc-timezone's parsing of the zoneinfo database.
@@ -44,7 +820,15 @@
 
 2017-12-08 Geert Janssens 
 
-	* Fix transient parent warnings in search callbacks (origin/master, origin/HEAD, master)
+	* Fix transient parent warnings in search callbacks
+
+2017-12-07 Robert Fewell 
+
+	* Add a test for empty values
+
+2017-12-07 Robert Fewell 
+
+	* Replace date parse function with one from gnc_datetime
 
 2017-12-06 Geert Janssens 
 
@@ -78,10 +862,98 @@
 
 	* Handle the splash/lock file warning more the gtk way
 
+2017-11-22 lmat 
+
+	* GUID/Flat bayes handling in 2.6
+
 2017-12-05 John Ralls 
 
 	* Rework directory determination in CMake builds.
 
+2017-12-04 Robert Fewell 
+
+	* Make changes for Gtk3 compatibility
+
+2017-12-01 Robert Fewell 
+
+	* Pot file changes for new files and settings rename
+
+2017-12-01 Robert Fewell 
+
+	* Rename gnc-csv-trans-settings.* to gnc-csv-import-settings.*
+
+2017-12-01 Robert Fewell 
+
+	* Reorder the create price procedure.
+
+2017-12-01 Robert Fewell 
+
+	* Add the ability to test from_commodity and to_currency being the same.
+
+2017-12-01 Robert Fewell 
+
+	* Add a test for from_commodity not being the same as to_currency
+
+2017-12-01 Robert Fewell 
+
+	* Various changes to comments in source files and displayed text.
+
+2017-12-01 Robert Fewell 
+
+	* Change the way commodity and currency combo's are shown.
+
+2017-12-01 Robert Fewell 
+
+	* Minor changes and tidy up
+
+2017-12-01 Robert Fewell 
+
+	* Change the settings file to save and load price settings.
+
+2017-12-01 Robert Fewell 
+
+	* Fix some errors in conversion of some function names
+
+2017-12-01 Robert Fewell 
+
+	* Remove duplicated function
+
+2017-12-01 Robert Fewell 
+
+	* Made changes to preset column types to align with other changes
+
+2017-12-01 Robert Fewell 
+
+	* Add option to specify Commodity from and Currency to for whole file
+
+2017-12-01 Robert Fewell 
+
+	* Some text changes
+
+2017-12-01 Robert Fewell 
+
+	* Remove not required account update
+
+2017-12-01 Robert Fewell 
+
+	* Add CSV Price importer assistant files
+
+2017-12-01 Robert Fewell 
+
+	* Rename function gnc_csv_price_col_type_strs to gnc_price_col_type_strs
+
+2017-12-01 Robert Fewell 
+
+	* Add price import files for the csv price importer
+
+2017-12-01 Robert Fewell 
+
+	* Add property files for the csv price importer
+
+2017-12-02 Rob Gowin 
+
+	* Partial fix for Bug 787497 - Disabling options cripples dist package
+
 2017-12-02 John Ralls 
 
 	* One more stray header in libexec.
@@ -138,6 +1010,10 @@
 
 	* Fix installation destinations for overrides and quotes scripts
 
+2017-11-19 Christopher Lam 
+
+	* Bugzilla 790526 Correct weeknum calculator
+
 2017-11-28 John Ralls 
 
 	* Fix python test failure on Travis.
@@ -1068,7 +1944,7 @@
 
 2017-09-26 Geert Janssens 
 
-	* Fix build failures if userdata_home exists but gnc_userdata_home  doesn't
+	* Fix build failures if userdata_home exists but gnc_userdata_home  doesn't (master)
 
 2017-09-26 Josep-Maria Prat 
 
@@ -7238,7 +8114,3 @@
 
 	* Add a missing gettext in plugin page owner tree
 
-2016-01-01 fell 
-
-	* Bug 760052 - missing flag translatable in Custom Report
-
diff --git a/NEWS b/NEWS
index 8697164..7b7311d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,124 @@
 Version history:
 ------- -------
+2.7.3 - 31 December 2017
+    The Gnucash Development Team is pleased to release Gnucash 2.7.2,
+    the third release of an unstable series leading to Gnucash 3.0.
+
+    Notice that we've decided that beginning with the upcoming major
+    release we will use two-digit release numbers and that the next
+    stable release will be 3.0. Mainenance releases will be 3.1, 3.2,
+    etc. The next unstable release will be 3.900 and will lead to 4.0.
+
+    This release is UNSTABLE and SHOULD NOT BE USED in production.
+    See the KNOWN PROBLEMS list at the bottom of the announcement.
+
+    This release changes file locations, binding APIs, report options,
+    and can make your data file no longer compatible with previous
+    versions. See https://wiki.gnucash.org/wiki/UpdateNotes for
+    details.
+
+New Features For Users:
+    A greatly enhanced Transaction report with many new options and features
+         including a reconciliation report thanks to Chris Lam.
+    Removed 6-figure rounding from price calculations, allowing prices to
+         have up to 18 digit precision.
+     A flatter storage scheme for Bayes account-matching scores, thanks
+           to Aaron Laws. This is a compatibility change guarded by a feature.
+     GnuCash no longer supports Guile-1.8 and now does support Guile-2.2
+     A CSV Price importer, thanks to Bob Fewell.
+     Enhanced python bindings exposing more GnuCash API thanks to Guy Taylor
+
+The following bugs are fixed only in unstable/master:
+    Bug 616709 - Pressing delete key while editing account name offers to
+         delete account.
+    Bug 771667 - Change reconciled splits warning
+         This patch displays two distinct warnings when changing
+         protected fields of a transaction that contains reconciled
+         splits. If the fields date, num and description are changed,
+         then the warning list the accounts that have reconciled
+         splits and also advises that they will be unreconciled after
+         editing the transaction. If the fields account, transfer,
+         debit or credit are changed then the warning advises that the
+         split will be unreconciled after editing the transaction.
+         There is still just one warning preference as it is all to do
+         with fields protected by reconciliation.
+    Bug 787497 - Disabling options cripples dist package PARTIAL FIX:
+          WITH_OFX, WITH_SQL and WITH_AQBANKING handling is fixed. The
+          dist files for these features are always included in the
+          dist tarball.
+    Bug 790526 - Mathematical bug
+    Bug 791848 - GC 2.6.x does not handle ISO dates introduced with GC 2.7.
+         Set a feature to prevent versions older that 2.6.20 from
+         loading a database from which they cannot read the
+         dates. Ideally we would do this only if the database is
+         written to, but the current persistence design includes
+         committing back to the database during the load so the net
+         effect is that the flag would be set anyway.
+         This is a compatibility change guarded by a feature.
+
+
+Other repairs not marked as bugs in git:
+    Tests now pass in all timezones.
+    More dialogs are made "transient for" so that they pop up centered on
+         the main Gnucash window instead of somewhere on the left edge of the
+         screen.
+    A lot of Gtk3 issues and errors are fixed, thanks to Bob Fewell.
+    Test struct tm* returns from gnc_gmtime and gnc_localtime to ensure that
+         we don't crash for dereffing a nullptr.
+    Removed the gnc:numeric type from Scheme code in favor of Scheme's own
+          rational numbers. This allows direct conversion between
+          Scheme numbers and gnc_numeric without the performance or
+          accuracy penalties arising from using doubles as an
+          intermediary.
+     Pass KVP paths as a collection of elements instead of a delimited string.
+           This allows keys to contain '/'. Thanks to Aaron Laws.
+     Added a frameowrk for migrating preferences.
+     Made separate functions for finding a widget's parent window:
+          gnc_ui_get_gtk_window tries to find the immediate parent and
+          gnc_ui_get_main_window tries to find the widget's parent
+          toplevel, returning the first-mapped window.
+     Make the splash/lock screen the transient parent for dialog boxes if the
+          main window is not yet mapped.
+     Rework directory determination in CMake builds.
+          Sets paths for finding componenents depending on the state
+          of ENABLE_BINRELOC, GNC_UNINSTALLED, GNC_BUILDDIR and
+          whether any install paths have been set outside of
+          CMAKE_INSTALL_PREFIX.  GNUInstallDirs changes the name of
+          CMAKE_INSTALL_LIBDIR depending on the operating system and
+          distro. When CMAKE_INSTALL_PREFIX is /usr, /usr/local, or
+          any subdirectory of /opt it also changes
+          CMAKE_INSTALL_FULL_SYSCONFDIR to /etc. An earlier commit by
+          Aaron Laws mirrors the name of CMAKE_INSTALL_LIBDIR to the
+          build library directory.  It's possible for builders to set
+          any of the install directories anywhere they please.
+          Setting any directory outside of CMAKE_INSTALL_PREFIX breaks
+          Binreloc so the toplevel CMakeLists.txt now detects that and
+          disables Binreloc.  If Binreloc is enabled then all path
+          queries use it to find paths. This works in the build
+          directory because the gnucash executable and all of the test
+          programs are in build_directory/bin and LIBDIR, DATADIR, and
+          SYSCONFDIR can be found in the same root path.  If Binreloc
+          is disabled then in order to build or run programs from the
+          build directory one must set GNC_UNINSTALLED and set
+          GNC_BUILDDIR to the absolute path of the build
+          directory. When those are set GNC_BUILDDIR replaces
+          CMAKE_INSTALL_PREFIX in all paths that are subdirectories of
+          CMAKE_INSTALL_PREFIX; paths that are not in
+          CMAKE_INSTALL_PREFIX are appended whole to
+          GNC_BUILDDIR. This process is constent between CMake and
+          gnc_path_get_foo. GnuCash is unlikely to run from a DESTDIR
+          without Binreloc.
+
+KNOWN PROBLEMS:
+
+    On Microsoft Windows starting the AQBanking Setup Wizard crashes GnuCash.
+
+    test-import-bayes built with autotools intermittently fails at
+    line 381, where the returned value is 1 instead of the expected 6.
+
+    Bug 789594 - Unable to overwrite splite3 database file
+    Bug 791823 - There is no Gtk3 theme-setting tool for Windows.
+    Bug 791825 - Accounting period dates off-by-1 
 2.6.19 - 16 December 2017
 
 The following bugs are fixed:
diff --git a/configure.ac b/configure.ac
index b3f1884..04d43c4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,7 +20,7 @@ dnl Process this file with autoconf to produce a configure script.
 
 # Autoconf initialization
 AC_PREREQ(2.60)
-AC_INIT([GnuCash], [2.7.2], [https://bugzilla.gnome.org/page.cgi?id=browse.html&product=GnuCash], , [http://www.gnucash.org/])
+AC_INIT([GnuCash], [2.7.3], [https://bugzilla.gnome.org/page.cgi?id=browse.html&product=GnuCash], , [http://www.gnucash.org/])
 AC_CONFIG_HEADERS(config.h)
 AC_CONFIG_SRCDIR(libgnucash/engine/Transaction.h)
 AC_CONFIG_MACRO_DIR([macros])

commit 0c6e2ebf8090ea693b02ee6b34c80f0ad088f695
Merge: 5823f1b 477e71b
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 30 13:58:04 2017 -0800

    Merge Bob Fewell's 'gtk3-update12' into unstable.


commit 5823f1b2bb6a927d9758958070bc65c46cd334da
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 30 12:06:52 2017 -0800

    Prevent "Save before closing" dialog from appearing at startup.
    
    Caused by simply loading the transaction report, which called
    gnc-get-current-book and that it in turn creates a session if one
    doesn't already exist.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 930fd71..0ee0df3 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -210,7 +210,8 @@ options specified in the Options panels."))
                                    (cons 'tip (_ "Sort by description."))
                                    (cons 'renderer-fn #f)))
 
-        (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
+        (if (and (gnc-current-session-exist)
+                 (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
             (cons 'number    (list (cons 'sortkey (list SPLIT-ACTION))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (_ "Number/Action"))
diff --git a/libgnucash/engine/engine.i b/libgnucash/engine/engine.i
index ad1e5f3..4604f25 100644
--- a/libgnucash/engine/engine.i
+++ b/libgnucash/engine/engine.i
@@ -31,6 +31,7 @@
 #include "gnc-filepath-utils.h"
 #include "gnc-pricedb.h"
 #include "gnc-lot.h"
+#include "gnc-session.h"
 #include "gnc-hooks-scm.h"
 #include "engine-helpers.h"
 #include "engine-helpers-guile.h"
@@ -187,6 +188,7 @@ SplitList * qof_query_run_subquery (QofQuery *q, const QofQuery *q);
 
 %typemap(in) QofQueryParamList * "$1 = gnc_query_scm2path($input);"
 
+%include <gnc-session.h>
 %include <Query.h>
 %ignore qof_query_run;
 %ignore qof_query_last_run;

commit 7feb9c65cf45e09f9f27c8197f09328139432395
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 30 13:40:14 2017 -0800

    Fix distcheck errors.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 867b577..4683798 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -821,7 +821,7 @@ ADD_CUSTOM_COMMAND(OUTPUT ${DIST_FILE}.gz ${DIST_FILE}.bz2
            -P ${CMAKE_SOURCE_DIR}/common/cmake_modules/MakeDist.cmake
 
         DEPENDS
-          ${ALL_DIST} ${DIST_GENERATED_FILES2} gnc-vcs-info iso-4217-c gnc-warnings-c build-config-scm gnucash-design-info ChangeLog
+          ${ALL_DIST} ${DIST_GENERATED_FILES2} gnc-vcs-info iso-4217-c gnc-warnings-c gnucash-design-info ChangeLog
         )
 
 ADD_CUSTOM_TARGET(dist DEPENDS ${DIST_FILE}.gz ${DIST_FILE}.bz2)
diff --git a/gnucash/report/report-system/CMakeLists.txt b/gnucash/report/report-system/CMakeLists.txt
index d97f5be..d8c4a77 100644
--- a/gnucash/report/report-system/CMakeLists.txt
+++ b/gnucash/report/report-system/CMakeLists.txt
@@ -111,8 +111,11 @@ GNC_ADD_SCHEME_TARGETS(scm-report-system-3
   FALSE
 )
 
-SET_LOCAL_DIST(report_system_DIST_local CMakeLists.txt Makefile.am report-system.i
-        ${report_system_HEADERS} ${report_system_SOURCES} ${report_system_SCHEME} ${report_system_SCHEME_1}
-        ${report_system_SCHEME_2} ${report_system_SCHEME_3})
+SET_LOCAL_DIST(report_system_DIST_local CMakeLists.txt Makefile.am
+  report-system.i
+  ${report_system_HEADERS} ${report_system_SOURCES}
+  ${report_system_SCHEME} ${report_system_SCHEME_1}
+  ${report_system_SCHEME_2a} ${report_system_SCHEME_2b}
+  ${report_system_SCHEME_3})
 
 SET(report_system_DIST ${report_system_DIST_local} ${test_report_system_DIST} PARENT_SCOPE)

commit d06ed7c1a8fafe473fcf786fd565ab7eea5c6c7e
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 30 13:39:55 2017 -0800

    Fix -Wsign-compare error.

diff --git a/libgnucash/engine/test/test-kvp-frame.cpp b/libgnucash/engine/test/test-kvp-frame.cpp
index 4a57c45..9e0491c 100644
--- a/libgnucash/engine/test/test-kvp-frame.cpp
+++ b/libgnucash/engine/test/test-kvp-frame.cpp
@@ -201,16 +201,16 @@ TEST (KvpFrameTestForEachPrefix, for_each_prefix_1)
     unsigned count {};
     auto counter = [] (char const *, KvpValue*, unsigned & count) { ++count; };
     fr.for_each_slot_prefix("one", counter, count);
-    EXPECT_EQ(count, 3);
+    EXPECT_EQ(count, UINT32_C(3));
     count = 0;
     fr.for_each_slot_prefix("onetwo", counter, count);
-    EXPECT_EQ(count, 2);
+    EXPECT_EQ(count, UINT32_C(2));
     count = 0;
     fr.for_each_slot_prefix("onetwothree", counter, count);
-    EXPECT_EQ(count, 1);
+    EXPECT_EQ(count, UINT32_C(1));
     count = 0;
     fr.for_each_slot_prefix("two", counter, count);
-    EXPECT_EQ(count, 0);
+    EXPECT_EQ(count, UINT32_C(0));
 }
 
 TEST (KvpFrameTestForEachPrefix, for_each_prefix_2)

commit 3d2682ac046e8476bc33f4982453eca7e796637c
Merge: d6bb34e 82f1384
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 30 13:33:43 2017 -0800

    Merge branch 'maint' into unstable

diff --cc libgnucash/app-utils/test/CMakeLists.txt
index c455423,0000000..e343331
mode 100644,000000..100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@@ -1,75 -1,0 +1,76 @@@
 +
 +SET(APP_UTILS_TEST_INCLUDE_DIRS
 +  ${CMAKE_BINARY_DIR}/common # for config.h
 +  ${CMAKE_SOURCE_DIR}/common/test-core
 +  ${CMAKE_SOURCE_DIR}/libgnucash/app-utils
 +  ${CMAKE_SOURCE_DIR}/libgnucash/engine # for qof.h
 +  ${CMAKE_SOURCE_DIR}/libgnucash/engine/test-core
 +  ${GIO_INCLUDE_DIRS}
 +  ${GUILE_INCLUDE_DIRS}
 +)
 +
 +SET(APP_UTILS_TEST_LIBS gncmod-app-utils gncmod-test-engine test-core ${GIO_LDFLAGS} ${GUILE_LDFLAGS})
 +
 +SET(test_app_utils_SOURCES test-app-utils.c test-option-util.cpp test-gnc-ui-util.c)
 +
 +MACRO(ADD_APP_UTILS_TEST _TARGET _SOURCE_FILES)
 +  GNC_ADD_TEST(${_TARGET} "${_SOURCE_FILES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS)
 +ENDMACRO()
 +
 +ADD_APP_UTILS_TEST(test-exp-parser test-exp-parser.c)
 +GNC_ADD_TEST_WITH_GUILE(test-link-module-app-utils test-link-module APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS)
 +ADD_APP_UTILS_TEST(test-print-parse-amount test-print-parse-amount.cpp)
 +# This test not run in autotools build.
 +#GNC_ADD_TEST_WITH_GUILE(test-print-queries test-print-queries.cpp APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS)
 +GNC_ADD_TEST_WITH_GUILE(test-scm-query-string test-scm-query-string.cpp
 +  APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS
 +)
 +ADD_APP_UTILS_TEST(test-sx test-sx.cpp)
 +
 +SET(GUILE_DEPENDS
 +  scm-test-engine
 +  scm-app-utils
 +  gnc-core-utils
 +  gnc-module
 +  gncmod-engine
 +  gncmod-backend-xml
 +  gncmod-backend-xml
 +)
 +
 +set(test_app_utils_scheme_SOURCES
 +  test-c-interface.scm
 +  test-load-app-utils-module.scm
++  test-date-utilities.scm
 +)
 +
 +GNC_ADD_SCHEME_TARGETS(scm-test-load-app-utils-module
 +  "test-load-app-utils-module.scm"
 +  "gnucash/reports"
 +  "${GUILE_DEPENDS}"
 +  FALSE
 +)
 +
 +GNC_ADD_SCHEME_TARGETS(scm-test-c-interface
 +  "test-c-interface.scm"
 +  ""
 +  "${GUILE_DEPENDS}"
 +  FALSE
 +)
 +
 +GNC_ADD_SCHEME_TESTS(${test_app_utils_scheme_SOURCES})
 +# Doesn't work yet:
 +GNC_ADD_TEST_WITH_GUILE(test-app-utils "${test_app_utils_SOURCES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS)
 +
 +SET_DIST_LIST(test_app_utils_DIST
 +  CMakeLists.txt
 +  Makefile.am
 +  test-exp-parser.c
 +  test-link-module.c
 +  test-print-parse-amount.cpp
 +  test-print-queries.cpp
 +  test-scm-query-string.cpp
 +  test-sx.cpp
 +  test-c-interface.scm
 +  ${test_app_utils_scheme_SOURCES}
 +  ${test_app_utils_SOURCES}
 +)
diff --cc libgnucash/app-utils/test/Makefile.am
index 3ebb930,0000000..5a25f24
mode 100644,000000..100644
--- a/libgnucash/app-utils/test/Makefile.am
+++ b/libgnucash/app-utils/test/Makefile.am
@@@ -1,87 -1,0 +1,87 @@@
 +include $(top_srcdir)/test-templates/Makefile.decl
 +
 +check_PROGRAMS = \
 +  test-link-module \
 +  test-exp-parser \
 +  test-scm-query-string \
 +  test-print-parse-amount \
 +  test-sx \
 +  test-app-utils
 +
 +TESTS =  \
 +  ${check_PROGRAMS} \
 +  ${SCM_TESTS}
 +
 +test_scm_query_string_SOURCES = test-scm-query-string.cpp
 +test_sx_SOURCES = test-sx.cpp
 +test_print_parse_amount_SOURCES = test-print-parse-amount.cpp
 +
 +GNC_TEST_DEPS = \
 +  --gnc-module-dir ${top_builddir}/libgnucash/engine \
 +  --gnc-module-dir ${top_builddir}/libgnucash/engine/test \
 +  --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
 +  --guile-load-dir ${top_builddir}/libgnucash/core-utils \
 +  --guile-load-dir ${top_builddir}/libgnucash/gnc-module \
 +  --guile-load-dir ${top_builddir}/libgnucash/engine \
 +  --guile-load-dir ${top_builddir}/libgnucash/engine/test \
 +  --guile-load-dir ${top_builddir}/libgnucash/scm \
 +  --guile-load-dir ${top_builddir}/libgnucash/app-utils \
 +  --library-dir    ${top_builddir}/libgnucash/core-utils \
 +  --library-dir    ${top_builddir}/libgnucash/gnc-module \
 +  --library-dir    ${top_builddir}/libgnucash/engine \
 +  --library-dir    ${top_builddir}/libgnucash/backend/xml \
 +  --library-dir    ${top_builddir}/libgnucash/backend/sql \
 +  --library-dir    ${top_builddir}/libgnucash/app-utils
 +
 +TESTS_ENVIRONMENT = \
 +  GUILE_WARN_DEPRECATED=no \
 +  GUILE="${GUILE}" \
 +  SRCDIR=${srcdir} \
 +  GNC_BUILDDIR="${abs_top_builddir}" \
 +  $(shell ${abs_top_srcdir}/common/gnc-test-env.pl --noexports ${GNC_TEST_DEPS})
 +
 +LDADD = \
 +   ${top_builddir}/libgnucash/core-utils/libgnc-core-utils.la \
 +   ${top_builddir}/libgnucash/engine/libgncmod-engine.la \
 +   ${top_builddir}/libgnucash/gnc-module/libgnc-module.la \
 +   ${top_builddir}/libgnucash/app-utils/libgncmod-app-utils.la \
 +   ${top_builddir}/common/test-core/libtest-core.la \
 +   ${top_builddir}/libgnucash/engine/test-core/libgncmod-test-engine.la \
 +   ${GUILE_LIBS}
 +
 +EXTRA_DIST += \
 +  test-print-queries.cpp \
 +  ${SCM_TEST_SRCS} \
 +  CMakeLists.txt
 +
 +AM_CPPFLAGS = \
 +  -I${top_srcdir}/common \
 +  -I${top_srcdir}/common/test-core \
 +  -I${top_srcdir}/libgnucash/engine \
 +  -I${top_srcdir}/libgnucash/engine/test-core \
 +  -I${top_srcdir}/libgnucash/app-utils \
 +  -I${top_srcdir}/libgnucash/gnc-module \
 +  -I${top_srcdir}/libgnucash/core-utils \
 +  ${GUILE_CFLAGS} \
 +  ${GLIB_CFLAGS} \
 +  ${BOOST_CPPFLAGS}
 +
 +test_app_utils_SOURCES = \
 +	test-app-utils.c \
 +	test-option-util.cpp \
 +	test-gnc-ui-util.c
 +
 +test_app_utils_CXXFLAGS = \
 +	${DEFAULT_INCLUDES} \
 +	-I${top_srcdir}/${MODULEPATH}/ \
 +	-DTESTPROG=test_app_utils \
 +	${GLIB_CFLAGS}
 +
- SCM_TESTS =   test-load-app-utils-module test-c-interface
++SCM_TESTS =   test-load-app-utils-module test-c-interface test-date-utilities
 +SCM_TEST_SRCS = $(SCM_TESTS:%=%.scm)
 +
 +$(SCM_TESTS): %: $(srcdir)/%.scm Makefile
 +	echo 'export GNC_BUILDDIR="${abs_top_builddir}";' > $@
 +	echo 'export GNC_UNINSTALLED=yes;' >> $@
 +	echo '${GUILE} --debug -l $(srcdir)/$*.scm -c "(exit (run-test))"' >> $@
 +	chmod a+x $@
diff --cc libgnucash/app-utils/test/test-date-utilities.scm
index 0000000,6fca517..6fca517
mode 000000,100644..100644
--- a/libgnucash/app-utils/test/test-date-utilities.scm
+++ b/libgnucash/app-utils/test/test-date-utilities.scm

commit 477e71b7a8b23d8045236aedee7da84910b5f5ef
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:21:37 2017 +0000

    Fix register move to another window
    
    When the a register is moved to another window, the header ciaro surface
     is not created so test for one and create if null.

diff --git a/gnucash/register/register-gnome/gnucash-header.c b/gnucash/register/register-gnome/gnucash-header.c
index 156ae68..017a496 100644
--- a/gnucash/register/register-gnome/gnucash-header.c
+++ b/gnucash/register/register-gnome/gnucash-header.c
@@ -49,22 +49,6 @@ enum
     PROP_CURSOR_NAME, /* the name of the current cursor */
 };
 
-static gboolean
-gnc_header_draw (GtkWidget *header, cairo_t *cr)
-{
-    GnucashSheet *sheet = GNC_HEADER(header)->sheet;
-    GdkWindow *sheet_layout_win = gtk_layout_get_bin_window (GTK_LAYOUT(sheet));
-    gint x, y;
-
-    // use this to get the scroll x value to align the header
-    gdk_window_get_position (sheet_layout_win, &x, &y);
-
-    cairo_set_source_surface (cr, GNC_HEADER(header)->surface, x, 0);
-    cairo_paint (cr);
-
-    return TRUE;
-}
-
 static void
 gnc_header_draw_offscreen (GncHeader *header)
 {
@@ -202,6 +186,28 @@ gnc_header_draw_offscreen (GncHeader *header)
 }
 
 
+static gboolean
+gnc_header_draw (GtkWidget *header, cairo_t *cr)
+{
+    GnucashSheet *sheet = GNC_HEADER(header)->sheet;
+    GdkWindow *sheet_layout_win = gtk_layout_get_bin_window (GTK_LAYOUT(sheet));
+    gint x, y;
+
+    // use this to get the scroll x value to align the header
+    gdk_window_get_position (sheet_layout_win, &x, &y);
+
+    // if the register page is moved to another window, the surface is
+    // not created so test for a surface and create one if null
+    if (GNC_HEADER(header)->surface == NULL)
+        gnc_header_draw_offscreen (GNC_HEADER(header));
+
+    cairo_set_source_surface (cr, GNC_HEADER(header)->surface, x, 0);
+    cairo_paint (cr);
+
+    return TRUE;
+}
+
+
 void
 gnc_header_request_redraw (GncHeader *header)
 {

commit 108f7fbd117b32f7722fef89ffb8143a4031c07b
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:20:58 2017 +0000

    Fix some transient parent warnings

diff --git a/gnucash/gnome-utils/dialog-preferences.c b/gnucash/gnome-utils/dialog-preferences.c
index b3ba114..815587e 100644
--- a/gnucash/gnome-utils/dialog-preferences.c
+++ b/gnucash/gnome-utils/dialog-preferences.c
@@ -1197,7 +1197,7 @@ gnc_prefs_connect_one (const gchar *name,
  *  @return A pointer to the newly created dialog.
  */
 static GtkWidget *
-gnc_preferences_dialog_create(void)
+gnc_preferences_dialog_create(GtkWindow *parent)
 {
     GtkBuilder *builder;
     GtkWidget *dialog, *notebook, *label, *image;
@@ -1236,6 +1236,9 @@ gnc_preferences_dialog_create(void)
     // Set the style context for this dialog so it can be easily manipulated with css
     gnc_widget_set_style_context (GTK_WIDGET(dialog), "GncPreferenceDialog");
 
+    /* parent */
+    gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
+
 #ifndef REGISTER2_ENABLED
     /* Hide preferences that are related to register2 */
     box = GTK_WIDGET (gtk_builder_get_object (builder, "label14"));
@@ -1273,8 +1276,8 @@ gnc_preferences_dialog_create(void)
     book = gnc_get_current_book();
     g_date_clear (&fy_end, 1);
     qof_instance_get (QOF_INSTANCE (book),
-		      "fy-end", &fy_end,
-		      NULL);
+              "fy-end", &fy_end,
+              NULL);
     box = GTK_WIDGET(gtk_builder_get_object (builder,
                      "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_START_PERIOD));
     period = gnc_period_select_new(TRUE);
@@ -1425,7 +1428,7 @@ close_handler (gpointer user_data)
  *  preferences dialog already exists it will be raised to the top of
  *  the window stack instead of creating a new dialog. */
 void
-gnc_preferences_dialog (void)
+gnc_preferences_dialog (GtkWindow *parent)
 {
     GtkWidget *dialog;
 
@@ -1437,7 +1440,7 @@ gnc_preferences_dialog (void)
         return;
     }
 
-    dialog = gnc_preferences_dialog_create();
+    dialog = gnc_preferences_dialog_create(parent);
 
     gnc_restore_window_size(GNC_PREFS_GROUP, GTK_WINDOW(dialog));
     gtk_widget_show(dialog);
diff --git a/gnucash/gnome-utils/dialog-preferences.h b/gnucash/gnome-utils/dialog-preferences.h
index 50ec8fa..f3154ea 100644
--- a/gnucash/gnome-utils/dialog-preferences.h
+++ b/gnucash/gnome-utils/dialog-preferences.h
@@ -60,6 +60,8 @@
 #ifndef GNC_DIALOG_PREFERENCES_H
 #define GNC_DIALOG_PREFERENCES_H
 
+#include <gtk/gtk.h>
+
 /** This function adds a full page of preferences to the preferences
  *  dialog.  When the dialog is created, the specified widget will be
  *  pulled from the specified glade file and added to the preferences
@@ -100,7 +102,7 @@ void gnc_preferences_add_to_page (const gchar *filename,
  *  the user.  The preferences dialog is a singleton, so if a
  *  preferences dialog already exists it will be raised to the top of
  *  the window stack instead of creating a new dialog. */
-void gnc_preferences_dialog (void);
+void gnc_preferences_dialog (GtkWindow *parent);
 
 #endif
 /** @} */
diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.c
index a65f51e..38f1073 100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.c
@@ -4199,7 +4199,7 @@ gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window)
 static void
 gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window)
 {
-    gnc_preferences_dialog ();
+    gnc_preferences_dialog (GTK_WINDOW(window));
 }
 
 static void
diff --git a/gnucash/gnome/dialog-fincalc.c b/gnucash/gnome/dialog-fincalc.c
index 25152cf..d710183 100644
--- a/gnucash/gnome/dialog-fincalc.c
+++ b/gnucash/gnome/dialog-fincalc.c
@@ -549,7 +549,7 @@ fincalc_init_commodity_gae (GNCAmountEdit *edit)
 }
 
 void
-gnc_ui_fincalc_dialog_create(void)
+gnc_ui_fincalc_dialog_create(GtkWindow *parent)
 {
     FinCalcDialog *fcd;
     GtkWidget *button;
@@ -575,6 +575,10 @@ gnc_ui_fincalc_dialog_create(void)
     // Set the style context for this dialog so it can be easily manipulated with css
     gnc_widget_set_style_context (GTK_WIDGET(fcd->dialog), "GncFinCalcDialog");
 
+    /* parent */
+    if (parent != NULL)
+        gtk_window_set_transient_for (GTK_WINDOW(fcd->dialog), GTK_WINDOW(parent));
+
     gnc_register_gui_component (DIALOG_FINCALC_CM_CLASS,
                                 NULL, close_handler, fcd);
 
diff --git a/gnucash/gnome/dialog-fincalc.h b/gnucash/gnome/dialog-fincalc.h
index 8a91af1..bca4b33 100644
--- a/gnucash/gnome/dialog-fincalc.h
+++ b/gnucash/gnome/dialog-fincalc.h
@@ -25,7 +25,7 @@
 
 typedef struct _FinCalcDialog FinCalcDialog;
 
-void gnc_ui_fincalc_dialog_create(void);
+void gnc_ui_fincalc_dialog_create(GtkWindow *parent);
 void gnc_ui_fincalc_dialog_destroy(FinCalcDialog *fcd);
 
 #endif
diff --git a/gnucash/gnome/dialog-trans-assoc.c b/gnucash/gnome/dialog-trans-assoc.c
index f5d8658..5f83eeb 100644
--- a/gnucash/gnome/dialog-trans-assoc.c
+++ b/gnucash/gnome/dialog-trans-assoc.c
@@ -375,7 +375,7 @@ get_trans_info (AssocDialog *assoc_dialog)
 }
 
 static void
-gnc_assoc_dialog_create (AssocDialog *assoc_dialog)
+gnc_assoc_dialog_create (GtkWindow *parent, AssocDialog *assoc_dialog)
 {
     GtkWidget         *dialog;
     GtkBuilder        *builder;
@@ -396,6 +396,10 @@ gnc_assoc_dialog_create (AssocDialog *assoc_dialog)
     // Set the style context for this dialog so it can be easily manipulated with css
     gnc_widget_set_style_context (GTK_WIDGET(dialog), "GncTransAssocDialog");
 
+    /* parent */
+    if (parent != NULL)
+        gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
+
     assoc_dialog->view = GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
     path_head = GTK_WIDGET(gtk_builder_get_object (builder, "path-head"));
 
@@ -500,7 +504,7 @@ show_handler (const char *klass, gint component_id,
  * Return: nothing                                                  *
 \********************************************************************/
 void
-gnc_trans_assoc_dialog ()
+gnc_trans_assoc_dialog (GtkWindow *parent)
 {
     AssocDialog *assoc_dialog;
 
@@ -512,7 +516,7 @@ gnc_trans_assoc_dialog ()
     }
     assoc_dialog = g_new0 (AssocDialog, 1);
 
-    gnc_assoc_dialog_create (assoc_dialog);
+    gnc_assoc_dialog_create (parent, assoc_dialog);
 
     gnc_register_gui_component (DIALOG_ASSOC_CM_CLASS,
                    refresh_handler, close_handler,
diff --git a/gnucash/gnome/dialog-trans-assoc.h b/gnucash/gnome/dialog-trans-assoc.h
index fa82fcb..c95c5b7 100644
--- a/gnucash/gnome/dialog-trans-assoc.h
+++ b/gnucash/gnome/dialog-trans-assoc.h
@@ -23,6 +23,6 @@
 #ifndef DIALOG_TRANS_ASSOC_H
 #define DIALOG_TRANS_ASSOC_H
 
-void gnc_trans_assoc_dialog (void);
+void gnc_trans_assoc_dialog (GtkWindow *parent);
 
 #endif
diff --git a/gnucash/gnome/gnc-plugin-basic-commands.c b/gnucash/gnome/gnc-plugin-basic-commands.c
index 3142c71..d44cec7 100644
--- a/gnucash/gnome/gnc-plugin-basic-commands.c
+++ b/gnucash/gnome/gnc-plugin-basic-commands.c
@@ -616,7 +616,7 @@ static void
 gnc_main_window_cmd_tools_imap_editor (GtkAction *action, GncMainWindowActionData *data)
 {
     gnc_set_busy_cursor(NULL, TRUE);
-    gnc_imap_dialog (NULL);
+    gnc_imap_dialog (GTK_WIDGET (data->window));
     gnc_unset_busy_cursor(NULL);
 }
 
@@ -624,7 +624,7 @@ static void
 gnc_main_window_cmd_tools_trans_assoc (GtkAction *action, GncMainWindowActionData *data)
 {
     gnc_set_busy_cursor (NULL, TRUE);
-    gnc_trans_assoc_dialog ();
+    gnc_trans_assoc_dialog (GTK_WINDOW (data->window));
     gnc_unset_busy_cursor (NULL);
 }
 
@@ -632,7 +632,7 @@ static void
 gnc_main_window_cmd_tools_price_editor (GtkAction *action, GncMainWindowActionData *data)
 {
     gnc_set_busy_cursor(NULL, TRUE);
-    gnc_prices_dialog (NULL);
+    gnc_prices_dialog (GTK_WIDGET (data->window));
     gnc_unset_busy_cursor(NULL);
 }
 
@@ -640,14 +640,14 @@ static void
 gnc_main_window_cmd_tools_commodity_editor (GtkAction *action, GncMainWindowActionData *data)
 {
     gnc_set_busy_cursor(NULL, TRUE);
-    gnc_commodities_dialog (NULL);
+    gnc_commodities_dialog (GTK_WIDGET (data->window));
     gnc_unset_busy_cursor(NULL);
 }
 
 static void
 gnc_main_window_cmd_tools_financial_calculator (GtkAction *action, GncMainWindowActionData *data)
 {
-    gnc_ui_fincalc_dialog_create();
+    gnc_ui_fincalc_dialog_create(GTK_WINDOW (data->window));
 }
 
 static void
diff --git a/gnucash/gnome/gtkbuilder/dialog-price.glade b/gnucash/gnome/gtkbuilder/dialog-price.glade
index cbf5c51..e09c6cc 100644
--- a/gnucash/gnome/gtkbuilder/dialog-price.glade
+++ b/gnucash/gnome/gtkbuilder/dialog-price.glade
@@ -361,7 +361,6 @@
     </columns>
   </object>
   <object class="GtkDialog" id="deletion_date_dialog">
-    <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="border_width">6</property>
     <property name="title" translatable="yes">Remove Old Prices</property>
diff --git a/gnucash/gnome/gtkbuilder/dialog-trans-assoc.glade b/gnucash/gnome/gtkbuilder/dialog-trans-assoc.glade
index eaf2b40..57e92b7 100644
--- a/gnucash/gnome/gtkbuilder/dialog-trans-assoc.glade
+++ b/gnucash/gnome/gtkbuilder/dialog-trans-assoc.glade
@@ -21,7 +21,6 @@
     </columns>
   </object>
   <object class="GtkDialog" id="transaction_association_dialog">
-    <property name="visible">True</property>
     <property name="can_focus">True</property>
     <property name="has_focus">True</property>
     <property name="can_default">True</property>

commit 7c52f9ec43ba24c5bf89d2c5af1c02a616686e05
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:20:06 2017 +0000

    Fix There is no budget icon yet so set it to the account one.

diff --git a/gnucash/gnome-utils/gnc-icons.h b/gnucash/gnome-utils/gnc-icons.h
index 72d7645..3a00be3 100644
--- a/gnucash/gnome-utils/gnc-icons.h
+++ b/gnucash/gnome-utils/gnc-icons.h
@@ -49,7 +49,7 @@ G_BEGIN_DECLS
 #define GNC_ICON_PDF_EXPORT "gnc-gnome-pdf"
 
 //FIXME: use own budget icons?
-#define GNC_ICON_BUDGET "gnc-budget"
+#define GNC_ICON_BUDGET "gnc-account"
 #define GNC_ICON_NEW_BUDGET "gnc-account"
 #define GNC_ICON_OPEN_BUDGET "gnc-account-open"
 //#define GNC_ICON_CLOSE_BUDGET "gnc-close-account"

commit 7ec281f6474258d9f7fab259c78de137ffeebab3
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:19:28 2017 +0000

    Fix error when sheet is read only.
    
    When the sheet is read only the entry is not realized so test for this
    before passing event to it.

diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c
index 520d33b..68c5924 100644
--- a/gnucash/register/register-gnome/gnucash-sheet.c
+++ b/gnucash/register/register-gnome/gnucash-sheet.c
@@ -1809,9 +1809,13 @@ gnucash_sheet_key_press_event_internal (GtkWidget *widget, GdkEventKey *event)
     /* Forward the keystroke to the input line */
     if (pass_on)
     {
-        gboolean result;
+        gboolean result = FALSE;
         gtk_editable_set_editable(GTK_EDITABLE(sheet->entry), TRUE);
-        result = gtk_widget_event (sheet->entry, (GdkEvent *) event);
+
+        // If sheet is readonly, entry is not realized
+        if (gtk_widget_get_realized (GTK_WIDGET(sheet->entry)))
+            result = gtk_widget_event (sheet->entry, (GdkEvent *) event);
+
         gtk_editable_set_editable(GTK_EDITABLE(sheet->entry), FALSE);
         return result;
     }

commit 7dc3995aaa48fb1c438fe2274d38a572c211d584
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:18:46 2017 +0000

    Fix transient parent warnings for budgets

diff --git a/gnucash/gnome/gnc-plugin-budget.c b/gnucash/gnome/gnc-plugin-budget.c
index aab03fa..b885fb9 100644
--- a/gnucash/gnome/gnc-plugin-budget.c
+++ b/gnucash/gnome/gnc-plugin-budget.c
@@ -242,7 +242,7 @@ gnc_plugin_budget_cmd_open_budget (GtkAction *action,
         }
         else
         {
-            bgt = gnc_budget_gui_select_budget(book);
+            bgt = gnc_budget_gui_select_budget(GTK_WINDOW(data->window), book);
         }
 
         if (bgt) gnc_main_window_open_page(
@@ -276,7 +276,7 @@ gnc_plugin_budget_cmd_copy_budget (GtkAction *action,
         }
         else
         {
-            bgt = gnc_budget_gui_select_budget(book);
+            bgt = gnc_budget_gui_select_budget(GTK_WINDOW(data->window), book);
         }
 
         if (bgt)
@@ -311,7 +311,7 @@ row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *column,
 }
 
 GncBudget *
-gnc_budget_gui_select_budget(QofBook *book)
+gnc_budget_gui_select_budget(GtkWindow *parent, QofBook *book)
 {
     GncBudget *bgt;
     GtkDialog *dlg;
@@ -323,7 +323,7 @@ gnc_budget_gui_select_budget(QofBook *book)
     gboolean ok;
 
     dlg = GTK_DIALOG(gtk_dialog_new_with_buttons(
-                         _("Select a Budget"), NULL, GTK_DIALOG_MODAL,
+                         _("Select a Budget"), parent, GTK_DIALOG_MODAL,
                          _("_OK"), GTK_RESPONSE_OK,
                          _("_Cancel"), GTK_RESPONSE_CANCEL, NULL));
 
diff --git a/gnucash/gnome/gnc-plugin-budget.h b/gnucash/gnome/gnc-plugin-budget.h
index 5a1f6c2..7e63f35 100644
--- a/gnucash/gnome/gnc-plugin-budget.h
+++ b/gnucash/gnome/gnc-plugin-budget.h
@@ -59,7 +59,7 @@ GType gnc_plugin_budget_get_type(void);
 GncPlugin *gnc_plugin_budget_new(void);
 
 /* Launch the budget list dialog.*/
-GncBudget * gnc_budget_gui_select_budget(QofBook *book);
+GncBudget * gnc_budget_gui_select_budget(GtkWindow *parent, QofBook *book);
 
 
 G_END_DECLS
diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c
index 5f2607a..f39f2c4 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.c
+++ b/gnucash/gnome/gnc-plugin-page-budget.c
@@ -163,7 +163,7 @@ static GtkActionEntry gnc_plugin_page_budget_actions [] =
 static guint gnc_plugin_page_budget_n_actions =
     G_N_ELEMENTS (gnc_plugin_page_budget_actions);
 
-#if 0 
+#if 0
 static const gchar *actions_requiring_account[] =
 {
     "OpenAccountAction",
@@ -785,8 +785,7 @@ gnc_plugin_page_budget_cmd_view_options (GtkAction *action,
 
         priv->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "budget_options_container_dialog"));
 
-        gtk_window_set_transient_for(
-            GTK_WINDOW(priv->dialog),
+        gtk_window_set_transient_for(GTK_WINDOW(priv->dialog),
             GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))));
 
         gbname = GTK_WIDGET(gtk_builder_get_object (builder, "BudgetName"));
@@ -939,8 +938,7 @@ gnc_plugin_page_budget_cmd_estimate_budget(GtkAction *action,
 
     dialog = GTK_WIDGET(gtk_builder_get_object (builder, "budget_estimate_dialog"));
 
-    gtk_window_set_transient_for(
-        GTK_WINDOW(dialog),
+    gtk_window_set_transient_for(GTK_WINDOW(dialog),
         GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))));
 
     hb = GTK_WIDGET(gtk_builder_get_object (builder, "StartDate_hbox"));
diff --git a/gnucash/gnome/gtkbuilder/gnc-plugin-page-budget.glade b/gnucash/gnome/gtkbuilder/gnc-plugin-page-budget.glade
index b05fa0d..aa2b171 100644
--- a/gnucash/gnome/gtkbuilder/gnc-plugin-page-budget.glade
+++ b/gnucash/gnome/gtkbuilder/gnc-plugin-page-budget.glade
@@ -10,7 +10,6 @@
     <property name="page_increment">1</property>
   </object>
   <object class="GtkDialog" id="budget_estimate_dialog">
-    <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
     <property name="title" translatable="yes">Estimate Budget Values</property>
@@ -174,7 +173,6 @@
     <property name="page_increment">12</property>
   </object>
   <object class="GtkDialog" id="budget_options_container_dialog">
-    <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="title" translatable="yes">Budget Options</property>
     <property name="modal">True</property>
@@ -393,7 +391,6 @@
     </action-widgets>
   </object>
   <object class="GtkDialog" id="budget_list_dialog - Not Used">
-    <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="border_width">6</property>
     <property name="title" translatable="yes">Budget List</property>

commit 01ab889902ca81a4f795764aca80f0c027bf5fb6
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:15:43 2017 +0000

    Fix Invalid cast from GncWebkitHtml to GtkWindow
    
    Invalid cast from GncWebkitHtml to GtkWindow so use already set parent.

diff --git a/gnucash/html/gnc-html-webkit2.c b/gnucash/html/gnc-html-webkit2.c
index 0a8c377..b2ce26c 100644
--- a/gnucash/html/gnc-html-webkit2.c
+++ b/gnucash/html/gnc-html-webkit2.c
@@ -1123,7 +1123,7 @@ impl_webkit_print (GncHtml* self)
 
      priv = GNC_HTML_WEBKIT_GET_PRIVATE (self);
      op = webkit_print_operation_new (priv->web_view);
-     top = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)));
+     top = GTK_WINDOW(priv->base.parent);
      webkit_print_operation_run_dialog (op, top);
      g_object_unref (op);
 }

commit bde39c527cbdd4ebf0bb279ef488b121307a40b9
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:15:03 2017 +0000

    Fix Test for filepath is NULL when cancel pressed for export

diff --git a/gnucash/report/report-gnome/gnc-plugin-page-report.c b/gnucash/report/report-gnome/gnc-plugin-page-report.c
index a719e27..909e83f 100644
--- a/gnucash/report/report-gnome/gnc-plugin-page-report.c
+++ b/gnucash/report/report-gnome/gnc-plugin-page-report.c
@@ -1537,10 +1537,12 @@ gnc_get_export_filename (SCM choice)
     filepath = gnc_file_dialog (gnc_ui_get_main_window (NULL),
                                 title, NULL, default_dir, GNC_FILE_DIALOG_EXPORT);
 
-    /* Try to test for extension on file name, add if missing */
-    if (g_strrstr(filepath, ".") == NULL)
-        filepath = g_strconcat(filepath, ".", g_ascii_strdown(type, strlen(type)), NULL);
-
+    if (filepath != NULL) // test for cancel pressed
+    {
+        /* Try to test for extension on file name, add if missing */
+        if (g_strrstr(filepath, ".") == NULL)
+            filepath = g_strconcat(filepath, ".", g_ascii_strdown(type, strlen(type)), NULL);
+    }
     g_free (type);
     g_free (title);
     g_free (default_dir);

commit 19e0f58763497ba4c40de613ac7d21f706bbca2d
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:14:17 2017 +0000

    Fix Critical errors when SX editor loaded
    
    When the SX editor is loaded, an embedded register plug-in page is
    created and as part of this the business menus and actions are updated.
    As this is an GncEmbeddedWindow they fail so test for a normal main
    window before proceeding.

diff --git a/gnucash/gnome/gnc-plugin-business.c b/gnucash/gnome/gnc-plugin-business.c
index daad449..eb418b3 100644
--- a/gnucash/gnome/gnc-plugin-business.c
+++ b/gnucash/gnome/gnc-plugin-business.c
@@ -910,6 +910,10 @@ gnc_plugin_business_update_menus (GncPluginPage *plugin_page)
     if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
         return;
 
+    // Check that this is a main window and not embedded sx
+    if (!GNC_IS_MAIN_WINDOW(plugin_page->window))
+        return;
+
     is_txn_register = GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page);
     window = GNC_MAIN_WINDOW(plugin_page->window);
     g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
@@ -1055,6 +1059,10 @@ static void update_inactive_actions(GncPluginPage *plugin_page)
     if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
         return;
 
+    // Check that this is a main window and not embedded sx
+    if (!GNC_IS_MAIN_WINDOW(plugin_page->window))
+        return;
+
     window = GNC_MAIN_WINDOW(plugin_page->window);
     g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
     action_group = gnc_main_window_get_action_group(window, PLUGIN_ACTIONS_NAME);

commit b8a85ab80aa3c47de61bae00121391194f890a1f
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:13:22 2017 +0000

    Fix Transient parent warnings for SX editor

diff --git a/gnucash/gnome/dialog-sx-editor.c b/gnucash/gnome/dialog-sx-editor.c
index 61e246a..645bd16 100644
--- a/gnucash/gnome/dialog-sx-editor.c
+++ b/gnucash/gnome/dialog-sx-editor.c
@@ -1114,8 +1114,8 @@ sxed_delete_event( GtkWidget *widget, GdkEvent *event, gpointer ud )
  * Create the Schedule Editor Dialog *
  ************************************/
 GncSxEditorDialog *
-gnc_ui_scheduled_xaction_editor_dialog_create (SchedXaction *sx,
-					       gboolean newSX)
+gnc_ui_scheduled_xaction_editor_dialog_create (GtkWindow *parent, 
+    SchedXaction *sx, gboolean newSX)
 {
     GncSxEditorDialog *sxed;
     GtkBuilder *builder;
@@ -1192,12 +1192,13 @@ gnc_ui_scheduled_xaction_editor_dialog_create (SchedXaction *sx,
 
     // Set the style context for this dialog so it can be easily manipulated with css
     gnc_widget_set_style_context (GTK_WIDGET(sxed->dialog), "GncSxEditorDialog");
+    
+    gtk_window_set_transient_for (GTK_WINDOW (sxed->dialog), parent);
 
     /* Setup the end-date GNC widget */
     {
         GtkWidget *endDateBox = GTK_WIDGET(gtk_builder_get_object (builder, "editor_end_date_box"));
-        sxed->endDateEntry = GNC_DATE_EDIT(gnc_date_edit_new (gnc_time (NULL),
-							      FALSE, FALSE));
+        sxed->endDateEntry = GNC_DATE_EDIT(gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE));
         gtk_widget_show(GTK_WIDGET(sxed->endDateEntry));
         g_signal_connect( sxed->endDateEntry, "date-changed",
                           G_CALLBACK( sxed_excal_update_adapt_cb ), sxed );
@@ -1714,6 +1715,7 @@ typedef struct _acct_deletion_handler_data
 {
     GList *affected_sxes;
     GtkWidget *dialog;
+    GtkWindow *parent;
 } acct_deletion_handler_data;
 
 
@@ -1726,8 +1728,8 @@ _open_editors(GtkDialog *dialog, gint response_code, gpointer data)
         GList *sx_iter;
         for (sx_iter = adhd->affected_sxes; sx_iter; sx_iter = sx_iter->next)
         {
-            gnc_ui_scheduled_xaction_editor_dialog_create((SchedXaction*)sx_iter->data,
-                                                          FALSE);
+            gnc_ui_scheduled_xaction_editor_dialog_create(GTK_WINDOW(adhd->parent),
+                (SchedXaction*)sx_iter->data, FALSE);
         }
     }
     g_list_free(adhd->affected_sxes);
@@ -1759,6 +1761,7 @@ _sx_engine_event_handler(QofInstance *ent, QofEventId event_type, gpointer user_
         acct_deletion_handler_data *data;
         GtkBuilder *builder;
         GtkWidget *dialog;
+        GtkWindow *parent;
         GtkListStore *name_list;
         GtkTreeView *list;
         GtkTreeViewColumn *name_column;
@@ -1768,6 +1771,9 @@ _sx_engine_event_handler(QofInstance *ent, QofEventId event_type, gpointer user_
         gnc_builder_add_from_file (builder, "dialog-sx.glade", "account_deletion_dialog");
 
         dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_deletion_dialog"));
+        parent = gnc_ui_get_main_window (NULL);
+
+        gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
 
         list = GTK_TREE_VIEW(gtk_builder_get_object (builder, "sx_list"));
 
@@ -1776,6 +1782,7 @@ _sx_engine_event_handler(QofInstance *ent, QofEventId event_type, gpointer user_
 
         data = (acct_deletion_handler_data*)g_new0(acct_deletion_handler_data, 1);
         data->dialog = dialog;
+        data->parent = parent;
         data->affected_sxes = affected_sxes;
         name_list = gtk_list_store_new(1, G_TYPE_STRING);
         for (sx_iter = affected_sxes; sx_iter != NULL; sx_iter = sx_iter->next)
diff --git a/gnucash/gnome/dialog-sx-editor.h b/gnucash/gnome/dialog-sx-editor.h
index 7e6d203..c7d6e1e 100644
--- a/gnucash/gnome/dialog-sx-editor.h
+++ b/gnucash/gnome/dialog-sx-editor.h
@@ -24,6 +24,7 @@
 #define DIALOG_SX_EDITOR_H
 
 #include "SchedXaction.h"
+#include <gtk/gtk.h>
 
 #define DIALOG_SCHEDXACTION_CM_CLASS "dialog-scheduledtransactions"
 #define DIALOG_SCHEDXACTION_EDITOR_CM_CLASS "dialog-scheduledtransaction-editor"
@@ -36,8 +37,8 @@
 
 typedef struct _GncSxEditorDialog GncSxEditorDialog;
 
-GncSxEditorDialog* gnc_ui_scheduled_xaction_editor_dialog_create(SchedXaction *sx,
-        gboolean newSX);
+GncSxEditorDialog* gnc_ui_scheduled_xaction_editor_dialog_create(GtkWindow *parent,
+    SchedXaction *sx, gboolean newSX);
 
 void gnc_ui_scheduled_xaction_editor_dialog_destroy(GncSxEditorDialog *sxd);
 
diff --git a/gnucash/gnome/dialog-sx-editor2.c b/gnucash/gnome/dialog-sx-editor2.c
index 333da7f..6b280dd 100644
--- a/gnucash/gnome/dialog-sx-editor2.c
+++ b/gnucash/gnome/dialog-sx-editor2.c
@@ -1087,8 +1087,8 @@ sxed_delete_event (GtkWidget *widget, GdkEvent *event, gpointer ud)
  * Create the Schedule Editor Dialog *
  ************************************/
 GncSxEditorDialog2 *
-gnc_ui_scheduled_xaction_editor_dialog_create2 (SchedXaction *sx,
-					       gboolean newSX)
+gnc_ui_scheduled_xaction_editor_dialog_create2 (GtkWindow *parent, 
+    SchedXaction *sx, gboolean newSX)
 {
     GncSxEditorDialog2 *sxed;
     GtkBuilder *builder;
@@ -1166,11 +1166,12 @@ gnc_ui_scheduled_xaction_editor_dialog_create2 (SchedXaction *sx,
     // Set the style context for this dialog so it can be easily manipulated with css
     gnc_widget_set_style_context (GTK_WIDGET(sxed->dialog), "GncSxEditorDialog");
 
+    gtk_window_set_transient_for (GTK_WINDOW (sxed->dialog), parent);
+
     /* Setup the end-date GNC widget */
     {
         GtkWidget *endDateBox = GTK_WIDGET(gtk_builder_get_object (builder, "editor_end_date_box"));
-        sxed->endDateEntry = GNC_DATE_EDIT (gnc_date_edit_new (gnc_time (NULL),
-							      FALSE, FALSE));
+        sxed->endDateEntry = GNC_DATE_EDIT (gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE));
         gtk_widget_show (GTK_WIDGET (sxed->endDateEntry));
         g_signal_connect (sxed->endDateEntry, "date-changed",
                           G_CALLBACK (sxed_excal_update_adapt_cb), sxed);
@@ -1665,6 +1666,7 @@ typedef struct _acct_deletion_handler_data
 {
     GList *affected_sxes;
     GtkWidget *dialog;
+    GtkWindow *parent;
 } acct_deletion_handler_data;
 
 
@@ -1677,8 +1679,8 @@ _open_editors (GtkDialog *dialog, gint response_code, gpointer data)
         GList *sx_iter;
         for (sx_iter = adhd->affected_sxes; sx_iter; sx_iter = sx_iter->next)
         {
-            gnc_ui_scheduled_xaction_editor_dialog_create2 ((SchedXaction*)sx_iter->data,
-                    FALSE);
+            gnc_ui_scheduled_xaction_editor_dialog_create2 (GTK_WINDOW(adhd->parent),
+                (SchedXaction*)sx_iter->data, FALSE);
         }
     }
     g_list_free (adhd->affected_sxes);
@@ -1710,6 +1712,7 @@ _sx_engine_event_handler (QofInstance *ent, QofEventId event_type, gpointer user
         acct_deletion_handler_data *data;
         GtkBuilder *builder;
         GtkWidget *dialog;
+        GtkWindow *parent;
         GtkListStore *name_list;
         GtkTreeView *list;
         GtkTreeViewColumn *name_column;
@@ -1719,6 +1722,9 @@ _sx_engine_event_handler (QofInstance *ent, QofEventId event_type, gpointer user
         gnc_builder_add_from_file (builder, "dialog-sx.glade", "account_deletion_dialog");
 
         dialog = GTK_WIDGET (gtk_builder_get_object (builder, "account_deletion_dialog"));
+        parent = gnc_ui_get_main_window (NULL);
+        
+        gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
 
         list = GTK_TREE_VIEW (gtk_builder_get_object (builder, "sx_list"));
 
@@ -1727,6 +1733,7 @@ _sx_engine_event_handler (QofInstance *ent, QofEventId event_type, gpointer user
 
         data = (acct_deletion_handler_data*)g_new0 (acct_deletion_handler_data, 1);
         data->dialog = dialog;
+        data->parent = parent;
         data->affected_sxes = affected_sxes;
         name_list = gtk_list_store_new (1, G_TYPE_STRING);
         for (sx_iter = affected_sxes; sx_iter != NULL; sx_iter = sx_iter->next)
diff --git a/gnucash/gnome/dialog-sx-editor2.h b/gnucash/gnome/dialog-sx-editor2.h
index b8c7cdf..26ab52a 100644
--- a/gnucash/gnome/dialog-sx-editor2.h
+++ b/gnucash/gnome/dialog-sx-editor2.h
@@ -36,8 +36,8 @@
 
 typedef struct _GncSxEditorDialog2 GncSxEditorDialog2;
 
-GncSxEditorDialog2* gnc_ui_scheduled_xaction_editor_dialog_create2 (SchedXaction *sx,
-        gboolean newSX);
+GncSxEditorDialog2* gnc_ui_scheduled_xaction_editor_dialog_create2 (GtkWindow *parent,
+    SchedXaction *sx, gboolean newSX);
 
 void gnc_ui_scheduled_xaction_editor_dialog_destroy2 (GncSxEditorDialog2 *sxd);
 
diff --git a/gnucash/gnome/dialog-sx-from-trans.c b/gnucash/gnome/dialog-sx-from-trans.c
index f300b1e..c8625be 100644
--- a/gnucash/gnome/dialog-sx-from-trans.c
+++ b/gnucash/gnome/dialog-sx-from-trans.c
@@ -612,7 +612,8 @@ sxftd_advanced_clicked(SXFromTransInfo *sxfti)
     context = g_main_context_default();
     while (g_main_context_iteration(context, FALSE));
 
-    gnc_ui_scheduled_xaction_editor_dialog_create(sxfti->sx, TRUE /* newSX */);
+    gnc_ui_scheduled_xaction_editor_dialog_create(gnc_ui_get_main_window (sxfti->dialog),
+        sxfti->sx, TRUE /* newSX */);
     /* close ourself, since advanced editing entails us, and there are sync
      * issues otherwise. */
     sxftd_close(sxfti, FALSE);
@@ -752,7 +753,7 @@ sxftd_update_excal_adapt( GObject *o, gpointer ud )
  * Create the dialog *
  ********************/
 void
-gnc_sx_create_from_trans( Transaction *trans )
+gnc_sx_create_from_trans( GtkWindow *parent, Transaction *trans )
 {
 #ifndef __MINGW32__
     int errno;
@@ -771,6 +772,8 @@ gnc_sx_create_from_trans( Transaction *trans )
     // Set the style context for this dialog so it can be easily manipulated with css
     gnc_widget_set_style_context (GTK_WIDGET(dialog), "GncSxFromTransDialog");
 
+    gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
     sxfti->builder = builder;
     sxfti->dialog = dialog;
     sxfti->trans = trans;
diff --git a/gnucash/gnome/dialog-sx-from-trans.h b/gnucash/gnome/dialog-sx-from-trans.h
index 413aef2..aa66212 100644
--- a/gnucash/gnome/dialog-sx-from-trans.h
+++ b/gnucash/gnome/dialog-sx-from-trans.h
@@ -27,6 +27,6 @@
 
 #include "Transaction.h"
 
-void gnc_sx_create_from_trans(Transaction *trans);
+void gnc_sx_create_from_trans(GtkWindow *parent, Transaction *trans);
 
 #endif
diff --git a/gnucash/gnome/gnc-plugin-page-register.c b/gnucash/gnome/gnc-plugin-page-register.c
index 057db7e..0ef9fe9 100644
--- a/gnucash/gnome/gnc-plugin-page-register.c
+++ b/gnucash/gnome/gnc-plugin-page-register.c
@@ -3799,13 +3799,15 @@ gnc_plugin_page_register_cmd_schedule (GtkAction *action,
                                        GncPluginPageRegister *plugin_page)
 {
     GncPluginPageRegisterPrivate *priv;
+    GtkWindow *window;
 
     ENTER("(action %p, plugin_page %p)", action, plugin_page);
 
     g_return_if_fail(GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page));
 
+    window = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (plugin_page)));
     priv = GNC_PLUGIN_PAGE_REGISTER_GET_PRIVATE(plugin_page);
-    gsr_default_schedule_handler(priv->gsr, NULL);
+    gsr_default_schedule_handler(priv->gsr, window);
     LEAVE(" ");
 }
 
diff --git a/gnucash/gnome/gnc-plugin-page-register2.c b/gnucash/gnome/gnc-plugin-page-register2.c
index bd10fa9..58d9d36 100644
--- a/gnucash/gnome/gnc-plugin-page-register2.c
+++ b/gnucash/gnome/gnc-plugin-page-register2.c
@@ -3622,6 +3622,7 @@ gnc_plugin_page_register2_cmd_schedule (GtkAction *action,
     GncPluginPageRegister2Private *priv;
     GncTreeViewSplitReg *view;
     Transaction *trans;
+    GtkWindow *window;
 
     ENTER("(action %p, plugin_page %p)", action, plugin_page);
 
@@ -3629,6 +3630,7 @@ gnc_plugin_page_register2_cmd_schedule (GtkAction *action,
 
     priv = GNC_PLUGIN_PAGE_REGISTER2_GET_PRIVATE(plugin_page);
     view = gnc_ledger_display2_get_split_view_register (priv->ledger);
+    window = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (plugin_page)));
 
     trans = gnc_tree_view_split_reg_get_current_trans (view);
 
@@ -3682,12 +3684,12 @@ gnc_plugin_page_register2_cmd_schedule (GtkAction *action,
 
 	if (theSX)
 	{
-	    gnc_ui_scheduled_xaction_editor_dialog_create2 (theSX, FALSE);
+	    gnc_ui_scheduled_xaction_editor_dialog_create2 (window, theSX, FALSE);
 	    LEAVE(" ");
 	    return;
 	}
     }
-    gnc_sx_create_from_trans (trans);
+    gnc_sx_create_from_trans (window, trans);
     LEAVE(" ");
 }
 
diff --git a/gnucash/gnome/gnc-plugin-page-sx-list.c b/gnucash/gnome/gnc-plugin-page-sx-list.c
index 6b66868..cc0bf04 100644
--- a/gnucash/gnome/gnc-plugin-page-sx-list.c
+++ b/gnucash/gnome/gnc-plugin-page-sx-list.c
@@ -639,6 +639,7 @@ gnc_plugin_page_sx_list_recreate_page (GtkWidget *window,
 static void
 gnc_plugin_page_sx_list_cmd_new(GtkAction *action, GncPluginPageSxList *page)
 {
+    GtkWindow *window = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
     SchedXaction *new_sx;
     gboolean new_sx_flag = TRUE;
 
@@ -655,7 +656,7 @@ gnc_plugin_page_sx_list_cmd_new(GtkAction *action, GncPluginPageSxList *page)
         schedule = g_list_append(schedule, r);
         gnc_sx_set_schedule(new_sx, schedule);
     }
-    gnc_ui_scheduled_xaction_editor_dialog_create(new_sx, new_sx_flag);
+    gnc_ui_scheduled_xaction_editor_dialog_create(window, new_sx, new_sx_flag);
 }
 
 #ifdef REGISTER2_ENABLED
@@ -663,6 +664,7 @@ gnc_plugin_page_sx_list_cmd_new(GtkAction *action, GncPluginPageSxList *page)
 static void
 gnc_plugin_page_sx_list_cmd_new2 (GtkAction *action, GncPluginPageSxList *page)
 {
+    GtkWindow *window = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
     SchedXaction *new_sx;
     gboolean new_sx_flag = TRUE;
 
@@ -679,7 +681,7 @@ gnc_plugin_page_sx_list_cmd_new2 (GtkAction *action, GncPluginPageSxList *page)
         schedule = g_list_append (schedule, r);
         gnc_sx_set_schedule (new_sx, schedule);
     }
-    gnc_ui_scheduled_xaction_editor_dialog_create2 (new_sx, new_sx_flag);
+    gnc_ui_scheduled_xaction_editor_dialog_create2 (window, new_sx, new_sx_flag);
 }
 /*################## Added for Reg2 #################*/
 #endif
@@ -687,7 +689,8 @@ gnc_plugin_page_sx_list_cmd_new2 (GtkAction *action, GncPluginPageSxList *page)
 static void
 _edit_sx(gpointer data, gpointer user_data)
 {
-    gnc_ui_scheduled_xaction_editor_dialog_create((SchedXaction*)data, FALSE);
+    gnc_ui_scheduled_xaction_editor_dialog_create(GTK_WINDOW(user_data),
+        (SchedXaction*)data, FALSE);
 }
 
 #ifdef REGISTER2_ENABLED
@@ -695,7 +698,8 @@ _edit_sx(gpointer data, gpointer user_data)
 static void
 _edit_sx2 (gpointer data, gpointer user_data)
 {
-    gnc_ui_scheduled_xaction_editor_dialog_create2 ((SchedXaction*)data, FALSE);
+    gnc_ui_scheduled_xaction_editor_dialog_create2 (GTK_WINDOW(user_data),
+        (SchedXaction*)data, FALSE);
 }
 /*################## Added for Reg2 #################*/
 #endif
@@ -711,6 +715,7 @@ static void
 gnc_plugin_page_sx_list_cmd_edit(GtkAction *action, GncPluginPageSxList *page)
 {
     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
+    GtkWindow *window = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
     GtkTreeSelection *selection;
     GList *selected_paths, *to_edit;
     GtkTreeModel *model;
@@ -726,7 +731,7 @@ gnc_plugin_page_sx_list_cmd_edit(GtkAction *action, GncPluginPageSxList *page)
     to_edit = gnc_g_list_map(selected_paths,
                              (GncGMapFunc)_argument_reorder_fn,
                              priv->tree_view);
-    g_list_foreach(to_edit, (GFunc)_edit_sx, NULL);
+    g_list_foreach(to_edit, (GFunc)_edit_sx, window);
     g_list_free(to_edit);
     g_list_foreach(selected_paths, (GFunc)gtk_tree_path_free, NULL);
     g_list_free(selected_paths);
@@ -738,6 +743,7 @@ static void
 gnc_plugin_page_sx_list_cmd_edit2 (GtkAction *action, GncPluginPageSxList *page)
 {
     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE (page);
+    GtkWindow *window = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
     GtkTreeSelection *selection;
     GList *selected_paths, *to_edit;
     GtkTreeModel *model;
@@ -753,7 +759,7 @@ gnc_plugin_page_sx_list_cmd_edit2 (GtkAction *action, GncPluginPageSxList *page)
     to_edit = gnc_g_list_map (selected_paths,
                              (GncGMapFunc)_argument_reorder_fn,
                              priv->tree_view);
-    g_list_foreach(to_edit, (GFunc)_edit_sx2, NULL);
+    g_list_foreach(to_edit, (GFunc)_edit_sx2, window);
     g_list_free (to_edit);
     g_list_foreach (selected_paths, (GFunc)gtk_tree_path_free, NULL);
     g_list_free (selected_paths);
@@ -769,9 +775,10 @@ gppsl_row_activated_cb(GtkTreeView *tree_view,
 {
     GncPluginPageSxList *page = GNC_PLUGIN_PAGE_SX_LIST(user_data);
     GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
+    GtkWindow *window = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
 
     SchedXaction *sx = gnc_tree_view_sx_list_get_sx_from_path(GNC_TREE_VIEW_SX_LIST(priv->tree_view), path);
-    gnc_ui_scheduled_xaction_editor_dialog_create(sx, FALSE);
+    gnc_ui_scheduled_xaction_editor_dialog_create(window, sx, FALSE);
 }
 
 
diff --git a/gnucash/gnome/gnc-split-reg.c b/gnucash/gnome/gnc-split-reg.c
index 13cdc97..1cb02f2 100644
--- a/gnucash/gnome/gnc-split-reg.c
+++ b/gnucash/gnome/gnc-split-reg.c
@@ -1363,10 +1363,10 @@ gsr_default_schedule_handler( GNCSplitReg *gsr, gpointer data )
 
     if ( theSX )
     {
-        gnc_ui_scheduled_xaction_editor_dialog_create(theSX, FALSE);
+        gnc_ui_scheduled_xaction_editor_dialog_create(GTK_WINDOW(data), theSX, FALSE);
         return;
     }
-    gnc_sx_create_from_trans(pending_trans);
+    gnc_sx_create_from_trans(GTK_WINDOW(data), pending_trans);
 }
 
 void
diff --git a/gnucash/gnome/gtkbuilder/dialog-sx.glade b/gnucash/gnome/gtkbuilder/dialog-sx.glade
index f09c8f3..a08f943 100644
--- a/gnucash/gnome/gtkbuilder/dialog-sx.glade
+++ b/gnucash/gnome/gtkbuilder/dialog-sx.glade
@@ -3,7 +3,6 @@
 <interface>
   <requires lib="gtk+" version="3.10"/>
   <object class="GtkDialog" id="account_deletion_dialog">
-    <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="border_width">6</property>
     <property name="title" translatable="yes">Account Deletion</property>

commit dc1ec68646e231fddf2e72534ad848a18f5a312f
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:03:47 2017 +0000

    Add a left margin to report zoom to align with other options

diff --git a/gnucash/gnome-utils/gtkbuilder/dialog-preferences.glade b/gnucash/gnome-utils/gtkbuilder/dialog-preferences.glade
index 4aff574..51ee5b7 100644
--- a/gnucash/gnome-utils/gtkbuilder/dialog-preferences.glade
+++ b/gnucash/gnome-utils/gtkbuilder/dialog-preferences.glade
@@ -2837,6 +2837,7 @@ many months before the current month:</property>
                         <property name="can_focus">True</property>
                         <property name="tooltip_text" translatable="yes">On high resolution screens reports tend to be hard to read. This option allows you to scale reports up by the set factor. For example setting this to 2.0 will display reports at twice their typical size.</property>
                         <property name="halign">start</property>
+                        <property name="margin_left">12</property>
                         <property name="adjustment">default_zoom_adj</property>
                       </object>
                       <packing>

commit 9f3a357a2a7ea4813c406364182c1171d9ad21c3
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sat Dec 30 17:03:12 2017 +0000

    Fix register cell height by adding 1px for cell border

diff --git a/gnucash/register/register-gnome/gnucash-style.c b/gnucash/register/register-gnome/gnucash-style.c
index e999620..f62d2ee 100644
--- a/gnucash/register/register-gnome/gnucash-style.c
+++ b/gnucash/register/register-gnome/gnucash-style.c
@@ -209,8 +209,8 @@ set_dimensions_pass_one (GnucashSheet *sheet, CellBlock *cursor,
                 cd->pixel_height = gnc_item_edit_get_margin (item_edit, top_bottom) +
                                    gnc_item_edit_get_padding_border (item_edit, top_bottom);
             }
-
-            max_height = MAX(max_height, cd->pixel_height);
+            // add 1 to cd->pixel_height to allow for a cell border
+            max_height = MAX(max_height, cd->pixel_height + 1);
 
             if (cd->pixel_width > 0)
                 continue;

commit d6bb34efe799189649e6d646eb1f0beeab4f4404
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sat Dec 30 14:46:08 2017 +0100

    A few translatable string changes in the warnings for editing reconciled splits

diff --git a/gnucash/register/ledger-core/split-register-model.c b/gnucash/register/ledger-core/split-register-model.c
index 7df1c74..f011f13 100644
--- a/gnucash/register/ledger-core/split-register-model.c
+++ b/gnucash/register/ledger-core/split-register-model.c
@@ -2059,11 +2059,11 @@ gnc_split_register_confirm (VirtualLocation virt_loc, gpointer user_data)
                 g_free (name);
             }
         }
-        title = _("Change Transaction containing a reconciled split?");
+        title = _("Change transaction containing a reconciled split?");
         message_format =
-         _("You are about to change a protected transaction field as it contains reconciled splits in the following accounts... \n%s"
-           "\n\nAfter transaction editing is completed, all reconciled splits will be unreconcile and "
-          "this might make future reconciliation difficult! Continue with this change?");
+         _("The transaction you are about to change is protected because it contains reconciled splits in the following accounts:\n%s"
+           "\n\nIf you continue editing this transaction all reconciled splits will be unreconciled. "
+          "This might make future reconciliation difficult! Continue with this change?");
 
         message = g_strdup_printf (message_format, acc_list);
         g_free (acc_list);
@@ -2074,8 +2074,8 @@ gnc_split_register_confirm (VirtualLocation virt_loc, gpointer user_data)
         title = _("Change reconciled split?");
         message =
          _("You are about to change a protected field of a reconciled split. "
-           "After transaction editing is completed, this split will be unreconciled "
-           "and this might make future reconciliation difficult! Continue with this change?");
+           "If you continue editing this split it will be unreconciled. "
+           "This might make future reconciliation difficult! Continue with this change?");
     }
 
     if ((recn == YREC && protected_split_cell) || protected_trans_cell)

commit f25c065b203ebe3b51ab975a8e748bd5047b84a4
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Wed Nov 29 12:24:53 2017 +0000

    Bug 771667 - Change reconciled splits warning
    
    This patch displays two distinct warnings when changing protected
    fields of a transaction that contains reconciled splits. If the fields
    date, num and description are changed, then the warning list the
    accounts that have reconciled splits and also advises that they will be
    unreconciled after editing the transaction. If the fields account,
    transfer, debit or credit are changed then the warning advises that the
    split will be unreconciled after editing the transaction.
    
    There is still just one warning preference as it is all to do with
    fields protected by reconciliation.

diff --git a/gnucash/register/ledger-core/split-register-control.c b/gnucash/register/ledger-core/split-register-control.c
index b66c073..c300e5b 100644
--- a/gnucash/register/ledger-core/split-register-control.c
+++ b/gnucash/register/ledger-core/split-register-control.c
@@ -512,6 +512,10 @@ gnc_split_register_move_cursor (VirtualLocation *p_new_virt_loc,
         info->cursor_hint_cursor_class = new_class;
     }
 
+    /* change from split row to trans row */
+    if (old_class != new_class)
+        info->change_confirmed = FALSE;
+
     if (old_split != new_split)
     {
         info->change_confirmed = FALSE;
@@ -1569,6 +1573,13 @@ transaction_changed_confirm(VirtualLocation *p_new_virt_loc,
         Split *trans_split;
         CursorClass new_class;
 
+        /* Clear unreconcile split list */
+        if (reg->unrecn_splits != NULL)
+        {
+            g_list_free (reg->unrecn_splits);
+            reg->unrecn_splits =  NULL;
+        }
+
         new_split = gnc_split_register_get_split (reg, virt_loc->vcell_loc);
         trans_split = gnc_split_register_get_trans_split (reg,
                       virt_loc->vcell_loc,
diff --git a/gnucash/register/ledger-core/split-register-model.c b/gnucash/register/ledger-core/split-register-model.c
index f8b6614..7df1c74 100644
--- a/gnucash/register/ledger-core/split-register-model.c
+++ b/gnucash/register/ledger-core/split-register-model.c
@@ -1993,7 +1993,9 @@ gnc_split_register_confirm (VirtualLocation virt_loc, gpointer user_data)
     Split *split;
     char recn;
     const char *cell_name;
-    gboolean change_ok;
+    gboolean protected_split_cell, protected_trans_cell;
+    const gchar *title = NULL;
+    const gchar *message = NULL;
 
     /* This assumes we reset the flag whenever we change splits.
      * This happens in gnc_split_register_move_cursor(). */
@@ -2007,6 +2009,10 @@ gnc_split_register_confirm (VirtualLocation virt_loc, gpointer user_data)
     trans = xaccSplitGetParent (split);
     if (xaccTransWarnReadOnly(trans))
         return FALSE;
+
+    if (!xaccTransHasReconciledSplits (trans))
+        return TRUE;
+
     if (gnc_table_layout_get_cell_changed (reg->table->layout, RECN_CELL, FALSE))
         recn = gnc_recn_cell_get_flag
                ((RecnCell *) gnc_table_layout_get_cell (reg->table->layout, RECN_CELL));
@@ -2016,30 +2022,66 @@ gnc_split_register_confirm (VirtualLocation virt_loc, gpointer user_data)
     /* What Cell are we in */
     cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
 
-    /* These cells can be changed */
-    change_ok = (g_strcmp0(cell_name, "notes") == 0) || (g_strcmp0(cell_name, "memo") == 0) || (g_strcmp0(cell_name, "action") == 0);
+    /* if we change a transfer cell, we want the other split */
+    if (g_strcmp0(cell_name, "transfer") == 0)
+        recn = xaccSplitGetReconcile (xaccSplitGetOtherSplit (split));
+
+    /* These cells can not be changed */
+    protected_split_cell = (g_strcmp0(cell_name, "account") == 0) || (g_strcmp0(cell_name, "transfer") == 0) || (g_strcmp0(cell_name, "debit") == 0) || (g_strcmp0(cell_name, "credit") == 0);
+    protected_trans_cell = (g_strcmp0(cell_name, "date") == 0) || (g_strcmp0(cell_name, "num") == 0) || (g_strcmp0(cell_name, "description") == 0);
+
+    PINFO ("Protected transaction cell %d, Protected split cell %d, Cell is %s", protected_trans_cell, protected_split_cell, cell_name);
 
-    if ((recn == YREC || xaccTransHasReconciledSplits (trans)) && !change_ok)
+    if (protected_trans_cell)
     {
-        GtkWidget *dialog, *window;
-        gint response;
-        const gchar *title;
-        const gchar *message;
+        GList *node;
+        gchar *acc_list = NULL;
+        gchar *message_format;
 
-        if(recn == YREC)
-        {
-            title = _("Change reconciled split?");
-            message =
-             _("You are about to change a reconciled split. Doing so might make "
-               "future reconciliation difficult! Continue with this change?");
-        }
-        else
+        for (node = xaccTransGetSplitList (trans); node; node = node->next)
         {
-            title = _("Change split linked to a reconciled split?");
-            message =
-            _("You are about to change a split that is linked to a reconciled split. "
-              "Doing so might make future reconciliation difficult! Continue with this change?");
+            Split *split = node->data;
+
+            if (xaccSplitGetReconcile (split) == YREC)
+            {
+                Account *acc = xaccSplitGetAccount (split);
+                gchar *name = gnc_account_get_full_name (acc);
+
+                if (acc_list == NULL)
+                    acc_list = g_strconcat ("\n", name, NULL);
+                else
+                {
+                    gchar *acc_list_copy = g_strdup(acc_list);
+                    g_free (acc_list);
+                    acc_list = g_strconcat (acc_list_copy, "\n", name, NULL);
+                    g_free (acc_list_copy);
+                }
+                g_free (name);
+            }
         }
+        title = _("Change Transaction containing a reconciled split?");
+        message_format =
+         _("You are about to change a protected transaction field as it contains reconciled splits in the following accounts... \n%s"
+           "\n\nAfter transaction editing is completed, all reconciled splits will be unreconcile and "
+          "this might make future reconciliation difficult! Continue with this change?");
+
+        message = g_strdup_printf (message_format, acc_list);
+        g_free (acc_list);
+    }
+
+    if (protected_split_cell)
+    {
+        title = _("Change reconciled split?");
+        message =
+         _("You are about to change a protected field of a reconciled split. "
+           "After transaction editing is completed, this split will be unreconciled "
+           "and this might make future reconciliation difficult! Continue with this change?");
+    }
+
+    if ((recn == YREC && protected_split_cell) || protected_trans_cell)
+    {
+        GtkWidget *dialog, *window;
+        gint response;
 
         /* Does the user want to be warned? */
         window = gnc_split_register_get_parent(reg);
@@ -2051,16 +2093,36 @@ gnc_split_register_confirm (VirtualLocation virt_loc, gpointer user_data)
                                    "%s", title);
         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
                 "%s", message);
-        gtk_dialog_add_button(GTK_DIALOG(dialog), _("Chan_ge Split"),
+
+        if (protected_split_cell)
+            gtk_dialog_add_button(GTK_DIALOG(dialog), _("Chan_ge Split"),
+                              GTK_RESPONSE_YES);
+        else
+            gtk_dialog_add_button(GTK_DIALOG(dialog), _("Chan_ge Transaction"),
                               GTK_RESPONSE_YES);
         response = gnc_dialog_run(GTK_DIALOG(dialog), GNC_PREF_WARN_REG_RECD_SPLIT_MOD);
         gtk_widget_destroy(dialog);
         if (response != GTK_RESPONSE_YES)
             return FALSE;
 
+        // Response is Change, so record the splits
+        if (recn == YREC && protected_split_cell)
+        {
+            if (g_list_index (reg->unrecn_splits, split) == -1)
+                reg->unrecn_splits = g_list_append (reg->unrecn_splits, split);
+        }
+
+        if (protected_trans_cell)
+        {
+            if (reg->unrecn_splits != NULL)
+                g_list_free (reg->unrecn_splits);
+
+            reg->unrecn_splits = g_list_copy (xaccTransGetSplitList (trans));
+        }
+
+        PINFO ("Unreconcile split list length is %d", g_list_length(reg->unrecn_splits));
         info->change_confirmed = TRUE;
     }
-
     return TRUE;
 }
 
diff --git a/gnucash/register/ledger-core/split-register.c b/gnucash/register/ledger-core/split-register.c
index 672760f..2e95e08 100644
--- a/gnucash/register/ledger-core/split-register.c
+++ b/gnucash/register/ledger-core/split-register.c
@@ -1814,6 +1814,25 @@ gnc_split_register_save (SplitRegister *reg, gboolean do_commit)
         xaccTransCommitEdit (trans);
     }
 
+    /* If there are splits in the unreconcile list and we are committing
+     * we need to unreconcile them */
+    if (do_commit && (reg->unrecn_splits != NULL))
+    {
+        GList *node;
+
+        PINFO ("Unreconcile %d splits of reconciled transaction", g_list_length (reg->unrecn_splits));
+
+        for (node = reg->unrecn_splits; node; node = node->next)
+        {
+            Split *split = node->data;
+
+            if (xaccSplitGetReconcile (split) == YREC)
+                xaccSplitSetReconcile (split, NREC);
+        }
+        g_list_free (reg->unrecn_splits);
+        reg->unrecn_splits = NULL;
+    }
+
     gnc_table_clear_current_cursor_changes (reg->table);
 
     gnc_resume_gui_refresh ();
@@ -2715,6 +2734,8 @@ gnc_split_register_init (SplitRegister *reg,
 
     reg->sr_info = NULL;
 
+    reg->unrecn_splits = NULL;
+
     reg->type = type;
     reg->style = style;
     reg->use_double_line = use_double_line;
@@ -2855,6 +2876,12 @@ gnc_split_register_destroy_info (SplitRegister *reg)
     if (reg == NULL)
         return;
 
+    if (reg->unrecn_splits != NULL)
+    {
+        g_list_free (reg->unrecn_splits);
+        reg->unrecn_splits =  NULL;
+    }
+
     info = reg->sr_info;
     if (!info)
         return;
diff --git a/gnucash/register/ledger-core/split-register.h b/gnucash/register/ledger-core/split-register.h
index c9aae12..f5cd7d6 100644
--- a/gnucash/register/ledger-core/split-register.h
+++ b/gnucash/register/ledger-core/split-register.h
@@ -252,6 +252,8 @@ struct split_register
     gboolean is_template;
     gboolean do_auto_complete; /**< whether to use auto-completion */
 
+    SplitList *unrecn_splits; /**< list of splits to unreconcile after transaction edit */
+
     SRInfo * sr_info;   /**< private data; outsiders should not access this */
 };
 

commit 30f7f2fcf782564fdc447e1bdea66d019e2a9d39
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Dec 30 09:08:31 2017 +1100

    ENH: display infobox when no accounts matched

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 2e78ad0..930fd71 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1811,7 +1811,7 @@ tags within description, notes or memo. ")
                 (gnc:html-markup-h2 NO-MATCHING-ACCT-HEADER)
                 (gnc:html-markup-p NO-MATCHING-ACCT-TEXT)))
 
-              (if (member 'nomatch infobox-display)
+              (if (member 'no-match infobox-display)
                   (gnc:html-document-add-object!
                    document
                    (infobox)))))

commit 7951d4259e45b2632f26ab03dbb7d9603ec2a3c2
Merge: 47a4220 965685c
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Dec 29 14:32:21 2017 -0800

    Bug 616709 - Pressing delete key while editing account name offers...
    
    to delete account.


commit 47a422070e9bfa2d15e2766510be0e9851554fb1
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Dec 29 14:23:27 2017 -0800

    Fix crash in customer/employee/vendor reports.

diff --git a/gnucash/report/business-reports/owner-report.scm b/gnucash/report/business-reports/owner-report.scm
index a9ea698..3dd6563 100644
--- a/gnucash/report/business-reports/owner-report.scm
+++ b/gnucash/report/business-reports/owner-report.scm
@@ -757,13 +757,12 @@
                (gnc:date-option-absolute-time
                (opt-val gnc:pagename-general optname-to-date))))
      (book (gnc-account-get-book account))
-     (date-format (gnc:options-fancy-date book))
+     (date-format (if (not (null? book)) (gnc:options-fancy-date book)))
      (type (opt-val "__reg" "owner-type"))
      (owner-descr (owner-string type))
      (date-type (opt-val gnc:pagename-general optname-date-driver))
      (owner (opt-val owner-page owner-descr))
      (report-title (string-append (doctype-str type) " " (_ "Report"))))
-
     (if (not (gncOwnerIsValid owner))
      (gnc:html-document-add-object!
       document

commit 3c65a300173ab3ac938c081cdd332ee46db00cfd
Merge: fd9474b bd9af4a
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Dec 29 14:22:32 2017 -0800

    Merge Bob Fewell's 'gtk3-update11' into unstable.


commit fd9474b55fb4df4607d71cface8cd00f2d7a649e
Merge: bab266c a29c2db
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Dec 29 22:47:44 2017 +0100

    Merge branch 'unstable-TR-plus' of https://github.com/christopherlam/gnucash into unstable


commit a29c2db4686cbf7159eeae5941aa4b82c467f310
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Dec 30 07:47:57 2017 +1100

    COSMETIC: amend strings in options
    
    Use more precise and concise strings in Filter options.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index f6654df..2e78ad0 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -92,10 +92,10 @@
 
 ;;Filtering
 (define pagename-filter (N_ "Filter"))
-(define optname-account-matcher (N_ "Account Matcher"))
-(define optname-account-matcher-regex (N_ "Account Matcher uses regular expressions for extended matching"))
-(define optname-transaction-matcher (N_ "Transaction Matcher"))
-(define optname-transaction-matcher-regex (N_ "Transaction Matcher uses regular expressions for extended matching"))
+(define optname-account-matcher (N_ "Account Name Filter"))
+(define optname-account-matcher-regex (N_ "Use regular expressions for account name filter"))
+(define optname-transaction-matcher (N_ "Transaction Filter"))
+(define optname-transaction-matcher-regex (N_ "Use regular expressions for transaction filter"))
 (define optname-reconcile-status (N_ "Reconcile Status"))
 (define optname-void-transactions (N_ "Void Transactions"))
 
@@ -474,16 +474,16 @@ Credit Card, and Income accounts."))
   (gnc:register-trep-option
    (gnc:make-string-option
     pagename-filter optname-account-matcher
-    "a5" (_ "Match only accounts whose fullname is matched e.g. ':Travel' will match \
+    "a5" (_ "Show only accounts whose full name matches this filter e.g. ':Travel' will match \
 Expenses:Travel:Holiday and Expenses:Business:Travel. It can be left blank, which will \
-disable the matcher.")
+disable the filter.")
     ""))
 
   (gnc:register-trep-option
    (gnc:make-simple-boolean-option
     pagename-filter optname-account-matcher-regex
     "a6"
-    (_ "By default the account matcher will search substring only. Set this to true to \
+    (_ "By default the account filter will search substring only. Set this to true to \
 enable full POSIX regular expressions capabilities. 'Car|Flights' will match both \
 Expenses:Car and Expenses:Flights. Use a period (.) to match a single character e.g. \
 '20../.' will match 'Travel 2017/1 London'. ")
@@ -492,16 +492,16 @@ Expenses:Car and Expenses:Flights. Use a period (.) to match a single character
   (gnc:register-trep-option
    (gnc:make-string-option
     pagename-filter optname-transaction-matcher
-    "i1" (_ "Match only transactions whose substring is matched e.g. '#gift' \
-will find all transactions with #gift in description, notes or memo. It can be left \
-blank, which will disable the matcher.")
+    "i1" (_ "Show only transactions where description, notes, or memo matches this filter.
+e.g. '#gift' will find all transactions with #gift in description, notes or memo. It can be left \
+blank, which will disable the filter.")
     ""))
 
   (gnc:register-trep-option
    (gnc:make-simple-boolean-option
     pagename-filter optname-transaction-matcher-regex
     "i2"
-    (_ "By default the transaction matcher will search substring only. Set this to true to \
+    (_ "By default the transaction filter will search substring only. Set this to true to \
 enable full POSIX regular expressions capabilities. '#work|#family' will match both \
 tags within description, notes or memo. ")
     #f))

commit bab266c307936358e8f0e782a1abe4cf61953b15
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Dec 29 16:49:30 2017 +0100

    Update POTEFILES.in

diff --git a/po/POTFILES.in b/po/POTFILES.in
index b304d9f..5226d5f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -294,8 +294,10 @@ gnucash/import-export/csv-imp/assistant-csv-trans-import.glade
 gnucash/import-export/csv-imp/csv-account-import.c
 gnucash/import-export/csv-imp/gnc-csv-account-map.c
 gnucash/import-export/csv-imp/gnc-csv-gnumeric-popup.c
-gnucash/import-export/csv-imp/gnc-csv-tokenizer.cpp
 gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
+gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp
+gnucash/import-export/csv-imp/gnc-csv-tokenizer.cpp
+gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp
 gnucash/import-export/csv-imp/gnc-dummy-tokenizer.cpp
 gnucash/import-export/csv-imp/gnc-fw-tokenizer.cpp
 gnucash/import-export/csv-imp/gncmod-csv-import.c

commit 6774f1223f346564caf371ef3809dc4198b39239
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Dec 29 16:49:11 2017 +0100

    Fix a few compile warnings-turned-errors

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 0911233..47a6f2b 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -335,12 +335,12 @@ static void csv_price_imp_preview_commodity_sel_cb (GtkComboBox* commodity_selec
     info->preview_update_commodity();
 }
 
-void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info)
+static void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info)
 {
     info->preview_update_col_type (cbox);
 }
 
-gboolean
+static gboolean
 csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton* event,
                                         CsvImpPriceAssist* info)
 {
@@ -1117,7 +1117,7 @@ CsvImpPriceAssist::preview_update_commodity ()
     preview_refresh_table ();
 }
 
-gboolean
+static gboolean
 csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist)
 {
     assist->preview_refresh_table ();
@@ -1518,7 +1518,7 @@ CsvImpPriceAssist::preview_style_column (uint32_t col_num, GtkTreeModel* model)
 
 /* Helper to create a shared store for the header comboboxes in the preview treeview.
  * It holds the possible column types */
-GtkTreeModel*
+static GtkTreeModel*
 make_column_header_model_price (void)
 {
     auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);

commit 3f44552e3649ec28559f2a7f7126992ee083d249
Merge: 26b82b5 5aa84e1
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Dec 29 16:29:46 2017 +0100

    Merge branch 'prices-in' of https://github.com/Bob-IT/gnucash into unstable


commit 965685cc7f24975b41a476f3abeb87af074f3860
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 12:29:57 2017 +0000

    Bug 616709 - Stop the delete button on the Account page
    
    When editing editable text fields on the Account page, if you press the
    delete key a 'delete account popup' would pop so these changes prevent
    that by disabling / enabling the delete key binding.

diff --git a/gnucash/gnome-utils/gnc-tree-view-account.c b/gnucash/gnome-utils/gnc-tree-view-account.c
index 75aeee3..c1cac82 100644
--- a/gnucash/gnome-utils/gnc-tree-view-account.c
+++ b/gnucash/gnome-utils/gnc-tree-view-account.c
@@ -659,7 +659,7 @@ gnc_tree_view_account_color_update (gpointer gsettings, gchar *key, gpointer use
         priv->show_account_color = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, key);
 }
 
-/** Add the account color background data function to the GncTreeViewAccount column to 
+/** Add the account color background data function to the GncTreeViewAccount column to
  *  show or not the column background in the account color.
  */
 void
@@ -1521,7 +1521,7 @@ gnc_tree_view_account_set_selected_accounts (GncTreeViewAccount *view,
              */
             continue;
         }
-        
+
         path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
         if (path == NULL)
         {
@@ -2671,3 +2671,15 @@ static gboolean gnc_tree_view_search_compare (GtkTreeModel *model, gint column,
     // inverted return (FALSE means a match)
     return !match;
 }
+
+void gnc_tree_view_account_set_editing_started_cb(GncTreeViewAccount *view,
+    GFunc editing_started_cb, gpointer editing_cb_data)
+{
+    gnc_tree_view_set_editing_started_cb (GNC_TREE_VIEW(view), editing_started_cb, editing_cb_data);
+}
+
+void gnc_tree_view_account_set_editing_finished_cb(GncTreeViewAccount *view,
+    GFunc editing_finished_cb, gpointer editing_cb_data)
+{
+    gnc_tree_view_set_editing_finished_cb (GNC_TREE_VIEW(view), editing_finished_cb, editing_cb_data);
+}
diff --git a/gnucash/gnome-utils/gnc-tree-view-account.h b/gnucash/gnome-utils/gnc-tree-view-account.h
index b6381f2..072fd96 100644
--- a/gnucash/gnome-utils/gnc-tree-view-account.h
+++ b/gnucash/gnome-utils/gnc-tree-view-account.h
@@ -471,11 +471,24 @@ void gnc_tree_view_account_select_subaccounts (GncTreeViewAccount *view,
  */
 void gnc_tree_view_account_expand_to_account (GncTreeViewAccount *view, Account *account);
 
-/** Add the account color background data function to the GncTreeViewAccount column to 
+/** Add the account color background data function to the GncTreeViewAccount column to
  *  show or not the column background in the account color.
  */
 void gnc_tree_view_account_column_add_color (GncTreeViewAccount *view, GtkTreeViewColumn *col);
 
+/** Setup the callback for when the user starts editing the account tree so actions can be disabled
+ *  like the delete menu option as required.
+ */
+void gnc_tree_view_account_set_editing_started_cb
+    (GncTreeViewAccount *view, GFunc editing_started_cb, gpointer editing_cb_data );
+
+/** Setup the callback for when the user finishes editing the account tree so actions can be enabled
+ *  like the delete menu option as required.
+ */
+void gnc_tree_view_account_set_editing_finished_cb
+    (GncTreeViewAccount *view, GFunc editing_finished_cb, gpointer editing_cb_data );
+
+
 /** @} */
 
 /** @} */
diff --git a/gnucash/gnome-utils/gnc-tree-view.c b/gnucash/gnome-utils/gnc-tree-view.c
index 015e2fd..a5ea1ac 100644
--- a/gnucash/gnome-utils/gnc-tree-view.c
+++ b/gnucash/gnome-utils/gnc-tree-view.c
@@ -116,6 +116,11 @@ typedef struct GncTreeViewPrivate
     /* Sort callback model */
     GtkTreeModel      *sort_model;
 
+    /* Editing callback functions */
+    GFunc editing_started_cb;
+    GFunc editing_finished_cb;
+    gpointer editing_cb_data;
+
     /* State related values */
     gchar             *state_section;
     gboolean           seen_state_visibility;
@@ -1756,6 +1761,35 @@ gnc_tree_view_add_toggle_column (GncTreeView *view,
     return column;
 }
 
+static void
+renderer_editing_canceled_cb (GtkCellRenderer *renderer, gpointer user_data)
+{
+    GncTreeView *view = user_data;
+    GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
+    if (priv->editing_finished_cb)
+        (priv->editing_finished_cb)(view, priv->editing_cb_data);
+}
+
+static void
+renderer_editing_started_cb (GtkCellRenderer *renderer,
+               GtkCellEditable *editable, gchar *path, gpointer user_data)
+{
+    GncTreeView *view = user_data;
+    GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
+    if (priv->editing_started_cb)
+        (priv->editing_started_cb)(view, priv->editing_cb_data);
+}
+
+static void
+renderer_edited_cb (GtkCellRendererText *renderer, gchar *path,
+                    gchar *new_text, gpointer user_data)
+{
+    GncTreeView *view = user_data;
+    GncTreeViewPrivate *priv = GNC_TREE_VIEW_GET_PRIVATE(view);
+    if (priv->editing_finished_cb)
+        (priv->editing_finished_cb)(view, priv->editing_cb_data);
+}
+
 /** This function adds a new text column to a GncTreeView base view.
  *  It takes all the parameters necessary to hook a GtkTreeModel
  *  column to a GtkTreeViewColumn.  If the tree has a state section
@@ -1796,6 +1830,16 @@ gnc_tree_view_add_text_column (GncTreeView *view,
     renderer = gtk_cell_renderer_text_new ();
     gtk_tree_view_column_pack_start (column, renderer, TRUE);
 
+    /* Set up the callbacks for when editing */
+    g_signal_connect(G_OBJECT(renderer), "editing-canceled",
+                         (GCallback) renderer_editing_canceled_cb, view);
+
+    g_signal_connect(G_OBJECT(renderer), "editing-started",
+                         (GCallback) renderer_editing_started_cb, view);
+
+    g_signal_connect(G_OBJECT(renderer), "edited",
+                         (GCallback) renderer_edited_cb, view);
+
     /* Set renderer attributes controlled by the model */
     if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
         gtk_tree_view_column_add_attribute (column, renderer,
@@ -2149,5 +2193,33 @@ gnc_tree_view_keynav(GncTreeView *view, GtkTreeViewColumn **col,
     return;
 }
 
+void
+gnc_tree_view_set_editing_started_cb(GncTreeView *view, GFunc editing_started_cb, gpointer editing_cb_data)
+{
+    GncTreeViewPrivate *priv;
+
+    if (!view && !editing_started_cb)
+        return;
+
+    priv = GNC_TREE_VIEW_GET_PRIVATE(view);
+
+    priv->editing_started_cb = editing_started_cb;
+    priv->editing_cb_data = editing_cb_data;
+}
+
+void
+gnc_tree_view_set_editing_finished_cb(GncTreeView *view, GFunc editing_finished_cb, gpointer editing_cb_data)
+{
+    GncTreeViewPrivate *priv;
+
+    if (!view && !editing_finished_cb)
+        return;
+
+    priv = GNC_TREE_VIEW_GET_PRIVATE(view);
+
+    priv->editing_finished_cb = editing_finished_cb;
+    priv->editing_cb_data = editing_cb_data;
+}
+
 /** @} */
 /** @} */
diff --git a/gnucash/gnome-utils/gnc-tree-view.h b/gnucash/gnome-utils/gnc-tree-view.h
index 27f01f6..9b379a4 100644
--- a/gnucash/gnome-utils/gnc-tree-view.h
+++ b/gnucash/gnome-utils/gnc-tree-view.h
@@ -453,6 +453,20 @@ gnc_tree_view_keynav(GncTreeView *view, GtkTreeViewColumn **col,
 gboolean
 gnc_tree_view_path_is_valid(GncTreeView *view, GtkTreePath *path);
 
+/** Setup a callback for when the user starts editing so appropiate actions can be taken
+ *  like disable the actions delete menu option.
+ */
+void
+gnc_tree_view_set_editing_started_cb(GncTreeView *view,
+                    GFunc editing_started_cb, gpointer editing_cb_data);
+
+/** Setup a callback for when the user finishes editing so appropiate actions can be taken
+ *  like enable the actions delete menu option.
+ */
+void
+gnc_tree_view_set_editing_finished_cb(GncTreeView *view,
+                   GFunc editing_finished_cb, gpointer editing_cb_data);
+
 /** @} */
 
 /** @} */
diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.c b/gnucash/gnome/gnc-plugin-page-account-tree.c
index 1630dfe..aeee963 100644
--- a/gnucash/gnome/gnc-plugin-page-account-tree.c
+++ b/gnucash/gnome/gnc-plugin-page-account-tree.c
@@ -613,6 +613,28 @@ gnc_plugin_page_account_tree_close_cb (gpointer user_data)
     gnc_main_window_close_page(plugin_page);
 }
 
+static void
+gnc_plugin_page_account_editing_started_cd (gpointer various, GncPluginPageRegister *page)
+{
+    GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(page);
+    GtkAction *action = gnc_main_window_find_action (GNC_MAIN_WINDOW(plugin_page->window),
+                                                     "EditDeleteAccountAction");
+
+    if (action != NULL)
+        gtk_action_set_sensitive (action, FALSE);
+}
+
+static void
+gnc_plugin_page_account_editing_finished_cb (gpointer various, GncPluginPageRegister *page)
+{
+    GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(page);
+    GtkAction *action = gnc_main_window_find_action (GNC_MAIN_WINDOW(plugin_page->window),
+                                                     "EditDeleteAccountAction");
+
+    if (action != NULL)
+        gtk_action_set_sensitive (action, TRUE);
+}
+
 static GtkWidget *
 gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page)
 {
@@ -668,6 +690,12 @@ gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page)
     gnc_tree_view_account_set_notes_edited(GNC_TREE_VIEW_ACCOUNT(tree_view),
                                            gnc_tree_view_account_notes_edited_cb);
 
+    // Setup some callbacks so menu actions can be disabled/enabled
+    gnc_tree_view_account_set_editing_started_cb(GNC_TREE_VIEW_ACCOUNT(tree_view),
+        (GFunc)gnc_plugin_page_account_editing_started_cd, page);
+    gnc_tree_view_account_set_editing_finished_cb(GNC_TREE_VIEW_ACCOUNT(tree_view),
+        (GFunc)gnc_plugin_page_account_editing_finished_cb, page);
+
     priv->tree_view = tree_view;
     selection = gtk_tree_view_get_selection(tree_view);
     g_signal_connect (G_OBJECT (selection), "changed",

commit bd9af4aed072faeafac1fd8e88e85b39d45edb7e
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:41:48 2017 +0000

    Set focus for invoice pages
    
    If the sheet is read only then set the focus on the notes field else
    set it to the sheet.

diff --git a/gnucash/gnome/dialog-invoice.c b/gnucash/gnome/dialog-invoice.c
index b766c6f..f92c0ba 100644
--- a/gnucash/gnome/dialog-invoice.c
+++ b/gnucash/gnome/dialog-invoice.c
@@ -237,6 +237,14 @@ gnc_invoice_get_register(InvoiceWindow *iw)
     return NULL;
 }
 
+GtkWidget *
+gnc_invoice_get_notes(InvoiceWindow *iw)
+{
+    if (iw)
+        return (GtkWidget *)iw->notes_text;
+    return NULL;
+}
+
 /*******************************************************************************/
 /* FUNCTIONS FOR UNPOSTING */
 
diff --git a/gnucash/gnome/dialog-invoice.h b/gnucash/gnome/dialog-invoice.h
index 93a92fa..7cdbda6 100644
--- a/gnucash/gnome/dialog-invoice.h
+++ b/gnucash/gnome/dialog-invoice.h
@@ -86,6 +86,7 @@ void gnc_invoice_save_page (InvoiceWindow *iw, GKeyFile *key_file, const gchar *
 GtkWidget * gnc_invoice_create_page (InvoiceWindow *iw, gpointer page);
 
 GtkWidget *gnc_invoice_get_register(InvoiceWindow *iw);
+GtkWidget *gnc_invoice_get_notes(InvoiceWindow *iw);
 
 /* definitions for CB functions */
 void gnc_invoice_window_destroy_cb (GtkWidget *widget, gpointer data);
diff --git a/gnucash/gnome/gnc-plugin-page-invoice.c b/gnucash/gnome/gnc-plugin-page-invoice.c
index ced56d3..1b8b869 100644
--- a/gnucash/gnome/gnc-plugin-page-invoice.c
+++ b/gnucash/gnome/gnc-plugin-page-invoice.c
@@ -256,12 +256,12 @@ static const gchar *can_unpost_actions[] =
 /** Short labels for use on the toolbar buttons. */
 static action_toolbar_labels toolbar_labels[] =
 {
-    { "RecordEntryAction", 	  N_("Enter") },
-    { "CancelEntryAction", 	  N_("Cancel") },
-    { "DeleteEntryAction", 	  N_("Delete") },
-    { "DuplicateEntryAction",       N_("Duplicate") },
-    { "EntryUpAction", N_("Up") },
-    { "EntryDownAction", N_("Down") },
+    { "RecordEntryAction",    N_("Enter") },
+    { "CancelEntryAction",    N_("Cancel") },
+    { "DeleteEntryAction",    N_("Delete") },
+    { "DuplicateEntryAction", N_("Duplicate") },
+    { "EntryUpAction",        N_("Up") },
+    { "EntryDownAction",      N_("Down") },
     { "BlankEntryAction",           N_("Blank") },
     { "EditPostInvoiceAction",      N_("Post") },
     { "EditUnpostInvoiceAction",    N_("Unpost") },
@@ -443,6 +443,63 @@ gnc_plugin_page_invoice_update_menus (GncPluginPage *page, gboolean is_posted, g
 }
 
 
+static gboolean
+gnc_plugin_page_invoice_focus (InvoiceWindow *iw)
+{
+    GtkWidget *regWidget = gnc_invoice_get_register(iw);
+    GtkWidget *notes = gnc_invoice_get_notes(iw);
+    GnucashSheet *sheet;
+
+    if (!GNUCASH_IS_REGISTER(regWidget))
+        return FALSE;
+    
+    sheet = gnucash_register_get_sheet (GNUCASH_REGISTER(regWidget));
+
+    // Test for the sheet being read only
+    if (!gnucash_sheet_is_read_only (sheet))
+    {
+        if (!gtk_widget_is_focus (GTK_WIDGET(sheet)))
+            gtk_widget_grab_focus (GTK_WIDGET(sheet));
+    }
+    else // set focus to the notes field
+    {
+        if (!gtk_widget_is_focus (GTK_WIDGET(notes)))
+            gtk_widget_grab_focus (GTK_WIDGET(notes));
+    }
+    return FALSE;
+}
+
+
+/**
+ * Whenever the current page is changed, if an invoice page is
+ * the current page, set focus on the sheet or notes field.
+ */
+static void
+gnc_plugin_page_invoice_main_window_page_changed (GncMainWindow *window,
+        GncPluginPage *plugin_page, gpointer user_data)
+{
+    // We continue only if the plugin_page is a valid
+    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
+        return;
+
+    if (gnc_main_window_get_current_page (window) == plugin_page)
+    {
+        GncPluginPageInvoice *page;
+        GncPluginPageInvoicePrivate *priv;
+
+        if (!GNC_IS_PLUGIN_PAGE_INVOICE(plugin_page))
+            return;
+
+        page = GNC_PLUGIN_PAGE_INVOICE(plugin_page);
+        priv = GNC_PLUGIN_PAGE_INVOICE_GET_PRIVATE(page);
+
+        // The page changed signal is emitted multiple times so we need
+        // to use an idle_add to change the focus to the sheet
+        g_idle_add ((GSourceFunc)gnc_plugin_page_invoice_focus, priv->iw);
+    }
+}
+
+
 /* Virtual Functions */
 
 static GtkWidget *
@@ -451,6 +508,7 @@ gnc_plugin_page_invoice_create_widget (GncPluginPage *plugin_page)
     GncPluginPageInvoice *page;
     GncPluginPageInvoicePrivate *priv;
     GtkWidget *regWidget, *widget;
+    GncMainWindow  *window;
 
     ENTER("page %p", plugin_page);
     page = GNC_PLUGIN_PAGE_INVOICE (plugin_page);
@@ -497,6 +555,11 @@ gnc_plugin_page_invoice_create_widget (GncPluginPage *plugin_page)
                                    gnc_plugin_page_invoice_refresh_cb,
                                    NULL, page);
 
+    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(plugin_page)->window);
+    g_signal_connect(window, "page_changed",
+                     G_CALLBACK(gnc_plugin_page_invoice_main_window_page_changed),
+                     plugin_page);
+
     LEAVE("");
     return priv->widget;
 }

commit 4ccc965e0990fe0e2de3dab6b8b8f8a785cc129e
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:41:02 2017 +0000

    Add function to get whether sheet is read only

diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c
index 64d2b42..520d33b 100644
--- a/gnucash/register/register-gnome/gnucash-sheet.c
+++ b/gnucash/register/register-gnome/gnucash-sheet.c
@@ -746,6 +746,14 @@ gnucash_sheet_redraw_block (GnucashSheet *sheet, VirtualCellLocation vcell_loc)
     gtk_widget_queue_draw_area (GTK_WIDGET(sheet), x, y, w + 1, h + 1);
 }
 
+gboolean
+gnucash_sheet_is_read_only (GnucashSheet *sheet)
+{
+    g_return_val_if_fail (sheet != NULL, TRUE);
+    g_return_val_if_fail (GNUCASH_IS_SHEET(sheet), TRUE);
+    return gnc_table_model_read_only (sheet->table->model);
+}
+
 static void
 gnucash_sheet_finalize (GObject *object)
 {
diff --git a/gnucash/register/register-gnome/gnucash-sheet.h b/gnucash/register/register-gnome/gnucash-sheet.h
index f729e8f..907cd34 100644
--- a/gnucash/register/register-gnome/gnucash-sheet.h
+++ b/gnucash/register/register-gnome/gnucash-sheet.h
@@ -110,5 +110,7 @@ void gnucash_sheet_set_text_bounds (GnucashSheet *sheet, GdkRectangle *rect,
 gint gnucash_sheet_get_text_offset (GnucashSheet *sheet, const VirtualLocation virt_loc,
                                     gint rect_width, gint logical_width);
 
+gboolean gnucash_sheet_is_read_only (GnucashSheet *sheet);
+
 /** @} */
 #endif

commit 34c9ba05491db3ce5ec3d1ffe411e2533818cf2a
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:40:21 2017 +0000

    Make sure the tree view is the focus on an owner page

diff --git a/gnucash/gnome/gnc-plugin-page-owner-tree.c b/gnucash/gnome/gnc-plugin-page-owner-tree.c
index e9097ed..ae76292 100644
--- a/gnucash/gnome/gnc-plugin-page-owner-tree.c
+++ b/gnucash/gnome/gnc-plugin-page-owner-tree.c
@@ -387,6 +387,50 @@ gnc_plugin_page_owner_tree_new (GncOwnerType owner_type)
     return GNC_PLUGIN_PAGE(plugin_page);
 }
 
+
+static gboolean
+gnc_plugin_page_owner_focus (GtkTreeView *tree_view)
+{
+    if (GTK_IS_TREE_VIEW(tree_view))
+    {
+        if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
+            gtk_widget_grab_focus (GTK_WIDGET(tree_view));
+    }
+    return FALSE;
+}
+
+
+/**
+ * Whenever the current page is changed, if an owner page is
+ * the current page, set focus on the treeview.
+ */
+static void
+gnc_plugin_page_owner_main_window_page_changed (GncMainWindow *window,
+        GncPluginPage *plugin_page, gpointer user_data)
+{
+    // We continue only if the plugin_page is a valid
+    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
+        return;
+
+    if (gnc_main_window_get_current_page (window) == plugin_page)
+    {
+        GncPluginPageOwnerTree *page;
+        GncPluginPageOwnerTreePrivate *priv;
+
+        if (!GNC_IS_PLUGIN_PAGE_OWNER_TREE(plugin_page))
+            return;
+
+        page = GNC_PLUGIN_PAGE_OWNER_TREE(plugin_page);
+        priv = GNC_PLUGIN_PAGE_OWNER_TREE_GET_PRIVATE(page);
+
+        // The page changed signal is emitted multiple times so we need
+        // to use an idle_add to change the focus to the tree view
+        g_idle_add ((GSourceFunc)gnc_plugin_page_owner_focus,
+                      GTK_TREE_VIEW (priv->tree_view));
+    }
+}
+
+
 static void
 gnc_plugin_page_owner_tree_class_init (GncPluginPageOwnerTreeClass *klass)
 {
@@ -554,6 +598,7 @@ gnc_plugin_page_owner_tree_create_widget (GncPluginPage *plugin_page)
 {
     GncPluginPageOwnerTree *page;
     GncPluginPageOwnerTreePrivate *priv;
+    GncMainWindow  *window;
     GtkTreeSelection *selection;
     GtkTreeView *tree_view;
     GtkWidget *scrolled_window;
@@ -668,6 +713,11 @@ gnc_plugin_page_owner_tree_create_widget (GncPluginPage *plugin_page)
     gnc_gui_component_set_session (priv->component_id,
                                    gnc_get_current_session());
 
+    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(plugin_page)->window);
+    g_signal_connect(window, "page_changed",
+                     G_CALLBACK(gnc_plugin_page_owner_main_window_page_changed),
+                     plugin_page);
+
     LEAVE("widget = %p", priv->widget);
     return priv->widget;
 }

commit 18fff9635f4fdd66fdb613f2a935cf8d10a91a4d
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:39:42 2017 +0000

    Replace tabs with spaces for budget files

diff --git a/gnucash/gnome/gnc-plugin-budget.c b/gnucash/gnome/gnc-plugin-budget.c
index 51ba0ea..aab03fa 100644
--- a/gnucash/gnome/gnc-plugin-budget.c
+++ b/gnucash/gnome/gnc-plugin-budget.c
@@ -97,13 +97,13 @@ gnc_plugin_budget_get_type (void)
         static const GTypeInfo our_info =
         {
             sizeof (GncPluginBudgetClass),
-            NULL,		/* base_init */
-            NULL,		/* base_finalize */
+            NULL,       /* base_init */
+            NULL,       /* base_finalize */
             (GClassInitFunc) gnc_plugin_budget_class_init,
-            NULL,		/* class_finalize */
-            NULL,		/* class_data */
+            NULL,       /* class_finalize */
+            NULL,       /* class_data */
             sizeof (GncPluginBudget),
-            0,		/* n_preallocs */
+            0,          /* n_preallocs */
             (GInstanceInitFunc) gnc_plugin_budget_init
         };
 
diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c
index 7436afc..5f2607a 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.c
+++ b/gnucash/gnome/gnc-plugin-page-budget.c
@@ -175,10 +175,10 @@ static const gchar *actions_requiring_account[] =
 /** Short labels for use on the toolbar buttons. */
 static action_toolbar_labels toolbar_labels[] =
 {
-    { "OpenAccountAction", 	    N_("Open") },
-    { "DeleteBudgetAction", 	    N_("Delete") },
-    { "OptionsBudgetAction", 	    N_("Options") },
-    { "EstimateBudgetAction", 	    N_("Estimate") },
+    { "OpenAccountAction",          N_("Open") },
+    { "DeleteBudgetAction",         N_("Delete") },
+    { "OptionsBudgetAction",        N_("Options") },
+    { "EstimateBudgetAction",       N_("Estimate") },
     { NULL, NULL },
 };
 

commit 437a375692c5a8ed24cb4267e1a747c4005702b7
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:38:45 2017 +0000

    ake sure the Account tree view has focus when budgets open

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index cc5e0c8..00be309 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -290,6 +290,17 @@ gnc_budget_view_get_account_from_path(GncBudgetView* view, GtkTreePath* path)
     return gnc_tree_view_account_get_account_from_path(GNC_TREE_VIEW_ACCOUNT(priv->tree_view), path);
 }
 
+GtkWidget*
+gnc_budget_view_get_account_tree_view (GncBudgetView* view)
+{
+    GncBudgetViewPrivate *priv;
+    
+    g_return_val_if_fail(GNC_IS_BUDGET_VIEW(view), NULL);
+        
+    priv =  GNC_BUDGET_VIEW_GET_PRIVATE(view);
+    return GTK_WIDGET(priv->fd->tree_view);
+}
+
 GList*
 gnc_budget_view_get_selected_accounts(GncBudgetView* view)
 {
diff --git a/gnucash/gnome/gnc-budget-view.h b/gnucash/gnome/gnc-budget-view.h
index db3abef..3e85b8d 100644
--- a/gnucash/gnome/gnc-budget-view.h
+++ b/gnucash/gnome/gnc-budget-view.h
@@ -68,6 +68,7 @@ gboolean gnc_budget_view_restore(GncBudgetView* view, GKeyFile *key_file, const
 GtkTreeSelection* gnc_budget_view_get_selection(GncBudgetView* view);
 Account* gnc_budget_view_get_account_from_path(GncBudgetView* view, GtkTreePath* path);
 GList* gnc_budget_view_get_selected_accounts(GncBudgetView* view);
+GtkWidget *gnc_budget_view_get_account_tree_view (GncBudgetView* view);
 
 G_END_DECLS
 
diff --git a/gnucash/gnome/gnc-plugin-budget.c b/gnucash/gnome/gnc-plugin-budget.c
index e4f765a..51ba0ea 100644
--- a/gnucash/gnome/gnc-plugin-budget.c
+++ b/gnucash/gnome/gnc-plugin-budget.c
@@ -44,6 +44,8 @@ static QofLogModule log_module = GNC_MOD_GUI;
 static void gnc_plugin_budget_class_init (GncPluginBudgetClass *klass);
 static void gnc_plugin_budget_init (GncPluginBudget *plugin);
 static void gnc_plugin_budget_finalize (GObject *object);
+static void gnc_plugin_budget_add_to_window (GncPlugin *plugin,
+                                             GncMainWindow *window, GQuark type);
 
 /* Command Callbacks */
 static void gnc_plugin_budget_cmd_new_budget (GtkAction *action,
@@ -127,6 +129,26 @@ GncPlugin * gnc_plugin_budget_new (void)
 }
 
 static void
+gnc_plugin_budget_main_window_page_changed (GncMainWindow *window,
+        GncPluginPage *plugin_page, gpointer user_data)
+{
+    // We continue only if the plugin_page is a valid
+    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
+        return;
+
+    if (gnc_main_window_get_current_page (window) == plugin_page)
+    {
+        if (!GNC_IS_PLUGIN_PAGE_BUDGET(plugin_page))
+            return;
+
+        // The page changed signal is emitted multiple times so we need
+        // to use an idle_add to change the focus to the tree view
+        g_idle_add ((GSourceFunc)gnc_plugin_page_budget_focus,
+                      GNC_PLUGIN_PAGE_BUDGET (plugin_page));
+    }
+}
+
+static void
 gnc_plugin_budget_class_init (GncPluginBudgetClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -136,6 +158,9 @@ gnc_plugin_budget_class_init (GncPluginBudgetClass *klass)
     parent_class = g_type_class_peek_parent (klass);
     object_class->finalize = gnc_plugin_budget_finalize;
 
+    /* function overrides */
+    plugin_class->add_to_window = gnc_plugin_budget_add_to_window;
+
     plugin_class->plugin_name  = GNC_PLUGIN_BUDGET_NAME;
     plugin_class->actions_name = PLUGIN_ACTIONS_NAME;
     plugin_class->actions      = gnc_plugin_actions;
@@ -162,6 +187,20 @@ gnc_plugin_budget_finalize(GObject *object)
 
 }
 
+/**
+ * Called when this plugin is added to a main window.  Connect a few callbacks
+ * here to track page changes.
+ *
+ */
+static void gnc_plugin_budget_add_to_window (GncPlugin *plugin,
+        GncMainWindow *mainwindow,
+        GQuark type)
+{
+    g_signal_connect(mainwindow, "page_changed",
+                     G_CALLBACK(gnc_plugin_budget_main_window_page_changed),
+                     plugin);
+}
+
 /************************************************************
  *                    Command Callbacks                     *
  ************************************************************/
diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c
index 39bc542..7436afc 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.c
+++ b/gnucash/gnome/gnc-plugin-page-budget.c
@@ -368,6 +368,22 @@ gnc_plugin_page_budget_close_cb (gpointer user_data)
 }
 
 
+gboolean
+gnc_plugin_page_budget_focus (GncPluginPageBudget *page)
+{
+    if (GNC_IS_PLUGIN_PAGE_BUDGET(page))
+    {
+        GncPluginPageBudgetPrivate *priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
+        GncBudgetView *budget_view = priv->budget_view;
+        GtkWidget *account_view = gnc_budget_view_get_account_tree_view (budget_view);
+
+        if (!gtk_widget_is_focus (GTK_WIDGET(account_view)))
+            gtk_widget_grab_focus (GTK_WIDGET(account_view));
+    }
+    return FALSE;
+}
+
+
 static void
 gnc_plugin_page_budget_refresh_cb(GHashTable *changes, gpointer user_data)
 {
diff --git a/gnucash/gnome/gnc-plugin-page-budget.h b/gnucash/gnome/gnc-plugin-page-budget.h
index 2cabc42..6b1979c 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.h
+++ b/gnucash/gnome/gnc-plugin-page-budget.h
@@ -70,6 +70,16 @@ GncPluginPage *gnc_plugin_page_budget_new  (GncBudget *budget);
 
 void gnc_budget_gui_delete_budget(GncBudget *budget);
 
+/** Given a pointer to a budget plugin page, set the focus to
+ *  the Account GtkTreeView. This is used in a g_idle_add so
+ *  return FALSE.
+ *
+ *  @param page The "budget" page.
+ *
+ *  @return FALSE
+ */
+gboolean gnc_plugin_page_budget_focus (GncPluginPageBudget *page);
+
 G_END_DECLS
 
 #endif /* __GNC_PLUGIN_PAGE_BUDGET_H */

commit 45a52a5d5e73f84768a6a26830245275705a3bc1
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:37:41 2017 +0000

    Change tabs to spaces in gnc-html-webkit2.c

diff --git a/gnucash/html/gnc-html-webkit2.c b/gnucash/html/gnc-html-webkit2.c
index 5b88474..0a8c377 100644
--- a/gnucash/html/gnc-html-webkit2.c
+++ b/gnucash/html/gnc-html-webkit2.c
@@ -92,21 +92,21 @@ static char error_404_body[] = N_("The specified URL could not be loaded.");
 #define GNC_PREF_RPT_DFLT_ZOOM "default-zoom"
 
 static gboolean webkit_decide_policy_cb (WebKitWebView* web_view,
-					 WebKitPolicyDecision *decision,
-					 WebKitPolicyDecisionType decision_type,
-					 gpointer user_data);
+                     WebKitPolicyDecision *decision,
+                     WebKitPolicyDecisionType decision_type,
+                     gpointer user_data);
 static void webkit_mouse_target_cb (WebKitWebView* web_view,
-				    WebKitHitTestResult *hit,
-				    guint modifiers, gpointer data);
+                     WebKitHitTestResult *hit,
+                     guint modifiers, gpointer data);
 #if WEBKIT_MINOR_VERSION >= 8
 static gboolean webkit_notification_cb (WebKitWebView *web_view,
-					WebKitNotification *note,
-					gpointer user_data);
+                     WebKitNotification *note,
+                     gpointer user_data);
 #endif
 static gboolean webkit_load_failed_cb (WebKitWebView *web_view,
-				       WebKitLoadEvent event,
-				       gchar *uri, GError *error,
-				       gpointer user_data);
+                     WebKitLoadEvent event,
+                     gchar *uri, GError *error,
+                     gpointer user_data);
 static void webkit_resource_load_started_cb (WebKitWebView *web_view,
                                              WebKitWebResource *resource,
                                              WebKitURIRequest *request,
@@ -134,14 +134,14 @@ gnc_html_webkit_webview_new (void)
      GValue val = G_VALUE_INIT;
      GtkStateFlags state = gtk_style_context_get_state (style);
      gtk_style_context_get_property (style, GTK_STYLE_PROPERTY_FONT,
-				     state, &val);
+                     state, &val);
 
      if (G_VALUE_HOLDS_BOXED (&val))
      {
-	  const PangoFontDescription *font =
-	       (const PangoFontDescription*)g_value_get_boxed (&val);
-	  default_font_family = pango_font_description_get_family (font);
-	  g_value_unset (&val);
+      const PangoFontDescription *font =
+           (const PangoFontDescription*)g_value_get_boxed (&val);
+      default_font_family = pango_font_description_get_family (font);
+      g_value_unset (&val);
      }
 /* Set default webkit settings */
      webkit_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
@@ -162,9 +162,8 @@ gnc_html_webkit_webview_new (void)
                    NULL);
      if (default_font_family != NULL)
      {
-	  g_object_set (G_OBJECT (webkit_settings),
-			"default-font-family", default_font_family,
-			NULL);
+          g_object_set (G_OBJECT (webkit_settings),
+              "default-font-family", default_font_family, NULL);
      }
      return view;
 }
@@ -186,7 +185,7 @@ gnc_html_webkit_init( GncHtmlWebkit* self )
 
      /* Scale everything up */
      zoom = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL_REPORT,
-				 GNC_PREF_RPT_DFLT_ZOOM);
+                 GNC_PREF_RPT_DFLT_ZOOM);
      webkit_web_view_set_zoom_level (priv->web_view, zoom);
 
 
@@ -297,8 +296,8 @@ extract_base_name(URLType type, const gchar* path)
 {
      gchar       machine_rexp[] = "^(//[^/]*)/*(/.*)?$";
      gchar       path_rexp[] = "^/*(.*)/+([^/]*)$";
-     regex_t    compiled_m, compiled_p;
-     regmatch_t match[4];
+     regex_t     compiled_m, compiled_p;
+     regmatch_t  match[4];
      gchar       * machine = NULL, * location = NULL, * base = NULL;
      gchar       * basename = NULL;
 
@@ -330,7 +329,6 @@ extract_base_name(URLType type, const gchar* path)
                                          match[2].rm_eo - match[2].rm_so);
                }
           }
-
      }
      else
      {
@@ -533,7 +531,7 @@ load_to_stream( GncHtmlWebkit* self, URLType type,
                          g_strdup_printf( error_404_format,
                                           _(error_404_title), _(error_404_body) );
                     webkit_web_view_load_html (priv->web_view, fdata,
-					       BASE_URI_NAME);
+                           BASE_URI_NAME);
                }
 
                g_free( fdata );
@@ -546,7 +544,6 @@ load_to_stream( GncHtmlWebkit* self, URLType type,
                     }
                     /* No action required: Webkit jumps to the anchor on its own. */
                }
-
                return;
           }
      }
@@ -580,7 +577,6 @@ load_to_stream( GncHtmlWebkit* self, URLType type,
                {
                     gnc_build_url( type, location, label );
                }
-
           }
           else
           {
@@ -593,26 +589,25 @@ load_to_stream( GncHtmlWebkit* self, URLType type,
                webkit_web_view_load_html (priv->web_view, fdata, BASE_URI_NAME);
                g_free( fdata );
           }
-
      }
      while ( FALSE );
 }
 #ifdef WEBKIT2_4
 static gboolean
 perform_navigation_policy (WebKitWebView *web_view,
-			   WebKitNavigationPolicyDecision *decision,
-			   GncHtml *self)
+               WebKitNavigationPolicyDecision *decision,
+               GncHtml *self)
 {
      WebKitURIRequest *req = NULL;
      const gchar* uri; // Can't init it here.
      gchar *scheme = NULL, *location = NULL, *label = NULL;
      WebKitNavigationAction *action =
-	  webkit_navigation_policy_decision_get_navigation_action (decision);
+      webkit_navigation_policy_decision_get_navigation_action (decision);
      if (webkit_navigation_action_get_navigation_type (action) !=
-	 WEBKIT_NAVIGATION_TYPE_LINK_CLICKED)
+         WEBKIT_NAVIGATION_TYPE_LINK_CLICKED)
      {
-	  webkit_policy_decision_use ((WebKitPolicyDecision*)decision);
-	  return TRUE;
+          webkit_policy_decision_use ((WebKitPolicyDecision*)decision);
+          return TRUE;
      }
      req = webkit_navigation_action_get_request (action);
      uri = webkit_uri_request_get_uri (req);
@@ -631,20 +626,20 @@ perform_navigation_policy (WebKitWebView *web_view,
 
 static gboolean
 webkit_decide_policy_cb (WebKitWebView *web_view,
-			 WebKitPolicyDecision *decision,
-			 WebKitPolicyDecisionType decision_type,
-			 gpointer user_data)
+             WebKitPolicyDecision *decision,
+             WebKitPolicyDecisionType decision_type,
+             gpointer user_data)
 {
 /* This turns out to be the signal to intercept for handling a link-click. */
 #ifdef WEBKIT2_4
      if (decision_type != WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION)
      {
-	  webkit_policy_decision_use (decision);
-	  return TRUE;
+          webkit_policy_decision_use (decision);
+          return TRUE;
      }
      return perform_navigation_policy (
-	  web_view, (WebKitNavigationPolicyDecision*) decision,
-	  GNC_HTML (user_data));
+      web_view, (WebKitNavigationPolicyDecision*) decision,
+      GNC_HTML (user_data));
 #else
      webkit_policy_decision_use (decision);
      return TRUE;
@@ -653,14 +648,14 @@ webkit_decide_policy_cb (WebKitWebView *web_view,
 
 static void
 webkit_mouse_target_cb (WebKitWebView *web_view, WebKitHitTestResult *hit,
-			guint modifiers, gpointer user_data)
+            guint modifiers, gpointer user_data)
 {
      GncHtmlWebkitPrivate* priv;
      GncHtmlWebkit *self = (GncHtmlWebkit*)user_data;
      gchar *uri;
 
      if (!webkit_hit_test_result_context_is_link (hit))
-	  return;
+         return;
 
      priv = GNC_HTML_WEBKIT_GET_PRIVATE (self);
      uri = g_strdup (webkit_hit_test_result_get_link_uri (hit));
@@ -669,13 +664,13 @@ webkit_mouse_target_cb (WebKitWebView *web_view, WebKitHitTestResult *hit,
      if (priv->base.flyover_cb)
      {
           (priv->base.flyover_cb) (GNC_HTML (self), uri,
-				   priv->base.flyover_cb_data);
+                   priv->base.flyover_cb_data);
      }
 }
 #if WEBKIT_MINOR_VERSION >= 8
 static gboolean
 webkit_notification_cb (WebKitWebView* web_view, WebKitNotification *note,
-			gpointer user_data)
+            gpointer user_data)
 {
      GtkWindow *top = NULL;
      GtkWidget *dialog = NULL;
@@ -963,7 +958,6 @@ impl_webkit_show_url( GncHtml* self, URLType type,
 
           }
           while ( FALSE );
-
      }
      else
      {
@@ -1056,7 +1050,7 @@ impl_webkit_copy_to_clipboard( GncHtml* self )
 
      priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
      webkit_web_view_execute_editing_command (priv->web_view,
-					      WEBKIT_EDITING_COMMAND_COPY);
+                          WEBKIT_EDITING_COMMAND_COPY);
 }
 
 /**************************************************************

commit 9191df2f5820e3fb5cb14eb0145e32a6f8938920
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:36:41 2017 +0000

     Make sure the webkit widget has focus on report load

diff --git a/gnucash/report/report-gnome/gnc-plugin-page-report.c b/gnucash/report/report-gnome/gnc-plugin-page-report.c
index cd47261..a719e27 100644
--- a/gnucash/report/report-gnome/gnc-plugin-page-report.c
+++ b/gnucash/report/report-gnome/gnc-plugin-page-report.c
@@ -258,6 +258,48 @@ gnc_plugin_page_report_set_property( GObject *obj,
 
 }
 
+static gboolean
+gnc_plugin_page_report_focus (GtkWidget *widget)
+{
+    if (GTK_IS_WIDGET(widget))
+    {
+        if (!gtk_widget_is_focus (GTK_WIDGET(widget)))
+            gtk_widget_grab_focus (GTK_WIDGET(widget));
+    }
+    return FALSE;
+}
+
+/**
+ * Whenever the current page is changed, if a report page is
+ * the current page, set focus on the report.
+ */
+static void
+gnc_plugin_page_report_main_window_page_changed (GncMainWindow *window,
+        GncPluginPage *plugin_page, gpointer user_data)
+{
+    // We continue only if the plugin_page is a valid
+    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
+        return;
+
+    if (gnc_main_window_get_current_page (window) == plugin_page)
+    {
+        GncPluginPageReport *report;
+        GncPluginPageReportPrivate *priv;
+        GtkWidget *widget;
+
+        if (!GNC_IS_PLUGIN_PAGE_REPORT(plugin_page))
+            return;
+
+        report = GNC_PLUGIN_PAGE_REPORT(plugin_page);
+        priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
+        widget = gnc_html_get_widget(priv->html);
+
+        // The page changed signal is emitted multiple times so we need
+        // to use an idle_add to change the focus to the webkit widget
+        g_idle_add ((GSourceFunc)gnc_plugin_page_report_focus, widget);
+    }
+}
+
 static void
 gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass)
 {
@@ -328,7 +370,7 @@ gnc_plugin_page_report_set_progressbar (GncPluginPage *page, gboolean set)
     GtkAllocation allocation;
 
     progressbar = gnc_window_get_progressbar (GNC_WINDOW(page->window));
-    gtk_widget_get_allocation (GTK_WIDGET(progressbar), &allocation); 
+    gtk_widget_get_allocation (GTK_WIDGET(progressbar), &allocation);
 
     // this sets the minimum size of the progressbar to that allocated
     if (set)
@@ -401,6 +443,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
 {
     GncPluginPageReport *report;
     GncPluginPageReportPrivate *priv;
+    GncMainWindow  *window;
     GtkWindow *topLvl;
     URLType type;
     char * id_name;
@@ -457,6 +500,11 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
     g_signal_connect (G_OBJECT(GTK_WIDGET(priv->container)), "realize",
                       G_CALLBACK(gnc_plugin_page_report_realize_uri), page);
 
+    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(page)->window);
+    g_signal_connect(window, "page_changed",
+                     G_CALLBACK(gnc_plugin_page_report_main_window_page_changed),
+                     page);
+
     gtk_widget_show_all( GTK_WIDGET(priv->container) );
     LEAVE("container %p", priv->container);
     return GTK_WIDGET( priv->container );

commit 2f2d1b68bae8650200d62d904099e1a540771de4
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:35:49 2017 +0000

    Replace tabs with spaces in a couple of source files

diff --git a/gnucash/gnome/gnc-plugin-business.c b/gnucash/gnome/gnc-plugin-business.c
index 01f061d..daad449 100644
--- a/gnucash/gnome/gnc-plugin-business.c
+++ b/gnucash/gnome/gnc-plugin-business.c
@@ -354,13 +354,13 @@ gnc_plugin_business_get_type (void)
         static const GTypeInfo our_info =
         {
             sizeof (GncPluginBusinessClass),
-            NULL,		/* base_init */
-            NULL,		/* base_finalize */
+            NULL,       /* base_init */
+            NULL,       /* base_finalize */
             (GClassInitFunc) gnc_plugin_business_class_init,
-            NULL,		/* class_finalize */
-            NULL,		/* class_data */
+            NULL,       /* class_finalize */
+            NULL,       /* class_data */
             sizeof (GncPluginBusiness),
-            0,		/* n_preallocs */
+            0,          /* n_preallocs */
             (GInstanceInitFunc) gnc_plugin_business_init
         };
 
@@ -953,17 +953,17 @@ static void
 gnc_plugin_business_cmd_test_init_data (GtkAction *action,
                                         GncMainWindowActionData *data)
 {
-    QofBook *book		= gnc_get_current_book();
-    GncCustomer *customer	= gncCustomerCreate(book);
-    GncAddress *address	= gncCustomerGetAddr(customer);
-    GncInvoice *invoice	= gncInvoiceCreate(book);
-    GncOwner *owner		= gncOwnerNew();
-    GncJob *job		= gncJobCreate(book);
-    Account *root		= gnc_book_get_root_account(book);
-    Account *inc_acct	= xaccMallocAccount(book);
-    Account *bank_acct	= xaccMallocAccount(book);
-    Account *tax_acct	= xaccMallocAccount(book);
-    Account *ar_acct	= xaccMallocAccount(book);
+    QofBook *book       = gnc_get_current_book();
+    GncCustomer *customer   = gncCustomerCreate(book);
+    GncAddress *address = gncCustomerGetAddr(customer);
+    GncInvoice *invoice = gncInvoiceCreate(book);
+    GncOwner *owner     = gncOwnerNew();
+    GncJob *job         = gncJobCreate(book);
+    Account *root       = gnc_book_get_root_account(book);
+    Account *inc_acct   = xaccMallocAccount(book);
+    Account *bank_acct  = xaccMallocAccount(book);
+    Account *tax_acct   = xaccMallocAccount(book);
+    Account *ar_acct    = xaccMallocAccount(book);
     Timespec now;
 
     // Create Customer
diff --git a/gnucash/gnome/gnc-plugin-page-register.c b/gnucash/gnome/gnc-plugin-page-register.c
index dd84079..057db7e 100644
--- a/gnucash/gnome/gnc-plugin-page-register.c
+++ b/gnucash/gnome/gnc-plugin-page-register.c
@@ -393,8 +393,8 @@ static GtkActionEntry gnc_plugin_page_register_actions [] =
     },
     {
         "ScrubAllAction", NULL,
-		/* Translators: The following 2 are Scrub actions in register view */
-		N_("_All transactions"), NULL, NULL,
+        /* Translators: The following 2 are Scrub actions in register view */
+        N_("_All transactions"), NULL, NULL,
         G_CALLBACK (gnc_plugin_page_register_cmd_scrub_all)
     },
     {
@@ -488,11 +488,11 @@ static const gchar *view_style_actions[] =
 /** Short labels for use on the toolbar buttons. */
 static action_toolbar_labels toolbar_labels[] =
 {
-    { "ActionsTransferAction", 	            N_("Transfer") },
-    { "RecordTransactionAction", 	        N_("Enter") },
-    { "CancelTransactionAction", 	        N_("Cancel") },
-    { "DeleteTransactionAction", 	        N_("Delete") },
-	{ "DuplicateTransactionAction",         N_("Duplicate") },
+    { "ActionsTransferAction",              N_("Transfer") },
+    { "RecordTransactionAction",            N_("Enter") },
+    { "CancelTransactionAction",            N_("Cancel") },
+    { "DeleteTransactionAction",            N_("Delete") },
+    { "DuplicateTransactionAction",         N_("Duplicate") },
     { "SplitTransactionAction",             N_("Split") },
     { "ScheduleTransactionAction",          N_("Schedule") },
     { "BlankTransactionAction",             N_("Blank") },
@@ -1323,10 +1323,10 @@ static const gchar *style_names[] =
 #define KEY_REGISTER_STYLE      "RegisterStyle"
 #define KEY_DOUBLE_LINE         "DoubleLineMode"
 
-#define LABEL_ACCOUNT		"Account"
-#define LABEL_SUBACCOUNT	"SubAccount"
-#define LABEL_GL		"GL"
-#define LABEL_SEARCH		"Search"
+#define LABEL_ACCOUNT       "Account"
+#define LABEL_SUBACCOUNT    "SubAccount"
+#define LABEL_GL            "GL"
+#define LABEL_SEARCH        "Search"
 
 
 /** Save enough information about this register page that it can be
@@ -2219,10 +2219,10 @@ gnc_plugin_page_register_filter_dmy2time (char *date_string)
     struct tm when;
 
     PINFO("Date string is %s", date_string);
-    memset (&when, 0, sizeof (when));
+        memset (&when, 0, sizeof (when));
 
     sscanf (date_string, "%d-%d-%d", &when.tm_mday,
-	    &when.tm_mon, &when.tm_year);
+        &when.tm_mon, &when.tm_year);
 
     when.tm_mon -= 1;
     when.tm_year -= 1900;
@@ -4162,7 +4162,7 @@ gnc_plugin_page_register_event_handler (QofInstance *entity,
     GtkWidget *window;
     gchar *label, *color;
 
-    g_return_if_fail(page);	/* Required */
+    g_return_if_fail(page); /* Required */
     if (!GNC_IS_TRANS(entity) && !GNC_IS_ACCOUNT(entity))
         return;
 
diff --git a/gnucash/gnome/gnc-plugin-page-sx-list.c b/gnucash/gnome/gnc-plugin-page-sx-list.c
index b70b1f3..6b66868 100644
--- a/gnucash/gnome/gnc-plugin-page-sx-list.c
+++ b/gnucash/gnome/gnc-plugin-page-sx-list.c
@@ -200,10 +200,10 @@ gnc_plugin_page_sx_list_new (void)
     GncPluginPageSxList *plugin_page;
     const GList *object = gnc_gobject_tracking_get_list (GNC_PLUGIN_PAGE_SX_LIST_NAME);
     if (object && GNC_IS_PLUGIN_PAGE_SX_LIST (object->data))
-	plugin_page = GNC_PLUGIN_PAGE_SX_LIST (object->data);
+        plugin_page = GNC_PLUGIN_PAGE_SX_LIST (object->data);
     else
     {
-	plugin_page = g_object_new (GNC_TYPE_PLUGIN_PAGE_SX_LIST, NULL);
+        plugin_page = g_object_new (GNC_TYPE_PLUGIN_PAGE_SX_LIST, NULL);
     }
     return GNC_PLUGIN_PAGE(plugin_page);
 }
diff --git a/gnucash/gnome/gnc-split-reg.c b/gnucash/gnome/gnc-split-reg.c
index eb31b16..13cdc97 100644
--- a/gnucash/gnome/gnc-split-reg.c
+++ b/gnucash/gnome/gnc-split-reg.c
@@ -181,13 +181,13 @@ gnc_split_reg_get_type( void )
         GTypeInfo type_info =
         {
             sizeof(GNCSplitRegClass),      /* class_size */
-            NULL,   			/* base_init */
-            NULL,				/* base_finalize */
+            NULL,               /* base_init */
+            NULL,               /* base_finalize */
             (GClassInitFunc)gnc_split_reg_class_init,
-            NULL,				/* class_finalize */
-            NULL,				/* class_data */
-            sizeof(GNCSplitReg),		/* */
-            0,				/* n_preallocs */
+            NULL,               /* class_finalize */
+            NULL,               /* class_data */
+            sizeof(GNCSplitReg),        /* */
+            0,                  /* n_preallocs */
             (GInstanceInitFunc)gnc_split_reg_init,
         };
 
@@ -928,7 +928,7 @@ gsr_default_associate_handler_file (GNCSplitReg *gsr, Transaction *trans, gboole
 
         const gchar *uri = xaccTransGetAssociation (trans);
 
-        if (valid_path_head && g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://")) 
+        if (valid_path_head && g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://"))
         {
             const gchar *part = uri + strlen ("file:");
             new_uri = g_strconcat (path_head, part, NULL);
@@ -952,7 +952,7 @@ gsr_default_associate_handler_file (GNCSplitReg *gsr, Transaction *trans, gboole
 
     if (response == GTK_RESPONSE_ACCEPT)
     {
-	gchar *dialog_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
+        gchar *dialog_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
 
         PINFO("Dialog File URI: %s\n", dialog_uri);
 
@@ -1337,37 +1337,35 @@ gnc_split_reg_duplicate_trans_cb(GtkWidget *w, gpointer data)
 void
 gsr_default_schedule_handler( GNCSplitReg *gsr, gpointer data )
 {
+    GncGUID *fromSXId = NULL;
+    SchedXaction *theSX = NULL;
+    GList *sxElts;
     SplitRegister *reg = gnc_ledger_display_get_split_register( gsr->ledger );
     Transaction *pending_trans = gnc_split_register_get_current_trans (reg);
 
     /* If the transaction has a sched-xact KVP frame, then go to the editor
      * for the existing SX; otherwise, do the sx-from-trans dialog. */
+
+    qof_instance_get (QOF_INSTANCE (pending_trans),
+              "from-sched-xaction", &fromSXId,
+              NULL);
+
+    /* Get the correct SX */
+    for ( sxElts = gnc_book_get_schedxactions (gnc_get_current_book())->sx_list;
+          (!theSX) && sxElts;
+          sxElts = sxElts->next )
     {
-	GncGUID *fromSXId = NULL;
-	SchedXaction *theSX = NULL;
-	GList *sxElts;
-	qof_instance_get (QOF_INSTANCE (pending_trans),
-			  "from-sched-xaction", &fromSXId,
-			  NULL);
-
-	/* Get the correct SX */
-	for ( sxElts = gnc_book_get_schedxactions (gnc_get_current_book())->sx_list;
-	      (!theSX) && sxElts;
-	      sxElts = sxElts->next )
-	{
-	    SchedXaction *sx = (SchedXaction*)sxElts->data;
-	    theSX =
-		((guid_equal (xaccSchedXactionGetGUID (sx), fromSXId))
-		 ? sx : NULL);
-	}
-
-	if ( theSX )
-	{
-	    gnc_ui_scheduled_xaction_editor_dialog_create(theSX, FALSE);
-	    return;
-	}
+        SchedXaction *sx = (SchedXaction*)sxElts->data;
+        theSX =
+        ((guid_equal (xaccSchedXactionGetGUID (sx), fromSXId))
+         ? sx : NULL);
     }
 
+    if ( theSX )
+    {
+        gnc_ui_scheduled_xaction_editor_dialog_create(theSX, FALSE);
+        return;
+    }
     gnc_sx_create_from_trans(pending_trans);
 }
 

commit f00f7335b7986ea2c7d14e17cbd4e3b48313d7e1
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:22:50 2017 +0000

    Make sure the sx tree view has focus on page load

diff --git a/gnucash/gnome/gnc-plugin-page-sx-list.c b/gnucash/gnome/gnc-plugin-page-sx-list.c
index e233459..b70b1f3 100644
--- a/gnucash/gnome/gnc-plugin-page-sx-list.c
+++ b/gnucash/gnome/gnc-plugin-page-sx-list.c
@@ -209,6 +209,49 @@ gnc_plugin_page_sx_list_new (void)
 }
 
 
+static gboolean
+gnc_plugin_page_sx_list_focus (GtkTreeView *tree_view)
+{
+    if (GTK_IS_TREE_VIEW(tree_view))
+    {
+        if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
+            gtk_widget_grab_focus (GTK_WIDGET(tree_view));
+    }
+    return FALSE;
+}
+
+
+/**
+ * Whenever the current page is changed, if a schedule editor page is
+ * the current page, set focus on the treeview.
+ */
+static void
+gnc_plugin_page_sx_list_main_window_page_changed (GncMainWindow *window,
+        GncPluginPage *plugin_page, gpointer user_data)
+{
+    // We continue only if the plugin_page is a valid
+    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
+        return;
+
+    if (gnc_main_window_get_current_page (window) == plugin_page)
+    {
+        GncPluginPageSxList *page;
+        GncPluginPageSxListPrivate *priv;
+
+        if (!GNC_IS_PLUGIN_PAGE_SX_LIST(plugin_page))
+            return;
+
+        page = GNC_PLUGIN_PAGE_SX_LIST(plugin_page);
+        priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
+
+        // The page changed signal is emitted multiple times so we need
+        // to use an idle_add to change the focus to the tree view
+        g_idle_add ((GSourceFunc)gnc_plugin_page_sx_list_focus,
+                      GTK_TREE_VIEW (priv->tree_view));
+    }
+}
+
+
 static void
 gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass)
 {
@@ -355,6 +398,7 @@ gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
 {
     GncPluginPageSxList *page;
     GncPluginPageSxListPrivate *priv;
+    GncMainWindow  *window;
     GtkWidget *widget;
     GtkWidget *vbox;
     GtkWidget *label;
@@ -481,6 +525,11 @@ gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
     gnc_gui_component_set_session (priv->gnc_component_id,
                                    gnc_get_current_session());
 
+    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(plugin_page)->window);
+    g_signal_connect(window, "page_changed",
+                     G_CALLBACK(gnc_plugin_page_sx_list_main_window_page_changed),
+                     plugin_page);
+
     return priv->widget;
 }
 

commit 13d5570b471152bbbfab606ef2c79a6601b7aaa1
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:22:12 2017 +0000

    Make sure the sheet has focus in a register when opened

diff --git a/gnucash/gnome/gnc-plugin-page-register.c b/gnucash/gnome/gnc-plugin-page-register.c
index b42270d..dd84079 100644
--- a/gnucash/gnome/gnc-plugin-page-register.c
+++ b/gnucash/gnome/gnc-plugin-page-register.c
@@ -836,6 +836,18 @@ gnc_plugin_page_register_get_current_txn (GncPluginPageRegister *page)
     return gnc_split_register_get_current_trans(reg);
 }
 
+gboolean
+gnc_plugin_page_register_focus (GncPluginPageRegister *page)
+{
+    if (GNC_IS_PLUGIN_PAGE_REGISTER(page))
+    {
+        GncPluginPageRegisterPrivate *priv = GNC_PLUGIN_PAGE_REGISTER_GET_PRIVATE(page);
+        GNCSplitReg *gsr = gnc_plugin_page_register_get_gsr(GNC_PLUGIN_PAGE(page));
+        gnc_split_reg_focus_on_sheet (gsr);
+    }
+    return FALSE;
+}
+
 /* This is the list of actions which are switched inactive in a read-only book. */
 static const char* readonly_inactive_actions[] =
 {
diff --git a/gnucash/gnome/gnc-plugin-page-register.h b/gnucash/gnome/gnc-plugin-page-register.h
index 43b2b2e..604836f 100644
--- a/gnucash/gnome/gnc-plugin-page-register.h
+++ b/gnucash/gnome/gnc-plugin-page-register.h
@@ -163,6 +163,16 @@ gnc_plugin_page_register_get_account (GncPluginPageRegister *page);
 Transaction *
 gnc_plugin_page_register_get_current_txn (GncPluginPageRegister *page);
 
+/** Given a pointer to a register plugin page, set the focus to
+ *  the sheet. This is used in a g_idle_add so return FALSE.
+ *
+ *  @param page The "register" page.
+ *
+ *  @return FALSE
+ */
+gboolean
+gnc_plugin_page_register_focus (GncPluginPageRegister *page);
+
 G_END_DECLS
 /** @} */
 /** @} */
diff --git a/gnucash/gnome/gnc-plugin-register.c b/gnucash/gnome/gnc-plugin-register.c
index b8bc200..19f1ccc 100644
--- a/gnucash/gnome/gnc-plugin-register.c
+++ b/gnucash/gnome/gnc-plugin-register.c
@@ -147,6 +147,26 @@ gnc_plugin_register_new (void)
 }
 
 static void
+gnc_plugin_register_main_window_page_changed(GncMainWindow *window,
+        GncPluginPage *plugin_page, gpointer user_data)
+{
+    // We continue only if the plugin_page is a valid
+    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
+        return;
+
+    if (gnc_main_window_get_current_page (window) == plugin_page)
+    {
+        if (!GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page))
+            return;
+
+        // The page changed signal is emitted multiple times so we need
+        // to use an idle_add to change the focus to the register
+        g_idle_add ((GSourceFunc)gnc_plugin_page_register_focus,
+                      GNC_PLUGIN_PAGE_REGISTER (plugin_page));
+    }
+}
+
+static void
 gnc_plugin_register_class_init (GncPluginRegisterClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -211,6 +231,10 @@ gnc_plugin_register_add_to_window (GncPlugin *plugin,
 {
     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL_REGISTER, NULL,
                            gnc_plugin_register_pref_changed, window);
+
+    g_signal_connect(window, "page_changed",
+                     G_CALLBACK(gnc_plugin_register_main_window_page_changed),
+                     plugin);
 }
 
 
diff --git a/gnucash/gnome/gnc-split-reg.c b/gnucash/gnome/gnc-split-reg.c
index e01223a..eb31b16 100644
--- a/gnucash/gnome/gnc-split-reg.c
+++ b/gnucash/gnome/gnc-split-reg.c
@@ -1520,6 +1520,17 @@ gnc_split_reg_jump_to_blank (GNCSplitReg *gsr)
 }
 
 void
+gnc_split_reg_focus_on_sheet (GNCSplitReg *gsr)
+{
+    GnucashRegister *reg = gsr->reg;
+    GnucashSheet *sheet = gnucash_register_get_sheet (reg);
+
+    // Make sure the sheet is the focus
+    if (!gtk_widget_has_focus(GTK_WIDGET (sheet)))
+        gtk_widget_grab_focus (GTK_WIDGET (sheet));
+}
+
+void
 gnc_split_reg_balancing_entry(GNCSplitReg *gsr, Account *account,
                               time64 statement_date, gnc_numeric balancing_amount)
 {
diff --git a/gnucash/gnome/gnc-split-reg.h b/gnucash/gnome/gnc-split-reg.h
index 412b013..02bd07c 100644
--- a/gnucash/gnome/gnc-split-reg.h
+++ b/gnucash/gnome/gnc-split-reg.h
@@ -233,6 +233,11 @@ void gnc_split_reg_jump_to_blank (GNCSplitReg *gsr);
 void gnc_split_reg_jump_to_split(GNCSplitReg *gsr, Split *split);
 void gnc_split_reg_jump_to_split_amount(GNCSplitReg *gsr, Split *split);
 
+/**
+ * Set the focus of the register to the sheet
+ **/
+void gnc_split_reg_focus_on_sheet (GNCSplitReg *gsr);
+
 /*
  * Create a transaction entry with given amount and date. One account is
  * specified, the other is undefined i.e. it defaults to orphan account.
diff --git a/gnucash/register/register-gnome/gnucash-register.c b/gnucash/register/register-gnome/gnucash-register.c
index 0bea4fa..98891a2 100644
--- a/gnucash/register/register-gnome/gnucash-register.c
+++ b/gnucash/register/register-gnome/gnucash-register.c
@@ -272,10 +272,6 @@ gnucash_register_sheet_resize (GnucashRegister *reg)
     if (!reg->hscrollbar_visible)
         gtk_widget_queue_resize (GTK_WIDGET (reg->sheet));
 
-    // Make sure the sheet is the focus
-    if (!gtk_widget_has_focus(GTK_WIDGET (reg->sheet)))
-        gtk_widget_grab_focus (GTK_WIDGET (reg->sheet));
-
     return FALSE;
 }
 

commit 660ab62df96f6e98df7ff09e68665a96a2474348
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:21:27 2017 +0000

    Prevent the tab being the focus

diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.c
index 7b6670e..a65f51e 100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.c
@@ -3539,6 +3539,18 @@ gnc_main_window_window_menu (GncMainWindow *window)
 #endif
 };
 
+/* This is used to prevent the tab having focus */
+static gboolean
+gnc_main_window_page_focus_in (GtkWidget *widget, GdkEvent  *event,
+                               gpointer user_data)
+{
+    GncMainWindow *window = user_data;
+    GncPluginPage *page = gnc_main_window_get_current_page (window);
+
+    g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
+    return FALSE;
+}
+
 static void
 gnc_main_window_setup_window (GncMainWindow *window)
 {
@@ -3579,6 +3591,8 @@ gnc_main_window_setup_window (GncMainWindow *window)
                       G_CALLBACK (gnc_main_window_switch_page), window);
     g_signal_connect (G_OBJECT (priv->notebook), "page-reordered",
                       G_CALLBACK (gnc_main_window_page_reordered), window);
+    g_signal_connect (G_OBJECT (priv->notebook), "focus-in-event",
+                      G_CALLBACK (gnc_main_window_page_focus_in), window);
     gtk_box_pack_start (GTK_BOX (main_vbox), priv->notebook,
                         TRUE, TRUE, 0);
 

commit 2314a322d401081dc105762332ba17ad94d9c124
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 29 10:20:28 2017 +0000

    Make sure the tree view has focus on Account tree page load

diff --git a/gnucash/gnome/gnc-plugin-account-tree.c b/gnucash/gnome/gnc-plugin-account-tree.c
index 675da22..c9ebc0c 100644
--- a/gnucash/gnome/gnc-plugin-account-tree.c
+++ b/gnucash/gnome/gnc-plugin-account-tree.c
@@ -44,6 +44,8 @@
 static void gnc_plugin_account_tree_class_init (GncPluginAccountTreeClass *klass);
 static void gnc_plugin_account_tree_init (GncPluginAccountTree *plugin);
 static void gnc_plugin_account_tree_finalize (GObject *object);
+static void gnc_plugin_account_tree_add_to_window (GncPlugin *plugin,
+                                                   GncMainWindow *window, GQuark type);
 
 /* Command callbacks */
 static void gnc_plugin_account_tree_cmd_new_account_tree (GtkAction *action, GncMainWindowActionData *data);
@@ -125,6 +127,27 @@ gnc_plugin_account_tree_new (void)
 }
 
 
+static void
+gnc_plugin_account_tree_main_window_page_changed (GncMainWindow *window,
+        GncPluginPage *plugin_page, gpointer user_data)
+{
+    // We continue only if the plugin_page is a valid
+    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
+        return;
+
+    if (gnc_main_window_get_current_page (window) == plugin_page)
+    {
+        if (!GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(plugin_page))
+            return;
+
+        // The page changed signal is emitted multiple times so we need
+        // to use an idle_add to change the focus to the tree view
+        g_idle_add ((GSourceFunc)gnc_plugin_page_account_tree_focus,
+                      GNC_PLUGIN_PAGE_ACCOUNT_TREE (plugin_page));
+    }
+}
+
+
 /** Initialize the class for a new account tree plugin.  This will set
  *  up any function pointers that override functions in the parent
  *  class, and also configure the private data storage for this
@@ -145,6 +168,9 @@ gnc_plugin_account_tree_class_init (GncPluginAccountTreeClass *klass)
     /* plugin info */
     plugin_class->plugin_name  = GNC_PLUGIN_ACCOUNT_TREE_NAME;
 
+    /* function overrides */
+    plugin_class->add_to_window = gnc_plugin_account_tree_add_to_window;
+
     /* widget addition/removal */
     plugin_class->actions_name = PLUGIN_ACTIONS_NAME;
     plugin_class->actions      = gnc_plugin_actions;
@@ -182,6 +208,20 @@ gnc_plugin_account_tree_finalize (GObject *object)
     G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+
+/**
+ * Called when this plugin is added to a main window.  Connect a few callbacks
+ * here to track page changes.
+ *
+ */
+static void gnc_plugin_account_tree_add_to_window (GncPlugin *plugin,
+        GncMainWindow *mainwindow,
+        GQuark type)
+{
+    g_signal_connect(mainwindow, "page_changed",
+                     G_CALLBACK(gnc_plugin_account_tree_main_window_page_changed),
+                     plugin);
+}
 /************************************************************
  *                    Command Callbacks                     *
  ************************************************************/
diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.c b/gnucash/gnome/gnc-plugin-page-account-tree.c
index 1630dfe..402f51e 100644
--- a/gnucash/gnome/gnc-plugin-page-account-tree.c
+++ b/gnucash/gnome/gnc-plugin-page-account-tree.c
@@ -587,6 +587,19 @@ gnc_plugin_page_account_tree_get_current_account (GncPluginPageAccountTree *page
     return account;
 }
 
+gboolean
+gnc_plugin_page_account_tree_focus (GncPluginPageAccountTree *page)
+{
+    if (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page))
+    {
+        GncPluginPageAccountTreePrivate *priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
+        GtkTreeView *view = GTK_TREE_VIEW(priv->tree_view);
+
+        if (!gtk_widget_is_focus (GTK_WIDGET(view)))
+            gtk_widget_grab_focus (GTK_WIDGET(view));
+    }
+    return FALSE;
+}
 
 /* Virtual Functions */
 
diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.h b/gnucash/gnome/gnc-plugin-page-account-tree.h
index 57b8629..2dad1e7 100644
--- a/gnucash/gnome/gnc-plugin-page-account-tree.h
+++ b/gnucash/gnome/gnc-plugin-page-account-tree.h
@@ -95,6 +95,15 @@ GncPluginPage *gnc_plugin_page_account_tree_new  (void);
 Account * gnc_plugin_page_account_tree_get_current_account (GncPluginPageAccountTree *page);
 
 
+/** Given a pointer to an account tree plugin page, set the focus to
+ *  the GtkTreeView. This is used in a g_idle_add so return FALSE.
+ *
+ *  @param page The "account tree" page.
+ * 
+ *  @return FALSE;
+ */
+gboolean gnc_plugin_page_account_tree_focus (GncPluginPageAccountTree *page);
+
 /** Given a pointer to an account, the account tree will open
  *  and the account will be selected (if any).
  *

commit 26b82b56e086f0138e50c79e03d0c1c71069e884
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Dec 27 06:32:54 2017 -0800

    Fix Travis 'dangling else' warning.

diff --git a/libgnucash/engine/test/gtest-gnc-datetime.cpp b/libgnucash/engine/test/gtest-gnc-datetime.cpp
index 364a7dc..350d22a 100644
--- a/libgnucash/engine/test/gtest-gnc-datetime.cpp
+++ b/libgnucash/engine/test/gtest-gnc-datetime.cpp
@@ -328,8 +328,11 @@ TEST(gnc_datetime_constructors, test_gncdate_neutral_constructor)
      */
     constexpr time64 max_western_offset = -10 * 3600;
     constexpr time64 max_eastern_offset = 13 * 3600;
-    if (gncdt.offset() >= max_western_offset && gncdt.offset() <= max_eastern_offset)
+    if (gncdt.offset() >= max_western_offset &&
+        gncdt.offset() <= max_eastern_offset)
+    {
         EXPECT_EQ(atime.format("%d-%m-%Y %H:%M:%S %z"), "20-04-2017 10:59:00 UTC");
+    }
 }
 
 TEST(gnc_datetime_functions, test_format)

commit 8ae330c804cd1feab5b653dee7c3f7d3c3406e7e
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 26 21:38:59 2017 -0800

    Another attempt to make Travis's g++ happy about compiler warnings.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b84795b..867b577 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -515,7 +515,10 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")  # FIXME: should be -std=
 
 IF (UNIX)
   SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wdeclaration-after-statement -Wno-pointer-sign -Wall -Wmissing-prototypes -Wmissing-declarations -Wno-unused")
-  SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wmissing-prototypes -Wmissing-declarations -Wno-unused")
+  SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wmissing-declarations -Wno-unused")
+  IF (APPLE)
+      SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmissing-prototypes")
+    ENDIF()
   SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations -std=gnu11")
   SET( CMAKE_C_FLAGS_RELEASE "-O3 ${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2")
 ENDIF (UNIX)

commit 44fc5b0555dd6cbc21b304a3aa68fe1335278208
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 26 18:23:09 2017 -0800

    Fix CXX Flags for g++.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a8a994b..b84795b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -514,8 +514,8 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")  # FIXME: should be -std=
 
 
 IF (UNIX)
-  SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wdeclaration-after-statement -Wno-pointer-sign -Wall -Wunused -Wmissing-prototypes -Wmissing-declarations  -Wno-unused")
-  SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wdeclaration-after-statement -Wno-pointer-sign -Wall -Wunused -Wmissing-prototypes -Wmissing-declarations  -Wno-unused")
+  SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wdeclaration-after-statement -Wno-pointer-sign -Wall -Wmissing-prototypes -Wmissing-declarations -Wno-unused")
+  SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wmissing-prototypes -Wmissing-declarations -Wno-unused")
   SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations -std=gnu11")
   SET( CMAKE_C_FLAGS_RELEASE "-O3 ${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2")
 ENDIF (UNIX)
diff --git a/common/test-core/CMakeLists.txt b/common/test-core/CMakeLists.txt
index 7d426fc..d9a7276 100644
--- a/common/test-core/CMakeLists.txt
+++ b/common/test-core/CMakeLists.txt
@@ -66,12 +66,20 @@ GNC_ADD_SCHEME_TARGETS(scm-test-core
 IF(NOT GTEST_SHARED_LIB)
   SET (lib_gtest_SOURCES ${GTEST_SRC_DIR}/src/gtest-all.cc)
   ADD_LIBRARY(gtest STATIC  ${lib_gtest_SOURCES})
-  TARGET_COMPILE_OPTIONS(gtest PRIVATE -Wno-missing-prototypes)
+  IF(APPLE)
+    TARGET_COMPILE_OPTIONS(gtest PRIVATE -Wno-missing-prototypes)
+  ELSE()
+    TARGET_COMPILE_OPTIONS(gtest PRIVATE -Wno-missing-declarations)
+  ENDIF()
   TARGET_INCLUDE_DIRECTORIES(gtest PUBLIC ${GTEST_INCLUDE_DIR} ${GTEST_SRC_DIR})
 ENDIF()
 SET (lib_gmock_SOURCES ${GMOCK_SRC})
 ADD_LIBRARY(gmock STATIC  ${lib_gmock_SOURCES})
-TARGET_COMPILE_OPTIONS(gmock PRIVATE -Wno-missing-prototypes)
+IF (APPLE)
+  TARGET_COMPILE_OPTIONS(gmock PRIVATE -Wno-missing-prototypes)
+ELSE()
+  TARGET_COMPILE_OPTIONS(gmock PRIVATE -Wno-missing-declarations)
+ENDIF()
 TARGET_INCLUDE_DIRECTORIES(gmock PUBLIC
   ${GTEST_INCLUDE_DIR} ${GTEST_SRC_DIR}
   ${GMOCK_INCLUDE_DIR} ${GMOCK_SRC_DIR})

commit bbd2df6a3bb39ae62d91ec4f740f3366209aa675
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 23 14:27:04 2017 -0800

    Bug 791848 - GC 2.6.x does not handle ISO dates introduced with GC 2.7.
    
    Set a feature to prevent versions older that 2.6.20 from loading a
    database from which they cannot read the dates. Ideally we would do this
    only if the database is written to, but the current persistence design
    includes committing back to the database during the load so the net
    effect is that the flag would be set anyway.

diff --git a/libgnucash/backend/dbi/gnc-backend-dbi.cpp b/libgnucash/backend/dbi/gnc-backend-dbi.cpp
index bd50be6..68b8933 100644
--- a/libgnucash/backend/dbi/gnc-backend-dbi.cpp
+++ b/libgnucash/backend/dbi/gnc-backend-dbi.cpp
@@ -59,7 +59,7 @@ extern "C"
 #include "gnc-engine.h"
 #include "SX-book.h"
 #include "Recurrence.h"
-
+#include <gnc-features.h>
 #include "gnc-uri-utils.h"
 #include "gnc-filepath-utils.h"
 #include <gnc-path.h>
@@ -76,6 +76,7 @@ extern "C"
 #include <string>
 #include <iomanip>
 
+#include <qofsession.hpp>
 #include <gnc-backend-prov.hpp>
 #include "gnc-backend-dbi.h"
 #include "gnc-backend-dbi.hpp"
@@ -470,7 +471,6 @@ GncDbiBackend<DbType::DBI_SQLITE>::session_begin(QofSession* session,
      * Let's start logging */
     xaccLogSetBaseName (filepath.c_str());
     PINFO ("logpath=%s", filepath.c_str() ? filepath.c_str() : "(null)");
-
     LEAVE ("");
 }
 
@@ -830,6 +830,9 @@ GncDbiBackend<Type>::load (QofBook* book, QofBackendLoadType loadType)
 
     GncSqlBackend::load(book, loadType);
 
+    if (Type == DbType::DBI_SQLITE)
+        gnc_features_set_used(book, GNC_FEATURE_SQLITE3_ISO_DATES);
+
     if (GNUCASH_RESAVE_VERSION > get_table_version("Gnucash"))
     {
         /* The database was loaded with an older database schema or
diff --git a/libgnucash/engine/gnc-features.c b/libgnucash/engine/gnc-features.c
index 4d38fa7..ca4cb9b 100644
--- a/libgnucash/engine/gnc-features.c
+++ b/libgnucash/engine/gnc-features.c
@@ -47,6 +47,7 @@ static gncFeature known_features[] =
     { GNC_FEATURE_BOOK_CURRENCY, "User specifies a 'book-currency'; costs of other currencies/commodities tracked in terms of book-currency (requires at least GnuCash 2.7.0)" },
     { GNC_FEATURE_GUID_BAYESIAN, "Use account GUID as key for Bayesian data (requires at least GnuCash 2.6.12)" },
     { GNC_FEATURE_GUID_FLAT_BAYESIAN, "Use account GUID as key for bayesian data and store KVP flat (requires at least Gnucash 2.6.19)" },
+    { GNC_FEATURE_SQLITE3_ISO_DATES, "Use ISO formatted date-time strings in SQLite3 databases (requires at least GnuCash 2.6.20)"},
     { NULL },
 };
 
diff --git a/libgnucash/engine/gnc-features.h b/libgnucash/engine/gnc-features.h
index 2beca42..0386077 100644
--- a/libgnucash/engine/gnc-features.h
+++ b/libgnucash/engine/gnc-features.h
@@ -51,6 +51,7 @@ extern "C" {
 #define GNC_FEATURE_BOOK_CURRENCY "Use a Book-Currency"
 #define GNC_FEATURE_GUID_BAYESIAN "Account GUID based Bayesian data"
 #define GNC_FEATURE_GUID_FLAT_BAYESIAN "Account GUID based bayesian with flat KVP"
+#define GNC_FEATURE_SQLITE3_ISO_DATES "ISO-8601 formatted date strings in SQLite3 databases."
 
 /** @} */
 

commit 91727525b9cd3c12f4fa25268131b0161c6fc79e
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 26 13:01:50 2017 -0800

    Enforce -Werror on C++ files and fix resulting errors.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5378340..a8a994b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -515,6 +515,7 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")  # FIXME: should be -std=
 
 IF (UNIX)
   SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wdeclaration-after-statement -Wno-pointer-sign -Wall -Wunused -Wmissing-prototypes -Wmissing-declarations  -Wno-unused")
+  SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wdeclaration-after-statement -Wno-pointer-sign -Wall -Wunused -Wmissing-prototypes -Wmissing-declarations  -Wno-unused")
   SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations -std=gnu11")
   SET( CMAKE_C_FLAGS_RELEASE "-O3 ${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2")
 ENDIF (UNIX)
diff --git a/common/test-core/CMakeLists.txt b/common/test-core/CMakeLists.txt
index 7774b9c..7d426fc 100644
--- a/common/test-core/CMakeLists.txt
+++ b/common/test-core/CMakeLists.txt
@@ -66,10 +66,12 @@ GNC_ADD_SCHEME_TARGETS(scm-test-core
 IF(NOT GTEST_SHARED_LIB)
   SET (lib_gtest_SOURCES ${GTEST_SRC_DIR}/src/gtest-all.cc)
   ADD_LIBRARY(gtest STATIC  ${lib_gtest_SOURCES})
+  TARGET_COMPILE_OPTIONS(gtest PRIVATE -Wno-missing-prototypes)
   TARGET_INCLUDE_DIRECTORIES(gtest PUBLIC ${GTEST_INCLUDE_DIR} ${GTEST_SRC_DIR})
 ENDIF()
 SET (lib_gmock_SOURCES ${GMOCK_SRC})
 ADD_LIBRARY(gmock STATIC  ${lib_gmock_SOURCES})
+TARGET_COMPILE_OPTIONS(gmock PRIVATE -Wno-missing-prototypes)
 TARGET_INCLUDE_DIRECTORIES(gmock PUBLIC
   ${GTEST_INCLUDE_DIR} ${GTEST_SRC_DIR}
   ${GMOCK_INCLUDE_DIR} ${GMOCK_SRC_DIR})
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index 9402f38..1bfa08e 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -355,12 +355,12 @@ static void csv_tximp_preview_currency_fmt_sel_cb (GtkComboBox* format_selector,
     info->preview_update_currency_format();
 }
 
-void csv_tximp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpTransAssist* info)
+static void csv_tximp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpTransAssist* info)
 {
     info->preview_update_col_type (cbox);
 }
 
-gboolean
+static bool
 csv_tximp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton* event,
                                         CsvImpTransAssist* info)
 {
@@ -1054,7 +1054,7 @@ CsvImpTransAssist::preview_update_currency_format ()
     preview_refresh_table ();
 }
 
-gboolean
+static gboolean
 csv_imp_preview_queue_rebuild_table (CsvImpTransAssist *assist)
 {
     assist->preview_refresh_table ();
@@ -1417,7 +1417,7 @@ CsvImpTransAssist::preview_style_column (uint32_t col_num, GtkTreeModel* model)
 
 /* Helper to create a shared store for the header comboboxes in the preview treeview.
  * It holds the possible column types */
-GtkTreeModel*
+static GtkTreeModel*
 make_column_header_model (bool multi_split)
 {
     auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
diff --git a/libgnucash/backend/sql/gnc-price-sql.cpp b/libgnucash/backend/sql/gnc-price-sql.cpp
index f2cc188..85c440c 100644
--- a/libgnucash/backend/sql/gnc-price-sql.cpp
+++ b/libgnucash/backend/sql/gnc-price-sql.cpp
@@ -203,7 +203,7 @@ GncSqlPriceBackend::commit (GncSqlBackend* sql_be, QofInstance* inst)
     return is_ok;
 }
 
-gboolean
+static gboolean
 write_price (GNCPrice* p, gpointer data)
 {
     auto s = reinterpret_cast<write_objects_t*>(data);
diff --git a/libgnucash/engine/CMakeLists.txt b/libgnucash/engine/CMakeLists.txt
index 993eb5e..23f228c 100644
--- a/libgnucash/engine/CMakeLists.txt
+++ b/libgnucash/engine/CMakeLists.txt
@@ -235,6 +235,7 @@ ADD_LIBRARY (gncmod-engine
 TARGET_LINK_LIBRARIES(gncmod-engine gnc-core-utils gnc-module ${Boost_DATE_TIME_LIBRARIES}  ${Boost_REGEX_LIBRARIES} ${REGEX_LDFLAGS} ${GMODULE_LDFLAGS} ${GLIB2_LDFLAGS} ${GOBJECT_LDFLAGS} ${GUILE_LDFLAGS})
 
 TARGET_COMPILE_DEFINITIONS (gncmod-engine PRIVATE -DG_LOG_DOMAIN=\"gnc.engine\")
+TARGET_COMPILE_OPTIONS (gncmod-engine PRIVATE -Wno-deprecated-register)
 
 TARGET_INCLUDE_DIRECTORIES (gncmod-engine
     PRIVATE ${CMAKE_CURRENT_BINARY_DIR} # for iso-4217-currencies.c
diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index 11aaebf..43b32fb 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -133,7 +133,7 @@ guid_null (void)
     return s_null_gncguid;
 }
 
-void
+static void
 guid_assign (GncGUID & target, gnc::GUID const & source)
 {
     memcpy (&target, &source, sizeof (GncGUID));
diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp
index e82980c..cc7c46b 100644
--- a/libgnucash/engine/qofinstance.cpp
+++ b/libgnucash/engine/qofinstance.cpp
@@ -1247,7 +1247,7 @@ qof_instance_kvp_merge_guids (const QofInstance *target,
     g_return_if_fail (target != NULL);
     g_return_if_fail (donor != NULL);
 
-    if (! qof_instance_has_slot (donor, {path})) return;
+    if (! qof_instance_has_slot (donor, path)) return;
     auto v = donor->kvp_data->get_slot({path});
     if (v == NULL) return;
 
diff --git a/libgnucash/engine/qofsession.cpp b/libgnucash/engine/qofsession.cpp
index 2049b5b..fbebf26 100644
--- a/libgnucash/engine/qofsession.cpp
+++ b/libgnucash/engine/qofsession.cpp
@@ -701,12 +701,14 @@ void qof_session_load_backend (QofSession * session, const char * access_method)
     session->load_backend (access_method);
 }
 
-void qof_session_clear_error (QofSession * session)
+static void
+qof_session_clear_error (QofSession * session)
 {
     session->clear_error ();
 }
 
-void qof_session_destroy_backend (QofSession * session)
+static void
+qof_session_destroy_backend (QofSession * session)
 {
     session->destroy_backend ();
 }
diff --git a/libgnucash/engine/test/test-qofsession.cpp b/libgnucash/engine/test/test-qofsession.cpp
index 8e64370..38a7cc6 100644
--- a/libgnucash/engine/test/test-qofsession.cpp
+++ b/libgnucash/engine/test/test-qofsession.cpp
@@ -51,7 +51,8 @@ public:
     void export_coa(QofBook*);
 };
 
-void example_hook (QofSession & session)
+static void
+example_hook (QofSession & session)
 {
     hook_called = true;
 }
@@ -77,7 +78,8 @@ void QofSessionMockBackend::export_coa(QofBook * book)
     exported_book = book;
 }
 
-QofBackend * test_backend_factory ()
+static QofBackend*
+test_backend_factory ()
 {
     return new QofSessionMockBackend;
 }
@@ -90,7 +92,8 @@ struct MockProvider : public QofBackendProvider
     bool type_check (char const * type) {return true;}
 };
 
-QofBackendProvider_ptr get_provider ()
+static QofBackendProvider_ptr
+get_provider ()
 {
     return QofBackendProvider_ptr {new MockProvider {"Mock Backend", "file"}};
 }

commit a6a46d7cdcb032a878cd1c844c2636f7676e05d6
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 23 15:40:01 2017 -0800

    Test struct tm* returns from gnc_gmtime and gnc_localtime
    
    To ensure that we don't crash for dereffing a nullptr.

diff --git a/common/base-typemaps.i b/common/base-typemaps.i
index 62c8f96..e9f7ac0 100644
--- a/common/base-typemaps.i
+++ b/common/base-typemaps.i
@@ -21,6 +21,7 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  *                                                                  *
 \********************************************************************/
+%include "constraints.i"
 
 typedef void * gpointer; // Not sure why SWIG doesn't figure this out.
 %typemap(newfree) gchar * "g_free($1);"
@@ -93,22 +94,25 @@ typedef char gchar;
 %typemap(out) struct tm * {
     SCM tm = scm_c_make_vector(11, SCM_UNDEFINED);
     struct tm* t = $1;
-    SCM_SIMPLE_VECTOR_SET(tm, 0, scm_from_int(t->tm_sec));
-    SCM_SIMPLE_VECTOR_SET(tm, 1, scm_from_int(t->tm_min));
-    SCM_SIMPLE_VECTOR_SET(tm, 2, scm_from_int(t->tm_hour));
-    SCM_SIMPLE_VECTOR_SET(tm, 3, scm_from_int(t->tm_mday));
-    SCM_SIMPLE_VECTOR_SET(tm, 4, scm_from_int(t->tm_mon));
-    SCM_SIMPLE_VECTOR_SET(tm, 5, scm_from_int(t->tm_year));
-    SCM_SIMPLE_VECTOR_SET(tm, 6, scm_from_int(t->tm_wday));
-    SCM_SIMPLE_VECTOR_SET(tm, 7, scm_from_int(t->tm_yday));
-    SCM_SIMPLE_VECTOR_SET(tm, 8, scm_from_int(t->tm_isdst));
+    if (t != NULL)
+    {
+        SCM_SIMPLE_VECTOR_SET(tm, 0, scm_from_int(t->tm_sec));
+        SCM_SIMPLE_VECTOR_SET(tm, 1, scm_from_int(t->tm_min));
+        SCM_SIMPLE_VECTOR_SET(tm, 2, scm_from_int(t->tm_hour));
+        SCM_SIMPLE_VECTOR_SET(tm, 3, scm_from_int(t->tm_mday));
+        SCM_SIMPLE_VECTOR_SET(tm, 4, scm_from_int(t->tm_mon));
+        SCM_SIMPLE_VECTOR_SET(tm, 5, scm_from_int(t->tm_year));
+        SCM_SIMPLE_VECTOR_SET(tm, 6, scm_from_int(t->tm_wday));
+        SCM_SIMPLE_VECTOR_SET(tm, 7, scm_from_int(t->tm_yday));
+        SCM_SIMPLE_VECTOR_SET(tm, 8, scm_from_int(t->tm_isdst));
 %#ifdef HAVE_STRUCT_TM_GMTOFF
-    SCM_SIMPLE_VECTOR_SET(tm, 9, scm_from_long(t->tm_gmtoff));
-    SCM_SIMPLE_VECTOR_SET(tm, 10, scm_from_locale_string(t->tm_zone?t->tm_zone:"Unset"));
+        SCM_SIMPLE_VECTOR_SET(tm, 9, scm_from_long(t->tm_gmtoff));
+        SCM_SIMPLE_VECTOR_SET(tm, 10, scm_from_locale_string(t->tm_zone?t->tm_zone:"Unset"));
 %#else
-    SCM_SIMPLE_VECTOR_SET(tm, 9, scm_from_long(0));
-    SCM_SIMPLE_VECTOR_SET(tm, 10, scm_from_locale_string("GMT"));
+        SCM_SIMPLE_VECTOR_SET(tm, 9, scm_from_long(0));
+        SCM_SIMPLE_VECTOR_SET(tm, 10, scm_from_locale_string("GMT"));
 %#endif
+     }
     $result = tm;
  }
  
@@ -117,22 +121,25 @@ typedef char gchar;
 %typemap(argout) struct tm * {
     struct tm* t = $1;
     SCM tm = $input;
-    SCM_SIMPLE_VECTOR_SET(tm, 0, scm_from_int(t->tm_sec));
-    SCM_SIMPLE_VECTOR_SET(tm, 1, scm_from_int(t->tm_min));
-    SCM_SIMPLE_VECTOR_SET(tm, 2, scm_from_int(t->tm_hour));
-    SCM_SIMPLE_VECTOR_SET(tm, 3, scm_from_int(t->tm_mday));
-    SCM_SIMPLE_VECTOR_SET(tm, 4, scm_from_int(t->tm_mon));
-    SCM_SIMPLE_VECTOR_SET(tm, 5, scm_from_int(t->tm_year));
-    SCM_SIMPLE_VECTOR_SET(tm, 6, scm_from_int(t->tm_wday));
-    SCM_SIMPLE_VECTOR_SET(tm, 7, scm_from_int(t->tm_yday));
-    SCM_SIMPLE_VECTOR_SET(tm, 8, scm_from_int(t->tm_isdst));
+    if (t == NULL)
+    {
+        SCM_SIMPLE_VECTOR_SET(tm, 0, scm_from_int(t->tm_sec));
+        SCM_SIMPLE_VECTOR_SET(tm, 1, scm_from_int(t->tm_min));
+        SCM_SIMPLE_VECTOR_SET(tm, 2, scm_from_int(t->tm_hour));
+        SCM_SIMPLE_VECTOR_SET(tm, 3, scm_from_int(t->tm_mday));
+        SCM_SIMPLE_VECTOR_SET(tm, 4, scm_from_int(t->tm_mon));
+        SCM_SIMPLE_VECTOR_SET(tm, 5, scm_from_int(t->tm_year));
+        SCM_SIMPLE_VECTOR_SET(tm, 6, scm_from_int(t->tm_wday));
+        SCM_SIMPLE_VECTOR_SET(tm, 7, scm_from_int(t->tm_yday));
+        SCM_SIMPLE_VECTOR_SET(tm, 8, scm_from_int(t->tm_isdst));
 %#ifdef HAVE_STRUCT_TM_GMTOFF
-    SCM_SIMPLE_VECTOR_SET(tm, 9, scm_from_long(t->tm_gmtoff));
-    SCM_SIMPLE_VECTOR_SET(tm, 10, scm_from_locale_string(t->tm_zone?t->tm_zone:"Unset"));
+        SCM_SIMPLE_VECTOR_SET(tm, 9, scm_from_long(t->tm_gmtoff));
+        SCM_SIMPLE_VECTOR_SET(tm, 10, scm_from_locale_string(t->tm_zone?t->tm_zone:"Unset"));
 %#else
-    SCM_SIMPLE_VECTOR_SET(tm, 9, scm_from_long(0));
-    SCM_SIMPLE_VECTOR_SET(tm, 10, scm_from_locale_string("GMT"));
+        SCM_SIMPLE_VECTOR_SET(tm, 9, scm_from_long(0));
+        SCM_SIMPLE_VECTOR_SET(tm, 10, scm_from_locale_string("GMT"));
 %#endif
+     }
  }
 
 %define GLIST_HELPER_INOUT(ListType, ElemSwigType)
diff --git a/gnucash/gnome-utils/gnc-cell-renderer-date.c b/gnucash/gnome-utils/gnc-cell-renderer-date.c
index 210c04a..e47a386 100644
--- a/gnucash/gnome-utils/gnc-cell-renderer-date.c
+++ b/gnucash/gnome-utils/gnc-cell-renderer-date.c
@@ -437,7 +437,8 @@ gcrd_time2dmy (time64 raw_time, gint *day, gint *month, gint *year)
     struct tm * timeinfo;
   
     timeinfo = gnc_localtime (&raw_time);
- 
+    if (timeinfo == NULL)
+        return FALSE;
     *day = timeinfo->tm_mday;
     *month = timeinfo->tm_mon + 1;
     *year = timeinfo->tm_year + 1900;
diff --git a/gnucash/gnome/assistant-loan.c b/gnucash/gnome/assistant-loan.c
index 9ed3257..26370f5 100644
--- a/gnucash/gnome/assistant-loan.c
+++ b/gnucash/gnome/assistant-loan.c
@@ -1091,11 +1091,14 @@ loan_info_page_save( GtkAssistant *assistant, gpointer user_data )
 
         tmpTT = gnc_date_edit_get_date( ldd->prmStartDateGDE );
         tmpTm = gnc_localtime ( &tmpTT );
-        g_date_set_dmy( ldd->ld.startDate,
-                        tmpTm->tm_mday,
-                        (tmpTm->tm_mon + 1),
-                        (1900 + tmpTm->tm_year) );
-	gnc_tm_free (tmpTm);
+        if (tmpTm)
+        {
+            g_date_set_dmy( ldd->ld.startDate,
+                            tmpTm->tm_mday,
+                            (tmpTm->tm_mon + 1),
+                            (1900 + tmpTm->tm_year) );
+            gnc_tm_free (tmpTm);
+        }
     }
 
     /* len / periods */
diff --git a/libgnucash/engine/Transaction.c b/libgnucash/engine/Transaction.c
index 5fc7c7e..17e019e 100644
--- a/libgnucash/engine/Transaction.c
+++ b/libgnucash/engine/Transaction.c
@@ -2426,10 +2426,13 @@ xaccTransGetDatePostedGDate (const Transaction *trans)
               */
              time64 time = xaccTransGetDate(trans);
              struct tm *stm = gnc_gmtime(&time);
-             g_date_set_dmy(&result, stm->tm_mday,
-                            (GDateMonth)(stm->tm_mon + 1),
-                            stm->tm_year + 1900);
-             free(stm);
+             if (stm)
+             {
+                 g_date_set_dmy(&result, stm->tm_mday,
+                                (GDateMonth)(stm->tm_mon + 1),
+                                stm->tm_year + 1900);
+                 free(stm);
+             }
         }
     }
     return result;

commit 82f1384c5827114d44ba51eafabd8ae00590b3aa
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 23 17:31:18 2017 -0800

    Fix autotools test setup for test-date-utilities.

diff --git a/src/app-utils/test/Makefile.am b/src/app-utils/test/Makefile.am
index b78f046..085089d 100644
--- a/src/app-utils/test/Makefile.am
+++ b/src/app-utils/test/Makefile.am
@@ -4,8 +4,12 @@ TESTS = \
   test-exp-parser \
   test-scm-query-string \
   test-print-parse-amount \
-  test-date-utilities \
-  test-sx
+  test-sx \
+  $(SCM_TESTS)
+
+SCM_TESTS =   test-date-utilities
+
+SCM_TEST_SRCS = $(SCM_TESTS:%=%.scm)
 
 test_exp_parser_SOURCES = \
   test-exp-parser.c
@@ -18,6 +22,7 @@ GNC_TEST_DEPS = --gnc-module-dir ${top_builddir}/src/engine \
   --guile-load-dir ${top_builddir}/src/core-utils \
   --guile-load-dir ${top_builddir}/src/gnc-module \
   --guile-load-dir ${top_builddir}/src/engine \
+  --guile-load-dir ${top_builddir}/src/engine/test \
   --guile-load-dir ${top_builddir}/src/scm \
   --guile-load-dir ${top_builddir}/src/app-utils \
   --library-dir    ${top_builddir}/src/libqof/qof \
@@ -28,6 +33,12 @@ GNC_TEST_DEPS = --gnc-module-dir ${top_builddir}/src/engine \
   --library-dir    ${top_builddir}/src/backend/sql \
   --library-dir    ${top_builddir}/src/app-utils
 
+$(SCM_TESTS): %: $(srcdir)/%.scm Makefile .scm-links
+	echo 'export GNC_BUILDDIR="${abs_top_builddir}";' > $@
+	echo 'export GNC_UNINSTALLED=yes;' >> $@
+	echo '${GUILE} --debug -l $(srcdir)/$*.scm -c "(exit (run-test))"' >> $@
+	chmod a+x $@
+
 TESTS_ENVIRONMENT = \
   GUILE_WARN_DEPRECATED=no \
   GUILE="${GUILE}" \
@@ -68,3 +79,20 @@ AM_CPPFLAGS = \
   -I${top_srcdir}/src/libqof/qof \
   ${GUILE_CFLAGS} \
   ${GLIB_CFLAGS}
+
+.scm-links:
+	$(RM) -rf gnucash
+	mkdir -p  gnucash/app-utils/test
+	( cd gnucash/app-utils/test; for A in $(SCM_TEST_HELPERS) ; do $(LN_S) -f $(abs_srcdir)/$$A . ; done )
+if ! OS_WIN32
+# Windows knows no "ln -s" but uses "cp": must copy every time (see bug #566567).
+	touch .scm-links
+endif
+
+clean-local:
+	$(RM) -rf gnucash
+	$(RM) *.log
+
+noinst_DATA = .scm-links
+CLEANFILES = .scm-links
+DISTCLEANFILES = $(SCM_TESTS)

commit 40654cf50cbc03973d245059f98da0f938fe5ba5
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 23 17:17:48 2017 -0800

    Fix picky gcc-7.2 complaint about a %d conversion not fitting in 3 bytes.

diff --git a/src/gnome-utils/gnc-dense-cal.c b/src/gnome-utils/gnc-dense-cal.c
index 596a0ae..564ddcd 100644
--- a/src/gnome-utils/gnc-dense-cal.c
+++ b/src/gnome-utils/gnc-dense-cal.c
@@ -1104,7 +1104,7 @@ gnc_dense_cal_draw_to_buffer(GncDenseCal *dcal)
         {
             doc_coords(dcal, doc, &x1, &y1, &x2, &y2);
             memset(dayNumBuf, 0, 3);
-            snprintf(dayNumBuf, 3, "%d", g_date_get_day(&d));
+            snprintf(dayNumBuf, 3, "%hhu", g_date_get_day(&d));
             pango_layout_set_text(layout, dayNumBuf, -1);
             pango_layout_get_pixel_size(layout, &numW, &numH);
             w = (x2 - x1) + 1;

commit 44c51f433b0a07e44d1485cac5c16537031703ba
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 23 16:42:43 2017 -0800

    Bug 791848 - GC 2.6.x does not handle ISO dates introduced with GC 2.7
    
    Enable reading ISO-formatted dates, recognize GNC_FEATURE_SQLITE3_ISO_DATES.

diff --git a/src/backend/sql/gnc-backend-sql.c b/src/backend/sql/gnc-backend-sql.c
index 7d169d1..76aa181 100644
--- a/src/backend/sql/gnc-backend-sql.c
+++ b/src/backend/sql/gnc-backend-sql.c
@@ -1855,7 +1855,9 @@ typedef Timespec (*TimespecAccessFunc)( const gpointer );
 typedef void (*TimespecSetterFunc)( const gpointer, Timespec );
 
 #define TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d"
+#define TIMESPEC_ALT_FORMAT "%04d-%02d%-02d %02d:%02d:%02d"
 #define TIMESPEC_COL_SIZE (4+2+2+2+2+2)
+#define TIMESPEC_ALT_COL_SIZE (4+3+3+3+3+3)
 
 /* This is required because we're passing be->timespace_format to
  * g_strdup_printf.
@@ -1926,22 +1928,24 @@ load_timespec( const GncSqlBackend* be, GncSqlRow* row,
 	else if (G_VALUE_HOLDS_STRING (val))
 	{
             const gchar* s = g_value_get_string( val );
-            if ( s != NULL )
+            if ( s != NULL && strlen(s) == TIMESPEC_COL_SIZE)
+            {
+                gchar* buf;
+                buf = g_strdup_printf( "%c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c",
+                                       s[0], s[1], s[2], s[3],
+                                       s[4], s[5],
+                                       s[6], s[7],
+                                       s[8], s[9],
+                                       s[10], s[11],
+                                       s[12], s[13] );
+                ts = gnc_iso8601_to_timespec_gmt( buf );
+                g_free( buf );
+                isOK = TRUE;
+            }
+            else if (! g_str_has_prefix (s, "0000-00-00"))
             {
-                 if (! g_str_has_prefix (s, "0000-00-00"))
-                 {
-                      gchar* buf;
-                      buf = g_strdup_printf( "%c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c",
-                                             s[0], s[1], s[2], s[3],
-                                             s[4], s[5],
-                                             s[6], s[7],
-                                             s[8], s[9],
-                                             s[10], s[11],
-                                             s[12], s[13] );
-                      ts = gnc_iso8601_to_timespec_gmt( buf );
-                      g_free( buf );
-                 }
-                 isOK = TRUE;
+                ts = gnc_iso8601_to_timespec_gmt (s);
+                isOK = TRUE;
             }
         }
         else
diff --git a/src/core-utils/gnc-features.c b/src/core-utils/gnc-features.c
index 1cc495a..03c9418 100644
--- a/src/core-utils/gnc-features.c
+++ b/src/core-utils/gnc-features.c
@@ -46,6 +46,7 @@ static gncFeature known_features[] =
     { GNC_FEATURE_KVP_EXTRA_DATA, "Extra data for addresses, jobs or invoice entries (requires at least GnuCash 2.6.4)" },
     { GNC_FEATURE_GUID_BAYESIAN, "Use account GUID as key for Bayesian data (requires at least GnuCash 2.6.12)" },
     { GNC_FEATURE_GUID_FLAT_BAYESIAN, "Use account GUID as key for bayesian data and store KVP flat (requires at least GnuCash 2.6.19)" },
+    { GNC_FEATURE_SQLITE3_ISO_DATES, "Use ISO formatted date-time strings in SQLite3 databases (requires at least GnuCash 2.6.20)"},
     { NULL },
 };
 
diff --git a/src/core-utils/gnc-features.h b/src/core-utils/gnc-features.h
index a7ed421..df4b590 100644
--- a/src/core-utils/gnc-features.h
+++ b/src/core-utils/gnc-features.h
@@ -46,6 +46,7 @@
 #define GNC_FEATURE_KVP_EXTRA_DATA "Extra data in addresses, jobs or invoice entries"
 #define GNC_FEATURE_GUID_BAYESIAN "Account GUID based Bayesian data"
 #define GNC_FEATURE_GUID_FLAT_BAYESIAN "Account GUID based bayesian with flat KVP"
+#define GNC_FEATURE_SQLITE3_ISO_DATES "ISO-8601 formatted date strings in SQLite3 databases."
 
 /** @} */
 

commit a17bc85a022339dad4fa125257b86f1878cb0b36
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 23 14:25:15 2017 -0800

    Enable reading undelimited YYYYMMDDHHMMSS time strings.
    
    For backwards compatibility with 2.4 and 2.6 SQLite3 databases.

diff --git a/libgnucash/engine/gnc-datetime.cpp b/libgnucash/engine/gnc-datetime.cpp
index 5af0683..2bec648 100644
--- a/libgnucash/engine/gnc-datetime.cpp
+++ b/libgnucash/engine/gnc-datetime.cpp
@@ -314,8 +314,15 @@ GncDateTimeImpl::GncDateTimeImpl(const std::string str) :
         input_facet->set_iso_extended_format();
         PTime pdt(not_a_date_time);
         ss >> pdt;
+        if (pdt.is_special())
+        {
+            input_facet->format("%Y%m%d%H%M%S");
+            ss.clear(); //Reset to the beginning.
+            ss.seekg(0);
+            ss >> pdt;
+        }
         m_time = LDT(pdt.date(), pdt.time_of_day(), tzptr,
-                     LDTBase::NOT_DATE_TIME_ON_ERROR);
+                         LDTBase::NOT_DATE_TIME_ON_ERROR);
     }
     catch(boost::gregorian::bad_year)
     {

commit aeb62724e57df9c7b801334a9e69eedba2f3be21
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 24 00:25:59 2017 +0800

    REFACTOR: gnc-numeric not available in scheme anymore

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index df153b2..f6654df 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1097,16 +1097,16 @@ tags within description, notes or memo. ")
                        (timespecCanonicalDayTime trans-date))))
            (split-value (lambda (s) (convert s (damount s)))) ; used for correct debit/credit
            (amount (lambda (s) (split-value s)))
-           (debit-amount (lambda (s) (and (gnc-numeric-positive-p (gnc:gnc-monetary-amount (split-value s)))
+           (debit-amount (lambda (s) (and (positive? (gnc:gnc-monetary-amount (split-value s)))
                                           (split-value s))))
-           (credit-amount (lambda (s) (if (gnc-numeric-positive-p (gnc:gnc-monetary-amount (split-value s)))
+           (credit-amount (lambda (s) (if (positive? (gnc:gnc-monetary-amount (split-value s)))
                                           #f
                                           (gnc:monetary-neg (split-value s)))))
            (original-amount (lambda (s) (gnc:make-gnc-monetary (currency s) (damount s))))
-           (original-debit-amount (lambda (s) (if (gnc-numeric-positive-p (damount s))
+           (original-debit-amount (lambda (s) (if (positive? (damount s))
                                                   (original-amount s)
                                                   #f)))
-           (original-credit-amount (lambda (s) (if (gnc-numeric-positive-p (damount s))
+           (original-credit-amount (lambda (s) (if (positive? (damount s))
                                                    #f
                                                    (gnc:monetary-neg (original-amount s)))))
            (running-balance (lambda (s) (gnc:make-gnc-monetary (currency s) (xaccSplitGetBalance s)))))
@@ -1117,7 +1117,7 @@ tags within description, notes or memo. ")
          ;;         reverse-column?                              ;; to optionally reverse signs
          ;;         subtotal?                                    ;; subtotal? to allow subtotals (ie irrelevant for running balance)
          ;;         (vector start-dual-column?                   ;; #t for the left side of a dual column (i.e. debit/credit)
-         ;;                 merging-function))                   ;; function to apply to dual-subtotal (gnc-numeric-add/sub)
+         ;;                 merging-function))                   ;; function to apply to dual-subtotal (+ / -)
          ;;         friendly-heading-fn                          ;; retrieve friendly heading name for account debit/credit
          (if (column-uses? 'amount-single)
              (list (vector (header-commodity (_ "Amount"))
@@ -1128,11 +1128,11 @@ tags within description, notes or memo. ")
          (if (column-uses? 'amount-double)
              (list (vector (header-commodity (_ "Debit"))
                            debit-amount #f #t
-                           (vector #t gnc-numeric-add)
+                           (vector #t +)
                            friendly-debit)
                    (vector (header-commodity (_ "Credit"))
                            credit-amount #f #t
-                           (vector #f gnc-numeric-sub)
+                           (vector #f -)
                            friendly-credit))
              '())
 
@@ -1148,11 +1148,11 @@ tags within description, notes or memo. ")
                   (column-uses? 'amount-double))
              (list (vector (_ "Debit")
                            original-debit-amount #f #t
-                           (vector #t gnc-numeric-add)
+                           (vector #t +)
                            friendly-debit)
                    (vector (_ "Credit")
                            original-credit-amount #f #t
-                           (vector #f gnc-numeric-sub)
+                           (vector #f -)
                            friendly-credit))
              '())
 
@@ -1254,20 +1254,19 @@ tags within description, notes or memo. ")
 
         (define (add-columns commodity)
           (let ((start-dual-column? #f)
-                (dual-subtotal (gnc:make-gnc-numeric 0 1)))
+                (dual-subtotal 0))
             (for-each (lambda (column merge-entry)
                         (let* ((mon (retrieve-commodity column commodity))
                                (column-amount (and mon (gnc:gnc-monetary-amount mon)))
                                (merge? (vector-ref merge-entry 0))
                                (merge-fn (vector-ref merge-entry 1)))
                           (if merge?
-                              ;; We're merging. Run merge-fn (usu gnc-numeric-add or sub)
+                              ;; We're merging. Run merge-fn (usu + or -)
                               ;; and store total in dual-subtotal. Do NOT add column.
                               (begin
                                 (if column-amount
                                     (set! dual-subtotal
-                                          (merge-fn dual-subtotal column-amount
-                                                    GNC-DENOM-AUTO GNC-HOW-RND-ROUND)))
+                                          (merge-fn dual-subtotal column-amount)))
                                 (set! start-dual-column? #t))
                               (if start-dual-column?
                                   (begin
@@ -1275,9 +1274,8 @@ tags within description, notes or memo. ")
                                     ;; and add the columns.
                                     (if column-amount
                                         (set! dual-subtotal
-                                              (merge-fn dual-subtotal column-amount
-                                                        GNC-DENOM-AUTO GNC-HOW-RND-ROUND)))
-                                    (if (gnc-numeric-positive-p dual-subtotal)
+                                              (merge-fn dual-subtotal column-amount)))
+                                    (if (positive? dual-subtotal)
                                         (begin
                                           (addto! row-contents
                                                   (gnc:make-html-table-cell/markup
@@ -1291,9 +1289,9 @@ tags within description, notes or memo. ")
                                                    "total-number-cell"
                                                    (gnc:make-gnc-monetary
                                                     commodity
-                                                    (gnc-numeric-neg dual-subtotal))))))
+                                                    (- dual-subtotal))))))
                                     (set! start-dual-column? #f)
-                                    (set! dual-subtotal (gnc:make-gnc-numeric 0 1)))
+                                    (set! dual-subtotal 0))
                                   ;; Default; not merging/completed merge. Just
                                   ;; display monetary amount
                                   (addto! row-contents
@@ -1689,7 +1687,7 @@ tags within description, notes or memo. ")
               ((corresponding-acc-code) (lambda (s) (xaccSplitGetCorrAccountCode s)))
               ((reconciled-status) (lambda (s) (length (memq (xaccSplitGetReconcile s)
                                                              '(#\n #\c #\y #\f #\v)))))
-              ((amount) (lambda (s) (gnc-numeric-to-double (xaccSplitGetValue s))))
+              ((amount) (lambda (s) (gnc-numeric-to-scm (xaccSplitGetValue s))))
               ((description) (lambda (s) (xaccTransGetDescription (xaccSplitGetParent s))))
               ((number) (lambda (s)
                           (if BOOK-SPLIT-ACTION

commit e6dcc0cc1eea910d1dbad29725825954bc14dd73
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Dec 5 10:28:33 2017 +0800

    ENH: Optionally hide transactions
    
    This will hide the subheadings and the transactional data, only rendering the subtotals.
    May be useful e.g. for daily income and daily expense reports.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index e49deab..df153b2 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -74,6 +74,7 @@
 (define optname-show-account-code (N_ "Show Account Code"))
 (define optname-show-account-description (N_ "Show Account Description"))
 (define optname-show-informal-headers (N_ "Show Informal Debit/Credit Headers"))
+(define optname-show-subtotals-only (N_ "Show subtotals only (hide transactional data)"))
 (define optname-indenting (N_ "Add indenting columns"))
 (define optname-sec-sortkey (N_ "Secondary Key"))
 (define optname-sec-subtotal (N_ "Secondary Subtotal"))
@@ -612,6 +613,11 @@ tags within description, notes or memo. ")
              (and sec-sortkey-subtotal-enabled sec-sortkey-subtotal-true)))
 
         (gnc-option-db-set-option-selectable-by-name
+         options pagename-sorting optname-show-subtotals-only
+         (or (and prime-sortkey-subtotal-enabled prime-sortkey-subtotal-true)
+             (and sec-sortkey-subtotal-enabled sec-sortkey-subtotal-true)))
+
+        (gnc-option-db-set-option-selectable-by-name
          options pagename-sorting optname-show-informal-headers
          (or (member prime-sortkey (list 'account-name 'account-code))
              (member sec-sortkey (list 'account-name 'account-code))))
@@ -671,6 +677,13 @@ tags within description, notes or memo. ")
       #t))
 
     (gnc:register-trep-option
+     (gnc:make-simple-boolean-option
+      pagename-sorting optname-show-subtotals-only
+      "j6"
+      (_ "Show subtotals only, hiding transactional detail?")
+      #f))
+
+    (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       pagename-sorting optname-prime-subtotal
       "e5"
@@ -896,6 +909,9 @@ tags within description, notes or memo. ")
                 (and (opt-val gnc:pagename-general optname-common-currency)
                      (opt-val gnc:pagename-general optname-orig-currency)))
           (cons 'indenting (opt-val pagename-sorting optname-indenting))
+          (cons 'subtotals-only (and (opt-val pagename-sorting optname-show-subtotals-only)
+                                     (or (primary-get-info 'renderer-fn)
+                                         (secondary-get-info 'renderer-fn))))
           (cons 'running-balance (opt-val gnc:pagename-display (N_ "Running Balance")))
           (cons 'account-full-name (opt-val gnc:pagename-display (N_ "Use Full Account Name")))
           (cons 'memo (opt-val gnc:pagename-display (N_ "Memo")))
@@ -1206,7 +1222,8 @@ tags within description, notes or memo. ")
                         calculated-cells))
             (addto! row-contents (gnc:make-html-table-cell/size
                                     1 (+ right-indent width-left-columns width-right-columns) data)))
-        (gnc:html-table-append-row/markup! table subheading-style (reverse row-contents))))
+        (if (not (column-uses? 'subtotals-only))
+            (gnc:html-table-append-row/markup! table subheading-style (reverse row-contents)))))
 
     (define (add-subtotal-row subtotal-string subtotal-collectors subtotal-style level)
       (let* ((row-contents '())
@@ -1434,7 +1451,8 @@ tags within description, notes or memo. ")
                           (addto! row-contents (gnc:html-make-empty-cell)))))
                   cells)
 
-        (gnc:html-table-append-row/markup! table row-style (reverse row-contents))
+        (if (not (column-uses? 'subtotals-only))
+            (gnc:html-table-append-row/markup! table row-style (reverse row-contents)))
 
         (map (lambda (cell)
                (let ((cell-content (vector-ref cell 0))

commit 93b17214f39e5355d7169ad0887e2bd879f78529
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Dec 13 22:17:10 2017 +0800

    ENH: Add 'daily subtotal strategy
    
    This may be useful for 'total daily report'. e.g. total expenses per day. Perhaps more useful combined with 'hide transactional data' as the next commit.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 28b2af5..e49deab 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -244,6 +244,8 @@ options specified in the Options panels."))
 (define (time64-quarter t64) (+ (* 10 (gnc:date-get-year (gnc-localtime t64)))  (gnc:date-get-quarter (gnc-localtime t64))))
 (define (time64-month t64)   (+ (* 100 (gnc:date-get-year (gnc-localtime t64))) (gnc:date-get-month (gnc-localtime t64))))
 (define (time64-week t64)    (gnc:date-get-week (gnc-localtime t64)))
+(define (time64-day t64)     (+ (* 500 (gnc:date-get-year (gnc-localtime t64))) (gnc:date-get-year-day (gnc-localtime t64))))
+(define (time64->daily-string t) (qof-print-date t))
 (define (split->time64 s) (xaccTransGetDate (xaccSplitGetParent s)))
 
 (define date-subtotal-list
@@ -262,6 +264,12 @@ options specified in the Options panels."))
                 (cons 'tip (_ "None."))
                 (cons 'renderer-fn #f)))
 
+   (cons 'daily (list
+                  (cons 'split-sortvalue (lambda (s) (time64-day (split->time64 s))))
+                  (cons 'text (_ "Daily"))
+                  (cons 'tip (_ "Daily."))
+                  (cons 'renderer-fn (lambda (s) (time64->daily-string (split->time64 s))))))
+
    (cons 'weekly (list
                   (cons 'split-sortvalue (lambda (s) (time64-week (split->time64 s))))
                   (cons 'text (_ "Weekly"))
@@ -1654,6 +1662,7 @@ tags within description, notes or memo. ")
                 ((monthly)   (lambda (s) (time64-month (date s))))
                 ((quarterly) (lambda (s) (time64-quarter (date s))))
                 ((weekly)    (lambda (s) (time64-week (date s))))
+                ((daily)     (lambda (s) (time64-day (date s))))
                 ((none)      (lambda (s) (date s)))))
             (case key
               ((account-name) (lambda (s) (gnc-account-get-full-name (xaccSplitGetAccount s))))

commit 1ea1bcb38ebc8ad2dc2f024b7f8116a1be7eeefb
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Dec 2 17:46:31 2017 +0800

    ENH: Formalise Reconciliation Report as a new menu item.
    
    This commit offers a new item with defaults appropriate for a reconcilation report.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 382bc29..28b2af5 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -17,6 +17,7 @@
 ;; - add informational box, summarising options used, useful
 ;;   to troubleshoot reports
 ;; - add support for indenting for better grouping
+;; - add defaults suitable for a reconciliation report
 ;;
 ;; This program is free software; you can redistribute it and/or    
 ;; modify it under the terms of the GNU General Public License as   
@@ -384,6 +385,24 @@ Credit Card, and Income accounts."))
    keylist))
 
 
+;;
+;; Set defaults for reconcilation report
+;;
+(define (reconcile-report-options-generator)
+  (define options (trep-options-generator))
+  (gnc:option-set-value (gnc:lookup-option options pagename-sorting optname-prime-sortkey) 'reconciled-status)
+  (gnc:option-set-value (gnc:lookup-option options pagename-sorting optname-sec-sortkey)   'date)
+  (gnc:option-set-value (gnc:lookup-option options pagename-sorting optname-sec-date-subtotal) 'none)
+  (gnc:option-set-value (gnc:lookup-option options gnc:pagename-general optname-startdate) (cons 'relative 'start-prev-quarter))
+  (gnc:option-set-value (gnc:lookup-option options gnc:pagename-general optname-enddate)   (cons 'relative 'today))
+  (gnc:option-set-value (gnc:lookup-option options gnc:pagename-display (N_ "Reconciled Date")) #t)
+  (gnc:option-set-value (gnc:lookup-option options gnc:pagename-display (N_ "Running Balance")) #t)
+  (gnc:option-set-value (gnc:lookup-option options gnc:pagename-display (N_ "Memo")) #f)
+  options)
+
+;;
+;; Default Transaction Report
+;;
 (define (trep-options-generator)
 
   (define options (gnc:new-options))
@@ -1865,11 +1884,16 @@ tags within description, notes or memo. ")
 
 ;; Define the report.
 (gnc:define-report
-
  'version 1
+ 'name (_ "Reconciliation Report")
+ 'report-guid "e45218c6d76f11e7b5ef0800277ef320"
+ 'options-generator reconcile-report-options-generator
+ 'renderer trep-renderer)
 
+;; Define the report.
+(gnc:define-report
+ 'version 1
  'name reportname
  'report-guid "2fe3b9833af044abb929a88d5a59620f"
-
  'options-generator trep-options-generator
  'renderer trep-renderer)

commit 7a5f2ed49bc4c7986d0f49f268b5d74d88199915
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Nov 30 22:37:32 2017 +0800

    ENH: Add indenting for main data and subheadings/subtotals
    
    Adds between 0-2 empty columns to the left, depending on subtotal strategy.
    Option toggle added to Sorting tab

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index bafafde..382bc29 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -16,6 +16,7 @@
 ;;   and enable multiple data columns
 ;; - add informational box, summarising options used, useful
 ;;   to troubleshoot reports
+;; - add support for indenting for better grouping
 ;;
 ;; This program is free software; you can redistribute it and/or    
 ;; modify it under the terms of the GNU General Public License as   
@@ -72,6 +73,7 @@
 (define optname-show-account-code (N_ "Show Account Code"))
 (define optname-show-account-description (N_ "Show Account Description"))
 (define optname-show-informal-headers (N_ "Show Informal Debit/Credit Headers"))
+(define optname-indenting (N_ "Add indenting columns"))
 (define optname-sec-sortkey (N_ "Secondary Key"))
 (define optname-sec-subtotal (N_ "Secondary Subtotal"))
 (define optname-sec-sortorder  (N_ "Secondary Sort Order"))
@@ -264,7 +266,7 @@ options specified in the Options panels."))
                   (cons 'text (_ "Weekly"))
                   (cons 'tip (_ "Weekly."))
                   (cons 'renderer-fn (lambda (s) (gnc:date-get-week-year-string (gnc-localtime (split->time64 s)))))))
-   
+
    (cons 'monthly (list
                    (cons 'split-sortvalue (lambda (s) (time64-month (split->time64 s))))
                    (cons 'text (_ "Monthly"))
@@ -578,10 +580,15 @@ tags within description, notes or memo. ")
              (and sec-sortkey-subtotal-enabled sec-sortkey-subtotal-true)))
 
         (gnc-option-db-set-option-selectable-by-name
+         options pagename-sorting optname-indenting
+         (or (and prime-sortkey-subtotal-enabled prime-sortkey-subtotal-true)
+             (and sec-sortkey-subtotal-enabled sec-sortkey-subtotal-true)))
+
+        (gnc-option-db-set-option-selectable-by-name
          options pagename-sorting optname-show-informal-headers
          (or (member prime-sortkey (list 'account-name 'account-code))
              (member sec-sortkey (list 'account-name 'account-code))))
-        
+
         (gnc-option-db-set-option-selectable-by-name
          options pagename-sorting optname-prime-date-subtotal
          prime-date-sortingtype-enabled)
@@ -622,7 +629,6 @@ tags within description, notes or memo. ")
       (_ "Show the account description for subheadings?")
       #f))
 
-
     (gnc:register-trep-option
      (gnc:make-simple-boolean-option
       pagename-sorting optname-show-informal-headers
@@ -631,6 +637,13 @@ tags within description, notes or memo. ")
       #f))
 
     (gnc:register-trep-option
+     (gnc:make-simple-boolean-option
+      pagename-sorting optname-indenting
+      "j5"
+      (_ "Add indenting columns with grouping and subtotals?")
+      #t))
+
+    (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       pagename-sorting optname-prime-subtotal
       "e5"
@@ -855,6 +868,7 @@ tags within description, notes or memo. ")
           (cons 'amount-original-currency
                 (and (opt-val gnc:pagename-general optname-common-currency)
                      (opt-val gnc:pagename-general optname-orig-currency)))
+          (cons 'indenting (opt-val pagename-sorting optname-indenting))
           (cons 'running-balance (opt-val gnc:pagename-display (N_ "Running Balance")))
           (cons 'account-full-name (opt-val gnc:pagename-display (N_ "Use Full Account Name")))
           (cons 'memo (opt-val gnc:pagename-display (N_ "Memo")))
@@ -1119,36 +1133,61 @@ tags within description, notes or memo. ")
     (define width-left-columns (length left-columns))
     (define width-right-columns (length calculated-cells))
 
+    (define primary-indent
+      (if (and (column-uses? 'indenting)
+               (primary-get-info 'renderer-fn))
+          1 0))
+
+    (define secondary-indent
+      (if (and (column-uses? 'indenting)
+               (secondary-get-info 'renderer-fn))
+          1 0))
+
+    (define indent-level
+      (+ primary-indent secondary-indent))
+
+
     (define (add-subheading data subheading-style split level)
-      (let ((sortkey (opt-val pagename-sorting
-                           (case level
-                             ((primary) optname-prime-sortkey)
-                             ((secondary) optname-sec-sortkey)))))
+      (let* ((row-contents '())
+             (sortkey (opt-val pagename-sorting
+                               (case level
+                                 ((primary) optname-prime-sortkey)
+                                 ((secondary) optname-sec-sortkey))))
+             (left-indent (case level
+                            ((primary total) 0)
+                            ((secondary) primary-indent)))
+             (right-indent (- indent-level left-indent)))
+        (for-each (lambda (cell) (addto! row-contents cell))
+                  (gnc:html-make-empty-cells left-indent))
         (if (and (opt-val pagename-sorting optname-show-informal-headers)
                  (member sortkey SORTKEY-INFORMAL-HEADERS))
-            (let ((row-contents '()))
-              (begin
-                (if export?
-                    (begin (addto! row-contents (gnc:make-html-table-cell subheading-style data))
-                           (for-each (lambda (cell) (addto! row-contents cell))
-                                     (gnc:html-make-empty-cells (- width-left-columns 1))))
-                    (addto! row-contents (gnc:make-html-table-cell/size 1 width-left-columns data)))
-                (map (lambda (col)
-                       (addto! row-contents
-                               (gnc:make-html-table-cell
-                                "<b>"
-                                ((vector-ref col 5)
-                                 ((keylist-get-info sortkey-list sortkey 'renderer-fn) split))
-                                "</b>")))
-                     calculated-cells)
-                (gnc:html-table-append-row/markup! table subheading-style (reverse row-contents))))
-            (let ((heading-cell (gnc:make-html-table-cell data)))
-              (gnc:html-table-cell-set-colspan! heading-cell (+ width-left-columns width-right-columns))
-              (gnc:html-table-append-row/markup!
-               table subheading-style (list heading-cell))))))
-
-    (define (add-subtotal-row subtotal-string subtotal-collectors subtotal-style)
+            (begin
+              (if export?
+                  (begin
+                    (addto! row-contents (gnc:make-html-table-cell data))
+                    (for-each (lambda (cell) (addto! row-contents cell))
+                              (gnc:html-make-empty-cells (+ right-indent width-left-columns -1))))
+                  (addto! row-contents (gnc:make-html-table-cell/size
+                                        1 (+ right-indent width-left-columns) data)))
+              (for-each (lambda (cell)
+                          (addto! row-contents
+                                  (gnc:make-html-table-cell
+                                   "<b>"
+                                   ((vector-ref cell 5)
+                                    ((keylist-get-info sortkey-list sortkey 'renderer-fn) split))
+                                   "</b>")))
+                        calculated-cells))
+            (addto! row-contents (gnc:make-html-table-cell/size
+                                    1 (+ right-indent width-left-columns width-right-columns) data)))
+        (gnc:html-table-append-row/markup! table subheading-style (reverse row-contents))))
+
+    (define (add-subtotal-row subtotal-string subtotal-collectors subtotal-style level)
       (let* ((row-contents '())
+             (left-indent (case level
+                            ((total) 0)
+                            ((primary) primary-indent)
+                            ((secondary) (+ primary-indent secondary-indent))))
+             (right-indent (- indent-level left-indent))
              (merge-list (map (lambda (cell) (vector-ref cell 4)) calculated-cells))
              (columns (map (lambda (coll) (coll 'format gnc:make-gnc-monetary #f)) subtotal-collectors))
              (list-of-commodities (delete-duplicates (map gnc:gnc-monetary-commodity (concatenate columns))
@@ -1166,8 +1205,8 @@ tags within description, notes or memo. ")
               (begin
                 (addto! row-contents (gnc:make-html-table-cell/markup "total-label-cell" string))
                 (for-each (lambda (cell) (addto! row-contents cell))
-                          (gnc:html-make-empty-cells (- width-left-columns 1))))
-              (addto! row-contents (gnc:make-html-table-cell/size/markup 1 width-left-columns "total-label-cell" string))))
+                          (gnc:html-make-empty-cells (+ right-indent width-left-columns -1))))
+              (addto! row-contents (gnc:make-html-table-cell/size/markup 1 (+ right-indent width-left-columns) "total-label-cell" string))))
 
         (define (add-columns commodity)
           (let ((start-dual-column? #f)
@@ -1219,6 +1258,8 @@ tags within description, notes or memo. ")
                       merge-list)))
 
         ;;first row
+        (for-each (lambda (cell) (addto! row-contents cell))
+                  (gnc:html-make-empty-cells left-indent))
         (add-first-column subtotal-string)
         (add-columns (if (pair? list-of-commodities)
                          (car list-of-commodities)
@@ -1229,6 +1270,8 @@ tags within description, notes or memo. ")
         (if (pair? list-of-commodities)
             (for-each (lambda (commodity)
                         (set! row-contents '())
+                        (for-each (lambda (cell) (addto! row-contents cell))
+                                  (gnc:html-make-empty-cells left-indent))
                         (add-first-column "")
                         (add-columns commodity)
                         (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents)))
@@ -1301,7 +1344,7 @@ tags within description, notes or memo. ")
           (render-account sortkey split anchor?))
          ((eq? sortkey 'reconciled-status)
           (render-generic sortkey split)))))
-    
+
     (define (render-grand-total)
       (_ "Grand Total"))
 
@@ -1333,7 +1376,10 @@ tags within description, notes or memo. ")
                            reverse?
                            subtotal?)))
                cell-calculators))
-        
+
+        (for-each (lambda (cell) (addto! row-contents cell))
+                  (gnc:html-make-empty-cells indent-level))
+
         (for-each (lambda (col)
                     (addto! row-contents col))
                   left-cols)
@@ -1400,10 +1446,10 @@ tags within description, notes or memo. ")
                  table def:grand-total-style
                  (list
                   (gnc:make-html-table-cell/size
-                   1 (+ width-left-columns width-right-columns)
+                   1 (+ indent-level width-left-columns width-right-columns)
                    (gnc:make-html-text (gnc:html-markup-hr)))))
 
-                (add-subtotal-row (render-grand-total) total-collectors def:grand-total-style)))
+                (add-subtotal-row (render-grand-total) total-collectors def:grand-total-style 'total)))
 
           (let* ((current (car splits))
                  (rest (cdr splits))
@@ -1453,13 +1499,15 @@ tags within description, notes or memo. ")
                         (add-subtotal-row (total-string
                                            (render-summary current 'secondary #f))
                                           secondary-subtotal-collectors
-                                          def:secondary-subtotal-style)
+                                          def:secondary-subtotal-style
+                                          'secondary)
                         (for-each (lambda (coll) (coll 'reset #f #f))
                                   secondary-subtotal-collectors)))
                   (add-subtotal-row (total-string
                                      (render-summary current 'primary #f))
                                     primary-subtotal-collectors
-                                    def:primary-subtotal-style)
+                                    def:primary-subtotal-style
+                                    'primary)
                   (for-each (lambda (coll) (coll 'reset #f #f))
                             primary-subtotal-collectors)
                   (if next
@@ -1478,7 +1526,8 @@ tags within description, notes or memo. ")
                     (begin (add-subtotal-row (total-string
                                               (render-summary current 'secondary #f))
                                              secondary-subtotal-collectors
-                                             def:secondary-subtotal-style)
+                                             def:secondary-subtotal-style
+                                             'secondary)
                            (for-each (lambda (coll) (coll 'reset #f #f))
                                      secondary-subtotal-collectors)
                            (if next
@@ -1487,7 +1536,10 @@ tags within description, notes or memo. ")
 
             (do-rows-with-subtotals rest (not odd-row?)))))
 
-    (gnc:html-table-set-col-headers! table (concatenate (list headings-left-columns headings-right-columns)))
+    (gnc:html-table-set-col-headers! table (concatenate (list
+                                                         (gnc:html-make-empty-cells indent-level)
+                                                         headings-left-columns
+                                                         headings-right-columns)))
 
     (if (primary-get-info 'renderer-fn)
         (add-subheading (render-summary (car splits) 'primary #t)

commit 408f609a58225426e83bbc41edb9150fb258c9dc
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Nov 30 17:04:58 2017 +0800

    ENH: Add sortkey Reconciled Status
    
    Can be useful for reconcilation report.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index e2bb438..bafafde 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -116,7 +116,12 @@ options specified in the Options panels."))
 ;; The option-values of the sorting key multichoice option, for
 ;; which a subtotal should be enabled.
 (define SUBTOTAL-ENABLED (list 'account-name 'corresponding-acc-name
-                               'account-code 'corresponding-acc-code))
+                               'account-code 'corresponding-acc-code
+                               'reconciled-status))
+
+(define ACCOUNT-SORTING-TYPES (list 'account-name 'corresponding-acc-name
+                                    'account-code 'corresponding-acc-code))
+(define CUSTOM-SORTING (list 'reconciled-status))
 
 (define SORTKEY-INFORMAL-HEADERS (list 'account-name 'account-code))
 
@@ -132,6 +137,7 @@ options specified in the Options panels."))
   ;;       behaviour varies according to sortkey.
   ;;       account-types converts split->account
   ;;       #f means the sortkey cannot be subtotalled
+  ;;       otherwise it converts split->string
   ;;
   (list (cons 'account-name  (list (cons 'sortkey (list SPLIT-ACCT-FULLNAME))
                                    (cons 'split-sortvalue (lambda (a) (gnc-account-get-full-name (xaccSplitGetAccount a))))
@@ -157,6 +163,19 @@ options specified in the Options panels."))
                                      (cons 'tip (_ "Sort by the Reconciled Date."))
                                      (cons 'renderer-fn #f)))
 
+        (cons 'reconciled-status (list (cons 'sortkey #f)
+                                       (cons 'split-sortvalue (lambda (s) (length (memq (xaccSplitGetReconcile s)
+                                                                                        '(#\n #\c #\y #\f #\v)))))
+                                       (cons 'text (_ "Reconciled Status"))
+                                       (cons 'tip (_ "Sort by the Reconciled Status"))
+                                       (cons 'renderer-fn (lambda (s) (case (xaccSplitGetReconcile s)
+                                                                        ((#\y) (_ "Reconciled"))
+                                                                        ((#\c) (_ "Cleared"))
+                                                                        ((#\n) (_ "Unreconciled"))
+                                                                        ((#\f) (_ "Frozen"))
+                                                                        ((#\v) (_ "Voided"))
+                                                                        (else (_ "Unknown")))))))
+
         (cons 'register-order (list (cons 'sortkey (list QUERY-DEFAULT-SORT))
                                     (cons 'split-sortvalue #f)
                                     (cons 'text (_ "Register Order"))
@@ -1262,6 +1281,10 @@ tags within description, notes or memo. ")
              description)
             name)))
 
+    ;; generic renderer. retrieve renderer-fn which should return a str
+    (define (render-generic sortkey split)
+      ((keylist-get-info sortkey-list sortkey 'renderer-fn) split))
+
     (define (render-summary split level anchor?)
       (let ((sortkey (opt-val pagename-sorting
                               (case level
@@ -1271,9 +1294,13 @@ tags within description, notes or memo. ")
                                         (case level
                                           ((primary) optname-prime-date-subtotal)
                                           ((secondary) optname-sec-date-subtotal)))))
-        (if (member sortkey DATE-SORTING-TYPES)
-            (render-date date-subtotal-key split)
-            (render-account sortkey split anchor?))))
+        (cond
+         ((member sortkey DATE-SORTING-TYPES)
+          (render-date date-subtotal-key split))
+         ((member sortkey ACCOUNT-SORTING-TYPES)
+          (render-account sortkey split anchor?))
+         ((eq? sortkey 'reconciled-status)
+          (render-generic sortkey split)))))
     
     (define (render-grand-total)
       (_ "Grand Total"))
@@ -1538,7 +1565,9 @@ tags within description, notes or memo. ")
          (custom-sort? (or (and (member primary-key DATE-SORTING-TYPES)   ; this will remain
                                 (not (eq? primary-date-subtotal 'none)))  ; until qof-query
                            (and (member secondary-key DATE-SORTING-TYPES) ; is upgraded
-                                (not (eq? secondary-date-subtotal 'none)))))
+                                (not (eq? secondary-date-subtotal 'none)))
+                           (or (member primary-key CUSTOM-SORTING)
+                               (member secondary-key CUSTOM-SORTING))))
          (infobox-display (opt-val gnc:pagename-general optname-infobox-display))
          (query (qof-query-create-for-splits)))
 
@@ -1560,6 +1589,8 @@ tags within description, notes or memo. ")
               ((account-code) (lambda (s) (xaccAccountGetCode (xaccSplitGetAccount s))))
               ((corresponding-acc-name) (lambda (s) (xaccSplitGetCorrAccountFullName s)))
               ((corresponding-acc-code) (lambda (s) (xaccSplitGetCorrAccountCode s)))
+              ((reconciled-status) (lambda (s) (length (memq (xaccSplitGetReconcile s)
+                                                             '(#\n #\c #\y #\f #\v)))))
               ((amount) (lambda (s) (gnc-numeric-to-double (xaccSplitGetValue s))))
               ((description) (lambda (s) (xaccTransGetDescription (xaccSplitGetParent s))))
               ((number) (lambda (s)

commit 3de3d3cc9a2cd03f819551e0a660fd8473c50144
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Nov 30 16:07:27 2017 +0800

    ENH: Add debit/credit friendly names in subheading rendering
    
    Also add UI to toggle friendly headers

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index fc5ce15..e2bb438 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -71,6 +71,7 @@
 (define optname-full-account-name (N_ "Show Full Account Name"))
 (define optname-show-account-code (N_ "Show Account Code"))
 (define optname-show-account-description (N_ "Show Account Description"))
+(define optname-show-informal-headers (N_ "Show Informal Debit/Credit Headers"))
 (define optname-sec-sortkey (N_ "Secondary Key"))
 (define optname-sec-subtotal (N_ "Secondary Subtotal"))
 (define optname-sec-sortorder  (N_ "Secondary Sort Order"))
@@ -117,6 +118,7 @@ options specified in the Options panels."))
 (define SUBTOTAL-ENABLED (list 'account-name 'corresponding-acc-name
                                'account-code 'corresponding-acc-code))
 
+(define SORTKEY-INFORMAL-HEADERS (list 'account-name 'account-code))
 
 (define sortkey-list
   ;;
@@ -557,6 +559,11 @@ tags within description, notes or memo. ")
              (and sec-sortkey-subtotal-enabled sec-sortkey-subtotal-true)))
 
         (gnc-option-db-set-option-selectable-by-name
+         options pagename-sorting optname-show-informal-headers
+         (or (member prime-sortkey (list 'account-name 'account-code))
+             (member sec-sortkey (list 'account-name 'account-code))))
+        
+        (gnc-option-db-set-option-selectable-by-name
          options pagename-sorting optname-prime-date-subtotal
          prime-date-sortingtype-enabled)
 
@@ -595,7 +602,15 @@ tags within description, notes or memo. ")
       "j3"
       (_ "Show the account description for subheadings?")
       #f))
-    
+
+
+    (gnc:register-trep-option
+     (gnc:make-simple-boolean-option
+      pagename-sorting optname-show-informal-headers
+      "j4"
+      (_ "Show the informal headers for debit/credit accounts?")
+      #f))
+
     (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       pagename-sorting optname-prime-subtotal
@@ -984,6 +999,8 @@ tags within description, notes or memo. ")
            (report-currency (lambda (s) (if (column-uses? 'common-currency)
                                             (opt-val gnc:pagename-general optname-currency)
                                             (currency s))))
+           (friendly-debit (lambda (a) (gnc:get-debit-string (xaccAccountGetType a))))
+           (friendly-credit (lambda (a) (gnc:get-credit-string (xaccAccountGetType a))))
            (header-commodity (lambda (str)
                                (string-append
                                 str
@@ -1019,47 +1036,55 @@ tags within description, notes or memo. ")
            (running-balance (lambda (s) (gnc:make-gnc-monetary (currency s) (xaccSplitGetBalance s)))))
         (append
          ;; each column will be a vector
-         ;; (vector heading calculator-function reverse-column? subtotal? (vector start-dual-column? merging-function))
-         ;; (calculator-function split) to obtain amount
-         ;; reverse? to optionally reverse signs
-         ;; subtotal? to allow subtotals (ie irrelevant for running balance)
-         ;; merge? to merge with the next cell (ie for debit/credit cells)
-         ;; merging-function - function (usually gnc-numeric-add/sub-fixed to apply to dual-subtotal
+         ;; (vector heading
+         ;;         calculator-function                          ;; (calculator-function split) to obtain amount
+         ;;         reverse-column?                              ;; to optionally reverse signs
+         ;;         subtotal?                                    ;; subtotal? to allow subtotals (ie irrelevant for running balance)
+         ;;         (vector start-dual-column?                   ;; #t for the left side of a dual column (i.e. debit/credit)
+         ;;                 merging-function))                   ;; function to apply to dual-subtotal (gnc-numeric-add/sub)
+         ;;         friendly-heading-fn                          ;; retrieve friendly heading name for account debit/credit
          (if (column-uses? 'amount-single)
              (list (vector (header-commodity (_ "Amount"))
                            amount #t #t
-                           (vector #f #f)))
+                           (vector #f #f)
+                           (lambda (a) "")))
              '())
          (if (column-uses? 'amount-double)
              (list (vector (header-commodity (_ "Debit"))
                            debit-amount #f #t
-                           (vector #t gnc-numeric-add))
+                           (vector #t gnc-numeric-add)
+                           friendly-debit)
                    (vector (header-commodity (_ "Credit"))
                            credit-amount #f #t
-                           (vector #f gnc-numeric-sub)))
+                           (vector #f gnc-numeric-sub)
+                           friendly-credit))
              '())
 
          (if (and (column-uses? 'amount-original-currency)
                   (column-uses? 'amount-single))
              (list (vector (_ "Amount")
                            original-amount #t #t
-                           (vector #f #f)))
+                           (vector #f #f)
+                           (lambda (a) "")))
              '())
 
          (if (and (column-uses? 'amount-original-currency)
                   (column-uses? 'amount-double))
              (list (vector (_ "Debit")
                            original-debit-amount #f #t
-                           (vector #t gnc-numeric-add))
+                           (vector #t gnc-numeric-add)
+                           friendly-debit)
                    (vector (_ "Credit")
                            original-credit-amount #f #t
-                           (vector #f gnc-numeric-sub)))
+                           (vector #f gnc-numeric-sub)
+                           friendly-credit))
              '())
 
          (if (column-uses? 'running-balance)
              (list (vector (_ "Running Balance")
                            running-balance #t #f
-                           (vector #f #f)))
+                           (vector #f #f)
+                           (lambda (a) "")))
              '()))))
 
     (define headings-left-columns
@@ -1075,11 +1100,33 @@ tags within description, notes or memo. ")
     (define width-left-columns (length left-columns))
     (define width-right-columns (length calculated-cells))
 
-    (define (add-subheading data subheading-style)
-      (let ((heading-cell (gnc:make-html-table-cell data)))
-        (gnc:html-table-cell-set-colspan! heading-cell (+ width-left-columns width-right-columns))
-        (gnc:html-table-append-row/markup!
-         table subheading-style (list heading-cell))))
+    (define (add-subheading data subheading-style split level)
+      (let ((sortkey (opt-val pagename-sorting
+                           (case level
+                             ((primary) optname-prime-sortkey)
+                             ((secondary) optname-sec-sortkey)))))
+        (if (and (opt-val pagename-sorting optname-show-informal-headers)
+                 (member sortkey SORTKEY-INFORMAL-HEADERS))
+            (let ((row-contents '()))
+              (begin
+                (if export?
+                    (begin (addto! row-contents (gnc:make-html-table-cell subheading-style data))
+                           (for-each (lambda (cell) (addto! row-contents cell))
+                                     (gnc:html-make-empty-cells (- width-left-columns 1))))
+                    (addto! row-contents (gnc:make-html-table-cell/size 1 width-left-columns data)))
+                (map (lambda (col)
+                       (addto! row-contents
+                               (gnc:make-html-table-cell
+                                "<b>"
+                                ((vector-ref col 5)
+                                 ((keylist-get-info sortkey-list sortkey 'renderer-fn) split))
+                                "</b>")))
+                     calculated-cells)
+                (gnc:html-table-append-row/markup! table subheading-style (reverse row-contents))))
+            (let ((heading-cell (gnc:make-html-table-cell data)))
+              (gnc:html-table-cell-set-colspan! heading-cell (+ width-left-columns width-right-columns))
+              (gnc:html-table-append-row/markup!
+               table subheading-style (list heading-cell))))))
 
     (define (add-subtotal-row subtotal-string subtotal-collectors subtotal-style)
       (let* ((row-contents '())
@@ -1391,10 +1438,10 @@ tags within description, notes or memo. ")
                   (if next
                       (begin
                         (add-subheading (render-summary next 'primary #t)
-                                        def:primary-subtotal-style)
+                                        def:primary-subtotal-style next 'primary)
                         (if secondary-subtotal-comparator
                             (add-subheading (render-summary next 'secondary #t)
-                                            def:secondary-subtotal-style)))))
+                                            def:secondary-subtotal-style next 'secondary)))))
 
                 (if (and secondary-subtotal-comparator
                          (or (not next)
@@ -1409,7 +1456,7 @@ tags within description, notes or memo. ")
                                      secondary-subtotal-collectors)
                            (if next
                                (add-subheading (render-summary next 'secondary #t)
-                                               def:secondary-subtotal-style)))))
+                                               def:secondary-subtotal-style next 'secondary)))))
 
             (do-rows-with-subtotals rest (not odd-row?)))))
 
@@ -1417,12 +1464,12 @@ tags within description, notes or memo. ")
 
     (if (primary-get-info 'renderer-fn)
         (add-subheading (render-summary (car splits) 'primary #t)
-                        def:primary-subtotal-style))
+                        def:primary-subtotal-style (car splits) 'primary))
 
     (if (secondary-get-info 'renderer-fn)
         (add-subheading (render-summary (car splits) 'secondary #t)
-                        def:secondary-subtotal-style))
-    
+                        def:secondary-subtotal-style (car splits) 'secondary))
+
     (do-rows-with-subtotals splits #t)
 
     table))

commit 139e2aa7f249ccef9b2dde91692936f8a82de0a9
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Nov 27 21:43:07 2017 +0800

    ENH: Add option to choose infobox display summary
    
    Options are: always/never display, or display if no splits are matched or found

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 38c7819..fc5ce15 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -83,6 +83,7 @@
 (define optname-common-currency (N_ "Common Currency"))
 (define optname-orig-currency (N_ "Show original currency amount"))
 (define optname-currency (N_ "Report's currency"))
+(define optname-infobox-display (N_ "Add options summary"))
 
 ;;Filtering
 (define pagename-filter (N_ "Filter"))
@@ -399,6 +400,24 @@ Credit Card, and Income accounts."))
     gnc:pagename-general optname-table-export
     "g" (_ "Formats the table suitable for cut & paste exporting with extra cells.") #f))
 
+  (gnc:register-trep-option
+   (gnc:make-multichoice-option
+    gnc:pagename-general optname-infobox-display
+    "h" (_ "Add summary of options.")
+    '(no-match)
+    ;; This is an alist of conditions for displaying the infobox
+    ;; 'no-match for empty-report
+    ;; 'match for generated report
+    (list (vector '(no-match)
+                  (_ "If no transactions matched")
+                  (_ "Display summary if no transactions were matched."))
+          (vector '(no-match match)
+                  (_ "Always")
+                  (_ "Always display summary."))
+          (vector '()
+                  (_ "Never")
+                  (_ "Disable report summary.")))))
+
   ;; Filtering Options
 
   (gnc:register-trep-option
@@ -1473,6 +1492,7 @@ tags within description, notes or memo. ")
                                 (not (eq? primary-date-subtotal 'none)))  ; until qof-query
                            (and (member secondary-key DATE-SORTING-TYPES) ; is upgraded
                                 (not (eq? secondary-date-subtotal 'none)))))
+         (infobox-display (opt-val gnc:pagename-general optname-infobox-display))
          (query (qof-query-create-for-splits)))
 
     (define (generic-less? X Y key date-subtotal ascend?)
@@ -1617,10 +1637,11 @@ tags within description, notes or memo. ")
                 (gnc:html-markup-h2 NO-MATCHING-ACCT-HEADER)
                 (gnc:html-markup-p NO-MATCHING-ACCT-TEXT)))
 
-              (gnc:html-document-add-object!
-               document                 
-               (infobox))))
-            
+              (if (member 'nomatch infobox-display)
+                  (gnc:html-document-add-object!
+                   document
+                   (infobox)))))
+
         (begin
 
           (qof-query-set-book query (gnc-get-current-book))
@@ -1683,9 +1704,10 @@ tags within description, notes or memo. ")
                   (gnc:html-markup-h2 NO-MATCHING-TRANS-HEADER)
                   (gnc:html-markup-p NO-MATCHING-TRANS-TEXT)))
 
-                (gnc:html-document-add-object!
-                 document
-                 (infobox)))
+                (if (member 'no-match infobox-display)
+                    (gnc:html-document-add-object!
+                     document
+                     (infobox))))
 
               (let ((table (make-split-table splits options)))
 
@@ -1700,9 +1722,10 @@ tags within description, notes or memo. ")
                             (gnc-print-date begindate)
                             (gnc-print-date enddate)))))
 
-                (gnc:html-document-add-object!
-                 document                 
-                 (infobox))
+                (if (member 'match infobox-display)
+                    (gnc:html-document-add-object!
+                     document
+                     (infobox)))
 
                 (gnc:html-document-add-object! document table)))))
 

commit f2df1bd49cd8e651a1838711909af13cfe45847f
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 06:42:56 2017 +0800

    COSMETIC: if grand-totals=#f then omit <hr>
    
    I think <hr> and grand-total belong together because they share
    the same style.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 39fbbe0..38c7819 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1300,16 +1300,16 @@ tags within description, notes or memo. ")
       (set! work-done (+ 1 work-done))
 
       (if (null? splits)
-          
-          (begin
-            
-            (gnc:html-table-append-row/markup!
-             table def:grand-total-style
-             (list
-              (gnc:make-html-table-cell/size
-               1 (+ width-left-columns width-right-columns) (gnc:make-html-text (gnc:html-markup-hr)))))
 
-            (if (opt-val gnc:pagename-display "Totals")
+          (if (opt-val gnc:pagename-display "Totals")
+              (begin
+                (gnc:html-table-append-row/markup!
+                 table def:grand-total-style
+                 (list
+                  (gnc:make-html-table-cell/size
+                   1 (+ width-left-columns width-right-columns)
+                   (gnc:make-html-text (gnc:html-markup-hr)))))
+
                 (add-subtotal-row (render-grand-total) total-collectors def:grand-total-style)))
 
           (let* ((current (car splits))

commit a81c348310a278f2d8a5448bb8be45ae6037a30c
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Nov 29 21:21:14 2017 +0800

    REFACTOR: remove 'renderer-key lookup symbol, simplify custom sorter

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 23bef7d..39fbbe0 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -125,102 +125,101 @@ options specified in the Options panels."))
   ;;  'split-sortvalue     - function which retrieves number/string used for comparing splits
   ;;  'text                - text displayed in Display tab
   ;;  'tip                 - tooltip displayed in Display tab
-  ;;  'renderer-key        - helper symbol to select subtotal/subheading renderer
+  ;;  'renderer-fn         - helper function to select subtotal/subheading renderer
+  ;;       behaviour varies according to sortkey.
+  ;;       account-types converts split->account
+  ;;       #f means the sortkey cannot be subtotalled
   ;;
   (list (cons 'account-name  (list (cons 'sortkey (list SPLIT-ACCT-FULLNAME))
                                    (cons 'split-sortvalue (lambda (a) (gnc-account-get-full-name (xaccSplitGetAccount a))))
                                    (cons 'text (_ "Account Name"))
                                    (cons 'tip (_ "Sort & subtotal by account name."))
-                                   (cons 'renderer-key 'account)))
+                                   (cons 'renderer-fn (lambda (a) (xaccSplitGetAccount a)))))
 
         (cons 'account-code (list (cons 'sortkey (list SPLIT-ACCOUNT ACCOUNT-CODE-))
                                   (cons 'split-sortvalue (lambda (a) (xaccAccountGetCode (xaccSplitGetAccount a))))
                                   (cons 'text (_ "Account Code"))
                                   (cons 'tip (_ "Sort & subtotal by account code."))
-                                  (cons 'renderer-key 'account)))
+                                  (cons 'renderer-fn (lambda (a) (xaccSplitGetAccount a)))))
 
         (cons 'date         (list (cons 'sortkey (list SPLIT-TRANS TRANS-DATE-POSTED))
                                   (cons 'split-sortvalue #f)
                                   (cons 'text (_ "Date"))
                                   (cons 'tip (_ "Sort by date."))
-                                  (cons 'renderer-key #f)))
+                                  (cons 'renderer-fn #f)))
 
         (cons 'reconciled-date (list (cons 'sortkey (list SPLIT-DATE-RECONCILED))
                                      (cons 'split-sortvalue #f)
                                      (cons 'text (_ "Reconciled Date"))
                                      (cons 'tip (_ "Sort by the Reconciled Date."))
-                                     (cons 'renderer-key #f)))
+                                     (cons 'renderer-fn #f)))
 
         (cons 'register-order (list (cons 'sortkey (list QUERY-DEFAULT-SORT))
                                     (cons 'split-sortvalue #f)
                                     (cons 'text (_ "Register Order"))
                                     (cons 'tip (_ "Sort as in the register."))
-                                    (cons 'renderer-key #f)))
+                                    (cons 'renderer-fn #f)))
 
         (cons 'corresponding-acc-name (list (cons 'sortkey (list SPLIT-CORR-ACCT-NAME))
                                             (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountFullName a)))
                                             (cons 'text (_ "Other Account Name"))
                                             (cons 'tip (_ "Sort by account transferred from/to's name."))
-                                            (cons 'renderer-key 'other-acc)))
+                                            (cons 'renderer-fn (lambda (a) (xaccSplitGetAccount (xaccSplitGetOtherSplit a))))))
 
         (cons 'corresponding-acc-code (list (cons 'sortkey (list SPLIT-CORR-ACCT-CODE))
                                             (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountCode a)))
                                             (cons 'text (_ "Other Account Code"))
                                             (cons 'tip (_ "Sort by account transferred from/to's code."))
-                                            (cons 'renderer-key 'other-acct)))
+                                            (cons 'renderer-fn (lambda (a) (xaccSplitGetAccount (xaccSplitGetOtherSplit a))))))
 
         (cons 'amount        (list (cons 'sortkey (list SPLIT-VALUE))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (_ "Amount"))
                                    (cons 'tip (_ "Sort by amount."))
-                                   (cons 'renderer-key #f)))
+                                   (cons 'renderer-fn #f)))
 
         (cons 'description   (list (cons 'sortkey (list SPLIT-TRANS TRANS-DESCRIPTION))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (_ "Description"))
                                    (cons 'tip (_ "Sort by description."))
-                                   (cons 'renderer-key #f)))
+                                   (cons 'renderer-fn #f)))
 
         (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
             (cons 'number    (list (cons 'sortkey (list SPLIT-ACTION))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (_ "Number/Action"))
                                    (cons 'tip (_ "Sort by check number/action."))
-                                   (cons 'renderer-key #f)))
+                                   (cons 'renderer-fn #f)))
 
             (cons 'number    (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (_ "Number"))
                                    (cons 'tip (_ "Sort by check/transaction number."))
-                                   (cons 'renderer-key #f))))
+                                   (cons 'renderer-fn #f))))
 
         (cons 't-number      (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (_ "Transaction Number"))
                                    (cons 'tip (_ "Sort by transaction number."))
-                                   (cons 'renderer-key #f)))
+                                   (cons 'renderer-fn #f)))
 
         (cons 'memo          (list (cons 'sortkey (list SPLIT-MEMO))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (_ "Memo"))
                                    (cons 'tip (_ "Sort by memo."))
-                                   (cons 'renderer-key #f)))
+                                   (cons 'renderer-fn #f)))
 
         (cons 'none          (list (cons 'sortkey '())
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (_ "None"))
                                    (cons 'tip (_ "Do not sort."))
-                                   (cons 'renderer-key #f)))))
-
+                                   (cons 'renderer-fn #f)))))
 
 (define (time64-year t64)    (gnc:date-get-year (gnc-localtime t64)))
 (define (time64-quarter t64) (+ (* 10 (gnc:date-get-year (gnc-localtime t64)))  (gnc:date-get-quarter (gnc-localtime t64))))
 (define (time64-month t64)   (+ (* 100 (gnc:date-get-year (gnc-localtime t64))) (gnc:date-get-month (gnc-localtime t64))))
 (define (time64-week t64)    (gnc:date-get-week (gnc-localtime t64)))
-(define (split-week a) (time64-week (xaccTransGetDate (xaccSplitGetParent a))))
-(define (split-month a) (time64-month (xaccTransGetDate (xaccSplitGetParent a))))
-(define (split-quarter a) (time64-quarter (xaccTransGetDate (xaccSplitGetParent a))))
-(define (split-year a) (time64-year (xaccTransGetDate (xaccSplitGetParent a))))
+(define (split->time64 s) (xaccTransGetDate (xaccSplitGetParent s)))
 
 (define date-subtotal-list
   ;; List for date option.
@@ -228,41 +227,39 @@ options specified in the Options panels."))
   ;;  'split-sortvalue     - function which retrieves number/string used for comparing splits
   ;;  'text                - text displayed in Display tab
   ;;  'tip                 - tooltip displayed in Display tab
-  ;;  'renderer-key        - helper symbol to select subtotal/subheading renderer
+  ;;  'renderer-fn         - func retrieve string for subtotal/subheading renderer
+  ;;         #f means the date sortkey is not grouped
+  ;;         otherwise it converts split->string
   (list
    (cons 'none (list
                 (cons 'split-sortvalue #f)
                 (cons 'text (_ "None"))
                 (cons 'tip (_ "None."))
-                (cons 'renderer-key #f)))
+                (cons 'renderer-fn #f)))
 
    (cons 'weekly (list
-                  (cons 'split-sortvalue split-week)
+                  (cons 'split-sortvalue (lambda (s) (time64-week (split->time64 s))))
                   (cons 'text (_ "Weekly"))
                   (cons 'tip (_ "Weekly."))
-                  (cons 'renderer-key 'weekly)
-                  (cons 'renderer-fn gnc:date-get-week-year-string)))
-
+                  (cons 'renderer-fn (lambda (s) (gnc:date-get-week-year-string (gnc-localtime (split->time64 s)))))))
+   
    (cons 'monthly (list
-                   (cons 'split-sortvalue split-month)
+                   (cons 'split-sortvalue (lambda (s) (time64-month (split->time64 s))))
                    (cons 'text (_ "Monthly"))
                    (cons 'tip (_ "Monthly."))
-                   (cons 'renderer-key 'monthly)
-                   (cons 'renderer-fn gnc:date-get-month-year-string)))
+                   (cons 'renderer-fn (lambda (s) (gnc:date-get-month-year-string (gnc-localtime (split->time64 s)))))))
 
    (cons 'quarterly (list
-                     (cons 'split-sortvalue split-quarter)
+                     (cons 'split-sortvalue (lambda (s) (time64-quarter (split->time64 s))))
                      (cons 'text (_ "Quarterly"))
                      (cons 'tip (_ "Quarterly."))
-                     (cons 'renderer-key 'quarterly)
-                     (cons 'renderer-fn gnc:date-get-quarter-year-string)))
+                     (cons 'renderer-fn (lambda (s) (gnc:date-get-quarter-year-string (gnc-localtime (split->time64 s)))))))
 
    (cons 'yearly (list
-                  (cons 'split-sortvalue split-year)
+                  (cons 'split-sortvalue (lambda (s) (time64-year (split->time64 s))))
                   (cons 'text (_ "Yearly"))
                   (cons 'tip (_ "Yearly."))
-                  (cons 'renderer-key 'yearly)
-                  (cons 'renderer-fn gnc:date-get-year-string)))))
+                  (cons 'renderer-fn (lambda (s) (gnc:date-get-year-string (gnc-localtime (split->time64 s)))))))))
 
 (define filter-list
   (list
@@ -947,8 +944,8 @@ tags within description, notes or memo. ")
 
         (if (and (null? left-cols-list)
                  (or (opt-val gnc:pagename-display "Totals")
-                     (primary-get-info 'renderer-key)
-                     (secondary-get-info 'renderer-key)))
+                     (primary-get-info 'renderer-fn)
+                     (secondary-get-info 'renderer-fn)))
             (list (vector "" (lambda (s t) #f)))
             left-cols-list)))
 
@@ -1178,16 +1175,13 @@ tags within description, notes or memo. ")
                    (xaccAccountGetName account))
                ""))))
 
-    (define (render-date renderer-key split)
-      ((keylist-get-info date-subtotal-list renderer-key 'renderer-fn)
-       (gnc-localtime
-        (xaccTransGetDate
-         (xaccSplitGetParent split)))))
+    ;; retrieve date renderer from the date-subtotal-list
+    (define (render-date date-subtotal-key split)
+      ((keylist-get-info date-subtotal-list date-subtotal-key 'renderer-fn) split))
 
-    (define (render-account renderer-key split anchor?)
-      (let* ((account (case renderer-key
-                        ((account) (xaccSplitGetAccount split))
-                        ((other-acc) (xaccSplitGetAccount (xaccSplitGetOtherSplit split)))))
+    ;; generate account name, optionally with anchor to account register
+    (define (render-account sortkey split anchor?)
+      (let* ((account ((keylist-get-info sortkey-list sortkey 'renderer-fn) split))
              (name (account-namestring account
                                        (column-uses? 'sort-account-code)
                                        #t
@@ -1203,14 +1197,18 @@ tags within description, notes or memo. ")
             name)))
 
     (define (render-summary split level anchor?)
-      (let ((renderer-key (case level
-                            ((primary) (primary-get-info 'renderer-key))
-                            ((secondary) (secondary-get-info 'renderer-key)))))
-        (case renderer-key
-          ((weekly monthly quarterly yearly) (render-date renderer-key split))
-          ((account other-acc) (render-account renderer-key split anchor?))
-          (else #f))))
-
+      (let ((sortkey (opt-val pagename-sorting
+                              (case level
+                                ((primary) optname-prime-sortkey)
+                                ((secondary) optname-sec-sortkey))))
+            (date-subtotal-key (opt-val pagename-sorting
+                                        (case level
+                                          ((primary) optname-prime-date-subtotal)
+                                          ((secondary) optname-sec-date-subtotal)))))
+        (if (member sortkey DATE-SORTING-TYPES)
+            (render-date date-subtotal-key split)
+            (render-account sortkey split anchor?))))
+    
     (define (render-grand-total)
       (_ "Grand Total"))
 
@@ -1398,11 +1396,11 @@ tags within description, notes or memo. ")
 
     (gnc:html-table-set-col-headers! table (concatenate (list headings-left-columns headings-right-columns)))
 
-    (if (primary-get-info 'renderer-key)
+    (if (primary-get-info 'renderer-fn)
         (add-subheading (render-summary (car splits) 'primary #t)
                         def:primary-subtotal-style))
-    
-    (if (secondary-get-info 'renderer-key)
+
+    (if (secondary-get-info 'renderer-fn)
         (add-subheading (render-summary (car splits) 'secondary #t)
                         def:secondary-subtotal-style))
     
@@ -1480,21 +1478,16 @@ tags within description, notes or memo. ")
     (define (generic-less? X Y key date-subtotal ascend?)
       (define comparator-function
         (if (member key DATE-SORTING-TYPES)
-            (let* ((date (lambda (s)
-                           (case key
-                             ((date) (xaccTransGetDate (xaccSplitGetParent s)))
-                             ((reconciled-date) (xaccSplitGetDateReconciled s)))))
-                   (year    (lambda (s) (gnc:date-get-year (gnc-localtime (date s)))))
-                   (month   (lambda (s) (gnc:date-get-month (gnc-localtime (date s)))))
-                   (quarter (lambda (s) (gnc:date-get-quarter (gnc-localtime (date s)))))
-                   (week    (lambda (s) (gnc:date-get-week (gnc-localtime (date s)))))
-                   (secs    (lambda (s) (date s))))
+            (let ((date (lambda (s)
+                          (case key
+                            ((date) (xaccTransGetDate (xaccSplitGetParent s)))
+                            ((reconciled-date) (xaccSplitGetDateReconciled s))))))
               (case date-subtotal
-                ((yearly)    (lambda (s) (year s)))
-                ((monthly)   (lambda (s) (+ (* 100 (year s)) (month s))))
-                ((quarterly) (lambda (s) (+ (*  10 (year s)) (quarter s))))
-                ((weekly)    (lambda (s) (week s)))
-                ((none)      (lambda (s) (secs s)))))
+                ((yearly)    (lambda (s) (time64-year (date s))))
+                ((monthly)   (lambda (s) (time64-month (date s))))
+                ((quarterly) (lambda (s) (time64-quarter (date s))))
+                ((weekly)    (lambda (s) (time64-week (date s))))
+                ((none)      (lambda (s) (date s)))))
             (case key
               ((account-name) (lambda (s) (gnc-account-get-full-name (xaccSplitGetAccount s))))
               ((account-code) (lambda (s) (xaccAccountGetCode (xaccSplitGetAccount s))))

commit 39dceb55344eb07db45ab6c87d9eb2b9e2c55161
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Nov 29 18:27:30 2017 +0800

    REFACTOR: simplify num/t-num display code

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index aa64236..23bef7d 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -877,20 +877,18 @@ tags within description, notes or memo. ")
                                    (_ "Num/T-Num")
                                    (_ "Num"))
                                (lambda (split transaction-row?)
-                                 (define trans (xaccSplitGetParent split))
-                                 (if transaction-row?
-                                     (if BOOK-SPLIT-ACTION
-                                         (let* ((num (gnc-get-num-action trans split))
-                                                (t-num (if (opt-val gnc:pagename-display (N_ "Trans Number"))
-                                                           (gnc-get-num-action trans #f)
-                                                           ""))
-                                                (num-string (if (string-null? t-num)
-                                                                num
-                                                                (string-append num "/" t-num))))
-                                           (gnc:make-html-table-cell/markup "text-cell" num-string))
-                                         (gnc:make-html-table-cell/markup "text-cell"
-                                                                          (gnc-get-num-action trans split)))
-                                     ""))))
+                                 (let* ((trans (xaccSplitGetParent split))
+                                        (num (gnc-get-num-action trans split))
+                                        (t-num (if (and BOOK-SPLIT-ACTION
+                                                        (opt-val gnc:pagename-display (N_ "Trans Number")))
+                                                   (gnc-get-num-action trans #f)
+                                                   ""))
+                                        (num-string (if (string-null? t-num)
+                                                        num
+                                                        (string-append num "/" t-num))))
+                                   (if transaction-row?
+                                       (gnc:make-html-table-cell/markup "text-cell" num-string)
+                                       "")))))
 
                (add-if (column-uses? 'description)
                        (vector (_ "Description")

commit 005fdb5f43e23b069681c369fc3629b716d6c5cf
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 06:36:33 2017 +0800

    REFACTOR: centralize left-cols to a vector-list

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 4cd5287..aa64236 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -853,33 +853,106 @@ tags within description, notes or memo. ")
              (left-cols-list
               (append
                (add-if (column-uses? 'date)
-                       (_ "Date"))
+                       (vector (_ "Date")
+                               (lambda (split transaction-row?)
+                                 (if transaction-row?
+                                     (gnc:make-html-table-cell/markup
+                                      "date-cell"
+                                      (qof-print-date (xaccTransGetDate (xaccSplitGetParent split))))
+                                     ""))))
+
                (add-if (column-uses? 'reconciled-date)
-                       (_ "Reconciled Date"))
+                       (vector (_ "Reconciled Date")
+                               (lambda (split transaction-row?)
+                                 (gnc:make-html-table-cell/markup
+                                  "date-cell"
+                                  (let ((date (xaccSplitGetDateReconciled split)))
+                                    (if (zero? date)
+                                        ""
+                                        (qof-print-date date)))))))
+
                (add-if (column-uses? 'num)
-                       (if (and BOOK-SPLIT-ACTION
-                                (opt-val gnc:pagename-display (N_ "Trans Number")))
-                           (_ "Num/T-Num")
-                           (_ "Num")))
+                       (vector (if (and BOOK-SPLIT-ACTION
+                                        (opt-val gnc:pagename-display (N_ "Trans Number")))
+                                   (_ "Num/T-Num")
+                                   (_ "Num"))
+                               (lambda (split transaction-row?)
+                                 (define trans (xaccSplitGetParent split))
+                                 (if transaction-row?
+                                     (if BOOK-SPLIT-ACTION
+                                         (let* ((num (gnc-get-num-action trans split))
+                                                (t-num (if (opt-val gnc:pagename-display (N_ "Trans Number"))
+                                                           (gnc-get-num-action trans #f)
+                                                           ""))
+                                                (num-string (if (string-null? t-num)
+                                                                num
+                                                                (string-append num "/" t-num))))
+                                           (gnc:make-html-table-cell/markup "text-cell" num-string))
+                                         (gnc:make-html-table-cell/markup "text-cell"
+                                                                          (gnc-get-num-action trans split)))
+                                     ""))))
+
                (add-if (column-uses? 'description)
-                       (_ "Description"))
+                       (vector (_ "Description")
+                               (lambda (split transaction-row?)
+                                 (define trans (xaccSplitGetParent split))
+                                 (if transaction-row?
+                                     (gnc:make-html-table-cell/markup
+                                      "text-cell"
+                                      (xaccTransGetDescription trans))
+                                     ""))))
+
                (add-if (column-uses? 'memo)
-                       (if (column-uses? 'notes)
-                           (string-append (_ "Memo") "/" (_ "Notes"))
-                           (_ "Memo")))
-               (add-if (or (column-uses? 'account-name)
-                           (column-uses? 'account-code))
-                       (_ "Account"))
-               (add-if (or (column-uses? 'other-account-name)
-                           (column-uses? 'other-account-code))
-                       (_ "Transfer from/to"))
+                       (vector (if (column-uses? 'notes)
+                                   (string-append (_ "Memo") "/" (_ "Notes"))
+                                   (_ "Memo"))
+                               (lambda (split transaction-row?)
+                                 (define trans (xaccSplitGetParent split))
+                                 (define memo (xaccSplitGetMemo split))
+                                 (if (and (string-null? memo) (column-uses? 'notes))
+                                     (xaccTransGetNotes trans)
+                                     memo))))
+
+               (add-if (or (column-uses? 'account-name) (column-uses? 'account-code))
+                       (vector (_ "Account")
+                               (lambda (split transaction-row?)
+                                 (define account (xaccSplitGetAccount split))
+                                 (account-namestring account
+                                                     (column-uses? 'account-code)
+                                                     (column-uses? 'account-name)
+                                                     (column-uses? 'account-full-name)))))
+
+               (add-if (or (column-uses? 'other-account-name) (column-uses? 'other-account-code))
+                       (vector (_ "Transfer from/to")
+                               (lambda (split transaction-row?)
+                                 (define other-account (xaccSplitGetAccount (xaccSplitGetOtherSplit split)))
+                                 (account-namestring other-account
+                                                     (column-uses? 'other-account-code)
+                                                     (column-uses? 'other-account-name)
+                                                     (column-uses? 'other-account-full-name)))))
+
                (add-if (column-uses? 'shares)
-                       (_ "Shares"))
+                       (vector (_ "Shares")
+                               (lambda (split transaction-row?)
+                                 (gnc:make-html-table-cell/markup
+                                  "number-cell"
+                                  (xaccSplitGetAmount split)))))
+
                (add-if (column-uses? 'price)
-                       (_ "Price")))))
-        (if (null? headings-list)
-            (list "")
-            headings-list)))
+                       (vector (_ "Price")
+                               (lambda (split transaction-row?)
+                                 (define trans (xaccSplitGetParent split))
+                                 (gnc:make-html-table-cell/markup
+                                  "number-cell"
+                                  (gnc:make-gnc-monetary (xaccTransGetCurrency trans)
+                                                         (xaccSplitGetSharePrice split)))))))))
+
+        (if (and (null? left-cols-list)
+                 (or (opt-val gnc:pagename-display "Totals")
+                     (primary-get-info 'renderer-key)
+                     (secondary-get-info 'renderer-key)))
+            (list (vector "" (lambda (s t) #f)))
+            left-cols-list)))
 
     ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;;
@@ -1154,6 +1227,13 @@ tags within description, notes or memo. ")
              (trans (xaccSplitGetParent split))
              (account (xaccSplitGetAccount split)))
 
+        (define left-cols
+          (map (lambda (left-col)
+                 (let* ((col-fn (vector-ref left-col 1))
+                        (col-data (col-fn split transaction-row?)))
+                   col-data))
+               left-columns))
+
         (define cells
           (map (lambda (cell)
                  (let* ((calculator (vector-ref cell 1))
@@ -1164,87 +1244,11 @@ tags within description, notes or memo. ")
                            reverse?
                            subtotal?)))
                cell-calculators))
-
-        (if (column-uses? 'date)
-            (addto! row-contents
-                    (if transaction-row?
-                        (gnc:make-html-table-cell/markup
-                         "date-cell"
-                         (qof-print-date (xaccTransGetDate trans)))
-                        "")))
-
-        (if (column-uses? 'reconciled-date)
-            (addto! row-contents
-                    (gnc:make-html-table-cell/markup
-                     "date-cell"
-                     (let ((date (xaccSplitGetDateReconciled split)))
-                       (if (zero? date)
-                           ""
-                           (qof-print-date date))))))
-
-        (if (column-uses? 'num)
-            (addto! row-contents
-                    (if transaction-row?
-                        (if BOOK-SPLIT-ACTION
-                            (let* ((num (gnc-get-num-action trans split))
-                                   (t-num (if (if (gnc:lookup-option options
-                                                                     gnc:pagename-display
-                                                                     (N_ "Trans Number"))
-                                                  (opt-val gnc:pagename-display (N_ "Trans Number"))
-                                                  "")
-                                              (gnc-get-num-action trans #f)
-                                              ""))
-                                   (num-string (if (string-null? t-num)
-                                                   num
-                                                   (string-append num "/" t-num))))
-                              (gnc:make-html-table-cell/markup "text-cell" num-string))
-                            (gnc:make-html-table-cell/markup "text-cell"
-                                                             (gnc-get-num-action trans split)))
-                        "")))
-
-        (if (column-uses? 'description)
-            (addto! row-contents
-                    (if transaction-row?
-                        (gnc:make-html-table-cell/markup
-                         "text-cell"
-                         (xaccTransGetDescription trans))
-                        "")))
-
-        (if (column-uses? 'memo)
-            (let ((memo (xaccSplitGetMemo split)))
-              (if (and (string-null? memo) (column-uses? 'notes))
-                  (addto! row-contents (xaccTransGetNotes trans))
-                  (addto! row-contents memo))))
-
-        (if (or (column-uses? 'account-name) (column-uses? 'account-code))
-            (addto! row-contents (account-namestring account
-                                                     (column-uses? 'account-code)
-                                                     (column-uses? 'account-name)
-                                                     (column-uses? 'account-full-name))))
-
-        (if (or (column-uses? 'other-account-name) (column-uses? 'other-account-code))
-            (addto! row-contents (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
-                                                     (column-uses? 'other-account-code)
-                                                     (column-uses? 'other-account-name)
-                                                     (column-uses? 'other-account-full-name))))
-
-        (if (column-uses? 'shares)
-            (addto! row-contents (gnc:make-html-table-cell/markup
-                                  "number-cell"
-                                  (xaccSplitGetAmount split))))
-
-        (if (column-uses? 'price)
-            (addto! row-contents
-                    (gnc:make-html-table-cell/markup
-                     "number-cell"
-                     (gnc:make-gnc-monetary (xaccTransGetCurrency trans)
-                                            (xaccSplitGetSharePrice split)))))
-
-        ;; hack to ensure cell alignment is corrected when no left columns were selected
-        ;; this is needed because subtotal renderer will always insert at least 1 column
-        (if (null? row-contents)
-            (addto! row-contents (gnc:html-make-empty-cell)))
         
+        (for-each (lambda (col)
+                    (addto! row-contents col))
+                  left-cols)
+
         (for-each (lambda (cell)
                     (let ((cell-content (vector-ref cell 0))
                           ;; reverse? returns a bool - will check if the cell type has reversible sign,

commit 7b6ac3a0773c173a33b39bdd1e3b7863929f3103
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Nov 28 21:06:34 2017 +0800

    ENH: if no Display/* selected, insert empty left-column
    
    Previously, if user selected NO split header (eg Date
    Description Memo etc) the report would display the data
    columns, which means the subtotal would cause misalignment.
    This commit ensures the columns are aligned when user
    selects no split information. At least 1 left-column is
    required to allow for subtotal headers and summaries within
    the table.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 45c9857..4cd5287 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -849,33 +849,37 @@ tags within description, notes or memo. ")
       (cdr (assq param used-columns)))
 
     (define left-columns
-      (let* ((add-if (lambda pred? . items) (if pred? items '())))
-        (append
-         (add-if (column-uses? 'date)
-                 (_ "Date"))
-         (add-if (column-uses? 'reconciled-date)
-                 (_ "Reconciled Date"))
-         (add-if (column-uses? 'num)
-                 (if (and BOOK-SPLIT-ACTION
-                          (opt-val gnc:pagename-display (N_ "Trans Number")))
-                     (_ "Num/T-Num")
-                     (_ "Num")))
-         (add-if (column-uses? 'description)
-                 (_ "Description"))
-         (add-if (column-uses? 'memo)
-                 (if (column-uses? 'notes)
-                     (string-append (_ "Memo") "/" (_ "Notes"))
-                     (_ "Memo")))
-         (add-if (or (column-uses? 'account-name)
-                     (column-uses? 'account-code))
-                 (_ "Account"))
-         (add-if (or (column-uses? 'other-account-name)
-                     (column-uses? 'other-account-code))
-                 (_ "Transfer from/to"))
-         (add-if (column-uses? 'shares)
-                 (_ "Shares"))
-         (add-if (column-uses? 'price)
-                 (_ "Price"))))
+      (let* ((add-if (lambda (pred? . items) (if pred? items '())))
+             (left-cols-list
+              (append
+               (add-if (column-uses? 'date)
+                       (_ "Date"))
+               (add-if (column-uses? 'reconciled-date)
+                       (_ "Reconciled Date"))
+               (add-if (column-uses? 'num)
+                       (if (and BOOK-SPLIT-ACTION
+                                (opt-val gnc:pagename-display (N_ "Trans Number")))
+                           (_ "Num/T-Num")
+                           (_ "Num")))
+               (add-if (column-uses? 'description)
+                       (_ "Description"))
+               (add-if (column-uses? 'memo)
+                       (if (column-uses? 'notes)
+                           (string-append (_ "Memo") "/" (_ "Notes"))
+                           (_ "Memo")))
+               (add-if (or (column-uses? 'account-name)
+                           (column-uses? 'account-code))
+                       (_ "Account"))
+               (add-if (or (column-uses? 'other-account-name)
+                           (column-uses? 'other-account-code))
+                       (_ "Transfer from/to"))
+               (add-if (column-uses? 'shares)
+                       (_ "Shares"))
+               (add-if (column-uses? 'price)
+                       (_ "Price")))))
+        (if (null? headings-list)
+            (list "")
+            headings-list)))
 
     ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;;
@@ -1236,6 +1240,11 @@ tags within description, notes or memo. ")
                      (gnc:make-gnc-monetary (xaccTransGetCurrency trans)
                                             (xaccSplitGetSharePrice split)))))
 
+        ;; hack to ensure cell alignment is corrected when no left columns were selected
+        ;; this is needed because subtotal renderer will always insert at least 1 column
+        (if (null? row-contents)
+            (addto! row-contents (gnc:html-make-empty-cell)))
+        
         (for-each (lambda (cell)
                     (let ((cell-content (vector-ref cell 0))
                           ;; reverse? returns a bool - will check if the cell type has reversible sign,

commit 230493f2942dbd5ba0466b6c030134a82e356eec
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Nov 28 17:24:45 2017 +0800

    REFACTOR: simplify render-summary
    
    it's the sole user of renderer-keys. access from *-get-info directly.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 2f98ba4..45c9857 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -826,7 +826,6 @@ tags within description, notes or memo. ")
                (and (opt-val pagename-sorting optname-prime-subtotal)
                     (keylist-get-info sortkey-list sortkey info))))))
 
-  
   (define (secondary-get-info info)
     (let ((sortkey (opt-val pagename-sorting optname-sec-sortkey)))
       (if (member sortkey DATE-SORTING-TYPES)
@@ -835,18 +834,6 @@ tags within description, notes or memo. ")
                (and (opt-val pagename-sorting optname-sec-subtotal)
                     (keylist-get-info sortkey-list sortkey info))))))
 
-  (define primary-subtotal-comparator
-    (primary-get-info 'split-sortvalue))
-
-  (define primary-renderer-key
-    (primary-get-info 'renderer-key))
-
-  (define secondary-subtotal-comparator
-    (secondary-get-info 'split-sortvalue))
-
-  (define secondary-renderer-key
-    (secondary-get-info 'renderer-key))
-
   (let* ((work-to-do (length splits))
          (work-done 0)
          (table (gnc:make-html-table))
@@ -1140,11 +1127,14 @@ tags within description, notes or memo. ")
              description)
             name)))
 
-    (define (render-summary split renderer-key anchor?)
-      (case renderer-key
-        ((weekly monthly quarterly yearly) (render-date renderer-key split))
-        ((account other-acc) (render-account renderer-key split anchor?))
-        (else #f)))
+    (define (render-summary split level anchor?)
+      (let ((renderer-key (case level
+                            ((primary) (primary-get-info 'renderer-key))
+                            ((secondary) (secondary-get-info 'renderer-key)))))
+        (case renderer-key
+          ((weekly monthly quarterly yearly) (render-date renderer-key split))
+          ((account other-acc) (render-account renderer-key split anchor?))
+          (else #f))))
 
     (define (render-grand-total)
       (_ "Grand Total"))
@@ -1293,6 +1283,8 @@ tags within description, notes or memo. ")
       (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells))
 
     (define (do-rows-with-subtotals splits odd-row?)
+      (define primary-subtotal-comparator (primary-get-info 'split-sortvalue))
+      (define secondary-subtotal-comparator (secondary-get-info 'split-sortvalue))
 
       (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
 
@@ -1357,23 +1349,23 @@ tags within description, notes or memo. ")
                   (if secondary-subtotal-comparator
                       (begin
                         (add-subtotal-row (total-string
-                                           (render-summary current secondary-renderer-key #f))
+                                           (render-summary current 'secondary #f))
                                           secondary-subtotal-collectors
                                           def:secondary-subtotal-style)
                         (for-each (lambda (coll) (coll 'reset #f #f))
                                   secondary-subtotal-collectors)))
                   (add-subtotal-row (total-string
-                                     (render-summary current primary-renderer-key #f))
+                                     (render-summary current 'primary #f))
                                     primary-subtotal-collectors
                                     def:primary-subtotal-style)
                   (for-each (lambda (coll) (coll 'reset #f #f))
                             primary-subtotal-collectors)
                   (if next
                       (begin
-                        (add-subheading (render-summary next primary-renderer-key #t)
+                        (add-subheading (render-summary next 'primary #t)
                                         def:primary-subtotal-style)
                         (if secondary-subtotal-comparator
-                            (add-subheading (render-summary next secondary-renderer-key #t)
+                            (add-subheading (render-summary next 'secondary #t)
                                             def:secondary-subtotal-style)))))
 
                 (if (and secondary-subtotal-comparator
@@ -1382,25 +1374,25 @@ tags within description, notes or memo. ")
                                   (not (equal? (secondary-subtotal-comparator current)
                                                (secondary-subtotal-comparator next))))))
                     (begin (add-subtotal-row (total-string
-                                              (render-summary current secondary-renderer-key #f))
+                                              (render-summary current 'secondary #f))
                                              secondary-subtotal-collectors
                                              def:secondary-subtotal-style)
                            (for-each (lambda (coll) (coll 'reset #f #f))
                                      secondary-subtotal-collectors)
                            (if next
-                               (add-subheading (render-summary next secondary-renderer-key #t)
+                               (add-subheading (render-summary next 'secondary #t)
                                                def:secondary-subtotal-style)))))
 
             (do-rows-with-subtotals rest (not odd-row?)))))
 
     (gnc:html-table-set-col-headers! table (concatenate (list headings-left-columns headings-right-columns)))
 
-    (if primary-renderer-key
-        (add-subheading (render-summary (car splits) primary-renderer-key #t)
+    (if (primary-get-info 'renderer-key)
+        (add-subheading (render-summary (car splits) 'primary #t)
                         def:primary-subtotal-style))
     
-    (if secondary-renderer-key
-        (add-subheading (render-summary (car splits) secondary-renderer-key #t)
+    (if (secondary-get-info 'renderer-key)
+        (add-subheading (render-summary (car splits) 'secondary #t)
                         def:secondary-subtotal-style))
     
     (do-rows-with-subtotals splits #t)

commit 1ce2f3f6d8639b8e073a6807efb2aca159570b44
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Nov 28 14:40:02 2017 +0800

    REFACTOR: simplify do-rows-with-subtotals

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index d811fa9..2f98ba4 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1283,11 +1283,16 @@ tags within description, notes or memo. ")
 
     ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-    (define (do-rows-with-subtotals splits
-                                    odd-row?
-                                    primary-subtotal-collectors
-                                    secondary-subtotal-collectors
-                                    total-collectors)
+    (define primary-subtotal-collectors
+      (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells))
+
+    (define secondary-subtotal-collectors
+      (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells))
+
+    (define total-collectors
+      (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells))
+
+    (define (do-rows-with-subtotals splits odd-row?)
 
       (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
 
@@ -1386,26 +1391,19 @@ tags within description, notes or memo. ")
                                (add-subheading (render-summary next secondary-renderer-key #t)
                                                def:secondary-subtotal-style)))))
 
-            (do-rows-with-subtotals rest
-                                    (not odd-row?)
-                                    primary-subtotal-collectors
-                                    secondary-subtotal-collectors
-                                    total-collectors))))
+            (do-rows-with-subtotals rest (not odd-row?)))))
 
     (gnc:html-table-set-col-headers! table (concatenate (list headings-left-columns headings-right-columns)))
 
     (if primary-renderer-key
         (add-subheading (render-summary (car splits) primary-renderer-key #t)
                         def:primary-subtotal-style))
-
+    
     (if secondary-renderer-key
         (add-subheading (render-summary (car splits) secondary-renderer-key #t)
                         def:secondary-subtotal-style))
-
-    (do-rows-with-subtotals splits #t
-                            (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells)
-                            (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells)
-                            (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells))
+    
+    (do-rows-with-subtotals splits #t)
 
     table))
 

commit 725767521893023d2bc0f8ed526112ba2cde1fc3
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Nov 27 20:18:16 2017 +0800

    REFACTOR:bisect subtotal-get-info into primary/secondary

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 4dd5983..d811fa9 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -818,38 +818,34 @@ tags within description, notes or memo. ")
           (cons 'sort-account-description (opt-val pagename-sorting (N_ "Show Account Description")))
           (cons 'notes (opt-val gnc:pagename-display (N_ "Notes")))))
 
+  (define (primary-get-info info)
+    (let ((sortkey (opt-val pagename-sorting optname-prime-sortkey)))
+      (if (member sortkey DATE-SORTING-TYPES)
+          (keylist-get-info date-subtotal-list (opt-val pagename-sorting optname-prime-date-subtotal) info)
+          (and (member sortkey SUBTOTAL-ENABLED)
+               (and (opt-val pagename-sorting optname-prime-subtotal)
+                    (keylist-get-info sortkey-list sortkey info))))))
 
-  (define (subtotal-get-info name-sortkey name-subtotal name-date-subtotal info)
-    (let ((sortkey (opt-val pagename-sorting name-sortkey)))
+  
+  (define (secondary-get-info info)
+    (let ((sortkey (opt-val pagename-sorting optname-sec-sortkey)))
       (if (member sortkey DATE-SORTING-TYPES)
-          (keylist-get-info date-subtotal-list (opt-val pagename-sorting name-date-subtotal) info)
+          (keylist-get-info date-subtotal-list (opt-val pagename-sorting optname-sec-date-subtotal) info)
           (and (member sortkey SUBTOTAL-ENABLED)
-               (and (opt-val pagename-sorting name-subtotal)
+               (and (opt-val pagename-sorting optname-sec-subtotal)
                     (keylist-get-info sortkey-list sortkey info))))))
 
   (define primary-subtotal-comparator
-    (subtotal-get-info optname-prime-sortkey
-                       optname-prime-subtotal
-                       optname-prime-date-subtotal
-                       'split-sortvalue))
-
-  (define secondary-subtotal-comparator
-    (subtotal-get-info optname-sec-sortkey
-                       optname-sec-subtotal
-                       optname-sec-date-subtotal
-                       'split-sortvalue))
+    (primary-get-info 'split-sortvalue))
 
   (define primary-renderer-key
-    (subtotal-get-info optname-prime-sortkey
-                       optname-prime-subtotal
-                       optname-prime-date-subtotal
-                       'renderer-key))
+    (primary-get-info 'renderer-key))
+
+  (define secondary-subtotal-comparator
+    (secondary-get-info 'split-sortvalue))
 
   (define secondary-renderer-key
-    (subtotal-get-info optname-sec-sortkey
-                       optname-sec-subtotal
-                       optname-sec-date-subtotal
-                       'renderer-key))
+    (secondary-get-info 'renderer-key))
 
   (let* ((work-to-do (length splits))
          (work-done 0)

commit e2912d1b9c7a34f2979f176fbfba55d08f3edae9
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 06:22:41 2017 +0800

    REFACTOR: start refactor subtotal

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index b971627..4dd5983 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -240,25 +240,29 @@ options specified in the Options panels."))
                   (cons 'split-sortvalue split-week)
                   (cons 'text (_ "Weekly"))
                   (cons 'tip (_ "Weekly."))
-                  (cons 'renderer-key 'week)))
+                  (cons 'renderer-key 'weekly)
+                  (cons 'renderer-fn gnc:date-get-week-year-string)))
 
    (cons 'monthly (list
                    (cons 'split-sortvalue split-month)
                    (cons 'text (_ "Monthly"))
                    (cons 'tip (_ "Monthly."))
-                   (cons 'renderer-key 'month)))
+                   (cons 'renderer-key 'monthly)
+                   (cons 'renderer-fn gnc:date-get-month-year-string)))
 
    (cons 'quarterly (list
                      (cons 'split-sortvalue split-quarter)
                      (cons 'text (_ "Quarterly"))
                      (cons 'tip (_ "Quarterly."))
-                     (cons 'renderer-key 'quarter)))
+                     (cons 'renderer-key 'quarterly)
+                     (cons 'renderer-fn gnc:date-get-quarter-year-string)))
 
    (cons 'yearly (list
                   (cons 'split-sortvalue split-year)
                   (cons 'text (_ "Yearly"))
                   (cons 'tip (_ "Yearly."))
-                  (cons 'renderer-key 'year)))))
+                  (cons 'renderer-key 'yearly)
+                  (cons 'renderer-fn gnc:date-get-year-string)))))
 
 (define filter-list
   (list
@@ -776,11 +780,7 @@ tags within description, notes or memo. ")
 ;; ;;;;;;;;;;;;;;;;;;;;
 ;; Here comes the big function that builds the whole table.
 
-(define (make-split-table splits options
-                          primary-subtotal-comparator
-                          secondary-subtotal-comparator
-                          primary-renderer-key
-                          secondary-renderer-key)
+(define (make-split-table splits options)
 
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
   (define BOOK-SPLIT-ACTION (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
@@ -819,6 +819,38 @@ tags within description, notes or memo. ")
           (cons 'notes (opt-val gnc:pagename-display (N_ "Notes")))))
 
 
+  (define (subtotal-get-info name-sortkey name-subtotal name-date-subtotal info)
+    (let ((sortkey (opt-val pagename-sorting name-sortkey)))
+      (if (member sortkey DATE-SORTING-TYPES)
+          (keylist-get-info date-subtotal-list (opt-val pagename-sorting name-date-subtotal) info)
+          (and (member sortkey SUBTOTAL-ENABLED)
+               (and (opt-val pagename-sorting name-subtotal)
+                    (keylist-get-info sortkey-list sortkey info))))))
+
+  (define primary-subtotal-comparator
+    (subtotal-get-info optname-prime-sortkey
+                       optname-prime-subtotal
+                       optname-prime-date-subtotal
+                       'split-sortvalue))
+
+  (define secondary-subtotal-comparator
+    (subtotal-get-info optname-sec-sortkey
+                       optname-sec-subtotal
+                       optname-sec-date-subtotal
+                       'split-sortvalue))
+
+  (define primary-renderer-key
+    (subtotal-get-info optname-prime-sortkey
+                       optname-prime-subtotal
+                       optname-prime-date-subtotal
+                       'renderer-key))
+
+  (define secondary-renderer-key
+    (subtotal-get-info optname-sec-sortkey
+                       optname-sec-subtotal
+                       optname-sec-date-subtotal
+                       'renderer-key))
+
   (let* ((work-to-do (length splits))
          (work-done 0)
          (table (gnc:make-html-table))
@@ -1089,11 +1121,7 @@ tags within description, notes or memo. ")
                ""))))
 
     (define (render-date renderer-key split)
-      ((case renderer-key
-         ((week) gnc:date-get-week-year-string)
-         ((month) gnc:date-get-month-year-string)
-         ((quarter) gnc:date-get-quarter-year-string)
-         ((year) gnc:date-get-year-string))
+      ((keylist-get-info date-subtotal-list renderer-key 'renderer-fn)
        (gnc-localtime
         (xaccTransGetDate
          (xaccSplitGetParent split)))))
@@ -1118,7 +1146,7 @@ tags within description, notes or memo. ")
 
     (define (render-summary split renderer-key anchor?)
       (case renderer-key
-        ((week month quarter year) (render-date renderer-key split))
+        ((weekly monthly quarterly yearly) (render-date renderer-key split))
         ((account other-acc) (render-account renderer-key split anchor?))
         (else #f)))
 
@@ -1394,22 +1422,6 @@ tags within description, notes or memo. ")
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
   (define BOOK-SPLIT-ACTION (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
 
-  (define (subtotal-get-info name-sortkey name-subtotal name-date-subtotal info)
-    ;; The value of the sorting-key multichoice option.
-    (let ((sortkey (opt-val pagename-sorting name-sortkey)))
-      (if (member sortkey DATE-SORTING-TYPES)
-          ;; If sorting by date, look up the value of the
-          ;; date-subtotalling multichoice option and return the
-          ;; corresponding funcs in the assoc-list.
-          (keylist-get-info date-subtotal-list (opt-val pagename-sorting name-date-subtotal) info)
-          ;; For everything else: 1. check whether sortkey has
-          ;; subtotalling enabled at all, 2. check whether the
-          ;; enable-subtotal boolean option is #t, 3. look up the
-          ;; appropriate funcs in the assoc-list.
-          (and (member sortkey SUBTOTAL-ENABLED)
-               (and (opt-val pagename-sorting name-subtotal)
-                    (keylist-get-info sortkey-list sortkey info))))))
-
   (define (is-filter-member split account-list)
     (let* ((txn (xaccSplitGetParent split))
            (splitcount (xaccTransCountSplits txn))
@@ -1685,24 +1697,7 @@ tags within description, notes or memo. ")
                  document
                  (infobox)))
 
-              (let ((table (make-split-table
-                            splits options
-                            (subtotal-get-info optname-prime-sortkey
-                                               optname-prime-subtotal
-                                               optname-prime-date-subtotal
-                                               'split-sortvalue)
-                            (subtotal-get-info optname-sec-sortkey
-                                               optname-sec-subtotal
-                                               optname-sec-date-subtotal
-                                               'split-sortvalue)
-                            (subtotal-get-info optname-prime-sortkey
-                                               optname-prime-subtotal
-                                               optname-prime-date-subtotal
-                                               'renderer-key)
-                            (subtotal-get-info optname-sec-sortkey
-                                               optname-sec-subtotal
-                                               optname-sec-date-subtotal
-                                               'renderer-key))))
+              (let ((table (make-split-table splits options)))
 
                 (gnc:html-document-set-title! document report-title)
 

commit 00b2e76d352e0e531a829250b2762338d4317a90
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 06:21:46 2017 +0800

    BUGFIX: Reverse sign on display only

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 0b9b0fc..b971627 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1141,19 +1141,9 @@ tags within description, notes or memo. ")
                  (let* ((calculator (vector-ref cell 1))
                         (reverse? (vector-ref cell 2))
                         (subtotal? (vector-ref cell 3))
-                        (calculated (calculator split))
-                        (reverse-amount (lambda (mon)
-                                            (let ((currency (gnc:gnc-monetary-commodity mon))
-                                                  (amount (gnc:gnc-monetary-amount mon)))
-                                              (gnc:make-gnc-monetary
-                                               currency
-                                               (gnc-numeric-neg amount))))))
-                   (vector (if (and reverse?
-                                    (if account-types-to-reverse
-                                        (member (xaccAccountGetType account) account-types-to-reverse)
-                                        (gnc-reverse-balance account)))
-                               (reverse-amount calculated)
-                               calculated)
+                        (calculated (calculator split)))
+                   (vector calculated
+                           reverse?
                            subtotal?)))
                cell-calculators))
 
@@ -1234,22 +1224,23 @@ tags within description, notes or memo. ")
 
         (for-each (lambda (cell)
                     (let ((cell-content (vector-ref cell 0))
-                          (reverse? (vector-ref cell 1))
-                          (reverse-amount (lambda (mon)
-                                            (let ((currency (gnc:gnc-monetary-commodity mon))
-                                                  (amount (gnc:gnc-monetary-amount mon)))
-                                              (gnc:make-gnc-monetary
-                                               currency
-                                               (gnc-numeric-neg amount))))))
+                          ;; reverse? returns a bool - will check if the cell type has reversible sign,
+                          ;; whether the account is also reversible according to Report Option, or
+                          ;; if Report Option follows Global Settings, will retrieve bool from it.
+                          (reverse? (and (vector-ref cell 1)
+                                         (if account-types-to-reverse
+                                             (member (xaccAccountGetType account) account-types-to-reverse)
+                                             (gnc-reverse-balance account)))))
                       (if cell-content
                           (addto! row-contents
                                   (gnc:make-html-table-cell/markup
                                    "number-cell"
                                    (gnc:html-transaction-anchor
                                     trans
-                                    (if (and reverse?
-                                             (member (xaccAccountGetType account) account-types-to-reverse))
-                                        (reverse-amount cell-content)
+                                    ;; if conditions for reverse are satisfied, apply sign reverse to
+                                    ;; monetary amount
+                                    (if reverse?
+                                        (gnc:monetary-neg cell-content)
                                         cell-content))))
                           (addto! row-contents (gnc:html-make-empty-cell)))))
                   cells)

commit 0854caba95e0b163acb85442653d8ddfb8234114
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 06:16:27 2017 +0800

    BUGFIX: Fix incorrect N_ and _ handling

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 7ced336..0b9b0fc 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -104,8 +104,8 @@
 (define NO-MATCHING-TRANS-TEXT (_ "No transactions were found that \
 match the time interval and account selection specified \
 in the Options panel."))
-(define NO-MATCHING-ACCT-HEADER (N_ "No matching accounts found"))
-(define NO-MATCHING-ACCT-TEXT (N_ "No account were found that match the \
+(define NO-MATCHING-ACCT-HEADER (_ "No matching accounts found"))
+(define NO-MATCHING-ACCT-TEXT (_ "No account were found that match the \
 options specified in the Options panels."))
 
 
@@ -129,87 +129,87 @@ options specified in the Options panels."))
   ;;
   (list (cons 'account-name  (list (cons 'sortkey (list SPLIT-ACCT-FULLNAME))
                                    (cons 'split-sortvalue (lambda (a) (gnc-account-get-full-name (xaccSplitGetAccount a))))
-                                   (cons 'text (N_ "Account Name"))
-                                   (cons 'tip (N_ "Sort & subtotal by account name."))
+                                   (cons 'text (_ "Account Name"))
+                                   (cons 'tip (_ "Sort & subtotal by account name."))
                                    (cons 'renderer-key 'account)))
 
         (cons 'account-code (list (cons 'sortkey (list SPLIT-ACCOUNT ACCOUNT-CODE-))
                                   (cons 'split-sortvalue (lambda (a) (xaccAccountGetCode (xaccSplitGetAccount a))))
-                                  (cons 'text (N_ "Account Code"))
-                                  (cons 'tip (N_ "Sort & subtotal by account code."))
+                                  (cons 'text (_ "Account Code"))
+                                  (cons 'tip (_ "Sort & subtotal by account code."))
                                   (cons 'renderer-key 'account)))
 
         (cons 'date         (list (cons 'sortkey (list SPLIT-TRANS TRANS-DATE-POSTED))
                                   (cons 'split-sortvalue #f)
-                                  (cons 'text (N_ "Date"))
-                                  (cons 'tip (N_ "Sort by date."))
+                                  (cons 'text (_ "Date"))
+                                  (cons 'tip (_ "Sort by date."))
                                   (cons 'renderer-key #f)))
 
         (cons 'reconciled-date (list (cons 'sortkey (list SPLIT-DATE-RECONCILED))
                                      (cons 'split-sortvalue #f)
-                                     (cons 'text (N_ "Reconciled Date"))
-                                     (cons 'tip (N_ "Sort by the Reconciled Date."))
+                                     (cons 'text (_ "Reconciled Date"))
+                                     (cons 'tip (_ "Sort by the Reconciled Date."))
                                      (cons 'renderer-key #f)))
 
         (cons 'register-order (list (cons 'sortkey (list QUERY-DEFAULT-SORT))
                                     (cons 'split-sortvalue #f)
-                                    (cons 'text (N_ "Register Order"))
-                                    (cons 'tip (N_ "Sort as in the register."))
+                                    (cons 'text (_ "Register Order"))
+                                    (cons 'tip (_ "Sort as in the register."))
                                     (cons 'renderer-key #f)))
 
         (cons 'corresponding-acc-name (list (cons 'sortkey (list SPLIT-CORR-ACCT-NAME))
                                             (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountFullName a)))
-                                            (cons 'text (N_ "Other Account Name"))
-                                            (cons 'tip (N_ "Sort by account transferred from/to's name."))
+                                            (cons 'text (_ "Other Account Name"))
+                                            (cons 'tip (_ "Sort by account transferred from/to's name."))
                                             (cons 'renderer-key 'other-acc)))
 
         (cons 'corresponding-acc-code (list (cons 'sortkey (list SPLIT-CORR-ACCT-CODE))
                                             (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountCode a)))
-                                            (cons 'text (N_ "Other Account Code"))
-                                            (cons 'tip (N_ "Sort by account transferred from/to's code."))
+                                            (cons 'text (_ "Other Account Code"))
+                                            (cons 'tip (_ "Sort by account transferred from/to's code."))
                                             (cons 'renderer-key 'other-acct)))
 
         (cons 'amount        (list (cons 'sortkey (list SPLIT-VALUE))
                                    (cons 'split-sortvalue #f)
-                                   (cons 'text (N_ "Amount"))
-                                   (cons 'tip (N_ "Sort by amount."))
+                                   (cons 'text (_ "Amount"))
+                                   (cons 'tip (_ "Sort by amount."))
                                    (cons 'renderer-key #f)))
 
         (cons 'description   (list (cons 'sortkey (list SPLIT-TRANS TRANS-DESCRIPTION))
                                    (cons 'split-sortvalue #f)
-                                   (cons 'text (N_ "Description"))
-                                   (cons 'tip (N_ "Sort by description."))
+                                   (cons 'text (_ "Description"))
+                                   (cons 'tip (_ "Sort by description."))
                                    (cons 'renderer-key #f)))
 
         (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
             (cons 'number    (list (cons 'sortkey (list SPLIT-ACTION))
                                    (cons 'split-sortvalue #f)
-                                   (cons 'text (N_ "Number/Action"))
-                                   (cons 'tip (N_ "Sort by check number/action."))
+                                   (cons 'text (_ "Number/Action"))
+                                   (cons 'tip (_ "Sort by check number/action."))
                                    (cons 'renderer-key #f)))
 
             (cons 'number    (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
                                    (cons 'split-sortvalue #f)
-                                   (cons 'text (N_ "Number"))
-                                   (cons 'tip (N_ "Sort by check/transaction number."))
+                                   (cons 'text (_ "Number"))
+                                   (cons 'tip (_ "Sort by check/transaction number."))
                                    (cons 'renderer-key #f))))
 
         (cons 't-number      (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
                                    (cons 'split-sortvalue #f)
-                                   (cons 'text (N_ "Transaction Number"))
-                                   (cons 'tip (N_ "Sort by transaction number."))
+                                   (cons 'text (_ "Transaction Number"))
+                                   (cons 'tip (_ "Sort by transaction number."))
                                    (cons 'renderer-key #f)))
 
         (cons 'memo          (list (cons 'sortkey (list SPLIT-MEMO))
                                    (cons 'split-sortvalue #f)
-                                   (cons 'text (N_ "Memo"))
-                                   (cons 'tip (N_ "Sort by memo."))
+                                   (cons 'text (_ "Memo"))
+                                   (cons 'tip (_ "Sort by memo."))
                                    (cons 'renderer-key #f)))
 
         (cons 'none          (list (cons 'sortkey '())
                                    (cons 'split-sortvalue #f)
-                                   (cons 'text (N_ "None"))
-                                   (cons 'tip (N_ "Do not sort."))
+                                   (cons 'text (_ "None"))
+                                   (cons 'tip (_ "Do not sort."))
                                    (cons 'renderer-key #f)))))
 
 
@@ -232,61 +232,61 @@ options specified in the Options panels."))
   (list
    (cons 'none (list
                 (cons 'split-sortvalue #f)
-                (cons 'text (N_ "None"))
-                (cons 'tip (N_ "None."))
+                (cons 'text (_ "None"))
+                (cons 'tip (_ "None."))
                 (cons 'renderer-key #f)))
 
    (cons 'weekly (list
                   (cons 'split-sortvalue split-week)
-                  (cons 'text (N_ "Weekly"))
-                  (cons 'tip (N_ "Weekly."))
+                  (cons 'text (_ "Weekly"))
+                  (cons 'tip (_ "Weekly."))
                   (cons 'renderer-key 'week)))
 
    (cons 'monthly (list
                    (cons 'split-sortvalue split-month)
-                   (cons 'text (N_ "Monthly"))
-                   (cons 'tip (N_ "Monthly."))
+                   (cons 'text (_ "Monthly"))
+                   (cons 'tip (_ "Monthly."))
                    (cons 'renderer-key 'month)))
 
    (cons 'quarterly (list
                      (cons 'split-sortvalue split-quarter)
-                     (cons 'text (N_ "Quarterly"))
-                     (cons 'tip (N_ "Quarterly."))
+                     (cons 'text (_ "Quarterly"))
+                     (cons 'tip (_ "Quarterly."))
                      (cons 'renderer-key 'quarter)))
 
    (cons 'yearly (list
                   (cons 'split-sortvalue split-year)
-                  (cons 'text (N_ "Yearly"))
-                  (cons 'tip (N_ "Yearly."))
+                  (cons 'text (_ "Yearly"))
+                  (cons 'tip (_ "Yearly."))
                   (cons 'renderer-key 'year)))))
 
 (define filter-list
   (list
    (cons 'none (list
-                (cons 'text (N_ "None"))
-                (cons 'tip (N_ "Do not do any filtering."))))
+                (cons 'text (_ "None"))
+                (cons 'tip (_ "Do not do any filtering."))))
 
    (cons 'include (list
-                   (cons 'text (N_ "Include Transactions to/from Filter Accounts"))
-                   (cons 'tip (N_ "Include transactions to/from filter accounts only."))))
+                   (cons 'text (_ "Include Transactions to/from Filter Accounts"))
+                   (cons 'tip (_ "Include transactions to/from filter accounts only."))))
 
    (cons 'exclude (list
-                   (cons 'text (N_ "Exclude Transactions to/from Filter Accounts"))
-                   (cons 'tip (N_ "Exclude transactions to/from all filter accounts."))))))
+                   (cons 'text (_ "Exclude Transactions to/from Filter Accounts"))
+                   (cons 'tip (_ "Exclude transactions to/from all filter accounts."))))))
 
 (define show-void-list
   (list
    (cons 'non-void-only (list
-                         (cons 'text (N_ "Non-void only"))
-                         (cons 'tip (N_ "Show only non-voided transactions."))))
-         
+                         (cons 'text (_ "Non-void only"))
+                         (cons 'tip (_ "Show only non-voided transactions."))))
+
    (cons 'void-only (list
-                     (cons 'text (N_ "Void only"))
-                     (cons 'tip (N_ "Show only voided transactions."))))
-   
+                     (cons 'text (_ "Void only"))
+                     (cons 'tip (_ "Show only voided transactions."))))
+
    (cons 'both (list
-                (cons 'text (N_ "Both"))
-                (cons 'tip (N_ "Show both (and include void transactions in totals)."))))))
+                (cons 'text (_ "Both"))
+                (cons 'tip (_ "Show both (and include void transactions in totals)."))))))
 
 (define reconcile-status-list
   ;; value will be either #f to disable reconciled-status filter
@@ -294,30 +294,30 @@ options specified in the Options panels."))
   ;; be '(#\c #\y) to retrieve list of cleared and reconciled splits.
   (list
    (cons  #f (list
-              (cons 'text (N_ "All"))
-              (cons 'tip (N_ "Show All Transactions"))))
+              (cons 'text (_ "All"))
+              (cons 'tip (_ "Show All Transactions"))))
 
    (cons '(#\n) (list
-                 (cons 'text (N_ "Unreconciled"))
-                 (cons 'tip (N_ "Unreconciled only"))))
-   
+                 (cons 'text (_ "Unreconciled"))
+                 (cons 'tip (_ "Unreconciled only"))))
+
    (cons '(#\c) (list
-                 (cons 'text (N_ "Cleared"))
-                 (cons 'tip (N_ "Cleared only"))))
-   
+                 (cons 'text (_ "Cleared"))
+                 (cons 'tip (_ "Cleared only"))))
+
    (cons '(#\y) (list
-                 (cons 'text (N_ "Reconciled"))
-                 (cons 'tip (N_ "Reconciled only"))))))
+                 (cons 'text (_ "Reconciled"))
+                 (cons 'tip (_ "Reconciled only"))))))
 
 
 (define ascending-list
   (list
    (cons 'ascend (list
-                  (cons 'text (N_ "Ascending"))
-                  (cons 'tip (N_ "Smallest to largest, earliest to latest."))))
+                  (cons 'text (_ "Ascending"))
+                  (cons 'tip (_ "Smallest to largest, earliest to latest."))))
    (cons 'descend (list
-                   (cons 'text (N_ "Descending"))
-                   (cons 'tip (N_ "Largest to smallest, latest to earliest."))))))
+                   (cons 'text (_ "Descending"))
+                   (cons 'tip (_ "Largest to smallest, latest to earliest."))))))
 
 (define sign-reverse-list
   (list
@@ -328,18 +328,18 @@ options specified in the Options panels."))
           (cons 'acct-types #f)))
    (cons 'none
          (list
-          (cons 'text (N_ "None"))
-          (cons 'tip (N_ "Don't change any displayed amounts."))
+          (cons 'text (_ "None"))
+          (cons 'tip (_ "Don't change any displayed amounts."))
           (cons 'acct-types '())))
    (cons 'income-expense
          (list
-          (cons 'text (N_ "Income and Expense"))
-          (cons 'tip (N_ "Reverse amount display for Income and Expense Accounts."))
+          (cons 'text (_ "Income and Expense"))
+          (cons 'tip (_ "Reverse amount display for Income and Expense Accounts."))
           (cons 'acct-types (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))))
    (cons 'credit-accounts
          (list
-          (cons 'text (N_ "Credit Accounts"))
-          (cons 'tip (N_ "Reverse amount display for Liability, Payable, Equity, \
+          (cons 'text (_ "Credit Accounts"))
+          (cons 'tip (_ "Reverse amount display for Liability, Payable, Equity, \
 Credit Card, and Income accounts."))
           (cons 'acct-types (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE
                                   ACCT-TYPE-EQUITY ACCT-TYPE-CREDIT
@@ -374,7 +374,7 @@ Credit Card, and Income accounts."))
   (gnc:register-trep-option
    (gnc:make-complex-boolean-option
     gnc:pagename-general optname-common-currency
-    "e" (N_ "Convert all transactions into a common currency.") #f
+    "e" (_ "Convert all transactions into a common currency.") #f
     #f
     (lambda (x)
       (begin
@@ -391,19 +391,19 @@ Credit Card, and Income accounts."))
   (gnc:register-trep-option
    (gnc:make-simple-boolean-option
     gnc:pagename-general optname-orig-currency
-    "f1" (N_ "Also show original currency amounts") #f))
+    "f1" (_ "Also show original currency amounts") #f))
 
   (gnc:register-trep-option
    (gnc:make-simple-boolean-option
     gnc:pagename-general optname-table-export
-    "g" (N_ "Formats the table suitable for cut & paste exporting with extra cells.") #f))
+    "g" (_ "Formats the table suitable for cut & paste exporting with extra cells.") #f))
 
   ;; Filtering Options
 
   (gnc:register-trep-option
    (gnc:make-string-option
     pagename-filter optname-account-matcher
-    "a5" (N_ "Match only accounts whose fullname is matched e.g. ':Travel' will match \
+    "a5" (_ "Match only accounts whose fullname is matched e.g. ':Travel' will match \
 Expenses:Travel:Holiday and Expenses:Business:Travel. It can be left blank, which will \
 disable the matcher.")
     ""))
@@ -412,7 +412,7 @@ disable the matcher.")
    (gnc:make-simple-boolean-option
     pagename-filter optname-account-matcher-regex
     "a6"
-    (N_ "By default the account matcher will search substring only. Set this to true to \
+    (_ "By default the account matcher will search substring only. Set this to true to \
 enable full POSIX regular expressions capabilities. 'Car|Flights' will match both \
 Expenses:Car and Expenses:Flights. Use a period (.) to match a single character e.g. \
 '20../.' will match 'Travel 2017/1 London'. ")
@@ -421,7 +421,7 @@ Expenses:Car and Expenses:Flights. Use a period (.) to match a single character
   (gnc:register-trep-option
    (gnc:make-string-option
     pagename-filter optname-transaction-matcher
-    "i1" (N_ "Match only transactions whose substring is matched e.g. '#gift' \
+    "i1" (_ "Match only transactions whose substring is matched e.g. '#gift' \
 will find all transactions with #gift in description, notes or memo. It can be left \
 blank, which will disable the matcher.")
     ""))
@@ -430,7 +430,7 @@ blank, which will disable the matcher.")
    (gnc:make-simple-boolean-option
     pagename-filter optname-transaction-matcher-regex
     "i2"
-    (N_ "By default the transaction matcher will search substring only. Set this to true to \
+    (_ "By default the transaction matcher will search substring only. Set this to true to \
 enable full POSIX regular expressions capabilities. '#work|#family' will match both \
 tags within description, notes or memo. ")
     #f))
@@ -438,7 +438,7 @@ tags within description, notes or memo. ")
   (gnc:register-trep-option
    (gnc:make-multichoice-option
     pagename-filter optname-reconcile-status
-    "j1" (N_ "Filter by reconcile status.")
+    "j1" (_ "Filter by reconcile status.")
     #f
     (keylist->vectorlist reconcile-status-list)))
 
@@ -455,7 +455,7 @@ tags within description, notes or memo. ")
   (gnc:register-trep-option
    (gnc:make-account-list-option
     gnc:pagename-accounts optname-accounts
-    "a" (N_ "Report on these accounts.")
+    "a" (_ "Report on these accounts.")
     ;; select, by default, no accounts! Selecting all accounts will
     ;; always imply an insanely long waiting time upon opening, and it
     ;; is almost never useful. So we instead display the normal error
@@ -468,7 +468,7 @@ tags within description, notes or memo. ")
   (gnc:register-trep-option
    (gnc:make-account-list-option
     gnc:pagename-accounts optname-filterby
-    "c1" (N_ "Filter on these accounts.")
+    "c1" (_ "Filter on these accounts.")
     (lambda ()
       '())
     #f #t))
@@ -476,7 +476,7 @@ tags within description, notes or memo. ")
   (gnc:register-trep-option
    (gnc:make-multichoice-callback-option
     gnc:pagename-accounts optname-filtertype
-    "c" (N_ "Filter account.")
+    "c" (_ "Filter account.")
     'none
     (keylist->vectorlist filter-list)
     #f
@@ -548,7 +548,7 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-multichoice-callback-option
       pagename-sorting optname-prime-sortkey
-      "a" (N_ "Sort by this criterion first.")
+      "a" (_ "Sort by this criterion first.")
       prime-sortkey
       key-choice-list #f
       (lambda (x)
@@ -559,28 +559,28 @@ tags within description, notes or memo. ")
      (gnc:make-simple-boolean-option
       pagename-sorting optname-full-account-name
       "j1"
-      (N_ "Show the full account name for subtotals and subheadings?")
+      (_ "Show the full account name for subtotals and subheadings?")
       #f))
 
     (gnc:register-trep-option
      (gnc:make-simple-boolean-option
       pagename-sorting optname-show-account-code
       "j2"
-      (N_ "Show the account code for subtotals and subheadings?")
+      (_ "Show the account code for subtotals and subheadings?")
       #f))
 
     (gnc:register-trep-option
      (gnc:make-simple-boolean-option
       pagename-sorting optname-show-account-description
       "j3"
-      (N_ "Show the account description for subheadings?")
+      (_ "Show the account description for subheadings?")
       #f))
     
     (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       pagename-sorting optname-prime-subtotal
       "e5"
-      (N_ "Subtotal according to the primary key?")
+      (_ "Subtotal according to the primary key?")
       prime-sortkey-subtotal-true #f
       (lambda (x)
         (set! prime-sortkey-subtotal-true x)
@@ -589,14 +589,14 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-multichoice-option
       pagename-sorting optname-prime-date-subtotal
-      "e2" (N_ "Do a date subtotal.")
+      "e2" (_ "Do a date subtotal.")
       'monthly
       date-subtotal-choice-list))
 
     (gnc:register-trep-option
      (gnc:make-multichoice-option
       pagename-sorting optname-prime-sortorder
-      "e" (N_ "Order of primary sorting.")
+      "e" (_ "Order of primary sorting.")
       'ascend
       ascending-choice-list))
 
@@ -605,7 +605,7 @@ tags within description, notes or memo. ")
      (gnc:make-multichoice-callback-option
       pagename-sorting optname-sec-sortkey
       "f"
-      (N_ "Sort by this criterion second.")
+      (_ "Sort by this criterion second.")
       sec-sortkey
       key-choice-list #f
       (lambda (x)
@@ -616,7 +616,7 @@ tags within description, notes or memo. ")
      (gnc:make-complex-boolean-option
       pagename-sorting optname-sec-subtotal
       "i5"
-      (N_ "Subtotal according to the secondary key?")
+      (_ "Subtotal according to the secondary key?")
       sec-sortkey-subtotal-true #f
       (lambda (x)
         (set! sec-sortkey-subtotal-true x)
@@ -625,14 +625,14 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-multichoice-option
       pagename-sorting optname-sec-date-subtotal
-      "i2" (N_ "Do a date subtotal.")
+      "i2" (_ "Do a date subtotal.")
       'monthly
       date-subtotal-choice-list))
 
     (gnc:register-trep-option
      (gnc:make-multichoice-option
       pagename-sorting optname-sec-sortorder
-      "i" (N_ "Order of Secondary sorting.")
+      "i" (_ "Order of Secondary sorting.")
       'ascend
       ascending-choice-list)))
 
@@ -677,37 +677,37 @@ tags within description, notes or memo. ")
      ;; One list per option here with: option-name, sort-tag,
      ;; help-string, default-value
      (list
-      (list (N_ "Date")                         "a"  (N_ "Display the date?") #t)
-      (list (N_ "Reconciled Date")              "a2" (N_ "Display the reconciled date?") #f)
+      (list (N_ "Date")                         "a"  (_ "Display the date?") #t)
+      (list (N_ "Reconciled Date")              "a2" (_ "Display the reconciled date?") #f)
       (if BOOK-SPLIT-ACTION
-          (list (N_ "Num/Action")               "b"  (N_ "Display the check number?") #t)
-          (list (N_ "Num")                      "b"  (N_ "Display the check number?") #t))
-      (list (N_ "Description")                  "c"  (N_ "Display the description?") #t)
-      (list (N_ "Notes")                        "d2" (N_ "Display the notes if the memo is unavailable?") #t)
+          (list (N_ "Num/Action")               "b"  (_ "Display the check number?") #t)
+          (list (N_ "Num")                      "b"  (_ "Display the check number?") #t))
+      (list (N_ "Description")                  "c"  (_ "Display the description?") #t)
+      (list (N_ "Notes")                        "d2" (_ "Display the notes if the memo is unavailable?") #t)
       ;; account name option appears here
-      (list (N_ "Use Full Account Name")        "f"  (N_ "Display the full account name?") #t)
-      (list (N_ "Account Code")                 "g"  (N_ "Display the account code?") #f)
+      (list (N_ "Use Full Account Name")        "f"  (_ "Display the full account name?") #t)
+      (list (N_ "Account Code")                 "g"  (_ "Display the account code?") #f)
       ;; other account name option appears here
-      (list (N_ "Use Full Other Account Name")  "i"  (N_ "Display the full account name?") #f)
-      (list (N_ "Other Account Code")           "j"  (N_ "Display the other account code?") #f)
-      (list (N_ "Shares")                       "k"  (N_ "Display the number of shares?") #f)
-      (list (N_ "Price")                        "l"  (N_ "Display the shares price?") #f)
+      (list (N_ "Use Full Other Account Name")  "i"  (_ "Display the full account name?") #f)
+      (list (N_ "Other Account Code")           "j"  (_ "Display the other account code?") #f)
+      (list (N_ "Shares")                       "k"  (_ "Display the number of shares?") #f)
+      (list (N_ "Price")                        "l"  (_ "Display the shares price?") #f)
       ;; note the "Amount" multichoice option in between here
-      (list (N_ "Running Balance")              "n"  (N_ "Display a running balance?") #f)
-      (list (N_ "Totals")                       "o"  (N_ "Display the totals?") #t)))
+      (list (N_ "Running Balance")              "n"  (_ "Display a running balance?") #f)
+      (list (N_ "Totals")                       "o"  (_ "Display the totals?") #t)))
 
     (if BOOK-SPLIT-ACTION
         (gnc:register-trep-option
          (gnc:make-simple-boolean-option
           gnc:pagename-display (N_ "Trans Number")
-          "b2" (N_ "Display the trans number?") #f)))
+          "b2" (_ "Display the trans number?") #f)))
 
     ;; Add an option to display the memo, and disable the notes option
     ;; when memos are not included.
     (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       gnc:pagename-display (N_ "Memo")
-      "d"  (N_ "Display the memo?") #t
+      "d"  (_ "Display the memo?") #t
       disp-memo?
       (lambda (x)
         (set! disp-memo? x)
@@ -717,7 +717,7 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       gnc:pagename-display (N_ "Account Name")
-      "e"  (N_ "Display the account name?") #t
+      "e"  (_ "Display the account name?") #t
       disp-accname?
       (lambda (x)
         (set! disp-accname? x)
@@ -727,7 +727,7 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       gnc:pagename-display (N_ "Other Account Name")
-      "h5"  (N_ "Display the other account name? (if this is a split transaction, this parameter is guessed).") #f
+      "h5"  (_ "Display the other account name? (if this is a split transaction, this parameter is guessed).") #f
       disp-other-accname?
       (lambda (x)
         (set! disp-other-accname? x)
@@ -736,14 +736,14 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-multichoice-callback-option
       gnc:pagename-display optname-detail-level
-      "h" (N_ "Amount of detail to display per transaction.")
+      "h" (_ "Amount of detail to display per transaction.")
       'single
       (list (vector 'multi-line
-                    (N_ "Multi-Line")
-                    (N_ "Display all splits in a transaction on a separate line."))
+                    (_ "Multi-Line")
+                    (_ "Display all splits in a transaction on a separate line."))
             (vector 'single
-                    (N_ "Single")
-                    (N_ "Display one line per transaction, merging multiple splits where required.")))
+                    (_ "Single")
+                    (_ "Display one line per transaction, merging multiple splits where required.")))
       #f
       (lambda (x)
         (set! detail-is-single? (eq? x 'single))
@@ -752,12 +752,12 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-multichoice-callback-option
       gnc:pagename-display (N_ "Amount")
-      "m" (N_ "Display the amount?")
+      "m" (_ "Display the amount?")
       'single
       (list
-       (vector 'none   (N_ "None") (N_ "No amount display."))
-       (vector 'single (N_ "Single") (N_ "Single Column Display."))
-       (vector 'double (N_ "Double") (N_ "Two Column Display.")))
+       (vector 'none   (_ "None") (_ "No amount display."))
+       (vector 'single (_ "Single") (_ "Single Column Display."))
+       (vector 'double (_ "Double") (_ "Two Column Display.")))
       #f
       (lambda (x)
         (set! amount-is-single? (eq? x 'single))
@@ -766,7 +766,7 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-multichoice-option
       gnc:pagename-display (N_ "Sign Reverses")
-      "m1" (N_ "Reverse amount display for certain account types.")
+      "m1" (_ "Reverse amount display for certain account types.")
       'global
       (keylist->vectorlist sign-reverse-list))))
 
@@ -920,15 +920,15 @@ tags within description, notes or memo. ")
          ;; merge? to merge with the next cell (ie for debit/credit cells)
          ;; merging-function - function (usually gnc-numeric-add/sub-fixed to apply to dual-subtotal
          (if (column-uses? 'amount-single)
-             (list (vector (header-commodity (N_ "Amount"))
+             (list (vector (header-commodity (_ "Amount"))
                            amount #t #t
                            (vector #f #f)))
              '())
          (if (column-uses? 'amount-double)
-             (list (vector (header-commodity (N_ "Debit"))
+             (list (vector (header-commodity (_ "Debit"))
                            debit-amount #f #t
                            (vector #t gnc-numeric-add))
-                   (vector (header-commodity (N_ "Credit"))
+                   (vector (header-commodity (_ "Credit"))
                            credit-amount #f #t
                            (vector #f gnc-numeric-sub)))
              '())
@@ -951,7 +951,7 @@ tags within description, notes or memo. ")
              '())
 
          (if (column-uses? 'running-balance)
-             (list (vector (N_ "Running Balance")
+             (list (vector (_ "Running Balance")
                            running-balance #t #f
                            (vector #f #f)))
              '()))))
@@ -1545,11 +1545,11 @@ tags within description, notes or memo. ")
             (highlight
              (string-append optname-account-matcher
                             (if (opt-val pagename-filter optname-account-matcher-regex)
-                                (N_ " regex")
+                                (_ " regex")
                                 ""))
              account-matcher)
             (highlight
-             (N_ "Accounts produced")
+             (_ "Accounts produced")
              (string-join (map xaccAccountGetName c_account_1) ", "))))
        (if (eq? filter-mode 'none)
            ""
@@ -1562,7 +1562,7 @@ tags within description, notes or memo. ")
             (highlight
              (string-append optname-transaction-matcher
                             (if (opt-val pagename-filter optname-transaction-matcher-regex)
-                                (N_ " regex")
+                                (_ " regex")
                                 ""))
              transaction-matcher)))
        (if reconcile-status-filter

commit c26af85e82c37edb3ceecb3833a4f2f04c9244fc
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 06:15:04 2017 +0800

    ENH: Upgrade Sign Reversal to use global preference by default

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 255a05c..7ced336 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -321,6 +321,11 @@ options specified in the Options panels."))
 
 (define sign-reverse-list
   (list
+   (cons 'global
+         (list
+          (cons 'text (_ "Use Global Preference"))
+          (cons 'tip (_ "Use reversing option specified in global preference."))
+          (cons 'acct-types #f)))
    (cons 'none
          (list
           (cons 'text (N_ "None"))
@@ -762,7 +767,7 @@ tags within description, notes or memo. ")
      (gnc:make-multichoice-option
       gnc:pagename-display (N_ "Sign Reverses")
       "m1" (N_ "Reverse amount display for certain account types.")
-      'credit-accounts
+      'global
       (keylist->vectorlist sign-reverse-list))))
 
   (gnc:options-set-default-section options gnc:pagename-general)
@@ -1136,8 +1141,20 @@ tags within description, notes or memo. ")
                  (let* ((calculator (vector-ref cell 1))
                         (reverse? (vector-ref cell 2))
                         (subtotal? (vector-ref cell 3))
-                        (calculated (calculator split)))
-                   (vector calculated reverse? subtotal?)))
+                        (calculated (calculator split))
+                        (reverse-amount (lambda (mon)
+                                            (let ((currency (gnc:gnc-monetary-commodity mon))
+                                                  (amount (gnc:gnc-monetary-amount mon)))
+                                              (gnc:make-gnc-monetary
+                                               currency
+                                               (gnc-numeric-neg amount))))))
+                   (vector (if (and reverse?
+                                    (if account-types-to-reverse
+                                        (member (xaccAccountGetType account) account-types-to-reverse)
+                                        (gnc-reverse-balance account)))
+                               (reverse-amount calculated)
+                               calculated)
+                           subtotal?)))
                cell-calculators))
 
         (if (column-uses? 'date)

commit e8dc5c545d3baa5b06e69a030abac371ce61bb90
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Nov 26 17:31:05 2017 +0800

    REFACTOR: move calculated-cells to allow access from add-subtotal-row
    
    This will negate the need to zip calculated cells to call add-subtotal-row.
    Note git-diff seems to think lots of functions were moved - it's calculated-cells
    that's been moved by a few lines so that it is accessible to add-subtotal-row.
    
    Also rename a few keywords to better describe their use.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index ca112cb..255a05c 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -827,8 +827,8 @@ tags within description, notes or memo. ")
 
     (define (column-uses? param)
       (cdr (assq param used-columns)))
-    
-    (define headings
+
+    (define left-columns
       (let* ((add-if (lambda pred? . items) (if pred? items '())))
         (append
          (add-if (column-uses? 'date)
@@ -855,106 +855,7 @@ tags within description, notes or memo. ")
          (add-if (column-uses? 'shares)
                  (_ "Shares"))
          (add-if (column-uses? 'price)
-                 (_ "Price")))))
-
-    (define width (length headings))
-
-    (define (add-subheading data subheading-style)
-      (let ((heading-cell (gnc:make-html-table-cell data)))
-        (gnc:html-table-cell-set-colspan! heading-cell (+ width width-amount))
-        (gnc:html-table-append-row/markup!
-         table subheading-style
-         (list heading-cell))))
-
-
-    (define (add-subtotal-row subtotal-string subtotal-collectors-and-calculated-cells subtotal-style)
-      (let* ((row-contents '())
-             (subtotal-collectors (map car subtotal-collectors-and-calculated-cells))
-             (calculated-cells  (map cadr subtotal-collectors-and-calculated-cells))
-             (merge-list (map (lambda (cell) (vector-ref cell 4)) calculated-cells))
-             (columns (map (lambda (coll) (coll 'format gnc:make-gnc-monetary #f)) subtotal-collectors))
-             (list-of-commodities (delete-duplicates (map gnc:gnc-monetary-commodity (concatenate columns))
-                                                     gnc-commodity-equal)))
-
-        (define (retrieve-commodity list-of-monetary commodity)
-          (and (not (null? list-of-monetary))
-               (if (gnc-commodity-equal (gnc:gnc-monetary-commodity (car list-of-monetary)) commodity)
-                   (car list-of-monetary)
-                   (retrieve-commodity (cdr list-of-monetary) commodity))))
-
-        (define (add-first-column string)
-          (if export?
-              (begin
-                (addto! row-contents (gnc:make-html-table-cell/markup "total-label-cell" string))
-                (for-each (lambda (cell) (addto! row-contents cell))
-                          (gnc:html-make-empty-cells (- width 1))))
-              (addto! row-contents (gnc:make-html-table-cell/size/markup 1 width "total-label-cell" string))))
-
-        (define (add-columns commodity)
-          (let ((start-dual-column? #f)
-                (dual-subtotal (gnc:make-gnc-numeric 0 1)))
-            (for-each (lambda (column merge-entry)
-                        (let* ((mon (retrieve-commodity column commodity))
-                               (col (and mon (gnc:gnc-monetary-amount mon)))
-                               (merge? (vector-ref merge-entry 0))
-                               (merge-fn (vector-ref merge-entry 1)))
-                          (if merge?
-                              ;; We're merging. Run merge-fn (usu gnc-numeric-add or sub)
-                              ;; and store total in dual-subtotal. Do NOT add column.
-                              (begin
-                                (if column-amount
-                                    (set! dual-subtotal
-                                          (merge-fn dual-subtotal column-amount
-                                                    GNC-DENOM-AUTO GNC-HOW-RND-ROUND)))
-                                (set! start-dual-column? #t))
-                              (if start-dual-column?
-                                  (begin
-                                    ;; We've completed merging. Add this column amount
-                                    ;; and add the columns.
-                                    (if column-amount
-                                        (set! dual-subtotal
-                                              (merge-fn dual-subtotal column-amount
-                                                        GNC-DENOM-AUTO GNC-HOW-RND-ROUND)))
-                                    (if (gnc-numeric-positive-p dual-subtotal)
-                                        (begin
-                                          (addto! row-contents
-                                                  (gnc:make-html-table-cell/markup
-                                                   "total-number-cell"
-                                                   (gnc:make-gnc-monetary commodity dual-subtotal)))
-                                          (addto! row-contents ""))
-                                        (begin
-                                          (addto! row-contents "")
-                                          (addto! row-contents
-                                                  (gnc:make-html-table-cell/markup
-                                                   "total-number-cell"
-                                                   (gnc:make-gnc-monetary
-                                                    commodity
-                                                    (gnc-numeric-neg dual-subtotal))))))
-                                    (set! start-dual-column? #f)
-                                    (set! dual-subtotal (gnc:make-gnc-numeric 0 1)))
-				  ;; Default; not merging/completed merge. Just
-				  ;; display monetary amount
-                                  (addto! row-contents
-                                          (gnc:make-html-table-cell/markup "total-number-cell" mon))))))
-                      columns
-                      merge-list)))
-
-        ;;first row
-        (add-first-column subtotal-string)
-        (add-columns (and (pair? list-of-commodities)
-                          (car list-of-commodities))) ;to account for empty-row subtotals
-        (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents))
-
-        ;;subsequent rows
-        (if (pair? list-of-commodities)
-            (for-each (lambda (commodity)
-                        (set! row-contents '())
-                        (add-first-column "")
-                        (add-columns commodity)
-                        (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents)))
-                      (cdr list-of-commodities)))))
-
-    (define (total-string str) (string-append (_ "Total For ") str))
+                 (_ "Price"))))
 
     ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;;
@@ -998,11 +899,9 @@ tags within description, notes or memo. ")
                                           #f
                                           (gnc:monetary-neg (split-value s)))))
            (original-amount (lambda (s) (gnc:make-gnc-monetary (currency s) (damount s))))
-
            (original-debit-amount (lambda (s) (if (gnc-numeric-positive-p (damount s))
                                                   (original-amount s)
                                                   #f)))
-
            (original-credit-amount (lambda (s) (if (gnc-numeric-positive-p (damount s))
                                                    #f
                                                    (gnc:monetary-neg (original-amount s)))))
@@ -1052,12 +951,113 @@ tags within description, notes or memo. ")
                            (vector #f #f)))
              '()))))
 
-    (define amount-headings
+    (define headings-left-columns
+      (map (lambda (column)
+             (vector-ref column 0))
+           left-columns))
+
+    (define headings-right-columns
       (map (lambda (column)
              (vector-ref column 0))
            calculated-cells))
 
-    (define width-amount (length amount-headings))
+    (define width-left-columns (length left-columns))
+    (define width-right-columns (length calculated-cells))
+
+    (define (add-subheading data subheading-style)
+      (let ((heading-cell (gnc:make-html-table-cell data)))
+        (gnc:html-table-cell-set-colspan! heading-cell (+ width-left-columns width-right-columns))
+        (gnc:html-table-append-row/markup!
+         table subheading-style (list heading-cell))))
+
+    (define (add-subtotal-row subtotal-string subtotal-collectors subtotal-style)
+      (let* ((row-contents '())
+             (merge-list (map (lambda (cell) (vector-ref cell 4)) calculated-cells))
+             (columns (map (lambda (coll) (coll 'format gnc:make-gnc-monetary #f)) subtotal-collectors))
+             (list-of-commodities (delete-duplicates (map gnc:gnc-monetary-commodity (concatenate columns))
+                                                     gnc-commodity-equal)))
+
+        (define (retrieve-commodity list-of-monetary commodity)
+          (if (null? list-of-monetary)
+              #f
+              (if (gnc-commodity-equal (gnc:gnc-monetary-commodity (car list-of-monetary)) commodity)
+                  (car list-of-monetary)
+                  (retrieve-commodity (cdr list-of-monetary) commodity))))
+
+        (define (add-first-column string)
+          (if export?
+              (begin
+                (addto! row-contents (gnc:make-html-table-cell/markup "total-label-cell" string))
+                (for-each (lambda (cell) (addto! row-contents cell))
+                          (gnc:html-make-empty-cells (- width-left-columns 1))))
+              (addto! row-contents (gnc:make-html-table-cell/size/markup 1 width-left-columns "total-label-cell" string))))
+
+        (define (add-columns commodity)
+          (let ((start-dual-column? #f)
+                (dual-subtotal (gnc:make-gnc-numeric 0 1)))
+            (for-each (lambda (column merge-entry)
+                        (let* ((mon (retrieve-commodity column commodity))
+                               (column-amount (and mon (gnc:gnc-monetary-amount mon)))
+                               (merge? (vector-ref merge-entry 0))
+                               (merge-fn (vector-ref merge-entry 1)))
+                          (if merge?
+                              ;; We're merging. Run merge-fn (usu gnc-numeric-add or sub)
+                              ;; and store total in dual-subtotal. Do NOT add column.
+                              (begin
+                                (if column-amount
+                                    (set! dual-subtotal
+                                          (merge-fn dual-subtotal column-amount
+                                                    GNC-DENOM-AUTO GNC-HOW-RND-ROUND)))
+                                (set! start-dual-column? #t))
+                              (if start-dual-column?
+                                  (begin
+                                    ;; We've completed merging. Add this column amount
+                                    ;; and add the columns.
+                                    (if column-amount
+                                        (set! dual-subtotal
+                                              (merge-fn dual-subtotal column-amount
+                                                        GNC-DENOM-AUTO GNC-HOW-RND-ROUND)))
+                                    (if (gnc-numeric-positive-p dual-subtotal)
+                                        (begin
+                                          (addto! row-contents
+                                                  (gnc:make-html-table-cell/markup
+                                                   "total-number-cell"
+                                                   (gnc:make-gnc-monetary commodity dual-subtotal)))
+                                          (addto! row-contents ""))
+                                        (begin
+                                          (addto! row-contents "")
+                                          (addto! row-contents
+                                                  (gnc:make-html-table-cell/markup
+                                                   "total-number-cell"
+                                                   (gnc:make-gnc-monetary
+                                                    commodity
+                                                    (gnc-numeric-neg dual-subtotal))))))
+                                    (set! start-dual-column? #f)
+                                    (set! dual-subtotal (gnc:make-gnc-numeric 0 1)))
+                                  ;; Default; not merging/completed merge. Just
+                                  ;; display monetary amount
+                                  (addto! row-contents
+                                          (gnc:make-html-table-cell/markup "total-number-cell" mon))))))
+                      columns
+                      merge-list)))
+
+        ;;first row
+        (add-first-column subtotal-string)
+        (add-columns (if (pair? list-of-commodities)
+                         (car list-of-commodities)
+                         #f)) ;to account for empty-row subtotals
+        (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents))
+
+        ;;subsequent rows
+        (if (pair? list-of-commodities)
+            (for-each (lambda (commodity)
+                        (set! row-contents '())
+                        (add-first-column "")
+                        (add-columns commodity)
+                        (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents)))
+                      (cdr list-of-commodities)))))
+
+    (define (total-string str) (string-append (_ "Total For ") str))
 
     ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -1269,10 +1269,10 @@ tags within description, notes or memo. ")
              table def:grand-total-style
              (list
               (gnc:make-html-table-cell/size
-               1 (+ width width-amount) (gnc:make-html-text (gnc:html-markup-hr)))))
+               1 (+ width-left-columns width-right-columns) (gnc:make-html-text (gnc:html-markup-hr)))))
 
             (if (opt-val gnc:pagename-display "Totals")
-                (add-subtotal-row (render-grand-total) (zip total-collectors calculated-cells) def:grand-total-style)))
+                (add-subtotal-row (render-grand-total) total-collectors def:grand-total-style)))
 
           (let* ((current (car splits))
                  (rest (cdr splits))
@@ -1321,13 +1321,13 @@ tags within description, notes or memo. ")
                       (begin
                         (add-subtotal-row (total-string
                                            (render-summary current secondary-renderer-key #f))
-                                          (zip secondary-subtotal-collectors calculated-cells)
+                                          secondary-subtotal-collectors
                                           def:secondary-subtotal-style)
                         (for-each (lambda (coll) (coll 'reset #f #f))
                                   secondary-subtotal-collectors)))
                   (add-subtotal-row (total-string
                                      (render-summary current primary-renderer-key #f))
-                                    (zip primary-subtotal-collectors calculated-cells)
+                                    primary-subtotal-collectors
                                     def:primary-subtotal-style)
                   (for-each (lambda (coll) (coll 'reset #f #f))
                             primary-subtotal-collectors)
@@ -1346,7 +1346,7 @@ tags within description, notes or memo. ")
                                                (secondary-subtotal-comparator next))))))
                     (begin (add-subtotal-row (total-string
                                               (render-summary current secondary-renderer-key #f))
-                                             (zip secondary-subtotal-collectors calculated-cells)
+                                             secondary-subtotal-collectors
                                              def:secondary-subtotal-style)
                            (for-each (lambda (coll) (coll 'reset #f #f))
                                      secondary-subtotal-collectors)
@@ -1360,7 +1360,7 @@ tags within description, notes or memo. ")
                                     secondary-subtotal-collectors
                                     total-collectors))))
 
-    (gnc:html-table-set-col-headers! table (concatenate (list headings amount-headings)))
+    (gnc:html-table-set-col-headers! table (concatenate (list headings-left-columns headings-right-columns)))
 
     (if primary-renderer-key
         (add-subheading (render-summary (car splits) primary-renderer-key #t)

commit 6f87138bce21a5b13fbb08acf1967b0860c2ff6d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 06:04:07 2017 +0800

    ENH: 'original currency amt' now shows dual columns

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 15e8e6d..ca112cb 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -998,6 +998,14 @@ tags within description, notes or memo. ")
                                           #f
                                           (gnc:monetary-neg (split-value s)))))
            (original-amount (lambda (s) (gnc:make-gnc-monetary (currency s) (damount s))))
+
+           (original-debit-amount (lambda (s) (if (gnc-numeric-positive-p (damount s))
+                                                  (original-amount s)
+                                                  #f)))
+
+           (original-credit-amount (lambda (s) (if (gnc-numeric-positive-p (damount s))
+                                                   #f
+                                                   (gnc:monetary-neg (original-amount s)))))
            (running-balance (lambda (s) (gnc:make-gnc-monetary (currency s) (xaccSplitGetBalance s)))))
         (append
          ;; each column will be a vector
@@ -1020,11 +1028,24 @@ tags within description, notes or memo. ")
                            credit-amount #f #t
                            (vector #f gnc-numeric-sub)))
              '())
-         (if (column-uses? 'amount-original-currency)
-             (list (vector (N_ "Original")
+
+         (if (and (column-uses? 'amount-original-currency)
+                  (column-uses? 'amount-single))
+             (list (vector (_ "Amount")
                            original-amount #t #t
                            (vector #f #f)))
              '())
+
+         (if (and (column-uses? 'amount-original-currency)
+                  (column-uses? 'amount-double))
+             (list (vector (_ "Debit")
+                           original-debit-amount #f #t
+                           (vector #t gnc-numeric-add))
+                   (vector (_ "Credit")
+                           original-credit-amount #f #t
+                           (vector #f gnc-numeric-sub)))
+             '())
+
          (if (column-uses? 'running-balance)
              (list (vector (N_ "Running Balance")
                            running-balance #t #f

commit 2e06c8fc33e2425a03a9c70f241033ac43e4c3a6
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 05:59:09 2017 +0800

    REFACTOR+ENH:Add common-currency mnemonic to header if enabled
    
    This requires refactoring calculated cells to centralize
    the headers and enable their modification.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 5ae12fe..15e8e6d 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -796,6 +796,7 @@ tags within description, notes or memo. ")
           (cons 'price (opt-val gnc:pagename-display (N_ "Price")))
           (cons 'amount-single (eq? amount-setting 'single))
           (cons 'amount-double (eq? amount-setting 'double))
+          (cons 'common-currency (opt-val gnc:pagename-general optname-common-currency))
           (cons 'amount-original-currency
                 (and (opt-val gnc:pagename-general optname-common-currency)
                      (opt-val gnc:pagename-general optname-orig-currency)))
@@ -854,22 +855,9 @@ tags within description, notes or memo. ")
          (add-if (column-uses? 'shares)
                  (_ "Shares"))
          (add-if (column-uses? 'price)
-                 (_ "Price"))))
-    
-    (define amount-headings
-      (let* ((add-if (lambda pred? . items) (if pred? items '())))
-        (append (add-if (column-uses? 'amount-single)
-                        (_ "Amount"))
-                (add-if (column-uses? 'amount-double)
-                        (_ "Debit")
-                        (_ "Credit"))
-                (add-if (column-uses? 'amount-original-currency)
-                        (_ "Original"))
-                (add-if (column-uses? 'running-balance)
-                        (_ "Balance")))))
+                 (_ "Price")))))
 
     (define width (length headings))
-    (define width-amount (length amount-headings))
 
     (define (add-subheading data subheading-style)
       (let ((heading-cell (gnc:make-html-table-cell data)))
@@ -981,9 +969,19 @@ tags within description, notes or memo. ")
                                     (xaccSplitGetAmount s))))
            (trans-date (lambda (s) (gnc-transaction-get-date-posted (xaccSplitGetTransaction s))))
            (currency (lambda (s) (xaccAccountGetCommodity (xaccSplitGetAccount s))))
-           (report-currency (lambda (s) (if (opt-val gnc:pagename-general optname-common-currency)
+           (report-currency (lambda (s) (if (column-uses? 'common-currency)
                                             (opt-val gnc:pagename-general optname-currency)
                                             (currency s))))
+           (header-commodity (lambda (str)
+                               (string-append
+                                str
+                                (if (column-uses? 'common-currency)
+                                    (string-append
+                                     "<br>"
+                                     (gnc-commodity-get-mnemonic
+                                      (opt-val gnc:pagename-general optname-currency)))
+                                    ""))))
+           (time64CanonicalDayTime (lambda (t64)  (gnc-tm-set-day-middle (gnc-localtime t64))))
            (convert (lambda (s num)
                       (gnc:exchange-by-pricedb-nearest
                        (gnc:make-gnc-monetary (currency s) num)
@@ -1010,19 +1008,36 @@ tags within description, notes or memo. ")
          ;; merge? to merge with the next cell (ie for debit/credit cells)
          ;; merging-function - function (usually gnc-numeric-add/sub-fixed to apply to dual-subtotal
          (if (column-uses? 'amount-single)
-             (list (vector "Amount" amount #t #t (vector #f #f)))
+             (list (vector (header-commodity (N_ "Amount"))
+                           amount #t #t
+                           (vector #f #f)))
              '())
          (if (column-uses? 'amount-double)
-             (list (vector "Debit" debit-amount #f #t (vector #t gnc-numeric-add))
-                   (vector "Credit" credit-amount #f #t (vector #f gnc-numeric-sub)))
+             (list (vector (header-commodity (N_ "Debit"))
+                           debit-amount #f #t
+                           (vector #t gnc-numeric-add))
+                   (vector (header-commodity (N_ "Credit"))
+                           credit-amount #f #t
+                           (vector #f gnc-numeric-sub)))
              '())
          (if (column-uses? 'amount-original-currency)
-             (list (vector "Original" original-amount #f #t (vector #f #f)))
+             (list (vector (N_ "Original")
+                           original-amount #t #t
+                           (vector #f #f)))
              '())
          (if (column-uses? 'running-balance)
-             (list (vector "Running Balance" running-balance #t #f (vector #f #f)))
+             (list (vector (N_ "Running Balance")
+                           running-balance #t #f
+                           (vector #f #f)))
              '()))))
 
+    (define amount-headings
+      (map (lambda (column)
+             (vector-ref column 0))
+           calculated-cells))
+
+    (define width-amount (length amount-headings))
+
     ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
     ;; renderers

commit aaa23dc51e393917a38e8cff9016f1f8bcc5caa0
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 05:54:29 2017 +0800

    REFACTOR: move column-uses? location
    
    This commit moves column-uses? helper to allow access
    to used-columns instead of needing to pass this parameter
    around with every call.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 44eb8f3..5ae12fe 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -812,60 +812,11 @@ tags within description, notes or memo. ")
           (cons 'sort-account-description (opt-val pagename-sorting (N_ "Show Account Description")))
           (cons 'notes (opt-val gnc:pagename-display (N_ "Notes")))))
 
-  (define (column-uses? param columns-used)
-    (cdr (assq param columns-used)))
-
-  (define (make-heading-list columns-used)
-    (define (add-if pred? . items) (if pred? items '()))
-    (append
-     (add-if (column-uses? 'date columns-used)
-             (_ "Date"))
-     (add-if (column-uses? 'reconciled-date columns-used)
-             (_ "Reconciled Date"))
-     (add-if (column-uses? 'num columns-used)
-             (if (and (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-                      (opt-val gnc:pagename-display (N_ "Trans Number")))
-                 (_ "Num/T-Num")
-                 (_ "Num")))
-     (add-if (column-uses? 'description columns-used)
-             (_ "Description"))
-     (add-if (column-uses? 'memo columns-used)
-             (if (column-uses? 'notes columns-used)
-                 (string-append (_ "Memo") "/" (_ "Notes"))
-                 (_ "Memo")))
-     (add-if (or (column-uses? 'account-name columns-used)
-                 (column-uses? 'account-code columns-used))
-             (_ "Account"))
-     (add-if (or (column-uses? 'other-account-name columns-used)
-                 (column-uses? 'other-account-code columns-used))
-             (_ "Transfer from/to"))
-     (add-if (column-uses? 'shares columns-used)
-             (_ "Shares"))
-     (add-if (column-uses? 'price columns-used)
-             (_ "Price"))))
-
-  (define (make-amount-heading-list columns-used)
-    (define (add-if pred? . items) (if pred? items '()))
-    (append
-     (add-if (column-uses? 'amount-single columns-used)
-             (_ "Amount"))
-     ;; FIXME: Proper labels: what?
-     (add-if (column-uses? 'amount-double columns-used)
-             (_ "Debit")
-             (_ "Credit"))
-     (add-if (column-uses? 'amount-original-currency columns-used)
-             (_ "Original"))
-     (add-if (column-uses? 'running-balance columns-used)
-             (_ "Balance"))))
 
   (let* ((work-to-do (length splits))
          (work-done 0)
          (table (gnc:make-html-table))
          (used-columns (build-columns-used))
-         (headings (make-heading-list used-columns))
-         (amount-headings (make-amount-heading-list used-columns))
-         (width (length headings))
-         (width-amount (length amount-headings))
          (account-types-to-reverse
           (keylist-get-info sign-reverse-list
                             (opt-val gnc:pagename-display (N_ "Sign Reverses"))
@@ -873,6 +824,53 @@ tags within description, notes or memo. ")
          (is-multiline? (eq? (opt-val gnc:pagename-display optname-detail-level) 'multi-line))
          (export? (opt-val gnc:pagename-general optname-table-export)))
 
+    (define (column-uses? param)
+      (cdr (assq param used-columns)))
+    
+    (define headings
+      (let* ((add-if (lambda pred? . items) (if pred? items '())))
+        (append
+         (add-if (column-uses? 'date)
+                 (_ "Date"))
+         (add-if (column-uses? 'reconciled-date)
+                 (_ "Reconciled Date"))
+         (add-if (column-uses? 'num)
+                 (if (and BOOK-SPLIT-ACTION
+                          (opt-val gnc:pagename-display (N_ "Trans Number")))
+                     (_ "Num/T-Num")
+                     (_ "Num")))
+         (add-if (column-uses? 'description)
+                 (_ "Description"))
+         (add-if (column-uses? 'memo)
+                 (if (column-uses? 'notes)
+                     (string-append (_ "Memo") "/" (_ "Notes"))
+                     (_ "Memo")))
+         (add-if (or (column-uses? 'account-name)
+                     (column-uses? 'account-code))
+                 (_ "Account"))
+         (add-if (or (column-uses? 'other-account-name)
+                     (column-uses? 'other-account-code))
+                 (_ "Transfer from/to"))
+         (add-if (column-uses? 'shares)
+                 (_ "Shares"))
+         (add-if (column-uses? 'price)
+                 (_ "Price"))))
+    
+    (define amount-headings
+      (let* ((add-if (lambda pred? . items) (if pred? items '())))
+        (append (add-if (column-uses? 'amount-single)
+                        (_ "Amount"))
+                (add-if (column-uses? 'amount-double)
+                        (_ "Debit")
+                        (_ "Credit"))
+                (add-if (column-uses? 'amount-original-currency)
+                        (_ "Original"))
+                (add-if (column-uses? 'running-balance)
+                        (_ "Balance")))))
+
+    (define width (length headings))
+    (define width-amount (length amount-headings))
+
     (define (add-subheading data subheading-style)
       (let ((heading-cell (gnc:make-html-table-cell data)))
         (gnc:html-table-cell-set-colspan! heading-cell (+ width width-amount))
@@ -1011,17 +1009,17 @@ tags within description, notes or memo. ")
          ;; subtotal? to allow subtotals (ie irrelevant for running balance)
          ;; merge? to merge with the next cell (ie for debit/credit cells)
          ;; merging-function - function (usually gnc-numeric-add/sub-fixed to apply to dual-subtotal
-         (if (column-uses? 'amount-single used-columns)
+         (if (column-uses? 'amount-single)
              (list (vector "Amount" amount #t #t (vector #f #f)))
              '())
-         (if (column-uses? 'amount-double used-columns)
+         (if (column-uses? 'amount-double)
              (list (vector "Debit" debit-amount #f #t (vector #t gnc-numeric-add))
                    (vector "Credit" credit-amount #f #t (vector #f gnc-numeric-sub)))
              '())
-         (if (column-uses? 'amount-original-currency used-columns)
-             (list (vector "Original" original-amount #t #t (vector #f #f)))
+         (if (column-uses? 'amount-original-currency)
+             (list (vector "Original" original-amount #f #t (vector #f #f)))
              '())
-         (if (column-uses? 'running-balance used-columns)
+         (if (column-uses? 'running-balance)
              (list (vector "Running Balance" running-balance #t #f (vector #f #f)))
              '()))))
 
@@ -1064,10 +1062,10 @@ tags within description, notes or memo. ")
                         ((account) (xaccSplitGetAccount split))
                         ((other-acc) (xaccSplitGetAccount (xaccSplitGetOtherSplit split)))))
              (name (account-namestring account
-                                       (column-uses? 'sort-account-code      used-columns)
+                                       (column-uses? 'sort-account-code)
                                        #t
-                                       (column-uses? 'sort-account-full-name used-columns)))
-             (description (if (and (column-uses? 'sort-account-description used-columns)
+                                       (column-uses? 'sort-account-full-name)))
+             (description (if (and (column-uses? 'sort-account-description)
                                    (not (string-null? (xaccAccountGetDescription account))))
                               (string-append ": " (xaccAccountGetDescription account))
                               "")))
@@ -1106,7 +1104,7 @@ tags within description, notes or memo. ")
                    (vector calculated reverse? subtotal?)))
                cell-calculators))
 
-        (if (column-uses? 'date used-columns)
+        (if (column-uses? 'date)
             (addto! row-contents
                     (if transaction-row?
                         (gnc:make-html-table-cell/markup
@@ -1114,7 +1112,7 @@ tags within description, notes or memo. ")
                          (qof-print-date (xaccTransGetDate trans)))
                         "")))
 
-        (if (column-uses? 'reconciled-date used-columns)
+        (if (column-uses? 'reconciled-date)
             (addto! row-contents
                     (gnc:make-html-table-cell/markup
                      "date-cell"
@@ -1123,7 +1121,7 @@ tags within description, notes or memo. ")
                            ""
                            (qof-print-date date))))))
 
-        (if (column-uses? 'num used-columns)
+        (if (column-uses? 'num)
             (addto! row-contents
                     (if transaction-row?
                         (if BOOK-SPLIT-ACTION
@@ -1143,7 +1141,7 @@ tags within description, notes or memo. ")
                                                              (gnc-get-num-action trans split)))
                         "")))
 
-        (if (column-uses? 'description used-columns)
+        (if (column-uses? 'description)
             (addto! row-contents
                     (if transaction-row?
                         (gnc:make-html-table-cell/markup
@@ -1151,30 +1149,30 @@ tags within description, notes or memo. ")
                          (xaccTransGetDescription trans))
                         "")))
 
-        (if (column-uses? 'memo used-columns)
+        (if (column-uses? 'memo)
             (let ((memo (xaccSplitGetMemo split)))
-              (if (and (string-null? memo) (column-uses? 'notes used-columns))
+              (if (and (string-null? memo) (column-uses? 'notes))
                   (addto! row-contents (xaccTransGetNotes trans))
                   (addto! row-contents memo))))
 
-        (if (or (column-uses? 'account-name used-columns) (column-uses? 'account-code used-columns))
+        (if (or (column-uses? 'account-name) (column-uses? 'account-code))
             (addto! row-contents (account-namestring account
-                                                     (column-uses? 'account-code      used-columns)
-                                                     (column-uses? 'account-name      used-columns)
-                                                     (column-uses? 'account-full-name used-columns))))
+                                                     (column-uses? 'account-code)
+                                                     (column-uses? 'account-name)
+                                                     (column-uses? 'account-full-name))))
 
-        (if (or (column-uses? 'other-account-name used-columns) (column-uses? 'other-account-code used-columns))
+        (if (or (column-uses? 'other-account-name) (column-uses? 'other-account-code))
             (addto! row-contents (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
-                                                     (column-uses? 'other-account-code      used-columns)
-                                                     (column-uses? 'other-account-name      used-columns)
-                                                     (column-uses? 'other-account-full-name used-columns))))
+                                                     (column-uses? 'other-account-code)
+                                                     (column-uses? 'other-account-name)
+                                                     (column-uses? 'other-account-full-name))))
 
-        (if (column-uses? 'shares used-columns)
+        (if (column-uses? 'shares)
             (addto! row-contents (gnc:make-html-table-cell/markup
                                   "number-cell"
                                   (xaccSplitGetAmount split))))
 
-        (if (column-uses? 'price used-columns)
+        (if (column-uses? 'price)
             (addto! row-contents
                     (gnc:make-html-table-cell/markup
                      "number-cell"

commit 43cbe65282eacc736df1e350a11b0072f614688d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 05:50:00 2017 +0800

    REFACTOR:Move Void-status filter to filter tab
    
    Also upgrade lookup-value Void Transactions now in Filter tab

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 51a4076..44eb8f3 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -58,7 +58,6 @@
 (define optname-accounts (N_ "Accounts"))
 (define optname-filterby (N_ "Filter By..."))
 (define optname-filtertype (N_ "Filter Type"))
-(define optname-void-transactions (N_ "Void Transactions"))
 
 ;;Display
 (define optname-detail-level (N_ "Detail Level"))
@@ -92,6 +91,7 @@
 (define optname-transaction-matcher (N_ "Transaction Matcher"))
 (define optname-transaction-matcher-regex (N_ "Transaction Matcher uses regular expressions for extended matching"))
 (define optname-reconcile-status (N_ "Reconcile Status"))
+(define optname-void-transactions (N_ "Void Transactions"))
 
 ;;Styles
 (define def:grand-total-style "grand-total")
@@ -437,6 +437,13 @@ tags within description, notes or memo. ")
     #f
     (keylist->vectorlist reconcile-status-list)))
 
+  (gnc:register-trep-option
+   (gnc:make-multichoice-option
+    pagename-filter optname-void-transactions
+    "k" (N_ "How to handle void transactions.")
+    'non-void-only
+    (keylist->vectorlist show-void-list)))
+
   ;; Accounts options
 
   ;; account to do report on
@@ -474,12 +481,6 @@ tags within description, notes or memo. ")
        (not (eq? x 'none))))))
   ;;
 
-  (gnc:register-trep-option
-   (gnc:make-multichoice-option
-    gnc:pagename-accounts optname-void-transactions
-    "d" (N_ "How to handle void transactions.")
-    'non-void-only
-    (keylist->vectorlist show-void-list)))
 
   ;; Sorting options
 
@@ -1417,7 +1418,7 @@ tags within description, notes or memo. ")
          (secondary-key (opt-val pagename-sorting optname-sec-sortkey))
          (secondary-order (opt-val pagename-sorting optname-sec-sortorder))
          (secondary-date-subtotal (opt-val pagename-sorting optname-sec-date-subtotal))
-         (void-status (opt-val gnc:pagename-accounts optname-void-transactions))
+         (void-status (opt-val pagename-filter optname-void-transactions))
          (splits '())
          (custom-sort? (or (and (member primary-key DATE-SORTING-TYPES)   ; this will remain
                                 (not (eq? primary-date-subtotal 'none)))  ; until qof-query
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 3bb5e96..6083a14 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -1708,7 +1708,8 @@
                                         "To" (cons #f "End Date")
                                         "Use Full Account Name?" (cons #f "Use Full Account Name")
                                         "Use Full Other Account Name?" (cons #f "Use Full Other Account Name")
-                                        "Void Transactions?" (cons #f "Void Transactions")
+                                        "Void Transactions?" (cons "Filter" "Void Transactions")
+                                        "Void Transactions" (cons "Filter" "Void Transactions")
                                         "Account Substring" (cons "Filter" "Account Matcher")
                                         ))
                        (name-match (member name new-names-list)))

commit 20feefe681e6c8a41f857b2c9f2414ba57fffff9
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Nov 26 13:27:29 2017 +0800

    REFACTOR:Centralise sign-reverse-list
    
    This allows us to centralise its account type list.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index d249411..51a4076 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -319,6 +319,28 @@ options specified in the Options panels."))
                    (cons 'text (N_ "Descending"))
                    (cons 'tip (N_ "Largest to smallest, latest to earliest."))))))
 
+(define sign-reverse-list
+  (list
+   (cons 'none
+         (list
+          (cons 'text (N_ "None"))
+          (cons 'tip (N_ "Don't change any displayed amounts."))
+          (cons 'acct-types '())))
+   (cons 'income-expense
+         (list
+          (cons 'text (N_ "Income and Expense"))
+          (cons 'tip (N_ "Reverse amount display for Income and Expense Accounts."))
+          (cons 'acct-types (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))))
+   (cons 'credit-accounts
+         (list
+          (cons 'text (N_ "Credit Accounts"))
+          (cons 'tip (N_ "Reverse amount display for Liability, Payable, Equity, \
+Credit Card, and Income accounts."))
+          (cons 'acct-types (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE
+                                  ACCT-TYPE-EQUITY ACCT-TYPE-CREDIT
+                                  ACCT-TYPE-INCOME))))))
+
+
 (define (keylist-get-info keylist key info)
   (cdr (assq info (cdr (assq key keylist)))))
 
@@ -461,13 +483,13 @@ tags within description, notes or memo. ")
 
   ;; Sorting options
 
-  (let ((ascending-choice-list (keylist->vectorlist ascending-list))        
+  (let ((ascending-choice-list (keylist->vectorlist ascending-list))
+        (key-choice-list (keylist->vectorlist sortkey-list))
+        (date-subtotal-choice-list (keylist->vectorlist date-subtotal-list))
         (prime-sortkey 'account-name)
         (prime-sortkey-subtotal-true #t)
         (sec-sortkey 'register-order)
-        (sec-sortkey-subtotal-true #f)
-        (key-choice-list (keylist->vectorlist sortkey-list))
-        (date-subtotal-choice-list (keylist->vectorlist date-subtotal-list)))
+        (sec-sortkey-subtotal-true #f))
 
     (define (apply-selectable-by-name-sorting-options)
       (let* ((prime-sortkey-enabled (not (eq? prime-sortkey 'none)))
@@ -740,16 +762,7 @@ tags within description, notes or memo. ")
       gnc:pagename-display (N_ "Sign Reverses")
       "m1" (N_ "Reverse amount display for certain account types.")
       'credit-accounts
-      (list (vector 'none
-                    (N_ "None")
-                    (N_ "Don't change any displayed amounts."))
-            (vector 'income-expense
-                    (N_ "Income and Expense")
-                    (N_ "Reverse amount display for Income and Expense Accounts."))
-            (vector 'credit-accounts
-                    (N_ "Credit Accounts")
-                    (N_ "Reverse amount display for Liability, Payable, Equity, \
-Credit Card, and Income accounts."))))))
+      (keylist->vectorlist sign-reverse-list))))
 
   (gnc:options-set-default-section options gnc:pagename-general)
   options)
@@ -853,12 +866,9 @@ Credit Card, and Income accounts."))))))
          (width (length headings))
          (width-amount (length amount-headings))
          (account-types-to-reverse
-          (case (opt-val gnc:pagename-display (N_ "Sign Reverses"))
-            ((none) '())
-            ((income-expense) (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))
-            ((credit-accounts)  (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE
-                                      ACCT-TYPE-EQUITY ACCT-TYPE-CREDIT
-                                      ACCT-TYPE-INCOME))))
+          (keylist-get-info sign-reverse-list
+                            (opt-val gnc:pagename-display (N_ "Sign Reverses"))
+                            'acct-types))
          (is-multiline? (eq? (opt-val gnc:pagename-display optname-detail-level) 'multi-line))
          (export? (opt-val gnc:pagename-general optname-table-export)))
 

commit 3b3c0322cf3020fa156ff9640e82405cdd29dbe4
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 21:49:17 2017 +0800

    ENH: add infobox to summarise options used

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 14f93b2..d249411 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -14,6 +14,8 @@
 ;; - add custom sorter in scheme
 ;; - common currency - optionally show original currency amount
 ;;   and enable multiple data columns
+;; - add informational box, summarising options used, useful
+;;   to troubleshoot reports
 ;;
 ;; This program is free software; you can redistribute it and/or    
 ;; modify it under the terms of the GNU General Public License as   
@@ -1466,6 +1468,82 @@ Credit Card, and Income accounts."))))))
       (generic-less? X Y 'date 'none #t))
 
 
+    ;; infobox
+    (define (infobox)
+      (define (highlight title . data)
+        (string-append "<b>" title "</b>: " (string-join data " ") "<br>"))
+      (define (bool->string tf)
+        (if tf
+            (_ "Enabled")
+            (_ "Disabled")))
+      (gnc:make-html-text
+       (if (string-null? account-matcher)
+           ""
+           (string-append
+            (highlight
+             (string-append optname-account-matcher
+                            (if (opt-val pagename-filter optname-account-matcher-regex)
+                                (N_ " regex")
+                                ""))
+             account-matcher)
+            (highlight
+             (N_ "Accounts produced")
+             (string-join (map xaccAccountGetName c_account_1) ", "))))
+       (if (eq? filter-mode 'none)
+           ""
+           (highlight
+            (keylist-get-info filter-list filter-mode 'text)
+            (string-join (map xaccAccountGetName c_account_2) ", ")))
+       (if (string-null? transaction-matcher)
+           ""
+           (string-append
+            (highlight
+             (string-append optname-transaction-matcher
+                            (if (opt-val pagename-filter optname-transaction-matcher-regex)
+                                (N_ " regex")
+                                ""))
+             transaction-matcher)))
+       (if reconcile-status-filter
+           (highlight
+            optname-reconcile-status
+            (keylist-get-info reconcile-status-list reconcile-status-filter 'text))
+           "")
+       (if (eq? void-status 'non-void-only)
+           ""
+           (highlight
+            optname-void-transactions
+            (keylist-get-info show-void-list void-status 'text)))
+       (if (eq? primary-key 'none)
+           ""
+           (highlight
+            optname-prime-sortkey
+            (keylist-get-info sortkey-list primary-key 'text)
+            (keylist-get-info ascending-list primary-order 'text)))
+       (if (eq? primary-key 'none)
+           ""
+           (if (member primary-key DATE-SORTING-TYPES)
+               (highlight
+                optname-prime-date-subtotal
+                (keylist-get-info date-subtotal-list primary-date-subtotal 'text))
+               (highlight
+                optname-prime-subtotal
+                (bool->string (opt-val pagename-sorting optname-prime-subtotal)))))
+       (if (eq? secondary-key 'none)
+           ""
+           (highlight
+            optname-sec-sortkey
+            (keylist-get-info sortkey-list secondary-key 'text)
+            (keylist-get-info ascending-list secondary-order 'text)))
+       (if (eq? secondary-key 'none)
+           ""
+           (if (member secondary-key DATE-SORTING-TYPES)
+               (highlight
+                optname-sec-date-subtotal
+                (keylist-get-info date-subtotal-list secondary-date-subtotal 'text))
+               (highlight
+                optname-sec-subtotal
+                (bool->string (opt-val pagename-sorting optname-sec-subtotal)))))
+       "<br>"))
 
     (if (or (null? c_account_1) (and-map not c_account_1))
 
@@ -1477,12 +1555,17 @@ Credit Card, and Income accounts."))))))
              (gnc:html-make-no-account-warning report-title (gnc:report-id report-obj)))
 
             ;; error condition: accounts were specified but none matched string/regex
-            (gnc:html-document-add-object!
-             document
-             (gnc:make-html-text
-              (gnc:html-markup-h2 NO-MATCHING-ACCT-HEADER)
-              (gnc:html-markup-p NO-MATCHING-ACCT-TEXT))))
+            (begin
+              (gnc:html-document-add-object!
+               document
+               (gnc:make-html-text
+                (gnc:html-markup-h2 NO-MATCHING-ACCT-HEADER)
+                (gnc:html-markup-p NO-MATCHING-ACCT-TEXT)))
 
+              (gnc:html-document-add-object!
+               document                 
+               (infobox))))
+            
         (begin
 
           (qof-query-set-book query (gnc-get-current-book))
@@ -1538,11 +1621,16 @@ Credit Card, and Income accounts."))))))
           (if (null? splits)
 
               ;; error condition: no splits found
-              (gnc:html-document-add-object!
-               document
-               (gnc:make-html-text
-                (gnc:html-markup-h2 NO-MATCHING-TRANS-HEADER)
-                (gnc:html-markup-p NO-MATCHING-TRANS-TEXT)))
+              (begin
+                (gnc:html-document-add-object!
+                 document
+                 (gnc:make-html-text
+                  (gnc:html-markup-h2 NO-MATCHING-TRANS-HEADER)
+                  (gnc:html-markup-p NO-MATCHING-TRANS-TEXT)))
+
+                (gnc:html-document-add-object!
+                 document
+                 (infobox)))
 
               (let ((table (make-split-table
                             splits options
@@ -1574,6 +1662,10 @@ Credit Card, and Income accounts."))))))
                             (gnc-print-date begindate)
                             (gnc-print-date enddate)))))
 
+                (gnc:html-document-add-object!
+                 document                 
+                 (infobox))
+
                 (gnc:html-document-add-object! document table)))))
 
     (gnc:report-finished)

commit ea416e16d526b79d9dc58031f34020563cb7ed2c
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 15:14:21 2017 +0800

    REFACTOR: Centralize options

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index ee13b1b..14f93b2 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -211,9 +211,6 @@ options specified in the Options panels."))
                                    (cons 'renderer-key #f)))))
 
 
-(define (sortkey-get-info sortkey info)
-  (cdr (assq info (cdr (assq sortkey sortkey-list)))))
-
 (define (time64-year t64)    (gnc:date-get-year (gnc-localtime t64)))
 (define (time64-quarter t64) (+ (* 10 (gnc:date-get-year (gnc-localtime t64)))  (gnc:date-get-quarter (gnc-localtime t64))))
 (define (time64-month t64)   (+ (* 100 (gnc:date-get-year (gnc-localtime t64))) (gnc:date-get-month (gnc-localtime t64))))
@@ -261,8 +258,76 @@ options specified in the Options panels."))
                   (cons 'tip (N_ "Yearly."))
                   (cons 'renderer-key 'year)))))
 
-(define (date-subtotal-get-info sortkey info)
-  (cdr (assq info (cdr (assq sortkey date-subtotal-list)))))
+(define filter-list
+  (list
+   (cons 'none (list
+                (cons 'text (N_ "None"))
+                (cons 'tip (N_ "Do not do any filtering."))))
+
+   (cons 'include (list
+                   (cons 'text (N_ "Include Transactions to/from Filter Accounts"))
+                   (cons 'tip (N_ "Include transactions to/from filter accounts only."))))
+
+   (cons 'exclude (list
+                   (cons 'text (N_ "Exclude Transactions to/from Filter Accounts"))
+                   (cons 'tip (N_ "Exclude transactions to/from all filter accounts."))))))
+
+(define show-void-list
+  (list
+   (cons 'non-void-only (list
+                         (cons 'text (N_ "Non-void only"))
+                         (cons 'tip (N_ "Show only non-voided transactions."))))
+         
+   (cons 'void-only (list
+                     (cons 'text (N_ "Void only"))
+                     (cons 'tip (N_ "Show only voided transactions."))))
+   
+   (cons 'both (list
+                (cons 'text (N_ "Both"))
+                (cons 'tip (N_ "Show both (and include void transactions in totals)."))))))
+
+(define reconcile-status-list
+  ;; value will be either #f to disable reconciled-status filter
+  ;; or a list of xaccSplitGetReconcile values. e.g. value can
+  ;; be '(#\c #\y) to retrieve list of cleared and reconciled splits.
+  (list
+   (cons  #f (list
+              (cons 'text (N_ "All"))
+              (cons 'tip (N_ "Show All Transactions"))))
+
+   (cons '(#\n) (list
+                 (cons 'text (N_ "Unreconciled"))
+                 (cons 'tip (N_ "Unreconciled only"))))
+   
+   (cons '(#\c) (list
+                 (cons 'text (N_ "Cleared"))
+                 (cons 'tip (N_ "Cleared only"))))
+   
+   (cons '(#\y) (list
+                 (cons 'text (N_ "Reconciled"))
+                 (cons 'tip (N_ "Reconciled only"))))))
+
+
+(define ascending-list
+  (list
+   (cons 'ascend (list
+                  (cons 'text (N_ "Ascending"))
+                  (cons 'tip (N_ "Smallest to largest, earliest to latest."))))
+   (cons 'descend (list
+                   (cons 'text (N_ "Descending"))
+                   (cons 'tip (N_ "Largest to smallest, latest to earliest."))))))
+
+(define (keylist-get-info keylist key info)
+  (cdr (assq info (cdr (assq key keylist)))))
+
+(define (keylist->vectorlist keylist)
+  (map
+   (lambda (item)
+     (vector
+      (car item)
+      (keylist-get-info keylist (car item) 'text)
+      (keylist-get-info keylist (car item) 'tip)))
+   keylist))
 
 
 (define (trep-options-generator)
@@ -346,10 +411,7 @@ tags within description, notes or memo. ")
     pagename-filter optname-reconcile-status
     "j1" (N_ "Filter by reconcile status.")
     #f
-    (list (vector #f      (N_ "All")           (N_ "Show All Transactions"))
-          (vector '(#\n)  (N_ "Unreconciled")  (N_ "Unreconciled only"))
-          (vector '(#\c)  (N_ "Cleared")       (N_ "Cleared only"))
-          (vector '(#\y)  (N_ "Reconciled")    (N_ "Reconciled only")))))
+    (keylist->vectorlist reconcile-status-list)))
 
   ;; Accounts options
 
@@ -380,20 +442,12 @@ tags within description, notes or memo. ")
     gnc:pagename-accounts optname-filtertype
     "c" (N_ "Filter account.")
     'none
-    (list (vector 'none
-                  (N_ "None")
-                  (N_ "Do not do any filtering."))
-          (vector 'include
-                  (N_ "Include Transactions to/from Filter Accounts")
-                  (N_ "Include transactions to/from filter accounts only."))
-          (vector 'exclude
-                  (N_ "Exclude Transactions to/from Filter Accounts")
-                  (N_ "Exclude transactions to/from all filter accounts.")))
-   #f
-   (lambda (x)
-     (gnc-option-db-set-option-selectable-by-name
-      options gnc:pagename-accounts optname-filterby
-      (not (eq? x 'none))))))
+    (keylist->vectorlist filter-list)
+    #f
+    (lambda (x)
+      (gnc-option-db-set-option-selectable-by-name
+       options gnc:pagename-accounts optname-filterby
+       (not (eq? x 'none))))))
   ;;
 
   (gnc:register-trep-option
@@ -401,39 +455,17 @@ tags within description, notes or memo. ")
     gnc:pagename-accounts optname-void-transactions
     "d" (N_ "How to handle void transactions.")
     'non-void-only
-    (list
-     (vector 'non-void-only (N_ "Non-void only") (N_ "Show only non-voided transactions."))
-     (vector 'void-only     (N_ "Void only") (N_ "Show only voided transactions."))
-     (vector 'both          (N_ "Both") (N_ "Show both (and include void transactions in totals).")))))
+    (keylist->vectorlist show-void-list)))
 
   ;; Sorting options
-  
-
-  (let ((ascending-choice-list 
-         (list (vector 'ascend
-                       (N_ "Ascending")
-                       (N_ "Smallest to largest, earliest to latest."))
-               (vector 'descend
-                       (N_ "Descending")
-                       (N_ "Largest to smallest, latest to earliest."))))
+
+  (let ((ascending-choice-list (keylist->vectorlist ascending-list))        
         (prime-sortkey 'account-name)
         (prime-sortkey-subtotal-true #t)
         (sec-sortkey 'register-order)
-        (sec-sortkey-subtotal-true #f)       
-        (key-choice-list (map
-                          (lambda (sortpair)
-                            (vector
-                             (car sortpair)
-                             (sortkey-get-info (car sortpair) 'text)
-                             (sortkey-get-info (car sortpair) 'tip)))
-                          sortkey-list))
-        (date-subtotal-choice-list (map
-                                    (lambda (date-sortpair)
-                                      (vector
-                                       (car date-sortpair)
-                                       (date-subtotal-get-info (car date-sortpair) 'text)
-                                       (date-subtotal-get-info (car date-sortpair) 'tip)))
-                                    date-subtotal-list)))
+        (sec-sortkey-subtotal-true #f)
+        (key-choice-list (keylist->vectorlist sortkey-list))
+        (date-subtotal-choice-list (keylist->vectorlist date-subtotal-list)))
 
     (define (apply-selectable-by-name-sorting-options)
       (let* ((prime-sortkey-enabled (not (eq? prime-sortkey 'none)))
@@ -1314,14 +1346,14 @@ Credit Card, and Income accounts."))))))
           ;; If sorting by date, look up the value of the
           ;; date-subtotalling multichoice option and return the
           ;; corresponding funcs in the assoc-list.
-          (date-subtotal-get-info (opt-val pagename-sorting name-date-subtotal) info)
+          (keylist-get-info date-subtotal-list (opt-val pagename-sorting name-date-subtotal) info)
           ;; For everything else: 1. check whether sortkey has
           ;; subtotalling enabled at all, 2. check whether the
           ;; enable-subtotal boolean option is #t, 3. look up the
           ;; appropriate funcs in the assoc-list.
           (and (member sortkey SUBTOTAL-ENABLED)
                (and (opt-val pagename-sorting name-subtotal)
-                    (sortkey-get-info sortkey info))))))
+                    (keylist-get-info sortkey-list sortkey info))))))
 
   (define (is-filter-member split account-list)
     (let* ((txn (xaccSplitGetParent split))
@@ -1463,8 +1495,8 @@ Credit Card, and Income accounts."))))))
           (if (not custom-sort?)
               (begin
                 (qof-query-set-sort-order query
-                                          (sortkey-get-info primary-key 'sortkey)
-                                          (sortkey-get-info secondary-key 'sortkey)
+                                          (keylist-get-info sortkey-list primary-key 'sortkey)
+                                          (keylist-get-info sortkey-list secondary-key 'sortkey)
                                           '())
                 (qof-query-set-sort-increasing query
                                                (eq? primary-order 'ascend)

commit d9d4ffaff2fc888d26d59120d4a36d8c4523e545
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Nov 27 08:01:48 2017 +0800

    ENH: disable filter accounts selector if filter-mode=none

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index abcb03e..ee13b1b 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -370,13 +370,13 @@ tags within description, notes or memo. ")
   (gnc:register-trep-option
    (gnc:make-account-list-option
     gnc:pagename-accounts optname-filterby
-    "b" (N_ "Filter on these accounts.")
+    "c1" (N_ "Filter on these accounts.")
     (lambda ()
       '())
     #f #t))
 
   (gnc:register-trep-option
-   (gnc:make-multichoice-option
+   (gnc:make-multichoice-callback-option
     gnc:pagename-accounts optname-filtertype
     "c" (N_ "Filter account.")
     'none
@@ -388,7 +388,12 @@ tags within description, notes or memo. ")
                   (N_ "Include transactions to/from filter accounts only."))
           (vector 'exclude
                   (N_ "Exclude Transactions to/from Filter Accounts")
-                  (N_ "Exclude transactions to/from all filter accounts.")))))
+                  (N_ "Exclude transactions to/from all filter accounts.")))
+   #f
+   (lambda (x)
+     (gnc-option-db-set-option-selectable-by-name
+      options gnc:pagename-accounts optname-filterby
+      (not (eq? x 'none))))))
   ;;
 
   (gnc:register-trep-option

commit 8399ee65bda36dd66c76659ce0b327ca9b54c367
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 22:30:11 2017 +0800

    ENH: dual columns subtotals now in correct column
    
    This commit changes dual column subtotal strategy to limit
    to debit/credit columns handling only. Values are summed
    and the subtotal is displayed in the appropriate debit or
    credit column.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index dc8f7a7..abcb03e 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -855,9 +855,8 @@ Credit Card, and Income accounts."))))))
               (addto! row-contents (gnc:make-html-table-cell/size/markup 1 width "total-label-cell" string))))
 
         (define (add-columns commodity)
-          (let ((merging? #f)
-                (merging-subtotal (gnc:make-gnc-numeric 0 1))
-                (width 0))
+          (let ((start-dual-column? #f)
+                (dual-subtotal (gnc:make-gnc-numeric 0 1)))
             (for-each (lambda (column merge-entry)
                         (let* ((mon (retrieve-commodity column commodity))
                                (col (and mon (gnc:gnc-monetary-amount mon)))
@@ -868,30 +867,37 @@ Credit Card, and Income accounts."))))))
                               ;; and store total in dual-subtotal. Do NOT add column.
                               (begin
                                 (if column-amount
-                                    (set! merging-subtotal
-                                          (merge-fn merging-subtotal column-amount)))
-                                (set! merging? #t)
-                                (if col
-                                    (set! merging-subtotal
-                                          (merge-fn merging-subtotal col GNC-DENOM-AUTO GNC-RND-ROUND)))
-                                (set! width (+ width 1)))
-                              (if merging?
+                                    (set! dual-subtotal
+                                          (merge-fn dual-subtotal column-amount
+                                                    GNC-DENOM-AUTO GNC-HOW-RND-ROUND)))
+                                (set! start-dual-column? #t))
+                              (if start-dual-column?
                                   (begin
                                     ;; We've completed merging. Add this column amount
                                     ;; and add the columns.
-                                    (set! merging? #f)
-                                    (if col
-                                        (set! merging-subtotal
-                                              (merge-fn merging-subtotal column-amount)))
-                                    (set! width (+ width 1))
-                                    (addto! row-contents
-                                            (gnc:make-html-table-cell/size/markup
-                                             1 width "total-number-cell"
-                                             (gnc:make-gnc-monetary commodity merging-subtotal)))
-                                    (set! width 0)
-                                    (set! merging-subtotal (gnc:make-gnc-numeric 0 1)))
-                                  ;; Default; not merging/completed merge. Just
-                                  ;; display monetary amount
+                                    (if column-amount
+                                        (set! dual-subtotal
+                                              (merge-fn dual-subtotal column-amount
+                                                        GNC-DENOM-AUTO GNC-HOW-RND-ROUND)))
+                                    (if (gnc-numeric-positive-p dual-subtotal)
+                                        (begin
+                                          (addto! row-contents
+                                                  (gnc:make-html-table-cell/markup
+                                                   "total-number-cell"
+                                                   (gnc:make-gnc-monetary commodity dual-subtotal)))
+                                          (addto! row-contents ""))
+                                        (begin
+                                          (addto! row-contents "")
+                                          (addto! row-contents
+                                                  (gnc:make-html-table-cell/markup
+                                                   "total-number-cell"
+                                                   (gnc:make-gnc-monetary
+                                                    commodity
+                                                    (gnc-numeric-neg dual-subtotal))))))
+                                    (set! start-dual-column? #f)
+                                    (set! dual-subtotal (gnc:make-gnc-numeric 0 1)))
+				  ;; Default; not merging/completed merge. Just
+				  ;; display monetary amount
                                   (addto! row-contents
                                           (gnc:make-html-table-cell/markup "total-number-cell" mon))))))
                       columns
@@ -949,18 +955,18 @@ Credit Card, and Income accounts."))))))
            (running-balance (lambda (s) (gnc:make-gnc-monetary (currency s) (xaccSplitGetBalance s)))))
         (append
          ;; each column will be a vector
-         ;; (vector heading calculator-function reverse-column? subtotal? (vector merge? merging-function))
+         ;; (vector heading calculator-function reverse-column? subtotal? (vector start-dual-column? merging-function))
          ;; (calculator-function split) to obtain amount
          ;; reverse? to optionally reverse signs
          ;; subtotal? to allow subtotals (ie irrelevant for running balance)
          ;; merge? to merge with the next cell (ie for debit/credit cells)
-         ;; merging-function - function (usually gnc-numeric-add/sub-fixed to apply to merging-subtotal
+         ;; merging-function - function (usually gnc-numeric-add/sub-fixed to apply to dual-subtotal
          (if (column-uses? 'amount-single used-columns)
              (list (vector "Amount" amount #t #t (vector #f #f)))
              '())
          (if (column-uses? 'amount-double used-columns)
-             (list (vector "Debit" debit-amount #f #t (vector #t gnc-numeric-add-fixed))
-                   (vector "Credit" credit-amount #f #t (vector #f gnc-numeric-sub-fixed)))
+             (list (vector "Debit" debit-amount #f #t (vector #t gnc-numeric-add))
+                   (vector "Credit" credit-amount #f #t (vector #f gnc-numeric-sub)))
              '())
          (if (column-uses? 'amount-original-currency used-columns)
              (list (vector "Original" original-amount #t #t (vector #f #f)))

commit 521c16241ddfd2da11284d4646ff5d6d32b73c31
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Dec 11 23:48:13 2017 +0800

    REFACTOR: Use time64 instead of timepair

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 775aa46..dc8f7a7 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -214,14 +214,14 @@ options specified in the Options panels."))
 (define (sortkey-get-info sortkey info)
   (cdr (assq info (cdr (assq sortkey sortkey-list)))))
 
-(define (timepair-year tp)    (gnc:timepair-get-year tp))
-(define (timepair-quarter tp) (+ (* 10 (timepair-year tp))  (gnc:timepair-get-quarter tp)))
-(define (timepair-month tp)   (+ (* 100 (timepair-year tp)) (gnc:timepair-get-month tp)))
-(define (timepair-week tp)    (+ (* 100 (timepair-year tp)) (gnc:timepair-get-week tp)))
-(define (split-week a) (timepair-week (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
-(define (split-month a) (timepair-month (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
-(define (split-quarter a) (timepair-quarter (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
-(define (split-year a) (timepair-year (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
+(define (time64-year t64)    (gnc:date-get-year (gnc-localtime t64)))
+(define (time64-quarter t64) (+ (* 10 (gnc:date-get-year (gnc-localtime t64)))  (gnc:date-get-quarter (gnc-localtime t64))))
+(define (time64-month t64)   (+ (* 100 (gnc:date-get-year (gnc-localtime t64))) (gnc:date-get-month (gnc-localtime t64))))
+(define (time64-week t64)    (gnc:date-get-week (gnc-localtime t64)))
+(define (split-week a) (time64-week (xaccTransGetDate (xaccSplitGetParent a))))
+(define (split-month a) (time64-month (xaccTransGetDate (xaccSplitGetParent a))))
+(define (split-quarter a) (time64-quarter (xaccTransGetDate (xaccSplitGetParent a))))
+(define (split-year a) (time64-year (xaccTransGetDate (xaccSplitGetParent a))))
 
 (define date-subtotal-list
   ;; List for date option.
@@ -999,8 +999,8 @@ Credit Card, and Income accounts."))))))
          ((month) gnc:date-get-month-year-string)
          ((quarter) gnc:date-get-quarter-year-string)
          ((year) gnc:date-get-year-string))
-       (gnc:timepair->date
-        (gnc-transaction-get-date-posted
+       (gnc-localtime
+        (xaccTransGetDate
          (xaccSplitGetParent split)))))
 
     (define (render-account renderer-key split anchor?)
@@ -1055,17 +1055,17 @@ Credit Card, and Income accounts."))))))
                     (if transaction-row?
                         (gnc:make-html-table-cell/markup
                          "date-cell"
-                         (gnc-print-date (gnc-transaction-get-date-posted trans)))
+                         (qof-print-date (xaccTransGetDate trans)))
                         "")))
 
         (if (column-uses? 'reconciled-date used-columns)
             (addto! row-contents
                     (gnc:make-html-table-cell/markup
                      "date-cell"
-                     (let ((date (gnc-split-get-date-reconciled split)))
-                       (if (equal? date (cons 0 0))
+                     (let ((date (xaccSplitGetDateReconciled split)))
+                       (if (zero? date)
                            ""
-                           (gnc-print-date date))))))
+                           (qof-print-date date))))))
 
         (if (column-uses? 'num used-columns)
             (addto! row-contents

commit 1be88ad17584e048a21c3805bf59f4fdb6dd58e5
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 13:27:06 2017 +0800

    COSMETIC: Move Display>Sign reversal option

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 8a7a359..775aa46 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -699,7 +699,7 @@ tags within description, notes or memo. ")
     (gnc:register-trep-option
      (gnc:make-multichoice-option
       gnc:pagename-display (N_ "Sign Reverses")
-      "p" (N_ "Reverse amount display for certain account types.")
+      "m1" (N_ "Reverse amount display for certain account types.")
       'credit-accounts
       (list (vector 'none
                     (N_ "None")

commit fa0bcf104a180fcc243ea8aea0d5accaf48fd20f
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 13:23:36 2017 +0800

    ENH: Enable sign reversal for amount 'single only

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 365b937..8a7a359 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -574,7 +574,8 @@ tags within description, notes or memo. ")
   (let ((disp-memo? #t)
         (disp-accname? #t)
         (disp-other-accname? #f)
-        (is-single? #t))
+        (detail-is-single? #t)
+        (amount-is-single? #t))
 
     (define (apply-selectable-by-name-display-options)
       (gnc-option-db-set-option-selectable-by-name
@@ -583,15 +584,19 @@ tags within description, notes or memo. ")
 
       (gnc-option-db-set-option-selectable-by-name
        options gnc:pagename-display (N_ "Other Account Name")
-       is-single?)
+       detail-is-single?)
+
+      (gnc-option-db-set-option-selectable-by-name
+       options gnc:pagename-display (N_ "Sign Reverses")
+       amount-is-single?)
 
       (gnc-option-db-set-option-selectable-by-name
        options gnc:pagename-display (N_ "Use Full Other Account Name")
-       (and disp-other-accname? is-single?))
+       (and disp-other-accname? detail-is-single?))
 
       (gnc-option-db-set-option-selectable-by-name
        options gnc:pagename-display (N_ "Other Account Code")
-       is-single?)
+       detail-is-single?)
 
       (gnc-option-db-set-option-selectable-by-name
        options gnc:pagename-display (N_ "Notes")
@@ -674,18 +679,22 @@ tags within description, notes or memo. ")
                     (N_ "Display one line per transaction, merging multiple splits where required.")))
       #f
       (lambda (x)
-        (set! is-single? (eq? x 'single))
+        (set! detail-is-single? (eq? x 'single))
         (apply-selectable-by-name-display-options))))
 
     (gnc:register-trep-option
-     (gnc:make-multichoice-option
+     (gnc:make-multichoice-callback-option
       gnc:pagename-display (N_ "Amount")
       "m" (N_ "Display the amount?")
       'single
       (list
        (vector 'none   (N_ "None") (N_ "No amount display."))
        (vector 'single (N_ "Single") (N_ "Single Column Display."))
-       (vector 'double (N_ "Double") (N_ "Two Column Display.")))))
+       (vector 'double (N_ "Double") (N_ "Two Column Display.")))
+      #f
+      (lambda (x)
+        (set! amount-is-single? (eq? x 'single))
+        (apply-selectable-by-name-display-options))))
 
     (gnc:register-trep-option
      (gnc:make-multichoice-option
@@ -719,7 +728,7 @@ Credit Card, and Income accounts."))))))
   (define BOOK-SPLIT-ACTION (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
 
   (define (build-columns-used)
-    (define is-single? (eq? (opt-val gnc:pagename-display optname-detail-level) 'single))
+    (define detail-is-single? (eq? (opt-val gnc:pagename-display optname-detail-level) 'single))
     (define amount-setting (opt-val gnc:pagename-display (N_ "Amount")))
     (list (cons 'date (opt-val gnc:pagename-display (N_ "Date")))
           (cons 'reconciled-date (opt-val gnc:pagename-display (N_ "Reconciled Date")))
@@ -728,7 +737,7 @@ Credit Card, and Income accounts."))))))
                          (opt-val gnc:pagename-display (N_ "Num"))))
           (cons 'description (opt-val gnc:pagename-display (N_ "Description")))
           (cons 'account-name (opt-val gnc:pagename-display (N_ "Account Name")))
-          (cons 'other-account-name (and is-single?
+          (cons 'other-account-name (and detail-is-single?
                                          (opt-val gnc:pagename-display (N_ "Other Account Name"))))
           (cons 'shares (opt-val gnc:pagename-display (N_ "Shares")))
           (cons 'price (opt-val gnc:pagename-display (N_ "Price")))
@@ -741,9 +750,9 @@ Credit Card, and Income accounts."))))))
           (cons 'account-full-name (opt-val gnc:pagename-display (N_ "Use Full Account Name")))
           (cons 'memo (opt-val gnc:pagename-display (N_ "Memo")))
           (cons 'account-code (opt-val gnc:pagename-display (N_ "Account Code")))
-          (cons 'other-account-code (and is-single?
+          (cons 'other-account-code (and detail-is-single?
                                          (opt-val gnc:pagename-display (N_ "Other Account Code"))))
-          (cons 'other-account-full-name (and is-single?
+          (cons 'other-account-full-name (and detail-is-single?
                                               (opt-val gnc:pagename-display (N_ "Use Full Other Account Name"))))
           (cons 'sort-account-code (opt-val pagename-sorting (N_ "Show Account Code")))
           (cons 'sort-account-full-name (opt-val pagename-sorting (N_ "Show Full Account Name")))
@@ -1029,7 +1038,8 @@ Credit Card, and Income accounts."))))))
 
     (define (add-split-row split cell-calculators row-style transaction-row?)
       (let* ((row-contents '())
-             (trans (xaccSplitGetParent split)))
+             (trans (xaccSplitGetParent split))
+             (account (xaccSplitGetAccount split)))
 
         (define cells
           (map (lambda (cell)
@@ -1092,7 +1102,7 @@ Credit Card, and Income accounts."))))))
                   (addto! row-contents memo))))
 
         (if (or (column-uses? 'account-name used-columns) (column-uses? 'account-code used-columns))
-            (addto! row-contents (account-namestring (xaccSplitGetAccount split)
+            (addto! row-contents (account-namestring account
                                                      (column-uses? 'account-code      used-columns)
                                                      (column-uses? 'account-name      used-columns)
                                                      (column-uses? 'account-full-name used-columns))))

commit db019ec51ed9e68e01df89357ba4c10320baad52
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 13:21:21 2017 +0800

    ENH: "Shares" column gets number-cell styling
    
    This commit enables styling for shares column which allows
    right-alignment of numeric amount.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 990d04d..365b937 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1104,7 +1104,9 @@ Credit Card, and Income accounts."))))))
                                                      (column-uses? 'other-account-full-name used-columns))))
 
         (if (column-uses? 'shares used-columns)
-            (addto! row-contents (xaccSplitGetAmount split)))
+            (addto! row-contents (gnc:make-html-table-cell/markup
+                                  "number-cell"
+                                  (xaccSplitGetAmount split))))
 
         (if (column-uses? 'price used-columns)
             (addto! row-contents

commit 1a886fac7d0d5cbb2b0de752f6732ad505851218
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Dec 13 07:11:49 2017 +0800

    ENH: "Price" column gets number-cell styling.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 7214e9e..990d04d 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1107,8 +1107,11 @@ Credit Card, and Income accounts."))))))
             (addto! row-contents (xaccSplitGetAmount split)))
 
         (if (column-uses? 'price used-columns)
-            (addto! row-contents  (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
-                                                         (xaccSplitGetSharePrice split))))
+            (addto! row-contents
+                    (gnc:make-html-table-cell/markup
+                     "number-cell"
+                     (gnc:make-gnc-monetary (xaccTransGetCurrency trans)
+                                            (xaccSplitGetSharePrice split)))))
 
         (for-each (lambda (cell)
                     (let ((cell-content (vector-ref cell 0))

commit ef65f544aaaa58d859d278ef7987d70d9ea88fc0
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Dec 15 18:11:30 2017 +0800

    ENH: show original currency, and enable multicolumns.
    
    This commit optionally displays the original currency
    if 'common currency' is ticked. This will require
    refactoring to enable multicolumn data display and
    multiple collectors for subtotals.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 951caef..7214e9e 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -12,6 +12,8 @@
 ;; Refactored by Christopher Lam (2017)
 ;; - introduced account/transaction substring/regex matcher
 ;; - add custom sorter in scheme
+;; - common currency - optionally show original currency amount
+;;   and enable multiple data columns
 ;;
 ;; This program is free software; you can redistribute it and/or    
 ;; modify it under the terms of the GNU General Public License as   
@@ -78,6 +80,7 @@
 (define optname-enddate (N_ "End Date"))
 (define optname-table-export (N_ "Table for Exporting"))
 (define optname-common-currency (N_ "Common Currency"))
+(define optname-orig-currency (N_ "Show original currency amount"))
 (define optname-currency (N_ "Report's currency"))
 
 ;;Filtering
@@ -279,15 +282,25 @@ options specified in the Options panels."))
     gnc:pagename-general optname-common-currency
     "e" (N_ "Convert all transactions into a common currency.") #f
     #f
-    (lambda (x) (gnc-option-db-set-option-selectable-by-name
-                 options gnc:pagename-general optname-currency
-                 x))))
+    (lambda (x)
+      (begin
+        (gnc-option-db-set-option-selectable-by-name options
+                                                     gnc:pagename-general
+                                                     optname-currency x)
+        (gnc-option-db-set-option-selectable-by-name options
+                                                     gnc:pagename-general
+                                                     optname-orig-currency x)))))
 
   (gnc:options-add-currency!
    options gnc:pagename-general optname-currency "f")
 
   (gnc:register-trep-option
    (gnc:make-simple-boolean-option
+    gnc:pagename-general optname-orig-currency
+    "f1" (N_ "Also show original currency amounts") #f))
+
+  (gnc:register-trep-option
+   (gnc:make-simple-boolean-option
     gnc:pagename-general optname-table-export
     "g" (N_ "Formats the table suitable for cut & paste exporting with extra cells.") #f))
 
@@ -721,6 +734,9 @@ Credit Card, and Income accounts."))))))
           (cons 'price (opt-val gnc:pagename-display (N_ "Price")))
           (cons 'amount-single (eq? amount-setting 'single))
           (cons 'amount-double (eq? amount-setting 'double))
+          (cons 'amount-original-currency
+                (and (opt-val gnc:pagename-general optname-common-currency)
+                     (opt-val gnc:pagename-general optname-orig-currency)))
           (cons 'running-balance (opt-val gnc:pagename-display (N_ "Running Balance")))
           (cons 'account-full-name (opt-val gnc:pagename-display (N_ "Use Full Account Name")))
           (cons 'memo (opt-val gnc:pagename-display (N_ "Memo")))
@@ -764,13 +780,19 @@ Credit Card, and Income accounts."))))))
      (add-if (column-uses? 'shares columns-used)
              (_ "Shares"))
      (add-if (column-uses? 'price columns-used)
-             (_ "Price"))
+             (_ "Price"))))
+
+  (define (make-amount-heading-list columns-used)
+    (define (add-if pred? . items) (if pred? items '()))
+    (append
      (add-if (column-uses? 'amount-single columns-used)
              (_ "Amount"))
      ;; FIXME: Proper labels: what?
      (add-if (column-uses? 'amount-double columns-used)
              (_ "Debit")
              (_ "Credit"))
+     (add-if (column-uses? 'amount-original-currency columns-used)
+             (_ "Original"))
      (add-if (column-uses? 'running-balance columns-used)
              (_ "Balance"))))
 
@@ -779,7 +801,9 @@ Credit Card, and Income accounts."))))))
          (table (gnc:make-html-table))
          (used-columns (build-columns-used))
          (headings (make-heading-list used-columns))
+         (amount-headings (make-amount-heading-list used-columns))
          (width (length headings))
+         (width-amount (length amount-headings))
          (account-types-to-reverse
           (case (opt-val gnc:pagename-display (N_ "Sign Reverses"))
             ((none) '())
@@ -792,39 +816,156 @@ Credit Card, and Income accounts."))))))
 
     (define (add-subheading data subheading-style)
       (let ((heading-cell (gnc:make-html-table-cell data)))
-        (gnc:html-table-cell-set-colspan! heading-cell width)
+        (gnc:html-table-cell-set-colspan! heading-cell (+ width width-amount))
         (gnc:html-table-append-row/markup!
          table subheading-style
          (list heading-cell))))
 
-    (define (add-subtotal-row string collector style)
-      (let ((currency-totals (collector 'format gnc:make-gnc-monetary #f)))
-        (gnc:html-table-append-row/markup!
-         table style 
-         (if export?
-             (append! (cons (gnc:make-html-table-cell/markup "total-label-cell" string)
-                            (gnc:html-make-empty-cells (- width 2)))
-                      (list (gnc:make-html-table-cell/markup 
-                             "total-number-cell"
-                             (car currency-totals))))
-             (list (gnc:make-html-table-cell/size/markup 1 (- width 1) "total-label-cell"
-                                                         string)
-                   (gnc:make-html-table-cell/markup 
-                    "total-number-cell"
-                    (car currency-totals)))))
-        (for-each (lambda (currency)
-                    (gnc:html-table-append-row/markup! 
-                     table style
-                     (append!
-                      (if export?
-                          (gnc:html-make-empty-cells (- width 1))
-                          (list (gnc:make-html-table-cell/size 1 (- width 1) #f)))
-                      (list (gnc:make-html-table-cell/markup
-                             "total-number-cell" currency)))))
-                  (cdr currency-totals))))
+
+    (define (add-subtotal-row subtotal-string subtotal-collectors-and-calculated-cells subtotal-style)
+      (let* ((row-contents '())
+             (subtotal-collectors (map car subtotal-collectors-and-calculated-cells))
+             (calculated-cells  (map cadr subtotal-collectors-and-calculated-cells))
+             (merge-list (map (lambda (cell) (vector-ref cell 4)) calculated-cells))
+             (columns (map (lambda (coll) (coll 'format gnc:make-gnc-monetary #f)) subtotal-collectors))
+             (list-of-commodities (delete-duplicates (map gnc:gnc-monetary-commodity (concatenate columns))
+                                                     gnc-commodity-equal)))
+
+        (define (retrieve-commodity list-of-monetary commodity)
+          (and (not (null? list-of-monetary))
+               (if (gnc-commodity-equal (gnc:gnc-monetary-commodity (car list-of-monetary)) commodity)
+                   (car list-of-monetary)
+                   (retrieve-commodity (cdr list-of-monetary) commodity))))
+
+        (define (add-first-column string)
+          (if export?
+              (begin
+                (addto! row-contents (gnc:make-html-table-cell/markup "total-label-cell" string))
+                (for-each (lambda (cell) (addto! row-contents cell))
+                          (gnc:html-make-empty-cells (- width 1))))
+              (addto! row-contents (gnc:make-html-table-cell/size/markup 1 width "total-label-cell" string))))
+
+        (define (add-columns commodity)
+          (let ((merging? #f)
+                (merging-subtotal (gnc:make-gnc-numeric 0 1))
+                (width 0))
+            (for-each (lambda (column merge-entry)
+                        (let* ((mon (retrieve-commodity column commodity))
+                               (col (and mon (gnc:gnc-monetary-amount mon)))
+                               (merge? (vector-ref merge-entry 0))
+                               (merge-fn (vector-ref merge-entry 1)))
+                          (if merge?
+                              ;; We're merging. Run merge-fn (usu gnc-numeric-add or sub)
+                              ;; and store total in dual-subtotal. Do NOT add column.
+                              (begin
+                                (if column-amount
+                                    (set! merging-subtotal
+                                          (merge-fn merging-subtotal column-amount)))
+                                (set! merging? #t)
+                                (if col
+                                    (set! merging-subtotal
+                                          (merge-fn merging-subtotal col GNC-DENOM-AUTO GNC-RND-ROUND)))
+                                (set! width (+ width 1)))
+                              (if merging?
+                                  (begin
+                                    ;; We've completed merging. Add this column amount
+                                    ;; and add the columns.
+                                    (set! merging? #f)
+                                    (if col
+                                        (set! merging-subtotal
+                                              (merge-fn merging-subtotal column-amount)))
+                                    (set! width (+ width 1))
+                                    (addto! row-contents
+                                            (gnc:make-html-table-cell/size/markup
+                                             1 width "total-number-cell"
+                                             (gnc:make-gnc-monetary commodity merging-subtotal)))
+                                    (set! width 0)
+                                    (set! merging-subtotal (gnc:make-gnc-numeric 0 1)))
+                                  ;; Default; not merging/completed merge. Just
+                                  ;; display monetary amount
+                                  (addto! row-contents
+                                          (gnc:make-html-table-cell/markup "total-number-cell" mon))))))
+                      columns
+                      merge-list)))
+
+        ;;first row
+        (add-first-column subtotal-string)
+        (add-columns (and (pair? list-of-commodities)
+                          (car list-of-commodities))) ;to account for empty-row subtotals
+        (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents))
+
+        ;;subsequent rows
+        (if (pair? list-of-commodities)
+            (for-each (lambda (commodity)
+                        (set! row-contents '())
+                        (add-first-column "")
+                        (add-columns commodity)
+                        (gnc:html-table-append-row/markup! table subtotal-style (reverse row-contents)))
+                      (cdr list-of-commodities)))))
 
     (define (total-string str) (string-append (_ "Total For ") str))
 
+    ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+    ;;
+    ;; calculated-cells
+    ;;
+    ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+    (define calculated-cells
+      (letrec
+          ((damount (lambda (s) (if (gnc:split-voided? s)
+                                    (xaccSplitVoidFormerAmount s)
+                                    (xaccSplitGetAmount s))))
+           (trans-date (lambda (s) (gnc-transaction-get-date-posted (xaccSplitGetTransaction s))))
+           (currency (lambda (s) (xaccAccountGetCommodity (xaccSplitGetAccount s))))
+           (report-currency (lambda (s) (if (opt-val gnc:pagename-general optname-common-currency)
+                                            (opt-val gnc:pagename-general optname-currency)
+                                            (currency s))))
+           (convert (lambda (s num)
+                      (gnc:exchange-by-pricedb-nearest
+                       (gnc:make-gnc-monetary (currency s) num)
+                       (report-currency s)
+                       ;; Use midday as the transaction time so it matches a price
+                       ;; on the same day.  Otherwise it uses midnight which will
+                       ;; likely match a price on the previous day
+                       (timespecCanonicalDayTime trans-date))))
+           (split-value (lambda (s) (convert s (damount s)))) ; used for correct debit/credit
+           (amount (lambda (s) (split-value s)))
+           (debit-amount (lambda (s) (and (gnc-numeric-positive-p (gnc:gnc-monetary-amount (split-value s)))
+                                          (split-value s))))
+           (credit-amount (lambda (s) (if (gnc-numeric-positive-p (gnc:gnc-monetary-amount (split-value s)))
+                                          #f
+                                          (gnc:monetary-neg (split-value s)))))
+           (original-amount (lambda (s) (gnc:make-gnc-monetary (currency s) (damount s))))
+           (running-balance (lambda (s) (gnc:make-gnc-monetary (currency s) (xaccSplitGetBalance s)))))
+        (append
+         ;; each column will be a vector
+         ;; (vector heading calculator-function reverse-column? subtotal? (vector merge? merging-function))
+         ;; (calculator-function split) to obtain amount
+         ;; reverse? to optionally reverse signs
+         ;; subtotal? to allow subtotals (ie irrelevant for running balance)
+         ;; merge? to merge with the next cell (ie for debit/credit cells)
+         ;; merging-function - function (usually gnc-numeric-add/sub-fixed to apply to merging-subtotal
+         (if (column-uses? 'amount-single used-columns)
+             (list (vector "Amount" amount #t #t (vector #f #f)))
+             '())
+         (if (column-uses? 'amount-double used-columns)
+             (list (vector "Debit" debit-amount #f #t (vector #t gnc-numeric-add-fixed))
+                   (vector "Credit" credit-amount #f #t (vector #f gnc-numeric-sub-fixed)))
+             '())
+         (if (column-uses? 'amount-original-currency used-columns)
+             (list (vector "Original" original-amount #t #t (vector #f #f)))
+             '())
+         (if (column-uses? 'running-balance used-columns)
+             (list (vector "Running Balance" running-balance #t #f (vector #f #f)))
+             '()))))
+
+    ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+    ;; renderers
+
+    ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
     ;; display an account name depending on the options the user has set
     (define (account-namestring account show-account-code? show-account-name? show-account-full-name?)
       ;;# on multi-line splits we can get an empty ('()) account
@@ -880,39 +1021,31 @@ Credit Card, and Income accounts."))))))
     (define (render-grand-total)
       (_ "Grand Total"))
 
-    (define (add-split-row split row-style transaction-row?)
+    ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+    ;;
+    ;; add-split-row
+    ;;
+    ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+    (define (add-split-row split cell-calculators row-style transaction-row?)
       (let* ((row-contents '())
-             (parent (xaccSplitGetParent split))
-             (account (xaccSplitGetAccount split))
-             (account-type (xaccAccountGetType account))
-             (currency (if (null? account)
-                           (gnc-default-currency)
-                           (xaccAccountGetCommodity account)))
-             (report-currency (if (opt-val gnc:pagename-general optname-common-currency)
-                                  (opt-val gnc:pagename-general optname-currency)
-                                  currency))
-             (damount (if (gnc:split-voided? split)
-                          (xaccSplitVoidFormerAmount split)
-                          (xaccSplitGetAmount split)))
-             (trans-date (gnc-transaction-get-date-posted parent))
-             (split-value (gnc:exchange-by-pricedb-nearest
-                           (gnc:make-gnc-monetary
-                            currency
-                            (if (member account-type account-types-to-reverse)
-                                (gnc-numeric-neg damount)
-                                damount))
-                           report-currency
-                           ;; Use midday as the transaction time so it matches a price
-                           ;; on the same day.  Otherwise it uses midnight which will
-                           ;; likely match a price on the previous day
-                           (timespecCanonicalDayTime trans-date))))
+             (trans (xaccSplitGetParent split)))
+
+        (define cells
+          (map (lambda (cell)
+                 (let* ((calculator (vector-ref cell 1))
+                        (reverse? (vector-ref cell 2))
+                        (subtotal? (vector-ref cell 3))
+                        (calculated (calculator split)))
+                   (vector calculated reverse? subtotal?)))
+               cell-calculators))
 
         (if (column-uses? 'date used-columns)
             (addto! row-contents
                     (if transaction-row?
                         (gnc:make-html-table-cell/markup
                          "date-cell"
-                         (gnc-print-date trans-date))
+                         (gnc-print-date (gnc-transaction-get-date-posted trans)))
                         "")))
 
         (if (column-uses? 'reconciled-date used-columns)
@@ -928,19 +1061,20 @@ Credit Card, and Income accounts."))))))
             (addto! row-contents
                     (if transaction-row?
                         (if BOOK-SPLIT-ACTION
-                            (let* ((num (gnc-get-num-action parent split))
-                                   (t-num (if (if (gnc:lookup-option options gnc:pagename-display
+                            (let* ((num (gnc-get-num-action trans split))
+                                   (t-num (if (if (gnc:lookup-option options
+                                                                     gnc:pagename-display
                                                                      (N_ "Trans Number"))
                                                   (opt-val gnc:pagename-display (N_ "Trans Number"))
                                                   "")
-                                              (gnc-get-num-action parent #f)
+                                              (gnc-get-num-action trans #f)
                                               ""))
                                    (num-string (if (string-null? t-num)
                                                    num
                                                    (string-append num "/" t-num))))
                               (gnc:make-html-table-cell/markup "text-cell" num-string))
                             (gnc:make-html-table-cell/markup "text-cell"
-                                                             (gnc-get-num-action parent split)))
+                                                             (gnc-get-num-action trans split)))
                         "")))
 
         (if (column-uses? 'description used-columns)
@@ -948,17 +1082,17 @@ Credit Card, and Income accounts."))))))
                     (if transaction-row?
                         (gnc:make-html-table-cell/markup
                          "text-cell"
-                         (xaccTransGetDescription parent))
+                         (xaccTransGetDescription trans))
                         "")))
 
         (if (column-uses? 'memo used-columns)
             (let ((memo (xaccSplitGetMemo split)))
               (if (and (string-null? memo) (column-uses? 'notes used-columns))
-                  (addto! row-contents (xaccTransGetNotes parent))
+                  (addto! row-contents (xaccTransGetNotes trans))
                   (addto! row-contents memo))))
 
         (if (or (column-uses? 'account-name used-columns) (column-uses? 'account-code used-columns))
-            (addto! row-contents (account-namestring account
+            (addto! row-contents (account-namestring (xaccSplitGetAccount split)
                                                      (column-uses? 'account-code      used-columns)
                                                      (column-uses? 'account-name      used-columns)
                                                      (column-uses? 'account-full-name used-columns))))
@@ -976,51 +1110,47 @@ Credit Card, and Income accounts."))))))
             (addto! row-contents  (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
                                                          (xaccSplitGetSharePrice split))))
 
-        (if (column-uses? 'amount-single used-columns)
-            (addto! row-contents
-                    (gnc:make-html-table-cell/markup
-                     "number-cell" (gnc:html-transaction-anchor parent split-value))))
-
-        (if (column-uses? 'amount-double used-columns)
-
-            (if (gnc-numeric-positive-p (gnc:gnc-monetary-amount split-value))
-
-                (begin
-                  (addto! row-contents
-                          (gnc:make-html-table-cell/markup
-                           "number-cell" (gnc:html-transaction-anchor
-                                          parent split-value)))
-                  (addto! row-contents ""))
-
-                (begin
-                  (addto! row-contents "")
-                  (addto! row-contents
-                          (gnc:make-html-table-cell/markup
-                           "number-cell" (gnc:html-transaction-anchor
-                                          parent (gnc:monetary-neg split-value)))))))
-
-        (if (column-uses? 'running-balance used-columns)
-            (begin
-              ;(gnc:debug "split is " split)
-              ;(gnc:debug "split get balance:" (xaccSplitGetBalance split))
-              (addto! row-contents
-                      (gnc:make-html-table-cell/markup
-                       "number-cell"
-                       (gnc:make-gnc-monetary currency
-                                              (xaccSplitGetBalance split))))))
+        (for-each (lambda (cell)
+                    (let ((cell-content (vector-ref cell 0))
+                          (reverse? (vector-ref cell 1))
+                          (reverse-amount (lambda (mon)
+                                            (let ((currency (gnc:gnc-monetary-commodity mon))
+                                                  (amount (gnc:gnc-monetary-amount mon)))
+                                              (gnc:make-gnc-monetary
+                                               currency
+                                               (gnc-numeric-neg amount))))))
+                      (if cell-content
+                          (addto! row-contents
+                                  (gnc:make-html-table-cell/markup
+                                   "number-cell"
+                                   (gnc:html-transaction-anchor
+                                    trans
+                                    (if (and reverse?
+                                             (member (xaccAccountGetType account) account-types-to-reverse))
+                                        (reverse-amount cell-content)
+                                        cell-content))))
+                          (addto! row-contents (gnc:html-make-empty-cell)))))
+                  cells)
 
         (gnc:html-table-append-row/markup! table row-style (reverse row-contents))
 
-        split-value))
+        (map (lambda (cell)
+               (let ((cell-content (vector-ref cell 0))
+                     (subtotal? (vector-ref cell 2)))
+                 (and subtotal? cell-content)))
+             cells)))
 
+    ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+    ;; do-rows-with-subtotals
 
+    ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
     (define (do-rows-with-subtotals splits
                                     odd-row?
-                                    primary-subtotal-collector
-                                    secondary-subtotal-collector
-                                    total-collector)
+                                    primary-subtotal-collectors
+                                    secondary-subtotal-collectors
+                                    total-collectors)
 
       (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
 
@@ -1034,36 +1164,46 @@ Credit Card, and Income accounts."))))))
              table def:grand-total-style
              (list
               (gnc:make-html-table-cell/size
-               1 width (gnc:make-html-text (gnc:html-markup-hr)))))
+               1 (+ width width-amount) (gnc:make-html-text (gnc:html-markup-hr)))))
 
             (if (opt-val gnc:pagename-display "Totals")
-                (add-subtotal-row (render-grand-total) total-collector def:grand-total-style)))
+                (add-subtotal-row (render-grand-total) (zip total-collectors calculated-cells) def:grand-total-style)))
 
           (let* ((current (car splits))
                  (rest (cdr splits))
                  (next (if (null? rest) #f (car rest)))
-                 (split-value (add-split-row
-                               current
-                               (if is-multiline? def:normal-row-style
-                                   (if odd-row?
-                                       def:normal-row-style
-                                       def:alternate-row-style))
-                               #t)))
+                 (split-values (add-split-row
+                                current
+                                calculated-cells
+                                (if is-multiline? def:normal-row-style
+                                    (if odd-row?
+                                        def:normal-row-style
+                                        def:alternate-row-style))
+                                #t)))
 
             (if is-multiline?
                 (for-each
                  (lambda (othersplits)
-                   (add-split-row othersplits def:alternate-row-style #f))
+                   (add-split-row othersplits calculated-cells def:alternate-row-style #f))
                  (delete current (xaccTransGetSplitList (xaccSplitGetParent current)))))
 
-            (primary-subtotal-collector
-             'add (gnc:gnc-monetary-commodity split-value) (gnc:gnc-monetary-amount split-value))
+            (map (lambda (collector value)
+                   (if value
+                       (collector 'add (gnc:gnc-monetary-commodity value) (gnc:gnc-monetary-amount value))))
+                 primary-subtotal-collectors
+                 split-values)
 
-            (secondary-subtotal-collector
-             'add (gnc:gnc-monetary-commodity split-value) (gnc:gnc-monetary-amount split-value))
+            (map (lambda (collector value)
+                   (if value
+                       (collector 'add (gnc:gnc-monetary-commodity value) (gnc:gnc-monetary-amount value))))
+                 secondary-subtotal-collectors
+                 split-values)
 
-            (total-collector
-             'add (gnc:gnc-monetary-commodity split-value) (gnc:gnc-monetary-amount split-value))
+            (map (lambda (collector value)
+                   (if value
+                       (collector 'add (gnc:gnc-monetary-commodity value) (gnc:gnc-monetary-amount value))))
+                 total-collectors
+                 split-values)
 
             (if (and primary-subtotal-comparator
                      (or (not next)
@@ -1076,17 +1216,16 @@ Credit Card, and Income accounts."))))))
                       (begin
                         (add-subtotal-row (total-string
                                            (render-summary current secondary-renderer-key #f))
-                                          secondary-subtotal-collector
+                                          (zip secondary-subtotal-collectors calculated-cells)
                                           def:secondary-subtotal-style)
-                        
-                        (secondary-subtotal-collector 'reset #f #f)))
+                        (for-each (lambda (coll) (coll 'reset #f #f))
+                                  secondary-subtotal-collectors)))
                   (add-subtotal-row (total-string
                                      (render-summary current primary-renderer-key #f))
-                                    primary-subtotal-collector
+                                    (zip primary-subtotal-collectors calculated-cells)
                                     def:primary-subtotal-style)
-                  
-                  (primary-subtotal-collector 'reset #f #f)
-                  
+                  (for-each (lambda (coll) (coll 'reset #f #f))
+                            primary-subtotal-collectors)
                   (if next
                       (begin
                         (add-subheading (render-summary next primary-renderer-key #t)
@@ -1102,20 +1241,21 @@ Credit Card, and Income accounts."))))))
                                                (secondary-subtotal-comparator next))))))
                     (begin (add-subtotal-row (total-string
                                               (render-summary current secondary-renderer-key #f))
-                                             secondary-subtotal-collector
+                                             (zip secondary-subtotal-collectors calculated-cells)
                                              def:secondary-subtotal-style)
-                           (secondary-subtotal-collector 'reset #f #f)
+                           (for-each (lambda (coll) (coll 'reset #f #f))
+                                     secondary-subtotal-collectors)
                            (if next
                                (add-subheading (render-summary next secondary-renderer-key #t)
                                                def:secondary-subtotal-style)))))
 
             (do-rows-with-subtotals rest
                                     (not odd-row?)
-                                    primary-subtotal-collector
-                                    secondary-subtotal-collector
-                                    total-collector))))
+                                    primary-subtotal-collectors
+                                    secondary-subtotal-collectors
+                                    total-collectors))))
 
-    (gnc:html-table-set-col-headers! table headings)
+    (gnc:html-table-set-col-headers! table (concatenate (list headings amount-headings)))
 
     (if primary-renderer-key
         (add-subheading (render-summary (car splits) primary-renderer-key #t)
@@ -1126,9 +1266,9 @@ Credit Card, and Income accounts."))))))
                         def:secondary-subtotal-style))
 
     (do-rows-with-subtotals splits #t
-                            (gnc:make-commodity-collector)
-                            (gnc:make-commodity-collector)
-                            (gnc:make-commodity-collector))
+                            (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells)
+                            (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells)
+                            (map (lambda (x) (gnc:make-commodity-collector)) calculated-cells))
 
     table))
 

commit b549dd68fbab55f13fc4c91d7ff5dbe81c300400
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Dec 15 18:10:42 2017 +0800

    ENH: add custom sorter which can handle periodic dates

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index f5edb6e..951caef 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -11,6 +11,7 @@
 ;; <tpo_deb at sourcepole.ch> with a lot of help from "warlord"
 ;; Refactored by Christopher Lam (2017)
 ;; - introduced account/transaction substring/regex matcher
+;; - add custom sorter in scheme
 ;;
 ;; This program is free software; you can redistribute it and/or    
 ;; modify it under the terms of the GNU General Public License as   
@@ -1202,14 +1203,71 @@ Credit Card, and Income accounts."))))))
          (report-title (opt-val gnc:pagename-general gnc:optname-reportname))
          (primary-key (opt-val pagename-sorting optname-prime-sortkey))
          (primary-order (opt-val pagename-sorting optname-prime-sortorder))
+         (primary-date-subtotal (opt-val pagename-sorting optname-prime-date-subtotal))
          (secondary-key (opt-val pagename-sorting optname-sec-sortkey))
          (secondary-order (opt-val pagename-sorting optname-sec-sortorder))
+         (secondary-date-subtotal (opt-val pagename-sorting optname-sec-date-subtotal))
          (void-status (opt-val gnc:pagename-accounts optname-void-transactions))
          (splits '())
+         (custom-sort? (or (and (member primary-key DATE-SORTING-TYPES)   ; this will remain
+                                (not (eq? primary-date-subtotal 'none)))  ; until qof-query
+                           (and (member secondary-key DATE-SORTING-TYPES) ; is upgraded
+                                (not (eq? secondary-date-subtotal 'none)))))
          (query (qof-query-create-for-splits)))
 
-    ;;(gnc:warn "accts in trep-renderer:" c_account_1)
-    ;;(gnc:warn "Report Account names:" (get-other-account-names c_account_1))
+    (define (generic-less? X Y key date-subtotal ascend?)
+      (define comparator-function
+        (if (member key DATE-SORTING-TYPES)
+            (let* ((date (lambda (s)
+                           (case key
+                             ((date) (xaccTransGetDate (xaccSplitGetParent s)))
+                             ((reconciled-date) (xaccSplitGetDateReconciled s)))))
+                   (year    (lambda (s) (gnc:date-get-year (gnc-localtime (date s)))))
+                   (month   (lambda (s) (gnc:date-get-month (gnc-localtime (date s)))))
+                   (quarter (lambda (s) (gnc:date-get-quarter (gnc-localtime (date s)))))
+                   (week    (lambda (s) (gnc:date-get-week (gnc-localtime (date s)))))
+                   (secs    (lambda (s) (date s))))
+              (case date-subtotal
+                ((yearly)    (lambda (s) (year s)))
+                ((monthly)   (lambda (s) (+ (* 100 (year s)) (month s))))
+                ((quarterly) (lambda (s) (+ (*  10 (year s)) (quarter s))))
+                ((weekly)    (lambda (s) (week s)))
+                ((none)      (lambda (s) (secs s)))))
+            (case key
+              ((account-name) (lambda (s) (gnc-account-get-full-name (xaccSplitGetAccount s))))
+              ((account-code) (lambda (s) (xaccAccountGetCode (xaccSplitGetAccount s))))
+              ((corresponding-acc-name) (lambda (s) (xaccSplitGetCorrAccountFullName s)))
+              ((corresponding-acc-code) (lambda (s) (xaccSplitGetCorrAccountCode s)))
+              ((amount) (lambda (s) (gnc-numeric-to-double (xaccSplitGetValue s))))
+              ((description) (lambda (s) (xaccTransGetDescription (xaccSplitGetParent s))))
+              ((number) (lambda (s)
+                          (if BOOK-SPLIT-ACTION
+                              (xaccSplitGetAction s)
+                              (xaccTransGetNum (xaccSplitGetParent s)))))
+              ((t-number) (lambda (s) (xaccTransGetNum (xaccSplitGetParent s))))
+              ((register-order) (lambda (s) #f))
+              ((memo) (lambda (s) (xaccSplitGetMemo s)))
+              ((none) (lambda (s) #f)))))
+      (cond
+       ((string? (comparator-function X)) ((if ascend? string<? string>?) (comparator-function X) (comparator-function Y)))
+       ((comparator-function X)           ((if ascend? < >)               (comparator-function X) (comparator-function Y)))
+       (else                              #f)))
+
+    (define (primary-comparator? X Y)
+      (generic-less? X Y primary-key
+                     primary-date-subtotal
+                     (eq? primary-order 'ascend)))
+
+    (define (secondary-comparator? X Y)
+      (generic-less? X Y secondary-key
+                     secondary-date-subtotal
+                     (eq? secondary-order 'ascend)))
+
+    ;; This will, by default, sort the split list by ascending posted-date.
+    (define (date-comparator? X Y)
+      (generic-less? X Y 'date 'none #t))
+
+
 
     (if (or (null? c_account_1) (and-map not c_account_1))
 
@@ -1232,22 +1290,30 @@ Credit Card, and Income accounts."))))))
           (qof-query-set-book query (gnc-get-current-book))
           (xaccQueryAddAccountMatch query c_account_1 QOF-GUID-MATCH-ANY QOF-QUERY-AND)
           (xaccQueryAddDateMatchTS query #t begindate #t enddate QOF-QUERY-AND)
-          (qof-query-set-sort-order query
-                                    (sortkey-get-info primary-key 'sortkey)
-                                    (sortkey-get-info secondary-key 'sortkey)
-                                    '())
-          (qof-query-set-sort-increasing query
-                                         (eq? primary-order 'ascend)
-                                         (eq? secondary-order 'ascend)
-                                         #t)
           (case void-status
             ((non-void-only) (gnc:query-set-match-non-voids-only! query (gnc-get-current-book)))
             ((void-only)     (gnc:query-set-match-voids-only! query (gnc-get-current-book)))
             (else #f))
+          (if (not custom-sort?)
+              (begin
+                (qof-query-set-sort-order query
+                                          (sortkey-get-info primary-key 'sortkey)
+                                          (sortkey-get-info secondary-key 'sortkey)
+                                          '())
+                (qof-query-set-sort-increasing query
+                                               (eq? primary-order 'ascend)
+                                               (eq? secondary-order 'ascend)
+                                               #t)))
           (set! splits (qof-query-run query))
 
           (qof-query-destroy query)
 
+          (if custom-sort?
+              (begin
+                (set! splits (stable-sort! splits date-comparator?))
+                (set! splits (stable-sort! splits secondary-comparator?))
+                (set! splits (stable-sort! splits primary-comparator?))))
+
           ;; Combined Filter:
           ;; - include/exclude splits to/from selected accounts
           ;; - substring/regex matcher for Transaction Description/Notes/Memo

commit 4bfd01e70608ce39f3eddf0b65d59d3324cfc6ca
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 20:33:20 2017 +0800

    REFACTOR: simplify do-rows-with-subtotals to use fewer args

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 130b5ab..f5edb6e 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1016,19 +1016,7 @@ Credit Card, and Income accounts."))))))
 
 
     (define (do-rows-with-subtotals splits
-                                    table
-                                    used-columns
-                                    width
-                                    multi-rows?
                                     odd-row?
-                                    export?
-                                    account-types-to-reverse
-                                    primary-subtotal-pred
-                                    secondary-subtotal-pred
-                                    primary-subheading-renderer
-                                    secondary-subheading-renderer
-                                    primary-subtotal-renderer
-                                    secondary-subtotal-renderer
                                     primary-subtotal-collector
                                     secondary-subtotal-collector
                                     total-collector)
@@ -1052,55 +1040,38 @@ Credit Card, and Income accounts."))))))
 
           (let* ((current (car splits))
                  (rest (cdr splits))
-                 (next (if (null? rest) #f (car rest))))
-
-            (define split-value (add-split-row
-                                 table
-                                 current
-                                 used-columns
-                                 options
-                                 (if multi-rows? def:normal-row-style
-                                     (if odd-row?
-                                         def:normal-row-style
-                                         def:alternate-row-style))
-                                 account-types-to-reverse
-                                 #t))
-            
-            (if multi-rows?
-
-                (for-each (lambda (othersplits)
-                            (add-split-row table
-                                           othersplits
-                                           used-columns
-                                           options
-                                           def:alternate-row-style
-                                           account-types-to-reverse
-                                           #f))
-                          (delete current (xaccTransGetSplitList
-                                           (xaccSplitGetParent current)))))
-
-            (primary-subtotal-collector 'add
-                                        (gnc:gnc-monetary-commodity split-value) 
-                                        (gnc:gnc-monetary-amount split-value))
-
-            (secondary-subtotal-collector 'add
-                                          (gnc:gnc-monetary-commodity split-value)
-                                          (gnc:gnc-monetary-amount split-value))
-            
-            (total-collector 'add
-                             (gnc:gnc-monetary-commodity split-value)
-                             (gnc:gnc-monetary-amount split-value))
-            
-            (if (and primary-subtotal-pred
+                 (next (if (null? rest) #f (car rest)))
+                 (split-value (add-split-row
+                               current
+                               (if is-multiline? def:normal-row-style
+                                   (if odd-row?
+                                       def:normal-row-style
+                                       def:alternate-row-style))
+                               #t)))
+
+            (if is-multiline?
+                (for-each
+                 (lambda (othersplits)
+                   (add-split-row othersplits def:alternate-row-style #f))
+                 (delete current (xaccTransGetSplitList (xaccSplitGetParent current)))))
+
+            (primary-subtotal-collector
+             'add (gnc:gnc-monetary-commodity split-value) (gnc:gnc-monetary-amount split-value))
+
+            (secondary-subtotal-collector
+             'add (gnc:gnc-monetary-commodity split-value) (gnc:gnc-monetary-amount split-value))
+
+            (total-collector
+             'add (gnc:gnc-monetary-commodity split-value) (gnc:gnc-monetary-amount split-value))
+
+            (if (and primary-subtotal-comparator
                      (or (not next)
                          (and next
-                              (not (equal? (primary-subtotal-pred current)
-                                           (primary-subtotal-pred next))))))
+                              (not (equal? (primary-subtotal-comparator current)
+                                           (primary-subtotal-comparator next))))))
 
                 (begin
-
-                  (if secondary-subtotal-pred
-                      
+                  (if secondary-subtotal-comparator
                       (begin
                         (add-subtotal-row (total-string
                                            (render-summary current secondary-renderer-key #f))
@@ -1123,27 +1094,21 @@ Credit Card, and Income accounts."))))))
                             (add-subheading (render-summary next secondary-renderer-key #t)
                                             def:secondary-subtotal-style)))))
 
-                (if (and secondary-subtotal-pred
+                (if (and secondary-subtotal-comparator
                          (or (not next)
                              (and next
-                                  (not (equal? (secondary-subtotal-pred current)
-                                               (secondary-subtotal-pred next))))))
+                                  (not (equal? (secondary-subtotal-comparator current)
+                                               (secondary-subtotal-comparator next))))))
                     (begin (add-subtotal-row (total-string
                                               (render-summary current secondary-renderer-key #f))
                                              secondary-subtotal-collector
                                              def:secondary-subtotal-style)
-                           
                            (secondary-subtotal-collector 'reset #f #f)
-                           
                            (if next
                                (add-subheading (render-summary next secondary-renderer-key #t)
                                                def:secondary-subtotal-style)))))
 
             (do-rows-with-subtotals rest
-                                    table
-                                    used-columns
-                                    width
-                                    multi-rows?
                                     (not odd-row?)
                                     primary-subtotal-collector
                                     secondary-subtotal-collector

commit c7f9fb1a3aa5eaf1a2bb8e1810a399e97ae6291c
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Dec 15 17:43:49 2017 +0800

    REFACTOR: use scheme idioms

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index db12bef..130b5ab 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1213,9 +1213,8 @@ Credit Card, and Income accounts."))))))
 
   (let* ((document (gnc:make-html-document))
          (account-matcher (opt-val pagename-filter optname-account-matcher))
-         (account-matcher-regexp (if (opt-val pagename-filter optname-account-matcher-regex)
-                                     (make-regexp account-matcher)
-                                     #f))
+         (account-matcher-regexp (and (opt-val pagename-filter optname-account-matcher-regex)
+                                      (make-regexp account-matcher)))
          (c_account_0 (opt-val gnc:pagename-accounts optname-accounts))
          (c_account_1 (filter
                        (lambda (acc)
@@ -1232,9 +1231,8 @@ Credit Card, and Income accounts."))))))
                    (gnc:date-option-absolute-time
                     (opt-val gnc:pagename-general optname-enddate))))
          (transaction-matcher (opt-val pagename-filter optname-transaction-matcher))
-         (transaction-matcher-regexp (if (opt-val pagename-filter optname-transaction-matcher-regex)
-                                         (make-regexp transaction-matcher)
-                                         #f))
+         (transaction-matcher-regexp (and (opt-val pagename-filter optname-transaction-matcher-regex)
+                                          (make-regexp transaction-matcher)))
          (reconcile-status-filter (opt-val pagename-filter optname-reconcile-status))
          (report-title (opt-val gnc:pagename-general gnc:optname-reportname))
          (primary-key (opt-val pagename-sorting optname-prime-sortkey))
@@ -1296,8 +1294,10 @@ Credit Card, and Income accounts."))))))
                                            (if transaction-matcher-regexp
                                                (regexp-exec transaction-matcher-regexp str)
                                                (string-contains str transaction-matcher)))))
-                            (and (if (eq? filter-mode 'include) (is-filter-member split c_account_2) #t)
-                                 (if (eq? filter-mode 'exclude) (not (is-filter-member split c_account_2)) #t)
+                            (and (case filter-mode
+                                   ((none) #t)
+                                   ((include) (is-filter-member split c_account_2))
+                                   ((exclude) (not (is-filter-member split c_account_2))))
                                  (or (string-null? transaction-matcher) ; null-string = ignore filters
                                      (match? (xaccTransGetDescription trans))
                                      (match? (xaccTransGetNotes trans))

commit 68aa61a37cb6ff652ebdee079e892f0d23036350
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Dec 15 17:42:58 2017 +0800

    REFACTOR: Simplify Trans Number handling
    
    Previously there was a check for the presence of "Trans Number".
    But if 'use-split-action' the options will always contain
    the toggle 'Trans Number'. Therefore remove unnecessary check.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 31674a6..db12bef 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -745,9 +745,7 @@ Credit Card, and Income accounts."))))))
              (_ "Reconciled Date"))
      (add-if (column-uses? 'num columns-used)
              (if (and (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-                      (if (gnc:lookup-option options gnc:pagename-display (N_ "Trans Number"))
-                          (opt-val gnc:pagename-display (N_ "Trans Number"))
-                          #f))
+                      (opt-val gnc:pagename-display (N_ "Trans Number")))
                  (_ "Num/T-Num")
                  (_ "Num")))
      (add-if (column-uses? 'description columns-used)

commit 8044f2b04e064fd1b799773f9a0340f10fcd87e1
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 20:30:02 2017 +0800

    COSMETIC:Rename subtitles -> subheadings in sorting/account display

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index ff510d5..31674a6 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -478,14 +478,14 @@ tags within description, notes or memo. ")
      (gnc:make-simple-boolean-option
       pagename-sorting optname-full-account-name
       "j1"
-      (N_ "Show the full account name for subtotals and subtitles?")
+      (N_ "Show the full account name for subtotals and subheadings?")
       #f))
 
     (gnc:register-trep-option
      (gnc:make-simple-boolean-option
       pagename-sorting optname-show-account-code
       "j2"
-      (N_ "Show the account code for subtotals and subtitles?")
+      (N_ "Show the account code for subtotals and subheadings?")
       #f))
 
     (gnc:register-trep-option

commit b6c6906bb10df1aaa69ddbbc7687af19def156e1
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 20:18:31 2017 +0800

    REFACTOR: initialize accounts/filter by to null list

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 7cfe8ca..ff510d5 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -358,14 +358,7 @@ tags within description, notes or memo. ")
     gnc:pagename-accounts optname-filterby
     "b" (N_ "Filter on these accounts.")
     (lambda ()
-      ;; FIXME : gnc:get-current-accounts disappeared.
-      (let* ((current-accounts '())
-             (root (gnc-get-current-root-account))
-             (num-accounts (gnc-account-n-children root))
-             (first-account (gnc-account-nth-child root 0)))
-        (cond ((not (null? current-accounts)) (list (car current-accounts)))
-              ((positive? num-accounts) (list first-account))
-              (else '()))))
+      '())
     #f #t))
 
   (gnc:register-trep-option

commit 8e4d72b5444b98dba027e6fd5ca90fd5c7574c54
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 20:17:09 2017 +0800

    REFACTOR: rewrite renderers to lookup 'renderer-key from sortlists

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index e6ef525..7cfe8ca 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -111,101 +111,100 @@ options specified in the Options panels."))
                                'account-code 'corresponding-acc-code))
 
 
-
 (define sortkey-list
   ;;
-  ;; Defines the different sorting keys, as an association-list 
-  ;; together with the subtotal functions. Each entry: 
+  ;; Defines the different sorting keys, as an association-list
+  ;; together with the subtotal functions. Each entry:
   ;;  'sortkey             - sort parameter sent via qof-query
   ;;  'split-sortvalue     - function which retrieves number/string used for comparing splits
   ;;  'text                - text displayed in Display tab
   ;;  'tip                 - tooltip displayed in Display tab
-  ;;  'renderer            - helper symbol to select subtotal/subheading renderer
+  ;;  'renderer-key        - helper symbol to select subtotal/subheading renderer
   ;;
   (list (cons 'account-name  (list (cons 'sortkey (list SPLIT-ACCT-FULLNAME))
                                    (cons 'split-sortvalue (lambda (a) (gnc-account-get-full-name (xaccSplitGetAccount a))))
                                    (cons 'text (N_ "Account Name"))
                                    (cons 'tip (N_ "Sort & subtotal by account name."))
-                                   (cons 'renderer 'account)))
-        
+                                   (cons 'renderer-key 'account)))
+
         (cons 'account-code (list (cons 'sortkey (list SPLIT-ACCOUNT ACCOUNT-CODE-))
                                   (cons 'split-sortvalue (lambda (a) (xaccAccountGetCode (xaccSplitGetAccount a))))
                                   (cons 'text (N_ "Account Code"))
                                   (cons 'tip (N_ "Sort & subtotal by account code."))
-                                  (cons 'renderer 'account)))
+                                  (cons 'renderer-key 'account)))
 
         (cons 'date         (list (cons 'sortkey (list SPLIT-TRANS TRANS-DATE-POSTED))
                                   (cons 'split-sortvalue #f)
                                   (cons 'text (N_ "Date"))
                                   (cons 'tip (N_ "Sort by date."))
-                                  (cons 'renderer #f)))
+                                  (cons 'renderer-key #f)))
 
         (cons 'reconciled-date (list (cons 'sortkey (list SPLIT-DATE-RECONCILED))
                                      (cons 'split-sortvalue #f)
                                      (cons 'text (N_ "Reconciled Date"))
                                      (cons 'tip (N_ "Sort by the Reconciled Date."))
-                                     (cons 'renderer #f)))
+                                     (cons 'renderer-key #f)))
 
         (cons 'register-order (list (cons 'sortkey (list QUERY-DEFAULT-SORT))
                                     (cons 'split-sortvalue #f)
                                     (cons 'text (N_ "Register Order"))
                                     (cons 'tip (N_ "Sort as in the register."))
-                                    (cons 'renderer #f)))                                 
+                                    (cons 'renderer-key #f)))
 
         (cons 'corresponding-acc-name (list (cons 'sortkey (list SPLIT-CORR-ACCT-NAME))
                                             (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountFullName a)))
                                             (cons 'text (N_ "Other Account Name"))
                                             (cons 'tip (N_ "Sort by account transferred from/to's name."))
-                                            (cons 'renderer 'other-acc)))
+                                            (cons 'renderer-key 'other-acc)))
 
         (cons 'corresponding-acc-code (list (cons 'sortkey (list SPLIT-CORR-ACCT-CODE))
                                             (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountCode a)))
                                             (cons 'text (N_ "Other Account Code"))
                                             (cons 'tip (N_ "Sort by account transferred from/to's code."))
-                                            (cons 'renderer 'other-acct)))
+                                            (cons 'renderer-key 'other-acct)))
 
         (cons 'amount        (list (cons 'sortkey (list SPLIT-VALUE))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Amount"))
                                    (cons 'tip (N_ "Sort by amount."))
-                                   (cons 'renderer #f)))
+                                   (cons 'renderer-key #f)))
 
         (cons 'description   (list (cons 'sortkey (list SPLIT-TRANS TRANS-DESCRIPTION))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Description"))
                                    (cons 'tip (N_ "Sort by description."))
-                                   (cons 'renderer #f)))
+                                   (cons 'renderer-key #f)))
 
         (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
             (cons 'number    (list (cons 'sortkey (list SPLIT-ACTION))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Number/Action"))
                                    (cons 'tip (N_ "Sort by check number/action."))
-                                   (cons 'renderer #f)))
+                                   (cons 'renderer-key #f)))
 
             (cons 'number    (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Number"))
                                    (cons 'tip (N_ "Sort by check/transaction number."))
-                                   (cons 'renderer #f))))
+                                   (cons 'renderer-key #f))))
 
         (cons 't-number      (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Transaction Number"))
                                    (cons 'tip (N_ "Sort by transaction number."))
-                                   (cons 'renderer #f)))
+                                   (cons 'renderer-key #f)))
 
         (cons 'memo          (list (cons 'sortkey (list SPLIT-MEMO))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Memo"))
                                    (cons 'tip (N_ "Sort by memo."))
-                                   (cons 'renderer #f)))
+                                   (cons 'renderer-key #f)))
 
         (cons 'none          (list (cons 'sortkey '())
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "None"))
                                    (cons 'tip (N_ "Do not sort."))
-                                   (cons 'renderer #f)))))
+                                   (cons 'renderer-key #f)))))
 
 
 (define (sortkey-get-info sortkey info)
@@ -221,40 +220,42 @@ options specified in the Options panels."))
 (define (split-year a) (timepair-year (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
 
 (define date-subtotal-list
-  ;; Extra list for date option. Each entry: (cons
-  ;; 'date-subtotal-option-value (vector subtotal-function
-  ;; subtotal-renderer))
+  ;; List for date option.
+  ;; Defines the different date sorting keys, as an association-list. Each entry:
+  ;;  'split-sortvalue     - function which retrieves number/string used for comparing splits
+  ;;  'text                - text displayed in Display tab
+  ;;  'tip                 - tooltip displayed in Display tab
+  ;;  'renderer-key        - helper symbol to select subtotal/subheading renderer
   (list
    (cons 'none (list
                 (cons 'split-sortvalue #f)
                 (cons 'text (N_ "None"))
                 (cons 'tip (N_ "None."))
-                (cons 'subheading-renderer #f)
-                (cons 'subtotal-renderer #f)))
+                (cons 'renderer-key #f)))
+
    (cons 'weekly (list
                   (cons 'split-sortvalue split-week)
                   (cons 'text (N_ "Weekly"))
                   (cons 'tip (N_ "Weekly."))
-                  (cons 'subheading-renderer render-week-subheading)
-                  (cons 'subtotal-renderer render-week-subtotal)))
+                  (cons 'renderer-key 'week)))
+
    (cons 'monthly (list
                    (cons 'split-sortvalue split-month)
                    (cons 'text (N_ "Monthly"))
                    (cons 'tip (N_ "Monthly."))
-                   (cons 'subheading-renderer render-month-subheading)
-                   (cons 'subtotal-renderer render-month-subtotal)))
+                   (cons 'renderer-key 'month)))
+
    (cons 'quarterly (list
                      (cons 'split-sortvalue split-quarter)
                      (cons 'text (N_ "Quarterly"))
                      (cons 'tip (N_ "Quarterly."))
-                     (cons 'subheading-renderer render-quarter-subheading)
-                     (cons 'subtotal-renderer render-quarter-subtotal)))
+                     (cons 'renderer-key 'quarter)))
+
    (cons 'yearly (list
                   (cons 'split-sortvalue split-year)
                   (cons 'text (N_ "Yearly"))
                   (cons 'tip (N_ "Yearly."))
-                  (cons 'subheading-renderer render-year-subheading)
-                  (cons 'subtotal-renderer render-year-subtotal)))))
+                  (cons 'renderer-key 'year)))))
 
 (define (date-subtotal-get-info sortkey info)
   (cdr (assq info (cdr (assq sortkey date-subtotal-list)))))
@@ -702,12 +703,10 @@ Credit Card, and Income accounts."))))))
 ;; Here comes the big function that builds the whole table.
 
 (define (make-split-table splits options
-                          primary-subtotal-pred
-                          secondary-subtotal-pred
-                          primary-subheading-renderer
-                          secondary-subheading-renderer
-                          primary-subtotal-renderer
-                          secondary-subtotal-renderer)
+                          primary-subtotal-comparator
+                          secondary-subtotal-comparator
+                          primary-renderer-key
+                          secondary-renderer-key)
 
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
   (define BOOK-SPLIT-ACTION (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
@@ -852,81 +851,16 @@ Credit Card, and Income accounts."))))))
                    (xaccAccountGetName account))
                ""))))
 
-    ;; render an account subheading - used-columns determines what is displayed
-    (define (render-account-subheading split)
-      (let ((account (xaccSplitGetAccount split)))
-        (gnc:make-html-text
-         (gnc:html-markup-anchor
-          (gnc:account-anchor-text account)
-          (account-namestring account
-                              (column-uses? 'sort-account-code      used-columns)
-                              #t
-                              (column-uses? 'sort-account-full-name used-columns))))))                        
-
-    (define (render-corresponding-account-subheading split)
-      (let ((account (xaccSplitGetAccount (xaccSplitGetOtherSplit split))))
-        (gnc:make-html-text
-         (gnc:html-markup-anchor
-          (if (null? account)
-              ""
-              (gnc:account-anchor-text account))
-          (account-namestring account
-                              (column-uses? 'sort-account-code      used-columns)
-                              #t
-                              (column-uses? 'sort-account-full-name used-columns))))))
-
-    (define (render-week-subheading split)
-      (gnc:date-get-week-year-string
-       (gnc:timepair->date
-        (gnc-transaction-get-date-posted
-         (xaccSplitGetParent split)))))
-
-    (define (render-month-subheading split)
-      (gnc:date-get-month-year-string
-       (gnc:timepair->date
-        (gnc-transaction-get-date-posted
-         (xaccSplitGetParent split)))))
-
-    (define (render-quarter-subheading split)
-      (gnc:date-get-quarter-year-string
+    (define (render-date renderer-key split)
+      ((case renderer-key
+         ((week) gnc:date-get-week-year-string)
+         ((month) gnc:date-get-month-year-string)
+         ((quarter) gnc:date-get-quarter-year-string)
+         ((year) gnc:date-get-year-string))
        (gnc:timepair->date
         (gnc-transaction-get-date-posted
          (xaccSplitGetParent split)))))
 
-
-    (define (render-year-subheading split)
-      (gnc:date-get-year-string
-       (gnc:timepair->date
-        (gnc-transaction-get-date-posted
-         (xaccSplitGetParent split)))))
-
-    (define (render-account-subtotal split)
-      (account-namestring (xaccSplitGetAccount split)
-                          (column-uses? 'sort-account-code      used-columns)
-                          #t
-                          (column-uses? 'sort-account-full-name used-columns)))
-
-    (define (render-corresponding-account-subtotal split)
-      (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
-                          (column-uses? 'sort-account-code      used-columns)
-                          #t
-                          (column-uses? 'sort-account-full-name used-columns)))
-
-    (define (render-week-subtotal split)
-      (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
-        (gnc:date-get-week-year-string tm)))
-
-    (define (render-month-subtotal split)
-      (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
-        (gnc:date-get-month-year-string tm)))
-
-    (define (render-quarter-subtotal split)
-      (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
-        (gnc:date-get-quarter-year-string tm)))
-
-    (define (render-year-subtotal split)
-      (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
-        (strftime "%Y" tm)))
     (define (render-account renderer-key split anchor?)
       (let* ((account (case renderer-key
                         ((account) (xaccSplitGetAccount split))
@@ -954,29 +888,7 @@ Credit Card, and Income accounts."))))))
     (define (render-grand-total)
       (_ "Grand Total"))
 
-    (define (subheading-renderer split key)
-      ((case key
-         ((week)      render-week-subheading)
-         ((month)     render-month-subheading)
-         ((quarter)   render-quarter-subheading)
-         ((year)      render-year-subheading)
-         ((account)   render-account-subheading)
-         ((other-acc) render-corresponding-account-subheading))
-       split))
-
-    (define (subtotal-renderer split key)
-      ((case key
-         ((week)      render-week-subtotal)
-         ((month)     render-month-subtotal)
-         ((quarter)   render-quarter-subtotal)
-         ((year)      render-year-subtotal)
-         ((account)   render-account-subtotal)
-         ((other-acc) render-corresponding-account-subtotal))
-       split))
-
-
     (define (add-split-row split row-style transaction-row?)
-
       (let* ((row-contents '())
              (parent (xaccSplitGetParent split))
              (account (xaccSplitGetAccount split))
@@ -1199,28 +1111,25 @@ Credit Card, and Income accounts."))))))
                   (if secondary-subtotal-pred
                       
                       (begin
-                        
-                        (add-subtotal-row (secondary-subtotal-renderer current used-columns)
+                        (add-subtotal-row (total-string
+                                           (render-summary current secondary-renderer-key #f))
                                           secondary-subtotal-collector
                                           def:secondary-subtotal-style)
                         
                         (secondary-subtotal-collector 'reset #f #f)))
-                  
-                  (add-subtotal-row (primary-subtotal-renderer current used-columns)
+                  (add-subtotal-row (total-string
+                                     (render-summary current primary-renderer-key #f))
                                     primary-subtotal-collector
                                     def:primary-subtotal-style)
                   
                   (primary-subtotal-collector 'reset #f #f)
                   
                   (if next
-
                       (begin
-
-                        (add-subheading (primary-subheading-renderer next used-columns)
+                        (add-subheading (render-summary next primary-renderer-key #t)
                                         def:primary-subtotal-style)
-
-                        (if secondary-subtotal-pred
-                            (add-subheading (secondary-subheading-renderer next used-columns)
+                        (if secondary-subtotal-comparator
+                            (add-subheading (render-summary next secondary-renderer-key #t)
                                             def:secondary-subtotal-style)))))
 
                 (if (and secondary-subtotal-pred
@@ -1228,15 +1137,15 @@ Credit Card, and Income accounts."))))))
                              (and next
                                   (not (equal? (secondary-subtotal-pred current)
                                                (secondary-subtotal-pred next))))))
-                    
-                    (begin (add-subtotal-row (secondary-subtotal-renderer current used-columns)
+                    (begin (add-subtotal-row (total-string
+                                              (render-summary current secondary-renderer-key #f))
                                              secondary-subtotal-collector
                                              def:secondary-subtotal-style)
                            
                            (secondary-subtotal-collector 'reset #f #f)
                            
                            (if next
-                               (add-subheading (secondary-subheading-renderer next used-columns)
+                               (add-subheading (render-summary next secondary-renderer-key #t)
                                                def:secondary-subtotal-style)))))
 
             (do-rows-with-subtotals rest
@@ -1245,38 +1154,21 @@ Credit Card, and Income accounts."))))))
                                     width
                                     multi-rows?
                                     (not odd-row?)
-                                    export?
-                                    account-types-to-reverse
-                                    primary-subtotal-pred
-                                    secondary-subtotal-pred
-                                    primary-subheading-renderer
-                                    secondary-subheading-renderer
-                                    primary-subtotal-renderer
-                                    secondary-subtotal-renderer
                                     primary-subtotal-collector
                                     secondary-subtotal-collector
                                     total-collector))))
 
     (gnc:html-table-set-col-headers! table headings)
 
-    (if primary-subheading-renderer
-        (add-subheading (primary-subheading-renderer (car splits) used-columns)
+    (if primary-renderer-key
+        (add-subheading (render-summary (car splits) primary-renderer-key #t)
                         def:primary-subtotal-style))
 
-    (if secondary-subheading-renderer
-        (add-subheading (secondary-subheading-renderer (car splits) used-columns)
+    (if secondary-renderer-key
+        (add-subheading (render-summary (car splits) secondary-renderer-key #t)
                         def:secondary-subtotal-style))
 
-    (do-rows-with-subtotals splits table used-columns width
-                            is-multiline? #t
-                            export?
-                            account-types-to-reverse
-                            primary-subtotal-pred
-                            secondary-subtotal-pred
-                            primary-subheading-renderer
-                            secondary-subheading-renderer
-                            primary-subtotal-renderer
-                            secondary-subtotal-renderer
+    (do-rows-with-subtotals splits #t
                             (gnc:make-commodity-collector)
                             (gnc:make-commodity-collector)
                             (gnc:make-commodity-collector))
@@ -1445,20 +1337,12 @@ Credit Card, and Income accounts."))))))
                             (subtotal-get-info optname-prime-sortkey
                                                optname-prime-subtotal
                                                optname-prime-date-subtotal
-                                               'subheading-renderer)
+                                               'renderer-key)
                             (subtotal-get-info optname-sec-sortkey
                                                optname-sec-subtotal
                                                optname-sec-date-subtotal
-                                               'subheading-renderer)
-                            (subtotal-get-info   optname-prime-sortkey
-                                                 optname-prime-subtotal
-                                                 optname-prime-date-subtotal
-                                                 'subtotal-renderer)
-                            (subtotal-get-info   optname-sec-sortkey
-                                                 optname-sec-subtotal
-                                                 optname-sec-date-subtotal
-                                                 'subtotal-renderer))))
-                
+                                               'renderer-key))))
+
                 (gnc:html-document-set-title! document report-title)
 
                 (gnc:html-document-add-object!

commit afc6ca078c0592ee1c9a61c1dab0b22bd916f2fb
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 16:57:07 2017 +0800

    ENH: Show account description in subheadings

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 0330ac8..e6ef525 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -66,6 +66,7 @@
 (define optname-prime-date-subtotal (N_ "Primary Subtotal for Date Key"))
 (define optname-full-account-name (N_ "Show Full Account Name"))
 (define optname-show-account-code (N_ "Show Account Code"))
+(define optname-show-account-description (N_ "Show Account Description"))
 (define optname-sec-sortkey (N_ "Secondary Key"))
 (define optname-sec-subtotal (N_ "Secondary Subtotal"))
 (define optname-sec-sortorder  (N_ "Secondary Sort Order"))
@@ -456,6 +457,11 @@ tags within description, notes or memo. ")
              (and sec-sortkey-subtotal-enabled sec-sortkey-subtotal-true)))
 
         (gnc-option-db-set-option-selectable-by-name
+         options pagename-sorting optname-show-account-description
+         (or (and prime-sortkey-subtotal-enabled prime-sortkey-subtotal-true)
+             (and sec-sortkey-subtotal-enabled sec-sortkey-subtotal-true)))
+
+        (gnc-option-db-set-option-selectable-by-name
          options pagename-sorting optname-prime-date-subtotal
          prime-date-sortingtype-enabled)
 
@@ -489,6 +495,13 @@ tags within description, notes or memo. ")
       #f))
 
     (gnc:register-trep-option
+     (gnc:make-simple-boolean-option
+      pagename-sorting optname-show-account-description
+      "j3"
+      (N_ "Show the account description for subheadings?")
+      #f))
+    
+    (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       pagename-sorting optname-prime-subtotal
       "e5"
@@ -725,6 +738,7 @@ Credit Card, and Income accounts."))))))
                                               (opt-val gnc:pagename-display (N_ "Use Full Other Account Name"))))
           (cons 'sort-account-code (opt-val pagename-sorting (N_ "Show Account Code")))
           (cons 'sort-account-full-name (opt-val pagename-sorting (N_ "Show Full Account Name")))
+          (cons 'sort-account-description (opt-val pagename-sorting (N_ "Show Account Description")))
           (cons 'notes (opt-val gnc:pagename-display (N_ "Notes")))))
 
   (define (column-uses? param columns-used)
@@ -913,6 +927,29 @@ Credit Card, and Income accounts."))))))
     (define (render-year-subtotal split)
       (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
         (strftime "%Y" tm)))
+    (define (render-account renderer-key split anchor?)
+      (let* ((account (case renderer-key
+                        ((account) (xaccSplitGetAccount split))
+                        ((other-acc) (xaccSplitGetAccount (xaccSplitGetOtherSplit split)))))
+             (name (account-namestring account
+                                       (column-uses? 'sort-account-code      used-columns)
+                                       #t
+                                       (column-uses? 'sort-account-full-name used-columns)))
+             (description (if (and (column-uses? 'sort-account-description used-columns)
+                                   (not (string-null? (xaccAccountGetDescription account))))
+                              (string-append ": " (xaccAccountGetDescription account))
+                              "")))
+        (if (and anchor? (not (null? account))) ;html anchor for 2-split transactions only
+            (gnc:make-html-text
+             (gnc:html-markup-anchor (gnc:account-anchor-text account) name)
+             description)
+            name)))
+
+    (define (render-summary split renderer-key anchor?)
+      (case renderer-key
+        ((week month quarter year) (render-date renderer-key split))
+        ((account other-acc) (render-account renderer-key split anchor?))
+        (else #f)))
 
     (define (render-grand-total)
       (_ "Grand Total"))

commit c4089ebcc3dc7497acfcb15abbb41909e0b9de1c
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 10:42:45 2017 +0800

    REFACTOR: move add-split-row into make-split-table

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index e096ed9..0330ac8 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -259,145 +259,6 @@ options specified in the Options panels."))
   (cdr (assq info (cdr (assq sortkey date-subtotal-list)))))
 
 
-(define (add-split-row table split column-vector options
-                       row-style account-types-to-reverse transaction-row?)
-
-  (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
-
-  (let* ((row-contents '())
-         (parent (xaccSplitGetParent split))
-         (account (xaccSplitGetAccount split))
-         (account-type (xaccAccountGetType account))
-         (currency (if (null? account)
-                       (gnc-default-currency)
-                       (xaccAccountGetCommodity account)))
-         (report-currency (if (opt-val gnc:pagename-general optname-common-currency)
-                              (opt-val gnc:pagename-general optname-currency)
-                              currency))
-         (damount (if (gnc:split-voided? split)
-                      (xaccSplitVoidFormerAmount split)
-                      (xaccSplitGetAmount split)))
-         (trans-date (gnc-transaction-get-date-posted parent))
-         (split-value (gnc:exchange-by-pricedb-nearest
-                       (gnc:make-gnc-monetary
-                        currency
-                        (if (member account-type account-types-to-reverse)
-                            (gnc-numeric-neg damount)
-                            damount))
-                       report-currency
-                       ;; Use midday as the transaction time so it matches a price
-                       ;; on the same day.  Otherwise it uses midnight which will
-                       ;; likely match a price on the previous day
-                       (timespecCanonicalDayTime trans-date))))
-
-    (if (column-uses? 'date column-vector)
-        (addto! row-contents
-                (if transaction-row?
-                    (gnc:make-html-table-cell/markup
-                     "date-cell"
-                     (gnc-print-date trans-date))
-                    "")))
-    
-    (if (column-uses? 'reconciled-date column-vector)
-        (addto! row-contents
-                (gnc:make-html-table-cell/markup
-                 "date-cell"
-                 (let ((date (gnc-split-get-date-reconciled split)))
-                   (if (equal? date (cons 0 0))
-                       ""
-                       (gnc-print-date date))))))
-
-    (if (column-uses? 'num column-vector)
-        (addto! row-contents
-                (if transaction-row?
-                    (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-                        (let* ((num (gnc-get-num-action parent split))
-                               (t-num (if (if (gnc:lookup-option options gnc:pagename-display
-                                                                 (N_ "Trans Number"))
-                                              (opt-val gnc:pagename-display (N_ "Trans Number"))
-                                              "")
-                                          (gnc-get-num-action parent #f)
-                                          ""))
-                               (num-string (if (string-null? t-num)
-                                               num
-                                               (string-append num "/" t-num))))
-                          (gnc:make-html-table-cell/markup "text-cell" num-string))
-                        (gnc:make-html-table-cell/markup "text-cell"
-                                                         (gnc-get-num-action parent split)))
-                    "")))
-
-    (if (column-uses? 'description column-vector)
-        (addto! row-contents
-                (if transaction-row?
-                    (gnc:make-html-table-cell/markup
-                     "text-cell"
-                     (xaccTransGetDescription parent))
-                    "")))
-
-    (if (column-uses? 'memo column-vector)
-        (let ((memo (xaccSplitGetMemo split)))
-          (if (and (string-null? memo) (column-uses? 'notes column-vector))
-              (addto! row-contents (xaccTransGetNotes parent))
-              (addto! row-contents memo))))
-
-    (if (or (column-uses? 'account-name column-vector) (column-uses? 'account-code column-vector))
-        (addto! row-contents (account-namestring account
-                                                 (column-uses? 'account-code      column-vector)
-                                                 (column-uses? 'account-name      column-vector)
-                                                 (column-uses? 'account-full-name column-vector))))
-
-    (if (or (column-uses? 'other-account-name column-vector) (column-uses? 'other-account-code column-vector))
-        (addto! row-contents (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
-                                                 (column-uses? 'other-account-code      column-vector)
-                                                 (column-uses? 'other-account-name      column-vector)
-                                                 (column-uses? 'other-account-full-name column-vector))))
-
-    (if (column-uses? 'shares column-vector)
-        (addto! row-contents (xaccSplitGetAmount split)))
-
-    (if (column-uses? 'price column-vector)
-        (addto! row-contents  (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
-                                                     (xaccSplitGetSharePrice split))))
-    
-    (if (column-uses? 'amount-single column-vector)
-        (addto! row-contents
-                (gnc:make-html-table-cell/markup
-                 "number-cell" (gnc:html-transaction-anchor parent split-value))))
-    
-    (if (column-uses? 'amount-double column-vector)        
-
-        (if (gnc-numeric-positive-p (gnc:gnc-monetary-amount split-value))
-
-            (begin
-              (addto! row-contents
-                      (gnc:make-html-table-cell/markup
-                       "number-cell" (gnc:html-transaction-anchor
-                                      parent split-value)))
-              (addto! row-contents ""))
-        
-            (begin
-              (addto! row-contents "")
-              (addto! row-contents
-                      (gnc:make-html-table-cell/markup
-                       "number-cell" (gnc:html-transaction-anchor
-                                      parent (gnc:monetary-neg split-value)))))))
-    
-    (if (column-uses? 'running-balance column-vector)
-        (begin
-          ;(gnc:debug "split is " split)
-          ;(gnc:debug "split get balance:" (xaccSplitGetBalance split))
-          (addto! row-contents
-                  (gnc:make-html-table-cell/markup
-                   "number-cell"
-                   (gnc:make-gnc-monetary currency
-                                          (xaccSplitGetBalance split))))))
-    
-    (gnc:html-table-append-row/markup! table row-style (reverse row-contents))
-
-    split-value))
-
-
-
 (define (trep-options-generator)
 
   (define options (gnc:new-options))
@@ -1076,6 +937,144 @@ Credit Card, and Income accounts."))))))
          ((other-acc) render-corresponding-account-subtotal))
        split))
 
+
+    (define (add-split-row split row-style transaction-row?)
+
+      (let* ((row-contents '())
+             (parent (xaccSplitGetParent split))
+             (account (xaccSplitGetAccount split))
+             (account-type (xaccAccountGetType account))
+             (currency (if (null? account)
+                           (gnc-default-currency)
+                           (xaccAccountGetCommodity account)))
+             (report-currency (if (opt-val gnc:pagename-general optname-common-currency)
+                                  (opt-val gnc:pagename-general optname-currency)
+                                  currency))
+             (damount (if (gnc:split-voided? split)
+                          (xaccSplitVoidFormerAmount split)
+                          (xaccSplitGetAmount split)))
+             (trans-date (gnc-transaction-get-date-posted parent))
+             (split-value (gnc:exchange-by-pricedb-nearest
+                           (gnc:make-gnc-monetary
+                            currency
+                            (if (member account-type account-types-to-reverse)
+                                (gnc-numeric-neg damount)
+                                damount))
+                           report-currency
+                           ;; Use midday as the transaction time so it matches a price
+                           ;; on the same day.  Otherwise it uses midnight which will
+                           ;; likely match a price on the previous day
+                           (timespecCanonicalDayTime trans-date))))
+
+        (if (column-uses? 'date used-columns)
+            (addto! row-contents
+                    (if transaction-row?
+                        (gnc:make-html-table-cell/markup
+                         "date-cell"
+                         (gnc-print-date trans-date))
+                        "")))
+
+        (if (column-uses? 'reconciled-date used-columns)
+            (addto! row-contents
+                    (gnc:make-html-table-cell/markup
+                     "date-cell"
+                     (let ((date (gnc-split-get-date-reconciled split)))
+                       (if (equal? date (cons 0 0))
+                           ""
+                           (gnc-print-date date))))))
+
+        (if (column-uses? 'num used-columns)
+            (addto! row-contents
+                    (if transaction-row?
+                        (if BOOK-SPLIT-ACTION
+                            (let* ((num (gnc-get-num-action parent split))
+                                   (t-num (if (if (gnc:lookup-option options gnc:pagename-display
+                                                                     (N_ "Trans Number"))
+                                                  (opt-val gnc:pagename-display (N_ "Trans Number"))
+                                                  "")
+                                              (gnc-get-num-action parent #f)
+                                              ""))
+                                   (num-string (if (string-null? t-num)
+                                                   num
+                                                   (string-append num "/" t-num))))
+                              (gnc:make-html-table-cell/markup "text-cell" num-string))
+                            (gnc:make-html-table-cell/markup "text-cell"
+                                                             (gnc-get-num-action parent split)))
+                        "")))
+
+        (if (column-uses? 'description used-columns)
+            (addto! row-contents
+                    (if transaction-row?
+                        (gnc:make-html-table-cell/markup
+                         "text-cell"
+                         (xaccTransGetDescription parent))
+                        "")))
+
+        (if (column-uses? 'memo used-columns)
+            (let ((memo (xaccSplitGetMemo split)))
+              (if (and (string-null? memo) (column-uses? 'notes used-columns))
+                  (addto! row-contents (xaccTransGetNotes parent))
+                  (addto! row-contents memo))))
+
+        (if (or (column-uses? 'account-name used-columns) (column-uses? 'account-code used-columns))
+            (addto! row-contents (account-namestring account
+                                                     (column-uses? 'account-code      used-columns)
+                                                     (column-uses? 'account-name      used-columns)
+                                                     (column-uses? 'account-full-name used-columns))))
+
+        (if (or (column-uses? 'other-account-name used-columns) (column-uses? 'other-account-code used-columns))
+            (addto! row-contents (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
+                                                     (column-uses? 'other-account-code      used-columns)
+                                                     (column-uses? 'other-account-name      used-columns)
+                                                     (column-uses? 'other-account-full-name used-columns))))
+
+        (if (column-uses? 'shares used-columns)
+            (addto! row-contents (xaccSplitGetAmount split)))
+
+        (if (column-uses? 'price used-columns)
+            (addto! row-contents  (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
+                                                         (xaccSplitGetSharePrice split))))
+
+        (if (column-uses? 'amount-single used-columns)
+            (addto! row-contents
+                    (gnc:make-html-table-cell/markup
+                     "number-cell" (gnc:html-transaction-anchor parent split-value))))
+
+        (if (column-uses? 'amount-double used-columns)
+
+            (if (gnc-numeric-positive-p (gnc:gnc-monetary-amount split-value))
+
+                (begin
+                  (addto! row-contents
+                          (gnc:make-html-table-cell/markup
+                           "number-cell" (gnc:html-transaction-anchor
+                                          parent split-value)))
+                  (addto! row-contents ""))
+
+                (begin
+                  (addto! row-contents "")
+                  (addto! row-contents
+                          (gnc:make-html-table-cell/markup
+                           "number-cell" (gnc:html-transaction-anchor
+                                          parent (gnc:monetary-neg split-value)))))))
+
+        (if (column-uses? 'running-balance used-columns)
+            (begin
+              ;(gnc:debug "split is " split)
+              ;(gnc:debug "split get balance:" (xaccSplitGetBalance split))
+              (addto! row-contents
+                      (gnc:make-html-table-cell/markup
+                       "number-cell"
+                       (gnc:make-gnc-monetary currency
+                                              (xaccSplitGetBalance split))))))
+
+        (gnc:html-table-append-row/markup! table row-style (reverse row-contents))
+
+        split-value))
+
+
+
+
     (define (do-rows-with-subtotals splits
                                     table
                                     used-columns

commit dd22216845fc62a7f3affbc58161f318fee26bc9
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 10:38:23 2017 +0800

    REFACTOR: move *-choice-list into options-generator

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 65fe6c1..e096ed9 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -110,235 +110,106 @@ options specified in the Options panels."))
                                'account-code 'corresponding-acc-code))
 
 
-(define (column-uses? param columns-used)
-  (cdr (assq param columns-used)))
-
-            
-;; display an account name depending on the options the user has set
-(define (account-namestring account show-account-code? show-account-name? show-account-full-name?)
-  ;;# on multi-line splits we can get an empty ('()) account
-  (if (null? account)
-      (_ "Split Transaction")
-      (string-append
-       ;; display account code?
-       (if show-account-code?
-           (string-append (xaccAccountGetCode account) " ")
-           "")
-       ;; display account name?
-       (if show-account-name?
-           ;; display full account name?
-           (if show-account-full-name?
-               (gnc-account-get-full-name account)
-               (xaccAccountGetName account))
-           ""))))
-
-;; render an account subheading - column-vector determines what is displayed
-(define (render-account-subheading split column-vector)
-  (let ((account (xaccSplitGetAccount split)))
-    (gnc:make-html-text
-     (gnc:html-markup-anchor
-      (gnc:account-anchor-text account)
-      (account-namestring account
-                          (column-uses? 'sort-account-code      column-vector)
-                          #t
-                          (column-uses? 'sort-account-full-name column-vector))))))                        
-
-(define (render-corresponding-account-subheading split column-vector)
-  (let ((account (xaccSplitGetAccount (xaccSplitGetOtherSplit split))))
-    (gnc:make-html-text
-     (gnc:html-markup-anchor
-      (if (null? account)
-          ""
-          (gnc:account-anchor-text account))
-      (account-namestring account
-                          (column-uses? 'sort-account-code      column-vector)
-                          #t
-                          (column-uses? 'sort-account-full-name column-vector))))))
-
-(define (render-week-subheading split column-vector)
-  (gnc:date-get-week-year-string
-   (gnc:timepair->date
-    (gnc-transaction-get-date-posted
-     (xaccSplitGetParent split)))))
-
-(define (render-month-subheading split column-vector)
-  (gnc:date-get-month-year-string
-   (gnc:timepair->date
-    (gnc-transaction-get-date-posted
-     (xaccSplitGetParent split)))))
-
-(define (render-quarter-subheading split column-vector)
-  (gnc:date-get-quarter-year-string
-   (gnc:timepair->date
-    (gnc-transaction-get-date-posted
-     (xaccSplitGetParent split)))))
-
-
-(define (render-year-subheading split column-vector)
-  (gnc:date-get-year-string
-   (gnc:timepair->date
-    (gnc-transaction-get-date-posted
-     (xaccSplitGetParent split)))))
-
-(define (total-string str) (string-append (_ "Total For ") str))
-
-(define (render-account-subtotal split column-vector)
-  (total-string (account-namestring (xaccSplitGetAccount split)
-                                    (column-uses? 'sort-account-code      column-vector)
-                                    #t
-                                    (column-uses? 'sort-account-full-name column-vector))))
-
-(define (render-corresponding-account-subtotal split column-vector)
-  (total-string (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
-                                    (column-uses? 'sort-account-code      column-vector)
-                                    #t
-                                    (column-uses? 'sort-account-full-name column-vector))))
-
-(define (render-week-subtotal split column-vector)
-  (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
-                                 (xaccSplitGetParent split)))))
-    (total-string (gnc:date-get-week-year-string tm))))
-
-(define (render-month-subtotal split column-vector)
-  (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
-                                 (xaccSplitGetParent split)))))
-    (total-string (gnc:date-get-month-year-string tm))))
-
-(define (render-quarter-subtotal split column-vector)
-  (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
-                                 (xaccSplitGetParent split)))))
-    (total-string (gnc:date-get-quarter-year-string tm))))
-
-(define (render-year-subtotal split column-vector)
-  (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
-                                 (xaccSplitGetParent split)))))
-    (total-string (strftime "%Y" tm))))
-
-(define (render-grand-total)
-  (_ "Grand Total")) ; def:grand-total-style
-
-
-
 
 (define sortkey-list
+  ;;
   ;; Defines the different sorting keys, as an association-list 
   ;; together with the subtotal functions. Each entry: 
   ;;  'sortkey             - sort parameter sent via qof-query
   ;;  'split-sortvalue     - function which retrieves number/string used for comparing splits
   ;;  'text                - text displayed in Display tab
   ;;  'tip                 - tooltip displayed in Display tab
-  ;;  'subheading-renderer - function which renders the subheading
-  ;;  'subtotal-renderer   - function which renders the subtotal
+  ;;  'renderer            - helper symbol to select subtotal/subheading renderer
+  ;;
   (list (cons 'account-name  (list (cons 'sortkey (list SPLIT-ACCT-FULLNAME))
                                    (cons 'split-sortvalue (lambda (a) (gnc-account-get-full-name (xaccSplitGetAccount a))))
                                    (cons 'text (N_ "Account Name"))
                                    (cons 'tip (N_ "Sort & subtotal by account name."))
-                                   (cons 'subheading-renderer render-account-subheading)
-                                   (cons 'subtotal-renderer render-account-subtotal)))
+                                   (cons 'renderer 'account)))
         
         (cons 'account-code (list (cons 'sortkey (list SPLIT-ACCOUNT ACCOUNT-CODE-))
                                   (cons 'split-sortvalue (lambda (a) (xaccAccountGetCode (xaccSplitGetAccount a))))
                                   (cons 'text (N_ "Account Code"))
                                   (cons 'tip (N_ "Sort & subtotal by account code."))
-                                  (cons 'subheading-renderer render-account-subheading)
-                                  (cons 'subtotal-renderer render-account-subtotal)))
+                                  (cons 'renderer 'account)))
 
         (cons 'date         (list (cons 'sortkey (list SPLIT-TRANS TRANS-DATE-POSTED))
                                   (cons 'split-sortvalue #f)
                                   (cons 'text (N_ "Date"))
                                   (cons 'tip (N_ "Sort by date."))
-                                  (cons 'subheading-renderer #f)
-                                  (cons 'subtotal-renderer #f)))
+                                  (cons 'renderer #f)))
 
         (cons 'reconciled-date (list (cons 'sortkey (list SPLIT-DATE-RECONCILED))
                                      (cons 'split-sortvalue #f)
                                      (cons 'text (N_ "Reconciled Date"))
                                      (cons 'tip (N_ "Sort by the Reconciled Date."))
-                                     (cons 'subheading-renderer #f)
-                                     (cons 'subtotal-renderer #f)))
+                                     (cons 'renderer #f)))
 
         (cons 'register-order (list (cons 'sortkey (list QUERY-DEFAULT-SORT))
                                     (cons 'split-sortvalue #f)
                                     (cons 'text (N_ "Register Order"))
                                     (cons 'tip (N_ "Sort as in the register."))
-                                    (cons 'subheading-renderer #f)
-                                    (cons 'subtotal-renderer #f)))                                 
+                                    (cons 'renderer #f)))                                 
 
         (cons 'corresponding-acc-name (list (cons 'sortkey (list SPLIT-CORR-ACCT-NAME))
                                             (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountFullName a)))
                                             (cons 'text (N_ "Other Account Name"))
                                             (cons 'tip (N_ "Sort by account transferred from/to's name."))
-                                            (cons 'subheading-renderer render-corresponding-account-subheading)
-                                            (cons 'subtotal-renderer render-corresponding-account-subtotal)))
+                                            (cons 'renderer 'other-acc)))
 
         (cons 'corresponding-acc-code (list (cons 'sortkey (list SPLIT-CORR-ACCT-CODE))
                                             (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountCode a)))
                                             (cons 'text (N_ "Other Account Code"))
                                             (cons 'tip (N_ "Sort by account transferred from/to's code."))
-                                            (cons 'subheading-renderer render-corresponding-account-subheading)
-                                            (cons 'subtotal-renderer render-corresponding-account-subtotal)))
+                                            (cons 'renderer 'other-acct)))
 
         (cons 'amount        (list (cons 'sortkey (list SPLIT-VALUE))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Amount"))
                                    (cons 'tip (N_ "Sort by amount."))
-                                   (cons 'subheading-renderer #f)
-                                   (cons 'subtotal-renderer #f)))
+                                   (cons 'renderer #f)))
 
         (cons 'description   (list (cons 'sortkey (list SPLIT-TRANS TRANS-DESCRIPTION))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Description"))
                                    (cons 'tip (N_ "Sort by description."))
-                                   (cons 'subheading-renderer #f)
-                                   (cons 'subtotal-renderer #f)))
+                                   (cons 'renderer #f)))
 
         (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
             (cons 'number    (list (cons 'sortkey (list SPLIT-ACTION))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Number/Action"))
                                    (cons 'tip (N_ "Sort by check number/action."))
-                                   (cons 'subheading-renderer #f)
-                                   (cons 'subtotal-renderer #f)))
+                                   (cons 'renderer #f)))
 
             (cons 'number    (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Number"))
                                    (cons 'tip (N_ "Sort by check/transaction number."))
-                                   (cons 'subheading-renderer #f)
-                                   (cons 'subtotal-renderer #f))))
+                                   (cons 'renderer #f))))
 
         (cons 't-number      (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Transaction Number"))
                                    (cons 'tip (N_ "Sort by transaction number."))
-                                   (cons 'subheading-renderer #f)
-                                   (cons 'subtotal-renderer #f)))
+                                   (cons 'renderer #f)))
 
         (cons 'memo          (list (cons 'sortkey (list SPLIT-MEMO))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Memo"))
                                    (cons 'tip (N_ "Sort by memo."))
-                                   (cons 'subheading-renderer #f)
-                                   (cons 'subtotal-renderer #f)))
+                                   (cons 'renderer #f)))
 
         (cons 'none          (list (cons 'sortkey '())
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "None"))
                                    (cons 'tip (N_ "Do not sort."))
-                                   (cons 'subheading-renderer #f)
-                                   (cons 'subtotal-renderer #f)))))
+                                   (cons 'renderer #f)))))
 
 
 (define (sortkey-get-info sortkey info)
   (cdr (assq info (cdr (assq sortkey sortkey-list)))))
 
-(define key-choice-list
-  (map (lambda (sortpair)
-         (vector (car sortpair)
-                 (sortkey-get-info (car sortpair) 'text)
-                 (sortkey-get-info (car sortpair) 'tip)))
-       sortkey-list))
-
 (define (timepair-year tp)    (gnc:timepair-get-year tp))
 (define (timepair-quarter tp) (+ (* 10 (timepair-year tp))  (gnc:timepair-get-quarter tp)))
 (define (timepair-month tp)   (+ (* 100 (timepair-year tp)) (gnc:timepair-get-month tp)))
@@ -387,12 +258,6 @@ options specified in the Options panels."))
 (define (date-subtotal-get-info sortkey info)
   (cdr (assq info (cdr (assq sortkey date-subtotal-list)))))
 
-(define date-subtotal-choice-list
-  (map (lambda (date-sortpair)
-         (vector (car date-sortpair)
-                 (date-subtotal-get-info (car date-sortpair) 'text)
-                 (date-subtotal-get-info (car date-sortpair) 'tip)))
-       date-subtotal-list))
 
 (define (add-split-row table split column-vector options
                        row-style account-types-to-reverse transaction-row?)
@@ -679,7 +544,21 @@ tags within description, notes or memo. ")
         (prime-sortkey 'account-name)
         (prime-sortkey-subtotal-true #t)
         (sec-sortkey 'register-order)
-        (sec-sortkey-subtotal-true #f))
+        (sec-sortkey-subtotal-true #f)       
+        (key-choice-list (map
+                          (lambda (sortpair)
+                            (vector
+                             (car sortpair)
+                             (sortkey-get-info (car sortpair) 'text)
+                             (sortkey-get-info (car sortpair) 'tip)))
+                          sortkey-list))
+        (date-subtotal-choice-list (map
+                                    (lambda (date-sortpair)
+                                      (vector
+                                       (car date-sortpair)
+                                       (date-subtotal-get-info (car date-sortpair) 'text)
+                                       (date-subtotal-get-info (car date-sortpair) 'tip)))
+                                    date-subtotal-list)))
 
     (define (apply-selectable-by-name-sorting-options)
       (let* ((prime-sortkey-enabled (not (eq? prime-sortkey 'none)))
@@ -987,6 +866,9 @@ Credit Card, and Income accounts."))))))
           (cons 'sort-account-full-name (opt-val pagename-sorting (N_ "Show Full Account Name")))
           (cons 'notes (opt-val gnc:pagename-display (N_ "Notes")))))
 
+  (define (column-uses? param columns-used)
+    (cdr (assq param columns-used)))
+
   (define (make-heading-list columns-used)
     (define (add-if pred? . items) (if pred? items '()))
     (append
@@ -1075,6 +957,125 @@ Credit Card, and Income accounts."))))))
                              "total-number-cell" currency)))))
                   (cdr currency-totals))))
 
+    (define (total-string str) (string-append (_ "Total For ") str))
+
+    ;; display an account name depending on the options the user has set
+    (define (account-namestring account show-account-code? show-account-name? show-account-full-name?)
+      ;;# on multi-line splits we can get an empty ('()) account
+      (if (null? account)
+          (_ "Split Transaction")
+          (string-append
+           ;; display account code?
+           (if show-account-code?
+               (string-append (xaccAccountGetCode account) " ")
+               "")
+           ;; display account name?
+           (if show-account-name?
+               ;; display full account name?
+               (if show-account-full-name?
+                   (gnc-account-get-full-name account)
+                   (xaccAccountGetName account))
+               ""))))
+
+    ;; render an account subheading - used-columns determines what is displayed
+    (define (render-account-subheading split)
+      (let ((account (xaccSplitGetAccount split)))
+        (gnc:make-html-text
+         (gnc:html-markup-anchor
+          (gnc:account-anchor-text account)
+          (account-namestring account
+                              (column-uses? 'sort-account-code      used-columns)
+                              #t
+                              (column-uses? 'sort-account-full-name used-columns))))))                        
+
+    (define (render-corresponding-account-subheading split)
+      (let ((account (xaccSplitGetAccount (xaccSplitGetOtherSplit split))))
+        (gnc:make-html-text
+         (gnc:html-markup-anchor
+          (if (null? account)
+              ""
+              (gnc:account-anchor-text account))
+          (account-namestring account
+                              (column-uses? 'sort-account-code      used-columns)
+                              #t
+                              (column-uses? 'sort-account-full-name used-columns))))))
+
+    (define (render-week-subheading split)
+      (gnc:date-get-week-year-string
+       (gnc:timepair->date
+        (gnc-transaction-get-date-posted
+         (xaccSplitGetParent split)))))
+
+    (define (render-month-subheading split)
+      (gnc:date-get-month-year-string
+       (gnc:timepair->date
+        (gnc-transaction-get-date-posted
+         (xaccSplitGetParent split)))))
+
+    (define (render-quarter-subheading split)
+      (gnc:date-get-quarter-year-string
+       (gnc:timepair->date
+        (gnc-transaction-get-date-posted
+         (xaccSplitGetParent split)))))
+
+
+    (define (render-year-subheading split)
+      (gnc:date-get-year-string
+       (gnc:timepair->date
+        (gnc-transaction-get-date-posted
+         (xaccSplitGetParent split)))))
+
+    (define (render-account-subtotal split)
+      (account-namestring (xaccSplitGetAccount split)
+                          (column-uses? 'sort-account-code      used-columns)
+                          #t
+                          (column-uses? 'sort-account-full-name used-columns)))
+
+    (define (render-corresponding-account-subtotal split)
+      (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
+                          (column-uses? 'sort-account-code      used-columns)
+                          #t
+                          (column-uses? 'sort-account-full-name used-columns)))
+
+    (define (render-week-subtotal split)
+      (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
+        (gnc:date-get-week-year-string tm)))
+
+    (define (render-month-subtotal split)
+      (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
+        (gnc:date-get-month-year-string tm)))
+
+    (define (render-quarter-subtotal split)
+      (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
+        (gnc:date-get-quarter-year-string tm)))
+
+    (define (render-year-subtotal split)
+      (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted (xaccSplitGetParent split)))))
+        (strftime "%Y" tm)))
+
+    (define (render-grand-total)
+      (_ "Grand Total"))
+
+    (define (subheading-renderer split key)
+      ((case key
+         ((week)      render-week-subheading)
+         ((month)     render-month-subheading)
+         ((quarter)   render-quarter-subheading)
+         ((year)      render-year-subheading)
+         ((account)   render-account-subheading)
+         ((other-acc) render-corresponding-account-subheading))
+       split))
+
+    (define (subtotal-renderer split key)
+      ((case key
+         ((week)      render-week-subtotal)
+         ((month)     render-month-subtotal)
+         ((quarter)   render-quarter-subtotal)
+         ((year)      render-year-subtotal)
+         ((account)   render-account-subtotal)
+         ((other-acc) render-corresponding-account-subtotal))
+       split))
+
     (define (do-rows-with-subtotals splits
                                     table
                                     used-columns

commit d88d503b38989e4b008097fda6fee5f05a7d667f
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 10:33:25 2017 +0800

    REFACTOR: simplify functions, reduce arguments

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 65f17eb..65fe6c1 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -109,12 +109,6 @@ options specified in the Options panels."))
 (define SUBTOTAL-ENABLED (list 'account-name 'corresponding-acc-name
                                'account-code 'corresponding-acc-code))
 
-(define (add-subheading-row data table width subheading-style)
-  (let ((heading-cell (gnc:make-html-table-cell data)))
-    (gnc:html-table-cell-set-colspan! heading-cell width)
-    (gnc:html-table-append-row/markup!
-     table subheading-style
-     (list heading-cell))))
 
 (define (column-uses? param columns-used)
   (cdr (assq param columns-used)))
@@ -138,151 +132,90 @@ options specified in the Options panels."))
                (xaccAccountGetName account))
            ""))))
 
-                                                   
-
 ;; render an account subheading - column-vector determines what is displayed
-(define (render-account-subheading
-         split table width subheading-style column-vector)
+(define (render-account-subheading split column-vector)
   (let ((account (xaccSplitGetAccount split)))
-    (add-subheading-row (gnc:make-html-text
-                         (gnc:html-markup-anchor
-                          (gnc:account-anchor-text account)
-                          (account-namestring account
-                                              (column-uses? 'sort-account-code      column-vector)
-                                              #t
-                                              (column-uses? 'sort-account-full-name column-vector))))
-                        table width subheading-style)))
-
-(define (render-corresponding-account-subheading
-         split table width subheading-style column-vector)
+    (gnc:make-html-text
+     (gnc:html-markup-anchor
+      (gnc:account-anchor-text account)
+      (account-namestring account
+                          (column-uses? 'sort-account-code      column-vector)
+                          #t
+                          (column-uses? 'sort-account-full-name column-vector))))))                        
+
+(define (render-corresponding-account-subheading split column-vector)
   (let ((account (xaccSplitGetAccount (xaccSplitGetOtherSplit split))))
-    (add-subheading-row (gnc:make-html-text
-                         (gnc:html-markup-anchor
-                          (if (null? account)
-                              ""
-                              (gnc:account-anchor-text account))
-                          (account-namestring account
-                                              (column-uses? 'sort-account-code      column-vector)
-                                              #t
-                                              (column-uses? 'sort-account-full-name column-vector))))
-                        table width subheading-style)))
-
-(define (render-week-subheading split table width subheading-style column-vector)
-  (add-subheading-row (gnc:date-get-week-year-string
-                       (gnc:timepair->date
-                        (gnc-transaction-get-date-posted
-                         (xaccSplitGetParent split))))
-                      table width subheading-style))
-
-(define (render-month-subheading split table width subheading-style column-vector)
-  (add-subheading-row (gnc:date-get-month-year-string
-                       (gnc:timepair->date
-                        (gnc-transaction-get-date-posted
-                         (xaccSplitGetParent split))))
-                      table width subheading-style))
-
-(define (render-quarter-subheading split table width subheading-style column-vector)
-  (add-subheading-row (gnc:date-get-quarter-year-string
-                       (gnc:timepair->date
-                        (gnc-transaction-get-date-posted
-                         (xaccSplitGetParent split))))
-                      table width subheading-style))
-
-(define (render-year-subheading split table width subheading-style column-vector)
-  (add-subheading-row (gnc:date-get-year-string
-                       (gnc:timepair->date
-                        (gnc-transaction-get-date-posted
-                         (xaccSplitGetParent split))))
-                      table width subheading-style))
-
-(define (add-subtotal-row table width subtotal-string subtotal-collector
-                          subtotal-style export?)
-  (let ((currency-totals (subtotal-collector 'format gnc:make-gnc-monetary #f)))
-    (gnc:html-table-append-row/markup!
-     table
-     subtotal-style
-     (if export?
-         (append! (cons (gnc:make-html-table-cell/markup "total-label-cell" subtotal-string)
-                        (gnc:html-make-empty-cells (- width 2)))
-                  (list (gnc:make-html-table-cell/markup
-                         "total-number-cell"
-                         (car currency-totals))))
-         (list (gnc:make-html-table-cell/size/markup 1 (- width 1) "total-label-cell"
-                                                     subtotal-string)
-               (gnc:make-html-table-cell/markup
-                "total-number-cell"
-                (car currency-totals)))))
-    (for-each (lambda (currency)
-                (gnc:html-table-append-row/markup!
-                 table
-                 subtotal-style
-                 (append!
-                  (if export?
-                      (gnc:html-make-empty-cells (- width 1))
-                      (list (gnc:make-html-table-cell/size 1 (- width 1) #f)))
-                  (list (gnc:make-html-table-cell/markup
-                         "total-number-cell" currency)))))
-              (cdr currency-totals))))
+    (gnc:make-html-text
+     (gnc:html-markup-anchor
+      (if (null? account)
+          ""
+          (gnc:account-anchor-text account))
+      (account-namestring account
+                          (column-uses? 'sort-account-code      column-vector)
+                          #t
+                          (column-uses? 'sort-account-full-name column-vector))))))
+
+(define (render-week-subheading split column-vector)
+  (gnc:date-get-week-year-string
+   (gnc:timepair->date
+    (gnc-transaction-get-date-posted
+     (xaccSplitGetParent split)))))
+
+(define (render-month-subheading split column-vector)
+  (gnc:date-get-month-year-string
+   (gnc:timepair->date
+    (gnc-transaction-get-date-posted
+     (xaccSplitGetParent split)))))
+
+(define (render-quarter-subheading split column-vector)
+  (gnc:date-get-quarter-year-string
+   (gnc:timepair->date
+    (gnc-transaction-get-date-posted
+     (xaccSplitGetParent split)))))
+
+
+(define (render-year-subheading split column-vector)
+  (gnc:date-get-year-string
+   (gnc:timepair->date
+    (gnc-transaction-get-date-posted
+     (xaccSplitGetParent split)))))
 
 (define (total-string str) (string-append (_ "Total For ") str))
 
-(define (render-account-subtotal
-         table width split total-collector subtotal-style column-vector export?)
-  (add-subtotal-row table width
-                    (total-string (account-namestring (xaccSplitGetAccount split)
-                                                      (column-uses? 'sort-account-code      column-vector)
-                                                      #t
-                                                      (column-uses? 'sort-account-full-name column-vector)))
-                    total-collector subtotal-style export?))
-
-(define (render-corresponding-account-subtotal
-         table width split total-collector subtotal-style column-vector export?)
-  (add-subtotal-row table width
-                    (total-string (account-namestring (xaccSplitGetAccount
-                                                       (xaccSplitGetOtherSplit split))
-                                                      (column-uses? 'sort-account-code      column-vector)
-                                                      #t
-                                                      (column-uses? 'sort-account-full-name column-vector)))
-                    total-collector subtotal-style export?))
-
-(define (render-week-subtotal
-         table width split total-collector subtotal-style column-vector export?)
+(define (render-account-subtotal split column-vector)
+  (total-string (account-namestring (xaccSplitGetAccount split)
+                                    (column-uses? 'sort-account-code      column-vector)
+                                    #t
+                                    (column-uses? 'sort-account-full-name column-vector))))
+
+(define (render-corresponding-account-subtotal split column-vector)
+  (total-string (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
+                                    (column-uses? 'sort-account-code      column-vector)
+                                    #t
+                                    (column-uses? 'sort-account-full-name column-vector))))
+
+(define (render-week-subtotal split column-vector)
   (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
                                  (xaccSplitGetParent split)))))
-    (add-subtotal-row table width
-                      (total-string (gnc:date-get-week-year-string tm))
-                      total-collector subtotal-style export?)))
+    (total-string (gnc:date-get-week-year-string tm))))
 
-(define (render-month-subtotal
-         table width split total-collector subtotal-style column-vector export?)
+(define (render-month-subtotal split column-vector)
   (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
                                  (xaccSplitGetParent split)))))
-    (add-subtotal-row table width
-                      (total-string (gnc:date-get-month-year-string tm))
-                      total-collector subtotal-style export?)))
+    (total-string (gnc:date-get-month-year-string tm))))
 
-
-(define (render-quarter-subtotal
-         table width split total-collector subtotal-style column-vector export?)
+(define (render-quarter-subtotal split column-vector)
   (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
                                  (xaccSplitGetParent split)))))
-    (add-subtotal-row table width
-                      (total-string (gnc:date-get-quarter-year-string tm))
-                      total-collector subtotal-style export?)))
+    (total-string (gnc:date-get-quarter-year-string tm))))
 
-(define (render-year-subtotal
-         table width split total-collector subtotal-style column-vector export?)
+(define (render-year-subtotal split column-vector)
   (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
                                  (xaccSplitGetParent split)))))
-    (add-subtotal-row table width
-                      (total-string (strftime "%Y" tm))
-                      total-collector subtotal-style export?)))
+    (total-string (strftime "%Y" tm))))
 
-(define (render-grand-total
-         table width total-collector export?)
-  (add-subtotal-row table width
-                    (_ "Grand Total")
-                    total-collector def:grand-total-style export?))
+(define (render-grand-total)
+  (_ "Grand Total")) ; def:grand-total-style
 
 
 
@@ -464,7 +397,7 @@ options specified in the Options panels."))
 (define (add-split-row table split column-vector options
                        row-style account-types-to-reverse transaction-row?)
 
-  (define (opt-val section name) (gnc:option-value  (gnc:lookup-option options section name)))
+  (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
 
   (let* ((row-contents '())
          (parent (xaccSplitGetParent split))
@@ -1093,8 +1026,54 @@ Credit Card, and Income accounts."))))))
      (add-if (column-uses? 'running-balance columns-used)
              (_ "Balance"))))
 
-  (let ((work-to-do (length splits))
-        (work-done 0))
+  (let* ((work-to-do (length splits))
+         (work-done 0)
+         (table (gnc:make-html-table))
+         (used-columns (build-columns-used))
+         (headings (make-heading-list used-columns))
+         (width (length headings))
+         (account-types-to-reverse
+          (case (opt-val gnc:pagename-display (N_ "Sign Reverses"))
+            ((none) '())
+            ((income-expense) (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))
+            ((credit-accounts)  (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE
+                                      ACCT-TYPE-EQUITY ACCT-TYPE-CREDIT
+                                      ACCT-TYPE-INCOME))))
+         (is-multiline? (eq? (opt-val gnc:pagename-display optname-detail-level) 'multi-line))
+         (export? (opt-val gnc:pagename-general optname-table-export)))
+
+    (define (add-subheading data subheading-style)
+      (let ((heading-cell (gnc:make-html-table-cell data)))
+        (gnc:html-table-cell-set-colspan! heading-cell width)
+        (gnc:html-table-append-row/markup!
+         table subheading-style
+         (list heading-cell))))
+
+    (define (add-subtotal-row string collector style)
+      (let ((currency-totals (collector 'format gnc:make-gnc-monetary #f)))
+        (gnc:html-table-append-row/markup!
+         table style 
+         (if export?
+             (append! (cons (gnc:make-html-table-cell/markup "total-label-cell" string)
+                            (gnc:html-make-empty-cells (- width 2)))
+                      (list (gnc:make-html-table-cell/markup 
+                             "total-number-cell"
+                             (car currency-totals))))
+             (list (gnc:make-html-table-cell/size/markup 1 (- width 1) "total-label-cell"
+                                                         string)
+                   (gnc:make-html-table-cell/markup 
+                    "total-number-cell"
+                    (car currency-totals)))))
+        (for-each (lambda (currency)
+                    (gnc:html-table-append-row/markup! 
+                     table style
+                     (append!
+                      (if export?
+                          (gnc:html-make-empty-cells (- width 1))
+                          (list (gnc:make-html-table-cell/size 1 (- width 1) #f)))
+                      (list (gnc:make-html-table-cell/markup
+                             "total-number-cell" currency)))))
+                  (cdr currency-totals))))
 
     (define (do-rows-with-subtotals splits
                                     table
@@ -1119,9 +1098,9 @@ Credit Card, and Income accounts."))))))
       (set! work-done (+ 1 work-done))
 
       (if (null? splits)
-
+          
           (begin
-
+            
             (gnc:html-table-append-row/markup!
              table def:grand-total-style
              (list
@@ -1129,12 +1108,11 @@ Credit Card, and Income accounts."))))))
                1 width (gnc:make-html-text (gnc:html-markup-hr)))))
 
             (if (opt-val gnc:pagename-display "Totals")
-                (render-grand-total table width total-collector export?)))
+                (add-subtotal-row (render-grand-total) total-collector def:grand-total-style)))
 
           (let* ((current (car splits))
                  (rest (cdr splits))
-                 (next (if (null? rest) #f
-                           (car rest))))
+                 (next (if (null? rest) #f (car rest))))
 
             (define split-value (add-split-row
                                  table
@@ -1147,7 +1125,7 @@ Credit Card, and Income accounts."))))))
                                          def:alternate-row-style))
                                  account-types-to-reverse
                                  #t))
-
+            
             (if multi-rows?
 
                 (for-each (lambda (othersplits)
@@ -1168,11 +1146,11 @@ Credit Card, and Income accounts."))))))
             (secondary-subtotal-collector 'add
                                           (gnc:gnc-monetary-commodity split-value)
                                           (gnc:gnc-monetary-amount split-value))
-
+            
             (total-collector 'add
                              (gnc:gnc-monetary-commodity split-value)
                              (gnc:gnc-monetary-amount split-value))
-
+            
             (if (and primary-subtotal-pred
                      (or (not next)
                          (and next
@@ -1182,53 +1160,47 @@ Credit Card, and Income accounts."))))))
                 (begin
 
                   (if secondary-subtotal-pred
-
+                      
                       (begin
-
-                        (secondary-subtotal-renderer
-                         table width current
-                         secondary-subtotal-collector
-                         def:secondary-subtotal-style used-columns export?)
-
+                        
+                        (add-subtotal-row (secondary-subtotal-renderer current used-columns)
+                                          secondary-subtotal-collector
+                                          def:secondary-subtotal-style)
+                        
                         (secondary-subtotal-collector 'reset #f #f)))
-
-                  (primary-subtotal-renderer table width current
-                                             primary-subtotal-collector
-                                             def:primary-subtotal-style used-columns
-                                             export?)
-
+                  
+                  (add-subtotal-row (primary-subtotal-renderer current used-columns)
+                                    primary-subtotal-collector
+                                    def:primary-subtotal-style)
+                  
                   (primary-subtotal-collector 'reset #f #f)
-
+                  
                   (if next
 
                       (begin
 
-                        (primary-subheading-renderer
-                         next table width def:primary-subtotal-style used-columns)
+                        (add-subheading (primary-subheading-renderer next used-columns)
+                                        def:primary-subtotal-style)
 
                         (if secondary-subtotal-pred
-                            (secondary-subheading-renderer
-                             next
-                             table
-                             width def:secondary-subtotal-style used-columns)))))
+                            (add-subheading (secondary-subheading-renderer next used-columns)
+                                            def:secondary-subtotal-style)))))
 
                 (if (and secondary-subtotal-pred
                          (or (not next)
                              (and next
                                   (not (equal? (secondary-subtotal-pred current)
                                                (secondary-subtotal-pred next))))))
-
-                    (begin (secondary-subtotal-renderer
-                            table width current
-                            secondary-subtotal-collector
-                            def:secondary-subtotal-style used-columns export?)
-
+                    
+                    (begin (add-subtotal-row (secondary-subtotal-renderer current used-columns)
+                                             secondary-subtotal-collector
+                                             def:secondary-subtotal-style)
+                           
                            (secondary-subtotal-collector 'reset #f #f)
-
+                           
                            (if next
-                               (secondary-subheading-renderer
-                                next table width
-                                def:secondary-subtotal-style used-columns)))))
+                               (add-subheading (secondary-subheading-renderer next used-columns)
+                                               def:secondary-subtotal-style)))))
 
             (do-rows-with-subtotals rest
                                     table
@@ -1248,45 +1220,31 @@ Credit Card, and Income accounts."))))))
                                     secondary-subtotal-collector
                                     total-collector))))
 
-    (let* ((table (gnc:make-html-table))
-           (used-columns (build-columns-used))
-           (headings (make-heading-list used-columns))
-           (width (length headings))
-           (account-types-to-reverse
-            (cdr (assq (opt-val gnc:pagename-display (N_ "Sign Reverses"))
-                       (list (cons 'none '())
-                             (cons 'income-expense (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))
-                             (cons 'credit-accounts  (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE
-                                                           ACCT-TYPE-EQUITY ACCT-TYPE-CREDIT
-                                                           ACCT-TYPE-INCOME))))))
-           (is-multiline? (eq? (opt-val gnc:pagename-display optname-detail-level) 'multi-line))
-           (export? (opt-val gnc:pagename-general optname-table-export)))
-
-      (gnc:html-table-set-col-headers! table headings)
-
-      (if primary-subheading-renderer
-          (primary-subheading-renderer
-           (car splits) table width def:primary-subtotal-style used-columns))
-
-      (if secondary-subheading-renderer
-          (secondary-subheading-renderer
-           (car splits) table width def:secondary-subtotal-style used-columns))
-
-      (do-rows-with-subtotals splits table used-columns width
-                              is-multiline? #t
-                              export?
-                              account-types-to-reverse
-                              primary-subtotal-pred
-                              secondary-subtotal-pred
-                              primary-subheading-renderer
-                              secondary-subheading-renderer
-                              primary-subtotal-renderer
-                              secondary-subtotal-renderer
-                              (gnc:make-commodity-collector)
-                              (gnc:make-commodity-collector)
-                              (gnc:make-commodity-collector))
-
-      table)))
+    (gnc:html-table-set-col-headers! table headings)
+
+    (if primary-subheading-renderer
+        (add-subheading (primary-subheading-renderer (car splits) used-columns)
+                        def:primary-subtotal-style))
+
+    (if secondary-subheading-renderer
+        (add-subheading (secondary-subheading-renderer (car splits) used-columns)
+                        def:secondary-subtotal-style))
+
+    (do-rows-with-subtotals splits table used-columns width
+                            is-multiline? #t
+                            export?
+                            account-types-to-reverse
+                            primary-subtotal-pred
+                            secondary-subtotal-pred
+                            primary-subheading-renderer
+                            secondary-subheading-renderer
+                            primary-subtotal-renderer
+                            secondary-subtotal-renderer
+                            (gnc:make-commodity-collector)
+                            (gnc:make-commodity-collector)
+                            (gnc:make-commodity-collector))
+
+    table))
 
 ;; ;;;;;;;;;;;;;;;;;;;;
 ;; Here comes the renderer function for this report.

commit a5306d045411028b15f5ac040a1cf33c749d5432
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 10:17:06 2017 +0800

    REFACTOR: improve heading-list to handle dual headings

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 7ec875b..65f17eb 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1055,7 +1055,7 @@ Credit Card, and Income accounts."))))))
           (cons 'notes (opt-val gnc:pagename-display (N_ "Notes")))))
 
   (define (make-heading-list columns-used)
-    (define (add-if pred? item) (if pred? (list item) '()))
+    (define (add-if pred? . items) (if pred? items '()))
     (append
      (add-if (column-uses? 'date columns-used)
              (_ "Date"))
@@ -1087,11 +1087,9 @@ Credit Card, and Income accounts."))))))
      (add-if (column-uses? 'amount-single columns-used)
              (_ "Amount"))
      ;; FIXME: Proper labels: what?
-     (if (column-uses? 'amount-double columns-used)
-         (list
-          (_ "Debit")
-          (_ "Credit"))
-         '())
+     (add-if (column-uses? 'amount-double columns-used)
+             (_ "Debit")
+             (_ "Credit"))
      (add-if (column-uses? 'running-balance columns-used)
              (_ "Balance"))))
 

commit e1ba5f3248269d6843fbbe1ef6fc36a086724b22
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 10:13:40 2017 +0800

    REFACTOR: centralize BOOK-SPLIT-ACTION
    
    Centralize BOOK-SPLIT-ACTION

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 5f854d6..7ec875b 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -109,7 +109,6 @@ options specified in the Options panels."))
 (define SUBTOTAL-ENABLED (list 'account-name 'corresponding-acc-name
                                'account-code 'corresponding-acc-code))
 
-
 (define (add-subheading-row data table width subheading-style)
   (let ((heading-cell (gnc:make-html-table-cell data)))
     (gnc:html-table-cell-set-colspan! heading-cell width)
@@ -360,7 +359,7 @@ options specified in the Options panels."))
                                    (cons 'subheading-renderer #f)
                                    (cons 'subtotal-renderer #f)))
 
-        (if BOOK-SPLIT-ACTION
+        (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
             (cons 'number    (list (cons 'sortkey (list SPLIT-ACTION))
                                    (cons 'split-sortvalue #f)
                                    (cons 'text (N_ "Number/Action"))
@@ -604,7 +603,7 @@ options specified in the Options panels."))
 (define (trep-options-generator)
 
   (define options (gnc:new-options))
-
+  (define BOOK-SPLIT-ACTION (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
   (define (gnc:register-trep-option new-option)
     (gnc:register-option options new-option))
 
@@ -914,7 +913,7 @@ tags within description, notes or memo. ")
      (list
       (list (N_ "Date")                         "a"  (N_ "Display the date?") #t)
       (list (N_ "Reconciled Date")              "a2" (N_ "Display the reconciled date?") #f)
-      (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
+      (if BOOK-SPLIT-ACTION
           (list (N_ "Num/Action")               "b"  (N_ "Display the check number?") #t)
           (list (N_ "Num")                      "b"  (N_ "Display the check number?") #t))
       (list (N_ "Description")                  "c"  (N_ "Display the description?") #t)
@@ -931,7 +930,7 @@ tags within description, notes or memo. ")
       (list (N_ "Running Balance")              "n"  (N_ "Display a running balance?") #f)
       (list (N_ "Totals")                       "o"  (N_ "Display the totals?") #t)))
 
-    (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
+    (if BOOK-SPLIT-ACTION
         (gnc:register-trep-option
          (gnc:make-simple-boolean-option
           gnc:pagename-display (N_ "Trans Number")
@@ -1025,15 +1024,16 @@ Credit Card, and Income accounts."))))))
                           secondary-subtotal-renderer)
 
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
+  (define BOOK-SPLIT-ACTION (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
 
   (define (build-columns-used)
     (define is-single? (eq? (opt-val gnc:pagename-display optname-detail-level) 'single))
     (define amount-setting (opt-val gnc:pagename-display (N_ "Amount")))
     (list (cons 'date (opt-val gnc:pagename-display (N_ "Date")))
           (cons 'reconciled-date (opt-val gnc:pagename-display (N_ "Reconciled Date")))
-          (cons 'num (if (gnc:lookup-option options gnc:pagename-display (N_ "Num"))
-                         (opt-val gnc:pagename-display (N_ "Num"))
-                         (opt-val gnc:pagename-display (N_ "Num/Action"))))
+          (cons 'num (if BOOK-SPLIT-ACTION
+                         (opt-val gnc:pagename-display (N_ "Num/Action"))
+                         (opt-val gnc:pagename-display (N_ "Num"))))
           (cons 'description (opt-val gnc:pagename-display (N_ "Description")))
           (cons 'account-name (opt-val gnc:pagename-display (N_ "Account Name")))
           (cons 'other-account-name (and is-single?
@@ -1297,6 +1297,7 @@ Credit Card, and Income accounts."))))))
 (define (trep-renderer report-obj)
   (define options (gnc:report-options report-obj))
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
+  (define BOOK-SPLIT-ACTION (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
 
   (define (subtotal-get-info name-sortkey name-subtotal name-date-subtotal info)
     ;; The value of the sorting-key multichoice option.

commit 070c99c1c2142317f093c5a27a1b8ce279030af0
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 09:02:51 2017 +0800

    REFACTOR: centralize DATE-SORTING-TYPES and SUBTOTAL-ENABLED
    
    Also minor whitespace changes

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 5e92334..5f854d6 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -101,12 +101,13 @@ in the Options panel."))
 (define NO-MATCHING-ACCT-TEXT (N_ "No account were found that match the \
 options specified in the Options panels."))
 
+
+(define DATE-SORTING-TYPES (list 'date 'reconciled-date))
+
 ;; The option-values of the sorting key multichoice option, for
 ;; which a subtotal should be enabled.
-(define subtotal-enabled '(account-name
-                           account-code
-                           corresponding-acc-name
-                           corresponding-acc-code))
+(define SUBTOTAL-ENABLED (list 'account-name 'corresponding-acc-name
+                               'account-code 'corresponding-acc-code))
 
 
 (define (add-subheading-row data table width subheading-style)
@@ -119,6 +120,7 @@ options specified in the Options panels."))
 (define (column-uses? param columns-used)
   (cdr (assq param columns-used)))
 
+            
 ;; display an account name depending on the options the user has set
 (define (account-namestring account show-account-code? show-account-name? show-account-full-name?)
   ;;# on multi-line splits we can get an empty ('()) account
@@ -598,7 +600,6 @@ options specified in the Options panels."))
     split-value))
 
 
-(define date-sorting-types (list 'date 'reconciled-date))
 
 (define (trep-options-generator)
 
@@ -750,11 +751,11 @@ tags within description, notes or memo. ")
 
     (define (apply-selectable-by-name-sorting-options)
       (let* ((prime-sortkey-enabled (not (eq? prime-sortkey 'none)))
-             (prime-sortkey-subtotal-enabled (member prime-sortkey subtotal-enabled))
-             (prime-date-sortingtype-enabled (member prime-sortkey date-sorting-types))
+             (prime-sortkey-subtotal-enabled (member prime-sortkey SUBTOTAL-ENABLED))
+             (prime-date-sortingtype-enabled (member prime-sortkey DATE-SORTING-TYPES))
              (sec-sortkey-enabled (not (eq? sec-sortkey 'none)))
-             (sec-sortkey-subtotal-enabled (member sec-sortkey subtotal-enabled))
-             (sec-date-sortingtype-enabled (member sec-sortkey date-sorting-types)))
+             (sec-sortkey-subtotal-enabled (member sec-sortkey SUBTOTAL-ENABLED))
+             (sec-date-sortingtype-enabled (member sec-sortkey DATE-SORTING-TYPES)))
 
         (gnc-option-db-set-option-selectable-by-name
          options pagename-sorting optname-prime-subtotal
@@ -1300,7 +1301,7 @@ Credit Card, and Income accounts."))))))
   (define (subtotal-get-info name-sortkey name-subtotal name-date-subtotal info)
     ;; The value of the sorting-key multichoice option.
     (let ((sortkey (opt-val pagename-sorting name-sortkey)))
-      (if (member sortkey date-sorting-types)
+      (if (member sortkey DATE-SORTING-TYPES)
           ;; If sorting by date, look up the value of the
           ;; date-subtotalling multichoice option and return the
           ;; corresponding funcs in the assoc-list.
@@ -1309,7 +1310,7 @@ Credit Card, and Income accounts."))))))
           ;; subtotalling enabled at all, 2. check whether the
           ;; enable-subtotal boolean option is #t, 3. look up the
           ;; appropriate funcs in the assoc-list.
-          (and (member sortkey subtotal-enabled)
+          (and (member sortkey SUBTOTAL-ENABLED)
                (and (opt-val pagename-sorting name-subtotal)
                     (sortkey-get-info sortkey info))))))
 
@@ -1377,8 +1378,7 @@ Credit Card, and Income accounts."))))))
             ;; error condition: no accounts specified
             (gnc:html-document-add-object!
              document
-             (gnc:html-make-no-account-warning
-              report-title (gnc:report-id report-obj)))
+             (gnc:html-make-no-account-warning report-title (gnc:report-id report-obj)))
 
             ;; error condition: accounts were specified but none matched string/regex
             (gnc:html-document-add-object!
@@ -1472,9 +1472,9 @@ Credit Card, and Income accounts."))))))
                  (gnc:make-html-text
                   (gnc:html-markup-h3
                    (sprintf #f
-                           (_ "From %s to %s")
-                           (gnc-print-date begindate)
-                           (gnc-print-date enddate)))))
+                            (_ "From %s to %s")
+                            (gnc-print-date begindate)
+                            (gnc-print-date enddate)))))
 
                 (gnc:html-document-add-object! document table)))))
 

commit d0c435e73d8638a2027b98655e891960e28c58e3
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 09:49:59 2017 +0800

    REFACTOR: centralize key-choice-list

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index a70bcce..5e92334 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -398,6 +398,13 @@ options specified in the Options panels."))
 (define (sortkey-get-info sortkey info)
   (cdr (assq info (cdr (assq sortkey sortkey-list)))))
 
+(define key-choice-list
+  (map (lambda (sortpair)
+         (vector (car sortpair)
+                 (sortkey-get-info (car sortpair) 'text)
+                 (sortkey-get-info (car sortpair) 'tip)))
+       sortkey-list))
+
 (define (timepair-year tp)    (gnc:timepair-get-year tp))
 (define (timepair-quarter tp) (+ (* 10 (timepair-year tp))  (gnc:timepair-get-quarter tp)))
 (define (timepair-month tp)   (+ (* 100 (timepair-year tp)) (gnc:timepair-get-month tp)))
@@ -728,71 +735,14 @@ tags within description, notes or memo. ")
 
   ;; Sorting options
   
-  (let ((key-choice-list
-         (append
-          (list (vector 'none
-                        (N_ "None")
-                        (N_ "Do not sort."))
-
-                (vector 'account-name
-                        (N_ "Account Name")
-                        (N_ "Sort & subtotal by account name."))
-
-                (vector 'account-code
-                        (N_ "Account Code")
-                        (N_ "Sort & subtotal by account code."))
-
-                (vector 'date
-                        (N_ "Date")
-                        (N_ "Sort by date."))
-
-                (vector 'reconciled-date
-                        (N_ "Reconciled Date")
-                        (N_ "Sort by the Reconciled Date."))
-
-                (vector 'register-order
-                        (N_ "Register Order")
-                        (N_ "Sort as in the register."))
-
-                (vector 'corresponding-acc-name
-                        (N_ "Other Account Name")
-                        (N_ "Sort by account transferred from/to's name."))
-
-                (vector 'corresponding-acc-code
-                        (N_ "Other Account Code")
-                        (N_ "Sort by account transferred from/to's code."))
-
-                (vector 'amount
-                        (N_ "Amount")
-                        (N_ "Sort by amount."))
-
-                (vector 'description
-                        (N_ "Description")
-                        (N_ "Sort by description."))
-
-                (vector 'number
-                        (N_ "Number/Action")
-                        (N_ "Sort by check number/action.")))
-          (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-              (list
-               (vector 't-number
-                       (N_ "Transaction Number")
-                       (N_ "Sort by transaction number.")))
-              '())
-          (list
-           (vector 'memo
-                   (N_ "Memo")
-                   (N_ "Sort by memo.")))))
-
-        (ascending-choice-list
-         (list
-          (vector 'ascend
-                  (N_ "Ascending")
-                  (N_ "Smallest to largest, earliest to latest."))
-          (vector 'descend
-                  (N_ "Descending")
-                  (N_ "Largest to smallest, latest to earliest."))))
 
+  (let ((ascending-choice-list 
+         (list (vector 'ascend
+                       (N_ "Ascending")
+                       (N_ "Smallest to largest, earliest to latest."))
+               (vector 'descend
+                       (N_ "Descending")
+                       (N_ "Largest to smallest, latest to earliest."))))
         (prime-sortkey 'account-name)
         (prime-sortkey-subtotal-true #t)
         (sec-sortkey 'register-order)

commit a2008c492d15d4c3cd1e7ed0b75f74da1e9d44b6
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 09:54:45 2017 +0800

    REFACTOR: centralize date-subtotal-list

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 0c25a5a..a70bcce 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -108,44 +108,6 @@ options specified in the Options panels."))
                            corresponding-acc-name
                            corresponding-acc-code))
 
-(define (timepair-same-year tp-a tp-b)
-  (= (gnc:timepair-get-year tp-a)
-     (gnc:timepair-get-year tp-b)))
-
-(define (timepair-same-quarter tp-a tp-b)
-  (and (timepair-same-year tp-a tp-b)
-       (= (gnc:timepair-get-quarter tp-a)
-          (gnc:timepair-get-quarter tp-b))))
-
-(define (timepair-same-month tp-a tp-b)
-  (and (timepair-same-year tp-a tp-b)
-       (= (gnc:timepair-get-month tp-a)
-          (gnc:timepair-get-month tp-b))))
-
-(define (timepair-same-week tp-a tp-b)
-  (and (timepair-same-year tp-a tp-b)
-       (= (gnc:timepair-get-week tp-a)
-          (gnc:timepair-get-week tp-b))))
-
-(define (split-same-week? a b)
-  (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
-        (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
-    (timepair-same-week tp-a tp-b)))
-
-(define (split-same-month? a b)
-  (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
-        (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
-    (timepair-same-month tp-a tp-b)))
-
-(define (split-same-quarter? a b)
-  (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
-        (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
-    (timepair-same-quarter tp-a tp-b)))
-
-(define (split-same-year? a b)
-  (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
-        (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
-    (timepair-same-year tp-a tp-b)))
 
 (define (add-subheading-row data table width subheading-style)
   (let ((heading-cell (gnc:make-html-table-cell data)))
@@ -436,6 +398,60 @@ options specified in the Options panels."))
 (define (sortkey-get-info sortkey info)
   (cdr (assq info (cdr (assq sortkey sortkey-list)))))
 
+(define (timepair-year tp)    (gnc:timepair-get-year tp))
+(define (timepair-quarter tp) (+ (* 10 (timepair-year tp))  (gnc:timepair-get-quarter tp)))
+(define (timepair-month tp)   (+ (* 100 (timepair-year tp)) (gnc:timepair-get-month tp)))
+(define (timepair-week tp)    (+ (* 100 (timepair-year tp)) (gnc:timepair-get-week tp)))
+(define (split-week a) (timepair-week (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
+(define (split-month a) (timepair-month (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
+(define (split-quarter a) (timepair-quarter (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
+(define (split-year a) (timepair-year (gnc-transaction-get-date-posted (xaccSplitGetParent a))))
+
+(define date-subtotal-list
+  ;; Extra list for date option. Each entry: (cons
+  ;; 'date-subtotal-option-value (vector subtotal-function
+  ;; subtotal-renderer))
+  (list
+   (cons 'none (list
+                (cons 'split-sortvalue #f)
+                (cons 'text (N_ "None"))
+                (cons 'tip (N_ "None."))
+                (cons 'subheading-renderer #f)
+                (cons 'subtotal-renderer #f)))
+   (cons 'weekly (list
+                  (cons 'split-sortvalue split-week)
+                  (cons 'text (N_ "Weekly"))
+                  (cons 'tip (N_ "Weekly."))
+                  (cons 'subheading-renderer render-week-subheading)
+                  (cons 'subtotal-renderer render-week-subtotal)))
+   (cons 'monthly (list
+                   (cons 'split-sortvalue split-month)
+                   (cons 'text (N_ "Monthly"))
+                   (cons 'tip (N_ "Monthly."))
+                   (cons 'subheading-renderer render-month-subheading)
+                   (cons 'subtotal-renderer render-month-subtotal)))
+   (cons 'quarterly (list
+                     (cons 'split-sortvalue split-quarter)
+                     (cons 'text (N_ "Quarterly"))
+                     (cons 'tip (N_ "Quarterly."))
+                     (cons 'subheading-renderer render-quarter-subheading)
+                     (cons 'subtotal-renderer render-quarter-subtotal)))
+   (cons 'yearly (list
+                  (cons 'split-sortvalue split-year)
+                  (cons 'text (N_ "Yearly"))
+                  (cons 'tip (N_ "Yearly."))
+                  (cons 'subheading-renderer render-year-subheading)
+                  (cons 'subtotal-renderer render-year-subtotal)))))
+
+(define (date-subtotal-get-info sortkey info)
+  (cdr (assq info (cdr (assq sortkey date-subtotal-list)))))
+
+(define date-subtotal-choice-list
+  (map (lambda (date-sortpair)
+         (vector (car date-sortpair)
+                 (date-subtotal-get-info (car date-sortpair) 'text)
+                 (date-subtotal-get-info (car date-sortpair) 'tip)))
+       date-subtotal-list))
 
 (define (add-split-row table split column-vector options
                        row-style account-types-to-reverse transaction-row?)
@@ -777,14 +793,6 @@ tags within description, notes or memo. ")
                   (N_ "Descending")
                   (N_ "Largest to smallest, latest to earliest."))))
 
-        (subtotal-choice-list
-         (list
-          (vector 'none (N_ "None") (N_ "None."))
-          (vector 'weekly (N_ "Weekly") (N_ "Weekly."))
-          (vector 'monthly (N_ "Monthly") (N_ "Monthly."))
-          (vector 'quarterly (N_ "Quarterly") (N_ "Quarterly."))
-          (vector 'yearly (N_ "Yearly") (N_ "Yearly."))))
-
         (prime-sortkey 'account-name)
         (prime-sortkey-subtotal-true #t)
         (sec-sortkey 'register-order)
@@ -872,7 +880,7 @@ tags within description, notes or memo. ")
       pagename-sorting optname-prime-date-subtotal
       "e2" (N_ "Do a date subtotal.")
       'monthly
-      subtotal-choice-list))
+      date-subtotal-choice-list))
 
     (gnc:register-trep-option
      (gnc:make-multichoice-option
@@ -908,7 +916,7 @@ tags within description, notes or memo. ")
       pagename-sorting optname-sec-date-subtotal
       "i2" (N_ "Do a date subtotal.")
       'monthly
-      subtotal-choice-list))
+      date-subtotal-choice-list))
 
     (gnc:register-trep-option
      (gnc:make-multichoice-option
@@ -1219,7 +1227,8 @@ Credit Card, and Income accounts."))))))
             (if (and primary-subtotal-pred
                      (or (not next)
                          (and next
-                              (not (primary-subtotal-pred current next)))))
+                              (not (equal? (primary-subtotal-pred current)
+                                           (primary-subtotal-pred next))))))
 
                 (begin
 
@@ -1257,7 +1266,8 @@ Credit Card, and Income accounts."))))))
                 (if (and secondary-subtotal-pred
                          (or (not next)
                              (and next
-                                  (not (secondary-subtotal-pred current next)))))
+                                  (not (equal? (secondary-subtotal-pred current)
+                                               (secondary-subtotal-pred next))))))
 
                     (begin (secondary-subtotal-renderer
                             table width current
@@ -1336,18 +1346,15 @@ Credit Card, and Income accounts."))))))
 (define (trep-renderer report-obj)
   (define options (gnc:report-options report-obj))
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
-  (define (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal
-           comp-index date-index)
+
+  (define (subtotal-get-info name-sortkey name-subtotal name-date-subtotal info)
     ;; The value of the sorting-key multichoice option.
     (let ((sortkey (opt-val pagename-sorting name-sortkey)))
       (if (member sortkey date-sorting-types)
           ;; If sorting by date, look up the value of the
           ;; date-subtotalling multichoice option and return the
           ;; corresponding funcs in the assoc-list.
-          (vector-ref
-           (cdr (assq (opt-val pagename-sorting name-date-subtotal)
-                      date-comp-funcs-assoc-list))
-           date-index)
+          (date-subtotal-get-info (opt-val pagename-sorting name-date-subtotal) info)
           ;; For everything else: 1. check whether sortkey has
           ;; subtotalling enabled at all, 2. check whether the
           ;; enable-subtotal boolean option is #t, 3. look up the
@@ -1356,18 +1363,6 @@ Credit Card, and Income accounts."))))))
                (and (opt-val pagename-sorting name-subtotal)
                     (sortkey-get-info sortkey info))))))
 
-  (define (get-query-sortkey sort-option-value)
-    (vector-ref  (cdr (assq sort-option-value comp-funcs-assoc-list)) 0))
-
-  (define (get-subtotal-pred name-sortkey name-subtotal name-date-subtotal)
-    (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal 1 0))
-
-  (define (get-subheading-renderer name-sortkey name-subtotal name-date-subtotal)
-    (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal 2 1))
-
-  (define (get-subtotal-renderer name-sortkey name-subtotal name-date-subtotal)
-    (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal 3 2))
-
   (define (is-filter-member split account-list)
     (let* ((txn (xaccSplitGetParent split))
            (splitcount (xaccTransCountSplits txn))

commit b9390cead111e9c9d8ef491970b4de83b6b7ecf2
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 09:54:20 2017 +0800

    REFACTOR: centralize sortkey-list

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 40d9f68..0c25a5a 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -322,6 +322,121 @@ options specified in the Options panels."))
                     total-collector def:grand-total-style export?))
 
 
+
+
+(define sortkey-list
+  ;; Defines the different sorting keys, as an association-list 
+  ;; together with the subtotal functions. Each entry: 
+  ;;  'sortkey             - sort parameter sent via qof-query
+  ;;  'split-sortvalue     - function which retrieves number/string used for comparing splits
+  ;;  'text                - text displayed in Display tab
+  ;;  'tip                 - tooltip displayed in Display tab
+  ;;  'subheading-renderer - function which renders the subheading
+  ;;  'subtotal-renderer   - function which renders the subtotal
+  (list (cons 'account-name  (list (cons 'sortkey (list SPLIT-ACCT-FULLNAME))
+                                   (cons 'split-sortvalue (lambda (a) (gnc-account-get-full-name (xaccSplitGetAccount a))))
+                                   (cons 'text (N_ "Account Name"))
+                                   (cons 'tip (N_ "Sort & subtotal by account name."))
+                                   (cons 'subheading-renderer render-account-subheading)
+                                   (cons 'subtotal-renderer render-account-subtotal)))
+        
+        (cons 'account-code (list (cons 'sortkey (list SPLIT-ACCOUNT ACCOUNT-CODE-))
+                                  (cons 'split-sortvalue (lambda (a) (xaccAccountGetCode (xaccSplitGetAccount a))))
+                                  (cons 'text (N_ "Account Code"))
+                                  (cons 'tip (N_ "Sort & subtotal by account code."))
+                                  (cons 'subheading-renderer render-account-subheading)
+                                  (cons 'subtotal-renderer render-account-subtotal)))
+
+        (cons 'date         (list (cons 'sortkey (list SPLIT-TRANS TRANS-DATE-POSTED))
+                                  (cons 'split-sortvalue #f)
+                                  (cons 'text (N_ "Date"))
+                                  (cons 'tip (N_ "Sort by date."))
+                                  (cons 'subheading-renderer #f)
+                                  (cons 'subtotal-renderer #f)))
+
+        (cons 'reconciled-date (list (cons 'sortkey (list SPLIT-DATE-RECONCILED))
+                                     (cons 'split-sortvalue #f)
+                                     (cons 'text (N_ "Reconciled Date"))
+                                     (cons 'tip (N_ "Sort by the Reconciled Date."))
+                                     (cons 'subheading-renderer #f)
+                                     (cons 'subtotal-renderer #f)))
+
+        (cons 'register-order (list (cons 'sortkey (list QUERY-DEFAULT-SORT))
+                                    (cons 'split-sortvalue #f)
+                                    (cons 'text (N_ "Register Order"))
+                                    (cons 'tip (N_ "Sort as in the register."))
+                                    (cons 'subheading-renderer #f)
+                                    (cons 'subtotal-renderer #f)))                                 
+
+        (cons 'corresponding-acc-name (list (cons 'sortkey (list SPLIT-CORR-ACCT-NAME))
+                                            (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountFullName a)))
+                                            (cons 'text (N_ "Other Account Name"))
+                                            (cons 'tip (N_ "Sort by account transferred from/to's name."))
+                                            (cons 'subheading-renderer render-corresponding-account-subheading)
+                                            (cons 'subtotal-renderer render-corresponding-account-subtotal)))
+
+        (cons 'corresponding-acc-code (list (cons 'sortkey (list SPLIT-CORR-ACCT-CODE))
+                                            (cons 'split-sortvalue (lambda (a) (xaccSplitGetCorrAccountCode a)))
+                                            (cons 'text (N_ "Other Account Code"))
+                                            (cons 'tip (N_ "Sort by account transferred from/to's code."))
+                                            (cons 'subheading-renderer render-corresponding-account-subheading)
+                                            (cons 'subtotal-renderer render-corresponding-account-subtotal)))
+
+        (cons 'amount        (list (cons 'sortkey (list SPLIT-VALUE))
+                                   (cons 'split-sortvalue #f)
+                                   (cons 'text (N_ "Amount"))
+                                   (cons 'tip (N_ "Sort by amount."))
+                                   (cons 'subheading-renderer #f)
+                                   (cons 'subtotal-renderer #f)))
+
+        (cons 'description   (list (cons 'sortkey (list SPLIT-TRANS TRANS-DESCRIPTION))
+                                   (cons 'split-sortvalue #f)
+                                   (cons 'text (N_ "Description"))
+                                   (cons 'tip (N_ "Sort by description."))
+                                   (cons 'subheading-renderer #f)
+                                   (cons 'subtotal-renderer #f)))
+
+        (if BOOK-SPLIT-ACTION
+            (cons 'number    (list (cons 'sortkey (list SPLIT-ACTION))
+                                   (cons 'split-sortvalue #f)
+                                   (cons 'text (N_ "Number/Action"))
+                                   (cons 'tip (N_ "Sort by check number/action."))
+                                   (cons 'subheading-renderer #f)
+                                   (cons 'subtotal-renderer #f)))
+
+            (cons 'number    (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
+                                   (cons 'split-sortvalue #f)
+                                   (cons 'text (N_ "Number"))
+                                   (cons 'tip (N_ "Sort by check/transaction number."))
+                                   (cons 'subheading-renderer #f)
+                                   (cons 'subtotal-renderer #f))))
+
+        (cons 't-number      (list (cons 'sortkey (list SPLIT-TRANS TRANS-NUM))
+                                   (cons 'split-sortvalue #f)
+                                   (cons 'text (N_ "Transaction Number"))
+                                   (cons 'tip (N_ "Sort by transaction number."))
+                                   (cons 'subheading-renderer #f)
+                                   (cons 'subtotal-renderer #f)))
+
+        (cons 'memo          (list (cons 'sortkey (list SPLIT-MEMO))
+                                   (cons 'split-sortvalue #f)
+                                   (cons 'text (N_ "Memo"))
+                                   (cons 'tip (N_ "Sort by memo."))
+                                   (cons 'subheading-renderer #f)
+                                   (cons 'subtotal-renderer #f)))
+
+        (cons 'none          (list (cons 'sortkey '())
+                                   (cons 'split-sortvalue #f)
+                                   (cons 'text (N_ "None"))
+                                   (cons 'tip (N_ "Do not sort."))
+                                   (cons 'subheading-renderer #f)
+                                   (cons 'subtotal-renderer #f)))))
+
+
+(define (sortkey-get-info sortkey info)
+  (cdr (assq info (cdr (assq sortkey sortkey-list)))))
+
+
 (define (add-split-row table split column-vector options
                        row-style account-types-to-reverse transaction-row?)
 
@@ -1221,62 +1336,6 @@ Credit Card, and Income accounts."))))))
 (define (trep-renderer report-obj)
   (define options (gnc:report-options report-obj))
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
-  (define comp-funcs-assoc-list
-    ;; Defines the different sorting keys, together with the
-    ;; subtotal functions. Each entry: (cons
-    ;; 'sorting-key-option-value (vector 'query-sorting-key
-    ;; subtotal-function subtotal-renderer))
-    (list (cons 'account-name  (vector
-                                (list SPLIT-ACCT-FULLNAME)
-                                (lambda (a b) (zero? (xaccSplitCompareAccountFullNames a b))) 
-                                render-account-subheading
-                                render-account-subtotal))
-          (cons 'account-code  (vector
-                                (list SPLIT-ACCOUNT ACCOUNT-CODE-)
-                                (lambda (a b) (zero? (xaccSplitCompareAccountCodes a b)))
-                                render-account-subheading
-                                render-account-subtotal))
-          (cons 'date          (vector
-                                (list SPLIT-TRANS TRANS-DATE-POSTED)
-                                #f #f #f))
-          (cons 'reconciled-date (vector
-                                  (list SPLIT-DATE-RECONCILED)
-                                  #f #f #f))
-          (cons 'register-order (vector
-                                 (list QUERY-DEFAULT-SORT)
-                                 #f #f #f))
-          (cons 'corresponding-acc-name
-                (vector
-                 (list SPLIT-CORR-ACCT-NAME)
-                 (lambda (a b) (zero? (xaccSplitCompareOtherAccountFullNames a b))) 
-                 render-corresponding-account-subheading
-                 render-corresponding-account-subtotal))
-          (cons 'corresponding-acc-code
-                (vector
-                 (list SPLIT-CORR-ACCT-CODE)
-                 (lambda (a b) (zero? (xaccSplitCompareOtherAccountCodes a b)))
-                 render-corresponding-account-subheading
-                 render-corresponding-account-subtotal))
-          (cons 'amount        (vector (list SPLIT-VALUE) #f #f #f))
-          (cons 'description   (vector (list SPLIT-TRANS TRANS-DESCRIPTION) #f #f #f))
-          (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-              (cons 'number    (vector (list SPLIT-ACTION) #f #f #f))
-              (cons 'number    (vector (list SPLIT-TRANS TRANS-NUM) #f #f #f)))
-          (cons 't-number      (vector (list SPLIT-TRANS TRANS-NUM) #f #f #f))
-          (cons 'memo          (vector (list SPLIT-MEMO) #f #f #f))
-          (cons 'none          (vector '() #f #f #f))))
-
-  (define date-comp-funcs-assoc-list
-    ;; Extra list for date option. Each entry: (cons
-    ;; 'date-subtotal-option-value (vector subtotal-function
-    ;; subtotal-renderer))
-    (list
-     (cons 'none (vector #f #f #f))
-     (cons 'weekly (vector split-same-week? render-week-subheading render-week-subtotal))
-     (cons 'monthly (vector split-same-month? render-month-subheading render-month-subtotal))
-     (cons 'quarterly (vector split-same-quarter? render-quarter-subheading render-quarter-subtotal))
-     (cons 'yearly (vector split-same-year? render-year-subheading render-year-subtotal))))
-
   (define (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal
            comp-index date-index)
     ;; The value of the sorting-key multichoice option.
@@ -1295,7 +1354,7 @@ Credit Card, and Income accounts."))))))
           ;; appropriate funcs in the assoc-list.
           (and (member sortkey subtotal-enabled)
                (and (opt-val pagename-sorting name-subtotal)
-                    (vector-ref (cdr (assq sortkey comp-funcs-assoc-list)) comp-index))))))
+                    (sortkey-get-info sortkey info))))))
 
   (define (get-query-sortkey sort-option-value)
     (vector-ref  (cdr (assq sort-option-value comp-funcs-assoc-list)) 0))
@@ -1389,8 +1448,8 @@ Credit Card, and Income accounts."))))))
           (xaccQueryAddAccountMatch query c_account_1 QOF-GUID-MATCH-ANY QOF-QUERY-AND)
           (xaccQueryAddDateMatchTS query #t begindate #t enddate QOF-QUERY-AND)
           (qof-query-set-sort-order query
-                                    (get-query-sortkey primary-key)
-                                    (get-query-sortkey secondary-key)
+                                    (sortkey-get-info primary-key 'sortkey)
+                                    (sortkey-get-info secondary-key 'sortkey)
                                     '())
           (qof-query-set-sort-increasing query
                                          (eq? primary-order 'ascend)
@@ -1436,25 +1495,31 @@ Credit Card, and Income accounts."))))))
 
               (let ((table (make-split-table
                             splits options
-                            (get-subtotal-pred optname-prime-sortkey
+                            (subtotal-get-info optname-prime-sortkey
                                                optname-prime-subtotal
-                                               optname-prime-date-subtotal)
-                            (get-subtotal-pred optname-sec-sortkey
+                                               optname-prime-date-subtotal
+                                               'split-sortvalue)
+                            (subtotal-get-info optname-sec-sortkey
                                                optname-sec-subtotal
-                                               optname-sec-date-subtotal)
-                            (get-subheading-renderer optname-prime-sortkey
-                                                     optname-prime-subtotal
-                                                     optname-prime-date-subtotal)
-                            (get-subheading-renderer optname-sec-sortkey
-                                                     optname-sec-subtotal
-                                                     optname-sec-date-subtotal)
-                            (get-subtotal-renderer   optname-prime-sortkey
-                                                     optname-prime-subtotal
-                                                     optname-prime-date-subtotal)
-                            (get-subtotal-renderer   optname-sec-sortkey
-                                                     optname-sec-subtotal
-                                                     optname-sec-date-subtotal))))
-
+                                               optname-sec-date-subtotal
+                                               'split-sortvalue)
+                            (subtotal-get-info optname-prime-sortkey
+                                               optname-prime-subtotal
+                                               optname-prime-date-subtotal
+                                               'subheading-renderer)
+                            (subtotal-get-info optname-sec-sortkey
+                                               optname-sec-subtotal
+                                               optname-sec-date-subtotal
+                                               'subheading-renderer)
+                            (subtotal-get-info   optname-prime-sortkey
+                                                 optname-prime-subtotal
+                                                 optname-prime-date-subtotal
+                                                 'subtotal-renderer)
+                            (subtotal-get-info   optname-sec-sortkey
+                                                 optname-sec-subtotal
+                                                 optname-sec-date-subtotal
+                                                 'subtotal-renderer))))
+                
                 (gnc:html-document-set-title! document report-title)
 
                 (gnc:html-document-add-object!

commit ff0d7cc2c46c415781d341b1696a7196a7f0fb8b
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 09:52:42 2017 +0800

    REFACTOR: centralize numerous used-* into column-uses? helper function

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 45422ca..40d9f68 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -154,6 +154,9 @@ options specified in the Options panels."))
      table subheading-style
      (list heading-cell))))
 
+(define (column-uses? param columns-used)
+  (cdr (assq param columns-used)))
+
 ;; display an account name depending on the options the user has set
 (define (account-namestring account show-account-code? show-account-name? show-account-full-name?)
   ;;# on multi-line splits we can get an empty ('()) account
@@ -172,6 +175,8 @@ options specified in the Options panels."))
                (xaccAccountGetName account))
            ""))))
 
+                                                   
+
 ;; render an account subheading - column-vector determines what is displayed
 (define (render-account-subheading
          split table width subheading-style column-vector)
@@ -180,9 +185,9 @@ options specified in the Options panels."))
                          (gnc:html-markup-anchor
                           (gnc:account-anchor-text account)
                           (account-namestring account
-                                              (used-sort-account-code      column-vector)
+                                              (column-uses? 'sort-account-code      column-vector)
                                               #t
-                                              (used-sort-account-full-name column-vector))))
+                                              (column-uses? 'sort-account-full-name column-vector))))
                         table width subheading-style)))
 
 (define (render-corresponding-account-subheading
@@ -194,9 +199,9 @@ options specified in the Options panels."))
                               ""
                               (gnc:account-anchor-text account))
                           (account-namestring account
-                                              (used-sort-account-code      column-vector)
+                                              (column-uses? 'sort-account-code      column-vector)
                                               #t
-                                              (used-sort-account-full-name column-vector))))
+                                              (column-uses? 'sort-account-full-name column-vector))))
                         table width subheading-style)))
 
 (define (render-week-subheading split table width subheading-style column-vector)
@@ -262,9 +267,9 @@ options specified in the Options panels."))
          table width split total-collector subtotal-style column-vector export?)
   (add-subtotal-row table width
                     (total-string (account-namestring (xaccSplitGetAccount split)
-                                                      (used-sort-account-code      column-vector)
+                                                      (column-uses? 'sort-account-code      column-vector)
                                                       #t
-                                                      (used-sort-account-full-name column-vector)))
+                                                      (column-uses? 'sort-account-full-name column-vector)))
                     total-collector subtotal-style export?))
 
 (define (render-corresponding-account-subtotal
@@ -272,9 +277,9 @@ options specified in the Options panels."))
   (add-subtotal-row table width
                     (total-string (account-namestring (xaccSplitGetAccount
                                                        (xaccSplitGetOtherSplit split))
-                                                      (used-sort-account-code      column-vector)
+                                                      (column-uses? 'sort-account-code      column-vector)
                                                       #t
-                                                      (used-sort-account-full-name column-vector)))
+                                                      (column-uses? 'sort-account-full-name column-vector)))
                     total-collector subtotal-style export?))
 
 (define (render-week-subtotal
@@ -348,95 +353,99 @@ options specified in the Options panels."))
                        ;; likely match a price on the previous day
                        (timespecCanonicalDayTime trans-date))))
 
-    (if (used-date column-vector)
+    (if (column-uses? 'date column-vector)
         (addto! row-contents
                 (if transaction-row?
-                    (gnc:make-html-table-cell/markup "date-cell"
-                                                     (gnc-print-date (gnc-transaction-get-date-posted parent)))
-                    " ")))
-
-    (if (used-reconciled-date column-vector)
+                    (gnc:make-html-table-cell/markup
+                     "date-cell"
+                     (gnc-print-date trans-date))
+                    "")))
+    
+    (if (column-uses? 'reconciled-date column-vector)
         (addto! row-contents
-                (gnc:make-html-table-cell/markup "date-cell"
-                                                 (let ((date (gnc-split-get-date-reconciled split)))
-                                                   (if (equal? date (cons 0 0))
-                                                       " "
-                                                       (gnc-print-date date))))))
-    (if (used-num column-vector)
+                (gnc:make-html-table-cell/markup
+                 "date-cell"
+                 (let ((date (gnc-split-get-date-reconciled split)))
+                   (if (equal? date (cons 0 0))
+                       ""
+                       (gnc-print-date date))))))
+
+    (if (column-uses? 'num column-vector)
         (addto! row-contents
                 (if transaction-row?
-                    (if (qof-book-use-split-action-for-num-field
-                         (gnc-get-current-book))
+                    (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
                         (let* ((num (gnc-get-num-action parent split))
                                (t-num (if (if (gnc:lookup-option options gnc:pagename-display
                                                                  (N_ "Trans Number"))
                                               (opt-val gnc:pagename-display (N_ "Trans Number"))
-                                              #f)
+                                              "")
                                           (gnc-get-num-action parent #f)
                                           ""))
                                (num-string (if (string-null? t-num)
                                                num
                                                (string-append num "/" t-num))))
-                          (gnc:make-html-table-cell/markup "text-cell"
-                                                           num-string))
+                          (gnc:make-html-table-cell/markup "text-cell" num-string))
                         (gnc:make-html-table-cell/markup "text-cell"
                                                          (gnc-get-num-action parent split)))
-                    " ")))
+                    "")))
 
-    (if (used-description column-vector)
+    (if (column-uses? 'description column-vector)
         (addto! row-contents
                 (if transaction-row?
-                    (gnc:make-html-table-cell/markup "text-cell"
-                                                     (xaccTransGetDescription parent))
-                    " ")))
+                    (gnc:make-html-table-cell/markup
+                     "text-cell"
+                     (xaccTransGetDescription parent))
+                    "")))
 
-    (if (used-memo column-vector)
+    (if (column-uses? 'memo column-vector)
         (let ((memo (xaccSplitGetMemo split)))
-          (if (and (equal? memo "") (used-notes column-vector))
+          (if (and (string-null? memo) (column-uses? 'notes column-vector))
               (addto! row-contents (xaccTransGetNotes parent))
               (addto! row-contents memo))))
 
-    (if (or (used-account-name column-vector) (used-account-code column-vector))
+    (if (or (column-uses? 'account-name column-vector) (column-uses? 'account-code column-vector))
         (addto! row-contents (account-namestring account
-                                                 (used-account-code      column-vector)
-                                                 (used-account-name      column-vector)
-                                                 (used-account-full-name column-vector))))
-
-    (if (or (used-other-account-name column-vector) (used-other-account-code column-vector))
-        (addto! row-contents (account-namestring (xaccSplitGetAccount
-                                                  (xaccSplitGetOtherSplit split))
-                                                 (used-other-account-code      column-vector)
-                                                 (used-other-account-name      column-vector)
-                                                 (used-other-account-full-name column-vector))))
-
-    (if (used-shares column-vector)
+                                                 (column-uses? 'account-code      column-vector)
+                                                 (column-uses? 'account-name      column-vector)
+                                                 (column-uses? 'account-full-name column-vector))))
+
+    (if (or (column-uses? 'other-account-name column-vector) (column-uses? 'other-account-code column-vector))
+        (addto! row-contents (account-namestring (xaccSplitGetAccount (xaccSplitGetOtherSplit split))
+                                                 (column-uses? 'other-account-code      column-vector)
+                                                 (column-uses? 'other-account-name      column-vector)
+                                                 (column-uses? 'other-account-full-name column-vector))))
+
+    (if (column-uses? 'shares column-vector)
         (addto! row-contents (xaccSplitGetAmount split)))
+
+    (if (column-uses? 'price column-vector)
+        (addto! row-contents  (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
+                                                     (xaccSplitGetSharePrice split))))
     
-    (if (used-price column-vector)
-        (addto! row-contents
-                (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
-                                (xaccSplitGetSharePrice split))))
-    
-    (if (used-amount-single column-vector)
+    (if (column-uses? 'amount-single column-vector)
         (addto! row-contents
-                (gnc:make-html-table-cell/markup "number-cell"
-                                                 (gnc:html-transaction-anchor parent split-value))))
+                (gnc:make-html-table-cell/markup
+                 "number-cell" (gnc:html-transaction-anchor parent split-value))))
     
-    (if (used-amount-double-positive column-vector)
+    (if (column-uses? 'amount-double column-vector)        
+
         (if (gnc-numeric-positive-p (gnc:gnc-monetary-amount split-value))
-            (addto! row-contents
-                    (gnc:make-html-table-cell/markup "number-cell"
-                                                     (gnc:html-transaction-anchor parent split-value)))
-            (addto! row-contents " ")))
-    
-    (if (used-amount-double-negative column-vector)
-        (if (gnc-numeric-negative-p (gnc:gnc-monetary-amount split-value))
-            (addto! row-contents
-                    (gnc:make-html-table-cell/markup
-                     "number-cell" (gnc:html-transaction-anchor parent (gnc:monetary-neg split-value))))
-            (addto! row-contents " ")))
+
+            (begin
+              (addto! row-contents
+                      (gnc:make-html-table-cell/markup
+                       "number-cell" (gnc:html-transaction-anchor
+                                      parent split-value)))
+              (addto! row-contents ""))
+        
+            (begin
+              (addto! row-contents "")
+              (addto! row-contents
+                      (gnc:make-html-table-cell/markup
+                       "number-cell" (gnc:html-transaction-anchor
+                                      parent (gnc:monetary-neg split-value)))))))
     
-    (if (used-running-balance column-vector)
+    (if (column-uses? 'running-balance column-vector)
         (begin
           ;(gnc:debug "split is " split)
           ;(gnc:debug "split get balance:" (xaccSplitGetBalance split))
@@ -943,92 +952,73 @@ Credit Card, and Income accounts."))))))
 
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
 
-  (define (used-date columns-used) (vector-ref columns-used 0))
-  (define (used-reconciled-date columns-used) (vector-ref columns-used 1))
-  (define (used-num columns-used) (vector-ref columns-used 2))
-  (define (used-description columns-used) (vector-ref columns-used 3))
-  (define (used-account-name columns-used) (vector-ref columns-used 4))
-  (define (used-other-account-name columns-used) (vector-ref columns-used 5))	
-  (define (used-shares columns-used) (vector-ref columns-used 6))	
-  (define (used-price columns-used) (vector-ref columns-used 7))	
-  (define (used-amount-single columns-used) (vector-ref columns-used 8))	
-  (define (used-amount-double-positive columns-used) (vector-ref columns-used 9))	
-  (define (used-amount-double-negative columns-used) (vector-ref columns-used 10))	
-  (define (used-running-balance columns-used) (vector-ref columns-used 11))
-  (define (used-account-full-name columns-used) (vector-ref columns-used 12))
-  (define (used-memo columns-used) (vector-ref columns-used 13))
-  (define (used-account-code columns-used) (vector-ref columns-used 14))
-  (define (used-other-account-code columns-used) (vector-ref columns-used 15))
-  (define (used-other-account-full-name columns-used) (vector-ref columns-used 16))
-  (define (used-sort-account-code columns-used) (vector-ref columns-used 17))
-  (define (used-sort-account-full-name columns-used) (vector-ref columns-used 18))
-  (define (used-notes columns-used) (vector-ref columns-used 19))
-
   (define (build-columns-used)
     (define is-single? (eq? (opt-val gnc:pagename-display optname-detail-level) 'single))
     (define amount-setting (opt-val gnc:pagename-display (N_ "Amount")))
-    (vector
-     (opt-val gnc:pagename-display (N_ "Date"))
-     (opt-val gnc:pagename-display (N_ "Reconciled Date"))
-     (if (gnc:lookup-option options gnc:pagename-display (N_ "Num"))
-         (opt-val gnc:pagename-display (N_ "Num"))
-         (opt-val gnc:pagename-display (N_ "Num/Action")))
-     (opt-val gnc:pagename-display (N_ "Description"))
-     (opt-val gnc:pagename-display (N_ "Account Name"))
-     (and is-single? (opt-val gnc:pagename-display (N_ "Other Account Name")))
-     (opt-val gnc:pagename-display (N_ "Shares"))
-     (opt-val gnc:pagename-display (N_ "Price"))
-     (eq? amount-setting 'single)
-     (eq? amount-setting 'double)
-     (eq? amount-setting 'double)
-     (opt-val gnc:pagename-display (N_ "Running Balance"))
-     (opt-val gnc:pagename-display (N_ "Use Full Account Name"))
-     (opt-val gnc:pagename-display (N_ "Memo"))
-     (opt-val gnc:pagename-display (N_ "Account Code"))
-     (and is-single? (opt-val gnc:pagename-display (N_ "Other Account Code")))
-     (and is-single? (opt-val gnc:pagename-display (N_ "Use Full Other Account Name")))
-     (opt-val pagename-sorting (N_ "Show Account Code"))
-     (opt-val pagename-sorting (N_ "Show Full Account Name"))
-     (opt-val gnc:pagename-display (N_ "Notes"))))
+    (list (cons 'date (opt-val gnc:pagename-display (N_ "Date")))
+          (cons 'reconciled-date (opt-val gnc:pagename-display (N_ "Reconciled Date")))
+          (cons 'num (if (gnc:lookup-option options gnc:pagename-display (N_ "Num"))
+                         (opt-val gnc:pagename-display (N_ "Num"))
+                         (opt-val gnc:pagename-display (N_ "Num/Action"))))
+          (cons 'description (opt-val gnc:pagename-display (N_ "Description")))
+          (cons 'account-name (opt-val gnc:pagename-display (N_ "Account Name")))
+          (cons 'other-account-name (and is-single?
+                                         (opt-val gnc:pagename-display (N_ "Other Account Name"))))
+          (cons 'shares (opt-val gnc:pagename-display (N_ "Shares")))
+          (cons 'price (opt-val gnc:pagename-display (N_ "Price")))
+          (cons 'amount-single (eq? amount-setting 'single))
+          (cons 'amount-double (eq? amount-setting 'double))
+          (cons 'running-balance (opt-val gnc:pagename-display (N_ "Running Balance")))
+          (cons 'account-full-name (opt-val gnc:pagename-display (N_ "Use Full Account Name")))
+          (cons 'memo (opt-val gnc:pagename-display (N_ "Memo")))
+          (cons 'account-code (opt-val gnc:pagename-display (N_ "Account Code")))
+          (cons 'other-account-code (and is-single?
+                                         (opt-val gnc:pagename-display (N_ "Other Account Code"))))
+          (cons 'other-account-full-name (and is-single?
+                                              (opt-val gnc:pagename-display (N_ "Use Full Other Account Name"))))
+          (cons 'sort-account-code (opt-val pagename-sorting (N_ "Show Account Code")))
+          (cons 'sort-account-full-name (opt-val pagename-sorting (N_ "Show Full Account Name")))
+          (cons 'notes (opt-val gnc:pagename-display (N_ "Notes")))))
 
   (define (make-heading-list columns-used)
     (define (add-if pred? item) (if pred? (list item) '()))
     (append
-     (add-if (used-date columns-used)
+     (add-if (column-uses? 'date columns-used)
              (_ "Date"))
-     (add-if (used-reconciled-date columns-used)
+     (add-if (column-uses? 'reconciled-date columns-used)
              (_ "Reconciled Date"))
-     (add-if (used-num columns-used)
+     (add-if (column-uses? 'num columns-used)
              (if (and (qof-book-use-split-action-for-num-field (gnc-get-current-book))
                       (if (gnc:lookup-option options gnc:pagename-display (N_ "Trans Number"))
                           (opt-val gnc:pagename-display (N_ "Trans Number"))
                           #f))
                  (_ "Num/T-Num")
                  (_ "Num")))
-     (add-if (used-description columns-used)
+     (add-if (column-uses? 'description columns-used)
              (_ "Description"))
-     (add-if (used-memo columns-used)
-             (if (used-notes columns-used)
+     (add-if (column-uses? 'memo columns-used)
+             (if (column-uses? 'notes columns-used)
                  (string-append (_ "Memo") "/" (_ "Notes"))
                  (_ "Memo")))
-     (add-if (or (used-account-name columns-used)
-                 (used-account-code columns-used))
+     (add-if (or (column-uses? 'account-name columns-used)
+                 (column-uses? 'account-code columns-used))
              (_ "Account"))
-     (add-if (or (used-other-account-name columns-used)
-                 (used-other-account-code columns-used))
+     (add-if (or (column-uses? 'other-account-name columns-used)
+                 (column-uses? 'other-account-code columns-used))
              (_ "Transfer from/to"))
-     (add-if (used-shares columns-used)
+     (add-if (column-uses? 'shares columns-used)
              (_ "Shares"))
-     (add-if (used-price columns-used)
+     (add-if (column-uses? 'price columns-used)
              (_ "Price"))
-     (add-if (used-amount-single columns-used)
+     (add-if (column-uses? 'amount-single columns-used)
              (_ "Amount"))
      ;; FIXME: Proper labels: what?
-     (add-if (used-amount-double-positive columns-used)
-             (_ "Debit"))
-     (add-if (used-amount-double-negative columns-used)
-             (_ "Credit"))
-     (add-if (used-running-balance columns-used)
+     (if (column-uses? 'amount-double columns-used)
+         (list
+          (_ "Debit")
+          (_ "Credit"))
+         '())
+     (add-if (column-uses? 'running-balance columns-used)
              (_ "Balance"))))
 
   (let ((work-to-do (length splits))

commit 02905fe38f504f5c6e61d20cf8b2512f7ffc442e
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Dec 22 17:40:11 2017 +0800

    REFACTOR: combine 2 key-choice-list into 1
    
    Previously key-choice-list was selected from 2 lists depending
    on use-split-action setting. This commit combines to 1 list
    with suitable (if) clause in the middle.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 6f9d900..45422ca 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -587,108 +587,62 @@ tags within description, notes or memo. ")
      (vector 'both          (N_ "Both") (N_ "Show both (and include void transactions in totals).")))))
 
   ;; Sorting options
-
-  (let ((key-choice-list
-         (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-             (list (vector 'none
-                           (N_ "None")
-                           (N_ "Do not sort."))
-
-                   (vector 'account-name
-                           (N_ "Account Name")
-                           (N_ "Sort & subtotal by account name."))
-
-                   (vector 'account-code
-                           (N_ "Account Code")
-                           (N_ "Sort & subtotal by account code."))
-
-                   (vector 'date
-                           (N_ "Date")
-                           (N_ "Sort by date."))
-
-                   (vector 'reconciled-date
-                           (N_ "Reconciled Date")
-                           (N_ "Sort by the Reconciled Date."))
-
-                   (vector 'register-order
-                           (N_ "Register Order")
-                           (N_ "Sort as in the register."))
-
-                   (vector 'corresponding-acc-name
-                           (N_ "Other Account Name")
-                           (N_ "Sort by account transferred from/to's name."))
-
-                   (vector 'corresponding-acc-code
-                           (N_ "Other Account Code")
-                           (N_ "Sort by account transferred from/to's code."))
-
-                   (vector 'amount
-                           (N_ "Amount")
-                           (N_ "Sort by amount."))
-
-                   (vector 'description
-                           (N_ "Description")
-                           (N_ "Sort by description."))
-
-                   (vector 'number
-                           (N_ "Number/Action")
-                           (N_ "Sort by check number/action."))
   
-                   (vector 't-number
-                           (N_ "Transaction Number")
-                           (N_ "Sort by transaction number."))
-
-                   (vector 'memo
-                           (N_ "Memo")
-                           (N_ "Sort by memo.")))
-
-             (list (vector 'none
-                           (N_ "None")
-                           (N_ "Do not sort."))
-
-                   (vector 'account-name
-                           (N_ "Account Name")
-                           (N_ "Sort & subtotal by account name."))
-
-                   (vector 'account-code
-                           (N_ "Account Code")
-                           (N_ "Sort & subtotal by account code."))
-
-                   (vector 'date
-                           (N_ "Date")
-                           (N_ "Sort by date."))
-
-                   (vector 'reconciled-date
-                           (N_ "Reconciled Date")
-                           (N_ "Sort by the Reconciled Date."))
-
-                   (vector 'register-order
-                           (N_ "Register Order")
-                           (N_ "Sort as in the register."))
-
-                   (vector 'corresponding-acc-name
-                           (N_ "Other Account Name")
-                           (N_ "Sort by account transferred from/to's name."))
-
-                   (vector 'corresponding-acc-code
-                           (N_ "Other Account Code")
-                           (N_ "Sort by account transferred from/to's code."))
-
-                   (vector 'amount
-                           (N_ "Amount")
-                           (N_ "Sort by amount."))
-
-                   (vector 'description
-                           (N_ "Description")
-                           (N_ "Sort by description."))
-
-                   (vector 'number
-                           (N_ "Number")
-                           (N_ "Sort by check/transaction number."))
-
-                   (vector 'memo
-                           (N_ "Memo")
-                           (N_ "Sort by memo.")))))
+  (let ((key-choice-list
+         (append
+          (list (vector 'none
+                        (N_ "None")
+                        (N_ "Do not sort."))
+
+                (vector 'account-name
+                        (N_ "Account Name")
+                        (N_ "Sort & subtotal by account name."))
+
+                (vector 'account-code
+                        (N_ "Account Code")
+                        (N_ "Sort & subtotal by account code."))
+
+                (vector 'date
+                        (N_ "Date")
+                        (N_ "Sort by date."))
+
+                (vector 'reconciled-date
+                        (N_ "Reconciled Date")
+                        (N_ "Sort by the Reconciled Date."))
+
+                (vector 'register-order
+                        (N_ "Register Order")
+                        (N_ "Sort as in the register."))
+
+                (vector 'corresponding-acc-name
+                        (N_ "Other Account Name")
+                        (N_ "Sort by account transferred from/to's name."))
+
+                (vector 'corresponding-acc-code
+                        (N_ "Other Account Code")
+                        (N_ "Sort by account transferred from/to's code."))
+
+                (vector 'amount
+                        (N_ "Amount")
+                        (N_ "Sort by amount."))
+
+                (vector 'description
+                        (N_ "Description")
+                        (N_ "Sort by description."))
+
+                (vector 'number
+                        (N_ "Number/Action")
+                        (N_ "Sort by check number/action.")))
+          (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
+              (list
+               (vector 't-number
+                       (N_ "Transaction Number")
+                       (N_ "Sort by transaction number.")))
+              '())
+          (list
+           (vector 'memo
+                   (N_ "Memo")
+                   (N_ "Sort by memo.")))))
 
         (ascending-choice-list
          (list

commit ee01038ee5b4c9932ca923f66b396d46334de023
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Dec 22 17:39:38 2017 +0800

    REFACTOR: move some funcs to refactor later

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index d0e5b47..6f9d900 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -108,18 +108,6 @@ options specified in the Options panels."))
                            corresponding-acc-name
                            corresponding-acc-code))
 
-(define (split-account-fullname-same? a b)
-  (= (xaccSplitCompareAccountFullNames a b) 0))
-
-(define (split-account-code-same? a b)
-  (= (xaccSplitCompareAccountCodes a b) 0))
-
-(define (split-otheracct-fullname-same? a b)
-  (= (xaccSplitCompareOtherAccountFullNames a b) 0))
-
-(define (split-otheracct-code-same? a b)
-  (= (xaccSplitCompareOtherAccountCodes a b) 0))
-
 (define (timepair-same-year tp-a tp-b)
   (= (gnc:timepair-get-year tp-a)
      (gnc:timepair-get-year tp-b)))
@@ -163,8 +151,7 @@ options specified in the Options panels."))
   (let ((heading-cell (gnc:make-html-table-cell data)))
     (gnc:html-table-cell-set-colspan! heading-cell width)
     (gnc:html-table-append-row/markup!
-     table
-     subheading-style
+     table subheading-style
      (list heading-cell))))
 
 ;; display an account name depending on the options the user has set
@@ -242,8 +229,7 @@ options specified in the Options panels."))
 
 (define (add-subtotal-row table width subtotal-string subtotal-collector
                           subtotal-style export?)
-  (let ((currency-totals (subtotal-collector 'format gnc:make-gnc-monetary #f))
-        (blanks (gnc:make-html-table-cell/size 1 (- width 1) #f)))
+  (let ((currency-totals (subtotal-collector 'format gnc:make-gnc-monetary #f)))
     (gnc:html-table-append-row/markup!
      table
      subtotal-style
@@ -265,7 +251,7 @@ options specified in the Options panels."))
                  (append!
                   (if export?
                       (gnc:html-make-empty-cells (- width 1))
-                      (list blanks))
+                      (list (gnc:make-html-table-cell/size 1 (- width 1) #f)))
                   (list (gnc:make-html-table-cell/markup
                          "total-number-cell" currency)))))
               (cdr currency-totals))))
@@ -330,168 +316,6 @@ options specified in the Options panels."))
                     (_ "Grand Total")
                     total-collector def:grand-total-style export?))
 
-(define account-types-to-reverse-assoc-list
-  (list (cons 'none '())
-        (cons 'income-expense
-              (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))
-        (cons 'credit-accounts
-              (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE ACCT-TYPE-EQUITY
-                    ACCT-TYPE-CREDIT ACCT-TYPE-INCOME))))
-
-(define (used-date columns-used)
-  (vector-ref columns-used 0))
-(define (used-reconciled-date columns-used)
-  (vector-ref columns-used 1))
-(define (used-num columns-used)
-  (vector-ref columns-used 2))
-(define (used-description columns-used)
-  (vector-ref columns-used 3))
-(define (used-account-name columns-used)
-  (vector-ref columns-used 4))
-(define (used-other-account-name columns-used)
-  (vector-ref columns-used 5))
-(define (used-shares columns-used)
-  (vector-ref columns-used 6))
-(define (used-price columns-used)
-  (vector-ref columns-used 7))
-(define (used-amount-single columns-used)
-  (vector-ref columns-used 8))
-(define (used-amount-double-positive columns-used)
-  (vector-ref columns-used 9))
-(define (used-amount-double-negative columns-used)
-  (vector-ref columns-used 10))
-(define (used-running-balance columns-used)
-  (vector-ref columns-used 11))
-(define (used-account-full-name columns-used)
-  (vector-ref columns-used 12))
-(define (used-memo columns-used)
-  (vector-ref columns-used 13))
-(define (used-account-code columns-used)
-  (vector-ref columns-used 14))
-(define (used-other-account-code columns-used)
-  (vector-ref columns-used 15))
-(define (used-other-account-full-name columns-used)
-  (vector-ref columns-used 16))
-(define (used-sort-account-code columns-used)
-  (vector-ref columns-used 17))
-(define (used-sort-account-full-name columns-used)
-  (vector-ref columns-used 18))
-(define (used-notes columns-used)
-  (vector-ref columns-used 19))
-
-(define columns-used-size 20)
-
-(define (num-columns-required columns-used)
-  (do ((i 0 (+ i 1))
-       (col-req 0 col-req))
-    ((>= i columns-used-size) col-req)
-    ; If column toggle is true, increase column count. But attention:
-    ; some toggles only change the meaning of another toggle. Don't count these modifier toggles
-    (if (and (not (= i 12)) ; Skip Account Full Name toggle - modifies Account Name column
-             (not (= i 16)) ; Skip Other Account Full Name toggle - modifies Other Account Name column
-             (not (= i 17)) ; Skip Sort Account Code - modifies Account Name subheading
-             (not (= i 18)) ; Skip Sort Account Full Name - modifies Account Name subheading
-             (not (= i 19)) ; Skip Note toggle - modifies Memo column
-             (vector-ref columns-used i))
-        (set! col-req (+ col-req 1)))
-    ; Account Code and Account Name share one column so if both were ticked the
-    ; the check above would have set up one column too much. The check below
-    ; will compensate these again.
-    (if (or (and (= i 14) (vector-ref columns-used 14) (vector-ref columns-used 4)) ; Account Code and Name
-            (and (= i 15) (vector-ref columns-used 15) (vector-ref columns-used 5))) ; Other Account Code and Name
-        (set! col-req (- col-req 1)))))
-
-(define (build-column-used options)
-  (define (opt-val section name) (gnc:option-value  (gnc:lookup-option options section name)))
-  (let ((column-list (make-vector columns-used-size #f))
-        (is-single? (eq? (opt-val gnc:pagename-display optname-detail-level) 'single)))
-    (if (opt-val gnc:pagename-display (N_ "Date"))
-        (vector-set! column-list 0 #t))
-    (if (opt-val gnc:pagename-display (N_ "Reconciled Date"))
-        (vector-set! column-list 1 #t))
-    (if (if (gnc:lookup-option options gnc:pagename-display (N_ "Num"))
-            (opt-val gnc:pagename-display (N_ "Num"))
-            (opt-val gnc:pagename-display (N_ "Num/Action")))
-        (vector-set! column-list 2 #t))
-    (if (opt-val gnc:pagename-display (N_ "Description"))
-        (vector-set! column-list 3 #t))
-    (if (opt-val gnc:pagename-display (N_ "Account Name"))
-        (vector-set! column-list 4 #t))
-    (if (and is-single? (opt-val gnc:pagename-display (N_ "Other Account Name")))
-        (vector-set! column-list 5 #t))
-    (if (opt-val gnc:pagename-display (N_ "Shares"))
-        (vector-set! column-list 6 #t))
-    (if (opt-val gnc:pagename-display (N_ "Price"))
-        (vector-set! column-list 7 #t))
-    (let ((amount-setting (opt-val gnc:pagename-display (N_ "Amount"))))
-      (if (eq? amount-setting 'single)
-          (vector-set! column-list 8 #t))
-      (if (eq? amount-setting 'double)
-          (begin (vector-set! column-list 9 #t)
-                 (vector-set! column-list 10 #t))))
-    (if (opt-val gnc:pagename-display (N_ "Running Balance"))
-        (vector-set! column-list 11 #t))
-    (if (opt-val gnc:pagename-display  (N_ "Use Full Account Name"))
-        (vector-set! column-list 12 #t))
-    (if (opt-val gnc:pagename-display (N_ "Memo"))
-        (vector-set! column-list 13 #t))
-    (if (opt-val gnc:pagename-display (N_ "Account Code"))
-        (vector-set! column-list 14 #t))
-    (if (and is-single? (opt-val gnc:pagename-display (N_ "Other Account Code")))
-        (vector-set! column-list 15 #t))
-    (if (and is-single? (opt-val gnc:pagename-display (N_ "Use Full Other Account Name")))
-        (vector-set! column-list 16 #t))
-    (if (opt-val pagename-sorting (N_ "Show Account Code"))
-        (vector-set! column-list 17 #t))
-    (if (opt-val pagename-sorting (N_ "Show Full Account Name"))
-        (vector-set! column-list 18 #t))
-    (if (opt-val gnc:pagename-display (N_ "Notes"))
-        (vector-set! column-list 19 #t))
-    column-list))
-
-(define (make-heading-list column-vector options)
-
-  (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
-
-  (let ((heading-list '()))
-    (if (used-date column-vector)
-        (addto! heading-list (_ "Date")))
-    (if (used-reconciled-date column-vector)
-        (addto! heading-list (_ "Reconciled Date")))
-    (if (used-num column-vector)
-        (addto! heading-list (if (and (qof-book-use-split-action-for-num-field
-                                       (gnc-get-current-book))
-                                      (if (gnc:lookup-option options
-                                                             gnc:pagename-display
-                                                             (N_ "Trans Number"))
-                                          (opt-val gnc:pagename-display (N_ "Trans Number"))
-                                          #f))
-                                 (_ "Num/T-Num")
-                                 (_ "Num"))))
-    (if (used-description column-vector)
-        (addto! heading-list (_ "Description")))
-    (if (used-memo column-vector)
-        (if (used-notes column-vector)
-            (addto! heading-list (string-append (_ "Memo") "/" (_ "Notes")))
-            (addto! heading-list (_ "Memo"))))
-    (if (or (used-account-name column-vector) (used-account-code column-vector))
-        (addto! heading-list (_ "Account")))
-    (if (or (used-other-account-name column-vector) (used-other-account-code column-vector))
-        (addto! heading-list (_ "Transfer from/to")))
-    (if (used-shares column-vector)
-        (addto! heading-list (_ "Shares")))
-    (if (used-price column-vector)
-        (addto! heading-list (_ "Price")))
-    (if (used-amount-single column-vector)
-        (addto! heading-list (_ "Amount")))
-    ;; FIXME: Proper labels: what?
-    (if (used-amount-double-positive column-vector)
-        (addto! heading-list (_ "Debit")))
-    (if (used-amount-double-negative column-vector)
-        (addto! heading-list (_ "Credit")))
-    (if (used-running-balance column-vector)
-        (addto! heading-list (_ "Balance")))
-    (reverse heading-list)))
 
 (define (add-split-row table split column-vector options
                        row-style account-types-to-reverse transaction-row?)
@@ -1138,15 +962,15 @@ tags within description, notes or memo. ")
       gnc:pagename-display (N_ "Sign Reverses")
       "p" (N_ "Reverse amount display for certain account types.")
       'credit-accounts
-      (list        (vector 'none
-                           (N_ "None")
-                           (N_ "Don't change any displayed amounts."))
-                   (vector 'income-expense
-                           (N_ "Income and Expense")
-                           (N_ "Reverse amount display for Income and Expense Accounts."))
-                   (vector 'credit-accounts
-                           (N_ "Credit Accounts")
-                           (N_ "Reverse amount display for Liability, Payable, Equity, \
+      (list (vector 'none
+                    (N_ "None")
+                    (N_ "Don't change any displayed amounts."))
+            (vector 'income-expense
+                    (N_ "Income and Expense")
+                    (N_ "Reverse amount display for Income and Expense Accounts."))
+            (vector 'credit-accounts
+                    (N_ "Credit Accounts")
+                    (N_ "Reverse amount display for Liability, Payable, Equity, \
 Credit Card, and Income accounts."))))))
 
   (gnc:options-set-default-section options gnc:pagename-general)
@@ -1165,26 +989,96 @@ Credit Card, and Income accounts."))))))
 
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
 
+  (define (used-date columns-used) (vector-ref columns-used 0))
+  (define (used-reconciled-date columns-used) (vector-ref columns-used 1))
+  (define (used-num columns-used) (vector-ref columns-used 2))
+  (define (used-description columns-used) (vector-ref columns-used 3))
+  (define (used-account-name columns-used) (vector-ref columns-used 4))
+  (define (used-other-account-name columns-used) (vector-ref columns-used 5))	
+  (define (used-shares columns-used) (vector-ref columns-used 6))	
+  (define (used-price columns-used) (vector-ref columns-used 7))	
+  (define (used-amount-single columns-used) (vector-ref columns-used 8))	
+  (define (used-amount-double-positive columns-used) (vector-ref columns-used 9))	
+  (define (used-amount-double-negative columns-used) (vector-ref columns-used 10))	
+  (define (used-running-balance columns-used) (vector-ref columns-used 11))
+  (define (used-account-full-name columns-used) (vector-ref columns-used 12))
+  (define (used-memo columns-used) (vector-ref columns-used 13))
+  (define (used-account-code columns-used) (vector-ref columns-used 14))
+  (define (used-other-account-code columns-used) (vector-ref columns-used 15))
+  (define (used-other-account-full-name columns-used) (vector-ref columns-used 16))
+  (define (used-sort-account-code columns-used) (vector-ref columns-used 17))
+  (define (used-sort-account-full-name columns-used) (vector-ref columns-used 18))
+  (define (used-notes columns-used) (vector-ref columns-used 19))
+
+  (define (build-columns-used)
+    (define is-single? (eq? (opt-val gnc:pagename-display optname-detail-level) 'single))
+    (define amount-setting (opt-val gnc:pagename-display (N_ "Amount")))
+    (vector
+     (opt-val gnc:pagename-display (N_ "Date"))
+     (opt-val gnc:pagename-display (N_ "Reconciled Date"))
+     (if (gnc:lookup-option options gnc:pagename-display (N_ "Num"))
+         (opt-val gnc:pagename-display (N_ "Num"))
+         (opt-val gnc:pagename-display (N_ "Num/Action")))
+     (opt-val gnc:pagename-display (N_ "Description"))
+     (opt-val gnc:pagename-display (N_ "Account Name"))
+     (and is-single? (opt-val gnc:pagename-display (N_ "Other Account Name")))
+     (opt-val gnc:pagename-display (N_ "Shares"))
+     (opt-val gnc:pagename-display (N_ "Price"))
+     (eq? amount-setting 'single)
+     (eq? amount-setting 'double)
+     (eq? amount-setting 'double)
+     (opt-val gnc:pagename-display (N_ "Running Balance"))
+     (opt-val gnc:pagename-display (N_ "Use Full Account Name"))
+     (opt-val gnc:pagename-display (N_ "Memo"))
+     (opt-val gnc:pagename-display (N_ "Account Code"))
+     (and is-single? (opt-val gnc:pagename-display (N_ "Other Account Code")))
+     (and is-single? (opt-val gnc:pagename-display (N_ "Use Full Other Account Name")))
+     (opt-val pagename-sorting (N_ "Show Account Code"))
+     (opt-val pagename-sorting (N_ "Show Full Account Name"))
+     (opt-val gnc:pagename-display (N_ "Notes"))))
+
+  (define (make-heading-list columns-used)
+    (define (add-if pred? item) (if pred? (list item) '()))
+    (append
+     (add-if (used-date columns-used)
+             (_ "Date"))
+     (add-if (used-reconciled-date columns-used)
+             (_ "Reconciled Date"))
+     (add-if (used-num columns-used)
+             (if (and (qof-book-use-split-action-for-num-field (gnc-get-current-book))
+                      (if (gnc:lookup-option options gnc:pagename-display (N_ "Trans Number"))
+                          (opt-val gnc:pagename-display (N_ "Trans Number"))
+                          #f))
+                 (_ "Num/T-Num")
+                 (_ "Num")))
+     (add-if (used-description columns-used)
+             (_ "Description"))
+     (add-if (used-memo columns-used)
+             (if (used-notes columns-used)
+                 (string-append (_ "Memo") "/" (_ "Notes"))
+                 (_ "Memo")))
+     (add-if (or (used-account-name columns-used)
+                 (used-account-code columns-used))
+             (_ "Account"))
+     (add-if (or (used-other-account-name columns-used)
+                 (used-other-account-code columns-used))
+             (_ "Transfer from/to"))
+     (add-if (used-shares columns-used)
+             (_ "Shares"))
+     (add-if (used-price columns-used)
+             (_ "Price"))
+     (add-if (used-amount-single columns-used)
+             (_ "Amount"))
+     ;; FIXME: Proper labels: what?
+     (add-if (used-amount-double-positive columns-used)
+             (_ "Debit"))
+     (add-if (used-amount-double-negative columns-used)
+             (_ "Credit"))
+     (add-if (used-running-balance columns-used)
+             (_ "Balance"))))
+
   (let ((work-to-do (length splits))
-        (work-done 0)
-        (used-columns (build-column-used options))
-        (account-types-to-reverse
-         (cdr (assq
-               (opt-val gnc:pagename-display (N_ "Sign Reverses"))
-               account-types-to-reverse-assoc-list)))
-        (is-multiline? (eq? (opt-val gnc:pagename-display optname-detail-level) 'multi-line))
-        (export? (opt-val gnc:pagename-general optname-table-export)))
-
-    (define (add-other-split-rows
-             split table used-columns row-style account-types-to-reverse)
-
-      (let* ((txn (xaccSplitGetParent split))
-             (other-splits (delete split (xaccTransGetSplitList txn))))
-
-        (for-each (lambda (s)
-                    (add-split-row table s used-columns options
-                                   row-style account-types-to-reverse #f))
-                  other-splits)))
+        (work-done 0))
 
     (define (do-rows-with-subtotals splits
                                     table
@@ -1222,24 +1116,34 @@ Credit Card, and Income accounts."))))))
                 (render-grand-total table width total-collector export?)))
 
           (let* ((current (car splits))
-                 (current-row-style (if multi-rows? def:normal-row-style
-                                        (if odd-row? def:normal-row-style
-                                            def:alternate-row-style)))
                  (rest (cdr splits))
                  (next (if (null? rest) #f
-                           (car rest)))
-                 (split-value (add-split-row
-                               table
-                               current
-                               used-columns
-                               options
-                               current-row-style
-                               account-types-to-reverse
-                               #t)))
+                           (car rest))))
+
+            (define split-value (add-split-row
+                                 table
+                                 current
+                                 used-columns
+                                 options
+                                 (if multi-rows? def:normal-row-style
+                                     (if odd-row?
+                                         def:normal-row-style
+                                         def:alternate-row-style))
+                                 account-types-to-reverse
+                                 #t))
+
             (if multi-rows?
-                (add-other-split-rows
-                 current table used-columns def:alternate-row-style
-                 account-types-to-reverse))
+
+                (for-each (lambda (othersplits)
+                            (add-split-row table
+                                           othersplits
+                                           used-columns
+                                           options
+                                           def:alternate-row-style
+                                           account-types-to-reverse
+                                           #f))
+                          (delete current (xaccTransGetSplitList
+                                           (xaccSplitGetParent current)))))
 
             (primary-subtotal-collector 'add
                                         (gnc:gnc-monetary-commodity split-value) 
@@ -1294,8 +1198,7 @@ Credit Card, and Income accounts."))))))
                 (if (and secondary-subtotal-pred
                          (or (not next)
                              (and next
-                                  (not (secondary-subtotal-pred
-                                        current next)))))
+                                  (not (secondary-subtotal-pred current next)))))
 
                     (begin (secondary-subtotal-renderer
                             table width current
@@ -1328,10 +1231,20 @@ Credit Card, and Income accounts."))))))
                                     total-collector))))
 
     (let* ((table (gnc:make-html-table))
-           (width (num-columns-required used-columns)))
-
-      (gnc:html-table-set-col-headers! table
-       (make-heading-list used-columns options))
+           (used-columns (build-columns-used))
+           (headings (make-heading-list used-columns))
+           (width (length headings))
+           (account-types-to-reverse
+            (cdr (assq (opt-val gnc:pagename-display (N_ "Sign Reverses"))
+                       (list (cons 'none '())
+                             (cons 'income-expense (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))
+                             (cons 'credit-accounts  (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE
+                                                           ACCT-TYPE-EQUITY ACCT-TYPE-CREDIT
+                                                           ACCT-TYPE-INCOME))))))
+           (is-multiline? (eq? (opt-val gnc:pagename-display optname-detail-level) 'multi-line))
+           (export? (opt-val gnc:pagename-general optname-table-export)))
+
+      (gnc:html-table-set-col-headers! table headings)
 
       (if primary-subheading-renderer
           (primary-subheading-renderer
@@ -1369,47 +1282,45 @@ Credit Card, and Income accounts."))))))
     ;; subtotal functions. Each entry: (cons
     ;; 'sorting-key-option-value (vector 'query-sorting-key
     ;; subtotal-function subtotal-renderer))
-    ;;  (let* ((used-columns (build-column-used options))) ;; tpo: gives unbound variable options?
-    (let* ((used-columns (build-column-used (gnc:report-options report-obj))))
-      (list (cons 'account-name  (vector
-                                  (list SPLIT-ACCT-FULLNAME)
-                                  split-account-fullname-same?
-                                  render-account-subheading
-                                  render-account-subtotal))
-            (cons 'account-code  (vector
-                                  (list SPLIT-ACCOUNT ACCOUNT-CODE-)
-                                  split-account-code-same?
-                                  render-account-subheading
-                                  render-account-subtotal))
-            (cons 'date          (vector
-                                  (list SPLIT-TRANS TRANS-DATE-POSTED)
+    (list (cons 'account-name  (vector
+                                (list SPLIT-ACCT-FULLNAME)
+                                (lambda (a b) (zero? (xaccSplitCompareAccountFullNames a b))) 
+                                render-account-subheading
+                                render-account-subtotal))
+          (cons 'account-code  (vector
+                                (list SPLIT-ACCOUNT ACCOUNT-CODE-)
+                                (lambda (a b) (zero? (xaccSplitCompareAccountCodes a b)))
+                                render-account-subheading
+                                render-account-subtotal))
+          (cons 'date          (vector
+                                (list SPLIT-TRANS TRANS-DATE-POSTED)
+                                #f #f #f))
+          (cons 'reconciled-date (vector
+                                  (list SPLIT-DATE-RECONCILED)
                                   #f #f #f))
-            (cons 'reconciled-date (vector
-                                    (list SPLIT-DATE-RECONCILED)
-                                    #f #f #f))
-            (cons 'register-order (vector
-                                   (list QUERY-DEFAULT-SORT)
-                                   #f #f #f))
-            (cons 'corresponding-acc-name
-                  (vector
-                   (list SPLIT-CORR-ACCT-NAME)
-                   split-otheracct-fullname-same?
-                   render-corresponding-account-subheading
-                   render-corresponding-account-subtotal))
-            (cons 'corresponding-acc-code
-                  (vector
-                   (list SPLIT-CORR-ACCT-CODE)
-                   split-otheracct-code-same?
-                   render-corresponding-account-subheading
-                   render-corresponding-account-subtotal))
-            (cons 'amount        (vector (list SPLIT-VALUE) #f #f #f))
-            (cons 'description   (vector (list SPLIT-TRANS TRANS-DESCRIPTION) #f #f #f))
-            (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-                (cons 'number    (vector (list SPLIT-ACTION) #f #f #f))
-                (cons 'number    (vector (list SPLIT-TRANS TRANS-NUM) #f #f #f)))
-            (cons 't-number      (vector (list SPLIT-TRANS TRANS-NUM) #f #f #f))
-            (cons 'memo          (vector (list SPLIT-MEMO) #f #f #f))
-            (cons 'none          (vector '() #f #f #f)))))
+          (cons 'register-order (vector
+                                 (list QUERY-DEFAULT-SORT)
+                                 #f #f #f))
+          (cons 'corresponding-acc-name
+                (vector
+                 (list SPLIT-CORR-ACCT-NAME)
+                 (lambda (a b) (zero? (xaccSplitCompareOtherAccountFullNames a b))) 
+                 render-corresponding-account-subheading
+                 render-corresponding-account-subtotal))
+          (cons 'corresponding-acc-code
+                (vector
+                 (list SPLIT-CORR-ACCT-CODE)
+                 (lambda (a b) (zero? (xaccSplitCompareOtherAccountCodes a b)))
+                 render-corresponding-account-subheading
+                 render-corresponding-account-subtotal))
+          (cons 'amount        (vector (list SPLIT-VALUE) #f #f #f))
+          (cons 'description   (vector (list SPLIT-TRANS TRANS-DESCRIPTION) #f #f #f))
+          (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
+              (cons 'number    (vector (list SPLIT-ACTION) #f #f #f))
+              (cons 'number    (vector (list SPLIT-TRANS TRANS-NUM) #f #f #f)))
+          (cons 't-number      (vector (list SPLIT-TRANS TRANS-NUM) #f #f #f))
+          (cons 'memo          (vector (list SPLIT-MEMO) #f #f #f))
+          (cons 'none          (vector '() #f #f #f))))
 
   (define date-comp-funcs-assoc-list
     ;; Extra list for date option. Each entry: (cons
@@ -1422,8 +1333,7 @@ Credit Card, and Income accounts."))))))
      (cons 'quarterly (vector split-same-quarter? render-quarter-subheading render-quarter-subtotal))
      (cons 'yearly (vector split-same-year? render-year-subheading render-year-subtotal))))
 
-  (define (get-subtotalstuff-helper
-           name-sortkey name-subtotal name-date-subtotal
+  (define (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal
            comp-index date-index)
     ;; The value of the sorting-key multichoice option.
     (let ((sortkey (opt-val pagename-sorting name-sortkey)))
@@ -1441,32 +1351,19 @@ Credit Card, and Income accounts."))))))
           ;; appropriate funcs in the assoc-list.
           (and (member sortkey subtotal-enabled)
                (and (opt-val pagename-sorting name-subtotal)
-                    (vector-ref
-                     (cdr (assq sortkey comp-funcs-assoc-list))
-                     comp-index))))))
+                    (vector-ref (cdr (assq sortkey comp-funcs-assoc-list)) comp-index))))))
 
   (define (get-query-sortkey sort-option-value)
-    (vector-ref
-     (cdr (assq sort-option-value comp-funcs-assoc-list))
-     0))
-
-  (define (get-subtotal-pred
-           name-sortkey name-subtotal name-date-subtotal)
-    (get-subtotalstuff-helper
-     name-sortkey name-subtotal name-date-subtotal
-     1 0))
-
-  (define (get-subheading-renderer
-           name-sortkey name-subtotal name-date-subtotal)
-    (get-subtotalstuff-helper
-     name-sortkey name-subtotal name-date-subtotal
-     2 1))
-
-  (define (get-subtotal-renderer
-           name-sortkey name-subtotal name-date-subtotal)
-    (get-subtotalstuff-helper
-     name-sortkey name-subtotal name-date-subtotal
-     3 2))
+    (vector-ref  (cdr (assq sort-option-value comp-funcs-assoc-list)) 0))
+
+  (define (get-subtotal-pred name-sortkey name-subtotal name-date-subtotal)
+    (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal 1 0))
+
+  (define (get-subheading-renderer name-sortkey name-subtotal name-date-subtotal)
+    (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal 2 1))
+
+  (define (get-subtotal-renderer name-sortkey name-subtotal name-date-subtotal)
+    (get-subtotalstuff-helper name-sortkey name-subtotal name-date-subtotal 3 2))
 
   (define (is-filter-member split account-list)
     (let* ((txn (xaccSplitGetParent split))
@@ -1489,11 +1386,11 @@ Credit Card, and Income accounts."))))))
   (gnc:report-starting reportname)
 
   (let* ((document (gnc:make-html-document))
-         (c_account_0 (opt-val gnc:pagename-accounts optname-accounts))
          (account-matcher (opt-val pagename-filter optname-account-matcher))
          (account-matcher-regexp (if (opt-val pagename-filter optname-account-matcher-regex)
                                      (make-regexp account-matcher)
                                      #f))
+         (c_account_0 (opt-val gnc:pagename-accounts optname-accounts))
          (c_account_1 (filter
                        (lambda (acc)
                          (if account-matcher-regexp
@@ -1544,7 +1441,6 @@ Credit Card, and Income accounts."))))))
 
         (begin
 
-          ;;(gnc:warn "query is:" query)
           (qof-query-set-book query (gnc-get-current-book))
           (xaccQueryAddAccountMatch query c_account_1 QOF-GUID-MATCH-ANY QOF-QUERY-AND)
           (xaccQueryAddDateMatchTS query #t begindate #t enddate QOF-QUERY-AND)

commit 7127df58dabda5338d81895d450db25dbb36abff
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 14:12:13 2017 +0800

    REFACTOR: always run qof-query-destroyer
    
    Formerly the qof-query-destroyer is only called upon completion
    of a successful report. This commit moves this destroyer to be
    nearer the query call, thereby the destroyer is always called.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index a85d6eb..d0e5b47 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1562,7 +1562,7 @@ Credit Card, and Income accounts."))))))
             (else #f))
           (set! splits (qof-query-run query))
 
-          ;;(gnc:warn "Splits in trep-renderer:" splits)
+          (qof-query-destroy query)
 
           ;; Combined Filter:
           ;; - include/exclude splits to/from selected accounts
@@ -1626,9 +1626,7 @@ Credit Card, and Income accounts."))))))
                            (gnc-print-date begindate)
                            (gnc-print-date enddate)))))
 
-                (gnc:html-document-add-object! document table)
-
-                (qof-query-destroy query)))))
+                (gnc:html-document-add-object! document table)))))
 
     (gnc:report-finished)
 

commit fe757dbe6a924589b484961cfbbe632eae51cf57
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Dec 9 22:28:00 2017 +0800

    ENH: Optimise Transaction Matcher filter
    
    This commit will trigger the transaction matcher only if the search string is not empty. Will speed up filtering.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 686ca8e..a85d6eb 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -1577,7 +1577,8 @@ Credit Card, and Income accounts."))))))
                                                (string-contains str transaction-matcher)))))
                             (and (if (eq? filter-mode 'include) (is-filter-member split c_account_2) #t)
                                  (if (eq? filter-mode 'exclude) (not (is-filter-member split c_account_2)) #t)
-                                 (or (match? (xaccTransGetDescription trans))
+                                 (or (string-null? transaction-matcher) ; null-string = ignore filters
+                                     (match? (xaccTransGetDescription trans))
                                      (match? (xaccTransGetNotes trans))
                                      (match? (xaccSplitGetMemo split)))
                                  (or (not reconcile-status-filter) ; #f = ignore next filter

commit e5a7660ac0b14062cc016f6f6dfe87510c6c3863
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 14:39:33 2017 +0800

    ENH: add reconciled status filtering

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 894f267..686ca8e 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -695,6 +695,16 @@ enable full POSIX regular expressions capabilities. '#work|#family' will match b
 tags within description, notes or memo. ")
     #f))
 
+  (gnc:register-trep-option
+   (gnc:make-multichoice-option
+    pagename-filter optname-reconcile-status
+    "j1" (N_ "Filter by reconcile status.")
+    #f
+    (list (vector #f      (N_ "All")           (N_ "Show All Transactions"))
+          (vector '(#\n)  (N_ "Unreconciled")  (N_ "Unreconciled only"))
+          (vector '(#\c)  (N_ "Cleared")       (N_ "Cleared only"))
+          (vector '(#\y)  (N_ "Reconciled")    (N_ "Reconciled only")))))
+
   ;; Accounts options
 
   ;; account to do report on
@@ -1554,9 +1564,10 @@ Credit Card, and Income accounts."))))))
 
           ;;(gnc:warn "Splits in trep-renderer:" splits)
 
-          ; Combined Filter:
-          ; - include/exclude splits to/from selected accounts
-          ; - substring/regex matcher for Transaction Description/Notes/Memo
+          ;; Combined Filter:
+          ;; - include/exclude splits to/from selected accounts
+          ;; - substring/regex matcher for Transaction Description/Notes/Memo
+          ;; - by reconcile status
           (set! splits (filter
                         (lambda (split)
                           (let* ((trans (xaccSplitGetParent split))
@@ -1568,7 +1579,9 @@ Credit Card, and Income accounts."))))))
                                  (if (eq? filter-mode 'exclude) (not (is-filter-member split c_account_2)) #t)
                                  (or (match? (xaccTransGetDescription trans))
                                      (match? (xaccTransGetNotes trans))
-                                     (match? (xaccSplitGetMemo split))))))
+                                     (match? (xaccSplitGetMemo split)))
+                                 (or (not reconcile-status-filter) ; #f = ignore next filter
+                                     (member (xaccSplitGetReconcile split) reconcile-status-filter)))))
                         splits))
 
           (if (null? splits)

commit 8990553e2eaf453f454480eb54f370f98f4849ac
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 07:10:03 2017 +0800

    ENH: Move Account matcher to Filter tab
    
    This commit moves the Account matcher into the Filtering tab in preparation for further options

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 75caba4..894f267 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -658,6 +658,26 @@ options specified in the Options panels."))
     gnc:pagename-general optname-table-export
     "g" (N_ "Formats the table suitable for cut & paste exporting with extra cells.") #f))
 
+  ;; Filtering Options
+
+  (gnc:register-trep-option
+   (gnc:make-string-option
+    pagename-filter optname-account-matcher
+    "a5" (N_ "Match only accounts whose fullname is matched e.g. ':Travel' will match \
+Expenses:Travel:Holiday and Expenses:Business:Travel. It can be left blank, which will \
+disable the matcher.")
+    ""))
+
+  (gnc:register-trep-option
+   (gnc:make-simple-boolean-option
+    pagename-filter optname-account-matcher-regex
+    "a6"
+    (N_ "By default the account matcher will search substring only. Set this to true to \
+enable full POSIX regular expressions capabilities. 'Car|Flights' will match both \
+Expenses:Car and Expenses:Flights. Use a period (.) to match a single character e.g. \
+'20../.' will match 'Travel 2017/1 London'. ")
+    #f))
+
   (gnc:register-trep-option
    (gnc:make-string-option
     pagename-filter optname-transaction-matcher
@@ -692,23 +712,6 @@ tags within description, notes or memo. ")
     #f #t))
 
   (gnc:register-trep-option
-   (gnc:make-string-option
-    gnc:pagename-accounts optname-account-matcher
-    "a5" (N_ "Match only above accounts whose fullname is matched e.g. ':Travel' will match \
-Expenses:Travel:Holiday and Expenses:Business:Travel. It can be left blank, which will disable \
-the matcher.")
-    ""))
-
-  (gnc:register-trep-option
-   (gnc:make-simple-boolean-option
-    gnc:pagename-accounts optname-account-matcher-regex
-    "a6"
-    (N_ "By default the account matcher will search substring only. Set this to true to enable full \
-POSIX regular expressions capabilities. 'Car|Flights' will match both Expenses:Car and Expenses:Flights. \
-Use a period (.) to match a single character e.g. '20../.' will match 'Travel 2017/1 London'. ")
-    #f))
-
-  (gnc:register-trep-option
    (gnc:make-account-list-option
     gnc:pagename-accounts optname-filterby
     "b" (N_ "Filter on these accounts.")
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 2a8adbf..3bb5e96 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -1709,6 +1709,7 @@
                                         "Use Full Account Name?" (cons #f "Use Full Account Name")
                                         "Use Full Other Account Name?" (cons #f "Use Full Other Account Name")
                                         "Void Transactions?" (cons #f "Void Transactions")
+                                        "Account Substring" (cons "Filter" "Account Matcher")
                                         ))
                        (name-match (member name new-names-list)))
 

commit 4187cc1cd2d195dd7b127595b780aea5429f18f4
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Dec 9 22:24:58 2017 +0800

    REFACTOR: Delete unused functions
    
    These functions were probably deprecated from prior work

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 4d03d3c..75caba4 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -159,13 +159,6 @@ options specified in the Options panels."))
         (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
     (timepair-same-year tp-a tp-b)))
 
-(define (set-last-row-style! table tag . rest)
-  (let ((arg-list
-         (cons table
-               (cons (- (gnc:html-table-num-rows table) 1)
-                     (cons tag rest)))))
-    (apply gnc:html-table-set-row-style! arg-list)))
-
 (define (add-subheading-row data table width subheading-style)
   (let ((heading-cell (gnc:make-html-table-cell data)))
     (gnc:html-table-cell-set-colspan! heading-cell width)
@@ -803,7 +796,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                    (vector 'number
                            (N_ "Number/Action")
                            (N_ "Sort by check number/action."))
-
+  
                    (vector 't-number
                            (N_ "Transaction Number")
                            (N_ "Sort by transaction number."))
@@ -1146,40 +1139,9 @@ Credit Card, and Income accounts."))))))
   (gnc:options-set-default-section options gnc:pagename-general)
   options)
 
-
-(define (get-primary-subtotal-style options)
-  (let ((bgcolor (gnc:lookup-option options
-                                    (N_ "Colors")
-                                    (N_ "Primary Subtotals/headings"))))
-    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))
-
-(define (get-secondary-subtotal-style options)
-  (let ((bgcolor (gnc:lookup-option options
-                                    (N_ "Colors")
-                                    (N_ "Secondary Subtotals/headings"))))
-    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))
-
-(define (get-grand-total-style options)
-  (let ((bgcolor (gnc:lookup-option options
-                                    (N_ "Colors")
-                                    (N_ "Grand Total"))))
-    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))
-
-(define (get-odd-row-style options)
-  (let ((bgcolor (gnc:lookup-option options
-                                    (N_ "Colors")
-                                    (N_ "Split Odd"))))
-    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))
-
-(define (get-even-row-style options)
-  (let ((bgcolor (gnc:lookup-option options
-                                    (N_ "Colors")
-                                    (N_ "Split Even"))))
-    (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))
-
-
 ;; ;;;;;;;;;;;;;;;;;;;;
 ;; Here comes the big function that builds the whole table.
+
 (define (make-split-table splits options
                           primary-subtotal-pred
                           secondary-subtotal-pred
@@ -1384,6 +1346,8 @@ Credit Card, and Income accounts."))))))
 
 ;; ;;;;;;;;;;;;;;;;;;;;
 ;; Here comes the renderer function for this report.
+
+
 (define (trep-renderer report-obj)
   (define options (gnc:report-options report-obj))
   (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))

commit 3f03cce164ca4896e2ce4ef95a3d8cab46cf4895
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 07:39:57 2017 +0800

    REFACTOR: rename funcs, centralize strings

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index ed50685..4d03d3c 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -9,6 +9,8 @@
 ;; Michael T. Garrison Stuber
 ;; Modified account names display by Tomas Pospisek
 ;; <tpo_deb at sourcepole.ch> with a lot of help from "warlord"
+;; Refactored by Christopher Lam (2017)
+;; - introduced account/transaction substring/regex matcher
 ;;
 ;; This program is free software; you can redistribute it and/or    
 ;; modify it under the terms of the GNU General Public License as   
@@ -37,7 +39,6 @@
 (use-modules (ice-9 regex))
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
-
 (use-modules (gnucash printf))
 
 (gnc:module-load "gnucash/report/report-system" 0)
@@ -46,9 +47,18 @@
   `(set! ,alist (cons ,element ,alist)))
 
 ;; Define the strings here to avoid typos and make changes easier.
-
 (define reportname (N_ "Transaction Report"))
+
+;;Accounts
+(define optname-accounts (N_ "Accounts"))
+(define optname-filterby (N_ "Filter By..."))
+(define optname-filtertype (N_ "Filter Type"))
+(define optname-void-transactions (N_ "Void Transactions"))
+
+;;Display
 (define optname-detail-level (N_ "Detail Level"))
+
+;;Sorting
 (define pagename-sorting (N_ "Sorting"))
 (define optname-prime-sortkey (N_ "Primary Key"))
 (define optname-prime-subtotal (N_ "Primary Subtotal"))
@@ -60,22 +70,37 @@
 (define optname-sec-subtotal (N_ "Secondary Subtotal"))
 (define optname-sec-sortorder  (N_ "Secondary Sort Order"))
 (define optname-sec-date-subtotal (N_ "Secondary Subtotal for Date Key"))
-(define optname-void-transactions (N_ "Void Transactions"))
+
+;;General
+(define optname-startdate (N_ "Start Date"))
+(define optname-enddate (N_ "End Date"))
 (define optname-table-export (N_ "Table for Exporting"))
 (define optname-common-currency (N_ "Common Currency"))
 (define optname-currency (N_ "Report's currency"))
-(define optname-account-matcher (N_ "Account Matcher"))
-(define optname-account-matcher-regex (N_ "Account Matcher uses regular expressions for extended matching"))
 
+;;Filtering
 (define pagename-filter (N_ "Filter"))
+(define optname-account-matcher (N_ "Account Matcher"))
+(define optname-account-matcher-regex (N_ "Account Matcher uses regular expressions for extended matching"))
 (define optname-transaction-matcher (N_ "Transaction Matcher"))
 (define optname-transaction-matcher-regex (N_ "Transaction Matcher uses regular expressions for extended matching"))
+(define optname-reconcile-status (N_ "Reconcile Status"))
 
+;;Styles
 (define def:grand-total-style "grand-total")
 (define def:normal-row-style "normal-row")
 (define def:alternate-row-style "alternate-row")
 (define def:primary-subtotal-style "primary-subheading")
 (define def:secondary-subtotal-style "secondary-subheading")
+
+(define NO-MATCHING-TRANS-HEADER (_ "No matching transactions found"))
+(define NO-MATCHING-TRANS-TEXT (_ "No transactions were found that \
+match the time interval and account selection specified \
+in the Options panel."))
+(define NO-MATCHING-ACCT-HEADER (N_ "No matching accounts found"))
+(define NO-MATCHING-ACCT-TEXT (N_ "No account were found that match the \
+options specified in the Options panels."))
+
 ;; The option-values of the sorting key multichoice option, for
 ;; which a subtotal should be enabled.
 (define subtotal-enabled '(account-name
@@ -83,16 +108,16 @@
                            corresponding-acc-name
                            corresponding-acc-code))
 
-(define (split-account-full-name-same-p a b)
+(define (split-account-fullname-same? a b)
   (= (xaccSplitCompareAccountFullNames a b) 0))
 
-(define (split-account-code-same-p a b)
+(define (split-account-code-same? a b)
   (= (xaccSplitCompareAccountCodes a b) 0))
 
-(define (split-same-corr-account-full-name-p a b)
+(define (split-otheracct-fullname-same? a b)
   (= (xaccSplitCompareOtherAccountFullNames a b) 0))
 
-(define (split-same-corr-account-code-p a b)
+(define (split-otheracct-code-same? a b)
   (= (xaccSplitCompareOtherAccountCodes a b) 0))
 
 (define (timepair-same-year tp-a tp-b)
@@ -114,22 +139,22 @@
        (= (gnc:timepair-get-week tp-a)
           (gnc:timepair-get-week tp-b))))
 
-(define (split-same-week-p a b)
+(define (split-same-week? a b)
   (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
         (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
     (timepair-same-week tp-a tp-b)))
 
-(define (split-same-month-p a b)
+(define (split-same-month? a b)
   (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
         (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
     (timepair-same-month tp-a tp-b)))
 
-(define (split-same-quarter-p a b)
+(define (split-same-quarter? a b)
   (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
         (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
     (timepair-same-quarter tp-a tp-b)))
 
-(define (split-same-year-p a b)
+(define (split-same-year? a b)
   (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
         (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
     (timepair-same-year tp-a tp-b)))
@@ -150,19 +175,19 @@
      (list heading-cell))))
 
 ;; display an account name depending on the options the user has set
-(define (account-namestring account show-account-code show-account-name show-account-full-name)
+(define (account-namestring account show-account-code? show-account-name? show-account-full-name?)
   ;;# on multi-line splits we can get an empty ('()) account
   (if (null? account)
       (_ "Split Transaction")
       (string-append
        ;; display account code?
-       (if show-account-code
+       (if show-account-code?
            (string-append (xaccAccountGetCode account) " ")
            "")
        ;; display account name?
-       (if show-account-name
+       (if show-account-name?
            ;; display full account name?
-           (if show-account-full-name
+           (if show-account-full-name?
                (gnc-account-get-full-name account)
                (xaccAccountGetName account))
            ""))))
@@ -185,9 +210,9 @@
   (let ((account (xaccSplitGetAccount (xaccSplitGetOtherSplit split))))
     (add-subheading-row (gnc:make-html-text
                          (gnc:html-markup-anchor
-                          (if (not (null? account))
-                              (gnc:account-anchor-text account)
-                              "")
+                          (if (null? account)
+                              ""
+                              (gnc:account-anchor-text account))
                           (account-namestring account
                                               (used-sort-account-code      column-vector)
                                               #t
@@ -222,11 +247,9 @@
                          (xaccSplitGetParent split))))
                       table width subheading-style))
 
-
 (define (add-subtotal-row table width subtotal-string subtotal-collector
                           subtotal-style export?)
-  (let ((currency-totals (subtotal-collector
-                          'format gnc:make-gnc-monetary #f))
+  (let ((currency-totals (subtotal-collector 'format gnc:make-gnc-monetary #f))
         (blanks (gnc:make-html-table-cell/size 1 (- width 1) #f)))
     (gnc:html-table-append-row/markup!
      table
@@ -308,7 +331,6 @@
                       (total-string (strftime "%Y" tm))
                       total-collector subtotal-style export?)))
 
-
 (define (render-grand-total
          table width total-collector export?)
   (add-subtotal-row table width
@@ -387,9 +409,7 @@
         (set! col-req (- col-req 1)))))
 
 (define (build-column-used options)
-  (define (opt-val section name)
-    (gnc:option-value
-     (gnc:lookup-option options section name)))
+  (define (opt-val section name) (gnc:option-value  (gnc:lookup-option options section name)))
   (let ((column-list (make-vector columns-used-size #f))
         (is-single? (eq? (opt-val gnc:pagename-display optname-detail-level) 'single)))
     (if (opt-val gnc:pagename-display (N_ "Date"))
@@ -437,6 +457,9 @@
     column-list))
 
 (define (make-heading-list column-vector options)
+
+  (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
+
   (let ((heading-list '()))
     (if (used-date column-vector)
         (addto! heading-list (_ "Date")))
@@ -448,10 +471,7 @@
                                       (if (gnc:lookup-option options
                                                              gnc:pagename-display
                                                              (N_ "Trans Number"))
-                                          (gnc:option-value
-                                           (gnc:lookup-option options
-                                                              gnc:pagename-display
-                                                              (N_ "Trans Number")))
+                                          (opt-val gnc:pagename-display (N_ "Trans Number"))
                                           #f))
                                  (_ "Num/T-Num")
                                  (_ "Num"))))
@@ -483,18 +503,15 @@
 (define (add-split-row table split column-vector options
                        row-style account-types-to-reverse transaction-row?)
 
-  (define (opt-val section name)
-    (gnc:option-value
-     (gnc:lookup-option options section name)))
+  (define (opt-val section name) (gnc:option-value  (gnc:lookup-option options section name)))
 
   (let* ((row-contents '())
-         (dummy  (gnc:debug "split is originally" split))
          (parent (xaccSplitGetParent split))
          (account (xaccSplitGetAccount split))
          (account-type (xaccAccountGetType account))
-         (currency (if (not (null? account))
-                       (xaccAccountGetCommodity account)
-                       (gnc-default-currency)))
+         (currency (if (null? account)
+                       (gnc-default-currency)
+                       (xaccAccountGetCommodity account)))
          (report-currency (if (opt-val gnc:pagename-general optname-common-currency)
                               (opt-val gnc:pagename-general optname-currency)
                               currency))
@@ -520,6 +537,7 @@
                     (gnc:make-html-table-cell/markup "date-cell"
                                                      (gnc-print-date (gnc-transaction-get-date-posted parent)))
                     " ")))
+
     (if (used-reconciled-date column-vector)
         (addto! row-contents
                 (gnc:make-html-table-cell/markup "date-cell"
@@ -533,15 +551,13 @@
                     (if (qof-book-use-split-action-for-num-field
                          (gnc-get-current-book))
                         (let* ((num (gnc-get-num-action parent split))
-                               (t-num (if (if (gnc:lookup-option options
-                                                                 gnc:pagename-display
+                               (t-num (if (if (gnc:lookup-option options gnc:pagename-display
                                                                  (N_ "Trans Number"))
-                                              (opt-val gnc:pagename-display
-                                                       (N_ "Trans Number"))
+                                              (opt-val gnc:pagename-display (N_ "Trans Number"))
                                               #f)
                                           (gnc-get-num-action parent #f)
                                           ""))
-                               (num-string (if (equal? t-num "")
+                               (num-string (if (string-null? t-num)
                                                num
                                                (string-append num "/" t-num))))
                           (gnc:make-html-table-cell/markup "text-cell"
@@ -578,54 +594,59 @@
 
     (if (used-shares column-vector)
         (addto! row-contents (xaccSplitGetAmount split)))
+    
     (if (used-price column-vector)
-        (addto!
-         row-contents
-         (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
+        (addto! row-contents
+                (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
                                 (xaccSplitGetSharePrice split))))
+    
     (if (used-amount-single column-vector)
         (addto! row-contents
                 (gnc:make-html-table-cell/markup "number-cell"
                                                  (gnc:html-transaction-anchor parent split-value))))
+    
     (if (used-amount-double-positive column-vector)
         (if (gnc-numeric-positive-p (gnc:gnc-monetary-amount split-value))
             (addto! row-contents
                     (gnc:make-html-table-cell/markup "number-cell"
                                                      (gnc:html-transaction-anchor parent split-value)))
             (addto! row-contents " ")))
+    
     (if (used-amount-double-negative column-vector)
         (if (gnc-numeric-negative-p (gnc:gnc-monetary-amount split-value))
             (addto! row-contents
                     (gnc:make-html-table-cell/markup
                      "number-cell" (gnc:html-transaction-anchor parent (gnc:monetary-neg split-value))))
             (addto! row-contents " ")))
+    
     (if (used-running-balance column-vector)
         (begin
-          (gnc:debug "split is " split)
-          (gnc:debug "split get balance:" (xaccSplitGetBalance split))
+          ;(gnc:debug "split is " split)
+          ;(gnc:debug "split get balance:" (xaccSplitGetBalance split))
           (addto! row-contents
                   (gnc:make-html-table-cell/markup
                    "number-cell"
                    (gnc:make-gnc-monetary currency
                                           (xaccSplitGetBalance split))))))
-    (gnc:html-table-append-row/markup! table row-style
-                                       (reverse row-contents))
+    
+    (gnc:html-table-append-row/markup! table row-style (reverse row-contents))
+
     split-value))
 
 
 (define date-sorting-types (list 'date 'reconciled-date))
 
 (define (trep-options-generator)
-  (define gnc:*transaction-report-options* (gnc:new-options))
+
+  (define options (gnc:new-options))
+
   (define (gnc:register-trep-option new-option)
-    (gnc:register-option gnc:*transaction-report-options* new-option))
+    (gnc:register-option options new-option))
 
   ;; General options
 
   (gnc:options-add-date-interval!
-   gnc:*transaction-report-options*
-   gnc:pagename-general (N_ "Start Date") (N_ "End Date") "a")
-
+   options gnc:pagename-general optname-startdate optname-enddate "a")
 
   (gnc:register-trep-option
    (gnc:make-complex-boolean-option
@@ -633,14 +654,11 @@
     "e" (N_ "Convert all transactions into a common currency.") #f
     #f
     (lambda (x) (gnc-option-db-set-option-selectable-by-name
-                 gnc:*transaction-report-options*
-                 gnc:pagename-general
-                 optname-currency
-                 x))
-    ))
+                 options gnc:pagename-general optname-currency
+                 x))))
 
   (gnc:options-add-currency!
-   gnc:*transaction-report-options* gnc:pagename-general optname-currency "f")
+   options gnc:pagename-general optname-currency "f")
 
   (gnc:register-trep-option
    (gnc:make-simple-boolean-option
@@ -669,7 +687,7 @@ tags within description, notes or memo. ")
   ;; account to do report on
   (gnc:register-trep-option
    (gnc:make-account-list-option
-    gnc:pagename-accounts (N_ "Accounts")
+    gnc:pagename-accounts optname-accounts
     "a" (N_ "Report on these accounts.")
     ;; select, by default, no accounts! Selecting all accounts will
     ;; always imply an insanely long waiting time upon opening, and it
@@ -699,7 +717,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
 
   (gnc:register-trep-option
    (gnc:make-account-list-option
-    gnc:pagename-accounts (N_ "Filter By...")
+    gnc:pagename-accounts optname-filterby
     "b" (N_ "Filter on these accounts.")
     (lambda ()
       ;; FIXME : gnc:get-current-accounts disappeared.
@@ -707,15 +725,14 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
              (root (gnc-get-current-root-account))
              (num-accounts (gnc-account-n-children root))
              (first-account (gnc-account-nth-child root 0)))
-        (cond ((not (null? current-accounts))
-               (list (car current-accounts)))
-              ((> num-accounts 0) (list first-account))
+        (cond ((not (null? current-accounts)) (list (car current-accounts)))
+              ((positive? num-accounts) (list first-account))
               (else '()))))
     #f #t))
 
   (gnc:register-trep-option
    (gnc:make-multichoice-option
-    gnc:pagename-accounts (N_ "Filter Type")
+    gnc:pagename-accounts optname-filtertype
     "c" (N_ "Filter account.")
     'none
     (list (vector 'none
@@ -726,9 +743,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                   (N_ "Include transactions to/from filter accounts only."))
           (vector 'exclude
                   (N_ "Exclude Transactions to/from Filter Accounts")
-                  (N_ "Exclude transactions to/from all filter accounts."))
-          )))
-
+                  (N_ "Exclude transactions to/from all filter accounts.")))))
   ;;
 
   (gnc:register-trep-option
@@ -736,24 +751,14 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
     gnc:pagename-accounts optname-void-transactions
     "d" (N_ "How to handle void transactions.")
     'non-void-only
-    (list (vector
-           'non-void-only
-           (N_ "Non-void only")
-           (N_ "Show only non-voided transactions."))
-          (vector
-           'void-only
-           (N_ "Void only")
-           (N_ "Show only voided transactions."))
-          (vector
-           'both
-           (N_ "Both")
-           (N_ "Show both (and include void transactions in totals).")))))
+    (list
+     (vector 'non-void-only (N_ "Non-void only") (N_ "Show only non-voided transactions."))
+     (vector 'void-only     (N_ "Void only") (N_ "Show only voided transactions."))
+     (vector 'both          (N_ "Both") (N_ "Show both (and include void transactions in totals).")))))
 
   ;; Sorting options
 
-  (let ((options gnc:*transaction-report-options*)
-
-        (key-choice-list
+  (let ((key-choice-list
          (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
              (list (vector 'none
                            (N_ "None")
@@ -806,6 +811,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                    (vector 'memo
                            (N_ "Memo")
                            (N_ "Sort by memo.")))
+
              (list (vector 'none
                            (N_ "None")
                            (N_ "Do not sort."))
@@ -1005,8 +1011,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
 
   ;; Display options
 
-  (let ((options gnc:*transaction-report-options*)
-        (disp-memo? #t)
+  (let ((disp-memo? #t)
         (disp-accname? #t)
         (disp-other-accname? #f)
         (is-single? #t))
@@ -1071,7 +1076,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
      (gnc:make-complex-boolean-option
       gnc:pagename-display (N_ "Memo")
       "d"  (N_ "Display the memo?") #t
-      #f
+      disp-memo?
       (lambda (x)
         (set! disp-memo? x)
         (apply-selectable-by-name-display-options))))
@@ -1081,7 +1086,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
      (gnc:make-complex-boolean-option
       gnc:pagename-display (N_ "Account Name")
       "e"  (N_ "Display the account name?") #t
-      #f
+      disp-accname?
       (lambda (x)
         (set! disp-accname? x)
         (apply-selectable-by-name-display-options))))
@@ -1091,7 +1096,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
      (gnc:make-complex-boolean-option
       gnc:pagename-display (N_ "Other Account Name")
       "h5"  (N_ "Display the other account name? (if this is a split transaction, this parameter is guessed).") #f
-      #f
+      disp-other-accname?
       (lambda (x)
         (set! disp-other-accname? x)
         (apply-selectable-by-name-display-options))))
@@ -1118,7 +1123,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
       "m" (N_ "Display the amount?")
       'single
       (list
-       (vector 'none (N_ "None") (N_ "No amount display."))
+       (vector 'none   (N_ "None") (N_ "No amount display."))
        (vector 'single (N_ "Single") (N_ "Single Column Display."))
        (vector 'double (N_ "Double") (N_ "Two Column Display.")))))
 
@@ -1127,25 +1132,20 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
       gnc:pagename-display (N_ "Sign Reverses")
       "p" (N_ "Reverse amount display for certain account types.")
       'credit-accounts
-      (list
-       (vector 'none (N_ "None") (N_ "Don't change any displayed amounts."))
-       (vector 'income-expense (N_ "Income and Expense")
-               (N_ "Reverse amount display for Income and Expense Accounts."))
-       (vector 'credit-accounts (N_ "Credit Accounts")
-               (N_ "Reverse amount display for Liability, Payable, Equity, \
+      (list        (vector 'none
+                           (N_ "None")
+                           (N_ "Don't change any displayed amounts."))
+                   (vector 'income-expense
+                           (N_ "Income and Expense")
+                           (N_ "Reverse amount display for Income and Expense Accounts."))
+                   (vector 'credit-accounts
+                           (N_ "Credit Accounts")
+                           (N_ "Reverse amount display for Liability, Payable, Equity, \
 Credit Card, and Income accounts."))))))
 
+  (gnc:options-set-default-section options gnc:pagename-general)
+  options)
 
-  (gnc:options-set-default-section gnc:*transaction-report-options*
-                                   gnc:pagename-general)
-
-  gnc:*transaction-report-options*)
-
-
-(define (display-date-interval begin end)
-  (let ((begin-string (gnc-print-date begin))
-        (end-string (gnc-print-date end)))
-    (sprintf #f (_ "From %s To %s") begin-string end-string)))
 
 (define (get-primary-subtotal-style options)
   (let ((bgcolor (gnc:lookup-option options
@@ -1188,42 +1188,28 @@ Credit Card, and Income accounts."))))))
                           primary-subtotal-renderer
                           secondary-subtotal-renderer)
 
+  (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
+
   (let ((work-to-do (length splits))
         (work-done 0)
-        (used-columns (build-column-used options)))
-    (define (get-account-types-to-reverse options)
-      (cdr (assq (gnc:option-value
-                  (gnc:lookup-option options
-                                     gnc:pagename-display
-                                     (N_ "Sign Reverses")))
-                 account-types-to-reverse-assoc-list)))
-
-
-    (define (transaction-report-multi-rows-p options)
-      (eq? (gnc:option-value
-            (gnc:lookup-option options gnc:pagename-display optname-detail-level))
-           'multi-line))
-
-    (define (transaction-report-export-p options)
-      (gnc:option-value
-       (gnc:lookup-option options gnc:pagename-general
-                          optname-table-export)))
-
-    (define (add-other-split-rows split table used-columns
-                                  row-style account-types-to-reverse)
-      (define (other-rows-driver split parent table used-columns i)
-        (let ((current (xaccTransGetSplit parent i)))
-          (cond ((null? current) #f)
-                ((equal? current split)
-                 (other-rows-driver split parent table used-columns (+ i 1)))
-                (else (begin
-                        (add-split-row table current used-columns options
-                                       row-style account-types-to-reverse #f)
-                        (other-rows-driver split parent table used-columns
-                                           (+ i 1)))))))
-
-      (other-rows-driver split (xaccSplitGetParent split)
-                         table used-columns 0))
+        (used-columns (build-column-used options))
+        (account-types-to-reverse
+         (cdr (assq
+               (opt-val gnc:pagename-display (N_ "Sign Reverses"))
+               account-types-to-reverse-assoc-list)))
+        (is-multiline? (eq? (opt-val gnc:pagename-display optname-detail-level) 'multi-line))
+        (export? (opt-val gnc:pagename-general optname-table-export)))
+
+    (define (add-other-split-rows
+             split table used-columns row-style account-types-to-reverse)
+
+      (let* ((txn (xaccSplitGetParent split))
+             (other-splits (delete split (xaccTransGetSplitList txn))))
+
+        (for-each (lambda (s)
+                    (add-split-row table s used-columns options
+                                   row-style account-types-to-reverse #f))
+                  other-splits)))
 
     (define (do-rows-with-subtotals splits
                                     table
@@ -1244,16 +1230,20 @@ Credit Card, and Income accounts."))))))
                                     total-collector)
 
       (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
+
       (set! work-done (+ 1 work-done))
+
       (if (null? splits)
+
           (begin
+
             (gnc:html-table-append-row/markup!
-             table
-             def:grand-total-style
+             table def:grand-total-style
              (list
               (gnc:make-html-table-cell/size
                1 width (gnc:make-html-text (gnc:html-markup-hr)))))
-            (if (gnc:option-value (gnc:lookup-option options "Display" "Totals"))
+
+            (if (opt-val gnc:pagename-display "Totals")
                 (render-grand-total table width total-collector export?)))
 
           (let* ((current (car splits))
@@ -1277,15 +1267,13 @@ Credit Card, and Income accounts."))))))
                  account-types-to-reverse))
 
             (primary-subtotal-collector 'add
-                                        (gnc:gnc-monetary-commodity
-                                         split-value)
-                                        (gnc:gnc-monetary-amount
-                                         split-value))
+                                        (gnc:gnc-monetary-commodity split-value) 
+                                        (gnc:gnc-monetary-amount split-value))
+
             (secondary-subtotal-collector 'add
-                                          (gnc:gnc-monetary-commodity
-                                           split-value)
-                                          (gnc:gnc-monetary-amount
-                                           split-value))
+                                          (gnc:gnc-monetary-commodity split-value)
+                                          (gnc:gnc-monetary-amount split-value))
+
             (total-collector 'add
                              (gnc:gnc-monetary-commodity split-value)
                              (gnc:gnc-monetary-amount split-value))
@@ -1294,14 +1282,18 @@ Credit Card, and Income accounts."))))))
                      (or (not next)
                          (and next
                               (not (primary-subtotal-pred current next)))))
+
                 (begin
+
                   (if secondary-subtotal-pred
 
                       (begin
+
                         (secondary-subtotal-renderer
                          table width current
                          secondary-subtotal-collector
                          def:secondary-subtotal-style used-columns export?)
+
                         (secondary-subtotal-collector 'reset #f #f)))
 
                   (primary-subtotal-renderer table width current
@@ -1312,7 +1304,9 @@ Credit Card, and Income accounts."))))))
                   (primary-subtotal-collector 'reset #f #f)
 
                   (if next
+
                       (begin
+
                         (primary-subheading-renderer
                          next table width def:primary-subtotal-style used-columns)
 
@@ -1327,11 +1321,14 @@ Credit Card, and Income accounts."))))))
                              (and next
                                   (not (secondary-subtotal-pred
                                         current next)))))
+
                     (begin (secondary-subtotal-renderer
                             table width current
                             secondary-subtotal-collector
                             def:secondary-subtotal-style used-columns export?)
+
                            (secondary-subtotal-collector 'reset #f #f)
+
                            (if next
                                (secondary-subheading-renderer
                                 next table width
@@ -1356,51 +1353,40 @@ Credit Card, and Income accounts."))))))
                                     total-collector))))
 
     (let* ((table (gnc:make-html-table))
-           (width (num-columns-required used-columns))
-           (multi-rows? (transaction-report-multi-rows-p options))
-           (export? (transaction-report-export-p options))
-           (account-types-to-reverse
-            (get-account-types-to-reverse options)))
-
-      (gnc:html-table-set-col-headers!
-       table
+           (width (num-columns-required used-columns)))
+
+      (gnc:html-table-set-col-headers! table
        (make-heading-list used-columns options))
-      ;;     (gnc:warn "Splits:" splits)
-      (if (not (null? splits))
-          (begin
-            (if primary-subheading-renderer
-                (primary-subheading-renderer
-                 (car splits) table width def:primary-subtotal-style used-columns))
-            (if secondary-subheading-renderer
-                (secondary-subheading-renderer
-                 (car splits) table width def:secondary-subtotal-style used-columns))
-
-            (do-rows-with-subtotals splits table used-columns width
-                                    multi-rows? #t
-                                    export?
-                                    account-types-to-reverse
-                                    primary-subtotal-pred
-                                    secondary-subtotal-pred
-                                    primary-subheading-renderer
-                                    secondary-subheading-renderer
-                                    primary-subtotal-renderer
-                                    secondary-subtotal-renderer
-                                    (gnc:make-commodity-collector)
-                                    (gnc:make-commodity-collector)
-                                    (gnc:make-commodity-collector))))
+
+      (if primary-subheading-renderer
+          (primary-subheading-renderer
+           (car splits) table width def:primary-subtotal-style used-columns))
+
+      (if secondary-subheading-renderer
+          (secondary-subheading-renderer
+           (car splits) table width def:secondary-subtotal-style used-columns))
+
+      (do-rows-with-subtotals splits table used-columns width
+                              is-multiline? #t
+                              export?
+                              account-types-to-reverse
+                              primary-subtotal-pred
+                              secondary-subtotal-pred
+                              primary-subheading-renderer
+                              secondary-subheading-renderer
+                              primary-subtotal-renderer
+                              secondary-subtotal-renderer
+                              (gnc:make-commodity-collector)
+                              (gnc:make-commodity-collector)
+                              (gnc:make-commodity-collector))
 
       table)))
 
 ;; ;;;;;;;;;;;;;;;;;;;;
 ;; Here comes the renderer function for this report.
 (define (trep-renderer report-obj)
-
   (define options (gnc:report-options report-obj))
-
-  (define (opt-val section name)
-    (gnc:option-value
-     (gnc:lookup-option options section name)))
-
+  (define (opt-val section name) (gnc:option-value (gnc:lookup-option options section name)))
   (define comp-funcs-assoc-list
     ;; Defines the different sorting keys, together with the
     ;; subtotal functions. Each entry: (cons
@@ -1410,12 +1396,12 @@ Credit Card, and Income accounts."))))))
     (let* ((used-columns (build-column-used (gnc:report-options report-obj))))
       (list (cons 'account-name  (vector
                                   (list SPLIT-ACCT-FULLNAME)
-                                  split-account-full-name-same-p
+                                  split-account-fullname-same?
                                   render-account-subheading
                                   render-account-subtotal))
             (cons 'account-code  (vector
                                   (list SPLIT-ACCOUNT ACCOUNT-CODE-)
-                                  split-account-code-same-p
+                                  split-account-code-same?
                                   render-account-subheading
                                   render-account-subtotal))
             (cons 'date          (vector
@@ -1430,13 +1416,13 @@ Credit Card, and Income accounts."))))))
             (cons 'corresponding-acc-name
                   (vector
                    (list SPLIT-CORR-ACCT-NAME)
-                   split-same-corr-account-full-name-p
+                   split-otheracct-fullname-same?
                    render-corresponding-account-subheading
                    render-corresponding-account-subtotal))
             (cons 'corresponding-acc-code
                   (vector
                    (list SPLIT-CORR-ACCT-CODE)
-                   split-same-corr-account-code-p
+                   split-otheracct-code-same?
                    render-corresponding-account-subheading
                    render-corresponding-account-subtotal))
             (cons 'amount        (vector (list SPLIT-VALUE) #f #f #f))
@@ -1454,14 +1440,10 @@ Credit Card, and Income accounts."))))))
     ;; subtotal-renderer))
     (list
      (cons 'none (vector #f #f #f))
-     (cons 'weekly (vector split-same-week-p render-week-subheading
-                           render-week-subtotal))
-     (cons 'monthly (vector split-same-month-p render-month-subheading
-                            render-month-subtotal))
-     (cons 'quarterly (vector split-same-quarter-p render-quarter-subheading
-                              render-quarter-subtotal))
-     (cons 'yearly (vector split-same-year-p render-year-subheading
-                           render-year-subtotal))))
+     (cons 'weekly (vector split-same-week? render-week-subheading render-week-subtotal))
+     (cons 'monthly (vector split-same-month? render-month-subheading render-month-subtotal))
+     (cons 'quarterly (vector split-same-quarter? render-quarter-subheading render-quarter-subtotal))
+     (cons 'yearly (vector split-same-year? render-year-subheading render-year-subtotal))))
 
   (define (get-subtotalstuff-helper
            name-sortkey name-subtotal name-date-subtotal
@@ -1509,52 +1491,30 @@ Credit Card, and Income accounts."))))))
      name-sortkey name-subtotal name-date-subtotal
      3 2))
 
-  ;;(define (get-other-account-names account-list)
-  ;;  ( map (lambda (acct)  (gnc-account-get-full-name acct)) account-list))
-
   (define (is-filter-member split account-list)
     (let* ((txn (xaccSplitGetParent split))
-           (splitcount (xaccTransCountSplits txn)))
-
+           (splitcount (xaccTransCountSplits txn))
+           (other-account (xaccSplitGetAccount (xaccSplitGetOtherSplit split)))
+           (splits-equal? (lambda (s1 s2) (xaccSplitEqual s1 s2 #t #f #f)))
+           (other-splits (delete split (xaccTransGetSplitList txn) splits-equal?))
+           (other-accounts (map xaccSplitGetAccount other-splits))
+           (is-in-account-list? (lambda (acc) (member acc account-list))))
       (cond
         ;; A 2-split transaction - test separately so it can be optimized
         ;; to significantly reduce the number of splits to traverse
         ;; in guile code
-        ((= splitcount 2)
-         (let* ((other      (xaccSplitGetOtherSplit split))
-                (other-acct (xaccSplitGetAccount other)))
-           (member other-acct account-list)))
-
+        ((= splitcount 2) (is-in-account-list? other-account))
         ;; A multi-split transaction - run over all splits
-        ((> splitcount 2)
-         (let ((splits (xaccTransGetSplitList txn)))
-
-           ;; Walk through the list of splits.
-           ;; if we reach the end, return #f
-           ;; if the 'this' != 'split' and the split->account is a member
-           ;; of the account-list, then return #t, else recurse
-           (define (is-member splits)
-             (if (null? splits)
-                 #f
-                 (let* ((this (car splits))
-                        (rest (cdr splits))
-                        (acct (xaccSplitGetAccount this)))
-                   (if (and (not (eq? this split))
-                            (member acct account-list))
-                       #t
-                       (is-member rest)))))
-
-           (is-member splits)))
-
+        ((> splitcount 2) (or-map is-in-account-list? other-accounts))
         ;; Single transaction splits
         (else #f))))
 
-
   (gnc:report-starting reportname)
+
   (let* ((document (gnc:make-html-document))
-         (c_account_0 (opt-val gnc:pagename-accounts "Accounts"))
-         (account-matcher (opt-val gnc:pagename-accounts optname-account-matcher))
-         (account-matcher-regexp (if (opt-val gnc:pagename-accounts optname-account-matcher-regex)
+         (c_account_0 (opt-val gnc:pagename-accounts optname-accounts))
+         (account-matcher (opt-val pagename-filter optname-account-matcher))
+         (account-matcher-regexp (if (opt-val pagename-filter optname-account-matcher-regex)
                                      (make-regexp account-matcher)
                                      #f))
          (c_account_1 (filter
@@ -1563,25 +1523,24 @@ Credit Card, and Income accounts."))))))
                              (regexp-exec account-matcher-regexp (gnc-account-get-full-name acc))
                              (string-contains (gnc-account-get-full-name acc) account-matcher)))
                        c_account_0))
-         (c_account_2 (opt-val gnc:pagename-accounts "Filter By..."))
-         (filter-mode (opt-val gnc:pagename-accounts "Filter Type"))
+         (c_account_2 (opt-val gnc:pagename-accounts optname-filterby))
+         (filter-mode (opt-val gnc:pagename-accounts optname-filtertype))
          (begindate (gnc:timepair-start-day-time
                      (gnc:date-option-absolute-time
-                      (opt-val gnc:pagename-general "Start Date"))))
+                      (opt-val gnc:pagename-general optname-startdate))))
          (enddate (gnc:timepair-end-day-time
                    (gnc:date-option-absolute-time
-                    (opt-val gnc:pagename-general "End Date"))))
+                    (opt-val gnc:pagename-general optname-enddate))))
          (transaction-matcher (opt-val pagename-filter optname-transaction-matcher))
          (transaction-matcher-regexp (if (opt-val pagename-filter optname-transaction-matcher-regex)
                                          (make-regexp transaction-matcher)
                                          #f))
-         (report-title (opt-val
-                        gnc:pagename-general
-                        gnc:optname-reportname))
+         (reconcile-status-filter (opt-val pagename-filter optname-reconcile-status))
+         (report-title (opt-val gnc:pagename-general gnc:optname-reportname))
          (primary-key (opt-val pagename-sorting optname-prime-sortkey))
-         (primary-order (opt-val pagename-sorting "Primary Sort Order"))
+         (primary-order (opt-val pagename-sorting optname-prime-sortorder))
          (secondary-key (opt-val pagename-sorting optname-sec-sortkey))
-         (secondary-order (opt-val pagename-sorting "Secondary Sort Order"))
+         (secondary-order (opt-val pagename-sorting optname-sec-sortorder))
          (void-status (opt-val gnc:pagename-accounts optname-void-transactions))
          (splits '())
          (query (qof-query-create-for-splits)))
@@ -1589,32 +1548,41 @@ Credit Card, and Income accounts."))))))
     ;;(gnc:warn "accts in trep-renderer:" c_account_1)
     ;;(gnc:warn "Report Account names:" (get-other-account-names c_account_1))
 
-    (if (not (or (null? c_account_1) (and-map not c_account_1)))
+    (if (or (null? c_account_1) (and-map not c_account_1))
+
+        (if (null? c_account_0)
+
+            ;; error condition: no accounts specified
+            (gnc:html-document-add-object!
+             document
+             (gnc:html-make-no-account-warning
+              report-title (gnc:report-id report-obj)))
+
+            ;; error condition: accounts were specified but none matched string/regex
+            (gnc:html-document-add-object!
+             document
+             (gnc:make-html-text
+              (gnc:html-markup-h2 NO-MATCHING-ACCT-HEADER)
+              (gnc:html-markup-p NO-MATCHING-ACCT-TEXT))))
+
         (begin
-          (qof-query-set-book query (gnc-get-current-book))
+
           ;;(gnc:warn "query is:" query)
-          (xaccQueryAddAccountMatch query
-                                    c_account_1
-                                    QOF-GUID-MATCH-ANY QOF-QUERY-AND)
-          (xaccQueryAddDateMatchTS
-           query #t begindate #t enddate QOF-QUERY-AND)
+          (qof-query-set-book query (gnc-get-current-book))
+          (xaccQueryAddAccountMatch query c_account_1 QOF-GUID-MATCH-ANY QOF-QUERY-AND)
+          (xaccQueryAddDateMatchTS query #t begindate #t enddate QOF-QUERY-AND)
           (qof-query-set-sort-order query
                                     (get-query-sortkey primary-key)
                                     (get-query-sortkey secondary-key)
                                     '())
-
           (qof-query-set-sort-increasing query
                                          (eq? primary-order 'ascend)
                                          (eq? secondary-order 'ascend)
                                          #t)
-
           (case void-status
-            ((non-void-only)
-             (gnc:query-set-match-non-voids-only! query (gnc-get-current-book)))
-            ((void-only)
-             (gnc:query-set-match-voids-only! query (gnc-get-current-book)))
+            ((non-void-only) (gnc:query-set-match-non-voids-only! query (gnc-get-current-book)))
+            ((void-only)     (gnc:query-set-match-voids-only! query (gnc-get-current-book)))
             (else #f))
-
           (set! splits (qof-query-run query))
 
           ;;(gnc:warn "Splits in trep-renderer:" splits)
@@ -1636,71 +1604,53 @@ Credit Card, and Income accounts."))))))
                                      (match? (xaccSplitGetMemo split))))))
                         splits))
 
-          (if (not (null? splits))
-              (let ((table
-                     (make-split-table
-                      splits
-                      options
-                      (get-subtotal-pred optname-prime-sortkey
-                                         optname-prime-subtotal
-                                         optname-prime-date-subtotal)
-                      (get-subtotal-pred optname-sec-sortkey
-                                         optname-sec-subtotal
-                                         optname-sec-date-subtotal)
-                      (get-subheading-renderer optname-prime-sortkey
+          (if (null? splits)
+
+              ;; error condition: no splits found
+              (gnc:html-document-add-object!
+               document
+               (gnc:make-html-text
+                (gnc:html-markup-h2 NO-MATCHING-TRANS-HEADER)
+                (gnc:html-markup-p NO-MATCHING-TRANS-TEXT)))
+
+              (let ((table (make-split-table
+                            splits options
+                            (get-subtotal-pred optname-prime-sortkey
                                                optname-prime-subtotal
                                                optname-prime-date-subtotal)
-                      (get-subheading-renderer optname-sec-sortkey
+                            (get-subtotal-pred optname-sec-sortkey
                                                optname-sec-subtotal
                                                optname-sec-date-subtotal)
-                      (get-subtotal-renderer   optname-prime-sortkey
-                                               optname-prime-subtotal
-                                               optname-prime-date-subtotal)
-                      (get-subtotal-renderer   optname-sec-sortkey
-                                               optname-sec-subtotal
-                                               optname-sec-date-subtotal))))
+                            (get-subheading-renderer optname-prime-sortkey
+                                                     optname-prime-subtotal
+                                                     optname-prime-date-subtotal)
+                            (get-subheading-renderer optname-sec-sortkey
+                                                     optname-sec-subtotal
+                                                     optname-sec-date-subtotal)
+                            (get-subtotal-renderer   optname-prime-sortkey
+                                                     optname-prime-subtotal
+                                                     optname-prime-date-subtotal)
+                            (get-subtotal-renderer   optname-sec-sortkey
+                                                     optname-sec-subtotal
+                                                     optname-sec-date-subtotal))))
+
+                (gnc:html-document-set-title! document report-title)
 
-                (gnc:html-document-set-title! document
-                                              report-title)
                 (gnc:html-document-add-object!
                  document
                  (gnc:make-html-text
                   (gnc:html-markup-h3
-                   (display-date-interval begindate enddate))))
-                (gnc:html-document-add-object!
-                 document
-                 table)
-                (qof-query-destroy query))
-              ;; error condition: no splits found
-              (let ((p (gnc:make-html-text)))
-                (gnc:html-text-append!
-                 p
-                 (gnc:html-markup-h2
-                  (_ "No matching transactions found"))
-                 (gnc:html-markup-p
-                  (_ "No transactions were found that \
-match the time interval and account selection specified \
-in the Options panel.")))
-                (gnc:html-document-add-object! document p))))
-
-        (if (null? c_account_0)
+                   (sprintf #f
+                           (_ "From %s to %s")
+                           (gnc-print-date begindate)
+                           (gnc-print-date enddate)))))
 
-            ;; error condition: no accounts specified
-            (gnc:html-document-add-object!
-             document
-             (gnc:html-make-no-account-warning
-              report-title (gnc:report-id report-obj)))
+                (gnc:html-document-add-object! document table)
 
-            ;; error condition: accounts were specified but none matcher string/regex
-            (gnc:html-document-add-object!
-             document
-             (gnc:make-html-text
-              (gnc:html-markup-h2
-               (N_ "No accounts were matched"))
-              (gnc:html-markup-p
-               (N_ "The account matcher specified in the report options did not match any accounts."))))))
+                (qof-query-destroy query)))))
 
     (gnc:report-finished)
+
     document))
 
 ;; Define the report.
@@ -1712,5 +1662,4 @@ in the Options panel.")))
  'report-guid "2fe3b9833af044abb929a88d5a59620f"
 
  'options-generator trep-options-generator
-
  'renderer trep-renderer)

commit 082811b909c66429157e72688445ec1b541a0f17
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Dec 10 06:51:24 2017 +0800

    ***reindent and remove trailing whitespace***

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 4957d2f..ed50685 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -79,7 +79,7 @@
 ;; The option-values of the sorting key multichoice option, for
 ;; which a subtotal should be enabled.
 (define subtotal-enabled '(account-name
-                           account-code 
+                           account-code
                            corresponding-acc-name
                            corresponding-acc-code))
 
@@ -100,23 +100,23 @@
      (gnc:timepair-get-year tp-b)))
 
 (define (timepair-same-quarter tp-a tp-b)
-  (and (timepair-same-year tp-a tp-b) 
+  (and (timepair-same-year tp-a tp-b)
        (= (gnc:timepair-get-quarter tp-a)
           (gnc:timepair-get-quarter tp-b))))
 
 (define (timepair-same-month tp-a tp-b)
-  (and (timepair-same-year tp-a tp-b) 
+  (and (timepair-same-year tp-a tp-b)
        (= (gnc:timepair-get-month tp-a)
           (gnc:timepair-get-month tp-b))))
 
 (define (timepair-same-week tp-a tp-b)
   (and (timepair-same-year tp-a tp-b)
        (= (gnc:timepair-get-week tp-a)
-	  (gnc:timepair-get-week tp-b))))
+          (gnc:timepair-get-week tp-b))))
 
 (define (split-same-week-p a b)
   (let ((tp-a (gnc-transaction-get-date-posted (xaccSplitGetParent a)))
-	(tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
+        (tp-b (gnc-transaction-get-date-posted (xaccSplitGetParent b))))
     (timepair-same-week tp-a tp-b)))
 
 (define (split-same-month-p a b)
@@ -135,8 +135,8 @@
     (timepair-same-year tp-a tp-b)))
 
 (define (set-last-row-style! table tag . rest)
-  (let ((arg-list 
-         (cons table 
+  (let ((arg-list
+         (cons table
                (cons (- (gnc:html-table-num-rows table) 1)
                      (cons tag rest)))))
     (apply gnc:html-table-set-row-style! arg-list)))
@@ -153,19 +153,19 @@
 (define (account-namestring account show-account-code show-account-name show-account-full-name)
   ;;# on multi-line splits we can get an empty ('()) account
   (if (null? account)
-        (_ "Split Transaction")
-        (string-append 
-           ;; display account code?
-           (if show-account-code
-                 (string-append (xaccAccountGetCode account) " ")
-                 "")
-           ;; display account name?
-           (if show-account-name
-                 ;; display full account name?
-                 (if show-account-full-name
-                      (gnc-account-get-full-name account)
-                      (xaccAccountGetName account))
-                 ""))))
+      (_ "Split Transaction")
+      (string-append
+       ;; display account code?
+       (if show-account-code
+           (string-append (xaccAccountGetCode account) " ")
+           "")
+       ;; display account name?
+       (if show-account-name
+           ;; display full account name?
+           (if show-account-full-name
+               (gnc-account-get-full-name account)
+               (xaccAccountGetName account))
+           ""))))
 
 ;; render an account subheading - column-vector determines what is displayed
 (define (render-account-subheading
@@ -173,121 +173,121 @@
   (let ((account (xaccSplitGetAccount split)))
     (add-subheading-row (gnc:make-html-text
                          (gnc:html-markup-anchor
-                           (gnc:account-anchor-text account)
-                           (account-namestring account
-                                               (used-sort-account-code      column-vector)
-                                               #t
-                                               (used-sort-account-full-name column-vector))))
+                          (gnc:account-anchor-text account)
+                          (account-namestring account
+                                              (used-sort-account-code      column-vector)
+                                              #t
+                                              (used-sort-account-full-name column-vector))))
                         table width subheading-style)))
 
-(define (render-corresponding-account-subheading 
+(define (render-corresponding-account-subheading
          split table width subheading-style column-vector)
   (let ((account (xaccSplitGetAccount (xaccSplitGetOtherSplit split))))
     (add-subheading-row (gnc:make-html-text
                          (gnc:html-markup-anchor
                           (if (not (null? account))
-                           (gnc:account-anchor-text account)
-                           "")
-                           (account-namestring account
-                                               (used-sort-account-code      column-vector)
-                                               #t
-                                               (used-sort-account-full-name column-vector))))
+                              (gnc:account-anchor-text account)
+                              "")
+                          (account-namestring account
+                                              (used-sort-account-code      column-vector)
+                                              #t
+                                              (used-sort-account-full-name column-vector))))
                         table width subheading-style)))
 
 (define (render-week-subheading split table width subheading-style column-vector)
   (add-subheading-row (gnc:date-get-week-year-string
-		       (gnc:timepair->date
-			(gnc-transaction-get-date-posted
-			 (xaccSplitGetParent split))))
-		      table width subheading-style))
+                       (gnc:timepair->date
+                        (gnc-transaction-get-date-posted
+                         (xaccSplitGetParent split))))
+                      table width subheading-style))
 
 (define (render-month-subheading split table width subheading-style column-vector)
   (add-subheading-row (gnc:date-get-month-year-string
-                      (gnc:timepair->date 
-                       (gnc-transaction-get-date-posted
-                        (xaccSplitGetParent split))))
-                     table width subheading-style))
+                       (gnc:timepair->date
+                        (gnc-transaction-get-date-posted
+                         (xaccSplitGetParent split))))
+                      table width subheading-style))
 
 (define (render-quarter-subheading split table width subheading-style column-vector)
-  (add-subheading-row (gnc:date-get-quarter-year-string 
-                      (gnc:timepair->date 
-                       (gnc-transaction-get-date-posted
-                        (xaccSplitGetParent split))))
-                     table width subheading-style))
+  (add-subheading-row (gnc:date-get-quarter-year-string
+                       (gnc:timepair->date
+                        (gnc-transaction-get-date-posted
+                         (xaccSplitGetParent split))))
+                      table width subheading-style))
 
 (define (render-year-subheading split table width subheading-style column-vector)
-  (add-subheading-row (gnc:date-get-year-string 
-                      (gnc:timepair->date 
-                       (gnc-transaction-get-date-posted
-                        (xaccSplitGetParent split))))
+  (add-subheading-row (gnc:date-get-year-string
+                       (gnc:timepair->date
+                        (gnc-transaction-get-date-posted
+                         (xaccSplitGetParent split))))
                       table width subheading-style))
 
 
-(define (add-subtotal-row table width subtotal-string subtotal-collector 
+(define (add-subtotal-row table width subtotal-string subtotal-collector
                           subtotal-style export?)
   (let ((currency-totals (subtotal-collector
                           'format gnc:make-gnc-monetary #f))
         (blanks (gnc:make-html-table-cell/size 1 (- width 1) #f)))
     (gnc:html-table-append-row/markup!
      table
-     subtotal-style 
+     subtotal-style
      (if export?
-      (append! (cons (gnc:make-html-table-cell/markup "total-label-cell" subtotal-string)
-                     (gnc:html-make-empty-cells (- width 2)))
-               (list (gnc:make-html-table-cell/markup 
-                      "total-number-cell"
-                      (car currency-totals))))
-     (list (gnc:make-html-table-cell/size/markup 1 (- width 1) "total-label-cell"
-                                          subtotal-string)
-           (gnc:make-html-table-cell/markup 
-            "total-number-cell"
-             (car currency-totals)))))
+         (append! (cons (gnc:make-html-table-cell/markup "total-label-cell" subtotal-string)
+                        (gnc:html-make-empty-cells (- width 2)))
+                  (list (gnc:make-html-table-cell/markup
+                         "total-number-cell"
+                         (car currency-totals))))
+         (list (gnc:make-html-table-cell/size/markup 1 (- width 1) "total-label-cell"
+                                                     subtotal-string)
+               (gnc:make-html-table-cell/markup
+                "total-number-cell"
+                (car currency-totals)))))
     (for-each (lambda (currency)
-                (gnc:html-table-append-row/markup! 
+                (gnc:html-table-append-row/markup!
                  table
                  subtotal-style
                  (append!
                   (if export?
-                   (gnc:html-make-empty-cells (- width 1))
-                   (list blanks))
-                         (list (gnc:make-html-table-cell/markup
-                                "total-number-cell" currency)))))
+                      (gnc:html-make-empty-cells (- width 1))
+                      (list blanks))
+                  (list (gnc:make-html-table-cell/markup
+                         "total-number-cell" currency)))))
               (cdr currency-totals))))
 
 (define (total-string str) (string-append (_ "Total For ") str))
 
-(define (render-account-subtotal 
+(define (render-account-subtotal
          table width split total-collector subtotal-style column-vector export?)
-    (add-subtotal-row table width 
-                      (total-string (account-namestring (xaccSplitGetAccount split)
-                                                        (used-sort-account-code      column-vector)
-                                                        #t
-                                                        (used-sort-account-full-name column-vector)))
-                      total-collector subtotal-style export?))
+  (add-subtotal-row table width
+                    (total-string (account-namestring (xaccSplitGetAccount split)
+                                                      (used-sort-account-code      column-vector)
+                                                      #t
+                                                      (used-sort-account-full-name column-vector)))
+                    total-collector subtotal-style export?))
 
 (define (render-corresponding-account-subtotal
          table width split total-collector subtotal-style column-vector export?)
-    (add-subtotal-row table width
-                      (total-string (account-namestring (xaccSplitGetAccount
-                                                          (xaccSplitGetOtherSplit split))
-                                                        (used-sort-account-code      column-vector)
-                                                        #t
-                                                        (used-sort-account-full-name column-vector)))
+  (add-subtotal-row table width
+                    (total-string (account-namestring (xaccSplitGetAccount
+                                                       (xaccSplitGetOtherSplit split))
+                                                      (used-sort-account-code      column-vector)
+                                                      #t
+                                                      (used-sort-account-full-name column-vector)))
                     total-collector subtotal-style export?))
 
 (define (render-week-subtotal
-	 table width split total-collector subtotal-style column-vector export?)
+         table width split total-collector subtotal-style column-vector export?)
   (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
-				 (xaccSplitGetParent split)))))
+                                 (xaccSplitGetParent split)))))
     (add-subtotal-row table width
-		      (total-string (gnc:date-get-week-year-string tm))
-		      total-collector subtotal-style export?)))
+                      (total-string (gnc:date-get-week-year-string tm))
+                      total-collector subtotal-style export?)))
 
 (define (render-month-subtotal
          table width split total-collector subtotal-style column-vector export?)
   (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
                                  (xaccSplitGetParent split)))))
-    (add-subtotal-row table width 
+    (add-subtotal-row table width
                       (total-string (gnc:date-get-month-year-string tm))
                       total-collector subtotal-style export?)))
 
@@ -296,15 +296,15 @@
          table width split total-collector subtotal-style column-vector export?)
   (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
                                  (xaccSplitGetParent split)))))
-    (add-subtotal-row table width 
+    (add-subtotal-row table width
                       (total-string (gnc:date-get-quarter-year-string tm))
-                     total-collector subtotal-style export?)))
+                      total-collector subtotal-style export?)))
 
 (define (render-year-subtotal
          table width split total-collector subtotal-style column-vector export?)
   (let ((tm (gnc:timepair->date (gnc-transaction-get-date-posted
                                  (xaccSplitGetParent split)))))
-    (add-subtotal-row table width 
+    (add-subtotal-row table width
                       (total-string (strftime "%Y" tm))
                       total-collector subtotal-style export?)))
 
@@ -334,17 +334,17 @@
 (define (used-account-name columns-used)
   (vector-ref columns-used 4))
 (define (used-other-account-name columns-used)
-  (vector-ref columns-used 5))	
+  (vector-ref columns-used 5))
 (define (used-shares columns-used)
-  (vector-ref columns-used 6))	
+  (vector-ref columns-used 6))
 (define (used-price columns-used)
-  (vector-ref columns-used 7))	
+  (vector-ref columns-used 7))
 (define (used-amount-single columns-used)
-  (vector-ref columns-used 8))	
+  (vector-ref columns-used 8))
 (define (used-amount-double-positive columns-used)
-  (vector-ref columns-used 9))	
+  (vector-ref columns-used 9))
 (define (used-amount-double-negative columns-used)
-  (vector-ref columns-used 10))	
+  (vector-ref columns-used 10))
 (define (used-running-balance columns-used)
   (vector-ref columns-used 11))
 (define (used-account-full-name columns-used)
@@ -366,10 +366,10 @@
 
 (define columns-used-size 20)
 
-(define (num-columns-required columns-used)  
-  (do ((i 0 (+ i 1)) 
-       (col-req 0 col-req)) 
-      ((>= i columns-used-size) col-req)
+(define (num-columns-required columns-used)
+  (do ((i 0 (+ i 1))
+       (col-req 0 col-req))
+    ((>= i columns-used-size) col-req)
     ; If column toggle is true, increase column count. But attention:
     ; some toggles only change the meaning of another toggle. Don't count these modifier toggles
     (if (and (not (= i 12)) ; Skip Account Full Name toggle - modifies Account Name column
@@ -378,17 +378,17 @@
              (not (= i 18)) ; Skip Sort Account Full Name - modifies Account Name subheading
              (not (= i 19)) ; Skip Note toggle - modifies Memo column
              (vector-ref columns-used i))
-      (set! col-req (+ col-req 1)))
+        (set! col-req (+ col-req 1)))
     ; Account Code and Account Name share one column so if both were ticked the
     ; the check above would have set up one column too much. The check below
     ; will compensate these again.
     (if (or (and (= i 14) (vector-ref columns-used 14) (vector-ref columns-used 4)) ; Account Code and Name
             (and (= i 15) (vector-ref columns-used 15) (vector-ref columns-used 5))) ; Other Account Code and Name
-      (set! col-req (- col-req 1)))))
+        (set! col-req (- col-req 1)))))
 
-(define (build-column-used options)   
+(define (build-column-used options)
   (define (opt-val section name)
-    (gnc:option-value 
+    (gnc:option-value
      (gnc:lookup-option options section name)))
   (let ((column-list (make-vector columns-used-size #f))
         (is-single? (eq? (opt-val gnc:pagename-display optname-detail-level) 'single)))
@@ -444,14 +444,14 @@
         (addto! heading-list (_ "Reconciled Date")))
     (if (used-num column-vector)
         (addto! heading-list (if (and (qof-book-use-split-action-for-num-field
-                                                        (gnc-get-current-book))
+                                       (gnc-get-current-book))
                                       (if (gnc:lookup-option options
-                                                    gnc:pagename-display
-                                                    (N_ "Trans Number"))
-                                          (gnc:option-value 
-                                            (gnc:lookup-option options
-                                                    gnc:pagename-display
-                                                    (N_ "Trans Number")))
+                                                             gnc:pagename-display
+                                                             (N_ "Trans Number"))
+                                          (gnc:option-value
+                                           (gnc:lookup-option options
+                                                              gnc:pagename-display
+                                                              (N_ "Trans Number")))
                                           #f))
                                  (_ "Num/T-Num")
                                  (_ "Num"))))
@@ -484,7 +484,7 @@
                        row-style account-types-to-reverse transaction-row?)
 
   (define (opt-val section name)
-    (gnc:option-value 
+    (gnc:option-value
      (gnc:lookup-option options section name)))
 
   (let* ((row-contents '())
@@ -495,92 +495,92 @@
          (currency (if (not (null? account))
                        (xaccAccountGetCommodity account)
                        (gnc-default-currency)))
-	 (report-currency (if (opt-val gnc:pagename-general optname-common-currency)
-			       (opt-val gnc:pagename-general optname-currency)
-			       currency))
+         (report-currency (if (opt-val gnc:pagename-general optname-common-currency)
+                              (opt-val gnc:pagename-general optname-currency)
+                              currency))
          (damount (if (gnc:split-voided? split)
-					 (xaccSplitVoidFormerAmount split)
-					 (xaccSplitGetAmount split)))
-	 (trans-date (gnc-transaction-get-date-posted parent))
-	 (split-value (gnc:exchange-by-pricedb-nearest
-		       (gnc:make-gnc-monetary 
-			currency
-			(if (member account-type account-types-to-reverse) 
-			    (gnc-numeric-neg damount)
-			    damount))
-		       report-currency
-		       ;; Use midday as the transaction time so it matches a price
-		       ;; on the same day.  Otherwise it uses midnight which will
-		       ;; likely match a price on the previous day
-		       (timespecCanonicalDayTime trans-date))))
-    
+                      (xaccSplitVoidFormerAmount split)
+                      (xaccSplitGetAmount split)))
+         (trans-date (gnc-transaction-get-date-posted parent))
+         (split-value (gnc:exchange-by-pricedb-nearest
+                       (gnc:make-gnc-monetary
+                        currency
+                        (if (member account-type account-types-to-reverse)
+                            (gnc-numeric-neg damount)
+                            damount))
+                       report-currency
+                       ;; Use midday as the transaction time so it matches a price
+                       ;; on the same day.  Otherwise it uses midnight which will
+                       ;; likely match a price on the previous day
+                       (timespecCanonicalDayTime trans-date))))
+
     (if (used-date column-vector)
         (addto! row-contents
                 (if transaction-row?
                     (gnc:make-html-table-cell/markup "date-cell"
-                        (gnc-print-date (gnc-transaction-get-date-posted parent)))
+                                                     (gnc-print-date (gnc-transaction-get-date-posted parent)))
                     " ")))
     (if (used-reconciled-date column-vector)
         (addto! row-contents
                 (gnc:make-html-table-cell/markup "date-cell"
-		    (let ((date (gnc-split-get-date-reconciled split)))
-		      (if (equal? date (cons 0 0))
-		          " "
-		          (gnc-print-date date))))))
+                                                 (let ((date (gnc-split-get-date-reconciled split)))
+                                                   (if (equal? date (cons 0 0))
+                                                       " "
+                                                       (gnc-print-date date))))))
     (if (used-num column-vector)
         (addto! row-contents
                 (if transaction-row?
                     (if (qof-book-use-split-action-for-num-field
-                                                        (gnc-get-current-book))
+                         (gnc-get-current-book))
                         (let* ((num (gnc-get-num-action parent split))
                                (t-num (if (if (gnc:lookup-option options
-                                                    gnc:pagename-display
-                                                    (N_ "Trans Number"))
+                                                                 gnc:pagename-display
+                                                                 (N_ "Trans Number"))
                                               (opt-val gnc:pagename-display
-                                                    (N_ "Trans Number"))
+                                                       (N_ "Trans Number"))
                                               #f)
                                           (gnc-get-num-action parent #f)
                                           ""))
                                (num-string (if (equal? t-num "")
                                                num
                                                (string-append num "/" t-num))))
-                              (gnc:make-html-table-cell/markup "text-cell"
-                                   num-string))
+                          (gnc:make-html-table-cell/markup "text-cell"
+                                                           num-string))
                         (gnc:make-html-table-cell/markup "text-cell"
-                            (gnc-get-num-action parent split)))
+                                                         (gnc-get-num-action parent split)))
                     " ")))
 
     (if (used-description column-vector)
         (addto! row-contents
                 (if transaction-row?
                     (gnc:make-html-table-cell/markup "text-cell"
-                        (xaccTransGetDescription parent))
+                                                     (xaccTransGetDescription parent))
                     " ")))
-    
+
     (if (used-memo column-vector)
         (let ((memo (xaccSplitGetMemo split)))
           (if (and (equal? memo "") (used-notes column-vector))
               (addto! row-contents (xaccTransGetNotes parent))
               (addto! row-contents memo))))
-    
+
     (if (or (used-account-name column-vector) (used-account-code column-vector))
-       (addto! row-contents (account-namestring account
-                                                (used-account-code      column-vector)
-                                                (used-account-name      column-vector)
-                                                (used-account-full-name column-vector))))
-    
+        (addto! row-contents (account-namestring account
+                                                 (used-account-code      column-vector)
+                                                 (used-account-name      column-vector)
+                                                 (used-account-full-name column-vector))))
+
     (if (or (used-other-account-name column-vector) (used-other-account-code column-vector))
-       (addto! row-contents (account-namestring (xaccSplitGetAccount
-                                                   (xaccSplitGetOtherSplit split))
-                                                (used-other-account-code      column-vector)
-                                                (used-other-account-name      column-vector)
-                                                (used-other-account-full-name column-vector))))
-    
+        (addto! row-contents (account-namestring (xaccSplitGetAccount
+                                                  (xaccSplitGetOtherSplit split))
+                                                 (used-other-account-code      column-vector)
+                                                 (used-other-account-name      column-vector)
+                                                 (used-other-account-full-name column-vector))))
+
     (if (used-shares column-vector)
         (addto! row-contents (xaccSplitGetAmount split)))
     (if (used-price column-vector)
-        (addto! 
-         row-contents 
+        (addto!
+         row-contents
          (gnc:make-gnc-monetary (xaccTransGetCurrency parent)
                                 (xaccSplitGetSharePrice split))))
     (if (used-amount-single column-vector)
@@ -600,15 +600,15 @@
                      "number-cell" (gnc:html-transaction-anchor parent (gnc:monetary-neg split-value))))
             (addto! row-contents " ")))
     (if (used-running-balance column-vector)
-	(begin
-	  (gnc:debug "split is " split)
-	  (gnc:debug "split get balance:" (xaccSplitGetBalance split))
-	  (addto! row-contents
-		  (gnc:make-html-table-cell/markup
-		   "number-cell"
-		   (gnc:make-gnc-monetary currency
-					  (xaccSplitGetBalance split))))))
-	(gnc:html-table-append-row/markup! table row-style
+        (begin
+          (gnc:debug "split is " split)
+          (gnc:debug "split get balance:" (xaccSplitGetBalance split))
+          (addto! row-contents
+                  (gnc:make-html-table-cell/markup
+                   "number-cell"
+                   (gnc:make-gnc-monetary currency
+                                          (xaccSplitGetBalance split))))))
+    (gnc:html-table-append-row/markup! table row-style
                                        (reverse row-contents))
     split-value))
 
@@ -619,24 +619,24 @@
   (define gnc:*transaction-report-options* (gnc:new-options))
   (define (gnc:register-trep-option new-option)
     (gnc:register-option gnc:*transaction-report-options* new-option))
-  
+
   ;; General options
-  
+
   (gnc:options-add-date-interval!
    gnc:*transaction-report-options*
    gnc:pagename-general (N_ "Start Date") (N_ "End Date") "a")
-  
-  
+
+
   (gnc:register-trep-option
    (gnc:make-complex-boolean-option
     gnc:pagename-general optname-common-currency
     "e" (N_ "Convert all transactions into a common currency.") #f
     #f
     (lambda (x) (gnc-option-db-set-option-selectable-by-name
-		 gnc:*transaction-report-options*
-		 gnc:pagename-general
-		 optname-currency
-		 x))
+                 gnc:*transaction-report-options*
+                 gnc:pagename-general
+                 optname-currency
+                 x))
     ))
 
   (gnc:options-add-currency!
@@ -645,7 +645,7 @@
   (gnc:register-trep-option
    (gnc:make-simple-boolean-option
     gnc:pagename-general optname-table-export
-    "g" (N_ "Formats the table suitable for cut & paste exporting with extra cells.") #f))  
+    "g" (N_ "Formats the table suitable for cut & paste exporting with extra cells.") #f))
 
   (gnc:register-trep-option
    (gnc:make-string-option
@@ -665,7 +665,7 @@ tags within description, notes or memo. ")
     #f))
 
   ;; Accounts options
-  
+
   ;; account to do report on
   (gnc:register-trep-option
    (gnc:make-account-list-option
@@ -704,13 +704,13 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
     (lambda ()
       ;; FIXME : gnc:get-current-accounts disappeared.
       (let* ((current-accounts '())
-	     (root (gnc-get-current-root-account))
-	     (num-accounts (gnc-account-n-children root))
-	     (first-account (gnc-account-nth-child root 0)))
-	(cond ((not (null? current-accounts))
-	       (list (car current-accounts)))
-	      ((> num-accounts 0) (list first-account))
-	      (else '()))))
+             (root (gnc-get-current-root-account))
+             (num-accounts (gnc-account-n-children root))
+             (first-account (gnc-account-nth-child root 0)))
+        (cond ((not (null? current-accounts))
+               (list (car current-accounts)))
+              ((> num-accounts 0) (list first-account))
+              (else '()))))
     #f #t))
 
   (gnc:register-trep-option
@@ -719,15 +719,15 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
     "c" (N_ "Filter account.")
     'none
     (list (vector 'none
-		  (N_ "None")
-		  (N_ "Do not do any filtering."))
-	  (vector 'include
-		  (N_ "Include Transactions to/from Filter Accounts")
-		  (N_ "Include transactions to/from filter accounts only."))
-	  (vector 'exclude
-		  (N_ "Exclude Transactions to/from Filter Accounts")
-		  (N_ "Exclude transactions to/from all filter accounts."))
-	  )))
+                  (N_ "None")
+                  (N_ "Do not do any filtering."))
+          (vector 'include
+                  (N_ "Include Transactions to/from Filter Accounts")
+                  (N_ "Include transactions to/from filter accounts only."))
+          (vector 'exclude
+                  (N_ "Exclude Transactions to/from Filter Accounts")
+                  (N_ "Exclude transactions to/from all filter accounts."))
+          )))
 
   ;;
 
@@ -737,23 +737,23 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
     "d" (N_ "How to handle void transactions.")
     'non-void-only
     (list (vector
-	   'non-void-only
-	   (N_ "Non-void only")
-	   (N_ "Show only non-voided transactions."))
-	  (vector
-	   'void-only
-	   (N_ "Void only")
-	   (N_ "Show only voided transactions."))
-	  (vector 
-	   'both
-	   (N_ "Both")
-	   (N_ "Show both (and include void transactions in totals).")))))
+           'non-void-only
+           (N_ "Non-void only")
+           (N_ "Show only non-voided transactions."))
+          (vector
+           'void-only
+           (N_ "Void only")
+           (N_ "Show only voided transactions."))
+          (vector
+           'both
+           (N_ "Both")
+           (N_ "Show both (and include void transactions in totals).")))))
 
   ;; Sorting options
-      
+
   (let ((options gnc:*transaction-report-options*)
 
-        (key-choice-list 
+        (key-choice-list
          (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
              (list (vector 'none
                            (N_ "None")
@@ -779,22 +779,22 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                            (N_ "Register Order")
                            (N_ "Sort as in the register."))
 
-                   (vector 'corresponding-acc-name 
+                   (vector 'corresponding-acc-name
                            (N_ "Other Account Name")
                            (N_ "Sort by account transferred from/to's name."))
 
                    (vector 'corresponding-acc-code
                            (N_ "Other Account Code")
                            (N_ "Sort by account transferred from/to's code."))
-               
+
                    (vector 'amount
                            (N_ "Amount")
                            (N_ "Sort by amount."))
-               
+
                    (vector 'description
                            (N_ "Description")
                            (N_ "Sort by description."))
-               
+
                    (vector 'number
                            (N_ "Number/Action")
                            (N_ "Sort by check number/action."))
@@ -802,7 +802,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                    (vector 't-number
                            (N_ "Transaction Number")
                            (N_ "Sort by transaction number."))
-               
+
                    (vector 'memo
                            (N_ "Memo")
                            (N_ "Sort by memo.")))
@@ -830,22 +830,22 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                            (N_ "Register Order")
                            (N_ "Sort as in the register."))
 
-                   (vector 'corresponding-acc-name 
+                   (vector 'corresponding-acc-name
                            (N_ "Other Account Name")
                            (N_ "Sort by account transferred from/to's name."))
 
                    (vector 'corresponding-acc-code
                            (N_ "Other Account Code")
                            (N_ "Sort by account transferred from/to's code."))
-               
+
                    (vector 'amount
                            (N_ "Amount")
                            (N_ "Sort by amount."))
-               
+
                    (vector 'description
                            (N_ "Description")
                            (N_ "Sort by description."))
-               
+
                    (vector 'number
                            (N_ "Number")
                            (N_ "Sort by check/transaction number."))
@@ -854,7 +854,7 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                            (N_ "Memo")
                            (N_ "Sort by memo.")))))
 
-        (ascending-choice-list 
+        (ascending-choice-list
          (list
           (vector 'ascend
                   (N_ "Ascending")
@@ -928,21 +928,21 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
       (lambda (x)
         (set! prime-sortkey x)
         (apply-selectable-by-name-sorting-options))))
-    
+
     (gnc:register-trep-option
      (gnc:make-simple-boolean-option
       pagename-sorting optname-full-account-name
       "j1"
       (N_ "Show the full account name for subtotals and subtitles?")
       #f))
-    
+
     (gnc:register-trep-option
      (gnc:make-simple-boolean-option
       pagename-sorting optname-show-account-code
       "j2"
       (N_ "Show the account code for subtotals and subtitles?")
       #f))
-    
+
     (gnc:register-trep-option
      (gnc:make-complex-boolean-option
       pagename-sorting optname-prime-subtotal
@@ -959,14 +959,14 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
       "e2" (N_ "Do a date subtotal.")
       'monthly
       subtotal-choice-list))
-    
+
     (gnc:register-trep-option
      (gnc:make-multichoice-option
       pagename-sorting optname-prime-sortorder
       "e" (N_ "Order of primary sorting.")
       'ascend
       ascending-choice-list))
-    
+
     ;; Secondary sorting criterion
     (gnc:register-trep-option
      (gnc:make-multichoice-callback-option
@@ -995,144 +995,144 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
       "i2" (N_ "Do a date subtotal.")
       'monthly
       subtotal-choice-list))
-    
+
     (gnc:register-trep-option
      (gnc:make-multichoice-option
       pagename-sorting optname-sec-sortorder
       "i" (N_ "Order of Secondary sorting.")
       'ascend
       ascending-choice-list)))
-  
-  ;; Display options
-  
-    (let ((options gnc:*transaction-report-options*)
-          (disp-memo? #t)
-          (disp-accname? #t)
-          (disp-other-accname? #f)
-          (is-single? #t))
-
-      (define (apply-selectable-by-name-display-options)
-        (gnc-option-db-set-option-selectable-by-name
-         options gnc:pagename-display (N_ "Use Full Account Name")
-         disp-accname?)
 
-        (gnc-option-db-set-option-selectable-by-name
-         options gnc:pagename-display (N_ "Other Account Name")
-         is-single?)
-
-        (gnc-option-db-set-option-selectable-by-name
-         options gnc:pagename-display (N_ "Use Full Other Account Name")
-         (and disp-other-accname? is-single?))
+  ;; Display options
 
-        (gnc-option-db-set-option-selectable-by-name
-         options gnc:pagename-display (N_ "Other Account Code")
-         is-single?)
+  (let ((options gnc:*transaction-report-options*)
+        (disp-memo? #t)
+        (disp-accname? #t)
+        (disp-other-accname? #f)
+        (is-single? #t))
+
+    (define (apply-selectable-by-name-display-options)
+      (gnc-option-db-set-option-selectable-by-name
+       options gnc:pagename-display (N_ "Use Full Account Name")
+       disp-accname?)
+
+      (gnc-option-db-set-option-selectable-by-name
+       options gnc:pagename-display (N_ "Other Account Name")
+       is-single?)
+
+      (gnc-option-db-set-option-selectable-by-name
+       options gnc:pagename-display (N_ "Use Full Other Account Name")
+       (and disp-other-accname? is-single?))
+
+      (gnc-option-db-set-option-selectable-by-name
+       options gnc:pagename-display (N_ "Other Account Code")
+       is-single?)
+
+      (gnc-option-db-set-option-selectable-by-name
+       options gnc:pagename-display (N_ "Notes")
+       disp-memo?))
+
+    (for-each
+     (lambda (l)
+       (gnc:register-trep-option
+        (gnc:make-simple-boolean-option
+         gnc:pagename-display (car l) (cadr l) (caddr l) (cadddr l))))
+     ;; One list per option here with: option-name, sort-tag,
+     ;; help-string, default-value
+     (list
+      (list (N_ "Date")                         "a"  (N_ "Display the date?") #t)
+      (list (N_ "Reconciled Date")              "a2" (N_ "Display the reconciled date?") #f)
+      (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
+          (list (N_ "Num/Action")               "b"  (N_ "Display the check number?") #t)
+          (list (N_ "Num")                      "b"  (N_ "Display the check number?") #t))
+      (list (N_ "Description")                  "c"  (N_ "Display the description?") #t)
+      (list (N_ "Notes")                        "d2" (N_ "Display the notes if the memo is unavailable?") #t)
+      ;; account name option appears here
+      (list (N_ "Use Full Account Name")        "f"  (N_ "Display the full account name?") #t)
+      (list (N_ "Account Code")                 "g"  (N_ "Display the account code?") #f)
+      ;; other account name option appears here
+      (list (N_ "Use Full Other Account Name")  "i"  (N_ "Display the full account name?") #f)
+      (list (N_ "Other Account Code")           "j"  (N_ "Display the other account code?") #f)
+      (list (N_ "Shares")                       "k"  (N_ "Display the number of shares?") #f)
+      (list (N_ "Price")                        "l"  (N_ "Display the shares price?") #f)
+      ;; note the "Amount" multichoice option in between here
+      (list (N_ "Running Balance")              "n"  (N_ "Display a running balance?") #f)
+      (list (N_ "Totals")                       "o"  (N_ "Display the totals?") #t)))
 
-        (gnc-option-db-set-option-selectable-by-name
-         options gnc:pagename-display (N_ "Notes")
-         disp-memo?))
-
-  (for-each
-   (lambda (l)
-     (gnc:register-trep-option
-      (gnc:make-simple-boolean-option
-       gnc:pagename-display (car l) (cadr l) (caddr l) (cadddr l))))
-   ;; One list per option here with: option-name, sort-tag,
-   ;; help-string, default-value
-   (list
-    (list (N_ "Date")                         "a"  (N_ "Display the date?") #t)
-    (list (N_ "Reconciled Date")              "a2" (N_ "Display the reconciled date?") #f)
     (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-        (list (N_ "Num/Action")               "b"  (N_ "Display the check number?") #t)
-        (list (N_ "Num")                      "b"  (N_ "Display the check number?") #t))
-    (list (N_ "Description")                  "c"  (N_ "Display the description?") #t)
-    (list (N_ "Notes")                        "d2" (N_ "Display the notes if the memo is unavailable?") #t)
-    ;; account name option appears here
-    (list (N_ "Use Full Account Name")        "f"  (N_ "Display the full account name?") #t)
-    (list (N_ "Account Code")                 "g"  (N_ "Display the account code?") #f)
-    ;; other account name option appears here
-    (list (N_ "Use Full Other Account Name")  "i"  (N_ "Display the full account name?") #f)
-    (list (N_ "Other Account Code")           "j"  (N_ "Display the other account code?") #f)
-    (list (N_ "Shares")                       "k"  (N_ "Display the number of shares?") #f)
-    (list (N_ "Price")                        "l"  (N_ "Display the shares price?") #f)
-    ;; note the "Amount" multichoice option in between here
-    (list (N_ "Running Balance")              "n"  (N_ "Display a running balance?") #f)
-    (list (N_ "Totals")                       "o"  (N_ "Display the totals?") #t)))
-
-  (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
-      (gnc:register-trep-option
-       (gnc:make-simple-boolean-option
-        gnc:pagename-display (N_ "Trans Number")
-                                    "b2" (N_ "Display the trans number?") #f)))
-
-  ;; Add an option to display the memo, and disable the notes option
-  ;; when memos are not included.
-  (gnc:register-trep-option
-   (gnc:make-complex-boolean-option
-    gnc:pagename-display (N_ "Memo")
-    "d"  (N_ "Display the memo?") #t
-    #f
-    (lambda (x)
+        (gnc:register-trep-option
+         (gnc:make-simple-boolean-option
+          gnc:pagename-display (N_ "Trans Number")
+          "b2" (N_ "Display the trans number?") #f)))
+
+    ;; Add an option to display the memo, and disable the notes option
+    ;; when memos are not included.
+    (gnc:register-trep-option
+     (gnc:make-complex-boolean-option
+      gnc:pagename-display (N_ "Memo")
+      "d"  (N_ "Display the memo?") #t
+      #f
+      (lambda (x)
         (set! disp-memo? x)
         (apply-selectable-by-name-display-options))))
 
-  ;; Ditto for Account Name #t -> Use Full Account Name is selectable
-  (gnc:register-trep-option
-   (gnc:make-complex-boolean-option
-    gnc:pagename-display (N_ "Account Name")
-    "e"  (N_ "Display the account name?") #t
-    #f
-    (lambda (x)
+    ;; Ditto for Account Name #t -> Use Full Account Name is selectable
+    (gnc:register-trep-option
+     (gnc:make-complex-boolean-option
+      gnc:pagename-display (N_ "Account Name")
+      "e"  (N_ "Display the account name?") #t
+      #f
+      (lambda (x)
         (set! disp-accname? x)
         (apply-selectable-by-name-display-options))))
 
-  ;; Ditto for Other Account Name #t -> Use Full Other Account Name is selectable
-  (gnc:register-trep-option
-   (gnc:make-complex-boolean-option
-    gnc:pagename-display (N_ "Other Account Name")
-    "h5"  (N_ "Display the other account name? (if this is a split transaction, this parameter is guessed).") #f
-    #f
-    (lambda (x)
+    ;; Ditto for Other Account Name #t -> Use Full Other Account Name is selectable
+    (gnc:register-trep-option
+     (gnc:make-complex-boolean-option
+      gnc:pagename-display (N_ "Other Account Name")
+      "h5"  (N_ "Display the other account name? (if this is a split transaction, this parameter is guessed).") #f
+      #f
+      (lambda (x)
         (set! disp-other-accname? x)
         (apply-selectable-by-name-display-options))))
 
-  (gnc:register-trep-option
-   (gnc:make-multichoice-callback-option
-    gnc:pagename-display optname-detail-level
-    "h" (N_ "Amount of detail to display per transaction.")
-    'single
-    (list (vector 'multi-line
-                  (N_ "Multi-Line")
-                  (N_ "Display all splits in a transaction on a separate line."))
-          (vector 'single
-                  (N_ "Single")
-                  (N_ "Display one line per transaction, merging multiple splits where required.")))
-    #f
-    (lambda (x)
+    (gnc:register-trep-option
+     (gnc:make-multichoice-callback-option
+      gnc:pagename-display optname-detail-level
+      "h" (N_ "Amount of detail to display per transaction.")
+      'single
+      (list (vector 'multi-line
+                    (N_ "Multi-Line")
+                    (N_ "Display all splits in a transaction on a separate line."))
+            (vector 'single
+                    (N_ "Single")
+                    (N_ "Display one line per transaction, merging multiple splits where required.")))
+      #f
+      (lambda (x)
         (set! is-single? (eq? x 'single))
         (apply-selectable-by-name-display-options))))
 
-  (gnc:register-trep-option
-   (gnc:make-multichoice-option
-    gnc:pagename-display (N_ "Amount")
-    "m" (N_ "Display the amount?")  
-    'single
-    (list
-     (vector 'none (N_ "None") (N_ "No amount display."))
-     (vector 'single (N_ "Single") (N_ "Single Column Display."))
-     (vector 'double (N_ "Double") (N_ "Two Column Display.")))))
-  
-  (gnc:register-trep-option
-   (gnc:make-multichoice-option
-    gnc:pagename-display (N_ "Sign Reverses")
-    "p" (N_ "Reverse amount display for certain account types.")
-    'credit-accounts
-    (list 
-     (vector 'none (N_ "None") (N_ "Don't change any displayed amounts."))
-     (vector 'income-expense (N_ "Income and Expense")
-             (N_ "Reverse amount display for Income and Expense Accounts."))
-     (vector 'credit-accounts (N_ "Credit Accounts")
-             (N_ "Reverse amount display for Liability, Payable, Equity, \
+    (gnc:register-trep-option
+     (gnc:make-multichoice-option
+      gnc:pagename-display (N_ "Amount")
+      "m" (N_ "Display the amount?")
+      'single
+      (list
+       (vector 'none (N_ "None") (N_ "No amount display."))
+       (vector 'single (N_ "Single") (N_ "Single Column Display."))
+       (vector 'double (N_ "Double") (N_ "Two Column Display.")))))
+
+    (gnc:register-trep-option
+     (gnc:make-multichoice-option
+      gnc:pagename-display (N_ "Sign Reverses")
+      "p" (N_ "Reverse amount display for certain account types.")
+      'credit-accounts
+      (list
+       (vector 'none (N_ "None") (N_ "Don't change any displayed amounts."))
+       (vector 'income-expense (N_ "Income and Expense")
+               (N_ "Reverse amount display for Income and Expense Accounts."))
+       (vector 'credit-accounts (N_ "Credit Accounts")
+               (N_ "Reverse amount display for Liability, Payable, Equity, \
 Credit Card, and Income accounts."))))))
 
 
@@ -1148,7 +1148,7 @@ Credit Card, and Income accounts."))))))
     (sprintf #f (_ "From %s To %s") begin-string end-string)))
 
 (define (get-primary-subtotal-style options)
-  (let ((bgcolor (gnc:lookup-option options 
+  (let ((bgcolor (gnc:lookup-option options
                                     (N_ "Colors")
                                     (N_ "Primary Subtotals/headings"))))
     (list 'attribute (list "bgcolor" (gnc:color-option->html bgcolor)))))
@@ -1187,209 +1187,209 @@ Credit Card, and Income accounts."))))))
                           secondary-subheading-renderer
                           primary-subtotal-renderer
                           secondary-subtotal-renderer)
-  
- (let ((work-to-do (length splits))
-       (work-done 0)
-       (used-columns (build-column-used options)))
-  (define (get-account-types-to-reverse options)
-    (cdr (assq (gnc:option-value 
-                (gnc:lookup-option options
-                                   gnc:pagename-display
-                                   (N_ "Sign Reverses")))
-               account-types-to-reverse-assoc-list)))
-  
-
-  (define (transaction-report-multi-rows-p options)
-    (eq? (gnc:option-value
-          (gnc:lookup-option options gnc:pagename-display optname-detail-level))
-         'multi-line))
-
-  (define (transaction-report-export-p options)
-    (gnc:option-value
-     (gnc:lookup-option options gnc:pagename-general
-       optname-table-export)))
-
-  (define (add-other-split-rows split table used-columns
-                                row-style account-types-to-reverse)
-    (define (other-rows-driver split parent table used-columns i)
-      (let ((current (xaccTransGetSplit parent i)))
-        (cond ((null? current) #f)
-              ((equal? current split)
-               (other-rows-driver split parent table used-columns (+ i 1)))
-              (else (begin
-                      (add-split-row table current used-columns options
-                                     row-style account-types-to-reverse #f)
-                      (other-rows-driver split parent table used-columns
-                                         (+ i 1)))))))
-
-    (other-rows-driver split (xaccSplitGetParent split)
-                       table used-columns 0))
-
-  (define (do-rows-with-subtotals splits 
-                                  table 
-                                  used-columns
-                                  width
-                                  multi-rows?
-                                  odd-row?
-                                  export?
-                                  account-types-to-reverse
-                                  primary-subtotal-pred
-                                  secondary-subtotal-pred 
-                                  primary-subheading-renderer
-                                  secondary-subheading-renderer
-                                  primary-subtotal-renderer
-                                  secondary-subtotal-renderer
-                                  primary-subtotal-collector 
-                                  secondary-subtotal-collector 
-                                  total-collector)
-
-    (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
-    (set! work-done (+ 1 work-done))
-    (if (null? splits)
-        (begin
-          (gnc:html-table-append-row/markup!
-           table
-           def:grand-total-style
-           (list
-            (gnc:make-html-table-cell/size
-             1 width (gnc:make-html-text (gnc:html-markup-hr)))))
-	  (if (gnc:option-value (gnc:lookup-option options "Display" "Totals"))
-	      (render-grand-total table width total-collector export?)))
-	
-        (let* ((current (car splits))
-               (current-row-style (if multi-rows? def:normal-row-style
-                                      (if odd-row? def:normal-row-style 
-                                          def:alternate-row-style)))
-               (rest (cdr splits))
-               (next (if (null? rest) #f
-                         (car rest)))
-               (split-value (add-split-row 
-                             table 
-                             current 
-                             used-columns
-			     options
-                             current-row-style
-                             account-types-to-reverse
-                             #t)))
-          (if multi-rows?
-              (add-other-split-rows
-               current table used-columns def:alternate-row-style
-               account-types-to-reverse))
-
-          (primary-subtotal-collector 'add 
-                                      (gnc:gnc-monetary-commodity
-                                       split-value) 
-                                      (gnc:gnc-monetary-amount
-                                       split-value))
-          (secondary-subtotal-collector 'add
+
+  (let ((work-to-do (length splits))
+        (work-done 0)
+        (used-columns (build-column-used options)))
+    (define (get-account-types-to-reverse options)
+      (cdr (assq (gnc:option-value
+                  (gnc:lookup-option options
+                                     gnc:pagename-display
+                                     (N_ "Sign Reverses")))
+                 account-types-to-reverse-assoc-list)))
+
+
+    (define (transaction-report-multi-rows-p options)
+      (eq? (gnc:option-value
+            (gnc:lookup-option options gnc:pagename-display optname-detail-level))
+           'multi-line))
+
+    (define (transaction-report-export-p options)
+      (gnc:option-value
+       (gnc:lookup-option options gnc:pagename-general
+                          optname-table-export)))
+
+    (define (add-other-split-rows split table used-columns
+                                  row-style account-types-to-reverse)
+      (define (other-rows-driver split parent table used-columns i)
+        (let ((current (xaccTransGetSplit parent i)))
+          (cond ((null? current) #f)
+                ((equal? current split)
+                 (other-rows-driver split parent table used-columns (+ i 1)))
+                (else (begin
+                        (add-split-row table current used-columns options
+                                       row-style account-types-to-reverse #f)
+                        (other-rows-driver split parent table used-columns
+                                           (+ i 1)))))))
+
+      (other-rows-driver split (xaccSplitGetParent split)
+                         table used-columns 0))
+
+    (define (do-rows-with-subtotals splits
+                                    table
+                                    used-columns
+                                    width
+                                    multi-rows?
+                                    odd-row?
+                                    export?
+                                    account-types-to-reverse
+                                    primary-subtotal-pred
+                                    secondary-subtotal-pred
+                                    primary-subheading-renderer
+                                    secondary-subheading-renderer
+                                    primary-subtotal-renderer
+                                    secondary-subtotal-renderer
+                                    primary-subtotal-collector
+                                    secondary-subtotal-collector
+                                    total-collector)
+
+      (gnc:report-percent-done (* 100 (/ work-done work-to-do)))
+      (set! work-done (+ 1 work-done))
+      (if (null? splits)
+          (begin
+            (gnc:html-table-append-row/markup!
+             table
+             def:grand-total-style
+             (list
+              (gnc:make-html-table-cell/size
+               1 width (gnc:make-html-text (gnc:html-markup-hr)))))
+            (if (gnc:option-value (gnc:lookup-option options "Display" "Totals"))
+                (render-grand-total table width total-collector export?)))
+
+          (let* ((current (car splits))
+                 (current-row-style (if multi-rows? def:normal-row-style
+                                        (if odd-row? def:normal-row-style
+                                            def:alternate-row-style)))
+                 (rest (cdr splits))
+                 (next (if (null? rest) #f
+                           (car rest)))
+                 (split-value (add-split-row
+                               table
+                               current
+                               used-columns
+                               options
+                               current-row-style
+                               account-types-to-reverse
+                               #t)))
+            (if multi-rows?
+                (add-other-split-rows
+                 current table used-columns def:alternate-row-style
+                 account-types-to-reverse))
+
+            (primary-subtotal-collector 'add
                                         (gnc:gnc-monetary-commodity
                                          split-value)
                                         (gnc:gnc-monetary-amount
                                          split-value))
-          (total-collector 'add
-                           (gnc:gnc-monetary-commodity split-value)
-                           (gnc:gnc-monetary-amount split-value))
-
-          (if (and primary-subtotal-pred
-                   (or (not next)
-                       (and next
-                            (not (primary-subtotal-pred current next)))))
-              (begin 
-                (if secondary-subtotal-pred
-
-                    (begin
-                      (secondary-subtotal-renderer
-                       table width current
-                       secondary-subtotal-collector
-                       def:secondary-subtotal-style used-columns export?)
-                      (secondary-subtotal-collector 'reset #f #f)))
-
-                (primary-subtotal-renderer table width current
-                                           primary-subtotal-collector
-                                           def:primary-subtotal-style used-columns
-                                           export?)
-
-                (primary-subtotal-collector 'reset #f #f)
-
-                (if next
-                    (begin 
-                      (primary-subheading-renderer
-                       next table width def:primary-subtotal-style used-columns)
-
-                      (if secondary-subtotal-pred
-                          (secondary-subheading-renderer
-                           next 
-                           table 
-                           width def:secondary-subtotal-style used-columns)))))
-
-              (if (and secondary-subtotal-pred
-                       (or (not next)
-                           (and next
-                                (not (secondary-subtotal-pred
-                                      current next)))))
-                  (begin (secondary-subtotal-renderer
-                          table width current
-                          secondary-subtotal-collector
-                          def:secondary-subtotal-style used-columns export?)
-                         (secondary-subtotal-collector 'reset #f #f)
-                         (if next
-                             (secondary-subheading-renderer
-                              next table width
-                              def:secondary-subtotal-style used-columns)))))
-
-          (do-rows-with-subtotals rest 
-                                  table 
-                                  used-columns
-                                  width 
-                                  multi-rows?
-                                  (not odd-row?)
-                                  export?
-                                  account-types-to-reverse
-                                  primary-subtotal-pred 
-                                  secondary-subtotal-pred
-                                  primary-subheading-renderer 
-                                  secondary-subheading-renderer
-                                  primary-subtotal-renderer
-                                  secondary-subtotal-renderer
-                                  primary-subtotal-collector 
-                                  secondary-subtotal-collector 
-                                  total-collector))))
-
-  (let* ((table (gnc:make-html-table))
-         (width (num-columns-required used-columns))
-         (multi-rows? (transaction-report-multi-rows-p options))
-	 (export? (transaction-report-export-p options))
-         (account-types-to-reverse
-          (get-account-types-to-reverse options)))
-
-    (gnc:html-table-set-col-headers!
-     table
-     (make-heading-list used-columns options))
-    ;;     (gnc:warn "Splits:" splits)
-    (if (not (null? splits))
-        (begin
-          (if primary-subheading-renderer 
-              (primary-subheading-renderer
-               (car splits) table width def:primary-subtotal-style used-columns))
-          (if secondary-subheading-renderer
-              (secondary-subheading-renderer
-               (car splits) table width def:secondary-subtotal-style used-columns))
-
-          (do-rows-with-subtotals splits table used-columns width
-                                  multi-rows? #t
-                                  export?
-                                  account-types-to-reverse
-                                  primary-subtotal-pred
-                                  secondary-subtotal-pred
-                                  primary-subheading-renderer
-                                  secondary-subheading-renderer
-                                  primary-subtotal-renderer
-                                  secondary-subtotal-renderer
-                                  (gnc:make-commodity-collector)
-                                  (gnc:make-commodity-collector)
-                                  (gnc:make-commodity-collector))))
-    
-    table)))
+            (secondary-subtotal-collector 'add
+                                          (gnc:gnc-monetary-commodity
+                                           split-value)
+                                          (gnc:gnc-monetary-amount
+                                           split-value))
+            (total-collector 'add
+                             (gnc:gnc-monetary-commodity split-value)
+                             (gnc:gnc-monetary-amount split-value))
+
+            (if (and primary-subtotal-pred
+                     (or (not next)
+                         (and next
+                              (not (primary-subtotal-pred current next)))))
+                (begin
+                  (if secondary-subtotal-pred
+
+                      (begin
+                        (secondary-subtotal-renderer
+                         table width current
+                         secondary-subtotal-collector
+                         def:secondary-subtotal-style used-columns export?)
+                        (secondary-subtotal-collector 'reset #f #f)))
+
+                  (primary-subtotal-renderer table width current
+                                             primary-subtotal-collector
+                                             def:primary-subtotal-style used-columns
+                                             export?)
+
+                  (primary-subtotal-collector 'reset #f #f)
+
+                  (if next
+                      (begin
+                        (primary-subheading-renderer
+                         next table width def:primary-subtotal-style used-columns)
+
+                        (if secondary-subtotal-pred
+                            (secondary-subheading-renderer
+                             next
+                             table
+                             width def:secondary-subtotal-style used-columns)))))
+
+                (if (and secondary-subtotal-pred
+                         (or (not next)
+                             (and next
+                                  (not (secondary-subtotal-pred
+                                        current next)))))
+                    (begin (secondary-subtotal-renderer
+                            table width current
+                            secondary-subtotal-collector
+                            def:secondary-subtotal-style used-columns export?)
+                           (secondary-subtotal-collector 'reset #f #f)
+                           (if next
+                               (secondary-subheading-renderer
+                                next table width
+                                def:secondary-subtotal-style used-columns)))))
+
+            (do-rows-with-subtotals rest
+                                    table
+                                    used-columns
+                                    width
+                                    multi-rows?
+                                    (not odd-row?)
+                                    export?
+                                    account-types-to-reverse
+                                    primary-subtotal-pred
+                                    secondary-subtotal-pred
+                                    primary-subheading-renderer
+                                    secondary-subheading-renderer
+                                    primary-subtotal-renderer
+                                    secondary-subtotal-renderer
+                                    primary-subtotal-collector
+                                    secondary-subtotal-collector
+                                    total-collector))))
+
+    (let* ((table (gnc:make-html-table))
+           (width (num-columns-required used-columns))
+           (multi-rows? (transaction-report-multi-rows-p options))
+           (export? (transaction-report-export-p options))
+           (account-types-to-reverse
+            (get-account-types-to-reverse options)))
+
+      (gnc:html-table-set-col-headers!
+       table
+       (make-heading-list used-columns options))
+      ;;     (gnc:warn "Splits:" splits)
+      (if (not (null? splits))
+          (begin
+            (if primary-subheading-renderer
+                (primary-subheading-renderer
+                 (car splits) table width def:primary-subtotal-style used-columns))
+            (if secondary-subheading-renderer
+                (secondary-subheading-renderer
+                 (car splits) table width def:secondary-subtotal-style used-columns))
+
+            (do-rows-with-subtotals splits table used-columns width
+                                    multi-rows? #t
+                                    export?
+                                    account-types-to-reverse
+                                    primary-subtotal-pred
+                                    secondary-subtotal-pred
+                                    primary-subheading-renderer
+                                    secondary-subheading-renderer
+                                    primary-subtotal-renderer
+                                    secondary-subtotal-renderer
+                                    (gnc:make-commodity-collector)
+                                    (gnc:make-commodity-collector)
+                                    (gnc:make-commodity-collector))))
+
+      table)))
 
 ;; ;;;;;;;;;;;;;;;;;;;;
 ;; Here comes the renderer function for this report.
@@ -1406,14 +1406,14 @@ Credit Card, and Income accounts."))))))
     ;; subtotal functions. Each entry: (cons
     ;; 'sorting-key-option-value (vector 'query-sorting-key
     ;; subtotal-function subtotal-renderer))
-;;  (let* ((used-columns (build-column-used options))) ;; tpo: gives unbound variable options?
+    ;;  (let* ((used-columns (build-column-used options))) ;; tpo: gives unbound variable options?
     (let* ((used-columns (build-column-used (gnc:report-options report-obj))))
-      (list (cons 'account-name  (vector 
+      (list (cons 'account-name  (vector
                                   (list SPLIT-ACCT-FULLNAME)
-                                  split-account-full-name-same-p 
+                                  split-account-full-name-same-p
                                   render-account-subheading
                                   render-account-subtotal))
-            (cons 'account-code  (vector 
+            (cons 'account-code  (vector
                                   (list SPLIT-ACCOUNT ACCOUNT-CODE-)
                                   split-account-code-same-p
                                   render-account-subheading
@@ -1422,23 +1422,23 @@ Credit Card, and Income accounts."))))))
                                   (list SPLIT-TRANS TRANS-DATE-POSTED)
                                   #f #f #f))
             (cons 'reconciled-date (vector
-                                  (list SPLIT-DATE-RECONCILED)
-                                  #f #f #f))
+                                    (list SPLIT-DATE-RECONCILED)
+                                    #f #f #f))
             (cons 'register-order (vector
-                                  (list QUERY-DEFAULT-SORT)
-                                  #f #f #f))
+                                   (list QUERY-DEFAULT-SORT)
+                                   #f #f #f))
             (cons 'corresponding-acc-name
-                                 (vector
-                                  (list SPLIT-CORR-ACCT-NAME)
-                                  split-same-corr-account-full-name-p 
-                                  render-corresponding-account-subheading
-                                  render-corresponding-account-subtotal))
+                  (vector
+                   (list SPLIT-CORR-ACCT-NAME)
+                   split-same-corr-account-full-name-p
+                   render-corresponding-account-subheading
+                   render-corresponding-account-subtotal))
             (cons 'corresponding-acc-code
-                                 (vector
-                                  (list SPLIT-CORR-ACCT-CODE)
-                                  split-same-corr-account-code-p 
-                                  render-corresponding-account-subheading
-                                  render-corresponding-account-subtotal))
+                  (vector
+                   (list SPLIT-CORR-ACCT-CODE)
+                   split-same-corr-account-code-p
+                   render-corresponding-account-subheading
+                   render-corresponding-account-subtotal))
             (cons 'amount        (vector (list SPLIT-VALUE) #f #f #f))
             (cons 'description   (vector (list SPLIT-TRANS TRANS-DESCRIPTION) #f #f #f))
             (if (qof-book-use-split-action-for-num-field (gnc-get-current-book))
@@ -1455,15 +1455,15 @@ Credit Card, and Income accounts."))))))
     (list
      (cons 'none (vector #f #f #f))
      (cons 'weekly (vector split-same-week-p render-week-subheading
-			   render-week-subtotal))
-     (cons 'monthly (vector split-same-month-p render-month-subheading 
+                           render-week-subtotal))
+     (cons 'monthly (vector split-same-month-p render-month-subheading
                             render-month-subtotal))
-     (cons 'quarterly (vector split-same-quarter-p render-quarter-subheading 
-                            render-quarter-subtotal))
+     (cons 'quarterly (vector split-same-quarter-p render-quarter-subheading
+                              render-quarter-subtotal))
      (cons 'yearly (vector split-same-year-p render-year-subheading
                            render-year-subtotal))))
 
-  (define (get-subtotalstuff-helper 
+  (define (get-subtotalstuff-helper
            name-sortkey name-subtotal name-date-subtotal
            comp-index date-index)
     ;; The value of the sorting-key multichoice option.
@@ -1474,32 +1474,32 @@ Credit Card, and Income accounts."))))))
           ;; corresponding funcs in the assoc-list.
           (vector-ref
            (cdr (assq (opt-val pagename-sorting name-date-subtotal)
-                      date-comp-funcs-assoc-list)) 
+                      date-comp-funcs-assoc-list))
            date-index)
           ;; For everything else: 1. check whether sortkey has
           ;; subtotalling enabled at all, 2. check whether the
           ;; enable-subtotal boolean option is #t, 3. look up the
           ;; appropriate funcs in the assoc-list.
-          (and (member sortkey subtotal-enabled) 
+          (and (member sortkey subtotal-enabled)
                (and (opt-val pagename-sorting name-subtotal)
-                    (vector-ref 
-                     (cdr (assq sortkey comp-funcs-assoc-list)) 
+                    (vector-ref
+                     (cdr (assq sortkey comp-funcs-assoc-list))
                      comp-index))))))
-  
+
   (define (get-query-sortkey sort-option-value)
-    (vector-ref 
-     (cdr (assq sort-option-value comp-funcs-assoc-list)) 
+    (vector-ref
+     (cdr (assq sort-option-value comp-funcs-assoc-list))
      0))
 
-  (define (get-subtotal-pred 
+  (define (get-subtotal-pred
            name-sortkey name-subtotal name-date-subtotal)
-    (get-subtotalstuff-helper 
+    (get-subtotalstuff-helper
      name-sortkey name-subtotal name-date-subtotal
      1 0))
 
   (define (get-subheading-renderer
            name-sortkey name-subtotal name-date-subtotal)
-    (get-subtotalstuff-helper 
+    (get-subtotalstuff-helper
      name-sortkey name-subtotal name-date-subtotal
      2 1))
 
@@ -1523,28 +1523,28 @@ Credit Card, and Income accounts."))))))
         ((= splitcount 2)
          (let* ((other      (xaccSplitGetOtherSplit split))
                 (other-acct (xaccSplitGetAccount other)))
-               (member other-acct account-list)))
+           (member other-acct account-list)))
 
         ;; A multi-split transaction - run over all splits
         ((> splitcount 2)
          (let ((splits (xaccTransGetSplitList txn)))
 
-                ;; Walk through the list of splits.
-                ;; if we reach the end, return #f
-                ;; if the 'this' != 'split' and the split->account is a member
-                ;; of the account-list, then return #t, else recurse
-                (define (is-member splits)
-                  (if (null? splits)
-                      #f
-                      (let* ((this (car splits))
-                             (rest (cdr splits))
-                             (acct (xaccSplitGetAccount this)))
-                        (if (and (not (eq? this split))
-                                 (member acct account-list))
-                            #t
-                            (is-member rest)))))
-
-                (is-member splits)))
+           ;; Walk through the list of splits.
+           ;; if we reach the end, return #f
+           ;; if the 'this' != 'split' and the split->account is a member
+           ;; of the account-list, then return #t, else recurse
+           (define (is-member splits)
+             (if (null? splits)
+                 #f
+                 (let* ((this (car splits))
+                        (rest (cdr splits))
+                        (acct (xaccSplitGetAccount this)))
+                   (if (and (not (eq? this split))
+                            (member acct account-list))
+                       #t
+                       (is-member rest)))))
+
+           (is-member splits)))
 
         ;; Single transaction splits
         (else #f))))
@@ -1552,39 +1552,39 @@ Credit Card, and Income accounts."))))))
 
   (gnc:report-starting reportname)
   (let* ((document (gnc:make-html-document))
-        (c_account_0 (opt-val gnc:pagename-accounts "Accounts"))
-        (account-matcher (opt-val gnc:pagename-accounts optname-account-matcher))
-        (account-matcher-regexp (if (opt-val gnc:pagename-accounts optname-account-matcher-regex)
-                                    (make-regexp account-matcher)
-                                    #f))
-        (c_account_1 (filter
-                      (lambda (acc)
-                        (if account-matcher-regexp
-                            (regexp-exec account-matcher-regexp (gnc-account-get-full-name acc))
-                            (string-contains (gnc-account-get-full-name acc) account-matcher)))
-                     c_account_0))
-        (c_account_2 (opt-val gnc:pagename-accounts "Filter By..."))
-	(filter-mode (opt-val gnc:pagename-accounts "Filter Type"))
-        (begindate (gnc:timepair-start-day-time
-                    (gnc:date-option-absolute-time
-                     (opt-val gnc:pagename-general "Start Date"))))
-        (enddate (gnc:timepair-end-day-time
-                  (gnc:date-option-absolute-time
-                   (opt-val gnc:pagename-general "End Date"))))
-        (transaction-matcher (opt-val pagename-filter optname-transaction-matcher))
-        (transaction-matcher-regexp (if (opt-val pagename-filter optname-transaction-matcher-regex)
-                                        (make-regexp transaction-matcher)
-                                        #f))
-        (report-title (opt-val 
-                       gnc:pagename-general
-                       gnc:optname-reportname))
-        (primary-key (opt-val pagename-sorting optname-prime-sortkey))
-        (primary-order (opt-val pagename-sorting "Primary Sort Order"))
-        (secondary-key (opt-val pagename-sorting optname-sec-sortkey))
-        (secondary-order (opt-val pagename-sorting "Secondary Sort Order"))
-        (void-status (opt-val gnc:pagename-accounts optname-void-transactions))
-        (splits '())
-        (query (qof-query-create-for-splits)))
+         (c_account_0 (opt-val gnc:pagename-accounts "Accounts"))
+         (account-matcher (opt-val gnc:pagename-accounts optname-account-matcher))
+         (account-matcher-regexp (if (opt-val gnc:pagename-accounts optname-account-matcher-regex)
+                                     (make-regexp account-matcher)
+                                     #f))
+         (c_account_1 (filter
+                       (lambda (acc)
+                         (if account-matcher-regexp
+                             (regexp-exec account-matcher-regexp (gnc-account-get-full-name acc))
+                             (string-contains (gnc-account-get-full-name acc) account-matcher)))
+                       c_account_0))
+         (c_account_2 (opt-val gnc:pagename-accounts "Filter By..."))
+         (filter-mode (opt-val gnc:pagename-accounts "Filter Type"))
+         (begindate (gnc:timepair-start-day-time
+                     (gnc:date-option-absolute-time
+                      (opt-val gnc:pagename-general "Start Date"))))
+         (enddate (gnc:timepair-end-day-time
+                   (gnc:date-option-absolute-time
+                    (opt-val gnc:pagename-general "End Date"))))
+         (transaction-matcher (opt-val pagename-filter optname-transaction-matcher))
+         (transaction-matcher-regexp (if (opt-val pagename-filter optname-transaction-matcher-regex)
+                                         (make-regexp transaction-matcher)
+                                         #f))
+         (report-title (opt-val
+                        gnc:pagename-general
+                        gnc:optname-reportname))
+         (primary-key (opt-val pagename-sorting optname-prime-sortkey))
+         (primary-order (opt-val pagename-sorting "Primary Sort Order"))
+         (secondary-key (opt-val pagename-sorting optname-sec-sortkey))
+         (secondary-order (opt-val pagename-sorting "Secondary Sort Order"))
+         (void-status (opt-val gnc:pagename-accounts optname-void-transactions))
+         (splits '())
+         (query (qof-query-create-for-splits)))
 
     ;;(gnc:warn "accts in trep-renderer:" c_account_1)
     ;;(gnc:warn "Report Account names:" (get-other-account-names c_account_1))
@@ -1592,28 +1592,28 @@ Credit Card, and Income accounts."))))))
     (if (not (or (null? c_account_1) (and-map not c_account_1)))
         (begin
           (qof-query-set-book query (gnc-get-current-book))
-	      ;;(gnc:warn "query is:" query)
+          ;;(gnc:warn "query is:" query)
           (xaccQueryAddAccountMatch query
-                                       c_account_1
-                                       QOF-GUID-MATCH-ANY QOF-QUERY-AND)
+                                    c_account_1
+                                    QOF-GUID-MATCH-ANY QOF-QUERY-AND)
           (xaccQueryAddDateMatchTS
            query #t begindate #t enddate QOF-QUERY-AND)
           (qof-query-set-sort-order query
-				    (get-query-sortkey primary-key)
-				    (get-query-sortkey secondary-key)
-				    '())
+                                    (get-query-sortkey primary-key)
+                                    (get-query-sortkey secondary-key)
+                                    '())
 
           (qof-query-set-sort-increasing query
                                          (eq? primary-order 'ascend)
                                          (eq? secondary-order 'ascend)
                                          #t)
 
-	  (case void-status
-	   ((non-void-only) 
-	    (gnc:query-set-match-non-voids-only! query (gnc-get-current-book)))
-	   ((void-only)
-	    (gnc:query-set-match-voids-only! query (gnc-get-current-book)))
-	   (else #f))
+          (case void-status
+            ((non-void-only)
+             (gnc:query-set-match-non-voids-only! query (gnc-get-current-book)))
+            ((void-only)
+             (gnc:query-set-match-voids-only! query (gnc-get-current-book)))
+            (else #f))
 
           (set! splits (qof-query-run query))
 
@@ -1637,20 +1637,20 @@ Credit Card, and Income accounts."))))))
                         splits))
 
           (if (not (null? splits))
-              (let ((table 
-                     (make-split-table 
-                      splits 
+              (let ((table
+                     (make-split-table
+                      splits
                       options
-                      (get-subtotal-pred optname-prime-sortkey 
+                      (get-subtotal-pred optname-prime-sortkey
                                          optname-prime-subtotal
                                          optname-prime-date-subtotal)
-                      (get-subtotal-pred optname-sec-sortkey 
+                      (get-subtotal-pred optname-sec-sortkey
                                          optname-sec-subtotal
                                          optname-sec-date-subtotal)
-                      (get-subheading-renderer optname-prime-sortkey 
+                      (get-subheading-renderer optname-prime-sortkey
                                                optname-prime-subtotal
                                                optname-prime-date-subtotal)
-                      (get-subheading-renderer optname-sec-sortkey 
+                      (get-subheading-renderer optname-sec-sortkey
                                                optname-sec-subtotal
                                                optname-sec-date-subtotal)
                       (get-subtotal-renderer   optname-prime-sortkey
@@ -1662,20 +1662,20 @@ Credit Card, and Income accounts."))))))
 
                 (gnc:html-document-set-title! document
                                               report-title)
-                (gnc:html-document-add-object! 
+                (gnc:html-document-add-object!
                  document
                  (gnc:make-html-text
-                  (gnc:html-markup-h3 
+                  (gnc:html-markup-h3
                    (display-date-interval begindate enddate))))
                 (gnc:html-document-add-object!
-                 document 
+                 document
                  table)
                 (qof-query-destroy query))
               ;; error condition: no splits found
               (let ((p (gnc:make-html-text)))
-                (gnc:html-text-append! 
-                 p 
-                 (gnc:html-markup-h2 
+                (gnc:html-text-append!
+                 p
+                 (gnc:html-markup-h2
                   (_ "No matching transactions found"))
                  (gnc:html-markup-p
                   (_ "No transactions were found that \
@@ -1684,11 +1684,11 @@ in the Options panel.")))
                 (gnc:html-document-add-object! document p))))
 
         (if (null? c_account_0)
-            
+
             ;; error condition: no accounts specified
             (gnc:html-document-add-object!
-             document 
-             (gnc:html-make-no-account-warning 
+             document
+             (gnc:html-make-no-account-warning
               report-title (gnc:report-id report-obj)))
 
             ;; error condition: accounts were specified but none matcher string/regex
@@ -1705,12 +1705,12 @@ in the Options panel.")))
 
 ;; Define the report.
 (gnc:define-report
- 
+
  'version 1
- 
+
  'name reportname
  'report-guid "2fe3b9833af044abb929a88d5a59620f"
- 
+
  'options-generator trep-options-generator
- 
+
  'renderer trep-renderer)

commit 809d27709785be60691624b4b901146e94ab9869
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Dec 9 22:12:30 2017 +0800

    ENH: Move Transaction Matcher to new Filter tab

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 231c2ae..4957d2f 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -66,8 +66,11 @@
 (define optname-currency (N_ "Report's currency"))
 (define optname-account-matcher (N_ "Account Matcher"))
 (define optname-account-matcher-regex (N_ "Account Matcher uses regular expressions for extended matching"))
+
+(define pagename-filter (N_ "Filter"))
 (define optname-transaction-matcher (N_ "Transaction Matcher"))
 (define optname-transaction-matcher-regex (N_ "Transaction Matcher uses regular expressions for extended matching"))
+
 (define def:grand-total-style "grand-total")
 (define def:normal-row-style "normal-row")
 (define def:alternate-row-style "alternate-row")
@@ -646,7 +649,7 @@
 
   (gnc:register-trep-option
    (gnc:make-string-option
-    gnc:pagename-general optname-transaction-matcher
+    pagename-filter optname-transaction-matcher
     "i1" (N_ "Match only transactions whose substring is matched e.g. '#gift' \
 will find all transactions with #gift in description, notes or memo. It can be left \
 blank, which will disable the matcher.")
@@ -654,7 +657,7 @@ blank, which will disable the matcher.")
 
   (gnc:register-trep-option
    (gnc:make-simple-boolean-option
-    gnc:pagename-general optname-transaction-matcher-regex
+    pagename-filter optname-transaction-matcher-regex
     "i2"
     (N_ "By default the transaction matcher will search substring only. Set this to true to \
 enable full POSIX regular expressions capabilities. '#work|#family' will match both \
@@ -1568,8 +1571,8 @@ Credit Card, and Income accounts."))))))
         (enddate (gnc:timepair-end-day-time
                   (gnc:date-option-absolute-time
                    (opt-val gnc:pagename-general "End Date"))))
-        (transaction-matcher (opt-val gnc:pagename-general optname-transaction-matcher))
-        (transaction-matcher-regexp (if (opt-val gnc:pagename-general optname-transaction-matcher-regex)
+        (transaction-matcher (opt-val pagename-filter optname-transaction-matcher))
+        (transaction-matcher-regexp (if (opt-val pagename-filter optname-transaction-matcher-regex)
                                         (make-regexp transaction-matcher)
                                         #f))
         (report-title (opt-val 

commit 7e8ac532bf7009e6f31ddab31d9c1a985b4ce244
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Dec 12 00:13:44 2017 +0800

    BUGFIX: change date-sorting-types
    
    This commit changes date sorting types.
    
    'date is posted-date and belongs to this list.
    'reconciled-date is also date and may benefit from periodic subtotals.
    'register-order is register default and may not be date.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index 21340a3..231c2ae 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -610,7 +610,7 @@
     split-value))
 
 
-(define date-sorting-types (list 'date 'register-order))
+(define date-sorting-types (list 'date 'reconciled-date))
 
 (define (trep-options-generator)
   (define gnc:*transaction-report-options* (gnc:new-options))

commit ba2e0c5ff668c5de4bcbb20f2a6c7ce65fcc0f75
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Dec 12 00:15:31 2017 +0800

    OBSOLETE: 'exact-time removed
    
    This sortkey is handled identically to 'date and must be removed.

diff --git a/gnucash/report/standard-reports/transaction.scm b/gnucash/report/standard-reports/transaction.scm
index bd3445c..21340a3 100644
--- a/gnucash/report/standard-reports/transaction.scm
+++ b/gnucash/report/standard-reports/transaction.scm
@@ -610,7 +610,7 @@
     split-value))
 
 
-(define date-sorting-types (list 'date 'exact-time 'register-order))
+(define date-sorting-types (list 'date 'register-order))
 
 (define (trep-options-generator)
   (define gnc:*transaction-report-options* (gnc:new-options))
@@ -768,10 +768,6 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                            (N_ "Date")
                            (N_ "Sort by date."))
 
-                   (vector 'exact-time
-                           (N_ "Exact Time")
-                           (N_ "Sort by exact time."))
-
                    (vector 'reconciled-date
                            (N_ "Reconciled Date")
                            (N_ "Sort by the Reconciled Date."))
@@ -823,10 +819,6 @@ Use a period (.) to match a single character e.g. '20../.' will match 'Travel 20
                            (N_ "Date")
                            (N_ "Sort by date."))
 
-                   (vector 'exact-time
-                           (N_ "Exact Time")
-                           (N_ "Sort by exact time."))
-
                    (vector 'reconciled-date
                            (N_ "Reconciled Date")
                            (N_ "Sort by the Reconciled Date."))
@@ -1423,9 +1415,6 @@ Credit Card, and Income accounts."))))))
                                   split-account-code-same-p
                                   render-account-subheading
                                   render-account-subtotal))
-            (cons 'exact-time    (vector
-                                  (list SPLIT-TRANS TRANS-DATE-POSTED)
-                                  #f #f #f))
             (cons 'date          (vector
                                   (list SPLIT-TRANS TRANS-DATE-POSTED)
                                   #f #f #f))

commit d93d4f68b0df61700a1d6bfe52896d8079fe180b
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Thu Dec 21 20:19:15 2017 +0800

    options.scm: upgrade lookup-value to learn section changes

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index b0e7b0e..2a8adbf 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -1681,32 +1681,48 @@
           (let ((option-hash (hash-ref section-hash name)))
             (if option-hash
                 option-hash
-                ; Option name was not found. Perhaps it was renamed ?
-                ; Let's try to map it to a known new name
+                ;; Option name was not found. Perhaps it was renamed ?
+                ;; Let's try to map it to a known new name.
+                ;; This list will try match names - if one is found
+                ;; the next item will describe a pair.
+                ;; (cons newsection newname)
+                ;; If newsection is #f then reuse previous section name.
+                ;;
+                ;; Please note the rename list currently supports renaming
+                ;; individual option names, or individual option names moved
+                ;; to another section. It does not currently support renaming
+                ;; whole sections.
                 (let* ((new-names-list (list
-                        "Accounts to include" "Accounts"
-                        "Exclude transactions between selected accounts?" "Exclude transactions between selected accounts"
-                        "Filter Accounts" "Filter By..."
-                        "Flatten list to depth limit?" "Flatten list to depth limit"
-                        "From" "Start Date"
-                        "Report Accounts" "Accounts"
-                        "Report Currency" "Report's currency"
-                        "Show Account Code?" "Show Account Code"
-                        "Show Full Account Name?" "Show Full Account Name"
-                        "Show Multi-currency Totals?" "Show Multi-currency Totals"
-                        "Show zero balance items?" "Show zero balance items"
-                        "Sign Reverses?" "Sign Reverses"
-                        "To" "End Date"
-                        "Use Full Account Name?" "Use Full Account Name"
-                        "Use Full Other Account Name?" "Use Full Other Account Name"
-                        "Void Transactions?" "Void Transactions"
-                        ))
+                                        "Accounts to include" (cons #f "Accounts")
+                                        "Exclude transactions between selected accounts?" (cons #f "Exclude transactions between selected accounts")
+                                        "Filter Accounts" (cons #f "Filter By...")
+                                        "Flatten list to depth limit?" (cons #f "Flatten list to depth limit")
+                                        "From" (cons #f "Start Date")
+                                        "Report Accounts" (cons #f "Accounts")
+                                        "Report Currency" (cons #f "Report's currency")
+                                        "Show Account Code?" (cons #f "Show Account Code")
+                                        "Show Full Account Name?" (cons #f "Show Full Account Name")
+                                        "Show Multi-currency Totals?" (cons #f "Show Multi-currency Totals")
+                                        "Show zero balance items?" (cons #f "Show zero balance items")
+                                        "Sign Reverses?" (cons #f "Sign Reverses")
+                                        "To" (cons #f "End Date")
+                                        "Use Full Account Name?" (cons #f "Use Full Account Name")
+                                        "Use Full Other Account Name?" (cons #f "Use Full Other Account Name")
+                                        "Void Transactions?" (cons #f "Void Transactions")
+                                        ))
                        (name-match (member name new-names-list)))
 
-                      (if name-match 
-                        (let ((new-name (cadr name-match)))
-                             (lookup-option section new-name))
-                        #f))))
+                  (and name-match
+                       (let ((new-section (car (cadr name-match)))
+                             (new-name (cdr (cadr name-match))))
+                         ;; compare if new-section name exists.
+                         (if new-section
+                             ;; if so, if it's different to current section name
+                             ;; then try new section name
+                             (and (not (string=? new-section section))
+                                  (lookup-option new-section new-name))
+                             ;; else reuse section-name with new-name
+                             (lookup-option section new-name)))))))
           #f)))
 
   (define (option-changed section name)

commit 2f96b19c77675a883efce2eed6ecfd4d53551fad
Merge: ddfd38d a80318e
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sat Dec 23 15:10:48 2017 +0100

    Merge branch 'fix_bayes' of https://github.com/limitedAtonement/gnucash into unstable


commit ddfd38d8c3bb0fa04bd7d88fce49b09dd30b8767
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Fri Dec 22 20:31:35 2017 +0100

    Remove cmake hoop to change file permissions
    
    As we are using cmake's own configure_file command, it will just copy
    the permissions of the input file, which is what we want here.

diff --git a/libgnucash/quotes/CMakeLists.txt b/libgnucash/quotes/CMakeLists.txt
index e42ec9c..91bd518 100644
--- a/libgnucash/quotes/CMakeLists.txt
+++ b/libgnucash/quotes/CMakeLists.txt
@@ -1,27 +1,10 @@
 
-
-
-SET(PERL ${PERL_EXECUTABLE})
-
-SET(_TMPDIR ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
-FILE(WRITE ${_TMPDIR}/copy_with_perms.cmake
-  "FILE(COPY \${SRC} DESTINATION \${DST}
-   FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)"
-)
-
 SET(_BIN_FILES "")
 FOREACH(file gnc-fq-check.in gnc-fq-helper.in gnc-fq-update.in gnc-fq-dump)
   STRING(REPLACE ".in" "" _OUTPUT_FILE_NAME ${file})
   SET(_ABS_OUTPUT_FILE ${BINDIR_BUILD}/${_OUTPUT_FILE_NAME})
-  configure_file( ${file} ${_OUTPUT_FILE_NAME} @ONLY)
+  configure_file( ${file} ${_ABS_OUTPUT_FILE} @ONLY)
   LIST(APPEND _BIN_FILES ${_ABS_OUTPUT_FILE})
-  ADD_CUSTOM_COMMAND(
-    OUTPUT ${_ABS_OUTPUT_FILE}
-    COMMAND ${CMAKE_COMMAND} -D SRC=${CMAKE_CURRENT_BINARY_DIR}/${_OUTPUT_FILE_NAME}
-                             -D DST=${BINDIR_BUILD}
-                             -P ${_TMPDIR}/copy_with_perms.cmake
-    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_OUTPUT_FILE_NAME}
-  )
 ENDFOREACH(file)
 
 SET(CMAKE_COMMAND_TMP "")
@@ -32,7 +15,7 @@ ENDIF()
 
 SET(_MAN_FILES "")
 FOREACH(file gnc-fq-dump gnc-fq-helper)
-  SET(_POD_INPUT ${CMAKE_CURRENT_BINARY_DIR}/${file})
+  SET(_POD_INPUT ${BINDIR_BUILD}/${file})
   SET(_MAN_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${file}.1)
   LIST(APPEND _MAN_FILES ${_MAN_OUTPUT})
   ADD_CUSTOM_COMMAND(

commit f11eab36d9a2e9d844a549e8a64cf356c8b4858f
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Thu Dec 21 17:54:49 2017 +0100

    Replace GNC_CONFIGURE(2) with configure_file

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0a021a7..5378340 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,7 +42,6 @@ INCLUDE (MacroAddSourceFileCompileFlags)
 INCLUDE (GncAddSwigCommand)
 INCLUDE (CheckIncludeFiles)
 INCLUDE (GncAddSchemeTargets)
-INCLUDE (GncConfigure)
 INCLUDE (GncAddGSchemaTargets)
 INCLUDE (GncAddTest)
 INCLUDE (MakeDistFiles)
diff --git a/Makefile.am b/Makefile.am
index 2823a0a..ce7ffae 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -110,15 +110,11 @@ EXTRA_DIST = \
   util/gnc-vcs-info \
   util/CMakeLists.txt
 
-## We borrow guile's convention and use @-...-@ as the substitution
-## brackets here, instead of the usual @... at .  This prevents autoconf
-## from substituting the values directly into the left-hand sides of
-## the sed substitutions.
 make-gnucash-potfiles: make-gnucash-potfiles.in Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-            -e 's:@-SRCDIR-@:${srcdir}:g' \
-            -e 's:@-PERL-@:${PERL}:g'
+            -e 's:[@]SRCDIR[@]:${srcdir}:g' \
+            -e 's:[@]PERL[@]:${PERL}:g'
 	chmod +x $@.tmp
 	mv $@.tmp $@
 
diff --git a/bindings/python/tests/Makefile.am b/bindings/python/tests/Makefile.am
index 62c50cb..f965265 100644
--- a/bindings/python/tests/Makefile.am
+++ b/bindings/python/tests/Makefile.am
@@ -23,16 +23,11 @@ TESTS_ENVIRONMENT = \
   PYTHONPATH=$$PYTHONPATH:$(top_srcdir)/common/test-core/ \
   PYTHONPATH=$$PYTHONPATH:$(top_builddir)/common/test-core/.libs \
   $(shell ${abs_top_srcdir}/common/gnc-test-env.pl --noexports ${GNC_TEST_DEPS})
-  
-## We borrow guile's convention and use @-...-@ as the substitution
-## brackets below, instead of the usual @... at .  This prevents autoconf
-## from substituting the values directly into the left-hand sides of
-## the sed substitutions.  *sigh*
 
 runTests.py: runTests.py.in ${top_builddir}/config.status Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-	    -e 's#@-PYTHON-@#${PYTHON}#'
+	    -e 's#[@]PYTHON[@]#${PYTHON}#'
 	mv $@.tmp $@
 	chmod u+x $@
 
diff --git a/bindings/python/tests/runTests.py.in b/bindings/python/tests/runTests.py.in
index 60d040f..6028c81 100755
--- a/bindings/python/tests/runTests.py.in
+++ b/bindings/python/tests/runTests.py.in
@@ -1,4 +1,4 @@
-#!@-PYTHON-@
+#!@PYTHON@
 
 import unittest
 import os
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
index 211daff..f7dbab7 100644
--- a/cmake/CMakeLists.txt
+++ b/cmake/CMakeLists.txt
@@ -8,31 +8,4 @@ IF (APPLE)
 ENDIF(APPLE)
 
 
-SET(CMAKE_COMMAND_TMP "")
-IF (${CMAKE_VERSION} VERSION_GREATER 3.1)
-  SET(CMAKE_COMMAND_TMP ${CMAKE_COMMAND} -E env)
-ENDIF()
-
-SET(schema-targets csv-exp-gschema csv-imp-gschema
-    generic-import-gschema gnome-gschema gnome-utils-gschema qif-imp-gschema)
-
-IF (WITH_AQBANKING)
-  LIST(APPEND schema-targets aqb-gschema)
-ENDIF (WITH_AQBANKING)
-
-IF (WITH_OFX)
-  LIST(APPEND schema-targets ofx-gschema)
-ENDIF (WITH_OFX)
-
-SET(SCHEMA_BUILD_DIR ${DATADIR_BUILD}/glib-2.0/schemas)
-ADD_CUSTOM_COMMAND(
-  OUTPUT ${SCHEMA_BUILD_DIR}/gschemas.compiled
-  COMMAND ${CMAKE_COMMAND_TMP} ${GLIB_COMPILE_SCHEMAS} ${SCHEMA_BUILD_DIR}
-  DEPENDS ${schema-targets}
-)
-
-ADD_CUSTOM_TARGET(compiled-schemas ALL DEPENDS ${SCHEMA_BUILD_DIR}/gschemas.compiled)
-
-INSTALL(FILES ${SCHEMA_BUILD_DIR}/gschemas.compiled DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
-
 SET_DIST_LIST(cmake_DIST CMakeLists.txt README_CMAKE.txt cmake_uninstall.cmake.in)
diff --git a/common/Makefile.am b/common/Makefile.am
index 29c5b8e..f5a7625 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -30,7 +30,6 @@ EXTRA_DIST = \
   cmake_modules/MakeDistFiles.cmake \
   cmake_modules/MacroAddSourceFileCompileFlags.cmake \
   cmake_modules/MakeDistCheck.cmake \
-  cmake_modules/GncConfigure.cmake \
   cmake_modules/GncAddSchemeTargets.cmake \
   cmake_modules/GncAddGSchemaTargets.cmake \
   cmake_modules/GncFindPkgConfig.cmake \
diff --git a/common/cmake_modules/CMakeLists.txt b/common/cmake_modules/CMakeLists.txt
index 2a014b3..19922a1 100644
--- a/common/cmake_modules/CMakeLists.txt
+++ b/common/cmake_modules/CMakeLists.txt
@@ -1,6 +1,6 @@
 
 SET(cmake_FILES GncAddGSchemaTargets.cmake GncAddSchemeTargets.cmake GncAddSwigCommand.cmake GncAddTest.cmake
-        GncConfigure.cmake GncFindPkgConfig.cmake MacroAddSourceFileCompileFlags.cmake MacroAppendForeach.cmake
+        GncFindPkgConfig.cmake MacroAddSourceFileCompileFlags.cmake MacroAppendForeach.cmake
         MakeDist.cmake MakeDistFiles.cmake MakeDistCheck.cmake)
 
-SET_DIST_LIST(cmake_modules_DIST CMakeLists.txt COPYING-CMAKE-SCRIPTS.txt ${cmake_FILES})
\ No newline at end of file
+SET_DIST_LIST(cmake_modules_DIST CMakeLists.txt COPYING-CMAKE-SCRIPTS.txt ${cmake_FILES})
diff --git a/common/cmake_modules/GncAddGSchemaTargets.cmake b/common/cmake_modules/GncAddGSchemaTargets.cmake
index dbe2c9d..8c519f9 100644
--- a/common/cmake_modules/GncAddGSchemaTargets.cmake
+++ b/common/cmake_modules/GncAddGSchemaTargets.cmake
@@ -1,7 +1,6 @@
-MACRO(ADD_GSCHEMA_TARGETS _TARGET _gschema_INPUTS)
+macro(add_gschema_targets _gschema_INPUTS)
   SET(_gschema_OUTPUTS "")
-  SET(_gschema_VALIDS "")
-  SET(_gschema_BUILDS "")
+  set(local_depends ${gschema_depends})
   # FIXME: I have no idea of I'm using the right options here for intltool-merge for Windows.
   SET(INITTOOL_OPTIONS "--no-translations")
   IF(WIN32)
@@ -12,27 +11,22 @@ MACRO(ADD_GSCHEMA_TARGETS _TARGET _gschema_INPUTS)
     SET(CMAKE_COMMAND_TMP ${CMAKE_COMMAND} -E env)
   ENDIF()
   FOREACH(file ${_gschema_INPUTS})
-    GNC_CONFIGURE2(${file}-target ${file}.in.in ${file}.in)
-    STRING(REPLACE ".xml" ".valid" file_no_xml ${file})
-    SET(_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${file})
-    SET(_BUILD_FILE ${DATADIR_BUILD}/glib-2.0/schemas/${file})
-    SET(_VALID_FILE ${CMAKE_CURRENT_BINARY_DIR}/${file_no_xml})
-    LIST(APPEND _gschema_OUTPUTS ${_OUTPUT_FILE})
-    LIST(APPEND _gschema_VALIDS ${_VALID_FILE})
-    LIST(APPEND _gschema_BUILDS ${_BUILD_FILE})
+    configure_file(${file}.in.in ${file}.in @ONLY)
+
+    set(_OUTPUT_FILE ${DATADIR_BUILD}/glib-2.0/schemas/${file})
     ADD_CUSTOM_COMMAND(
         OUTPUT ${_OUTPUT_FILE}
         COMMAND ${CMAKE_COMMAND_TMP}
           LC_ALL=C
-          ${PERL_EXECUTABLE} ${INTLTOOL_MERGE} -x -u ${INITTOOL_OPTIONS} ${CMAKE_CURRENT_BINARY_DIR}/${file}.in ${CMAKE_CURRENT_BINARY_DIR}/${file}
+          ${PERL_EXECUTABLE} ${INTLTOOL_MERGE} -x -u ${INITTOOL_OPTIONS} ${CMAKE_CURRENT_BINARY_DIR}/${file}.in ${_OUTPUT_FILE}
         DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${file}.in
         MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${file}.in.in
     )
-    ADD_CUSTOM_COMMAND(
-      OUTPUT ${_BUILD_FILE}
-      COMMAND ${CMAKE_COMMAND} -E copy ${_OUTPUT_FILE} ${_BUILD_FILE}
-      DEPENDS ${_OUTPUT_FILE}
-    )
+    list(APPEND _gschema_OUTPUTS ${_OUTPUT_FILE})
+
+    string(REPLACE ".xml" ".valid" file_no_xml ${file})
+    set(_VALID_FILE ${CMAKE_CURRENT_BINARY_DIR}/${file_no_xml})
+    list(APPEND _gschema_VALIDS ${_VALID_FILE})
     ADD_CUSTOM_COMMAND(
         OUTPUT ${_VALID_FILE}
         COMMAND ${CMAKE_COMMAND_TMP}
@@ -40,10 +34,18 @@ MACRO(ADD_GSCHEMA_TARGETS _TARGET _gschema_INPUTS)
         COMMAND ${CMAKE_COMMAND} -E touch ${_VALID_FILE}
         DEPENDS ${_OUTPUT_FILE}
     )
+    add_custom_target(${file_no_xml}-target DEPENDS ${_VALID_FILE})
+
+    # Add both a target and a file level dependency for the valid target/file
+    # to the gschemas.compiled target (local_depends will be propagated to that target)
+    # The target level one is to ensure a link between two targets in different directories (required for the make build chain)
+    # The file level one is to ensure gschemas.compiled will be rebuilt if any of the
+    # dependencies changes.
+    list(APPEND local_depends ${file_no_xml}-target ${_VALID_FILE})
   ENDFOREACH(file)
 
-  ADD_CUSTOM_TARGET(${_TARGET} DEPENDS ${_gschema_OUTPUTS} ${_gschema_VALIDS} ${_gschema_BUILDS})
+  set(gschema_depends ${local_depends} CACHE INTERNAL "gschemas.compiled dependencies")
 
   INSTALL(FILES ${_gschema_OUTPUTS} DESTINATION share/glib-2.0/schemas)
 
-ENDMACRO()
+endmacro()
diff --git a/common/cmake_modules/GncConfigure.cmake b/common/cmake_modules/GncConfigure.cmake
deleted file mode 100644
index 5c566b2..0000000
--- a/common/cmake_modules/GncConfigure.cmake
+++ /dev/null
@@ -1,44 +0,0 @@
-
-MACRO (GNC_CONFIGURE _INPUT _OUTPUT)
-  FILE(READ ${_INPUT} FILE_CONTENTS_IN_IN)
-  SET(FILE_CONTENTS_IN "${FILE_CONTENTS_IN_IN}")
-  STRING(REGEX REPLACE "@-|-@" "@" _TMP2 "${FILE_CONTENTS_IN}")
-  STRING(CONFIGURE "${_TMP2}" FILE_CONTENTS @ONLY)
-  SET(_OUTPUT_FILE ${_OUTPUT})
-  IF (NOT IS_ABSOLUTE ${_OUTPUT})
-    SET(_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${_OUTPUT})
-  ENDIF()
-  FILE(WRITE ${_OUTPUT_FILE} "${FILE_CONTENTS}")
-ENDMACRO()
-
-
-MACRO (GNC_CONFIGURE2 _TARGET _INPUT _OUTPUT)
-
-  SET(_TMPDIR ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
-  FILE(READ ${_INPUT} FILE_CONTENTS_IN_IN)
-  SET(FILE_CONTENTS_IN "${FILE_CONTENTS_IN_IN}")
-  STRING(REGEX REPLACE "@-|-@" "@" _TMP2 "${FILE_CONTENTS_IN}")
-  FILE(WRITE ${_TMPDIR}/${_INPUT}.tmp "${_TMP2}")
-
-  FILE(WRITE ${_TMPDIR}/${_INPUT}.cmake
-    "SET(PERL ${PERL_EXECUTABLE})
-     SET(VERSION ${VERSION})
-     SET(GNC_HELPDIR \"${GNC_HELPDIR}\")
-     SET(GETTEXT_PACKAGE ${GETTEXT_PACKAGE})
-     CONFIGURE_FILE(\${SRC} \${DST} @ONLY)")
-
-  SET(_OUTPUT_FILE ${_OUTPUT})
-  IF (NOT IS_ABSOLUTE ${_OUTPUT})
-    SET(_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${_OUTPUT})
-  ENDIF()
-
-  ADD_CUSTOM_COMMAND(
-    OUTPUT ${_OUTPUT_FILE}
-    COMMAND ${CMAKE_COMMAND} -D SRC=${_TMPDIR}/${_INPUT}.tmp
-                             -D DST=${_OUTPUT_FILE}
-                             -P ${_TMPDIR}/${_INPUT}.cmake
-    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_INPUT}
-    )
-
-  ADD_CUSTOM_TARGET(${_TARGET} DEPENDS ${_OUTPUT_FILE})
-ENDMACRO()
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 2959220..5d06e8a 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -38,33 +38,25 @@ EXTRA_DIST = \
   tip_of_the_day.list.in \
   CMakeLists.txt
 
-gnucash.1: gnucash.1.in Makefile
-	rm -f $@.tmp
 if BUILDING_FROM_VCS
-	GNC_VCS_REV_Y_M=`perl -ne 'print if $$_=~s/.*GNC_VCS_REV_Y_M "(.*)"/$$1/' ${top_builddir}/libgnucash/core-utils/gnc-vcs-info.h` ; \
-	${SED} < $< > $@.tmp \
-            -e 's:$${VERSION}:${VERSION}:g' \
-            -e 's:$${DATE}:'$${GNC_VCS_REV_Y_M}':g'
-	chmod +x $@.tmp
-	mv $@.tmp $@
+top_basedir = $(top_builddir)
 else
-	GNC_VCS_REV_Y_M=`perl -ne 'print if $$_=~s/.*GNC_VCS_REV_Y_M "(.*)"/$$1/' ${top_srcdir}/libgnucash/core-utils/gnc-vcs-info.h` ; \
+top_basedir = $(top_srcdir)
+endif
+
+gnucash.1: gnucash.1.in Makefile
+	rm -f $@.tmp
+	GNC_VCS_REV_Y_M=`perl -ne 'print if $$_=~s/.*GNC_VCS_REV_Y_M "(.*)"/$$1/' ${top_basedir}/libgnucash/core-utils/gnc-vcs-info.h` ; \
 	${SED} < $< > $@.tmp \
-	-e 's:$${VERSION}:${VERSION}:g' \
-	-e 's:$${DATE}:'$${GNC_VCS_REV_Y_M}':g'
+	-e 's:[@]VERSION[@]:${VERSION}:g' \
+	-e 's:[@]DATE[@]:'$${GNC_VCS_REV_Y_M}':g'
 	chmod +x $@.tmp
 	mv $@.tmp $@
-endif
 
-## We borrow guile's convention and use @-...-@ as the substitution
-## brackets here, instead of the usual @... at .  This prevents autoconf
-## from substituting the values directly into the left-hand sides of
-## the sed substitutions.
 tip_of_the_day.list: tip_of_the_day.list.in Makefile
 	${CC} -E -P -x c -D'N_(x)=x' -o $@.tmp $<
 	cat -s $@.tmp | ${SED} -e 's/^ *"//' \
-	                       -e 's/"* *[|] */|/' \
-                           -e 's:@-GNUCASH_LATEST_STABLE_SERIES-@:${GNUCASH_LATEST_STABLE_SERIES}:g' > $@
+	                       -e 's/"* *[|] */|/' > $@
 	rm -f $@.tmp
 if PLATFORM_WIN32
 	perl -pi.bak -e 's/" *$$//' $@
diff --git a/doc/README.build-system b/doc/README.build-system
index 6321e05..dc3dfb3 100644
--- a/doc/README.build-system
+++ b/doc/README.build-system
@@ -42,16 +42,12 @@ exceptions) manually with sed.  For example, we have
 src/scm/bootstrap.scm.in which is processed in src/scm/Makefile.in
 according to the following rule:
 
-  ## We borrow guile's convention and use @-...-@ as the substitution
-  ## brackets here, instead of the usual @... at .  This prevents autoconf
-  ## from substituting the values directly into the left-hand sides of
-  ## the sed substitutions.  *sigh*
   bootstrap.scm: bootstrap.scm.in
         rm -f $@.tmp
         sed < $@.in > $@.tmp \
-            -e 's:@-VERSION-@:${VERSION}:g' \
-            -e 's:@-GNC_CONFIGDIR-@:${GNC_SHAREDIR}:g' \
-            -e 's:@-GNC_SHAREDIR-@:${GNC_CONFIGDIR}:g'
+            -e 's:[@]VERSION[@]:${VERSION}:g' \
+            -e 's:[@]GNC_CONFIGDIR[@]:${GNC_SHAREDIR}:g' \
+            -e 's:[@]GNC_SHAREDIR[@]:${GNC_CONFIGDIR}:g'
         chmod +x $@.tmp
         mv $@.tmp $@
 
diff --git a/doc/gnucash.1.in b/doc/gnucash.1.in
index 9c4f68b..a1cc1e9 100644
--- a/doc/gnucash.1.in
+++ b/doc/gnucash.1.in
@@ -2,7 +2,7 @@
 .\" Process this file with
 .\" groff -man -Tascii foo.1
 .\"
-.TH GNUCASH 1 "${DATE}" Version "${VERSION}"
+.TH GNUCASH 1 "@DATE@" Version "@VERSION@"
 .SH NAME
 gnucash \- personal finance manager
 .SH SYNOPSIS
diff --git a/gnucash/CMakeLists.txt b/gnucash/CMakeLists.txt
index b4c33f9..29784aa 100644
--- a/gnucash/CMakeLists.txt
+++ b/gnucash/CMakeLists.txt
@@ -1,5 +1,10 @@
 # CMakeLists.txt for gnucash/
 
+# Preparations for macro add_gschema_targets and gschemas.compiled target
+set(SCHEMADIR_BUILD ${DATADIR_BUILD}/glib-2.0/schemas)
+file(MAKE_DIRECTORY ${SCHEMADIR_BUILD})
+unset(gschema_depends CACHE)
+
 # The subdirectories
 ADD_SUBDIRECTORY (gnome)
 ADD_SUBDIRECTORY (gnome-utils)
@@ -93,7 +98,7 @@ SET(GNUCASH_BIN_INSTALL_NAME "gnucash")
 SET(VALGRIND_OUTDIR ${BINDIR_BUILD})
 
 CONFIGURE_FILE(gnucash.rc.in gnucash.rc @ONLY NEWLINE_STYLE WIN32)
-GNC_CONFIGURE(gnucash-valgrind.in ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/gnucash-valgrind)
+configure_file(gnucash-valgrind.in ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/gnucash-valgrind @ONLY)
 
 FILE(COPY ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/gnucash-valgrind
           DESTINATION ${VALGRIND_OUTDIR}
@@ -107,10 +112,9 @@ FILE(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/environment.in ENV_STRINGS_IN)
 SET(ENV_STRINGS_LIST "")
 
 FOREACH(line ${ENV_STRINGS_IN})
-  STRING(REGEX REPLACE "@-|-@" "@" line2 "${line}")
-    STRING(REPLACE ";" "\;" line3 "${line2}")
-  IF(NOT "${line3}" MATCHES "@NOTE")
-    LIST(APPEND ENV_STRINGS_LIST "${line3}\n")
+  STRING(REPLACE ";" "\;" line2 "${line}")
+  IF(NOT "${line2}" MATCHES "@NOTE")
+    LIST(APPEND ENV_STRINGS_LIST "${line2}\n")
   ENDIF()
 ENDFOREACH()
 
@@ -219,6 +223,24 @@ IF (WIN32)
   INSTALL(PROGRAMS ${CMD_FILE} DESTINATION  ${CMAKE_INSTALL_BINDIR})
 ENDIF(WIN32)
 
+
+# Handle gschemas.compiled
+set(CMAKE_COMMAND_TMP "")
+if (${CMAKE_VERSION} VERSION_GREATER 3.1)
+    set(CMAKE_COMMAND_TMP ${CMAKE_COMMAND} -E env)
+endif()
+
+add_custom_command(
+    OUTPUT ${SCHEMADIR_BUILD}/gschemas.compiled
+    COMMAND ${CMAKE_COMMAND_TMP} ${GLIB_COMPILE_SCHEMAS} ${SCHEMADIR_BUILD}
+    DEPENDS ${gschema_depends}
+)
+
+add_custom_target(compiled-schemas ALL DEPENDS ${SCHEMADIR_BUILD}/gschemas.compiled)
+
+install(FILES ${SCHEMADIR_BUILD}/gschemas.compiled DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
+
+
 # The GResource Files are absolute paths but SET_LOCAL_DIST requires
 # relative paths.
 FOREACH(gres_file ${gresource_files})
diff --git a/gnucash/Makefile.am b/gnucash/Makefile.am
index 9e77444..0c0e125 100644
--- a/gnucash/Makefile.am
+++ b/gnucash/Makefile.am
@@ -81,16 +81,16 @@ GNUCASH_BIN_INSTALL_NAME=`echo ${BIN_NAME} | sed -e '$(transform)'`
 gnucash-valgrind: gnucash-valgrind.in ${top_builddir}/config.status Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-	    -e "s#@-TOP_SRC_DIR-@#${abs_top_srcdir}#g" \
-	    -e "s#@-GNUCASH_BIN_INSTALL_NAME-@#${GNUCASH_BIN_INSTALL_NAME}#g"
+	    -e "s#[@]TOP_SRC_DIR[@]#${abs_top_srcdir}#g" \
+	    -e "s#[@]GNUCASH_BIN_INSTALL_NAME[@]#${GNUCASH_BIN_INSTALL_NAME}#g"
 	mv $@.tmp $@
 	chmod u+x $@
 
 environment: environment.in ${top_builddir}/config.status Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-	    -e '/@-NOTE.*-@/ D' \
-	    -e "s#@-GUILE_EFFECTIVE_VERSION-@#@GUILE_EFFECTIVE_VERSION@#g"
+	    -e '/[@]NOTE.*[@]/ D' \
+	    -e "s#[@]GUILE_EFFECTIVE_VERSION[@]#@GUILE_EFFECTIVE_VERSION@#g"
 if CUSTOM_GNC_DBD_DIR
 	echo 'GNC_DBD_DIR=@GNC_DBD_DIR@' >> $@.tmp
 endif
diff --git a/gnucash/environment.in b/gnucash/environment.in
index eb582e2..8c903ac 100644
--- a/gnucash/environment.in
+++ b/gnucash/environment.in
@@ -1,6 +1,6 @@
- at -NOTE If you make any changes here, you should probably    -@
- at -NOTE also verify that modifications performed in          -@
- at -NOTE - gnucash-on-windows.git:gnucash.iss don't conflict. -@
+ at NOTE If you make any changes here, you should probably    @
+ at NOTE also verify that modifications performed in          @
+ at NOTE - gnucash-on-windows.git:gnucash.iss don't conflict. @
 # environment
 #
 # This configuration file defines a number of environment variables to set/alter
@@ -64,7 +64,7 @@ GUILE_LOAD_PATH={GNC_DATA}/scm;{GUILE_LIBS};{GUILE_LOAD_PATH}
 
 # On Windows {GNC_LIB} points to {GNC_HOME}/bin because that's where the DLLs
 # are. It's not where the compiled scheme files are so we use {SYS_LIB} here.
-GUILE_LOAD_COMPILED_PATH={SYS_LIB}/guile/@-GUILE_EFFECTIVE_VERSION-@/ccache;{SYS_LIB}/gnucash/scm/ccache/@-GUILE_EFFECTIVE_VERSION-@;{GUILE_COMPILED_LIBS};{GUILE_LOAD_COMPILED_PATH}
+GUILE_LOAD_COMPILED_PATH={SYS_LIB}/guile/@GUILE_EFFECTIVE_VERSION@/ccache;{SYS_LIB}/gnucash/scm/ccache/@GUILE_EFFECTIVE_VERSION@;{GUILE_COMPILED_LIBS};{GUILE_LOAD_COMPILED_PATH}
 
 # Tell Guile where to find GnuCash specific shared libraries
 GNC_LIBRARY_PATH={SYS_LIB};{GNC_LIB}
diff --git a/gnucash/gnome-utils/gschemas/CMakeLists.txt b/gnucash/gnome-utils/gschemas/CMakeLists.txt
index 7deac7e..3275cd3 100644
--- a/gnucash/gnome-utils/gschemas/CMakeLists.txt
+++ b/gnucash/gnome-utils/gschemas/CMakeLists.txt
@@ -1,7 +1,7 @@
 
 SET(gnome_utils_GSCHEMA org.gnucash.history.gschema.xml org.gnucash.warnings.gschema.xml)
 
-ADD_GSCHEMA_TARGETS(gnome-utils-gschema "${gnome_utils_GSCHEMA}")
+add_gschema_targets("${gnome_utils_GSCHEMA}")
 
 SET_DIST_LIST(gnome_utils_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.history.gschema.xml.in.in
-        org.gnucash.warnings.gschema.xml.in.in)
\ No newline at end of file
+        org.gnucash.warnings.gschema.xml.in.in)
diff --git a/gnucash/gnome/gschemas/CMakeLists.txt b/gnucash/gnome/gschemas/CMakeLists.txt
index 4e1f417..1337e8b 100644
--- a/gnucash/gnome/gschemas/CMakeLists.txt
+++ b/gnucash/gnome/gschemas/CMakeLists.txt
@@ -12,12 +12,7 @@ SET(gnome_GSCHEMA
   org.gnucash.window.pages.gschema.xml
 )
 
-# If you edit one of the above files, you need to rerun cmake.
-# When we can use CONFIGURE_FILE() instead of GNC_CONFIGURE()
-# this issue will go away.
-
-
-ADD_GSCHEMA_TARGETS(gnome-gschema "${gnome_GSCHEMA}")
+add_gschema_targets("${gnome_GSCHEMA}")
 
 SET(gnome_gschema_DIST_local "")
 FOREACH(file ${gnome_GSCHEMA})
diff --git a/gnucash/gnucash-valgrind.in b/gnucash/gnucash-valgrind.in
index a10a874..d62fda0 100644
--- a/gnucash/gnucash-valgrind.in
+++ b/gnucash/gnucash-valgrind.in
@@ -4,7 +4,7 @@ BIN_DIR=$(dirname $0)
 [ -e ${BIN_DIR}/gnucash-setup-env ] && \
    . ${BIN_DIR}/gnucash-setup-env
 
-TOP_SRC_DIR="@-TOP_SRC_DIR-@"
+TOP_SRC_DIR="@TOP_SRC_DIR@"
 #
 # Other potentially useful options, particularly for valgrind-2.x:
 # --tool=memcheck --trace-children=yes
@@ -21,4 +21,4 @@ exec valgrind -v \
     --error-limit=no \
     --tool=memcheck \
     --leak-check=full \
-    ${BIN_DIR}/@-GNUCASH_BIN_INSTALL_NAME-@ "$@"
+    ${BIN_DIR}/@GNUCASH_BIN_INSTALL_NAME@ "$@"
diff --git a/gnucash/import-export/aqb/gschemas/CMakeLists.txt b/gnucash/import-export/aqb/gschemas/CMakeLists.txt
index dce9c13..a16fdad 100644
--- a/gnucash/import-export/aqb/gschemas/CMakeLists.txt
+++ b/gnucash/import-export/aqb/gschemas/CMakeLists.txt
@@ -1,7 +1,8 @@
 
 IF (WITH_AQBANKING)
   SET(aqb_GSCHEMA org.gnucash.dialogs.import.hbci.gschema.xml)
-  ADD_GSCHEMA_TARGETS(aqb-gschema "${aqb_GSCHEMA}")
-ENDIF(WITH_AQBANKING)  
 
-SET_DIST_LIST(aqbanking_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.hbci.gschema.xml.in.in)
\ No newline at end of file
+  add_gschema_targets("${aqb_GSCHEMA}")
+ENDIF(WITH_AQBANKING)
+
+SET_DIST_LIST(aqbanking_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.hbci.gschema.xml.in.in)
diff --git a/gnucash/import-export/csv-exp/gschemas/CMakeLists.txt b/gnucash/import-export/csv-exp/gschemas/CMakeLists.txt
index a2e472c..d5cfcf1 100644
--- a/gnucash/import-export/csv-exp/gschemas/CMakeLists.txt
+++ b/gnucash/import-export/csv-exp/gschemas/CMakeLists.txt
@@ -1,6 +1,6 @@
 
 SET(csv_exp_GSCHEMA org.gnucash.dialogs.export.csv.gschema.xml)
 
-ADD_GSCHEMA_TARGETS(csv-exp-gschema "${csv_exp_GSCHEMA}")
+add_gschema_targets("${csv_exp_GSCHEMA}")
 
-SET_DIST_LIST(csv_exp_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.export.csv.gschema.xml.in.in)
\ No newline at end of file
+SET_DIST_LIST(csv_exp_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.export.csv.gschema.xml.in.in)
diff --git a/gnucash/import-export/csv-imp/gschemas/CMakeLists.txt b/gnucash/import-export/csv-imp/gschemas/CMakeLists.txt
index 066ae60..509ee81 100644
--- a/gnucash/import-export/csv-imp/gschemas/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/gschemas/CMakeLists.txt
@@ -1,5 +1,5 @@
 SET(csv_imp_GSCHEMA org.gnucash.dialogs.import.csv.gschema.xml)
 
-ADD_GSCHEMA_TARGETS(csv-imp-gschema "${csv_imp_GSCHEMA}")
+add_gschema_targets("${csv_imp_GSCHEMA}")
 
-SET_DIST_LIST(csv_import_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.csv.gschema.xml.in.in)
\ No newline at end of file
+SET_DIST_LIST(csv_import_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.csv.gschema.xml.in.in)
diff --git a/gnucash/import-export/gschemas/CMakeLists.txt b/gnucash/import-export/gschemas/CMakeLists.txt
index faf7c00..f674503 100644
--- a/gnucash/import-export/gschemas/CMakeLists.txt
+++ b/gnucash/import-export/gschemas/CMakeLists.txt
@@ -1,6 +1,6 @@
 
 SET(generic_import_GSCHEMA org.gnucash.dialogs.import.generic.gschema.xml)
 
-ADD_GSCHEMA_TARGETS(generic-import-gschema "${generic_import_GSCHEMA}")
+add_gschema_targets("${generic_import_GSCHEMA}")
 
-SET_DIST_LIST(generic_import_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.generic.gschema.xml.in.in)
\ No newline at end of file
+SET_DIST_LIST(generic_import_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.generic.gschema.xml.in.in)
diff --git a/gnucash/import-export/ofx/gschemas/CMakeLists.txt b/gnucash/import-export/ofx/gschemas/CMakeLists.txt
index c48c5b6..ff8a3f9 100644
--- a/gnucash/import-export/ofx/gschemas/CMakeLists.txt
+++ b/gnucash/import-export/ofx/gschemas/CMakeLists.txt
@@ -2,7 +2,7 @@
 IF (WITH_OFX)
   set(ofx_GSCHEMA org.gnucash.dialogs.import.ofx.gschema.xml)
 
-  ADD_GSCHEMA_TARGETS(ofx-gschema "${ofx_GSCHEMA}")
+  add_gschema_targets("${ofx_GSCHEMA}")
 ENDIF (WITH_OFX)
 
-SET_DIST_LIST(ofx_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.ofx.gschema.xml.in.in)
\ No newline at end of file
+SET_DIST_LIST(ofx_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.ofx.gschema.xml.in.in)
diff --git a/gnucash/import-export/qif-imp/gschemas/CMakeLists.txt b/gnucash/import-export/qif-imp/gschemas/CMakeLists.txt
index dbf8f53..0715558 100644
--- a/gnucash/import-export/qif-imp/gschemas/CMakeLists.txt
+++ b/gnucash/import-export/qif-imp/gschemas/CMakeLists.txt
@@ -1,6 +1,6 @@
 
 set(qif_imp_GSCHEMA org.gnucash.dialogs.import.qif.gschema.xml)
 
-ADD_GSCHEMA_TARGETS(qif-imp-gschema "${qif_imp_GSCHEMA}")
+add_gschema_targets("${qif_imp_GSCHEMA}")
 
-SET_DIST_LIST(qif_import_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.qif.gschema.xml.in.in)
\ No newline at end of file
+SET_DIST_LIST(qif_import_gschema_DIST CMakeLists.txt Makefile.am org.gnucash.dialogs.import.qif.gschema.xml.in.in)
diff --git a/libgnucash/core-utils/CMakeLists.txt b/libgnucash/core-utils/CMakeLists.txt
index a6f1c7b..80dd76b 100644
--- a/libgnucash/core-utils/CMakeLists.txt
+++ b/libgnucash/core-utils/CMakeLists.txt
@@ -56,7 +56,7 @@ else()
   SET(sysconfdir ${CMAKE_INSTALL_FULL_SYSCONFDIR})
   SET(localedir "${CMAKE_INSTALL_FULL_DATAROOTDIR}/locale")
 endif()
-GNC_CONFIGURE(gncla-dir.h.in gncla-dir.h)
+configure_file(gncla-dir.h.in gncla-dir.h)
 
 ### Create gnc-version.h ###
 
diff --git a/libgnucash/core-utils/Makefile.am b/libgnucash/core-utils/Makefile.am
index 51f07fa..f59f7a2 100644
--- a/libgnucash/core-utils/Makefile.am
+++ b/libgnucash/core-utils/Makefile.am
@@ -134,18 +134,14 @@ AM_CPPFLAGS += -DG_LOG_DOMAIN=\"gnc.core-utils\" -DGNC_SCM_INSTALL_DIR="\"${GNC_
 gncla-dir.h: gncla-dir.h.in ${top_builddir}/config.status Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-		-e 's#@-DATADIRNAME-@#${DATADIRNAME}#g' \
-		-e 's#@-libdir-@#${libdir}#g' \
-		-e 's#@-bindir-@#${bindir}#g' \
-		-e 's#@-sysconfdir-@#${sysconfdir}#g' \
-		-e 's#@-datadir-@#${datadir}#g' \
-		-e 's#@-prefix-@#${prefix}#g'
+		-e 's#[@]DATADIRNAME[@]#${DATADIRNAME}#g' \
+		-e 's#[@]libdir[@]#${libdir}#g' \
+		-e 's#[@]bindir[@]#${bindir}#g' \
+		-e 's#[@]sysconfdir[@]#${sysconfdir}#g' \
+		-e 's#[@]datadir[@]#${datadir}#g' \
+		-e 's#[@]prefix[@]#${prefix}#g'
 	mv $@.tmp $@
 
-## We borrow guile's convention and use @-...-@ as the substitution
-## brackets here, instead of the usual @... at .  This prevents autoconf
-## from substituting the values directly into the left-hand sides of
-## the sed substitutions.
 gnc-version.h: _gnc-version.h
 	-if [ ! -f gnc-version.h ]; then cp _gnc-version.h gnc-version.h; fi
 	-cmp -s _gnc-version.h gnc-version.h || cp _gnc-version.h gnc-version.h
diff --git a/libgnucash/core-utils/gncla-dir.h.in b/libgnucash/core-utils/gncla-dir.h.in
index a55d90f..d15dce7 100644
--- a/libgnucash/core-utils/gncla-dir.h.in
+++ b/libgnucash/core-utils/gncla-dir.h.in
@@ -23,11 +23,11 @@
  *  02110-1301, USA.
  */
 
-#define PREFIX "@-prefix-@"
-#define DATADIR "@-datadir-@"
-#define SYSCONFDIR "@-sysconfdir-@"
+#define PREFIX "@prefix@"
+#define DATADIR "@datadir@"
+#define SYSCONFDIR "@sysconfdir@"
 
-#define BINDIR "@-bindir-@"
-#define LIBDIR "@-libdir-@"
+#define BINDIR "@bindir@"
+#define LIBDIR "@libdir@"
 
-#define LOCALEDIR "@-localedir-@"
+#define LOCALEDIR "@localedir@"
diff --git a/libgnucash/doc/Makefile.am b/libgnucash/doc/Makefile.am
index 4c6124d..2827224 100644
--- a/libgnucash/doc/Makefile.am
+++ b/libgnucash/doc/Makefile.am
@@ -50,13 +50,9 @@ maintainer-clean-local:
 uninstall-hook:
 	rm -rf ${docdir}/html
 
-## We borrow guile's convention and use @-...-@ as the substitution
-## brackets here, instead of the usual @... at .  This prevents autoconf
-## from substituting the values directly into the left-hand sides of
-## the sed substitutions.
 doxygen.cfg: doxygen.cfg.in Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-            -e 's:@-top_srcdir-@:${top_srcdir}:g; s:@-VERSION-@:${VERSION}:g'
+            -e 's:[@]top_srcdir[@]:${top_srcdir}:g; s:[@]VERSION[@]:${VERSION}:g'
 	mv $@.tmp $@
 
diff --git a/libgnucash/doc/doxygen.cfg.in b/libgnucash/doc/doxygen.cfg.in
index cde836c..8663366 100644
--- a/libgnucash/doc/doxygen.cfg.in
+++ b/libgnucash/doc/doxygen.cfg.in
@@ -32,7 +32,7 @@ PROJECT_NAME           = GnuCash
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = @-VERSION-@
+PROJECT_NUMBER         = @VERSION@
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer
@@ -652,8 +652,8 @@ WARN_LOGFILE           = doxygen.log
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = @-top_srcdir-@/libgnucash \
-                         @-top_srcdir-@/libgnucash/engine/
+INPUT                  = @top_srcdir@/libgnucash \
+                         @top_srcdir@/libgnucash/engine/
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -1548,7 +1548,7 @@ SEARCH_INCLUDES        = YES
 # contain include files that are not input files but should be processed by
 # the preprocessor.
 
-INCLUDE_PATH           = @-top_srcdir-@/libgnucash/engine/
+INCLUDE_PATH           = @top_srcdir@/libgnucash/engine/
 
 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
 # patterns (like *.h and *.hpp) to filter out the header-files in the
diff --git a/libgnucash/quotes/CMakeLists.txt b/libgnucash/quotes/CMakeLists.txt
index aa7477e..e42ec9c 100644
--- a/libgnucash/quotes/CMakeLists.txt
+++ b/libgnucash/quotes/CMakeLists.txt
@@ -12,15 +12,15 @@ FILE(WRITE ${_TMPDIR}/copy_with_perms.cmake
 SET(_BIN_FILES "")
 FOREACH(file gnc-fq-check.in gnc-fq-helper.in gnc-fq-update.in gnc-fq-dump)
   STRING(REPLACE ".in" "" _OUTPUT_FILE_NAME ${file})
-  GNC_CONFIGURE2(${file}-target ${file} ${_OUTPUT_FILE_NAME})
-  SET(_ABS_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${_OUTPUT_FILE_NAME})
+  SET(_ABS_OUTPUT_FILE ${BINDIR_BUILD}/${_OUTPUT_FILE_NAME})
+  configure_file( ${file} ${_OUTPUT_FILE_NAME} @ONLY)
   LIST(APPEND _BIN_FILES ${_ABS_OUTPUT_FILE})
   ADD_CUSTOM_COMMAND(
     OUTPUT ${_ABS_OUTPUT_FILE}
-    APPEND
-    COMMAND ${CMAKE_COMMAND} -D SRC=${_ABS_OUTPUT_FILE}
+    COMMAND ${CMAKE_COMMAND} -D SRC=${CMAKE_CURRENT_BINARY_DIR}/${_OUTPUT_FILE_NAME}
                              -D DST=${BINDIR_BUILD}
                              -P ${_TMPDIR}/copy_with_perms.cmake
+    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_OUTPUT_FILE_NAME}
   )
 ENDFOREACH(file)
 
@@ -45,7 +45,7 @@ ENDFOREACH(file)
 
 
 ADD_CUSTOM_TARGET(quotes-man ALL DEPENDS ${_MAN_FILES})
-ADD_CUSTOM_TARGET(quotes-bin ALL DEPENDS gnc-fq-check gnc-fq-update)
+ADD_CUSTOM_TARGET(quotes-bin ALL DEPENDS ${_BIN_FILES})
 INSTALL(FILES ${_MAN_FILES} DESTINATION  ${CMAKE_INSTALL_MANDIR}/man1)
 INSTALL(PROGRAMS ${_BIN_FILES} DESTINATION ${CMAKE_INSTALL_BINDIR})
 
diff --git a/libgnucash/quotes/Makefile.am b/libgnucash/quotes/Makefile.am
index 5ba58cb..d507eaf 100644
--- a/libgnucash/quotes/Makefile.am
+++ b/libgnucash/quotes/Makefile.am
@@ -13,31 +13,27 @@ EXTRA_DIST = \
   gnc-value-portfolio \
   CMakeLists.txt
 
-## We borrow guile's convention and use @-...-@ as the substitution
-## brackets here, instead of the usual @... at .  This prevents autoconf
-## from substituting the values directly into the left-hand sides of
-## the sed substitutions.
 gnc-fq-helper: gnc-fq-helper.in Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-            -e 's:@-PERL-@:${PERL}:g' \
-            -e 's:@-PERLINCL-@:${PERLINCL}:g'
+            -e 's:[@]PERL[@]:${PERL}:g' \
+            -e 's:[@]PERLINCL[@]:${PERLINCL}:g'
 	chmod +x $@.tmp
 	mv $@.tmp $@
 
 gnc-fq-check: gnc-fq-check.in Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-            -e 's:@-PERL-@:${PERL}:g' \
-            -e 's:@-PERLINCL-@:${PERLINCL}:g'
+            -e 's:[@]PERL[@]:${PERL}:g' \
+            -e 's:[@]PERLINCL[@]:${PERLINCL}:g'
 	chmod +x $@.tmp
 	mv $@.tmp $@
 
 gnc-fq-update: gnc-fq-update.in Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-            -e 's:@-PERL-@:${PERL}:g' \
-            -e 's:@-PERLINCL-@:${PERLINCL}:g'
+            -e 's:[@]PERL[@]:${PERL}:g' \
+            -e 's:[@]PERLINCL[@]:${PERLINCL}:g'
 	chmod +x $@.tmp
 	mv $@.tmp $@
 
diff --git a/libgnucash/quotes/gnc-fq-check.in b/libgnucash/quotes/gnc-fq-check.in
index 48f31e6..6b76829 100755
--- a/libgnucash/quotes/gnc-fq-check.in
+++ b/libgnucash/quotes/gnc-fq-check.in
@@ -1,4 +1,4 @@
-#!@-PERL-@ -w
+#!@PERL@ -w
 ######################################################################
 ### gnc-fq-check - check for the presence of  Finance::Quote
 ### From gnc-fq-helper.
diff --git a/libgnucash/quotes/gnc-fq-helper.in b/libgnucash/quotes/gnc-fq-helper.in
index 79d8515..75ce578 100755
--- a/libgnucash/quotes/gnc-fq-helper.in
+++ b/libgnucash/quotes/gnc-fq-helper.in
@@ -1,4 +1,4 @@
-#!@-PERL-@ -w
+#!@PERL@ -w
 ######################################################################
 ### gnc-fq-helper - present a scheme interface to Finance::Quote
 ### Copyright 2001 Rob Browning <rlb at cs.utexas.edu>
diff --git a/libgnucash/quotes/gnc-fq-update.in b/libgnucash/quotes/gnc-fq-update.in
index 2002086..797a6d1 100755
--- a/libgnucash/quotes/gnc-fq-update.in
+++ b/libgnucash/quotes/gnc-fq-update.in
@@ -1,4 +1,4 @@
-#!@-PERL-@ -w
+#!@PERL@ -w
 ######################################################################
 ### gnc-fq-update - presents a scheme interface to Finance::Quote
 ### Copyright 2001 Gnumatic, Inc.
diff --git a/libgnucash/scm/CMakeLists.txt b/libgnucash/scm/CMakeLists.txt
index f11cc81..d79a645 100644
--- a/libgnucash/scm/CMakeLists.txt
+++ b/libgnucash/scm/CMakeLists.txt
@@ -7,16 +7,7 @@ SET (scm_SCHEME_4
     xml-generator.scm
 )
 
-# We need to replace a couple of variables in build-config.scm. Normally, I 
-# would use CONFIGURE_FILE to do this. But this scheme files is using a different
-# replacement marking scheme ("@-", "-@") than the usual one with just "@" 
-# that CONFIGURE_FILE expects.
-
-# FIXME: ${BUILD_CONFIG_SCM} needs a rule to cause it to be generated, I think.
-
-GNC_CONFIGURE2(build-config-scm build-config.scm.in build-config.scm)
-
-# CONFIGURE_FILE(build-config.scm.in ${BUILD_CONFIG_SCM})
+configure_file(build-config.scm.in ${BUILD_CONFIG_SCM})
 
 SET(GUILE_DEPENDS      scm-core-utils scm-gnc-module)
 
diff --git a/libgnucash/scm/Makefile.am b/libgnucash/scm/Makefile.am
index 2d69f08..5148c1d 100644
--- a/libgnucash/scm/Makefile.am
+++ b/libgnucash/scm/Makefile.am
@@ -70,15 +70,11 @@ EXTRA_DIST = \
   ${SCM_FILES} \
   CMakeLists.txt
 
-## We borrow guile's convention and use @-...-@ as the substitution
-## brackets here, instead of the usual @... at .  This prevents autoconf
-## from substituting the values directly into the left-hand sides of
-## the sed substitutions.  *sigh*
 build-config.scm: ${srcdir}/build-config.scm.in Makefile
 	rm -f $@.tmp
 	sed < $< > $@.tmp \
-            -e 's#@-VERSION-@#${VERSION}#' \
-            -e 's#@-GNC_HELPDIR-@#${GNC_HELPDIR}#'
+            -e 's#[@]VERSION[@]#${VERSION}#' \
+            -e 's#[@]GNC_HELPDIR[@]#${GNC_HELPDIR}#'
 	mv $@.tmp $@
 
 CLEANFILES = .scm-links ${gncscmmodcache_DATA} ${gncscmcache_DATA}
diff --git a/libgnucash/scm/build-config.scm.in b/libgnucash/scm/build-config.scm.in
index 13687ed..f906a33 100644
--- a/libgnucash/scm/build-config.scm.in
+++ b/libgnucash/scm/build-config.scm.in
@@ -1,6 +1,6 @@
 
-(define gnc:version "@-VERSION-@")
+(define gnc:version "@VERSION@")
 
 ;; Automatically generated defaults (don't use these directly --
 ;; they're used during actual initialization elsewhere)
-(define gnc:_install-doc-path_ '("@-GNC_HELPDIR-@"))
+(define gnc:_install-doc-path_ '("@GNC_HELPDIR@"))
diff --git a/make-gnucash-potfiles.in b/make-gnucash-potfiles.in
index 6571fe5..feed1f5 100644
--- a/make-gnucash-potfiles.in
+++ b/make-gnucash-potfiles.in
@@ -1,4 +1,4 @@
-#!@-PERL-@ -w
+#!@PERL@ -w
 # -*- perl -*-
 #
 # This perl script is used to make po/POTFILES.in, the list
@@ -15,12 +15,12 @@ use File::Basename;
 # Note: These are perl regexp patterns, *not* normal shell wildcards!
 my @ignorepatterns = ('gw-', 'test', 'experimental', 'python-bindings', 'swig-.*\.c');
 my (@skipped_files, @ignored_files);
-open(IN, "< @-SRCDIR-@/po/POTFILES.skip");
+open(IN, "< @SRCDIR@/po/POTFILES.skip");
 while (<IN>) {
     push @skipped_files, $_ unless $_ =~ /^\#/;
 }
 close IN;
-open(IN, "< @-SRCDIR-@/po/POTFILES.ignore");
+open(IN, "< @SRCDIR@/po/POTFILES.ignore");
 while (<IN>) {
     push @ignored_files, $_ unless $_ =~ /^\#/;
 }
@@ -36,7 +36,7 @@ close IN;
 # This sort function has been extracted into a separate file (util/elegant-sort.pl)
 # in order to use the same algorithm in both cmake and autools based builds
 
-my @possible_files = `cd @-SRCDIR-@ && \\
+my @possible_files = `cd @SRCDIR@ && \\
                       find bindings borrowed common libgnucash gnucash -name '*.c' \\
                       -o -name '*.cpp' -o -name '*.glade' \\
                       -o -name '*.desktop.in' -o -name '*.keys.in' \\
@@ -65,7 +65,7 @@ foreach my $file (@possible_files) {
     next if $ignores{$path . $name};
 
     # Ignore unreadable files, e.g. dangling symlinks
-    next unless (-r "@-SRCDIR-@/" . $path . $name);
+    next unless (-r "@SRCDIR@/" . $path . $name);
 
     # Force parse type for gsettings files
     my $type = "";

commit 19412de2216a624a6a27e4f1afd4264deb8e57aa
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Dec 22 11:07:29 2017 -0800

    Fix use of guile function introduced in 2.0.10, not available in Ubuntu14.04.
    
    Also generalize KVP type detection so that signed and unsigned values\nare appropriately converted.

diff --git a/libgnucash/engine/kvp-scm.cpp b/libgnucash/engine/kvp-scm.cpp
index 2e95e65..07a7859 100644
--- a/libgnucash/engine/kvp-scm.cpp
+++ b/libgnucash/engine/kvp-scm.cpp
@@ -27,16 +27,21 @@ gnc_scm_to_kvp_value_ptr(SCM val)
 {
     if (scm_is_rational(val))
     {
-        if (scm_is_exact_integer(val) &&
-            scm_is_signed_integer(val, INT64_MIN, INT64_MAX))
+        if (scm_is_exact(val) &&
+            (scm_is_signed_integer(val, INT64_MIN, INT64_MAX) ||
+             scm_is_unsigned_integer(val, INT64_MIN, INT64_MAX)))
         {
             return new KvpValue{scm_to_int64(val)};
         }
         else if (scm_is_exact(val) &&
-                 scm_is_signed_integer(scm_numerator(val),
-                                       INT64_MIN, INT64_MAX) &&
-                 scm_is_signed_integer(scm_denominator(val),
-                                       INT64_MIN, INT64_MAX))
+                 (scm_is_signed_integer(scm_numerator(val),
+                                       INT64_MIN, INT64_MAX) ||
+                  scm_is_unsigned_integer(scm_numerator(val),
+                                          INT64_MIN, INT64_MAX)) &&
+                 (scm_is_signed_integer(scm_denominator(val),
+                                        INT64_MIN, INT64_MAX) ||
+                  (scm_is_unsigned_integer(scm_denominator(val),
+                                           INT64_MIN, INT64_MAX))))
         {
             return new KvpValue{gnc_scm_to_numeric(val)};
         }

commit 6e9025d2f93c9c1f36c9da84ae083eb339ce26ab
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Dec 22 10:26:31 2017 -0800

    Don't build borrowed/gwengui-gtk3 if its provided by gwenhywfar.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 05aff73..0a021a7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -319,6 +319,10 @@ IF (WITH_AQBANKING)
     IF(KTOBLZCHECK_FOUND)
       SET(HAVE_KTOBLZCHECK_H 1)
     ENDIF(KTOBLZCHECK_FOUND)
+    GNC_PKG_CHECK_MODULES (GWEN_GTK3 gwengui-gtk3)
+    IF(GWEN_GTK3_FOUND)
+      SET(HAVE_GWEN_GTK3 1 CACHE BOOL "True if gwen-gtk3.pc exists")
+    ENDIF(GWEN_GTK3_FOUND)
   ENDIF(WITH_GNUCASH)
 ENDIF (WITH_AQBANKING)
 
diff --git a/borrowed/CMakeLists.txt b/borrowed/CMakeLists.txt
index 1d10252..bae9763 100644
--- a/borrowed/CMakeLists.txt
+++ b/borrowed/CMakeLists.txt
@@ -1,6 +1,6 @@
 ADD_SUBDIRECTORY(libc)
 ADD_SUBDIRECTORY(goffice)
-if (WITH_AQBANKING)
+if (WITH_AQBANKING AND NOT HAVE_GWEN_GTK3)
   ADD_SUBDIRECTORY(gwengui-gtk3)
 endif()
 SET_LOCAL_DIST(borrowed_DIST_local CMakeLists.txt Makefile.am README)
diff --git a/gnucash/import-export/aqb/gnc-gwen-gui.c b/gnucash/import-export/aqb/gnc-gwen-gui.c
index db8792f..592e029 100644
--- a/gnucash/import-export/aqb/gnc-gwen-gui.c
+++ b/gnucash/import-export/aqb/gnc-gwen-gui.c
@@ -196,7 +196,7 @@ static gint progress_advance_cb(GWEN_GUI *gwen_gui, uint32_t id,
 static gint progress_log_cb(GWEN_GUI *gwen_gui, guint32 id,
                             GWEN_LOGGER_LEVEL level, const gchar *text);
 static gint progress_end_cb(GWEN_GUI *gwen_gui, guint32 id);
-#if GWENHYWFAR_VERSION_INT <= 49900
+#if GWENHYWFAR_VERSION_INT < 49900
 static gint GNC_GWENHYWFAR_CB getpassword_cb(GWEN_GUI *gwen_gui, guint32 flags,
                                              const gchar *token,
                                              const gchar *title,
@@ -1410,7 +1410,7 @@ progress_end_cb(GWEN_GUI *gwen_gui, guint32 id)
 }
 
 static gint GNC_GWENHYWFAR_CB
-#if GWENHYWFAR_VERSION_INT <= 49900
+#if GWENHYWFAR_VERSION_INT < 49900
 getpassword_cb(GWEN_GUI *gwen_gui, guint32 flags, const gchar *token,
                const gchar *title, const gchar *text, gchar *buffer,
                gint min_len, gint max_len, guint32 guiid)

commit e0300d3a623fe21224c22fc4f2c44565111c2ec9
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 21 15:30:49 2017 -0800

    Replace the gnc:numeric pair with normal Scheme rationals.
    
    This allows direct conversion between Scheme numbers and gnc_numeric
    without the performance or accuracy penalties arising from using doubles
    as an intermediary.

diff --git a/gnucash/report/business-reports/balsheet-eg.eguile.scm b/gnucash/report/business-reports/balsheet-eg.eguile.scm
index c04f7f1..4066ad5 100644
--- a/gnucash/report/business-reports/balsheet-eg.eguile.scm
+++ b/gnucash/report/business-reports/balsheet-eg.eguile.scm
@@ -288,7 +288,7 @@
 <?scm
       (for xpair in xlist do
           (let* ((comm (car xpair))
-                 (one-num (gnc:make-gnc-numeric 10000 1))
+                 (one-num 10000/1)
                  (one-foreign-mny (gnc:make-gnc-monetary comm one-num))
                  (one-local-mny (exchange-fn one-foreign-mny opt-report-commodity)))
 ?>
diff --git a/gnucash/report/business-reports/receipt.scm b/gnucash/report/business-reports/receipt.scm
index 890510b..d920a44 100644
--- a/gnucash/report/business-reports/receipt.scm
+++ b/gnucash/report/business-reports/receipt.scm
@@ -46,7 +46,7 @@
   (if (or (not taxable) (eq? taxtable '()))
     (display " ")
     (let* ((amttot  (gnc:make-commodity-collector))
-           (pctot   (gnc:make-numeric-collector))
+           (pctot   (gnc:make-number-collector))
            (entries (gncTaxTableGetEntries taxtable))
            (amt?    #f)  ; becomes #t if any entries are amounts
            (pc?     #f)) ; becomes #t if any entries are percentages
diff --git a/gnucash/report/business-reports/taxinvoice.scm b/gnucash/report/business-reports/taxinvoice.scm
index 2417c6b..07dd1bc 100644
--- a/gnucash/report/business-reports/taxinvoice.scm
+++ b/gnucash/report/business-reports/taxinvoice.scm
@@ -53,7 +53,7 @@
   (if (or (not taxable) (eq? taxtable '()))
     (display " ")
     (let* ((amttot  (gnc:make-commodity-collector))
-           (pctot   (gnc:make-numeric-collector)) 
+           (pctot   (gnc:make-number-collector)) 
            (entries (gncTaxTableGetEntries taxtable))
            (amt?    #f)  ; becomes #t if any entries are amounts
            (pc?     #f)) ; becomes #t if any entries are percentages
diff --git a/gnucash/report/locale-specific/us/taxtxf.scm b/gnucash/report/locale-specific/us/taxtxf.scm
index fe17aa9..ba2ad26 100644
--- a/gnucash/report/locale-specific/us/taxtxf.scm
+++ b/gnucash/report/locale-specific/us/taxtxf.scm
@@ -701,7 +701,7 @@
                               (gnc-commodity-equiv account-commodity
                                                                  USD-currency)))
                      (xaccSplitGetValue split)
-                     (gnc:make-gnc-numeric 100 100)))
+                     100/100))
      (missing-pricedb-entry? #f)
      (pricedb-lookup-price #f)
      (pricedb-lookup-price-value (gnc-numeric-zero))
@@ -798,7 +798,7 @@
                                                            trans-currency
                                                            USD-currency))
                                            (gnc-numeric-div
-                                               (gnc:make-gnc-numeric 100 100)
+                                               100/100
                                                (xaccSplitGetSharePrice split)
                                                GNC-DENOM-AUTO
                                                (logior (GNC-DENOM-SIGFIGS 6)
diff --git a/gnucash/report/report-system/commodity-utilities.scm b/gnucash/report/report-system/commodity-utilities.scm
index d14afab..3ff6c4e 100644
--- a/gnucash/report/report-system/commodity-utilities.scm
+++ b/gnucash/report/report-system/commodity-utilities.scm
@@ -415,8 +415,8 @@
     ;; numeric-collectors, where [abc] are numeric-collectors. See the
     ;; real variable names below.
     (define (make-newrate unknown-coll un->known-coll known-pair)
-      (let ((a (gnc:make-numeric-collector))
-            (b (gnc:make-numeric-collector)))
+      (let ((a (gnc:make-number-collector))
+            (b (gnc:make-number-collector)))
         (a 'add (unknown-coll 'total #f))
         (b 'add
            ;; round to (at least) 8 significant digits
@@ -459,7 +459,7 @@
                          ;; If this is an Euro currency, create the
                          ;; pair of appropriately exchanged amounts.
                          (if euro-monetary
-                             (let ((a (gnc:make-numeric-collector)))
+                             (let ((a (gnc:make-number-collector)))
                                (a 'add
                                   (gnc:gnc-monetary-amount euro-monetary))
                                (list report-commodity
@@ -532,8 +532,8 @@
 
 (define (create-commodity-list inner-comm outer-comm share-amount value-amount)
   (let ((foreignlist (list inner-comm
-                    (cons (gnc:make-numeric-collector)
-                          (gnc:make-numeric-collector))))
+                    (cons (gnc:make-number-collector)
+                          (gnc:make-number-collector))))
         (comm-list #f))
     ((caadr foreignlist) 'add share-amount)
     ((cdadr foreignlist) 'add value-amount)
@@ -560,8 +560,8 @@
     (if (not pair)
         (begin
           (set! pair (list (car foreignlist)
-                         (cons (gnc:make-numeric-collector)
-                               (gnc:make-numeric-collector))))
+                         (cons (gnc:make-number-collector)
+                               (gnc:make-number-collector))))
           (gnc:debug "New commodity "
                      (gnc-commodity-get-mnemonic (car foreignlist)))))
     pair))
diff --git a/gnucash/report/report-system/html-barchart.scm b/gnucash/report/report-system/html-barchart.scm
index 76c18a2..6ef3c2d 100644
--- a/gnucash/report/report-system/html-barchart.scm
+++ b/gnucash/report/report-system/html-barchart.scm
@@ -300,8 +300,6 @@
              (lambda ()
                (let ((n (read)))
                  (if (number? n) n 0.0)))))
-          ((gnc:gnc-numeric? elt)
-           (gnc-numeric-to-double elt))
           (#t 
            0.0)))
   
diff --git a/gnucash/report/report-system/html-linechart.scm b/gnucash/report/report-system/html-linechart.scm
index 927836f..7b0688d 100644
--- a/gnucash/report/report-system/html-linechart.scm
+++ b/gnucash/report/report-system/html-linechart.scm
@@ -335,8 +335,6 @@
              (lambda ()
                (let ((n (read)))
                  (if (number? n) n 0.0)))))
-          ((gnc:gnc-numeric? elt)
-           (gnc-numeric-to-double elt))
           (#t
            0.0)))
 
diff --git a/gnucash/report/report-system/html-piechart.scm b/gnucash/report/report-system/html-piechart.scm
index f8b74a8..9fd79e6 100644
--- a/gnucash/report/report-system/html-piechart.scm
+++ b/gnucash/report/report-system/html-piechart.scm
@@ -152,9 +152,7 @@
                 (lambda ()
                   (let ((n (read)))
                     (if (number? n) (abs n) 0.0)))))
-             ((gnc:gnc-numeric? elt)
-              (abs (gnc-numeric-to-double elt)))
-             (#t 
+             (#t
               0.0)))
      nlist))
   
diff --git a/gnucash/report/report-system/html-scatter.scm b/gnucash/report/report-system/html-scatter.scm
index 13a7440..3134208 100644
--- a/gnucash/report/report-system/html-scatter.scm
+++ b/gnucash/report/report-system/html-scatter.scm
@@ -131,9 +131,7 @@
              (lambda ()
                (let ((n (read)))
                  (if (number? n) n 0.0)))))
-          ((gnc:gnc-numeric? elt)
-           (gnc-numeric-to-double elt))
-          (#t 
+          (#t
            0.0)))
   
   (let* ((retval '())
diff --git a/gnucash/report/report-system/report-system.scm b/gnucash/report/report-system/report-system.scm
index 0efdd9d..1d6c65c 100644
--- a/gnucash/report/report-system/report-system.scm
+++ b/gnucash/report/report-system/report-system.scm
@@ -671,7 +671,7 @@
 (export gnc:make-stats-collector)
 (export gnc:make-drcr-collector)
 (export gnc:make-value-collector)
-(export gnc:make-numeric-collector)
+(export gnc:make-number-collector)
 (export gnc:make-commodity-collector)
 (export gnc:commodity-collector-get-negated)
 (export gnc:commodity-collectorlist-get-merged)
diff --git a/gnucash/report/report-system/report-utilities.scm b/gnucash/report/report-system/report-utilities.scm
index 9a9ff131..462dd0e 100644
--- a/gnucash/report/report-system/report-utilities.scm
+++ b/gnucash/report/report-system/report-utilities.scm
@@ -266,24 +266,24 @@
 
 
 ;; Same as above but with gnc:numeric
-(define (gnc:make-numeric-collector)
+(define (gnc:make-number-collector)
   (let ;;; values
-      ((value (gnc-numeric-zero)))
+      ((value 0))
     (lambda (action amount)  ;;; Dispatch function
       (case action
-	((add) (if (gnc:gnc-numeric? amount) 
-                   (set! value (gnc-numeric-add amount value
-                                                GNC-DENOM-AUTO GNC-DENOM-LCD))
-                   (gnc:warn 
-                    "gnc:numeric-collector called with wrong argument: "
+	((add) (if (number? amount)
+                     (set! value (gnc-numeric-add amount value
+                                                  GNC-DENOM-AUTO GNC-DENOM-LCD))
+                   (gnc:warn
+                    "gnc:Number-collector called with wrong argument: "
                     amount)))
 	((total) value)
-	(else (gnc:warn "bad gnc:numeric-collector action: " action))))))
+	(else (gnc:warn "bad gnc:number-collector action: " action))))))
 
 ;; Replace all 'action function calls by the normal functions below.
-(define (gnc:numeric-collector-add collector amount)
+(define (gnc:number-collector-add collector amount)
   (collector 'add amount))
-(define (gnc:numeric-collector-total collector)
+(define (gnc:number-collector-total collector)
   (collector 'total #f))
 
 ;; A commodity collector. This is intended to handle multiple
@@ -338,12 +338,12 @@
                      (gnc-commodity-get-fraction commodity) GNC-RND-ROUND)))
 	(if (not pair)
 	    (begin
-	      ;; create a new pair, using the gnc:numeric-collector
-	      (set! pair (list commodity (gnc:make-numeric-collector)))
+	      ;; create a new pair, using the gnc:number-collector
+	      (set! pair (list commodity (gnc:make-number-collector)))
 	      ;; and add it to the alist
 	      (set! commoditylist (cons pair commoditylist))))
 	;; add the value
-	(gnc:numeric-collector-add (cadr pair) rvalue)))
+	(gnc:number-collector-add (cadr pair) rvalue)))
     
     ;; helper function to walk an association list, adding each
     ;; (commodity -> collector) pair to our list at the appropriate 
@@ -352,7 +352,7 @@
       (cond ((null? clist) '())
 	    (else (add-commodity-value 
 		   (caar clist) 
-		   (gnc:numeric-collector-total (cadar clist)))
+		   (gnc:number-collector-total (cadar clist)))
 		  (add-commodity-clist (cdr clist)))))
 
     (define (minus-commodity-clist clist)
@@ -360,7 +360,7 @@
 	    (else (add-commodity-value 
 		   (caar clist) 
 		   (gnc-numeric-neg
-		    (gnc:numeric-collector-total (cadar clist))))
+		    (gnc:number-collector-total (cadar clist))))
 		  (minus-commodity-clist (cdr clist)))))
 
     ;; helper function walk the association list doing a callback on
@@ -368,7 +368,7 @@
     (define (process-commodity-list fn clist)
       (map 
        (lambda (pair) (fn (car pair) 
-			  (gnc:numeric-collector-total (cadr pair))))
+			  (gnc:number-collector-total (cadr pair))))
        clist))
 
     ;; helper function which is given a commodity and returns, if
@@ -381,8 +381,8 @@
 		  (gnc-numeric-zero)
 		  (if sign?
 		      (gnc-numeric-neg
-		       (gnc:numeric-collector-total (cadr pair)))
-		      (gnc:numeric-collector-total (cadr pair))))
+		       (gnc:number-collector-total (cadr pair)))
+		      (gnc:number-collector-total (cadr pair))))
 	      '()))))
 
     ;; helper function which is given a commodity and returns, if
@@ -395,8 +395,8 @@
 	       (gnc-numeric-zero)
 	       (if sign?
 		   (gnc-numeric-neg
-		    (gnc:numeric-collector-total (cadr pair)))
-		   (gnc:numeric-collector-total (cadr pair)))))))
+		    (gnc:number-collector-total (cadr pair)))
+		   (gnc:number-collector-total (cadr pair)))))))
     
     ;; Dispatch function
     (lambda (action commodity amount)
diff --git a/gnucash/report/standard-reports/advanced-portfolio.scm b/gnucash/report/standard-reports/advanced-portfolio.scm
index 6a05845..07fbf32 100644
--- a/gnucash/report/standard-reports/advanced-portfolio.scm
+++ b/gnucash/report/standard-reports/advanced-portfolio.scm
@@ -321,7 +321,7 @@
                ;; If the units ratio is zero the stock is worthless and the value should be zero too
 	       (value-ratio (if (gnc-numeric-zero-p units-ratio)
 	                        (gnc-numeric-zero)
-                                (gnc-numeric-div (gnc:make-gnc-numeric 1 1) units-ratio GNC-DENOM-AUTO GNC-DENOM-REDUCE))))
+                                (gnc-numeric-div 1/1 units-ratio GNC-DENOM-AUTO GNC-DENOM-REDUCE))))
 
 	  (gnc:debug "blist is " b-list " current units is "
 	             (gnc-numeric-to-string current-units)
@@ -341,7 +341,7 @@
 
 	(gnc:debug "this is a spinoff")
 	(gnc:debug "blist is " b-list " value ratio is " (gnc-numeric-to-string value-ratio))
-	(apply-basis-ratio b-list (gnc:make-gnc-numeric 1 1) value-ratio))
+	(apply-basis-ratio b-list 1/1 value-ratio))
       )
 
      ;; when all else fails, just send the b-list back
@@ -473,7 +473,7 @@
                                        (exchange-fn
                                           (gnc:make-gnc-monetary
                                             (gnc-price-get-currency price)
-                                            (gnc:make-gnc-numeric 100 1))
+                                            100/1)
                                           currency))))
                 (set! price #f))
 
@@ -514,7 +514,7 @@
             ;; If we still don't have a price, use a price of 1 and complain later
             (if (not price)
               (begin
-                (set! price (gnc:make-gnc-monetary currency (gnc:make-gnc-numeric 1 1)))
+                (set! price (gnc:make-gnc-monetary currency 1/1))
                 ;; If use-txn is set, but pricing-txn isn't set, it's a bogus price
                 (set! use-txn #t)
                 (set! pricing-txn #f)
diff --git a/gnucash/report/standard-reports/cash-flow.scm b/gnucash/report/standard-reports/cash-flow.scm
index 7d857d0..cb2d200 100644
--- a/gnucash/report/standard-reports/cash-flow.scm
+++ b/gnucash/report/standard-reports/cash-flow.scm
@@ -427,9 +427,9 @@
 		 (gnc:timepair-ge (gnc-transaction-get-date-posted parent) from-date-tp))
 	    (let* ((parent-description (xaccTransGetDescription parent))
 		   (parent-currency (xaccTransGetCurrency parent)))
-					;(gnc:debug parent-description
-					;           " - "
-					;           (gnc-commodity-get-printname parent-currency))
+					(gnc:debug parent-description
+					           " - "
+					           (gnc-commodity-get-printname parent-currency))
 	      (for-each
 	       (lambda (s)
 		 (let* ((s-account (xaccSplitGetAccount s))
@@ -444,7 +444,7 @@
 			(string-append
 			 "WARNING: s-account is NULL for split: "
 			 (gncSplitGetGUID s) "\n")))
-					;(gnc:debug (xaccAccountGetName s-account))
+					(gnc:debug (xaccAccountGetName s-account))
 		   (if (and	 ;; make sure we don't have
 			(not (null? s-account)) ;;  any dangling splits
 			(or include-trading-accounts (not (eq? s-account-type ACCT-TYPE-TRADING)))
@@ -453,7 +453,7 @@
 			   (begin
 			     (if (gnc-numeric-negative-p s-value)
 				 (let ((s-account-in-collector (account-hashtable-ref money-in-hash s-account)))
-					;(gnc:debug "in:" (gnc-commodity-get-printname s-commodity)
+                                        ;(gnc:debug "in:" (gnc-commodity-get-printname s-commodity)
 					;	     (gnc-numeric-to-double s-amount)
 					;	     (gnc-commodity-get-printname parent-currency)
 					;	     (gnc-numeric-to-double s-value))
@@ -494,14 +494,14 @@
 				   )
 				 )
 			     )
-			   )
-		       )
+                           )
+                       )
 		   )
 		 )
 	       (xaccTransGetSplitList parent)
 	       )
 	      )
-	    )
+            )
 	)
       )
 
diff --git a/gnucash/report/standard-reports/category-barchart.scm b/gnucash/report/standard-reports/category-barchart.scm
index 3cb4bb2..cd04b91 100644
--- a/gnucash/report/standard-reports/category-barchart.scm
+++ b/gnucash/report/standard-reports/category-barchart.scm
@@ -306,20 +306,20 @@ developing over time"))
                      (let* ((start-frac-avg (averaging-fraction-func (gnc:timepair->secs from-date-tp)))
                              (end-frac-avg (averaging-fraction-func (+ 1 (gnc:timepair->secs to-date-tp))))
                              (diff-avg (- end-frac-avg start-frac-avg))
-                             (diff-avg-numeric (gnc:make-gnc-numeric
+                             (diff-avg-numeric (/
                                                 (inexact->exact (round (* diff-avg 1000000))) ; 6 decimals precision
                                                 1000000))
                              (start-frac-int (interval-fraction-func (gnc:timepair->secs from-date-tp)))
                              (end-frac-int (interval-fraction-func (+ 1 (gnc:timepair->secs to-date-tp))))
                              (diff-int (- end-frac-int start-frac-int))
-                             (diff-int-numeric (gnc:make-gnc-numeric
+                             (diff-int-numeric (/
                                                 (inexact->exact diff-int) 1))
                             )
                      ;; Extra sanity check to ensure a number smaller than 1
                      (if (> diff-avg diff-int)
                          (gnc-numeric-div diff-int-numeric diff-avg-numeric GNC-DENOM-AUTO GNC-RND-ROUND)
-                         (gnc:make-gnc-numeric 1 1)))
-                     (gnc:make-gnc-numeric 1 1)))
+                         1/1))
+                     1/1))
                ;; If there is averaging, the report-title is extended
                ;; accordingly.
                (report-title
diff --git a/gnucash/report/standard-reports/net-barchart.scm b/gnucash/report/standard-reports/net-barchart.scm
index 83f4890..ef1532b 100644
--- a/gnucash/report/standard-reports/net-barchart.scm
+++ b/gnucash/report/standard-reports/net-barchart.scm
@@ -314,11 +314,11 @@
 	      (liabilities (assoc-ref rpt 'liability)))
 	 (set! assets-list (if assets (car assets)
 			       (map (lambda (d)
-				      (gnc:make-gnc-monetary report-currency (gnc:make-gnc-numeric 0 1)))
+				      (gnc:make-gnc-monetary report-currency 0/1))
 				    dates-list)))
 	 (set! liability-list (if liabilities (car liabilities)
 				  (map (lambda (d)
-				      (gnc:make-gnc-monetary report-currency (gnc:make-gnc-numeric 0 1)))
+				      (gnc:make-gnc-monetary report-currency 0/1))
 				    dates-list)))
 	 )
 
diff --git a/gnucash/report/standard-reports/sx-summary.scm b/gnucash/report/standard-reports/sx-summary.scm
index f61608b..9d02186 100644
--- a/gnucash/report/standard-reports/sx-summary.scm
+++ b/gnucash/report/standard-reports/sx-summary.scm
@@ -355,7 +355,7 @@
                            (guid (gncAccountGetGUID account))
                            (num-bal (hash-ref sx-value-hash guid)))
                       (if num-bal
-                          (if (eq? 0 (gnc:gnc-numeric-denom num-bal))
+                          (if (eq? 0 (denominator num-bal))
                               (gnc:warn "Oops, invalid gnc_numeric when looking up SX balance for account GUID " guid ": " num-bal)
                               (begin
                                 (balance-collector
diff --git a/gnucash/report/standard-reports/test/test-cash-flow.scm b/gnucash/report/standard-reports/test/test-cash-flow.scm
index 47433b0..45db741 100644
--- a/gnucash/report/standard-reports/test/test-cash-flow.scm
+++ b/gnucash/report/standard-reports/test/test-cash-flow.scm
@@ -36,7 +36,7 @@
 	 (exchange-fn (lambda (currency amount date) amount))
 	 (report-currency (gnc-default-report-currency))
 	 )
-    (env-create-transaction env to-date-tp bank-account expense-account (gnc:make-gnc-numeric 100 1))
+    (env-create-transaction env to-date-tp bank-account expense-account 100/1)
     (let ((result (cash-flow-calc-money-in-out (list (cons 'accounts (list bank-account))
 						     (cons 'to-date-tp to-date-tp)
 						     (cons 'from-date-tp from-date-tp)
@@ -48,16 +48,24 @@
 	     (money-in-alist (cdr (assq 'money-in-alist result)))
 	     (money-out-alist (cdr (assq 'money-out-alist result)))
 	     (expense-acc-in-collector (cadr (assoc expense-account money-in-alist))))
-	(and (null? money-out-alist)
-	     (equal? (gnc:make-gnc-numeric 10000 100)
+	(and (or (null? money-out-alist)
+                 (begin (format #t "The money-out-alist is not null.~%") #f))
+	     (or (equal? 10000/100
 		     (gnc:gnc-monetary-amount (gnc:sum-collector-commodity expense-acc-in-collector
 									   report-currency exchange-fn)))
-	     (equal? (gnc:make-gnc-numeric 10000 100)
+                 (begin (format #t "Failed expense-acc-in-collector ~g expected 100.00~%" (gnc:gnc-monetary-amount (gnc:sum-collector-commodity expense-acc-in-collector
+									   report-currency exchange-fn))) #f))
+	     (or (equal? 10000/100
 		     (gnc:gnc-monetary-amount (gnc:sum-collector-commodity money-in-collector
 									   report-currency exchange-fn)))
-	     (equal? (gnc:make-gnc-numeric 0 1)
+                 (begin (format #t "Failed money-in-collector ~g expected 100.00~%" (gnc:gnc-monetary-amount (gnc:sum-collector-commodity money-in-collector
+									   report-currency exchange-fn))) #f))
+	     (or (equal? 0/1
 		     (gnc:gnc-monetary-amount (gnc:sum-collector-commodity money-out-collector
 									   report-currency exchange-fn)))
+                 (begin (format #t "Failed sum-collector-commodity ~g expected 100.00~%" (gnc:gnc-monetary-amount (gnc:sum-collector-commodity money-out-collector
+                                                                                                                                               report-currency exchange-fn))) #f))
+             (begin (format #t "test-one-tx-in-cash-flow success~%") #t)
 	     )))))
 
 (define (test-one-tx-skip-cash-flow)
@@ -72,7 +80,7 @@
 	 (exchange-fn (lambda (currency amount date) amount))
 	 (report-currency (gnc-default-report-currency))
 	 )
-    (env-create-transaction env to-date-tp bank-account wallet-account (gnc:make-gnc-numeric 100 1))
+    (env-create-transaction env to-date-tp bank-account wallet-account 100/1)
     (let ((result (cash-flow-calc-money-in-out (list (cons 'accounts (list wallet-account bank-account))
 						     (cons 'to-date-tp to-date-tp)
 						     (cons 'from-date-tp from-date-tp)
@@ -85,12 +93,14 @@
 	     (money-out-alist (cdr (assq 'money-out-alist result))))
 	(and (null? money-in-alist)
 	     (null? money-out-alist)
-	     (equal? (gnc:make-gnc-numeric 0 1)
+	     (equal? 0/1
 		     (gnc:gnc-monetary-amount (gnc:sum-collector-commodity money-in-collector
 									   report-currency exchange-fn)))
-	     (equal? (gnc:make-gnc-numeric 0 1)
+	     (equal? 0/1
 		     (gnc:gnc-monetary-amount (gnc:sum-collector-commodity money-out-collector
-									   report-currency exchange-fn))))))))
+									   report-currency exchange-fn)))
+             (begin (format #t "test-one-tx-skip-cash-flow success~%") #t)
+             )))))
 
 (define (test-both-way-cash-flow)
   (let* ((env (create-test-env))
@@ -104,8 +114,8 @@
 	 (exchange-fn (lambda (currency amount date) amount))
 	 (report-currency (gnc-default-report-currency))
 	 )
-    (env-create-transaction env to-date-tp bank-account expense-account (gnc:make-gnc-numeric 100 1))
-    (env-create-transaction env to-date-tp expense-account bank-account (gnc:make-gnc-numeric 50 1))
+    (env-create-transaction env to-date-tp bank-account expense-account 100/1)
+    (env-create-transaction env to-date-tp expense-account bank-account 50/1)
     (let ((result (cash-flow-calc-money-in-out (list (cons 'accounts (list wallet-account bank-account))
 						     (cons 'to-date-tp to-date-tp)
 						     (cons 'from-date-tp from-date-tp)
@@ -124,11 +134,13 @@
 	     (expenses-out-total (gnc:gnc-monetary-amount (gnc:sum-collector-commodity expense-acc-out-collector
 										       report-currency
 										       exchange-fn))))
-	(and (equal? (gnc:make-gnc-numeric 10000 100) expenses-in-total)
-	     (equal? (gnc:make-gnc-numeric 5000 100) expenses-out-total)
-	     (equal? (gnc:make-gnc-numeric 10000 100)
+	(and (equal? 10000/100 expenses-in-total)
+	     (equal? 5000/100 expenses-out-total)
+	     (equal? 10000/100
 		     (gnc:gnc-monetary-amount (gnc:sum-collector-commodity money-in-collector
 									   report-currency exchange-fn)))
-	     (equal? (gnc:make-gnc-numeric 5000 100)
+	     (equal? 5000/100
 		     (gnc:gnc-monetary-amount (gnc:sum-collector-commodity money-out-collector
-									   report-currency exchange-fn))))))))
+									   report-currency exchange-fn)))
+             (begin (format #t "test-both-way-cash-flow success~%") #t)
+             )))))
diff --git a/gnucash/report/standard-reports/test/test-cashflow-barchart.scm b/gnucash/report/standard-reports/test/test-cashflow-barchart.scm
index 5dc958f..2b85f09 100644
--- a/gnucash/report/standard-reports/test/test-cashflow-barchart.scm
+++ b/gnucash/report/standard-reports/test/test-cashflow-barchart.scm
@@ -79,12 +79,12 @@
                               date-1
                               bank-account
                               income-account
-                              (gnc:make-gnc-numeric 1 1))
+                              1/1)
       (env-create-transaction env
                               date-2
                               wallet-account
                               income-account
-                              (gnc:make-gnc-numeric 5 1))
+                              5/1)
       (begin
         (set-option report gnc:pagename-display "Show Table" #t)
         (set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
@@ -111,25 +111,27 @@
                                            (list (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
                                                  (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
                                                  (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
-					 result))))
+                                           result))))
+            (format #t "Report Result ~a~%" result)
             (and (every (lambda (row)                 ; test in=net & out=0 in all rows (all days)
                           (and (or (equal? (second row) (fourth row))
                                    (begin (format #t "Failed, ~a and ~a differ~%" (second row) (fourth row)) #f))
                                (or (= 0 (string->number (car (third row))))
                                    (begin (format #t "Failed ~d isn't 0~%" (car (third row))) #f))))
                         tbl)
-                 (or (= 0 (tbl-ref->number tbl 0 1)) (begin (format #t "Failed refnum ~d isn't 0~%" (tbl-ref->number tbl 0 1) )) #f))      ; 1st day in =0
-                 (or (= 1 (tbl-ref->number tbl 1 1)) (begin (format #t "Failed refnum ~d isn't 1~%" (tbl-ref->number tbl 1 1)) #f))      ; 2nd day in =1
-                 (or (= 5 (tbl-ref->number tbl 2 1)) (begin (format #t "Failed refnum ~d isn't 5~%" (tbl-ref->number tbl 2 1)) #f))     ; 3rd day in =5
-                 (or (= (tbl-ref->number total 0 0) (tbl-ref->number total 0 2)) (begin (format #t "Failed refnums ~d and ~d differ ~%" (tbl-ref->number total 0 0) (tbl-ref->number total 0 2)) #f)); total in=total net
-                 (or (= 0 (tbl-ref->number total 0 1)) (begin (format #t "Failed refnum ~d isn't 0~%" (tbl-ref->number total 0 1)) #f))   ; total out=0
-                 (or (= 3 (tbl-row-count tbl)) (begin (format #t "Failed row count ~d isn't 3~%" (tbl-row-count tbl)) #f))
-                 (or (= 4 (tbl-column-count tbl)) (begin (format #t "Failed column count ~d isn't 4~%" (tbl-column-count tbl)) #f))))
+                 (or (= 0 (tbl-ref->number tbl 0 1))
+                     (begin (format #t "Failed refnum ~g isn't 0~%" (tbl-ref->number tbl 0 1)) #f))      ; 1st day in =0
+                 (or (= 1 (tbl-ref->number tbl 1 1)) (begin (format #t "Failed refnum ~g isn't 1~%" (tbl-ref->number tbl 1 1)) #f))      ; 2nd day in =1
+                 (or (= 5 (tbl-ref->number tbl 2 1)) (begin (format #t "Failed refnum ~g isn't 5~%" (tbl-ref->number tbl 2 1)) #f))     ; 3rd day in =5
+                 (or (= (tbl-ref->number total 0 0) (tbl-ref->number total 0 2)) (begin (format #t "Failed refnums ~g and ~g differ ~%" (tbl-ref->number total 0 0) (tbl-ref->number total 0 2)) #f)); total in=total net
+                 (or (= 0 (tbl-ref->number total 0 1)) (begin (format #t "Failed refnum ~g isn't 0~%" (tbl-ref->number total 0 1)) #f))   ; total out=0
+                 (or (= 3 (tbl-row-count tbl)) (begin (format #t "Failed row count ~g isn't 3~%" (tbl-row-count tbl)) #f))
+                 (or (= 4 (tbl-column-count tbl)) (begin (format #t "Failed column count ~g isn't 4~%" (tbl-column-count tbl)) #f))))
         )
       )
     )
   )
-
+)
 
 ;; Test two transactions from two different assets to expense in two different days
 (define (test-out-txn)
@@ -151,22 +153,22 @@
                               date-1
                               bank-account
                               income-account
-                              (gnc:make-gnc-numeric 100 1))   ; large in txn to avoid negative net (hard to parse)
+                              100/1)   ; large in txn to avoid negative net (hard to parse)
       (env-create-transaction env
                               date-1
                               expense-account
                               bank-account
-                              (gnc:make-gnc-numeric 1 1))
+                              1/1)
       (env-create-transaction env
                               date-2
                               wallet-account
                               income-account
-                              (gnc:make-gnc-numeric 100 1))   ; large in txn to avoid negative net (hard to parse)
+                              100/1)   ; large in txn to avoid negative net (hard to parse)
       (env-create-transaction env
                               date-2
                               expense-account
                               wallet-account
-                              (gnc:make-gnc-numeric 5 1))
+                              5/1)
       (begin
         (set-option report gnc:pagename-display "Show Table" #t)
         (set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
@@ -234,17 +236,17 @@
                               date-1
                               bank-account
                               income-account
-                              (gnc:make-gnc-numeric 1 1))
+                              1/1)
       (env-create-transaction env
                               date-1
                               bank-account
                               wallet-account
-                              (gnc:make-gnc-numeric 20 1))  ; this transaction should not be counted
+                              20/1)  ; this transaction should not be counted
       (env-create-transaction env
                               date-2
                               wallet-account
                               income-account
-                              (gnc:make-gnc-numeric 5 1))
+                              5/1)
 
       (begin
         (set-option report gnc:pagename-display "Show Table" #t)
diff --git a/gnucash/report/standard-reports/test/test-generic-net-barchart.scm b/gnucash/report/standard-reports/test/test-generic-net-barchart.scm
index ac475e6..403eabe 100644
--- a/gnucash/report/standard-reports/test/test-generic-net-barchart.scm
+++ b/gnucash/report/standard-reports/test/test-generic-net-barchart.scm
@@ -82,7 +82,7 @@
 			       (gnc:get-start-this-month)
 			       my-income-account
 			       my-asset-account
-			       (gnc:make-gnc-numeric -1 1))
+			       -1/1)
       (begin
 	(set-option report gnc:pagename-display "Show table" #t)
 	(set-option report gnc:pagename-general "Start Date"
@@ -106,11 +106,13 @@
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
 					 result))))
-	    (and (= 1 (tbl-ref->number tbl 0 1))
+	    (or (and (= 1 (tbl-ref->number tbl 0 1))
 			 (= 0 (tbl-ref->number tbl 0 2))
 			 (= 1 (tbl-ref->number tbl 0 3))
 			 (= 1 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))))))))
+			 (= 4 (tbl-column-count tbl)))
+                (begin (format #t "Single-txn test ~a failed~%" uuid) #f))
+                ))))))
 
 
 (define (two-txn-test uuid)
@@ -133,12 +135,12 @@
 			       date-1
 			       my-income-account
 			       my-asset-account
-			       (gnc:make-gnc-numeric -1 1))
+			       -1/1)
       (env-create-transaction env
 			       date-2
 			       my-income-account
 			       my-asset-account
-			       (gnc:make-gnc-numeric -5 1))
+			       -5/1)
       (begin
 	(set-option report gnc:pagename-display "Show table" #t)
 	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
@@ -160,15 +162,24 @@
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
 					 result))))
-	    (and (every (lambda (row)
-				  (and (equal? (second row) (fourth row))
-				       (= 0 (string->number (car (third row))))))
+	    (or (and (every (lambda (row)
+                              (and (or (equal? (second row) (fourth row))
+                                       (begin (format "Second Element ~g != fourth element ~g~%" (second row) (fourth row)) #f))
+                                   (or (= 0 (string->number (car (third row))))
+                                       (begin (format "third row element ~a not 0~%" (car (third row))) #f))))
 				tbl)
-			 (= 0 (tbl-ref->number tbl 0 1))
-			 (= 1 (tbl-ref->number tbl 1 1))
-			 (= 6 (tbl-ref->number tbl 2 1))
-			 (= 3 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))))))))
+                     (or (= 0 (tbl-ref->number tbl 0 1))
+                         (begin (format #t "Item 1 failed: ~g not 0~%" (tbl-ref->number tbl 0 1)) #f))
+                     (or (= 1 (tbl-ref->number tbl 1 1))
+                         (begin (format #t "Item 1 failed: ~g not 1~%" (tbl-ref->number tbl 1 1)) #f))
+                     (or (= 6 (tbl-ref->number tbl 2 1))
+                         (begin (format #t "Item 2 failed: ~g not 6~%" (tbl-ref->number tbl 2 1)) #f))
+                     (or (= 3 (tbl-row-count tbl))
+                         (begin (format #t "Item 3 failed: ~g not 3~%" (tbl-row-count tbl)) #f))
+                     (or (= 4 (tbl-column-count tbl))
+                         (begin (format #t "Item 4 failed: ~g not 4~%" (tbl-column-count tbl)) #f)))
+                (begin (format #t "Two-txn test ~a failed~%" uuid) #f))
+                ))))))
 
 
 (define (two-txn-test-2 uuid)
@@ -189,10 +200,10 @@
 	   (date-0 (gnc:get-start-this-month))
 	   (date-1 (gnc:timepair-next-day date-0))
 	   (date-2 (gnc:timepair-next-day date-1)))
-      (env-create-transaction env date-1 my-income-account my-asset-account (gnc:make-gnc-numeric -1 1))
-      (env-create-transaction env date-1 my-expense-account my-liability-account (gnc:make-gnc-numeric -1 1))
-      (env-create-transaction env date-2 my-income-account my-asset-account (gnc:make-gnc-numeric -5 1))
-      (env-create-transaction env date-2 my-expense-account my-liability-account (gnc:make-gnc-numeric -5 1))
+      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
+      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
+      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
+      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
       (begin
 	(set-option report gnc:pagename-display "Show table" #t)
 	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
@@ -214,7 +225,7 @@
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
 					 result))))
-	    (and (every (lambda (row)
+	    (or (and (every (lambda (row)
 				  (and (= (string->number (car (fourth row)))
 					  (+ (string->number (car (second row)))
 					     (string->number (car (third row)))))
@@ -225,7 +236,9 @@
 			 (= 1 (tbl-ref->number tbl 1 1))
 			 (= 6 (tbl-ref->number tbl 2 1))
 			 (= 3 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))))))))
+			 (= 4 (tbl-column-count tbl)))
+                (begin (format #t "two-txn test 2 ~a failed~%" uuid) #f))
+                ))))))
 
 (define (two-txn-test-income uuid)
   (let* ((template (gnc:find-report-template uuid))
@@ -245,10 +258,10 @@
 	   (date-0 (gnc:get-start-this-month))
 	   (date-1 (gnc:timepair-next-day date-0))
 	   (date-2 (gnc:timepair-next-day date-1)))
-      (env-create-transaction env date-1 my-income-account my-asset-account (gnc:make-gnc-numeric -1 1))
-      (env-create-transaction env date-1 my-expense-account my-liability-account (gnc:make-gnc-numeric -1 1))
-      (env-create-transaction env date-2 my-income-account my-asset-account (gnc:make-gnc-numeric -5 1))
-      (env-create-transaction env date-2 my-expense-account my-liability-account (gnc:make-gnc-numeric -5 1))
+      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
+      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
+      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
+      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
       (begin
 	(set-option report gnc:pagename-display "Show table" #t)
 	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
@@ -270,7 +283,7 @@
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
 					 result))))
-	    (and (every (lambda (row)
+	    (or (and (every (lambda (row)
 				  (and (= (string->number (car (fourth row)))
 					  (+ (string->number (car (second row)))
 					     (string->number (car (third row)))))
@@ -281,7 +294,9 @@
 			 (= 1 (tbl-ref->number tbl 1 1))
 			 (= 5 (tbl-ref->number tbl 2 1))
 			 (= 3 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))))))))
+			 (= 4 (tbl-column-count tbl)))
+                (begin (format #t "two-txn-income test ~a failed~%" uuid) #f))
+                ))))))
 
 
 (define (closing-test uuid)
@@ -306,12 +321,12 @@
 	   (date-2 (gnc:timepair-next-day date-1))
 	   (date-3 (gnc:timepair-next-day date-2)))
 
-      (env-create-transaction env date-1 my-income-account my-asset-account (gnc:make-gnc-numeric -1 1))
-      (env-create-transaction env date-2 my-income-account my-asset-account (gnc:make-gnc-numeric -2 1))
-      (env-create-transaction env date-3 my-income-account my-asset-account (gnc:make-gnc-numeric -3 1))
+      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
+      (env-create-transaction env date-2 my-income-account my-asset-account -2/1)
+      (env-create-transaction env date-3 my-income-account my-asset-account -3/1)
 
       (let ((closing-txn (env-create-transaction env date-2 my-asset-account my-equity-account
-						 (gnc:make-gnc-numeric 300 1))))
+						 300/1)))
 	(xaccTransSetIsClosingTxn closing-txn #t))
 
       (begin
@@ -335,7 +350,7 @@
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1)
 					       (list "<td class=\"number-cell\">[^0-9]*([^<]*)</td>" 1))
 					 result))))
-	    (and (every (lambda (row)
+	    (or (and (every (lambda (row)
 				  (and (= (string->number (car (fourth row)))
 					  (+ (string->number (car (second row)))
 					     (string->number (car (third row)))))))
@@ -345,5 +360,7 @@
 			 (= 2 (tbl-ref->number tbl 2 1))
 			 (= 3 (tbl-ref->number tbl 3 1))
 			 (= 4 (tbl-row-count tbl))
-			 (= 4 (tbl-column-count tbl)))))))))
+			 (= 4 (tbl-column-count tbl)))
+                (begin (format #t "Closing-txn test ~a failed~%" uuid) #f))
+                ))))))
 
diff --git a/gnucash/report/standard-reports/test/test-generic-net-linechart.scm b/gnucash/report/standard-reports/test/test-generic-net-linechart.scm
index 3b26dfe..89825b7 100644
--- a/gnucash/report/standard-reports/test/test-generic-net-linechart.scm
+++ b/gnucash/report/standard-reports/test/test-generic-net-linechart.scm
@@ -80,7 +80,7 @@
 			       (gnc:get-start-this-month)
 			       my-income-account
 			       my-asset-account
-			       (gnc:make-gnc-numeric -1 1))
+			       -1/1)
       (begin
 	(set-option report gnc:pagename-display "Show table" #t)
 	(set-option report gnc:pagename-general "Start Date"
@@ -131,12 +131,12 @@
 			       date-1
 			       my-income-account
 			       my-asset-account
-			       (gnc:make-gnc-numeric -1 1))
+			       -1/1)
       (env-create-transaction env
 			       date-2
 			       my-income-account
 			       my-asset-account
-			       (gnc:make-gnc-numeric -5 1))
+			       -5/1)
       (begin
 	(set-option report gnc:pagename-display "Show table" #t)
 	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
@@ -187,10 +187,10 @@
 	   (date-0 (gnc:get-start-this-month))
 	   (date-1 (gnc:timepair-next-day date-0))
 	   (date-2 (gnc:timepair-next-day date-1)))
-      (env-create-transaction env date-1 my-income-account my-asset-account (gnc:make-gnc-numeric -1 1))
-      (env-create-transaction env date-1 my-expense-account my-liability-account (gnc:make-gnc-numeric -1 1))
-      (env-create-transaction env date-2 my-income-account my-asset-account (gnc:make-gnc-numeric -5 1))
-      (env-create-transaction env date-2 my-expense-account my-liability-account (gnc:make-gnc-numeric -5 1))
+      (env-create-transaction env date-1 my-income-account my-asset-account -1/1)
+      (env-create-transaction env date-1 my-expense-account my-liability-account -1/1)
+      (env-create-transaction env date-2 my-income-account my-asset-account -5/1)
+      (env-create-transaction env date-2 my-expense-account my-liability-account -5/1)
       (begin
 	(set-option report gnc:pagename-display "Show table" #t)
 	(set-option report gnc:pagename-general "Start Date" (cons 'absolute date-0))
diff --git a/gnucash/report/utility-reports/hello-world.scm b/gnucash/report/utility-reports/hello-world.scm
index 5337aa9..8ee7b30 100644
--- a/gnucash/report/utility-reports/hello-world.scm
+++ b/gnucash/report/utility-reports/hello-world.scm
@@ -409,7 +409,7 @@ new, totally cool report, consult the mailing list %s.")
           (_ "The number option formatted as currency is %s.")
           (gnc:html-markup-b
            (xaccPrintAmount
-            (gnc:make-gnc-numeric (inexact->exact num-val) 1)
+            (inexact->exact num-val)
             (gnc-default-print-info #f)))))))
 
       ;; you can add as many objects as you want.  Here's another 
diff --git a/libgnucash/app-utils/gnc-euro.c b/libgnucash/app-utils/gnc-euro.c
index 8853052..7793285 100644
--- a/libgnucash/app-utils/gnc-euro.c
+++ b/libgnucash/app-utils/gnc-euro.c
@@ -218,7 +218,7 @@ gnc_euro_currency_get_rate (const gnc_commodity *currency)
         return gnc_numeric_zero ();
 
     return double_to_gnc_numeric (result->rate, GNC_DENOM_AUTO,
-                                  GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND_HALF_UP);
+                                  GNC_HOW_RND_ROUND_HALF_UP);
 }
 
 /* ------------------------------------------------------ */
diff --git a/libgnucash/app-utils/guile-util.c b/libgnucash/app-utils/guile-util.c
index 5e1ef95..68325d0 100644
--- a/libgnucash/app-utils/guile-util.c
+++ b/libgnucash/app-utils/guile-util.c
@@ -520,7 +520,7 @@ gnc_split_scm_get_amount(SCM split_scm)
         return gnc_numeric_zero ();
 
     result = scm_call_1(getters.split_scm_amount, split_scm);
-    if (!gnc_numeric_p(result))
+    if (!scm_rational_p(result))
         return gnc_numeric_zero ();
 
     return gnc_scm_to_numeric(result);
@@ -545,7 +545,7 @@ gnc_split_scm_get_value(SCM split_scm)
         return gnc_numeric_zero ();
 
     result = scm_call_1(getters.split_scm_value, split_scm);
-    if (!gnc_numeric_p(result))
+    if (!scm_rational_p(result))
         return gnc_numeric_zero ();
 
     return gnc_scm_to_numeric(result);
diff --git a/libgnucash/engine/engine-helpers-guile.h b/libgnucash/engine/engine-helpers-guile.h
index e22a125..218d4f6 100644
--- a/libgnucash/engine/engine-helpers-guile.h
+++ b/libgnucash/engine/engine-helpers-guile.h
@@ -50,11 +50,8 @@ GSList * gnc_query_scm2path (SCM path_scm);
 SCM gnc_query2scm (QofQuery * q);
 QofQuery * gnc_scm2query (SCM query_scm);
 
-int gnc_gh_gint64_p(SCM num);
-
 SCM gnc_numeric_to_scm(gnc_numeric arg);
 gnc_numeric gnc_scm_to_numeric(SCM arg);
-int gnc_numeric_p(SCM arg);
 gnc_commodity * gnc_scm_to_commodity(SCM scm);
 SCM gnc_commodity_to_scm (const gnc_commodity *commodity);
 SCM gnc_book_to_scm (const QofBook *book);
diff --git a/libgnucash/engine/engine-helpers.c b/libgnucash/engine/engine-helpers.c
index d10de0a..f25b160 100644
--- a/libgnucash/engine/engine-helpers.c
+++ b/libgnucash/engine/engine-helpers.c
@@ -319,8 +319,8 @@ int
 gnc_timepair_p(SCM x)
 {
     return(scm_is_pair(x) &&
-           gnc_gh_gint64_p(SCM_CAR(x)) &&
-           gnc_gh_gint64_p(SCM_CDR(x)));
+           (scm_is_signed_integer(SCM_CAR(x), INT64_MIN, INT64_MAX) &&
+            scm_is_signed_integer(SCM_CDR(x), INT64_MIN, INT64_MAX)));
 }
 
 SCM
@@ -1110,10 +1110,8 @@ gnc_scm2query_term_query_v1 (SCM query_term_scm)
                 break;
             scm = SCM_CAR (query_term_scm);
             query_term_scm = SCM_CDR (query_term_scm);
-            amount = scm_to_double (scm);
-
-            val = double_to_gnc_numeric (amount, GNC_DENOM_AUTO,
-                                         GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND_HALF_UP);
+            val = gnc_numeric_create (scm_to_int64(scm_numerator(scm)),
+                                      scm_to_int64(scm_denominator(scm)));
 
             if (!g_strcmp0 (pr_type, "pr-price"))
             {
@@ -1997,96 +1995,23 @@ gnc_scm2query (SCM query_scm)
     return q;
 }
 
-int
-gnc_gh_gint64_p(SCM num)
-{
-    static int initialized = 0;
-    static SCM maxval;
-    static SCM minval;
-
-    if (!initialized)
-    {
-        /* to be super safe, we have to build these manually because
-           though we know that we have gint64's here, we *don't* know how
-           to portably specify a 64bit constant to the compiler (i.e. like
-           0x7FFFFFFFFFFFFFFF). */
-        gint64 tmp;
-
-        tmp = 0x7FFFFFFF;
-        tmp <<= 32;
-        tmp |= 0xFFFFFFFF;
-        maxval = scm_from_int64(tmp);
-
-        tmp = 0x80000000;
-        tmp <<= 32;
-        minval = scm_from_int64(tmp);
-
-        scm_gc_protect_object(maxval);
-        scm_gc_protect_object(minval);
-        initialized = 1;
-    }
-
-    return (scm_is_exact(num) &&
-            (scm_geq_p(num, minval) != SCM_BOOL_F) &&
-            (scm_leq_p(num, maxval) != SCM_BOOL_F));
-}
-
 gnc_numeric
 gnc_scm_to_numeric(SCM gncnum)
 {
-    static SCM get_num   = SCM_BOOL_F;
-    static SCM get_denom = SCM_BOOL_F;
-
-    if (get_num == SCM_BOOL_F)
-    {
-        get_num = scm_c_eval_string("gnc:gnc-numeric-num");
-    }
-    if (get_denom == SCM_BOOL_F)
-    {
-        get_denom = scm_c_eval_string("gnc:gnc-numeric-denom");
-    }
-
-    return gnc_numeric_create(scm_to_int64(scm_call_1(get_num, gncnum)),
-                              scm_to_int64(scm_call_1(get_denom, gncnum)));
+    if (scm_is_signed_integer(scm_numerator(gncnum), INT64_MIN, INT64_MAX) &&
+        scm_is_signed_integer(scm_denominator(gncnum), INT64_MIN, INT64_MAX))
+        return gnc_numeric_create(scm_to_int64(scm_numerator(gncnum)),
+                                  scm_to_int64(scm_denominator(gncnum)));
+    return gnc_numeric_create(0, GNC_ERROR_OVERFLOW);
 }
 
 SCM
 gnc_numeric_to_scm(gnc_numeric arg)
 {
-    static SCM maker = SCM_BOOL_F;
-
-    if (maker == SCM_BOOL_F)
-    {
-        maker = scm_c_eval_string("gnc:make-gnc-numeric");
-    }
-
-    return scm_call_2(maker, scm_from_int64(gnc_numeric_num(arg)),
-                      scm_from_int64(gnc_numeric_denom(arg)));
-}
-
-int
-gnc_numeric_p(SCM arg)
-{
-    static SCM type_p = SCM_BOOL_F;
-    SCM        ret    = SCM_BOOL_F;
-
-    if (type_p == SCM_BOOL_F)
-    {
-        type_p = scm_c_eval_string("gnc:gnc-numeric?");
-    }
-    ret = scm_call_1(type_p, arg);
-
-    if (ret == SCM_BOOL_F)
-    {
-        return FALSE;
-    }
-    else
-    {
-        return TRUE;
-    }
+    return scm_divide(scm_from_int64(arg.num),
+                           scm_from_int64(arg.denom));
 }
 
-
 static SCM
 gnc_generic_to_scm(const void *cx, const gchar *type_str)
 {
diff --git a/libgnucash/engine/engine.scm b/libgnucash/engine/engine.scm
index fdb0b40..b3023a3 100644
--- a/libgnucash/engine/engine.scm
+++ b/libgnucash/engine/engine.scm
@@ -43,12 +43,6 @@
 (export GNC-ERROR-OVERFLOW)
 (export GNC-ERROR-DENOM-DIFF)
 (export GNC-ERROR-REMAINDER)
-(export <gnc-numeric>)
-(export gnc:gnc-numeric?)
-(export gnc:make-gnc-numeric)
-(export gnc:gnc-numeric-denom)
-(export gnc:gnc-numeric-num)
-(export gnc:gnc-numeric-denom-reciprocal)
 (export <gnc-monetary>)
 (export gnc:gnc-monetary?)
 (export gnc:make-gnc-monetary)
diff --git a/libgnucash/engine/gnc-numeric.scm b/libgnucash/engine/gnc-numeric.scm
index c264dc1..f7c846a 100644
--- a/libgnucash/engine/gnc-numeric.scm
+++ b/libgnucash/engine/gnc-numeric.scm
@@ -47,26 +47,6 @@
 (define GNC-ERROR-DENOM-DIFF   -3)
 (define GNC-ERROR-REMAINDER    -4)
 
-(define <gnc-numeric>
-  (make-record-type "<gnc-numeric>" 
-                    '(num denom)))
-
-(define gnc:make-gnc-numeric 
-  (record-constructor <gnc-numeric>))
-
-(define gnc:gnc-numeric? 
-  (record-predicate <gnc-numeric>))
-
-(define gnc:gnc-numeric-num
-  (record-accessor <gnc-numeric> 'num))
-
-(define gnc:gnc-numeric-denom
-  (record-accessor <gnc-numeric> 'denom))
-
-(define (gnc:gnc-numeric-denom-reciprocal arg)
-  (- arg))
-
-
 
 (define <gnc-monetary> 
   (make-record-type "<gnc-monetary>" 
@@ -76,7 +56,7 @@
 (define (gnc:make-gnc-monetary c a)
   ;;FIXME: we used to type-check the values, like:
   ;; (gw:wcp-is-of-type? <gnc:commodity*> c)
-  (if (and #t (gnc:gnc-numeric? a))
+  (if (and #t (number? a))
       ((record-constructor <gnc-monetary>) c a)
       (warn "wrong arguments for gnc:make-gnc-monetary: " c a)))
 
diff --git a/libgnucash/engine/kvp-scm.cpp b/libgnucash/engine/kvp-scm.cpp
index 2648526..2e95e65 100644
--- a/libgnucash/engine/kvp-scm.cpp
+++ b/libgnucash/engine/kvp-scm.cpp
@@ -25,22 +25,26 @@ extern "C"
 KvpValue *
 gnc_scm_to_kvp_value_ptr(SCM val)
 {
-    if (scm_is_number(val))
+    if (scm_is_rational(val))
     {
-        /* in guile 1.8 (exact? ) only works on numbers */
-        if (scm_is_exact (val) && gnc_gh_gint64_p(val))
+        if (scm_is_exact_integer(val) &&
+            scm_is_signed_integer(val, INT64_MIN, INT64_MAX))
         {
             return new KvpValue{scm_to_int64(val)};
         }
+        else if (scm_is_exact(val) &&
+                 scm_is_signed_integer(scm_numerator(val),
+                                       INT64_MIN, INT64_MAX) &&
+                 scm_is_signed_integer(scm_denominator(val),
+                                       INT64_MIN, INT64_MAX))
+        {
+            return new KvpValue{gnc_scm_to_numeric(val)};
+        }
         else
         {
             return new KvpValue{scm_to_double(val)};
         }
     }
-    else if (gnc_numeric_p(val))
-    {
-        return new KvpValue{gnc_scm_to_numeric(val)};
-    }
     else if (gnc_guid_p(val))
     {
         auto guid = gnc_scm2guid(val);
diff --git a/libgnucash/engine/test/test-extras.scm b/libgnucash/engine/test/test-extras.scm
index 06e0ebd..faf6508 100644
--- a/libgnucash/engine/test/test-extras.scm
+++ b/libgnucash/engine/test/test-extras.scm
@@ -115,7 +115,7 @@
 	(cons 'sink (make-test-sink))))
 
 (define (env-random-amount env n)
-  (gnc:make-gnc-numeric (env-random env n) 1))
+  (/ (env-random env n) 1))
 
 (define (env-random env n)
   (random n (assoc-ref env 'random)))
@@ -183,9 +183,9 @@
       (for-each (lambda (date)
 		  (env-create-transaction env date to-account
 					  from-account
-					  (gnc:make-gnc-numeric
-					   (gnc:date-get-month-day (gnc:timepair->date date))
-					   1)))
+					  (/
+                                           (gnc:date-get-month-day (gnc:timepair->date date))
+                                           1)))
 		(cdr (reverse dates-this-month)))))
 
 (define (env-create-account-structure env account-structure)
diff --git a/libgnucash/engine/test/test-split.scm b/libgnucash/engine/test/test-split.scm
index 7c14389..7cd8183 100644
--- a/libgnucash/engine/test/test-split.scm
+++ b/libgnucash/engine/test/test-split.scm
@@ -17,8 +17,8 @@
 	 (bank-account (cdr (assoc "Bank" account-alist)))
 	 (expense-account (cdr (assoc "Expenses" account-alist)))
 	 (wallet-account (cdr (assoc "Wallet" account-alist)))
-	 (tx1 (env-create-transaction env today bank-account wallet-account (gnc:make-gnc-numeric 20 1)))
-	 (tx2 (env-create-transaction env today bank-account expense-account (gnc:make-gnc-numeric 10 1)))
+	 (tx1 (env-create-transaction env today bank-account wallet-account 20/1))
+	 (tx2 (env-create-transaction env today bank-account expense-account 10/1))
 	 (splits-tx1 (xaccTransGetSplitList tx1))
 	 (splits-tx2 (xaccTransGetSplitList tx2)))
     (and (split-in-list? (first splits-tx1) splits-tx1)

commit 7061803596ed61dd9829235927f44c0adc9c9ddc
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Dec 18 09:46:42 2017 -0800

    Remove SIGFIG rounding from price calculation.
    
    Prices shouldn't be rounded except for display.

diff --git a/libgnucash/engine/Split.c b/libgnucash/engine/Split.c
index 86072d1..c7e86d8 100644
--- a/libgnucash/engine/Split.c
+++ b/libgnucash/engine/Split.c
@@ -60,8 +60,6 @@
 const char *void_former_amt_str = "void-former-amount";
 const char *void_former_val_str = "void-former-value";
 
-#define PRICE_SIGFIGS 6
-
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ENGINE;
 
@@ -1956,7 +1954,6 @@ xaccSplitGetSharePrice (const Split * split)
     }
     price = gnc_numeric_div(val, amt,
                             GNC_DENOM_AUTO,
-                            GNC_HOW_DENOM_SIGFIGS(PRICE_SIGFIGS) |
                             GNC_HOW_RND_ROUND_HALF_UP);
 
     /* During random checks we can get some very weird prices.  Let's
diff --git a/libgnucash/engine/test/utest-Split.cpp b/libgnucash/engine/test/utest-Split.cpp
index 7609242..815c2ca 100644
--- a/libgnucash/engine/test/utest-Split.cpp
+++ b/libgnucash/engine/test/utest-Split.cpp
@@ -1670,7 +1670,6 @@ test_xaccSplitGetSharePrice (Fixture *fixture, gconstpointer pData)
     gnc_numeric expected = gnc_numeric_create (1, 1);
     Split *split = fixture->split;
     /* Warning: this is a define in Split.c */
-    const guint PRICE_SIGFIGS = 6;
     char *logdomain = "gnc.engine";
     GLogLevelFlags loglevel = static_cast<GLogLevelFlags>(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL);
     TestErrorStruct check = { loglevel, logdomain, NULL, 0 };
@@ -1685,7 +1684,6 @@ test_xaccSplitGetSharePrice (Fixture *fixture, gconstpointer pData)
 
     expected = gnc_numeric_div (split->value, split->amount,
                                 GNC_DENOM_AUTO,
-                                GNC_HOW_DENOM_SIGFIGS (PRICE_SIGFIGS) |
                                 GNC_HOW_RND_ROUND_HALF_UP);
 
     result = xaccSplitGetSharePrice (split);
@@ -1710,7 +1708,6 @@ test_xaccSplitGetSharePrice (Fixture *fixture, gconstpointer pData)
     split->value = gnc_numeric_create (3, 789304166);
     quotient = gnc_numeric_div (split->value, split->amount,
                                 GNC_DENOM_AUTO,
-                                GNC_HOW_DENOM_SIGFIGS (PRICE_SIGFIGS) |
                                 GNC_HOW_RND_ROUND_HALF_UP);
     check.msg = g_strdup_printf ("[xaccSplitGetSharePrice()] "
                                  "Computing share price failed (%d): [ %"
@@ -1730,7 +1727,6 @@ test_xaccSplitGetSharePrice (Fixture *fixture, gconstpointer pData)
     split->value = gnc_numeric_create (3, 0);
     quotient = gnc_numeric_div (split->value, split->amount,
                                 GNC_DENOM_AUTO,
-                                GNC_HOW_DENOM_SIGFIGS (PRICE_SIGFIGS) |
                                 GNC_HOW_RND_ROUND_HALF_UP);
     check.msg = g_strdup_printf ("[xaccSplitGetSharePrice()] "
                                  "Computing share price failed (%d): [ %"
@@ -1750,7 +1746,6 @@ test_xaccSplitGetSharePrice (Fixture *fixture, gconstpointer pData)
     split->value = gnc_numeric_create (3, 789304166);
     quotient = gnc_numeric_div (split->value, split->amount,
                                 GNC_DENOM_AUTO,
-                                GNC_HOW_DENOM_SIGFIGS (PRICE_SIGFIGS) |
                                 GNC_HOW_RND_ROUND_HALF_UP);
     check.msg = g_strdup_printf ("[xaccSplitGetSharePrice()] "
                                  "Computing share price failed (%d): [ %"

commit a80318ec5f05715b843bf0a038159076342ae0db
Author: lmat <dartme18 at gmail.com>
Date:   Thu Dec 21 07:41:07 2017 -0500

    Adding to version info to feature string

diff --git a/libgnucash/engine/gnc-features.c b/libgnucash/engine/gnc-features.c
index 0c58bad..4d38fa7 100644
--- a/libgnucash/engine/gnc-features.c
+++ b/libgnucash/engine/gnc-features.c
@@ -46,7 +46,7 @@ static gncFeature known_features[] =
     { GNC_FEATURE_KVP_EXTRA_DATA, "Extra data for addresses, jobs or invoice entries (requires at least GnuCash 2.6.4)" },
     { GNC_FEATURE_BOOK_CURRENCY, "User specifies a 'book-currency'; costs of other currencies/commodities tracked in terms of book-currency (requires at least GnuCash 2.7.0)" },
     { GNC_FEATURE_GUID_BAYESIAN, "Use account GUID as key for Bayesian data (requires at least GnuCash 2.6.12)" },
-    { GNC_FEATURE_GUID_FLAT_BAYESIAN, "Use account GUID as key for bayesian data and store KVP flat" },
+    { GNC_FEATURE_GUID_FLAT_BAYESIAN, "Use account GUID as key for bayesian data and store KVP flat (requires at least Gnucash 2.6.19)" },
     { NULL },
 };
 

commit a23438d5fbbc6cdb24d26f5a921b70fecb64750b
Author: lmat <dartme18 at gmail.com>
Date:   Tue Dec 12 09:34:44 2017 -0800

    Correct string cache code
    
    string cache replace was incorrect and covered by gpointer casting.

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index f04c43a..a4d4995 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -1108,7 +1108,7 @@ gnc_account_create_root (QofBook *book)
     rpriv = GET_PRIVATE(root);
     xaccAccountBeginEdit(root);
     rpriv->type = ACCT_TYPE_ROOT;
-    qof_string_cache_replace((void const **)(&rpriv->accountName), "Root Account");
+    rpriv->accountName = qof_string_cache_replace(rpriv->accountName, "Root Account");
     mark_account (root);
     xaccAccountCommitEdit(root);
     gnc_book_set_root_account(book, root);
@@ -2245,7 +2245,7 @@ xaccAccountSetName (Account *acc, const char *str)
         return;
 
     xaccAccountBeginEdit(acc);
-    qof_string_cache_replace((void const **)(&priv->accountName), str);
+    priv->accountName = qof_string_cache_replace(priv->accountName, str);
     mark_account (acc);
     xaccAccountCommitEdit(acc);
 }
@@ -2264,7 +2264,7 @@ xaccAccountSetCode (Account *acc, const char *str)
         return;
 
     xaccAccountBeginEdit(acc);
-    qof_string_cache_replace((void const **)(&priv->accountCode), str ? str : "");
+    priv->accountCode = qof_string_cache_replace(priv->accountCode, str ? str : "");
     mark_account (acc);
     xaccAccountCommitEdit(acc);
 }
@@ -2283,7 +2283,7 @@ xaccAccountSetDescription (Account *acc, const char *str)
         return;
 
     xaccAccountBeginEdit(acc);
-    qof_string_cache_replace((void const **)(&priv->description), str ? str : "");
+    priv->description = qof_string_cache_replace(priv->description, str ? str : "");
     mark_account (acc);
     xaccAccountCommitEdit(acc);
 }
diff --git a/libgnucash/engine/qof-string-cache.cpp b/libgnucash/engine/qof-string-cache.cpp
index 7390a63..caba239 100644
--- a/libgnucash/engine/qof-string-cache.cpp
+++ b/libgnucash/engine/qof-string-cache.cpp
@@ -82,7 +82,7 @@ qof_string_cache_destroy (void)
 /* If the key exists in the cache, check the refcount.  If 1, just
  * remove the key.  Otherwise, decrement the refcount */
 void
-qof_string_cache_remove(gconstpointer key)
+qof_string_cache_remove(const char * key)
 {
     if (key)
     {
@@ -106,8 +106,8 @@ qof_string_cache_remove(gconstpointer key)
 
 /* If the key exists in the cache, increment the refcount.  Otherwise,
  * add it with a refcount of 1. */
-gpointer
-qof_string_cache_insert(gconstpointer key)
+char *
+qof_string_cache_insert(const char * key)
 {
     if (key)
     {
@@ -118,7 +118,7 @@ qof_string_cache_insert(gconstpointer key)
         {
             guint* refcount = (guint*)value;
             ++(*refcount);
-            return cache_key;
+            return static_cast <char *> (cache_key);
         }
         else
         {
@@ -126,17 +126,17 @@ qof_string_cache_insert(gconstpointer key)
             guint* refcount = static_cast<unsigned int*>(g_malloc(sizeof(guint)));
             *refcount = 1;
             g_hash_table_insert(cache, new_key, refcount);
-            return new_key;
+            return static_cast <char *> (new_key);
         }
     }
     return NULL;
 }
 
-void
-qof_string_cache_replace(gconstpointer * dst, gconstpointer src)
+char *
+qof_string_cache_replace(char const * dst, char const * src)
 {
-    gpointer tmp {qof_string_cache_insert(src)};
-    qof_string_cache_remove(&dst);
-    *dst = tmp;
+    char * tmp {qof_string_cache_insert (src)};
+    qof_string_cache_remove (dst);
+    return tmp;
 }
 /* ************************ END OF FILE ***************************** */
diff --git a/libgnucash/engine/qof-string-cache.h b/libgnucash/engine/qof-string-cache.h
index 2fd5274..f45ff1b 100644
--- a/libgnucash/engine/qof-string-cache.h
+++ b/libgnucash/engine/qof-string-cache.h
@@ -79,18 +79,18 @@ void qof_string_cache_destroy(void);
 /** You can use this function as a destroy notifier for a GHashTable
    that uses common strings as keys (or values, for that matter.)
 */
-void qof_string_cache_remove(gconstpointer key);
+void qof_string_cache_remove(const char * key);
 
 /** You can use this function with g_hash_table_insert(), for the key
    (or value), as long as you use the destroy notifier above.
 */
-gpointer qof_string_cache_insert(gconstpointer key);
+char * qof_string_cache_insert(const char * key);
 
 /** Same as CACHE_REPLACE below, but safe to call from C++.
  */
-void qof_string_cache_replace(gconstpointer * dst, gconstpointer src);
+char * qof_string_cache_replace(const char * dst, const char * src);
 
-#define CACHE_INSERT(str) qof_string_cache_insert((gconstpointer)(str))
+#define CACHE_INSERT(str) qof_string_cache_insert((str))
 #define CACHE_REMOVE(str) qof_string_cache_remove((str))
 
 /* Replace cached string currently in 'dst' with string in 'src'.
diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index 15d8020..918e516 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -563,7 +563,7 @@ qof_book_get_collection (const QofBook *book, QofIdType entity_type)
         col = qof_collection_new (entity_type);
         g_hash_table_insert(
             book->hash_of_collections,
-            qof_string_cache_insert((gpointer) entity_type), col);
+            qof_string_cache_insert(entity_type), col);
     }
     return col;
 }

commit 805549ba247f79308f5e18e40f8999359dcc6e2a
Author: lmat <dartme18 at gmail.com>
Date:   Sun Dec 10 05:42:08 2017 -0800

    Rename qofinstance function
    
    This function was supposed to be renamed a while back. I had named it
    this way for debugging purposes.

diff --git a/libgnucash/engine/Scrub.c b/libgnucash/engine/Scrub.c
index b4b744b..f168a24 100644
--- a/libgnucash/engine/Scrub.c
+++ b/libgnucash/engine/Scrub.c
@@ -1228,10 +1228,10 @@ xaccAccountDeleteOldData (Account *account)
 {
     if (!account) return;
     xaccAccountBeginEdit (account);
-    qof_instance_set_var_kvp (QOF_INSTANCE (account), NULL, 1, "old-currency");
-    qof_instance_set_var_kvp (QOF_INSTANCE (account), NULL, 1, "old-security");
-    qof_instance_set_var_kvp (QOF_INSTANCE (account), NULL, 1, "old-currency-scu");
-    qof_instance_set_var_kvp (QOF_INSTANCE (account), NULL, 1, "old-security-scu");
+    qof_instance_set_kvp (QOF_INSTANCE (account), NULL, 1, "old-currency");
+    qof_instance_set_kvp (QOF_INSTANCE (account), NULL, 1, "old-security");
+    qof_instance_set_kvp (QOF_INSTANCE (account), NULL, 1, "old-currency-scu");
+    qof_instance_set_kvp (QOF_INSTANCE (account), NULL, 1, "old-security-scu");
     qof_instance_set_dirty (QOF_INSTANCE (account));
     xaccAccountCommitEdit (account);
 }
@@ -1337,7 +1337,7 @@ xaccAccountScrubKvp (Account *account)
 
     if (!account) return;
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (account), &v, 1, "notes");
+    qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "notes");
     if (G_VALUE_HOLDS_STRING (&v))
     {
         str2 = g_strstrip(g_value_dup_string(&v));
@@ -1346,7 +1346,7 @@ xaccAccountScrubKvp (Account *account)
         g_free(str2);
     }
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (account), &v, 1, "placeholder");
+    qof_instance_get_kvp (QOF_INSTANCE (account), &v, 1, "placeholder");
     if ((G_VALUE_HOLDS_STRING (&v) &&
         strcmp(g_value_get_string (&v), "false") == 0) ||
         (G_VALUE_HOLDS_BOOLEAN (&v) && ! g_value_get_boolean (&v)))
diff --git a/libgnucash/engine/Split.c b/libgnucash/engine/Split.c
index 772849f..452b36a 100644
--- a/libgnucash/engine/Split.c
+++ b/libgnucash/engine/Split.c
@@ -184,31 +184,31 @@ gnc_split_get_property(GObject         *object,
             g_value_take_object(value, split->lot);
             break;
         case PROP_SX_CREDIT_FORMULA:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
             break;
         case PROP_SX_CREDIT_NUMERIC:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
             break;
         case PROP_SX_DEBIT_FORMULA:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
             break;
         case PROP_SX_DEBIT_NUMERIC:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
             break;
         case PROP_SX_ACCOUNT:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
             break;
         case PROP_SX_SHARES:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
             break;
         case PROP_ONLINE_ACCOUNT:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 1, "online_id");
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "online_id");
             break;
         case PROP_GAINS_SPLIT:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
             break;
         case PROP_GAINS_SOURCE:
-            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
+            qof_instance_get_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
             break;
         default:
             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -261,31 +261,31 @@ gnc_split_set_property(GObject         *object,
             xaccSplitSetLot(split, g_value_get_object(value));
             break;
         case PROP_SX_CREDIT_FORMULA:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
             break;
         case PROP_SX_CREDIT_NUMERIC:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
             break;
         case PROP_SX_DEBIT_FORMULA:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
             break;
         case PROP_SX_DEBIT_NUMERIC:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
             break;
         case PROP_SX_ACCOUNT:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
             break;
         case PROP_SX_SHARES:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
             break;
         case PROP_ONLINE_ACCOUNT:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 1, "online_id");
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "online_id");
             break;
         case PROP_GAINS_SPLIT:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
             break;
         case PROP_GAINS_SOURCE:
-            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
+            qof_instance_set_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
             break;
         default:
             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -1079,7 +1079,7 @@ xaccSplitDetermineGainStatus (Split *split)
         return;
     }
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (split), &v, 1, "gains-source");
+    qof_instance_get_kvp (QOF_INSTANCE (split), &v, 1, "gains-source");
     if (G_VALUE_HOLDS_BOXED (&v))
         guid = (GncGUID*)g_value_get_boxed (&v);
     if (!guid)
@@ -1972,7 +1972,7 @@ xaccSplitGetType(const Split *s)
     const char *split_type = NULL;
 
     if (!s) return NULL;
-    qof_instance_get_var_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
+    qof_instance_get_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
     if (G_VALUE_HOLDS_STRING (&v))
         split_type = g_value_get_string (&v);
     return split_type ? split_type : "normal";
@@ -1989,7 +1989,7 @@ xaccSplitMakeStockSplit(Split *s)
     s->value = gnc_numeric_zero();
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, "stock-split");
-    qof_instance_set_var_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
+    qof_instance_set_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
     SET_GAINS_VDIRTY(s);
     mark_split(s);
     qof_instance_set_dirty(QOF_INSTANCE(s));
@@ -2126,7 +2126,7 @@ xaccSplitVoidFormerAmount(const Split *split)
     GValue v = G_VALUE_INIT;
     gnc_numeric *num = NULL;
     g_return_val_if_fail(split, gnc_numeric_zero());
-    qof_instance_get_var_kvp (QOF_INSTANCE (split), &v, 1, void_former_amt_str);
+    qof_instance_get_kvp (QOF_INSTANCE (split), &v, 1, void_former_amt_str);
     if (G_VALUE_HOLDS_BOXED (&v))
         num = (gnc_numeric*)g_value_get_boxed (&v);
     return num ? *num : gnc_numeric_zero();
@@ -2138,7 +2138,7 @@ xaccSplitVoidFormerValue(const Split *split)
     GValue v = G_VALUE_INIT;
     gnc_numeric *num = NULL;
     g_return_val_if_fail(split, gnc_numeric_zero());
-    qof_instance_get_var_kvp (QOF_INSTANCE (split), &v, 1, void_former_val_str);
+    qof_instance_get_kvp (QOF_INSTANCE (split), &v, 1, void_former_val_str);
     if (G_VALUE_HOLDS_BOXED (&v))
         num = (gnc_numeric*)g_value_get_boxed (&v);
     return num ? *num : gnc_numeric_zero();
@@ -2153,10 +2153,10 @@ xaccSplitVoid(Split *split)
     g_value_init (&v, GNC_TYPE_NUMERIC);
     num =  xaccSplitGetAmount(split);
     g_value_set_boxed (&v, &num);
-    qof_instance_set_var_kvp (QOF_INSTANCE (split), &v, 1, void_former_amt_str);
+    qof_instance_set_kvp (QOF_INSTANCE (split), &v, 1, void_former_amt_str);
     num =  xaccSplitGetValue(split);
     g_value_set_boxed (&v, &num);
-    qof_instance_set_var_kvp (QOF_INSTANCE (split), &v, 1, void_former_val_str);
+    qof_instance_set_kvp (QOF_INSTANCE (split), &v, 1, void_former_val_str);
 
     /* Marking dirty handled by SetAmount etc. */
     xaccSplitSetAmount (split, zero);
@@ -2170,8 +2170,8 @@ xaccSplitUnvoid(Split *split)
     xaccSplitSetAmount (split, xaccSplitVoidFormerAmount(split));
     xaccSplitSetValue (split, xaccSplitVoidFormerValue(split));
     xaccSplitSetReconcile(split, NREC);
-    qof_instance_set_var_kvp (QOF_INSTANCE (split), NULL, 1, void_former_amt_str);
-    qof_instance_set_var_kvp (QOF_INSTANCE (split), NULL, 1, void_former_val_str);
+    qof_instance_set_kvp (QOF_INSTANCE (split), NULL, 1, void_former_amt_str);
+    qof_instance_set_kvp (QOF_INSTANCE (split), NULL, 1, void_former_val_str);
     qof_instance_set_dirty (QOF_INSTANCE (split));
 }
 
diff --git a/libgnucash/engine/Transaction.c b/libgnucash/engine/Transaction.c
index cfa651a..5fc7c7e 100644
--- a/libgnucash/engine/Transaction.c
+++ b/libgnucash/engine/Transaction.c
@@ -338,13 +338,13 @@ gnc_transaction_get_property(GObject* object,
         g_value_set_boxed(value, &tx->date_entered);
         break;
     case PROP_INVOICE:
-        qof_instance_get_var_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
+        qof_instance_get_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
         break;
     case PROP_SX_TXN:
-        qof_instance_get_var_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
+        qof_instance_get_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
         break;
     case PROP_ONLINE_ACCOUNT:
-        qof_instance_get_var_kvp (QOF_INSTANCE (tx), value, 1, "online_id");
+        qof_instance_get_kvp (QOF_INSTANCE (tx), value, 1, "online_id");
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -384,13 +384,13 @@ gnc_transaction_set_property(GObject* object,
         xaccTransSetDateEnteredTS(tx, g_value_get_boxed(value));
         break;
     case PROP_INVOICE:
-        qof_instance_set_var_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
+        qof_instance_set_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
         break;
     case PROP_SX_TXN:
-        qof_instance_set_var_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
+        qof_instance_set_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
         break;
     case PROP_ONLINE_ACCOUNT:
-        qof_instance_set_var_kvp (QOF_INSTANCE (tx), value, 1, "online_id");
+        qof_instance_set_kvp (QOF_INSTANCE (tx), value, 1, "online_id");
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -1987,7 +1987,7 @@ xaccTransSetDatePostedGDate (Transaction *trans, GDate date)
      * clearly be distinguished from the Timespec. */
     g_value_init (&v, G_TYPE_DATE);
     g_value_set_boxed (&v, &date);
-    qof_instance_set_var_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_DATE_POSTED);
+    qof_instance_set_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_DATE_POSTED);
     /* mark dirty and commit handled by SetDateInternal */
     xaccTransSetDateInternal(trans, &trans->date_posted,
                              gdate_to_timespec(date));
@@ -2063,7 +2063,7 @@ xaccTransSetDateDueTS (Transaction *trans, const Timespec *ts)
     g_value_init (&v, GNC_TYPE_TIMESPEC);
     g_value_set_boxed (&v, ts);
     xaccTransBeginEdit(trans);
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_DUE_KVP);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_DUE_KVP);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2077,7 +2077,7 @@ xaccTransSetTxnType (Transaction *trans, char type)
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, s);
     xaccTransBeginEdit(trans);
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_TXN_TYPE_KVP);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_TXN_TYPE_KVP);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2087,7 +2087,7 @@ void xaccTransClearReadOnly (Transaction *trans)
     if (trans)
     {
         xaccTransBeginEdit(trans);
-        qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, TRANS_READ_ONLY_REASON);
+        qof_instance_set_kvp (QOF_INSTANCE (trans), NULL, 1, TRANS_READ_ONLY_REASON);
         qof_instance_set_dirty(QOF_INSTANCE(trans));
         xaccTransCommitEdit(trans);
     }
@@ -2102,7 +2102,7 @@ xaccTransSetReadOnly (Transaction *trans, const char *reason)
         g_value_init (&v, G_TYPE_STRING);
         g_value_set_string (&v, reason);
         xaccTransBeginEdit(trans);
-        qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_READ_ONLY_REASON);
+        qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_READ_ONLY_REASON);
         qof_instance_set_dirty(QOF_INSTANCE(trans));
         xaccTransCommitEdit(trans);
     }
@@ -2160,7 +2160,7 @@ xaccTransSetAssociation (Transaction *trans, const char *assoc)
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, assoc);
     xaccTransBeginEdit(trans);
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, assoc_uri_str);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, assoc_uri_str);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2182,7 +2182,7 @@ xaccTransSetNotes (Transaction *trans, const char *notes)
     g_value_set_string (&v, notes);
     xaccTransBeginEdit(trans);
 
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2198,10 +2198,10 @@ xaccTransSetIsClosingTxn (Transaction *trans, gboolean is_closing)
         GValue v = G_VALUE_INIT;
         g_value_init (&v, G_TYPE_INT64);
         g_value_set_int64 (&v, 1);
-        qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_is_closing_str);
+        qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, trans_is_closing_str);
     }
     else
-        qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, trans_is_closing_str);
+        qof_instance_set_kvp (QOF_INSTANCE (trans), NULL, 1, trans_is_closing_str);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2336,7 +2336,7 @@ xaccTransGetAssociation (const Transaction *trans)
 {
     GValue v = G_VALUE_INIT;
     if (!trans) return NULL;
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, assoc_uri_str);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, assoc_uri_str);
     if (G_VALUE_HOLDS_STRING (&v))
          return g_value_get_string (&v);
     return NULL;
@@ -2347,7 +2347,7 @@ xaccTransGetNotes (const Transaction *trans)
 {
     GValue v = G_VALUE_INIT;
     if (!trans) return NULL;
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     if (G_VALUE_HOLDS_STRING (&v))
          return g_value_get_string (&v);
     return NULL;
@@ -2358,7 +2358,7 @@ xaccTransGetIsClosingTxn (const Transaction *trans)
 {
     GValue v = G_VALUE_INIT;
     if (!trans) return FALSE;
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_is_closing_str);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, trans_is_closing_str);
     if (G_VALUE_HOLDS_INT64 (&v))
          return g_value_get_int64 (&v);
     return FALSE;
@@ -2413,7 +2413,7 @@ xaccTransGetDatePostedGDate (const Transaction *trans)
          * from there because it doesn't suffer from time zone
          * shifts. */
         GValue v = G_VALUE_INIT;
-        qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_POSTED);
+        qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_POSTED);
         if (G_VALUE_HOLDS_BOXED (&v))
              result = *(GDate*)g_value_get_boxed (&v);
         if (! g_date_valid (&result))
@@ -2448,7 +2448,7 @@ xaccTransGetDateDueTS (const Transaction *trans, Timespec *ts)
     GValue v = G_VALUE_INIT;
     if (!trans || !ts) return;
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_DUE_KVP);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_DUE_KVP);
     if (G_VALUE_HOLDS_BOXED (&v))
          *ts = *(Timespec*)g_value_get_boxed (&v);
     if (ts->tv_sec == 0)
@@ -2470,7 +2470,7 @@ xaccTransGetTxnType (const Transaction *trans)
     GValue v = G_VALUE_INIT;
 
     if (!trans) return TXN_TYPE_NONE;
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_TXN_TYPE_KVP);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_TXN_TYPE_KVP);
     if (G_VALUE_HOLDS_STRING (&v))
          s = g_value_get_string (&v);
     if (s && strlen (s) == 1)
@@ -2488,7 +2488,7 @@ xaccTransGetReadOnly (const Transaction *trans)
     GValue v = G_VALUE_INIT;
     const char *s = NULL;
     if (trans == NULL) return NULL;
-    qof_instance_get_var_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_READ_ONLY_REASON);
+    qof_instance_get_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_READ_ONLY_REASON);
     if (G_VALUE_HOLDS_STRING (&v))
          s = g_value_get_string (&v);
     if (s && strlen (s))
@@ -2686,20 +2686,20 @@ xaccTransVoid(Transaction *trans, const char *reason)
         return;
     }
     xaccTransBeginEdit(trans);
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     if (G_VALUE_HOLDS_STRING (&v))
-        qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, void_former_notes_str);
+        qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, void_former_notes_str);
     else
         g_value_init (&v, G_TYPE_STRING);
 
     g_value_set_string (&v, _("Voided transaction"));
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     g_value_set_string (&v, reason);
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
 
     gnc_timespec_to_iso8601_buff (timespec_now (), iso8601_str);
     g_value_set_string (&v, iso8601_str);
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, void_time_str);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, void_time_str);
 
     FOR_EACH_SPLIT(trans, xaccSplitVoid(s));
 
@@ -2715,7 +2715,7 @@ xaccTransGetVoidStatus(const Transaction *trans)
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(trans, FALSE);
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
     if (G_VALUE_HOLDS_STRING (&v))
          s = g_value_get_string (&v);
     return s && strlen(s);
@@ -2727,7 +2727,7 @@ xaccTransGetVoidReason(const Transaction *trans)
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(trans, FALSE);
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
     if (G_VALUE_HOLDS_STRING (&v))
          return g_value_get_string (&v);
     return NULL;
@@ -2741,7 +2741,7 @@ xaccTransGetVoidTime(const Transaction *tr)
     Timespec void_time = {0, 0};
 
     g_return_val_if_fail(tr, void_time);
-    qof_instance_get_var_kvp (QOF_INSTANCE (tr), &v, 1, void_time_str);
+    qof_instance_get_kvp (QOF_INSTANCE (tr), &v, 1, void_time_str);
     if (G_VALUE_HOLDS_STRING (&v))
         s = g_value_get_string (&v);
     if (s)
@@ -2756,18 +2756,18 @@ xaccTransUnvoid (Transaction *trans)
     const char *s = NULL;
     g_return_if_fail(trans);
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
     if (G_VALUE_HOLDS_STRING (&v))
         s = g_value_get_string (&v);
     if (s == NULL) return; /* Transaction isn't voided. Bail. */
     xaccTransBeginEdit(trans);
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, void_former_notes_str);
+    qof_instance_get_kvp (QOF_INSTANCE (trans), &v, 1, void_former_notes_str);
     if (G_VALUE_HOLDS_STRING (&v))
-        qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, void_former_notes_str);
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, void_reason_str);
-    qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, void_time_str);
+        qof_instance_set_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), NULL, 1, void_former_notes_str);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), NULL, 1, void_reason_str);
+    qof_instance_set_kvp (QOF_INSTANCE (trans), NULL, 1, void_time_str);
 
     FOR_EACH_SPLIT(trans, xaccSplitUnvoid(s));
 
@@ -2797,7 +2797,7 @@ xaccTransReverse (Transaction *orig)
     /* Now update the original with a pointer to the new one */
     g_value_init (&v, GNC_TYPE_GUID);
     g_value_set_boxed (&v, xaccTransGetGUID(trans));
-    qof_instance_set_var_kvp (QOF_INSTANCE (orig), &v, 1, TRANS_REVERSED_BY);
+    qof_instance_set_kvp (QOF_INSTANCE (orig), &v, 1, TRANS_REVERSED_BY);
 
     /* Make sure the reverse transaction is not read-only */
     xaccTransClearReadOnly(trans);
@@ -2812,7 +2812,7 @@ xaccTransGetReversedBy(const Transaction *trans)
 {
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(trans, NULL);
-    qof_instance_get_var_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_REVERSED_BY);
+    qof_instance_get_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_REVERSED_BY);
     if (G_VALUE_HOLDS_BOXED (&v))
         return xaccTransLookup((GncGUID*)g_value_get_boxed (&v),
                                qof_instance_get_book(trans));
diff --git a/libgnucash/engine/gnc-budget.c b/libgnucash/engine/gnc-budget.c
index 4da6cf1..a4a2a21 100644
--- a/libgnucash/engine/gnc-budget.c
+++ b/libgnucash/engine/gnc-budget.c
@@ -497,7 +497,7 @@ gnc_budget_unset_account_period_value(GncBudget *budget, const Account *account,
     make_period_path (account, period_num, path_part_one, path_part_two);
 
     gnc_budget_begin_edit(budget);
-    qof_instance_set_var_kvp (QOF_INSTANCE (budget), NULL, 2, path_part_one, path_part_two);
+    qof_instance_set_kvp (QOF_INSTANCE (budget), NULL, 2, path_part_one, path_part_two);
     qof_instance_set_dirty(&budget->inst);
     gnc_budget_commit_edit(budget);
 
@@ -529,13 +529,13 @@ gnc_budget_set_account_period_value(GncBudget *budget, const Account *account,
 
     gnc_budget_begin_edit(budget);
     if (gnc_numeric_check(val))
-        qof_instance_set_var_kvp (QOF_INSTANCE (budget), NULL, 2, path_part_one, path_part_two);
+        qof_instance_set_kvp (QOF_INSTANCE (budget), NULL, 2, path_part_one, path_part_two);
     else
     {
         GValue v = G_VALUE_INIT;
         g_value_init (&v, GNC_TYPE_NUMERIC);
         g_value_set_boxed (&v, &val);
-        qof_instance_set_var_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
+        qof_instance_set_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
     }
     qof_instance_set_dirty(&budget->inst);
     gnc_budget_commit_edit(budget);
@@ -561,7 +561,7 @@ gnc_budget_is_account_period_value_set(const GncBudget *budget,
     g_return_val_if_fail(account, FALSE);
 
     make_period_path (account, period_num, path_part_one, path_part_two);
-    qof_instance_get_var_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
+    qof_instance_get_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
     if (G_VALUE_HOLDS_BOXED (&v))
         ptr = g_value_get_boxed (&v);
     return (ptr != NULL);
@@ -581,7 +581,7 @@ gnc_budget_get_account_period_value(const GncBudget *budget,
     g_return_val_if_fail(account, gnc_numeric_zero());
 
     make_period_path (account, period_num, path_part_one, path_part_two);
-    qof_instance_get_var_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
+    qof_instance_get_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
     if (G_VALUE_HOLDS_BOXED (&v))
         numeric = (gnc_numeric*)g_value_get_boxed (&v);
 
diff --git a/libgnucash/engine/gnc-commodity.c b/libgnucash/engine/gnc-commodity.c
index 85570fb..c9c28ed 100644
--- a/libgnucash/engine/gnc-commodity.c
+++ b/libgnucash/engine/gnc-commodity.c
@@ -1083,7 +1083,7 @@ gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm)
     GValue v = G_VALUE_INIT;
 
     if (!cm) return FALSE;
-    qof_instance_get_var_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
+    qof_instance_get_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
     if (G_VALUE_HOLDS_STRING (&v) &&
         strcmp(g_value_get_string (&v), "false") == 0)
         return FALSE;
@@ -1145,7 +1145,7 @@ gnc_commodity_get_user_symbol(const gnc_commodity *cm)
 {
     GValue v = G_VALUE_INIT;
     if (!cm) return NULL;
-    qof_instance_get_var_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
+    qof_instance_get_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
     if (G_VALUE_HOLDS_STRING (&v))
         return g_value_get_string (&v);
     return NULL;
@@ -1316,12 +1316,12 @@ gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm,
     }
     gnc_commodity_begin_edit(cm);
     if (flag)
-        qof_instance_set_var_kvp (QOF_INSTANCE (cm), NULL, 1, "auto_quote_control");
+        qof_instance_set_kvp (QOF_INSTANCE (cm), NULL, 1, "auto_quote_control");
     else
     {
         g_value_init (&v, G_TYPE_STRING);
         g_value_set_string (&v, "false");
-        qof_instance_set_var_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
+        qof_instance_set_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
     }
     mark_commodity_dirty(cm);
     gnc_commodity_commit_edit(cm);
@@ -1456,10 +1456,10 @@ gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
     {
         g_value_init (&v, G_TYPE_STRING);
         g_value_set_string (&v, user_symbol);
-        qof_instance_set_var_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
+        qof_instance_set_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
     }
     else
-        qof_instance_set_var_kvp (QOF_INSTANCE(cm), NULL, 1, "user_symbol");
+        qof_instance_set_kvp (QOF_INSTANCE(cm), NULL, 1, "user_symbol");
 
     mark_commodity_dirty(cm);
     gnc_commodity_commit_edit(cm);
diff --git a/libgnucash/engine/gnc-lot.c b/libgnucash/engine/gnc-lot.c
index 896a39d..7df4483 100644
--- a/libgnucash/engine/gnc-lot.c
+++ b/libgnucash/engine/gnc-lot.c
@@ -148,13 +148,13 @@ gnc_lot_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec*
         g_value_set_int(value, priv->marker);
         break;
     case PROP_INVOICE:
-        qof_instance_get_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
+        qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
         break;
     case PROP_OWNER_TYPE:
-        qof_instance_get_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
+        qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
         break;
     case PROP_OWNER_GUID:
-        qof_instance_get_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
+        qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -188,13 +188,13 @@ gnc_lot_set_property (GObject* object,
         priv->marker = g_value_get_int(value);
         break;
     case PROP_INVOICE:
-        qof_instance_set_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
+        qof_instance_set_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
         break;
     case PROP_OWNER_TYPE:
-        qof_instance_set_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
+        qof_instance_set_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
         break;
     case PROP_OWNER_GUID:
-        qof_instance_set_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
+        qof_instance_set_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -427,7 +427,7 @@ gnc_lot_get_title (const GNCLot *lot)
 {
     GValue v = G_VALUE_INIT;
     if (!lot) return NULL;
-    qof_instance_get_var_kvp (QOF_INSTANCE (lot), &v, 1, "title");
+    qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "title");
     if (G_VALUE_HOLDS_STRING (&v))
         return g_value_get_string (&v);
     return NULL;
@@ -438,7 +438,7 @@ gnc_lot_get_notes (const GNCLot *lot)
 {
     GValue v = G_VALUE_INIT;
     if (!lot) return NULL;
-    qof_instance_get_var_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
+    qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
     if (G_VALUE_HOLDS_STRING (&v))
         return g_value_get_string (&v);
     return NULL;
@@ -452,7 +452,7 @@ gnc_lot_set_title (GNCLot *lot, const char *str)
     qof_begin_edit(QOF_INSTANCE(lot));
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, str);
-    qof_instance_set_var_kvp (QOF_INSTANCE (lot), &v, 1, "title");
+    qof_instance_set_kvp (QOF_INSTANCE (lot), &v, 1, "title");
     qof_instance_set_dirty(QOF_INSTANCE(lot));
     gnc_lot_commit_edit(lot);
 }
@@ -465,7 +465,7 @@ gnc_lot_set_notes (GNCLot *lot, const char *str)
     qof_begin_edit(QOF_INSTANCE(lot));
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, str);
-    qof_instance_set_var_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
+    qof_instance_set_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
     qof_instance_set_dirty(QOF_INSTANCE(lot));
     gnc_lot_commit_edit(lot);
 }
diff --git a/libgnucash/engine/gncCustomer.c b/libgnucash/engine/gncCustomer.c
index b318e71..48b470f 100644
--- a/libgnucash/engine/gncCustomer.c
+++ b/libgnucash/engine/gncCustomer.c
@@ -140,13 +140,13 @@ gnc_customer_get_property (GObject         *object,
         g_value_set_string(value, cust->name);
         break;
     case PROP_PDF_DIRNAME:
-        qof_instance_get_var_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     case PROP_LAST_POSTED:
-        qof_instance_get_var_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
+        qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
         break;
     case PROP_PAYMENT_LAST_ACCT:
-        qof_instance_get_var_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        qof_instance_get_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -174,13 +174,13 @@ gnc_customer_set_property (GObject         *object,
         gncCustomerSetName(cust, g_value_get_string(value));
         break;
     case PROP_PDF_DIRNAME:
-        qof_instance_set_var_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     case PROP_LAST_POSTED:
-        qof_instance_set_var_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
+        qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
         break;
     case PROP_PAYMENT_LAST_ACCT:
-        qof_instance_set_var_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        qof_instance_set_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
diff --git a/libgnucash/engine/gncEmployee.c b/libgnucash/engine/gncEmployee.c
index f41c388..d992b0a 100644
--- a/libgnucash/engine/gncEmployee.c
+++ b/libgnucash/engine/gncEmployee.c
@@ -163,13 +163,13 @@ gnc_employee_get_property (GObject         *object,
         g_value_take_object(value, emp->ccard_acc);
         break;
     case PROP_PDF_DIRNAME:
-        qof_instance_get_var_kvp (QOF_INSTANCE (emp), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        qof_instance_get_kvp (QOF_INSTANCE (emp), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     case PROP_LAST_POSTED:
-        qof_instance_get_var_kvp (QOF_INSTANCE (emp), value, 1, LAST_POSTED_TO_ACCT);
+        qof_instance_get_kvp (QOF_INSTANCE (emp), value, 1, LAST_POSTED_TO_ACCT);
         break;
     case PROP_PAYMENT_LAST_ACCT:
-        qof_instance_get_var_kvp (QOF_INSTANCE (emp), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        qof_instance_get_kvp (QOF_INSTANCE (emp), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -220,13 +220,13 @@ gnc_employee_set_property (GObject         *object,
         gncEmployeeSetCCard(emp, g_value_get_object(value));
         break;
     case PROP_PDF_DIRNAME:
-        qof_instance_set_var_kvp (QOF_INSTANCE (emp), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        qof_instance_set_kvp (QOF_INSTANCE (emp), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     case PROP_LAST_POSTED:
-        qof_instance_set_var_kvp (QOF_INSTANCE (emp), value, 1, LAST_POSTED_TO_ACCT);
+        qof_instance_set_kvp (QOF_INSTANCE (emp), value, 1, LAST_POSTED_TO_ACCT);
         break;
     case PROP_PAYMENT_LAST_ACCT:
-        qof_instance_set_var_kvp (QOF_INSTANCE (emp), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        qof_instance_set_kvp (QOF_INSTANCE (emp), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
diff --git a/libgnucash/engine/gncInvoice.c b/libgnucash/engine/gncInvoice.c
index 074270d..ac8eaac 100644
--- a/libgnucash/engine/gncInvoice.c
+++ b/libgnucash/engine/gncInvoice.c
@@ -354,9 +354,9 @@ GncInvoice *gncInvoiceCopy (const GncInvoice *from)
     invoice->billing_id = CACHE_INSERT (from->billing_id);
     invoice->active = from->active;
 
-    qof_instance_get_var_kvp (QOF_INSTANCE (from), &v, 1, GNC_INVOICE_IS_CN);
+    qof_instance_get_kvp (QOF_INSTANCE (from), &v, 1, GNC_INVOICE_IS_CN);
     if (G_VALUE_HOLDS_INT64 (&v))
-         qof_instance_set_var_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
+         qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
 
     invoice->terms = from->terms;
     gncBillTermIncRef (invoice->terms);
@@ -551,7 +551,7 @@ void gncInvoiceSetIsCreditNote (GncInvoice *invoice, gboolean credit_note)
     gncInvoiceBeginEdit (invoice);
     g_value_init (&v, G_TYPE_INT64);
     g_value_set_int64(&v, credit_note ? 1 : 0);
-    qof_instance_set_var_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
+    qof_instance_set_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
     mark_invoice (invoice);
     gncInvoiceCommitEdit (invoice);
 
@@ -1040,7 +1040,7 @@ gboolean gncInvoiceGetIsCreditNote (const GncInvoice *invoice)
 {
     GValue v = G_VALUE_INIT;
     if (!invoice) return FALSE;
-    qof_instance_get_var_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_IS_CN);
+    qof_instance_get_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_IS_CN);
     if (G_VALUE_HOLDS_INT64(&v) && g_value_get_int64(&v))
         return TRUE;
     else
diff --git a/libgnucash/engine/gncJob.c b/libgnucash/engine/gncJob.c
index 5d76c29..2ee97b7 100644
--- a/libgnucash/engine/gncJob.c
+++ b/libgnucash/engine/gncJob.c
@@ -120,7 +120,7 @@ gnc_job_get_property (GObject         *object,
         g_value_set_string(value, job->name);
         break;
     case PROP_PDF_DIRNAME:
-        qof_instance_get_var_kvp (QOF_INSTANCE (job), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        qof_instance_get_kvp (QOF_INSTANCE (job), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -148,7 +148,7 @@ gnc_job_set_property (GObject         *object,
         gncJobSetName(job, g_value_get_string(value));
         break;
     case PROP_PDF_DIRNAME:
-        qof_instance_set_var_kvp (QOF_INSTANCE (job), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        qof_instance_set_kvp (QOF_INSTANCE (job), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -316,11 +316,11 @@ void gncJobSetRate (GncJob *job, gnc_numeric rate)
         GValue v = G_VALUE_INIT;
         g_value_init (&v, GNC_TYPE_NUMERIC);
         g_value_set_boxed (&v, &rate);
-        qof_instance_set_var_kvp (QOF_INSTANCE (job), &v, 1, GNC_JOB_RATE);
+        qof_instance_set_kvp (QOF_INSTANCE (job), &v, 1, GNC_JOB_RATE);
     }
     else
     {
-        qof_instance_set_var_kvp (QOF_INSTANCE (job), NULL, 1, GNC_JOB_RATE);
+        qof_instance_set_kvp (QOF_INSTANCE (job), NULL, 1, GNC_JOB_RATE);
     }
     mark_job (job);
     gncJobCommitEdit (job);
@@ -454,7 +454,7 @@ gnc_numeric gncJobGetRate (const GncJob *job)
     GValue v = G_VALUE_INIT;
     gnc_numeric *rate = NULL;
     if (!job) return gnc_numeric_zero ();
-    qof_instance_get_var_kvp (QOF_INSTANCE (job), &v, 1, GNC_JOB_RATE);
+    qof_instance_get_kvp (QOF_INSTANCE (job), &v, 1, GNC_JOB_RATE);
     if (G_VALUE_HOLDS_BOXED (&v))
         rate = (gnc_numeric*)g_value_get_boxed (&v);
     if (rate)
diff --git a/libgnucash/engine/gncVendor.c b/libgnucash/engine/gncVendor.c
index 1df3fab..b5f418c 100644
--- a/libgnucash/engine/gncVendor.c
+++ b/libgnucash/engine/gncVendor.c
@@ -179,13 +179,13 @@ gnc_vendor_get_property (GObject         *object,
         g_value_set_string(value, qofVendorGetTaxIncluded(vendor));
         break;
     case PROP_PDF_DIRNAME:
-        qof_instance_get_var_kvp (QOF_INSTANCE (vendor), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        qof_instance_get_kvp (QOF_INSTANCE (vendor), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     case PROP_LAST_POSTED:
-        qof_instance_get_var_kvp (QOF_INSTANCE (vendor), value, 1, LAST_POSTED_TO_ACCT);
+        qof_instance_get_kvp (QOF_INSTANCE (vendor), value, 1, LAST_POSTED_TO_ACCT);
         break;
     case PROP_PAYMENT_LAST_ACCT:
-        qof_instance_get_var_kvp (QOF_INSTANCE (vendor), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        qof_instance_get_kvp (QOF_INSTANCE (vendor), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -243,13 +243,13 @@ gnc_vendor_set_property (GObject         *object,
         qofVendorSetTaxIncluded(vendor, g_value_get_string(value));
         break;
     case PROP_PDF_DIRNAME:
-        qof_instance_set_var_kvp (QOF_INSTANCE (vendor), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        qof_instance_set_kvp (QOF_INSTANCE (vendor), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     case PROP_LAST_POSTED:
-        qof_instance_set_var_kvp (QOF_INSTANCE (vendor), value, 1, LAST_POSTED_TO_ACCT);
+        qof_instance_set_kvp (QOF_INSTANCE (vendor), value, 1, LAST_POSTED_TO_ACCT);
         break;
     case PROP_PAYMENT_LAST_ACCT:
-        qof_instance_set_var_kvp (QOF_INSTANCE (vendor), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        qof_instance_set_kvp (QOF_INSTANCE (vendor), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
diff --git a/libgnucash/engine/qofinstance-p.h b/libgnucash/engine/qofinstance-p.h
index c0d79f9..102af1e 100644
--- a/libgnucash/engine/qofinstance-p.h
+++ b/libgnucash/engine/qofinstance-p.h
@@ -124,7 +124,7 @@ gboolean qof_instance_has_kvp (QofInstance *inst);
  * @param value: A GValue containing an item of a type which KvpValue knows
  *           how to store.
  */
-void qof_instance_set_var_kvp (QofInstance *, GValue const * value, unsigned count, ...);
+void qof_instance_set_kvp (QofInstance *, GValue const * value, unsigned count, ...);
 
 /** Retrieves the contents of a KVP slot into a provided GValue.
  * @param inst: The QofInstance
@@ -132,7 +132,7 @@ void qof_instance_set_var_kvp (QofInstance *, GValue const * value, unsigned cou
  * @param value: A GValue into which to store the value of the slot. It will be
  *               set to the correct type.
  */
-void qof_instance_get_var_kvp (QofInstance *, GValue * value, unsigned count, ...);
+void qof_instance_get_kvp (QofInstance *, GValue * value, unsigned count, ...);
 
 /** @} Close out the DOxygen ingroup */
 /* Functions to isolate the KVP mechanism inside QOF for cases where
diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp
index 57cc6bd..e82980c 100644
--- a/libgnucash/engine/qofinstance.cpp
+++ b/libgnucash/engine/qofinstance.cpp
@@ -1066,7 +1066,7 @@ void qof_instance_set_path_kvp (QofInstance * inst, GValue const * value, std::v
 }
 
 void
-qof_instance_set_var_kvp (QofInstance * inst, GValue const * value, unsigned count, ...)
+qof_instance_set_kvp (QofInstance * inst, GValue const * value, unsigned count, ...)
 {
     std::vector<std::string> path;
     va_list args;
@@ -1091,7 +1091,7 @@ void qof_instance_get_path_kvp (QofInstance * inst, GValue * value, std::vector<
 }
 
 void
-qof_instance_get_var_kvp (QofInstance * inst, GValue * value, unsigned count, ...)
+qof_instance_get_kvp (QofInstance * inst, GValue * value, unsigned count, ...)
 {
     std::vector<std::string> path;
     va_list args;

commit fbf4843f31b69eb588d6b47978aa1b8b2265324d
Author: lmat <dartme18 at gmail.com>
Date:   Sat Dec 9 19:49:03 2017 -0800

    Changed bayes import map design
    
    This commit introduces a new feature flag:
    GNC_FEATURE_GUID_FLAT_BAYESIAN. It signifies that the bayes import map
    data are stored flat and by guid. Any time bayes import map data are
    accessed, they are converted if necessary.

diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c
index 12d5e59..aa88b28 100644
--- a/gnucash/gnome-utils/gnc-file.c
+++ b/gnucash/gnome-utils/gnc-file.c
@@ -1022,12 +1022,6 @@ RESTART:
         gnc_warning_dialog(NULL, "%s", message);
         g_free ( message );
     }
-
-    // Convert imap mappings from account full name to guid strings
-    qof_event_suspend();
-    gnc_account_imap_convert_bayes (gnc_get_current_book());
-    qof_event_resume();
-
     return TRUE;
 }
 
diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 67692b3..f04c43a 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -5270,6 +5270,160 @@ get_first_pass_probabilities(GncImportMatchMap * imap, GList * tokens)
     return ret;
 }
 
+static std::string
+look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
+{
+    GList *top_accounts, *ptr;
+    gint   found_len = 0;
+    gchar  found_sep;
+    top_accounts = gnc_account_get_descendants (root);
+    PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
+    /* Go through list of top level accounts */
+    for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
+    {
+        const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
+        // we are looking for the longest top level account that matches
+        if (g_str_has_prefix (full_name.c_str (), name))
+        {
+            gint name_len = strlen (name);
+            const gchar old_sep = full_name[name_len];
+            if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
+            {
+                if (name_len > found_len)
+                {
+                    found_sep = full_name[name_len];
+                    found_len = name_len;
+                }
+            }
+        }
+    }
+    g_list_free (top_accounts); // Free the List
+    std::string new_name {full_name};
+    if (found_len > 1)
+        std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
+    PINFO ("Return full_name is '%s'", new_name.c_str ());
+    return new_name;
+}
+
+static std::string
+get_guid_from_account_name (Account * root, std::string const & name)
+{
+    auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
+    if (!map_account)
+    {
+        auto temp_account_name = look_for_old_separator_descendants (root, name,
+             gnc_get_account_separator_string ());
+        map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
+    }
+    auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
+    return temp_guid.to_string ();
+}
+
+static FlatKvpEntry
+convert_entry (KvpEntry entry, Account* root)
+{
+    /*We need to make a copy here.*/
+    auto account_name = entry.first.back();
+    if (!gnc::GUID::is_valid_guid (account_name))
+    {
+        /* Earlier version stored the account name in the import map, and
+         * there were early beta versions of 2.7 that stored a GUID.
+         * If there is no GUID, we assume it's an account name. */
+        /* Take off the account name and replace it with the GUID */
+        entry.first.pop_back();
+        auto guid_str = get_guid_from_account_name (root, account_name);
+        entry.first.emplace_back (guid_str);
+    }
+    std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
+    new_key = IMAP_FRAME_BAYES + new_key;
+    return {new_key, entry.second};
+}
+
+static std::vector<FlatKvpEntry>
+get_new_flat_imap (Account * acc)
+{
+    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
+    auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
+    if (!slot)
+        return {};
+    auto imap_frame = slot->get<KvpFrame*> ();
+    auto flat_kvp = imap_frame->flatten_kvp ();
+    auto root = gnc_account_get_root (acc);
+    std::vector <FlatKvpEntry> ret;
+    for (auto const & flat_entry : flat_kvp)
+    {
+        auto converted_entry = convert_entry (flat_entry, root);
+        /*If the entry was invalid, we don't perpetuate it.*/
+        if (converted_entry.first.size())
+            ret.emplace_back (converted_entry);
+    }
+    return ret;
+}
+
+static bool
+convert_imap_account_bayes_to_flat (Account *acc)
+{
+    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
+    if (!frame->get_keys().size())
+        return false;
+    auto new_imap = get_new_flat_imap(acc);
+    xaccAccountBeginEdit(acc);
+    frame->set({IMAP_FRAME_BAYES}, nullptr);
+    if (!new_imap.size ())
+    {
+        xaccAccountCommitEdit(acc);
+        return false;
+    }
+    std::for_each(new_imap.begin(), new_imap.end(), [&frame] (FlatKvpEntry const & entry) {
+        frame->set({entry.first.c_str()}, entry.second);
+    });
+    qof_instance_set_dirty (QOF_INSTANCE (acc));
+    xaccAccountCommitEdit(acc);
+    return true;
+}
+
+/*
+ * Checks for import map data and converts them when found.
+ */
+static bool
+imap_convert_bayes_to_flat (QofBook * book)
+{
+    auto root = gnc_book_get_root_account (book);
+    auto accts = gnc_account_get_descendants_sorted (root);
+    bool ret = false;
+    for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
+    {
+        Account *acc = static_cast <Account*> (ptr->data);
+        if (convert_imap_account_bayes_to_flat (acc))
+        {
+            ret = true;
+            gnc_features_set_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
+        }
+    }
+    g_list_free (accts);
+    return ret;
+}
+
+/*
+ * Here we check to see the state of import map data.
+ *
+ * If the GUID_FLAT_BAYESIAN feature flag is set, everything
+ * should be fine.
+ *
+ * If it is not set, there are two possibilities: import data
+ * are present from a previous version or not. If they are,
+ * they are converted, and the feature flag set. If there are
+ * no previous data, nothing is done.
+ */
+static void
+check_import_map_data (QofBook *book)
+{
+    if (gnc_features_check_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN))
+        return;
+    /* This function will set GNC_FEATURE_GUID_FLAT_BAYESIAN if necessary.*/
+    imap_convert_bayes_to_flat (book);
+}
+
 static constexpr double threshold = .90 * probability_factor; /* 90% */
 
 /** Look up an Account in the map */
@@ -5278,6 +5432,7 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
 {
     if (!imap)
         return nullptr;
+    check_import_map_data (imap->book);
     auto first_pass = get_first_pass_probabilities(imap, tokens);
     if (!first_pass.size())
         return nullptr;
@@ -5330,7 +5485,7 @@ change_imap_entry (GncImportMatchMap *imap, std::string const & path, int64_t to
 
     // Add or Update the entry based on guid
     qof_instance_set_path_kvp (QOF_INSTANCE (imap->acc), &value, {path});
-    gnc_features_set_used (imap->book, GNC_FEATURE_GUID_BAYESIAN);
+    gnc_features_set_used (imap->book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
 }
 
 /** Updates the imap for a given account using a list of tokens */
@@ -5350,6 +5505,7 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
         LEAVE(" ");
         return;
     }
+    check_import_map_data (imap->book);
 
     g_return_if_fail (acc != NULL);
     account_fullname = gnc_account_get_full_name(acc);
@@ -5462,6 +5618,7 @@ build_bayes (const char *key, KvpValue * value, GncImapInfo & imapInfo)
 GList *
 gnc_account_imap_get_info_bayes (Account *acc)
 {
+    check_import_map_data (gnc_account_get_book (acc));
     /* A dummy object which is used to hold the specified account, and the list
      * of data about which we care. */
     GncImapInfo imapInfo {acc, nullptr};
@@ -5533,151 +5690,6 @@ gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty)
     g_free (full_category);
 }
 
-/*******************************************************************************/
-
-static std::string
-look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
-{
-    GList *top_accounts, *ptr;
-    gint   found_len = 0;
-    gchar  found_sep;
-    top_accounts = gnc_account_get_descendants (root);
-    PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
-    /* Go through list of top level accounts */
-    for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
-    {
-        const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
-        // we are looking for the longest top level account that matches
-        if (g_str_has_prefix (full_name.c_str (), name))
-        {
-            gint name_len = strlen (name);
-            const gchar old_sep = full_name[name_len];
-            if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
-            {
-                if (name_len > found_len)
-                {
-                    found_sep = full_name[name_len];
-                    found_len = name_len;
-                }
-            }
-        }
-    }
-    g_list_free (top_accounts); // Free the List
-    std::string new_name {full_name};
-    if (found_len > 1)
-        std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
-    PINFO("Return full_name is '%s'", new_name);
-    return new_name;
-}
-
-static std::string
-get_guid_from_account_name (Account * root, std::string const & name)
-{
-    auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
-    if (!map_account)
-    {
-        auto temp_account_name = look_for_old_separator_descendants (root, name,
-             gnc_get_account_separator_string ());
-        map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
-    }
-    auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
-    return temp_guid.to_string ();
-}
-
-static FlatKvpEntry
-convert_entry (KvpEntry entry, Account* root)
-{
-    /*We need to make a copy here.*/
-    auto account_name = entry.first.back();
-    entry.first.pop_back();
-    auto guid_str = get_guid_from_account_name (root, account_name);
-    entry.first.emplace_back ("/");
-    entry.first.emplace_back (guid_str);
-    std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
-    new_key = IMAP_FRAME_BAYES + new_key;
-    return {new_key, entry.second};
-}
-
-static std::vector<FlatKvpEntry>
-get_new_guid_imap (Account * acc)
-{
-    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
-    auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
-    if (!slot)
-        return {};
-    auto imap_frame = slot->get<KvpFrame*> ();
-    auto flat_kvp = imap_frame->flatten_kvp ();
-    auto root = gnc_account_get_root (acc);
-    std::vector <FlatKvpEntry> ret;
-    for (auto const & flat_entry : flat_kvp)
-        ret.emplace_back (convert_entry (flat_entry, root));
-    return ret;
-}
-
-static bool
-convert_imap_account_bayes_to_guid (Account *acc)
-{
-    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
-    if (!frame->get_keys().size())
-        return false;
-    auto new_imap = get_new_guid_imap(acc);
-    xaccAccountBeginEdit(acc);
-    frame->set({IMAP_FRAME_BAYES}, nullptr);
-    if (!new_imap.size ())
-    {
-        xaccAccountCommitEdit(acc);
-        return false;
-    }
-    std::for_each(new_imap.begin(), new_imap.end(), [&frame] (FlatKvpEntry const & entry) {
-        frame->set({entry.first.c_str()}, entry.second);
-    });
-    qof_instance_set_dirty (QOF_INSTANCE (acc));
-    xaccAccountCommitEdit(acc);
-    return true;
-}
-
-char const * run_once_key_to_guid {"changed-bayesian-to-guid"};
-
-static void
-imap_convert_bayes_to_guid (QofBook * book)
-{
-    auto root = gnc_book_get_root_account (book);
-    auto accts = gnc_account_get_descendants_sorted (root);
-    for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
-    {
-        Account *acc = static_cast <Account*> (ptr->data);
-        if (convert_imap_account_bayes_to_guid (acc))
-            gnc_features_set_used (book, GNC_FEATURE_GUID_BAYESIAN);
-    }
-    g_list_free (accts);
-}
-
-static bool
-run_once_key_set (char const * key, QofBook * book)
-{
-    GValue value G_VALUE_INIT;
-    qof_instance_get_path_kvp (QOF_INSTANCE(book), &value, {key});
-    return G_VALUE_HOLDS_STRING(&value) && strcmp(g_value_get_string(&value), "true");
-}
-
-static void
-set_run_once_key (char const * key, QofBook * book)
-{
-    GValue value G_VALUE_INIT;
-    g_value_init(&value, G_TYPE_BOOLEAN);
-    g_value_set_boolean(&value, TRUE);
-    qof_instance_set_path_kvp(QOF_INSTANCE (book), &value, {key});
-}
-
-void
-gnc_account_imap_convert_bayes (QofBook *book)
-{
-    if (run_once_key_set (run_once_key_to_guid, book))
-        return;
-    imap_convert_bayes_to_guid (book);
-    set_run_once_key (run_once_key_to_guid, book);
-}
-
 /* ================================================================ */
 /* QofObject function implementation and registration */
 
diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h
index 2f80632..477d406 100644
--- a/libgnucash/engine/Account.h
+++ b/libgnucash/engine/Account.h
@@ -1449,11 +1449,6 @@ gchar *gnc_account_get_map_entry (Account *acc, const char *full_category);
  */
 void gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty);
 
-/** Search for Bayesian entries with mappings based on full account name and change
- *  them to be based on the account guid
- */
-void gnc_account_imap_convert_bayes (QofBook *book);
-
 /** @} */
 
 
diff --git a/libgnucash/engine/gnc-features.c b/libgnucash/engine/gnc-features.c
index f125602..0c58bad 100644
--- a/libgnucash/engine/gnc-features.c
+++ b/libgnucash/engine/gnc-features.c
@@ -46,6 +46,7 @@ static gncFeature known_features[] =
     { GNC_FEATURE_KVP_EXTRA_DATA, "Extra data for addresses, jobs or invoice entries (requires at least GnuCash 2.6.4)" },
     { GNC_FEATURE_BOOK_CURRENCY, "User specifies a 'book-currency'; costs of other currencies/commodities tracked in terms of book-currency (requires at least GnuCash 2.7.0)" },
     { GNC_FEATURE_GUID_BAYESIAN, "Use account GUID as key for Bayesian data (requires at least GnuCash 2.6.12)" },
+    { GNC_FEATURE_GUID_FLAT_BAYESIAN, "Use account GUID as key for bayesian data and store KVP flat" },
     { NULL },
 };
 
@@ -148,6 +149,32 @@ void gnc_features_set_used (QofBook *book, const gchar *feature)
     }
 
     qof_book_set_feature (book, feature, description);
+}
 
+struct CheckFeature
+{
+    gchar const * checked_feature;
+    gboolean found;
+};
 
+static void gnc_features_check_feature_cb (gpointer pkey, gpointer value,
+				  gpointer data)
+{
+    const gchar *key = (const gchar*)pkey;
+    struct CheckFeature * check_data = data;
+    g_assert(data);
+    if (!g_strcmp0 (key, check_data->checked_feature))
+        check_data->found = TRUE;
 }
+
+gboolean gnc_features_check_used (QofBook *book, const gchar * feature)
+{
+    GHashTable *features_used = qof_book_get_features (book);
+    struct CheckFeature check_data = {feature, FALSE};
+    /* Setup the known_features hash table */
+    gnc_features_init();
+    g_hash_table_foreach (features_used, &gnc_features_check_feature_cb, &check_data);
+    g_hash_table_unref (features_used);
+    return check_data.found;
+}
+
diff --git a/libgnucash/engine/gnc-features.h b/libgnucash/engine/gnc-features.h
index feefe8d..2beca42 100644
--- a/libgnucash/engine/gnc-features.h
+++ b/libgnucash/engine/gnc-features.h
@@ -50,6 +50,7 @@ extern "C" {
 #define GNC_FEATURE_KVP_EXTRA_DATA "Extra data in addresses, jobs or invoice entries"
 #define GNC_FEATURE_BOOK_CURRENCY "Use a Book-Currency"
 #define GNC_FEATURE_GUID_BAYESIAN "Account GUID based Bayesian data"
+#define GNC_FEATURE_GUID_FLAT_BAYESIAN "Account GUID based bayesian with flat KVP"
 
 /** @} */
 
@@ -68,10 +69,14 @@ gchar *gnc_features_test_unknown (QofBook *book);
  */
 void gnc_features_set_used (QofBook *book, const gchar *feature);
 
+/*
+ * Returns true if the specified feature is used.
+ */
+gboolean gnc_features_check_used (QofBook *, char const * feature);
+
 #ifdef __cplusplus
 } /* extern "C" */
-#endif
-
+#endif /*__cplusplus*/
 #endif /* GNC_FEATURES_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/kvp-frame.cpp b/libgnucash/engine/kvp-frame.cpp
index 6f0fc4a..3d09e2f 100644
--- a/libgnucash/engine/kvp-frame.cpp
+++ b/libgnucash/engine/kvp-frame.cpp
@@ -427,16 +427,15 @@ KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <KvpEntry
 {
     for (auto const & entry : m_valuemap)
     {
+        std::vector<std::string> new_path {path};
+        new_path.push_back("/");
         if (entry.second->get_type() == KvpValue::Type::FRAME)
         {
-            std::vector<std::string> send_path {path};
-            send_path.push_back("/");
-            send_path.push_back(entry.first);
-            entry.second->get<KvpFrame*>()->flatten_kvp_impl(send_path, entries);
+            new_path.push_back(entry.first);
+            entry.second->get<KvpFrame*>()->flatten_kvp_impl(new_path, entries);
         }
         else
         {
-            std::vector <std::string> new_path {path};
             new_path.emplace_back (entry.first);
             entries.emplace_back (new_path, entry.second);
         }
diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index 82f998e..15d8020 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -1076,7 +1076,7 @@ static void
 add_feature_to_hash (const gchar *key, KvpValue *value, GHashTable * user_data)
 {
     gchar *descr = g_strdup(value->get<const char*>());
-    g_hash_table_insert (*(GHashTable**)user_data, (gchar*)key, descr);
+    g_hash_table_insert (user_data, (gchar*)key, descr);
 }
 
 GHashTable *
diff --git a/libgnucash/engine/test/gtest-import-map.cpp b/libgnucash/engine/test/gtest-import-map.cpp
index df72445..4f6c7cd 100644
--- a/libgnucash/engine/test/gtest-import-map.cpp
+++ b/libgnucash/engine/test/gtest-import-map.cpp
@@ -318,13 +318,8 @@ TEST_F(ImapBayesTest, AddAccountBayes)
     EXPECT_EQ(2, value->get<int64_t>());
 }
 
-TEST_F(ImapBayesTest, ConvertAccountBayes)
+TEST_F(ImapBayesTest, ConvertBayesData)
 {
-    // prevent the embedded beginedit/committedit from doing anything
-    qof_instance_increase_editlevel(QOF_INSTANCE(t_bank_account));
-    qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
-    gnc_account_imap_add_account_bayes(t_imap, t_list1, t_expense_account1); //Food
-    gnc_account_imap_add_account_bayes(t_imap, t_list2, t_expense_account2); //Drink
     auto root = qof_instance_get_slots(QOF_INSTANCE(t_bank_account));
     auto book = qof_instance_get_slots(QOF_INSTANCE(t_imap->book));
     auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1)); //Food
@@ -334,15 +329,6 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     auto val1 = new KvpValue(static_cast<int64_t>(10));
     auto val2 = new KvpValue(static_cast<int64_t>(5));
     auto val3 = new KvpValue(static_cast<int64_t>(2));
-    // Test for existing entries, all will be 1
-    auto value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + acct1_guid});
-    EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + bar + "/" + acct1_guid});
-    EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + baz + "/" + acct2_guid});
-    EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + waldo + "/" + acct2_guid});
-    EXPECT_EQ(1, value->get<int64_t>());
     // Set up some old entries
     root->set_path({IMAP_FRAME_BAYES, "severely", "divided", "token", "Asset-Bank"}, val1);
     root->set_path({IMAP_FRAME_BAYES, salt, "Asset-Bank#Bank"}, new KvpValue{*val1});
@@ -350,13 +336,11 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     root->set_path({IMAP_FRAME_BAYES, pork, "Expense#Food"}, new KvpValue{*val2});
     root->set_path({IMAP_FRAME_BAYES, sausage, "Expense#Drink"}, val3);
     root->set_path({IMAP_FRAME_BAYES, foo, "Expense#Food"}, new KvpValue{*val2});
-    EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
-    EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
-    qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
-    // Start Convert
-    gnc_account_imap_convert_bayes (t_imap->book);
+    root->set_path({IMAP_FRAME_BAYES, salt, acct1_guid}, new KvpValue{*val1});
+    /*Calling into the imap functions should trigger a conversion.*/
+    gnc_account_imap_add_account_bayes(t_imap, t_list5, t_expense_account2); //pork and sausage; account Food
     // convert from 'Asset-Bank' to 'Asset-Bank' guid
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/severely/divided/token/" + acct3_guid});
+    auto value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/severely/divided/token/" + acct3_guid});
     EXPECT_EQ(10, value->get<int64_t>());
     // convert from 'Asset-Bank#Bank' to 'Sav Bank' guid
     value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + acct4_guid});
@@ -366,14 +350,14 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     EXPECT_EQ(5, value->get<int64_t>());
     // convert from 'Expense#Drink' to 'Drink' guid
     value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + sausage + "/" + acct2_guid});
-    EXPECT_EQ(2, value->get<int64_t>());
+    /*We put in 2, then called it once to bring it up to 3.*/
+    EXPECT_EQ(3, value->get<int64_t>());
     // convert from 'Expense#Food' to 'Food' guid but add to original value
     value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + acct1_guid});
     EXPECT_EQ(5, value->get<int64_t>());
-    // Check for run once flag
-    auto vals = book->get_slot({"changed-bayesian-to-guid"});
-    EXPECT_STREQ("true", vals->get<const char*>());
-    EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
+    // Keep GUID value from original
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + acct1_guid});
+    EXPECT_EQ(10, value->get<int64_t>());
     EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
 }
 

commit f782be1a5116a48b9e8521890da727561742520b
Author: lmat <dartme18 at gmail.com>
Date:   Wed Dec 6 13:52:01 2017 -0800

    Code review responses
    
    Using Aliases to represent cmplicated types
    Corrected variable-sized array on stack
    Using PascalCase for type names and aliases

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 6f09ce9..67692b3 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -58,6 +58,10 @@ static const char *KEY_ASSOC_INCOME_ACCOUNT = "ofx/associated-income-account";
 #define AB_BANK_CODE "bank-code"
 #define AB_TRANS_RETRIEVAL "trans-retrieval"
 
+using FinalProbabilityVec=std::vector<std::pair<std::string, int32_t>>;
+using ProbabilityVec=std::vector<std::pair<std::string, struct AccountProbability>>;
+using FlatKvpEntry=std::pair<std::string, KvpValue*>;
+
 enum
 {
     LAST_SIGNAL
@@ -3027,6 +3031,7 @@ gnc_account_get_full_name(const Account *account)
     AccountPrivate *priv;
     const Account *a;
     char *fullname;
+    gchar **names;
     int level;
 
     /* So much for hardening the API. Too many callers to this function don't
@@ -3053,7 +3058,7 @@ gnc_account_get_full_name(const Account *account)
 
     /* Get all the pointers in the right order. The root node "entry"
      * becomes the terminating NULL pointer for the array of strings. */
-    gchar* names[level*sizeof(gchar*)];
+    names = (gchar **)g_malloc(level * sizeof(gchar *));
     names[--level] = NULL;
     for (a = account; level > 0; a = priv->parent)
     {
@@ -3063,6 +3068,7 @@ gnc_account_get_full_name(const Account *account)
 
     /* Build the full name */
     fullname =  g_strjoinv(account_separator, names);
+    g_free(names);
 
     return fullname;
 }
@@ -5151,13 +5157,13 @@ gnc_account_imap_delete_account (GncImportMatchMap *imap,
   where p(AB) = (a*b)/[a*b + (1-a)(1-b)], product is (a*b),
   product_difference is (1-a) * (1-b)
  */
-struct account_probability
+struct AccountProbability
 {
     double product; /* product of probabilities */
     double product_difference; /* product of (1-probabilities) */
 };
 
-struct account_token_count
+struct AccountTokenCount
 {
     std::string account_guid;
     int64_t token_count; /** occurrences of a given token for this account_guid */
@@ -5166,26 +5172,26 @@ struct account_token_count
 /** total_count and the token_count for a given account let us calculate the
  * probability of a given account with any single token
  */
-struct token_accounts_info
+struct TokenAccountsInfo
 {
-    std::vector<account_token_count> accounts;
+    std::vector<AccountTokenCount> accounts;
     int64_t total_count;
 };
 
 /** holds an account guid and its corresponding integer probability
   the integer probability is some factor of 10
  */
-struct account_info
+struct AccountInfo
 {
     std::string account_guid;
     int32_t probability;
 };
 
 static void
-build_token_info(char const * key, KvpValue * value, token_accounts_info & tokenInfo)
+build_token_info(char const * key, KvpValue * value, TokenAccountsInfo & tokenInfo)
 {
     tokenInfo.total_count += value->get<int64_t>();
-    account_token_count this_account;
+    AccountTokenCount this_account;
     std::string account_guid {key};
     /*By convention, the key ends with the account GUID.*/
     this_account.account_guid = account_guid.substr(account_guid.size() - GUID_ENCODING_LENGTH);
@@ -5198,10 +5204,10 @@ build_token_info(char const * key, KvpValue * value, token_accounts_info & token
   0.10 * 100000 = 10000 */
 static constexpr int probability_factor = 100000;
 
-static std::vector<std::pair<std::string, int32_t>>
-build_probabilities(std::vector<std::pair<std::string, account_probability>> const & first_pass)
+static FinalProbabilityVec
+build_probabilities(ProbabilityVec const & first_pass)
 {
-    std::vector<std::pair<std::string, int32_t>> ret;
+    FinalProbabilityVec ret;
     for (auto const & first_pass_prob : first_pass)
     {
         auto const & account_probability = first_pass_prob.second;
@@ -5216,31 +5222,31 @@ build_probabilities(std::vector<std::pair<std::string, account_probability>> con
     return ret;
 }
 
-static account_info
-highest_probability(std::vector<std::pair<std::string, int32_t>> const & probabilities)
+static AccountInfo
+highest_probability(FinalProbabilityVec const & probabilities)
 {
-    account_info ret {"", std::numeric_limits<int32_t>::min()};
+    AccountInfo ret {"", std::numeric_limits<int32_t>::min()};
     for (auto const & prob : probabilities)
         if (prob.second > ret.probability)
-            ret = account_info{prob.first, prob.second};
+            ret = AccountInfo {prob.first, prob.second};
     return ret;
 }
 
-static std::vector<std::pair<std::string, account_probability>>
+static ProbabilityVec
 get_first_pass_probabilities(GncImportMatchMap * imap, GList * tokens)
 {
-    std::vector<std::pair<std::string, account_probability>> ret;
+    ProbabilityVec ret;
     /* find the probability for each account that contains any of the tokens
      * in the input tokens list. */
     for (auto current_token = tokens; current_token; current_token = current_token->next)
     {
-        token_accounts_info tokenInfo{};
+        TokenAccountsInfo tokenInfo{};
         auto path = std::string{IMAP_FRAME_BAYES "/"} + static_cast <char const *> (current_token->data);
         qof_instance_foreach_slot_prefix (QOF_INSTANCE (imap->acc), path, &build_token_info, tokenInfo);
         for (auto const & current_account_token : tokenInfo.accounts)
         {
             auto item = std::find_if(ret.begin(), ret.end(), [&current_account_token]
-                (std::pair<std::string, account_probability> const & a) {
+                (std::pair<std::string, AccountProbability> const & a) {
                     return current_account_token.account_guid == a.first;
                 });
             if (item != ret.end())
@@ -5253,7 +5259,7 @@ get_first_pass_probabilities(GncImportMatchMap * imap, GList * tokens)
             else
             {
                 /* add a new entry */
-                account_probability new_probability;
+                AccountProbability new_probability;
                 new_probability.product = ((double)current_account_token.token_count /
                                       (double)tokenInfo.total_count);
                 new_probability.product_difference = 1 - (new_probability.product);
@@ -5385,16 +5391,11 @@ build_non_bayes (const char *key, const GValue *value, gpointer user_data)
 {
     if (!G_VALUE_HOLDS_BOXED (value))
         return;
-
     QofBook     *book;
     GncGUID     *guid = NULL;
     gchar       *kvp_path;
     gchar       *guid_string = NULL;
-
-    struct imap_info *imapInfo_node;
-
-    struct imap_info *imapInfo = (struct imap_info*)user_data;
-
+    auto imapInfo = (GncImapInfo*)user_data;
     // Get the book
     book = qof_instance_get_book (imapInfo->source_account);
 
@@ -5408,7 +5409,7 @@ build_non_bayes (const char *key, const GValue *value, gpointer user_data)
 
     PINFO("build_non_bayes: kvp_path is '%s'", kvp_path);
 
-    imapInfo_node = static_cast <imap_info*> (g_malloc(sizeof(*imapInfo_node)));
+    auto imapInfo_node = static_cast <GncImapInfo*> (g_malloc(sizeof(GncImapInfo)));
 
     imapInfo_node->source_account = imapInfo->source_account;
     imapInfo_node->map_account    = xaccAccountLookup (guid, book);
@@ -5435,7 +5436,7 @@ parse_bayes_imap_info (std::string const & imap_bayes_entry)
 }
 
 static void
-build_bayes (const char *key, KvpValue * value, imap_info & imapInfo)
+build_bayes (const char *key, KvpValue * value, GncImapInfo & imapInfo)
 {
     auto slots = qof_instance_get_slots_prefix (QOF_INSTANCE (imapInfo.source_account), IMAP_FRAME_BAYES);
     if (!slots.size()) return;
@@ -5446,7 +5447,7 @@ build_bayes (const char *key, KvpValue * value, imap_info & imapInfo)
         GncGUID guid = temp_guid;
         auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
         std::string category_head {std::get <0> (parsed_key) + "/" + std::get <1> (parsed_key)};
-        auto imap_node = static_cast <imap_info*> (g_malloc (sizeof (imap_info)));
+        auto imap_node = static_cast <GncImapInfo*> (g_malloc (sizeof (GncImapInfo)));
         auto count = entry.second->get <int64_t> ();
         imap_node->source_account = imapInfo.source_account;
         imap_node->map_account = map_account;
@@ -5534,29 +5535,23 @@ gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty)
 
 /*******************************************************************************/
 
-static gchar *
-look_for_old_separator_descendants (Account *root, gchar const *full_name, const gchar *separator)
+static std::string
+look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
 {
     GList *top_accounts, *ptr;
     gint   found_len = 0;
     gchar  found_sep;
-    gchar * new_name = nullptr;
-
     top_accounts = gnc_account_get_descendants (root);
-
-    PINFO("Incoming full_name is '%s', current separator is '%s'", full_name, separator);
-
+    PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
     /* Go through list of top level accounts */
     for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
     {
         const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
-
         // we are looking for the longest top level account that matches
-        if (g_str_has_prefix (full_name, name))
+        if (g_str_has_prefix (full_name.c_str (), name))
         {
             gint name_len = strlen (name);
             const gchar old_sep = full_name[name_len];
-
             if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
             {
                 if (name_len > found_len)
@@ -5568,13 +5563,10 @@ look_for_old_separator_descendants (Account *root, gchar const *full_name, const
         }
     }
     g_list_free (top_accounts); // Free the List
-    new_name = g_strdup (full_name);
-
+    std::string new_name {full_name};
     if (found_len > 1)
-        g_strdelimit (new_name, &found_sep, *separator);
-
+        std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
     PINFO("Return full_name is '%s'", new_name);
-
     return new_name;
 }
 
@@ -5584,17 +5576,16 @@ get_guid_from_account_name (Account * root, std::string const & name)
     auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
     if (!map_account)
     {
-        auto temp_account_name = look_for_old_separator_descendants (root, name.c_str (),
+        auto temp_account_name = look_for_old_separator_descendants (root, name,
              gnc_get_account_separator_string ());
-        map_account = gnc_account_lookup_by_full_name (root, temp_account_name);
-        g_free (temp_account_name);
+        map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
     }
     auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
     return temp_guid.to_string ();
 }
 
-static std::pair <std::string, KvpValue*>
-convert_entry (std::pair <std::vector <std::string>, KvpValue*> entry, Account* root)
+static FlatKvpEntry
+convert_entry (KvpEntry entry, Account* root)
 {
     /*We need to make a copy here.*/
     auto account_name = entry.first.back();
@@ -5607,7 +5598,7 @@ convert_entry (std::pair <std::vector <std::string>, KvpValue*> entry, Account*
     return {new_key, entry.second};
 }
 
-static std::vector<std::pair<std::string, KvpValue*>>
+static std::vector<FlatKvpEntry>
 get_new_guid_imap (Account * acc)
 {
     auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
@@ -5617,7 +5608,7 @@ get_new_guid_imap (Account * acc)
     auto imap_frame = slot->get<KvpFrame*> ();
     auto flat_kvp = imap_frame->flatten_kvp ();
     auto root = gnc_account_get_root (acc);
-    std::vector <std::pair <std::string, KvpValue*>> ret;
+    std::vector <FlatKvpEntry> ret;
     for (auto const & flat_entry : flat_kvp)
         ret.emplace_back (convert_entry (flat_entry, root));
     return ret;
@@ -5637,7 +5628,7 @@ convert_imap_account_bayes_to_guid (Account *acc)
         xaccAccountCommitEdit(acc);
         return false;
     }
-    std::for_each(new_imap.begin(), new_imap.end(), [&frame] (std::pair<std::string, KvpValue*> const & entry) {
+    std::for_each(new_imap.begin(), new_imap.end(), [&frame] (FlatKvpEntry const & entry) {
         frame->set({entry.first.c_str()}, entry.second);
     });
     qof_instance_set_dirty (QOF_INSTANCE (acc));
diff --git a/libgnucash/engine/kvp-frame.cpp b/libgnucash/engine/kvp-frame.cpp
index 9687cc1..6f0fc4a 100644
--- a/libgnucash/engine/kvp-frame.cpp
+++ b/libgnucash/engine/kvp-frame.cpp
@@ -423,7 +423,7 @@ gnc_value_list_get_type (void)
 }
 
 void
-KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <std::pair <std::vector <std::string>, KvpValue*>> & entries) const noexcept
+KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <KvpEntry> & entries) const noexcept
 {
     for (auto const & entry : m_valuemap)
     {
@@ -443,10 +443,10 @@ KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <std::pai
     }
 }
 
-std::vector <std::pair <std::vector <std::string>, KvpValue*>>
+std::vector <KvpEntry>
 KvpFrame::flatten_kvp(void) const noexcept
 {
-    std::vector <std::pair <std::vector <std::string>, KvpValue*>> ret;
+    std::vector <KvpEntry> ret;
     flatten_kvp_impl({}, ret);
     return ret;
 }
diff --git a/libgnucash/engine/kvp-frame.hpp b/libgnucash/engine/kvp-frame.hpp
index 4cdf52d..23eb0c6 100644
--- a/libgnucash/engine/kvp-frame.hpp
+++ b/libgnucash/engine/kvp-frame.hpp
@@ -92,6 +92,7 @@
 #include <algorithm>
 #include <iostream>
 using Path = std::vector<std::string>;
+using KvpEntry = std::pair <std::vector <std::string>, KvpValue*>;
 
 /** Implements KvpFrame.
  *  It's a struct because QofInstance needs to use the typename to declare a
@@ -216,7 +217,7 @@ struct KvpFrameImpl
      * Returns all keys and values of this frame recursively, flattening
      * the frame-containing values.
      */
-    std::vector <std::pair <std::vector <std::string>, KvpValue*>>
+    std::vector <KvpEntry>
     flatten_kvp(void) const noexcept;
 
     /** Test for emptiness
@@ -230,7 +231,7 @@ struct KvpFrameImpl
 
     KvpFrame * get_child_frame_or_nullptr (Path const &) noexcept;
     KvpFrame * get_child_frame_or_create (Path const &) noexcept;
-    void flatten_kvp_impl(std::vector <std::string>, std::vector <std::pair <std::vector <std::string>, KvpValue*>> &) const noexcept;
+    void flatten_kvp_impl(std::vector <std::string>, std::vector <KvpEntry> &) const noexcept;
     KvpValue * set_impl (std::string const &, KvpValue *) noexcept;
 };
 

commit 3312fe2dcdc6a97e5c604b9149bd2026241ac7ff
Author: lmat <dartme18 at gmail.com>
Date:   Fri Dec 1 13:40:06 2017 -0500

    Changed some constants to constexpr

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 19d829d..6f09ce9 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -5193,10 +5193,10 @@ build_token_info(char const * key, KvpValue * value, token_accounts_info & token
     tokenInfo.accounts.push_back(this_account);
 }
 
-/** We scale the probability values by PROBABILITY_FACTOR.
-  ie. with PROBABILITY_FACTOR of 100000, 10% would be
+/** We scale the probability values by probability_factor.
+  ie. with probability_factor of 100000, 10% would be
   0.10 * 100000 = 10000 */
-#define PROBABILITY_FACTOR 100000
+static constexpr int probability_factor = 100000;
 
 static std::vector<std::pair<std::string, int32_t>>
 build_probabilities(std::vector<std::pair<std::string, account_probability>> const & first_pass)
@@ -5210,7 +5210,7 @@ build_probabilities(std::vector<std::pair<std::string, account_probability>> con
          * and product difference ((1-A)(1-B)...)
          */
         int32_t probability = (account_probability.product /
-                (account_probability.product + account_probability.product_difference)) * PROBABILITY_FACTOR;
+                (account_probability.product + account_probability.product_difference)) * probability_factor;
         ret.push_back({first_pass_prob.first, probability});
     }
     return ret;
@@ -5264,7 +5264,7 @@ get_first_pass_probabilities(GncImportMatchMap * imap, GList * tokens)
     return ret;
 }
 
-#define threshold (.90 * PROBABILITY_FACTOR) /* 90% */
+static constexpr double threshold = .90 * probability_factor; /* 90% */
 
 /** Look up an Account in the map */
 Account*

commit 29ad8ff9b07165d88d5fa722e233618c9610fe47
Author: lmat <dartme18 at gmail.com>
Date:   Tue Nov 28 17:15:07 2017 -0500

    Remove unused kvp function

diff --git a/libgnucash/engine/kvp-frame.hpp b/libgnucash/engine/kvp-frame.hpp
index 32dd102..4cdf52d 100644
--- a/libgnucash/engine/kvp-frame.hpp
+++ b/libgnucash/engine/kvp-frame.hpp
@@ -192,12 +192,6 @@ struct KvpFrameImpl
      */
     KvpValue* get_slot(Path keys) noexcept;
 
-    /**
-     * proc is called with each of the immediate contents of this frame, passing it the key,
-     * value, and specified data.
-     */
-    void for_each_slot(void (*proc)(const char *key, KvpValue *, void *data), void* data) const noexcept;
-
     /** The function should be of the form:
      * <anything> func (char const *, KvpValue *, data_type &);
      * Do not pass nullptr as the function.
diff --git a/libgnucash/engine/qofinstance-p.h b/libgnucash/engine/qofinstance-p.h
index 9967030..c0d79f9 100644
--- a/libgnucash/engine/qofinstance-p.h
+++ b/libgnucash/engine/qofinstance-p.h
@@ -175,18 +175,6 @@ void qof_instance_slot_path_delete_if_empty (QofInstance const *, std::vector<st
 std::vector <std::pair <std::string, KvpValue*>>
 qof_instance_get_slots_prefix (QofInstance const *, std::string const & prefix);
 
-/* Don't pass nullptr as the function */
-template<typename func_type, typename data_type>
-void qof_instance_foreach_slot_temp (QofInstance const * inst, std::string const & path,
-        func_type const & func, data_type & data)
-{
-    auto slot = inst->kvp_data->get_slot({path});
-    if (slot == nullptr || slot->get_type() != KvpValue::Type::FRAME)
-        return;
-    auto frame = slot->get<KvpFrame*>();
-    frame->for_each_slot(func, data);
-}
-
 /**
  * Similar to qof_instance_foreach_slot, but we don't traverse the depth of the key value frame,
  * we only check the root level for keys that match the specified prefix.

commit 4a88f05d1159350812969d952bf3d3d9a5173c9a
Author: lmat <dartme18 at gmail.com>
Date:   Tue Nov 28 15:44:20 2017 -0500

    kvp string: allocate enough space
    
    We need to allocate enough space for the terminating null character.
    Also, I double-checked the documents for std::basic_string::c_str () and
    verified that it does guarantee the terminating null, so I put a comment
    in the code that depends on that.

diff --git a/libgnucash/engine/gnc-budget.c b/libgnucash/engine/gnc-budget.c
index 16c121e..4da6cf1 100644
--- a/libgnucash/engine/gnc-budget.c
+++ b/libgnucash/engine/gnc-budget.c
@@ -489,7 +489,7 @@ void
 gnc_budget_unset_account_period_value(GncBudget *budget, const Account *account,
                                       guint period_num)
 {
-    gchar path_part_one [GUID_ENCODING_LENGTH];
+    gchar path_part_one [GUID_ENCODING_LENGTH + 1];
     gchar path_part_two [GNC_BUDGET_MAX_NUM_PERIODS_DIGITS];
 
     g_return_if_fail (budget != NULL);
@@ -511,7 +511,7 @@ void
 gnc_budget_set_account_period_value(GncBudget *budget, const Account *account,
                                     guint period_num, gnc_numeric val)
 {
-    gchar path_part_one [GUID_ENCODING_LENGTH];
+    gchar path_part_one [GUID_ENCODING_LENGTH + 1];
     gchar path_part_two [GNC_BUDGET_MAX_NUM_PERIODS_DIGITS];
 
     /* Watch out for an off-by-one error here:
@@ -553,7 +553,7 @@ gnc_budget_is_account_period_value_set(const GncBudget *budget,
                                        guint period_num)
 {
     GValue v = G_VALUE_INIT;
-    gchar path_part_one [GUID_ENCODING_LENGTH];
+    gchar path_part_one [GUID_ENCODING_LENGTH + 1];
     gchar path_part_two [GNC_BUDGET_MAX_NUM_PERIODS_DIGITS];
     gconstpointer ptr = NULL;
 
@@ -573,7 +573,7 @@ gnc_budget_get_account_period_value(const GncBudget *budget,
                                     guint period_num)
 {
     gnc_numeric *numeric = NULL;
-    gchar path_part_one [GUID_ENCODING_LENGTH];
+    gchar path_part_one [GUID_ENCODING_LENGTH + 1];
     gchar path_part_two [GNC_BUDGET_MAX_NUM_PERIODS_DIGITS];
     GValue v = G_VALUE_INIT;
 
diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index 5706802..11aaebf 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -177,7 +177,9 @@ guid_to_string_buff (const GncGUID * guid, gchar *str)
 
     gnc::GUID temp {*guid};
     auto val = temp.to_string ();
-    /*We need to be sure to copy the terminating null character.*/
+    /*We need to be sure to copy the terminating null character.
+     * The standard guarantees that std::basic_string::c_str ()
+     * returns with a terminating null character, too.*/
     std::copy (val.c_str (), val.c_str () + val.size () + 1, str);
     return str + val.size ();
 }

commit 5b03182963de1cc5c7e7a3d7cc6fd29ce6339848
Author: lmat <dartme18 at gmail.com>
Date:   Tue Nov 28 15:43:24 2017 -0500

    Correct kvp to_string typo

diff --git a/libgnucash/engine/kvp-value.cpp b/libgnucash/engine/kvp-value.cpp
index a0329d2..990197e 100644
--- a/libgnucash/engine/kvp-value.cpp
+++ b/libgnucash/engine/kvp-value.cpp
@@ -174,7 +174,7 @@ struct to_string_visitor : boost::static_visitor<void>
         {
             output << "(null)";
         }
-        output << " (timespec)";
+        output << " (gnc_numeric)";
     }
 
     void operator()(GncGUID * val)

commit f260a01bfd523886c807b3c901b0700712f577cd
Author: lmat <dartme18 at gmail.com>
Date:   Thu Nov 16 12:57:31 2017 -0500

    Keep tokens as they are, don't translate them
    
    Before, it was necessary to remove '/' from tokens so that they won't be
    divided up within kvp. Now that kvp doesn't parse tokens, it's okay to
    pass '/', and it's better not to translate user-provided tokens if at all
    possible.

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 895ff8f..19d829d 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -5234,10 +5234,8 @@ get_first_pass_probabilities(GncImportMatchMap * imap, GList * tokens)
      * in the input tokens list. */
     for (auto current_token = tokens; current_token; current_token = current_token->next)
     {
-        auto translated_token = std::string {static_cast <char const *> (current_token->data)};
-        std::replace (translated_token.begin (), translated_token.end (), '/', '-');
         token_accounts_info tokenInfo{};
-        auto path = std::string{IMAP_FRAME_BAYES "-"} + translated_token;
+        auto path = std::string{IMAP_FRAME_BAYES "/"} + static_cast <char const *> (current_token->data);
         qof_instance_foreach_slot_prefix (QOF_INSTANCE (imap->acc), path, &build_token_info, tokenInfo);
         for (auto const & current_account_token : tokenInfo.accounts)
         {
@@ -5329,7 +5327,6 @@ change_imap_entry (GncImportMatchMap *imap, std::string const & path, int64_t to
     gnc_features_set_used (imap->book, GNC_FEATURE_GUID_BAYESIAN);
 }
 
-
 /** Updates the imap for a given account using a list of tokens */
 void
 gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
@@ -5369,9 +5366,7 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
         /* start off with one token for this account */
         token_count = 1;
         PINFO("adding token '%s'", (char*)current_token->data);
-        std::string translated_token {static_cast<char*>(current_token->data)};
-        std::replace(translated_token.begin(), translated_token.end(), '/', '-');
-        auto path = std::string {IMAP_FRAME_BAYES} + '-' + translated_token + '-' + guid_string;
+        auto path = std::string {IMAP_FRAME_BAYES} + '/' + static_cast<char*>(current_token->data) + '/' + guid_string;
         /* change the imap entry for the account */
         change_imap_entry (imap, path, token_count);
     }
@@ -5450,7 +5445,7 @@ build_bayes (const char *key, KvpValue * value, imap_info & imapInfo)
         auto temp_guid = gnc::GUID::from_string (std::get <2> (parsed_key));
         GncGUID guid = temp_guid;
         auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
-        std::string category_head {std::get <0> (parsed_key) + "-" + std::get <1> (parsed_key)};
+        std::string category_head {std::get <0> (parsed_key) + "/" + std::get <1> (parsed_key)};
         auto imap_node = static_cast <imap_info*> (g_malloc (sizeof (imap_info)));
         auto count = entry.second->get <int64_t> ();
         imap_node->source_account = imapInfo.source_account;
@@ -5609,7 +5604,6 @@ convert_entry (std::pair <std::vector <std::string>, KvpValue*> entry, Account*
     entry.first.emplace_back (guid_str);
     std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
     new_key = IMAP_FRAME_BAYES + new_key;
-    std::replace (new_key.begin(), new_key.end(), '/', '-');
     return {new_key, entry.second};
 }
 
diff --git a/libgnucash/engine/test/gtest-import-map.cpp b/libgnucash/engine/test/gtest-import-map.cpp
index d811574..df72445 100644
--- a/libgnucash/engine/test/gtest-import-map.cpp
+++ b/libgnucash/engine/test/gtest-import-map.cpp
@@ -254,12 +254,12 @@ TEST_F(ImapBayesTest, FindAccountBayes)
     auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2));
     auto value = new KvpValue(INT64_C(42));
 
-    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid}, value);
-    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid}, new KvpValue{*value});
-    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid}, new KvpValue{*value});
-    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid}, new KvpValue{*value});
-    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid}, new KvpValue{*value});
-    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + acct1_guid}, value);
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "/" + bar + "/" + acct1_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "/" + baz + "/" + acct2_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "/" + waldo + "/" + acct2_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "/" + pepper + "/" + acct1_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + acct2_guid}, new KvpValue{*value});
 
     auto account = gnc_account_imap_find_account_bayes(t_imap, t_list1);
     EXPECT_EQ(t_expense_account1, account);
@@ -292,29 +292,29 @@ TEST_F(ImapBayesTest, AddAccountBayes)
     auto root = qof_instance_get_slots(QOF_INSTANCE(t_bank_account));
     auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1));
     auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2));
-    auto value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-foo-bar"});
+    auto value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/foo/bar"});
     auto check_account = [this](KvpValue* v) {
         return (v->get<const char*>(), this->t_imap->book); };
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + bar + "/" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + baz + "/" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + waldo + "/" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + pepper + "/" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct1_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + baz + "/" + acct1_guid});
     EXPECT_EQ(nullptr, value);
 
     qof_instance_increase_editlevel(QOF_INSTANCE(t_bank_account));
     gnc_account_imap_add_account_bayes(t_imap, t_list2, t_expense_account2);
     qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
     qof_instance_reset_editlevel(QOF_INSTANCE(t_bank_account));
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + baz + "/" + acct2_guid});
     EXPECT_EQ(2, value->get<int64_t>());
 }
 
@@ -335,16 +335,16 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     auto val2 = new KvpValue(static_cast<int64_t>(5));
     auto val3 = new KvpValue(static_cast<int64_t>(2));
     // Test for existing entries, all will be 1
-    auto value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid});
+    auto value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + bar + "/" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + baz + "/" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + waldo + "/" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
     // Set up some old entries
-    root->set_path({IMAP_FRAME_BAYES, "token", "with", "slashes", "Asset-Bank"}, val1);
+    root->set_path({IMAP_FRAME_BAYES, "severely", "divided", "token", "Asset-Bank"}, val1);
     root->set_path({IMAP_FRAME_BAYES, salt, "Asset-Bank#Bank"}, new KvpValue{*val1});
     root->set_path({IMAP_FRAME_BAYES, salt, "Asset>Bank#Bank"}, val2);
     root->set_path({IMAP_FRAME_BAYES, pork, "Expense#Food"}, new KvpValue{*val2});
@@ -356,19 +356,19 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     // Start Convert
     gnc_account_imap_convert_bayes (t_imap->book);
     // convert from 'Asset-Bank' to 'Asset-Bank' guid
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-token-with-slashes-" + acct3_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/severely/divided/token/" + acct3_guid});
     EXPECT_EQ(10, value->get<int64_t>());
     // convert from 'Asset-Bank#Bank' to 'Sav Bank' guid
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct4_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + acct4_guid});
     EXPECT_EQ(10, value->get<int64_t>());
     // convert from 'Expense#Food' to 'Food' guid
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + pork + "-" + acct1_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + pork + "/" + acct1_guid});
     EXPECT_EQ(5, value->get<int64_t>());
     // convert from 'Expense#Drink' to 'Drink' guid
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + sausage + "-" + acct2_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + sausage + "/" + acct2_guid});
     EXPECT_EQ(2, value->get<int64_t>());
     // convert from 'Expense#Food' to 'Food' guid but add to original value
-    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid});
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + acct1_guid});
     EXPECT_EQ(5, value->get<int64_t>());
     // Check for run once flag
     auto vals = book->get_slot({"changed-bayesian-to-guid"});
@@ -402,9 +402,9 @@ TEST_F (ImapBayesTest, get_bayes_info)
     EXPECT_EQ (info->source_account, t_bank_account);
     EXPECT_EQ (info->map_account, t_expense_account1);
     auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1)); //Food
-    EXPECT_STREQ (info->full_category, (std::string {IMAP_FRAME_BAYES} + "-one-two-three-" + acct1_guid).c_str ());
-    EXPECT_STREQ (info->match_string, "one-two-three");
-    EXPECT_STREQ (info->category_head, (std::string {IMAP_FRAME_BAYES} + "-one-two-three").c_str ());
+    EXPECT_STREQ (info->full_category, (std::string {IMAP_FRAME_BAYES} + "/one/two/three/" + acct1_guid).c_str ());
+    EXPECT_STREQ (info->match_string, "one/two/three");
+    EXPECT_STREQ (info->category_head, (std::string {IMAP_FRAME_BAYES} + "/one/two/three").c_str ());
     EXPECT_STREQ (info->count, "1");
 }
 

commit 551549598a45aff0accef9125b8100f7cf0f1330
Author: lmat <dartme18 at gmail.com>
Date:   Wed Nov 15 15:24:45 2017 -0500

    Corrected memory management issue

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 3ee7882..895ff8f 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -5326,10 +5326,6 @@ change_imap_entry (GncImportMatchMap *imap, std::string const & path, int64_t to
 
     // Add or Update the entry based on guid
     qof_instance_set_path_kvp (QOF_INSTANCE (imap->acc), &value, {path});
-
-    /* Set a feature flag in the book for the change to use guid.
-     * This will prevent older GnuCash versions that don't support this feature
-     * from opening this file. */
     gnc_features_set_used (imap->book, GNC_FEATURE_GUID_BAYESIAN);
 }
 
@@ -5605,7 +5601,8 @@ get_guid_from_account_name (Account * root, std::string const & name)
 static std::pair <std::string, KvpValue*>
 convert_entry (std::pair <std::vector <std::string>, KvpValue*> entry, Account* root)
 {
-    auto const & account_name = entry.first.back();
+    /*We need to make a copy here.*/
+    auto account_name = entry.first.back();
     entry.first.pop_back();
     auto guid_str = get_guid_from_account_name (root, account_name);
     entry.first.emplace_back ("/");
@@ -5632,20 +5629,26 @@ get_new_guid_imap (Account * acc)
     return ret;
 }
 
-static void
+static bool
 convert_imap_account_bayes_to_guid (Account *acc)
 {
     auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
     if (!frame->get_keys().size())
-        return;
+        return false;
     auto new_imap = get_new_guid_imap(acc);
     xaccAccountBeginEdit(acc);
     frame->set({IMAP_FRAME_BAYES}, nullptr);
+    if (!new_imap.size ())
+    {
+        xaccAccountCommitEdit(acc);
+        return false;
+    }
     std::for_each(new_imap.begin(), new_imap.end(), [&frame] (std::pair<std::string, KvpValue*> const & entry) {
         frame->set({entry.first.c_str()}, entry.second);
     });
     qof_instance_set_dirty (QOF_INSTANCE (acc));
     xaccAccountCommitEdit(acc);
+    return true;
 }
 
 char const * run_once_key_to_guid {"changed-bayesian-to-guid"};
@@ -5658,7 +5661,8 @@ imap_convert_bayes_to_guid (QofBook * book)
     for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
     {
         Account *acc = static_cast <Account*> (ptr->data);
-        convert_imap_account_bayes_to_guid (acc);
+        if (convert_imap_account_bayes_to_guid (acc))
+            gnc_features_set_used (book, GNC_FEATURE_GUID_BAYESIAN);
     }
     g_list_free (accts);
 }

commit d9eebd332b52b774f3ad5b4f0c85ce7ba0419381
Author: lmat <dartme18 at gmail.com>
Date:   Thu Nov 9 16:33:58 2017 -0500

    Renaming functions to get rid of temporary name
    
    _var_ was used to make sure I caught all references, but isn't intended
    as a permanent name.

diff --git a/libgnucash/engine/Scrub.c b/libgnucash/engine/Scrub.c
index a036fff..b4b744b 100644
--- a/libgnucash/engine/Scrub.c
+++ b/libgnucash/engine/Scrub.c
@@ -50,7 +50,7 @@
 #include "Transaction.h"
 #include "TransactionP.h"
 #include "gnc-commodity.h"
-#include <qofinstance-p.h>
+#include "qofinstance-p.h"
 
 #undef G_LOG_DOMAIN
 #define G_LOG_DOMAIN "gnc.engine.scrub"
@@ -1342,7 +1342,7 @@ xaccAccountScrubKvp (Account *account)
     {
         str2 = g_strstrip(g_value_dup_string(&v));
         if (strlen(str2) == 0)
-            qof_instance_slot_var_delete (QOF_INSTANCE (account), 1, "notes");
+            qof_instance_slot_delete (QOF_INSTANCE (account), "notes");
         g_free(str2);
     }
 
@@ -1350,9 +1350,9 @@ xaccAccountScrubKvp (Account *account)
     if ((G_VALUE_HOLDS_STRING (&v) &&
         strcmp(g_value_get_string (&v), "false") == 0) ||
         (G_VALUE_HOLDS_BOOLEAN (&v) && ! g_value_get_boolean (&v)))
-        qof_instance_slot_var_delete (QOF_INSTANCE (account), 1, "placeholder");
+        qof_instance_slot_delete (QOF_INSTANCE (account), "placeholder");
 
-    qof_instance_slot_var_delete_if_empty (QOF_INSTANCE (account), 1, "hbci");
+    qof_instance_slot_delete_if_empty (QOF_INSTANCE (account), "hbci");
 }
 
 /* ================================================================ */
diff --git a/libgnucash/engine/qofinstance-p.h b/libgnucash/engine/qofinstance-p.h
index 9fd5e4f..9967030 100644
--- a/libgnucash/engine/qofinstance-p.h
+++ b/libgnucash/engine/qofinstance-p.h
@@ -153,8 +153,8 @@ gboolean qof_instance_kvp_has_guid (const QofInstance *inst, const char *path,
 void qof_instance_kvp_merge_guids (const QofInstance *target,
                                    const QofInstance *donor, const char* path);
 gboolean qof_instance_has_slot (const QofInstance *inst, const char *path);
-void qof_instance_slot_var_delete (const QofInstance *, unsigned count, ...);
-void qof_instance_slot_var_delete_if_empty (const QofInstance *, unsigned count, ...);
+void qof_instance_slot_delete (const QofInstance *, const char * path);
+void qof_instance_slot_delete_if_empty (const QofInstance *, const char * path);
 void qof_instance_foreach_slot (const QofInstance *inst, const char *path,
                                 void(*proc)(const char*, const GValue*, void*),
                                 void* data);
diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp
index 7a3b6bf..57cc6bd 100644
--- a/libgnucash/engine/qofinstance.cpp
+++ b/libgnucash/engine/qofinstance.cpp
@@ -1295,15 +1295,9 @@ void qof_instance_slot_path_delete (QofInstance const * inst, std::vector<std::s
 }
 
 void
-qof_instance_slot_var_delete (QofInstance const *inst, unsigned count, ...)
+qof_instance_slot_delete (QofInstance const *inst, char const * path)
 {
-    std::vector<std::string> path;
-    va_list args;
-    va_start (args, count);
-    for (unsigned i{0}; i < count; ++i)
-        path.push_back (va_arg (args, char const *));
-    va_end (args);
-    delete inst->kvp_data->set (path, nullptr);
+    delete inst->kvp_data->set ({path}, nullptr);
 }
 
 void qof_instance_slot_path_delete_if_empty (QofInstance const * inst, std::vector<std::string> const & path)
@@ -1318,20 +1312,14 @@ void qof_instance_slot_path_delete_if_empty (QofInstance const * inst, std::vect
 }
 
 void
-qof_instance_slot_var_delete_if_empty (QofInstance const *inst, unsigned count, ...)
+qof_instance_slot_delete_if_empty (QofInstance const *inst, char const * path)
 {
-    std::vector<std::string> path;
-    va_list args;
-    va_start (args, count);
-    for (unsigned i{0}; i < count; ++i)
-        path.push_back (va_arg (args, char const *));
-    va_end (args);
-    auto slot = inst->kvp_data->get_slot (path);
+    auto slot = inst->kvp_data->get_slot ({path});
     if (slot)
     {
         auto frame = slot->get <KvpFrame*> ();
         if (frame && frame->empty ())
-            delete inst->kvp_data->set (path, nullptr);
+            delete inst->kvp_data->set ({path}, nullptr);
     }
 }
 

commit 5636afc4a298c355a5ffb6f849a21977aba3ac9a
Author: lmat <dartme18 at gmail.com>
Date:   Mon Nov 6 14:51:25 2017 -0500

    Kvp no longer parses entries looking for delimiters

diff --git a/libgnucash/app-utils/test/test-option-util.cpp b/libgnucash/app-utils/test/test-option-util.cpp
index fd560fe..4c7d358 100644
--- a/libgnucash/app-utils/test/test-option-util.cpp
+++ b/libgnucash/app-utils/test/test-option-util.cpp
@@ -69,7 +69,7 @@ setup_kvp (Fixture *fixture, gconstpointer pData)
                      "autoreadonly-days", (double)21,
                      NULL);
 
-    slots->set_path("options/Business/Company Name",
+    slots->set_path({"options", "Business", "Company Name"},
                new KvpValue("Bogus Company"));
     qof_commit_edit (QOF_INSTANCE (book));
 }
@@ -120,10 +120,10 @@ test_option_save (Fixture *fixture, gconstpointer pData)
 					       OPTION_NAME_AUTO_READONLY_DAYS,
 					       17));
     qof_book_save_options (book, gnc_option_db_save, odb, TRUE);
-    g_assert_cmpstr (slots->get_slot("options/Accounts/Use Trading Accounts")->get<const char*>(), == , "t");
-    g_assert_cmpstr (slots->get_slot("options/Accounts/Use Split Action Field for Number")->get<const char*>(), == , "t");
-    g_assert_cmpstr (slots->get_slot("options/Business/Company Name")->get<const char*>(), ==, "Bogus Company");
-    g_assert_cmpfloat (slots->get_slot("options/Accounts/Day Threshold for Read-Only Transactions (red line)")->get<double>(), ==, 17);
+    g_assert_cmpstr (slots->get_slot({"options", "Accounts", "Use Trading Accounts"})->get<const char*>(), == , "t");
+    g_assert_cmpstr (slots->get_slot({"options", "Accounts", "Use Split Action Field for Number"})->get<const char*>(), == , "t");
+    g_assert_cmpstr (slots->get_slot({"options", "Business", "Company Name"})->get<const char*>(), ==, "Bogus Company");
+    g_assert_cmpfloat (slots->get_slot({"options", "Accounts", "Day Threshold for Read-Only Transactions (red line)"})->get<double>(), ==, 17);
 
     gnc_option_db_destroy (odb);
 }
diff --git a/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp b/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp
index 245ca95..8c0d75e 100644
--- a/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp
+++ b/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp
@@ -132,15 +132,13 @@ setup_memory (Fixture* fixture, gconstpointer pData)
     xaccAccountSetCommodity (acct1, currency);
 
     auto frame = qof_instance_get_slots (QOF_INSTANCE (acct1));
-    frame->set ("int64-val", new KvpValue (INT64_C (100)));
-    frame->set ("double-val", new KvpValue (3.14159));
-    frame->set ("numeric-val", new KvpValue (gnc_numeric_zero ()));
-
-    frame->set ("timespec-val", new KvpValue (timespec_now ()));
-
-    frame->set ("string-val", new KvpValue ("abcdefghijklmnop"));
+    frame->set ({"int64-val"}, new KvpValue (INT64_C (100)));
+    frame->set ({"double-val"}, new KvpValue (3.14159));
+    frame->set ({"numeric-val"}, new KvpValue (gnc_numeric_zero ()));
+    frame->set ({"timespec-val"}, new KvpValue (timespec_now ()));
+    frame->set ({"string-val"}, new KvpValue ("abcdefghijklmnop"));
     auto guid = qof_instance_get_guid (QOF_INSTANCE (acct1));
-    frame->set ("guid-val", new KvpValue (const_cast<GncGUID*> (guid_copy (
+    frame->set ({"guid-val"}, new KvpValue (const_cast<GncGUID*> (guid_copy (
             guid))));
 
     gnc_account_append_child (root, acct1);
diff --git a/libgnucash/backend/sql/gnc-slots-sql.cpp b/libgnucash/backend/sql/gnc-slots-sql.cpp
index 30563a5..c8b36ff 100644
--- a/libgnucash/backend/sql/gnc-slots-sql.cpp
+++ b/libgnucash/backend/sql/gnc-slots-sql.cpp
@@ -226,7 +226,7 @@ set_slot_from_value (slot_info_t* pInfo, KvpValue* pValue)
     case FRAME:
     {
         auto key = get_key_from_path (pInfo->path);
-        pInfo->pKvpFrame->set (key.c_str(), pValue);
+        pInfo->pKvpFrame->set ({key.c_str()}, pValue);
         break;
     }
     case LIST:
@@ -245,7 +245,7 @@ set_slot_from_value (slot_info_t* pInfo, KvpValue* pValue)
             frame->set_path ({path.c_str(), key.c_str()}, pValue);
         }
         else
-            frame->set (key.c_str(), pValue);
+            frame->set ({key.c_str()}, pValue);
         break;
     }
     }
@@ -464,7 +464,7 @@ set_guid_val (gpointer pObject,  gpointer pValue)
 
         slots_load_info (newInfo);
         pValue = new KvpValue {newInfo->pList};
-        pInfo->pKvpFrame->set (key.c_str(), pValue);
+        pInfo->pKvpFrame->set ({key.c_str()}, pValue);
 	delete newInfo;
         break;
     }
@@ -487,7 +487,7 @@ set_guid_val (gpointer pObject,  gpointer pValue)
         default:
         {
             auto key = get_key_from_path (pInfo->path);
-            pInfo->pKvpFrame->set (key.c_str(), new KvpValue {newFrame});
+            pInfo->pKvpFrame->set ({key.c_str()}, new KvpValue {newFrame});
             break;
         }
         }
diff --git a/libgnucash/backend/xml/io-gncxml-v1.cpp b/libgnucash/backend/xml/io-gncxml-v1.cpp
index df50648..53d6039 100644
--- a/libgnucash/backend/xml/io-gncxml-v1.cpp
+++ b/libgnucash/backend/xml/io-gncxml-v1.cpp
@@ -808,7 +808,7 @@ kvp_frame_slot_end_handler (gpointer data_for_children,
     if (key_node_count != 1) return (FALSE);
 
     value_cr->should_cleanup = TRUE;
-    f->set (key, value);
+    f->set ({key}, value);
     if (delete_value)
         delete value;
     return (TRUE);
diff --git a/libgnucash/backend/xml/sixtp-dom-parsers.cpp b/libgnucash/backend/xml/sixtp-dom-parsers.cpp
index af4b197..fa0dca1 100644
--- a/libgnucash/backend/xml/sixtp-dom-parsers.cpp
+++ b/libgnucash/backend/xml/sixtp-dom-parsers.cpp
@@ -440,7 +440,7 @@ dom_tree_to_kvp_frame_given (xmlNodePtr node, KvpFrame* frame)
                 if (val)
                 {
                     //We're deleting the old KvpValue returned by replace_nc().
-                    delete frame->set (key, val);
+                    delete frame->set ({key}, val);
                 }
                 else
                 {
diff --git a/libgnucash/backend/xml/test/test-kvp-frames.cpp b/libgnucash/backend/xml/test/test-kvp-frames.cpp
index b9736ce..508d600 100644
--- a/libgnucash/backend/xml/test/test-kvp-frames.cpp
+++ b/libgnucash/backend/xml/test/test-kvp-frames.cpp
@@ -25,7 +25,7 @@ test_kvp_get_slot (int run,
                    KvpFrame* test_frame1, const KvpValue* test_val1,
                    const gchar* test_key)
 {
-    auto test_val2 = test_frame1->get_slot (test_key);
+    auto test_val2 = test_frame1->get_slot ({test_key});
     auto msg = "KvpFrame::get_slot";
     if (compare (test_val1, test_val2) == 0)
     {
@@ -70,7 +70,7 @@ test_kvp_copy_get_slot (int run,
                         const gchar* test_key)
 {
     auto test_frame2 = new KvpFrame (*test_frame1);
-    auto test_val2 = test_frame2->get_slot (test_key);
+    auto test_val2 = test_frame2->get_slot ({test_key});
     auto msg = "KvpFrame::get_slot() from a copy-constructed frame";
     if (compare (test_val1, test_val2) == 0)
     {
@@ -114,7 +114,7 @@ test_kvp_frames1 (void)
         auto test_frame1 = new KvpFrame;
         auto test_key = get_random_string_without ("/");
 
-        test_frame1->set (test_key, test_val1);
+        test_frame1->set ({test_key}, test_val1);
 
         test_kvp_get_slot (i, test_frame1, test_val1, test_key);
         test_kvp_copy_compare (i, test_frame1, test_val1, test_key);
diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 1208425..3ee7882 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -5440,7 +5440,7 @@ parse_bayes_imap_info (std::string const & imap_bayes_entry)
     auto guid_start = imap_bayes_entry.size() - GUID_ENCODING_LENGTH;
     std::string keyword {imap_bayes_entry.substr (header_length + 1, guid_start - header_length - 2)};
     std::string account_guid {imap_bayes_entry.substr (guid_start)};
-    return {header, keyword, account_guid};
+    return std::tuple <std::string, std::string, std::string> {header, keyword, account_guid};
 }
 
 static void
@@ -5530,9 +5530,9 @@ gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty)
     {
         xaccAccountBeginEdit (acc);
         if (empty)
-            qof_instance_slot_delete_if_empty (QOF_INSTANCE(acc), kvp_path);
+            qof_instance_slot_path_delete_if_empty (QOF_INSTANCE(acc), {kvp_path});
         else
-            qof_instance_slot_delete (QOF_INSTANCE(acc), kvp_path);
+            qof_instance_slot_path_delete (QOF_INSTANCE(acc), {kvp_path});
         PINFO("Account is '%s', path is '%s'", xaccAccountGetName (acc), kvp_path);
         qof_instance_set_dirty (QOF_INSTANCE(acc));
         xaccAccountCommitEdit (acc);
@@ -5620,7 +5620,7 @@ static std::vector<std::pair<std::string, KvpValue*>>
 get_new_guid_imap (Account * acc)
 {
     auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
-    auto slot = frame->get_slot (IMAP_FRAME_BAYES);
+    auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
     if (!slot)
         return {};
     auto imap_frame = slot->get<KvpFrame*> ();
@@ -5640,9 +5640,9 @@ convert_imap_account_bayes_to_guid (Account *acc)
         return;
     auto new_imap = get_new_guid_imap(acc);
     xaccAccountBeginEdit(acc);
-    frame->set(IMAP_FRAME_BAYES, nullptr);
+    frame->set({IMAP_FRAME_BAYES}, nullptr);
     std::for_each(new_imap.begin(), new_imap.end(), [&frame] (std::pair<std::string, KvpValue*> const & entry) {
-        frame->set(entry.first.c_str(), entry.second);
+        frame->set({entry.first.c_str()}, entry.second);
     });
     qof_instance_set_dirty (QOF_INSTANCE (acc));
     xaccAccountCommitEdit(acc);
diff --git a/libgnucash/engine/gnc-aqbanking-templates.cpp b/libgnucash/engine/gnc-aqbanking-templates.cpp
index fe2c37d..ad6b8fd 100644
--- a/libgnucash/engine/gnc-aqbanking-templates.cpp
+++ b/libgnucash/engine/gnc-aqbanking-templates.cpp
@@ -106,13 +106,13 @@ KvpFrame*
 _GncABTransTempl::make_kvp_frame()
 {
     auto frame = new KvpFrame;
-    frame->set(TT_NAME, new KvpValue(m_name.c_str()));
-    frame->set(TT_RNAME, new KvpValue(m_recipient_name.c_str()));
-    frame->set(TT_RACC, new KvpValue(m_recipient_account.c_str()));
-    frame->set(TT_RBCODE, new KvpValue(m_recipient_bankcode.c_str()));
-    frame->set(TT_AMOUNT, new KvpValue(m_amount));
-    frame->set(TT_PURPOS, new KvpValue(m_purpose.c_str()));
-    frame->set(TT_PURPOSCT, new KvpValue(m_purpose_continuation.c_str()));
+    frame->set({TT_NAME}, new KvpValue(m_name.c_str()));
+    frame->set({TT_RNAME}, new KvpValue(m_recipient_name.c_str()));
+    frame->set({TT_RACC}, new KvpValue(m_recipient_account.c_str()));
+    frame->set({TT_RBCODE}, new KvpValue(m_recipient_bankcode.c_str()));
+    frame->set({TT_AMOUNT}, new KvpValue(m_amount));
+    frame->set({TT_PURPOS}, new KvpValue(m_purpose.c_str()));
+    frame->set({TT_PURPOSCT}, new KvpValue(m_purpose_continuation.c_str()));
     return frame;
 }
 
@@ -145,12 +145,12 @@ gnc_ab_trans_templ_list_new_from_book(QofBook *b)
     {
         KvpFrame *frame = static_cast<KvpValue*>(node->data)->get<KvpFrame*>();
         auto c_func = [frame](const char* key)
-            { auto slot = frame->get_slot(key);
+            { auto slot = frame->get_slot({key});
               return slot == nullptr ? std::string("") : std::string(slot->get<const char*>());};
         auto n_func = [frame](const char* key)
-            { auto slot = frame->get_slot(key);
+            { auto slot = frame->get_slot({key});
               return slot == nullptr ? gnc_numeric_zero() : slot->get<gnc_numeric>();};
-        auto amt_slot = frame->get_slot(TT_AMOUNT);
+        auto amt_slot = frame->get_slot({TT_AMOUNT});
         auto templ = new _GncABTransTempl (c_func(TT_NAME), c_func(TT_RNAME),
                                            c_func(TT_RACC), c_func(TT_RBCODE),
                                            n_func(TT_AMOUNT), c_func(TT_PURPOS),
diff --git a/libgnucash/engine/kvp-frame.cpp b/libgnucash/engine/kvp-frame.cpp
index eff478a..9687cc1 100644
--- a/libgnucash/engine/kvp-frame.cpp
+++ b/libgnucash/engine/kvp-frame.cpp
@@ -68,124 +68,90 @@ KvpFrameImpl::~KvpFrameImpl() noexcept
     m_valuemap.clear();
 }
 
-static inline Path
-make_vector(std::string key)
+KvpFrame *
+KvpFrame::get_child_frame_or_nullptr (Path const & path) noexcept
 {
-    Path path;
-    for (auto length = key.find(delim); length != std::string::npos;)
-    {
-        if (length != 0)
-            path.push_back(key.substr(0, length));
-        key = key.substr(length + 1);
-        length = key.find(delim);
-    }
-    if (!key.empty())
-	path.push_back(key);
-    return path;
+    if (!path.size ())
+        return this;
+    auto key = path.front ();
+    if (m_valuemap.find (key.c_str ()) == m_valuemap.end ())
+        return nullptr;
+    auto child = m_valuemap.at (key.c_str ())->get <KvpFrame *> ();
+    Path send;
+    std::copy (path.begin () + 1, path.end (), std::back_inserter (send));
+    return child->get_child_frame_or_nullptr (send);
 }
 
-/*
- * If the key is delimited, calls set(path) with the parsed result
- * otherwise, inserts the key and value locally and returns the
- * old value if there was one.
- */
-KvpValue*
-KvpFrameImpl::set(const char* key, KvpValue* value) noexcept
+KvpFrame *
+KvpFrame::get_child_frame_or_create (Path const & path) noexcept
 {
-    if (!key) return nullptr;
-    if (strchr(key, delim))
-        return set(make_vector(key), value);
-    KvpValue* ret {nullptr};
-    auto spot = m_valuemap.find(key);
-    if (spot != m_valuemap.end())
+    if (!path.size ())
+        return this;
+    auto key = path.front ();
+    auto spot = m_valuemap.find (key.c_str ());
+    if (spot == m_valuemap.end () || spot->second->get_type () != KvpValue::Type::FRAME)
+        delete set_impl (key.c_str (), new KvpValue {new KvpFrame});
+    Path send;
+    std::copy (path.begin () + 1, path.end (), std::back_inserter (send));
+    auto child_val = m_valuemap.at (key.c_str ());
+    auto child = child_val->get <KvpFrame *> ();
+    return child->get_child_frame_or_create (send);
+}
+
+
+KvpValue *
+KvpFrame::set_impl (std::string const & key, KvpValue * value) noexcept
+{
+    KvpValue * ret {};
+    auto spot = m_valuemap.find (key.c_str ());
+    if (spot != m_valuemap.end ())
     {
-        qof_string_cache_remove(spot->first);
+        qof_string_cache_remove (spot->first);
         ret = spot->second;
-        m_valuemap.erase(spot);
+        m_valuemap.erase (spot);
     }
-
     if (value)
     {
-        auto cachedkey =
-            static_cast<const char *>(qof_string_cache_insert(key));
-        m_valuemap.insert({cachedkey,value});
+        auto cachedkey = static_cast <char const *> (qof_string_cache_insert (key.c_str ()));
+        m_valuemap.emplace (cachedkey, value);
     }
-
     return ret;
 }
 
-static inline KvpFrameImpl*
-walk_path_or_nullptr(const KvpFrameImpl* frame, Path& path)
+KvpValue *
+KvpFrameImpl::set (Path path, KvpValue* value) noexcept
 {
-    KvpFrameImpl* cur_frame = const_cast<KvpFrameImpl*>(frame);
-    for(auto key:path)
-    {
-        auto slot = cur_frame->get_slot(key.c_str());
-        if (slot == nullptr || slot->get_type() != KvpValue::Type::FRAME)
-            return nullptr;
-        cur_frame = slot->get<KvpFrame*>();
-    }
-    return cur_frame;
+    auto key = path.back ();
+    path.pop_back ();
+    auto target = get_child_frame_or_nullptr (path);
+    if (!target)
+        return nullptr;
+    return target->set_impl (key, value);
 }
 
-/*
- * If the last path parameter has a delimiter, the path before that point is ignored,
- * and set is called with only the last parameter with the delimiter as the key.
- */
-KvpValue*
-KvpFrameImpl::set(Path path, KvpValue* value) noexcept
+KvpValue *
+KvpFrameImpl::set_path (Path path, KvpValue* value) noexcept
 {
-    auto last_key = path.back();
+    auto key = path.back();
     path.pop_back();
-    auto cur_frame = walk_path_or_nullptr(this, path);
-    if (cur_frame == nullptr)
+    auto target = get_child_frame_or_create (path);
+    if (!target)
         return nullptr;
-    if (last_key.find(delim) != std::string::npos)
-        return set(make_vector(last_key), value);
-    return cur_frame->set(last_key.c_str(), value);
+    return target->set_impl (key, value);
 }
 
-static inline KvpFrameImpl*
-walk_path_and_create(KvpFrameImpl* frame, Path path)
+KvpValue *
+KvpFrameImpl::get_slot (Path path) noexcept
 {
-     for(auto key:path)
-    {
-	if (key.empty())
-	    continue;
-        if (key.find(delim) != std::string::npos)
-        {
-            frame = walk_path_and_create(frame, make_vector(key));
-            continue;
-        }
-        auto slot = frame->get_slot(key.c_str());
-        if (slot == nullptr || slot->get_type() != KvpValue::Type::FRAME)
-        {
-            auto new_frame = new KvpFrame;
-            delete frame->set(key.c_str(), new KvpValue{new_frame});
-            frame = new_frame;
-            continue;
-        }
-        frame = slot->get<KvpFrame*>();
-    }
-    return frame;
-}
-
-KvpValue*
-KvpFrameImpl::set_path(const char* path, KvpValue* value) noexcept
-{
-    return set_path(make_vector(path), value);
-}
-
-KvpValue*
-KvpFrameImpl::set_path(Path path, KvpValue* value) noexcept
-{
-    auto cur_frame = this;
-    auto last_key = path.back();
+    auto key = path.back();
     path.pop_back();
-    cur_frame = walk_path_and_create(const_cast<KvpFrame*>(this), path);
-    if (last_key.find(delim) != std::string::npos)
-        return set_path(make_vector(last_key), value);
-    return cur_frame->set(last_key.c_str(), value);
+    auto target = get_child_frame_or_nullptr (path);
+    if (!target)
+        return nullptr;
+    auto spot = target->m_valuemap.find (key.c_str ());
+    if (spot != target->m_valuemap.end ())
+        return spot->second;
+    return nullptr;
 }
 
 std::string
@@ -231,32 +197,6 @@ KvpFrameImpl::get_keys() const noexcept
     return ret;
 }
 
-KvpValueImpl *
-KvpFrameImpl::get_slot(const char * key) const noexcept
-{
-    if (!key) return nullptr;
-    if (strchr(key, delim))
-        return get_slot(make_vector(key));
-    auto spot = m_valuemap.find(key);
-    if (spot == m_valuemap.end())
-        return nullptr;
-    return spot->second;
-}
-
-KvpValueImpl *
-KvpFrameImpl::get_slot(Path path) const noexcept
-{
-    auto last_key = path.back();
-    path.pop_back();
-    auto cur_frame = walk_path_or_nullptr(this, path);
-    if (cur_frame == nullptr)
-        return nullptr;
-    if (last_key.find(delim) != std::string::npos)
-        return get_slot(make_vector(last_key));
-    return cur_frame->get_slot(last_key.c_str());
-
-}
-
 int compare(const KvpFrameImpl * one, const KvpFrameImpl * two) noexcept
 {
     if (one && !two) return 1;
@@ -483,7 +423,7 @@ gnc_value_list_get_type (void)
 }
 
 void
-KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <std::pair <std::vector <std::string>, KvpValue*>> & entries) const
+KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <std::pair <std::vector <std::string>, KvpValue*>> & entries) const noexcept
 {
     for (auto const & entry : m_valuemap)
     {
@@ -504,7 +444,7 @@ KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <std::pai
 }
 
 std::vector <std::pair <std::vector <std::string>, KvpValue*>>
-KvpFrame::flatten_kvp(void) const
+KvpFrame::flatten_kvp(void) const noexcept
 {
     std::vector <std::pair <std::vector <std::string>, KvpValue*>> ret;
     flatten_kvp_impl({}, ret);
diff --git a/libgnucash/engine/kvp-frame.hpp b/libgnucash/engine/kvp-frame.hpp
index 6bdb16f..32dd102 100644
--- a/libgnucash/engine/kvp-frame.hpp
+++ b/libgnucash/engine/kvp-frame.hpp
@@ -141,7 +141,7 @@ struct KvpFrameImpl
      * @param newvalue: The value to set at key.
      * @return The old value if there was one or nullptr.
      */
-    KvpValue* set(const char * key, KvpValue* newvalue) noexcept;
+    //KvpValue* set(const char * key, KvpValue* newvalue) noexcept;
     /**
      * Set the value with the key in a subframe following the keys in path,
      * replacing and returning the old value if it exists or nullptr if it
@@ -156,18 +156,6 @@ struct KvpFrameImpl
      * @return The old value if there was one or nullptr.
      */
     KvpValue* set(Path path, KvpValue* newvalue) noexcept;
-    /**
-     * Set the value with the key in a subframe following the keys in path,
-     * replacing and returning the old value if it exists or nullptr if it
-     * doesn't. Creates any missing intermediate frames. Takes
-     * ownership of new value and releases ownership of the returned old
-     * value. Values must be allocated on the free store with operator new.
-     * @param path: The path of subframes as a '/'-delimited string leading to
-     * the frame in which to insert/replace.
-     * @param newvalue: The value to set at key.
-     * @return The old value if there was one or nullptr.
-     */
-    KvpValue* set_path(const char* path, KvpValue* newvalue) noexcept;
      /**
      * Set the value with the key in a subframe following the keys in path,
      * replacing and returning the old value if it exists or nullptr if it
@@ -198,16 +186,11 @@ struct KvpFrameImpl
      */
     std::vector<std::string> get_keys() const noexcept;
 
-    /** Get the value for the key or nullptr if it doesn't exist.
-     * @param key: The key.
-     * @return The value at the key or nullptr.
-     */
-    KvpValue* get_slot(const char * key) const noexcept;
     /** Get the value for the tail of the path or nullptr if it doesn't exist.
      * @param path: Path of keys leading to the desired value.
      * @return The value at the key or nullptr.
      */
-    KvpValue* get_slot(Path keys) const noexcept;
+    KvpValue* get_slot(Path keys) noexcept;
 
     /**
      * proc is called with each of the immediate contents of this frame, passing it the key,
@@ -240,7 +223,7 @@ struct KvpFrameImpl
      * the frame-containing values.
      */
     std::vector <std::pair <std::vector <std::string>, KvpValue*>>
-    flatten_kvp(void) const;
+    flatten_kvp(void) const noexcept;
 
     /** Test for emptiness
      * @return true if the frame contains nothing.
@@ -251,7 +234,10 @@ struct KvpFrameImpl
     private:
     map_type m_valuemap;
 
-    void flatten_kvp_impl(std::vector <std::string>, std::vector <std::pair <std::vector <std::string>, KvpValue*>> &) const;
+    KvpFrame * get_child_frame_or_nullptr (Path const &) noexcept;
+    KvpFrame * get_child_frame_or_create (Path const &) noexcept;
+    void flatten_kvp_impl(std::vector <std::string>, std::vector <std::pair <std::vector <std::string>, KvpValue*>> &) const noexcept;
+    KvpValue * set_impl (std::string const &, KvpValue *) noexcept;
 };
 
 template<typename func_type>
diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index 1694ec4..82f998e 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -1040,7 +1040,7 @@ GDate* qof_book_get_autoreadonly_gdate (const QofBook *book)
 const char*
 qof_book_get_string_option(const QofBook* book, const char* opt_name)
 {
-    auto slot = qof_instance_get_slots(QOF_INSTANCE (book))->get_slot(opt_name);
+    auto slot = qof_instance_get_slots(QOF_INSTANCE (book))->get_slot({opt_name});
     if (slot == nullptr)
         return nullptr;
     return slot->get<const char*>();
@@ -1052,9 +1052,9 @@ qof_book_set_string_option(QofBook* book, const char* opt_name, const char* opt_
     qof_book_begin_edit(book);
     auto frame = qof_instance_get_slots(QOF_INSTANCE(book));
     if (opt_val && (*opt_val != '\0'))
-        delete frame->set(opt_name, new KvpValue(g_strdup(opt_val)));
+        delete frame->set({opt_name}, new KvpValue(g_strdup(opt_val)));
     else
-        delete frame->set(opt_name, nullptr);
+        delete frame->set({opt_name}, nullptr);
     qof_instance_set_dirty (QOF_INSTANCE (book));
     qof_book_commit_edit(book);
 }
@@ -1086,7 +1086,7 @@ qof_book_get_features (QofBook *book)
     GHashTable *features = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                   NULL, g_free);
 
-    auto slot = frame->get_slot(GNC_FEATURES);
+    auto slot = frame->get_slot({GNC_FEATURES});
     if (slot != nullptr)
     {
         frame = slot->get<KvpFrame*>();
@@ -1100,11 +1100,11 @@ qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr)
 {
     KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book));
     KvpValue* feature = nullptr;
-    auto feature_slot = frame->get_slot(GNC_FEATURES);
+    auto feature_slot = frame->get_slot({GNC_FEATURES});
     if (feature_slot)
     {
         auto feature_frame = feature_slot->get<KvpFrame*>();
-        feature = feature_frame->get_slot(key);
+        feature = feature_frame->get_slot({key});
     }
     if (feature == nullptr || g_strcmp0 (feature->get<const char*>(), descr))
     {
@@ -1178,7 +1178,7 @@ qof_book_options_delete (QofBook *book, GSList *path)
         delete root->set_path(path_v, nullptr);
     }
     else
-        delete root->set_path(KVP_OPTION_PATH, nullptr);
+        delete root->set_path({KVP_OPTION_PATH}, nullptr);
 }
 
 /* QofObject function implementation and registration */
diff --git a/libgnucash/engine/qofinstance-p.h b/libgnucash/engine/qofinstance-p.h
index bce6ead..9fd5e4f 100644
--- a/libgnucash/engine/qofinstance-p.h
+++ b/libgnucash/engine/qofinstance-p.h
@@ -154,10 +154,7 @@ void qof_instance_kvp_merge_guids (const QofInstance *target,
                                    const QofInstance *donor, const char* path);
 gboolean qof_instance_has_slot (const QofInstance *inst, const char *path);
 void qof_instance_slot_var_delete (const QofInstance *, unsigned count, ...);
-void qof_instance_slot_delete (const QofInstance *inst, const char *path);
 void qof_instance_slot_var_delete_if_empty (const QofInstance *, unsigned count, ...);
-void qof_instance_slot_delete_if_empty (const QofInstance *inst,
-                                        const char *path);
 void qof_instance_foreach_slot (const QofInstance *inst, const char *path,
                                 void(*proc)(const char*, const GValue*, void*),
                                 void* data);
@@ -183,7 +180,7 @@ template<typename func_type, typename data_type>
 void qof_instance_foreach_slot_temp (QofInstance const * inst, std::string const & path,
         func_type const & func, data_type & data)
 {
-    auto slot = inst->kvp_data->get_slot(path.c_str());
+    auto slot = inst->kvp_data->get_slot({path});
     if (slot == nullptr || slot->get_type() != KvpValue::Type::FRAME)
         return;
     auto frame = slot->get<KvpFrame*>();
diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp
index a222668..7a3b6bf 100644
--- a/libgnucash/engine/qofinstance.cpp
+++ b/libgnucash/engine/qofinstance.cpp
@@ -1144,8 +1144,8 @@ qof_instance_kvp_add_guid (const QofInstance *inst, const char* path,
     g_return_if_fail (inst->kvp_data != NULL);
 
     auto container = new KvpFrame;
-    container->set(key, new KvpValue(const_cast<GncGUID*>(guid)));
-    container->set("date", new KvpValue(time));
+    container->set({key}, new KvpValue(const_cast<GncGUID*>(guid)));
+    container->set({"date"}, new KvpValue(time));
     delete inst->kvp_data->set_path({path}, new KvpValue(container));
 }
 
@@ -1286,7 +1286,7 @@ bool qof_instance_has_path_slot (QofInstance const * inst, std::vector<std::stri
 gboolean
 qof_instance_has_slot (const QofInstance *inst, const char *path)
 {
-    return inst->kvp_data->get_slot(path) != NULL;
+    return inst->kvp_data->get_slot({path}) != NULL;
 }
 
 void qof_instance_slot_path_delete (QofInstance const * inst, std::vector<std::string> const & path)
@@ -1306,12 +1306,6 @@ qof_instance_slot_var_delete (QofInstance const *inst, unsigned count, ...)
     delete inst->kvp_data->set (path, nullptr);
 }
 
-void
-qof_instance_slot_delete (const QofInstance *inst, const char *path)
-{
-    delete inst->kvp_data->set(path, nullptr);
-}
-
 void qof_instance_slot_path_delete_if_empty (QofInstance const * inst, std::vector<std::string> const & path)
 {
     auto slot = inst->kvp_data->get_slot (path);
@@ -1341,18 +1335,6 @@ qof_instance_slot_var_delete_if_empty (QofInstance const *inst, unsigned count,
     }
 }
 
-void
-qof_instance_slot_delete_if_empty (const QofInstance *inst, const char *path)
-{
-    auto slot = inst->kvp_data->get_slot(path);
-    if (slot)
-    {
-        auto frame = slot->get<KvpFrame*>();
-        if (frame && frame->empty())
-            delete inst->kvp_data->set(path, nullptr);
-    }
-}
-
 std::vector <std::pair <std::string, KvpValue*>>
 qof_instance_get_slots_prefix (QofInstance const * inst, std::string const & prefix)
 {
@@ -1393,7 +1375,7 @@ qof_instance_foreach_slot (const QofInstance *inst, const char* path,
                            void (*proc)(const char*, const GValue*, void*),
                            void* data)
 {
-    auto slot = inst->kvp_data->get_slot(path);
+    auto slot = inst->kvp_data->get_slot({path});
     if (slot == nullptr || slot->get_type() != KvpValue::Type::FRAME)
         return;
     auto frame = slot->get<KvpFrame*>();
diff --git a/libgnucash/engine/test-core/test-engine-stuff.cpp b/libgnucash/engine/test-core/test-engine-stuff.cpp
index c4c4170..ab2cb02 100644
--- a/libgnucash/engine/test-core/test-engine-stuff.cpp
+++ b/libgnucash/engine/test-core/test-engine-stuff.cpp
@@ -381,7 +381,7 @@ get_random_kvp_frame_depth (gint depth)
 
         val_added = TRUE;
 
-        ret->set_path(key, val);
+        ret->set_path({key}, val);
 
         g_free(key);
     }
diff --git a/libgnucash/engine/test/gtest-import-map.cpp b/libgnucash/engine/test/gtest-import-map.cpp
index 1d5b693..d811574 100644
--- a/libgnucash/engine/test/gtest-import-map.cpp
+++ b/libgnucash/engine/test/gtest-import-map.cpp
@@ -254,12 +254,12 @@ TEST_F(ImapBayesTest, FindAccountBayes)
     auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2));
     auto value = new KvpValue(INT64_C(42));
 
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str(), value);
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid).c_str(), new KvpValue{*value});
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid).c_str(), new KvpValue{*value});
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid).c_str(), new KvpValue{*value});
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid).c_str(), new KvpValue{*value});
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid).c_str(), new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid}, value);
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid}, new KvpValue{*value});
+    root->set_path({std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid}, new KvpValue{*value});
 
     auto account = gnc_account_imap_find_account_bayes(t_imap, t_list1);
     EXPECT_EQ(t_expense_account1, account);
@@ -292,29 +292,29 @@ TEST_F(ImapBayesTest, AddAccountBayes)
     auto root = qof_instance_get_slots(QOF_INSTANCE(t_bank_account));
     auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1));
     auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2));
-    auto value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-foo-bar").c_str());
+    auto value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-foo-bar"});
     auto check_account = [this](KvpValue* v) {
         return (v->get<const char*>(), this->t_imap->book); };
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct1_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct1_guid});
     EXPECT_EQ(nullptr, value);
 
     qof_instance_increase_editlevel(QOF_INSTANCE(t_bank_account));
     gnc_account_imap_add_account_bayes(t_imap, t_list2, t_expense_account2);
     qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
     qof_instance_reset_editlevel(QOF_INSTANCE(t_bank_account));
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid});
     EXPECT_EQ(2, value->get<int64_t>());
 }
 
@@ -335,43 +335,43 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     auto val2 = new KvpValue(static_cast<int64_t>(5));
     auto val3 = new KvpValue(static_cast<int64_t>(2));
     // Test for existing entries, all will be 1
-    auto value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str());
+    auto value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid});
     EXPECT_EQ(1, value->get<int64_t>());
     // Set up some old entries
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/token/with/slashes/" + "Asset-Bank").c_str(), val1);
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + "Asset-Bank#Bank").c_str(), new KvpValue{*val1});
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + "Asset>Bank#Bank").c_str(), val2);
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pork + "/" + "Expense#Food").c_str(), new KvpValue{*val2});
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + sausage + "/" + "Expense#Drink").c_str(), val3);
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + "Expense#Food").c_str(), new KvpValue{*val2});
+    root->set_path({IMAP_FRAME_BAYES, "token", "with", "slashes", "Asset-Bank"}, val1);
+    root->set_path({IMAP_FRAME_BAYES, salt, "Asset-Bank#Bank"}, new KvpValue{*val1});
+    root->set_path({IMAP_FRAME_BAYES, salt, "Asset>Bank#Bank"}, val2);
+    root->set_path({IMAP_FRAME_BAYES, pork, "Expense#Food"}, new KvpValue{*val2});
+    root->set_path({IMAP_FRAME_BAYES, sausage, "Expense#Drink"}, val3);
+    root->set_path({IMAP_FRAME_BAYES, foo, "Expense#Food"}, new KvpValue{*val2});
     EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
     EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
     qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
     // Start Convert
     gnc_account_imap_convert_bayes (t_imap->book);
     // convert from 'Asset-Bank' to 'Asset-Bank' guid
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-token-with-slashes-" + acct3_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-token-with-slashes-" + acct3_guid});
     EXPECT_EQ(10, value->get<int64_t>());
     // convert from 'Asset-Bank#Bank' to 'Sav Bank' guid
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct4_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct4_guid});
     EXPECT_EQ(10, value->get<int64_t>());
     // convert from 'Expense#Food' to 'Food' guid
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pork + "-" + acct1_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + pork + "-" + acct1_guid});
     EXPECT_EQ(5, value->get<int64_t>());
     // convert from 'Expense#Drink' to 'Drink' guid
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + sausage + "-" + acct2_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + sausage + "-" + acct2_guid});
     EXPECT_EQ(2, value->get<int64_t>());
     // convert from 'Expense#Food' to 'Food' guid but add to original value
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str());
+    value = root->get_slot({std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid});
     EXPECT_EQ(5, value->get<int64_t>());
     // Check for run once flag
-    auto vals = book->get_slot("changed-bayesian-to-guid");
+    auto vals = book->get_slot({"changed-bayesian-to-guid"});
     EXPECT_STREQ("true", vals->get<const char*>());
     EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
     EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
diff --git a/libgnucash/engine/test/test-kvp-frame.cpp b/libgnucash/engine/test/test-kvp-frame.cpp
index d67639f..4a57c45 100644
--- a/libgnucash/engine/test/test-kvp-frame.cpp
+++ b/libgnucash/engine/test/test-kvp-frame.cpp
@@ -35,10 +35,10 @@ public:
         t_int_val{new KvpValue {INT64_C(15)}},
         t_str_val{new KvpValue{"a value"}}     {
         auto f1 = new KvpFrame;
-        t_root.set("top", new KvpValue{f1});
-        f1->set("first", t_int_val);
-        f1->set("second", new KvpValue{new KvpFrame});
-        f1->set("third", t_str_val);
+        t_root.set({"top"}, new KvpValue{f1});
+        f1->set({"first"}, t_int_val);
+        f1->set({"second"}, new KvpValue{new KvpFrame});
+        f1->set({"third"}, t_str_val);
     }
 protected:
     KvpFrameImpl t_root;
@@ -59,13 +59,12 @@ TEST_F (KvpFrameTest, SetLocal)
     auto v1 = new KvpValueImpl {15.0};
     auto v2 = new KvpValueImpl { (int64_t)52};
     const char* k1 = "first key";
-    const char* k2 = "first key/second key";
 
-    EXPECT_EQ (nullptr, f1->set (k1, v1));
+    EXPECT_EQ (nullptr, f1->set ({k1}, v1));
     auto first_frame = new KvpFrame;
-    EXPECT_EQ (v1, f1->set (k1, new KvpValue{first_frame}));
-    EXPECT_EQ (nullptr, f1->set(k2, v2));
-    EXPECT_EQ (v2, first_frame->get_slot("second key"));
+    EXPECT_EQ (v1, f1->set ({k1}, new KvpValue{first_frame}));
+    EXPECT_EQ (nullptr, f1->set({"first key", "second key"}, v2));
+    EXPECT_EQ (v2, first_frame->get_slot({"second key"}));
 
     delete f1; //this should also delete v2.
     delete v1;
@@ -88,15 +87,14 @@ TEST_F (KvpFrameTest, SetPath)
 
 TEST_F (KvpFrameTest, SetPathSlash)
 {
-    Path path1 {"top", "second/twenty", "twenty-first"};
+    Path path1 {"top", "second", "twenty", "twenty-first"};
     Path path2 {"top", "third", "thirty-first"};
     auto v1 = new KvpValueImpl {15.0};
     auto v2 = new KvpValueImpl { (int64_t)52};
-
     EXPECT_EQ (nullptr, t_root.set(path1, v1));
     EXPECT_EQ (nullptr, t_root.set(path1, v2));
-    auto second_frame = t_root.get_slot("top")->get<KvpFrame*>()->get_slot("second")->get<KvpFrame*>();
-    second_frame->set("twenty", new KvpValue{new KvpFrame});
+    auto second_frame = t_root.get_slot({"top"})->get<KvpFrame*>()->get_slot({"second"})->get<KvpFrame*>();
+    second_frame->set({"twenty"}, new KvpValue{new KvpFrame});
     EXPECT_EQ (nullptr, t_root.set(path1, v1));
     EXPECT_EQ (v1, t_root.set(path1, v2));
     EXPECT_EQ (v2, t_root.get_slot(path1));
@@ -104,16 +102,6 @@ TEST_F (KvpFrameTest, SetPathSlash)
     delete v1;
 }
 
-TEST_F (KvpFrameTest, SetPathIgnoreBeginEndSlash)
-{
-    Path path1 {"top", "/second/", "twenty-first"};
-    Path path2 {"top", "second", "twenty-first"};
-    auto v1 = new KvpValueImpl {15.0};
-
-    EXPECT_EQ (nullptr, t_root.set_path(path1, v1));
-    EXPECT_EQ (v1, t_root.get_slot(path2));
-}
-
 TEST_F (KvpFrameTest, SetPathWithCreate)
 {
     Path path1 {"top", "second", "twenty-first"};
@@ -130,7 +118,7 @@ TEST_F (KvpFrameTest, SetPathWithCreate)
 
 TEST_F (KvpFrameTest, SetPathWithCreateSlash)
 {
-    Path path1 {"top", "second/twenty", "twenty-first"};
+    Path path1 {"top", "second", "twenty", "twenty-first"};
     Path path2 {"top", "third", "thirty-first"};
     Path path1a {"top", "second", "twenty", "twenty-first"};
     auto v1 = new KvpValueImpl {15.0};
@@ -154,7 +142,7 @@ TEST_F (KvpFrameTest, GetKeys)
     EXPECT_EQ (keys.size (), 1ul);
 
     assert_contains (keys, k1);
-    auto frameval = t_root.get_slot(k1);
+    auto frameval = t_root.get_slot({k1});
     ASSERT_EQ(frameval->get_type(), KvpValue::Type::FRAME);
     keys = frameval->get<KvpFrame*>()->get_keys();
     assert_contains (keys, k2);
@@ -166,15 +154,13 @@ TEST_F (KvpFrameTest, GetLocalSlot)
     auto k1 = "first";
     auto k2 = "third";
     auto k3 = "doesn't exist";
-    auto k4 = "top/first";
-
-    auto frameval = t_root.get_slot("top");
+    auto frameval = t_root.get_slot({"top"});
     ASSERT_EQ(frameval->get_type(), KvpValue::Type::FRAME);
     auto f1 = frameval->get<KvpFrame*>();
-    EXPECT_EQ (t_int_val, f1->get_slot(k1));
-    EXPECT_EQ (t_str_val, f1->get_slot(k2));
-    EXPECT_EQ (nullptr, f1->get_slot(k3));
-    EXPECT_EQ (t_int_val, t_root.get_slot(k4));
+    EXPECT_EQ (t_int_val, f1->get_slot({k1}));
+    EXPECT_EQ (t_str_val, f1->get_slot({k2}));
+    EXPECT_EQ (nullptr, f1->get_slot({k3}));
+    EXPECT_EQ (t_int_val, t_root.get_slot({"top", "first"}));
 }
 
 TEST_F (KvpFrameTest, GetSlotPath)
@@ -182,7 +168,7 @@ TEST_F (KvpFrameTest, GetSlotPath)
     Path path1 {"top", "second", "twenty-first"};
     Path path2 {"top", "third", "thirty-first"};
     Path path3 {"top", "second", "twenty", "twenty-first"};
-    Path path3a {"top", "second/twenty", "twenty-first"};
+    Path path3a {"top", "second", "twenty", "twenty-first"};
     auto v1 = new KvpValueImpl {15.0};
     auto v2 = new KvpValueImpl { (int64_t)52};
 
@@ -198,7 +184,7 @@ TEST_F (KvpFrameTest, GetSlotPath)
 TEST_F (KvpFrameTest, Empty)
 {
     KvpFrameImpl f1, f2;
-    f2.set("value", new KvpValue {2.2});
+    f2.set({"value"}, new KvpValue {2.2});
     EXPECT_TRUE(f1.empty());
     EXPECT_FALSE(f2.empty());
 }
@@ -206,12 +192,12 @@ TEST_F (KvpFrameTest, Empty)
 TEST (KvpFrameTestForEachPrefix, for_each_prefix_1)
 {
     KvpFrame fr;
-    fr.set("one", new KvpValue{new KvpFrame});
-    fr.set("one/two", new KvpValue{new KvpFrame});
-    fr.set("top/two/three", new KvpValue {15.0});
-    fr.set("onetwo", new KvpValue{new KvpFrame});
-    fr.set("onetwo/three", new KvpValue {15.0});
-    fr.set("onetwothree", new KvpValue {(int64_t)52});
+    fr.set({"one"}, new KvpValue{new KvpFrame});
+    fr.set({"one", "two"}, new KvpValue{new KvpFrame});
+    fr.set({"top", "two", "three"}, new KvpValue {15.0});
+    fr.set({"onetwo"}, new KvpValue{new KvpFrame});
+    fr.set({"onetwo", "three"}, new KvpValue {15.0});
+    fr.set({"onetwothree"}, new KvpValue {(int64_t)52});
     unsigned count {};
     auto counter = [] (char const *, KvpValue*, unsigned & count) { ++count; };
     fr.for_each_slot_prefix("one", counter, count);
@@ -230,8 +216,8 @@ TEST (KvpFrameTestForEachPrefix, for_each_prefix_1)
 TEST (KvpFrameTestForEachPrefix, for_each_prefix_2)
 {
     KvpFrame fr;
-    fr.set("onetwo/three", new KvpValue {15.0});
-    fr.set("onethree", new KvpValue {(int64_t)52});
+    fr.set({"onetwo", "three"}, new KvpValue {15.0});
+    fr.set({"onethree"}, new KvpValue {(int64_t)52});
     unsigned count;
     fr.for_each_slot_prefix("onetwo", [](char const *, KvpValue * value, unsigned) {
             EXPECT_EQ(value->get_type(), KvpValue::Type::FRAME);
diff --git a/libgnucash/engine/test/utest-Split.cpp b/libgnucash/engine/test/utest-Split.cpp
index d60fbe1..704211d 100644
--- a/libgnucash/engine/test/utest-Split.cpp
+++ b/libgnucash/engine/test/utest-Split.cpp
@@ -735,12 +735,12 @@ test_xaccSplitDetermineGainStatus (Fixture *fixture, gconstpointer pData)
 
     fixture->split->gains = GAINS_STATUS_UNKNOWN;
     fixture->split->gains_split = NULL;
-    g_assert (fixture->split->inst.kvp_data->get_slot("gains_source") == NULL);
+    g_assert (fixture->split->inst.kvp_data->get_slot({"gains_source"}) == NULL);
     xaccSplitDetermineGainStatus (fixture->split);
     g_assert (fixture->split->gains_split == NULL);
     g_assert_cmpint (fixture->split->gains, ==, GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY);
 
-    fixture->split->inst.kvp_data->set("gains-source", new KvpValue(guid_copy(g_guid)));
+    fixture->split->inst.kvp_data->set({"gains-source"}, new KvpValue(guid_copy(g_guid)));
     g_assert (fixture->split->gains_split == NULL);
     fixture->split->gains = GAINS_STATUS_UNKNOWN;
     xaccSplitDetermineGainStatus (fixture->split);
@@ -1802,7 +1802,7 @@ test_xaccSplitGetOtherSplit (Fixture *fixture, gconstpointer pData)
     g_assert (xaccSplitGetOtherSplit (split1) == NULL);
 
     g_assert (xaccTransUseTradingAccounts (txn) == FALSE);
-    g_assert (split->inst.kvp_data->get_slot("lot-split") == NULL);
+    g_assert (split->inst.kvp_data->get_slot({"lot-split"}) == NULL);
     g_assert_cmpint (xaccTransCountSplits (txn), !=, 2);
     g_assert (xaccSplitGetOtherSplit (split) == NULL);
 
@@ -1813,18 +1813,18 @@ test_xaccSplitGetOtherSplit (Fixture *fixture, gconstpointer pData)
     xaccSplitSetParent (split2, txn);
     g_assert (xaccSplitGetOtherSplit (split) == NULL);
 
-    split->inst.kvp_data->set("lot-split", kvpnow);
-    g_assert (split->inst.kvp_data->get_slot("lot-split"));
+    split->inst.kvp_data->set({"lot-split"}, kvpnow);
+    g_assert (split->inst.kvp_data->get_slot({"lot-split"}));
     g_assert (xaccSplitGetOtherSplit (split) == NULL);
 
-    split1->inst.kvp_data->set("lot-split", kvpnow);
-    g_assert (split1->inst.kvp_data->get_slot("lot-split"));
+    split1->inst.kvp_data->set({"lot-split"}, kvpnow);
+    g_assert (split1->inst.kvp_data->get_slot({"lot-split"}));
     g_assert (xaccSplitGetOtherSplit (split) == split2);
 
-    split->inst.kvp_data->set("lot-split", NULL);
-    g_assert (split->inst.kvp_data->get_slot("lot-split") == NULL);
-    split1->inst.kvp_data->set("lot-split", NULL);
-    g_assert (split1->inst.kvp_data->get_slot("lot-split") == NULL);
+    split->inst.kvp_data->set({"lot-split"}, NULL);
+    g_assert (split->inst.kvp_data->get_slot({"lot-split"}) == NULL);
+    split1->inst.kvp_data->set({"lot-split"}, NULL);
+    g_assert (split1->inst.kvp_data->get_slot({"lot-split"}) == NULL);
     qof_book_begin_edit (book);
     qof_instance_set (QOF_INSTANCE (book),
 		      "trading-accts", "t",
diff --git a/libgnucash/engine/test/utest-Transaction.cpp b/libgnucash/engine/test/utest-Transaction.cpp
index a95a5ac..c322c66 100644
--- a/libgnucash/engine/test/utest-Transaction.cpp
+++ b/libgnucash/engine/test/utest-Transaction.cpp
@@ -153,8 +153,8 @@ setup (Fixture *fixture, gconstpointer pData)
         xaccTransSetCurrency (txn, fixture->curr);
         xaccSplitSetParent (split1, txn);
         xaccSplitSetParent (split2, txn);
-        frame->set(trans_notes_str, new KvpValue("Salt pork sausage"));
-        frame->set_path("/qux/quux/corge", new KvpValue(123.456));
+        frame->set({trans_notes_str}, new KvpValue("Salt pork sausage"));
+        frame->set_path({"qux", "quux", "corge"}, new KvpValue(123.456));
         qof_instance_set_slots (QOF_INSTANCE (txn), frame);
     }
     xaccTransCommitEdit (txn);
@@ -562,7 +562,7 @@ test_dupe_trans (Fixture *fixture, gconstpointer pData)
 
     oldtxn->date_posted = posted;
     oldtxn->date_entered = entered;
-    oldtxn->inst.kvp_data->set("/foo/bar/baz",
+    oldtxn->inst.kvp_data->set({"foo", "bar", "baz"},
                                new KvpValue("The Great Waldo Pepper"));
 
     newtxn = fixture->func->dupe_trans (oldtxn);
@@ -881,7 +881,7 @@ test_xaccTransEqual (Fixture *fixture, gconstpointer pData)
     g_free(clone->description);
     clone->description = static_cast<char*>(CACHE_INSERT ("Waldo Pepper"));
     auto frame = qof_instance_get_slots (QOF_INSTANCE (clone));
-    frame->set("/qux/quux/corge", new KvpValue(654.321));
+    frame->set({"qux", "quux", "corge"}, new KvpValue(654.321));
     xaccTransCommitEdit (clone);
     g_free (cleanup->msg);
     g_free (check->msg);
@@ -893,7 +893,7 @@ test_xaccTransEqual (Fixture *fixture, gconstpointer pData)
     xaccTransBeginEdit (clone);
     cleanup->msg = g_strdup_printf (cleanup_fmt, clone->orig);
     clone->description = static_cast<char*>(CACHE_INSERT ("Waldo Pepper"));
-    frame->set("/qux/quux/corge", new KvpValue(123.456));
+    frame->set({"qux", "quux", "corge"}, new KvpValue(123.456));
     xaccTransCommitEdit (clone);
     g_free (cleanup->msg);
     g_free (check->msg);
@@ -1858,22 +1858,22 @@ test_xaccTransVoid (Fixture *fixture, gconstpointer pData)
     /* Actual function variables start here. */
     auto frame = fixture->txn->inst.kvp_data;
     auto void_reason = "Voided for Unit Test";
-    auto txn_notes = g_strdup (frame->get_slot(trans_notes_str)->get<const char*>());
+    auto txn_notes = g_strdup (frame->get_slot({trans_notes_str})->get<const char*>());
     Timespec now = timespec_now ();
     char iso8601_str[ISO_DATELENGTH + 1] = "";
     GList *split = NULL;
 
     xaccTransVoid (fixture->txn, void_reason);
-    g_assert_cmpstr (frame->get_slot(trans_notes_str)->get<const char*>(), ==,
+    g_assert_cmpstr (frame->get_slot({trans_notes_str})->get<const char*>(), ==,
                      "Voided transaction");
-    g_assert_cmpstr (frame->get_slot(void_former_notes_str)->get<const char*>(),
+    g_assert_cmpstr (frame->get_slot({void_former_notes_str})->get<const char*>(),
                      ==, txn_notes);
-    g_assert_cmpstr (frame->get_slot(void_reason_str)->get<const char*>(), ==,
+    g_assert_cmpstr (frame->get_slot({void_reason_str})->get<const char*>(), ==,
                      void_reason);
     gnc_timespec_to_iso8601_buff (now, iso8601_str);
-    g_assert_cmpstr (frame->get_slot(void_time_str)->get<const char*>(), ==,
+    g_assert_cmpstr (frame->get_slot({void_time_str})->get<const char*>(), ==,
                      iso8601_str);
-    g_assert_cmpstr (frame->get_slot(TRANS_READ_ONLY_REASON)->get<const char*>(),
+    g_assert_cmpstr (frame->get_slot({TRANS_READ_ONLY_REASON})->get<const char*>(),
                      ==, "Transaction Voided");
     for (split = fixture->txn->splits; split; split=g_list_next (split))
     {
@@ -1883,12 +1883,12 @@ test_xaccTransVoid (Fixture *fixture, gconstpointer pData)
 
     xaccTransUnvoid (fixture->txn);
 
-    g_assert_cmpstr (frame->get_slot(trans_notes_str)->get<const char*>(), ==,
+    g_assert_cmpstr (frame->get_slot({trans_notes_str})->get<const char*>(), ==,
                      txn_notes);
-    g_assert (frame->get_slot(void_former_notes_str) == NULL);
-    g_assert (frame->get_slot(void_reason_str) == NULL);
-    g_assert (frame->get_slot(void_time_str) == NULL);
-    g_assert (frame->get_slot(TRANS_READ_ONLY_REASON) == NULL);
+    g_assert (frame->get_slot({void_former_notes_str}) == NULL);
+    g_assert (frame->get_slot({void_reason_str}) == NULL);
+    g_assert (frame->get_slot({void_time_str}) == NULL);
+    g_assert (frame->get_slot({TRANS_READ_ONLY_REASON}) == NULL);
     for (split = fixture->txn->splits; split; split=g_list_next (split))
     {
         g_assert (!gnc_numeric_zero_p (((Split*)(split->data))->value));
@@ -1909,7 +1909,7 @@ test_xaccTransReverse (Fixture *fixture, gconstpointer pData)
     auto frame = fixture->txn->inst.kvp_data;
     GList *orig_splits = NULL, *rev_splits = NULL;
 
-    g_assert (guid_equal (frame->get_slot(TRANS_REVERSED_BY)->get<GncGUID*>(),
+    g_assert (guid_equal (frame->get_slot({TRANS_REVERSED_BY})->get<GncGUID*>(),
                           xaccTransGetGUID (rev)));
 
     g_assert (!qof_instance_is_dirty (QOF_INSTANCE (rev))); //Cleared by commit

commit 2cda65e01237db48bf5399bfb1b1bdbce156830f
Author: lmat <dartme18 at gmail.com>
Date:   Fri Nov 3 21:27:48 2017 -0400

    Added test for and corrected get_bayes_info

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index b7aaef0..1208425 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -5432,60 +5432,39 @@ build_non_bayes (const char *key, const GValue *value, gpointer user_data)
     g_free (guid_string);
 }
 
-static void
-build_bayes_layer_two (const char *key, KvpValue * val, imap_info imapInfo)
+static std::tuple<std::string, std::string, std::string>
+parse_bayes_imap_info (std::string const & imap_bayes_entry)
 {
-    QofBook     *book;
-    Account     *map_account = NULL;
-    GncGUID     *guid;
-    gchar       *kvp_path;
-    gchar       *count;
-    struct imap_info *imapInfo_node;
-    // Get the book
-    book = qof_instance_get_book (imapInfo.source_account);
-    if (val->get_type() == KvpValue::Type::INT64)
-    {
-        PINFO("build_bayes_layer_two: account '%s', token_count: '%" G_GINT64_FORMAT "'",
-                                  key, val->get<int64_t>());
-        count = g_strdup_printf ("%" G_GINT64_FORMAT, val->get<int64_t>());
-    }
-    else
-        count = g_strdup ("0");
-    kvp_path = g_strconcat (imapInfo.category_head, "/", key, NULL);
-    PINFO("build_bayes_layer_two: kvp_path is '%s'", kvp_path);
-    guid = g_new (GncGUID, 1);
-    if (string_to_guid (key, guid))
-        map_account = xaccAccountLookup (guid, book);
-    g_free (guid);
-    imapInfo_node = static_cast <imap_info*> (g_malloc(sizeof(*imapInfo_node)));
-    imapInfo_node->source_account = imapInfo.source_account;
-    imapInfo_node->map_account    = map_account;
-    imapInfo_node->full_category  = g_strdup (kvp_path);
-    imapInfo_node->match_string   = g_strdup (imapInfo.match_string);
-    imapInfo_node->category_head  = g_strdup (imapInfo.category_head);
-    imapInfo_node->count          = g_strdup (count);
-    imapInfo.list = g_list_append (imapInfo.list, imapInfo_node);
-    g_free (kvp_path);
-    g_free (count);
+    auto header_length = strlen (IMAP_FRAME_BAYES);
+    std::string header {imap_bayes_entry.substr (0, header_length)};
+    auto guid_start = imap_bayes_entry.size() - GUID_ENCODING_LENGTH;
+    std::string keyword {imap_bayes_entry.substr (header_length + 1, guid_start - header_length - 2)};
+    std::string account_guid {imap_bayes_entry.substr (guid_start)};
+    return {header, keyword, account_guid};
 }
 
 static void
 build_bayes (const char *key, KvpValue * value, imap_info & imapInfo)
 {
-    struct imap_info imapInfol2;
-    PINFO("build_bayes: match string '%s'", (char*)key);
-
-    std::string prefix {g_strdup_printf (IMAP_FRAME_BAYES "-%s", key)};
-    PINFO("build_bayes: prefix is '%s', key '%s'", prefix.c_str(), key);
-    imapInfol2.source_account = imapInfo.source_account;
-    imapInfol2.match_string   = g_strdup (key);
-    imapInfol2.category_head  = g_strdup (prefix.c_str());
-    imapInfol2.list           = imapInfo.list;
-    qof_instance_foreach_slot_prefix (QOF_INSTANCE(imapInfo.source_account), prefix,
-                               build_bayes_layer_two, imapInfol2);
-    imapInfo.list = imapInfol2.list;
-    g_free (imapInfol2.match_string);
-    g_free (imapInfol2.category_head);
+    auto slots = qof_instance_get_slots_prefix (QOF_INSTANCE (imapInfo.source_account), IMAP_FRAME_BAYES);
+    if (!slots.size()) return;
+    for (auto const & entry : slots)
+    {
+        auto parsed_key = parse_bayes_imap_info (entry.first);
+        auto temp_guid = gnc::GUID::from_string (std::get <2> (parsed_key));
+        GncGUID guid = temp_guid;
+        auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
+        std::string category_head {std::get <0> (parsed_key) + "-" + std::get <1> (parsed_key)};
+        auto imap_node = static_cast <imap_info*> (g_malloc (sizeof (imap_info)));
+        auto count = entry.second->get <int64_t> ();
+        imap_node->source_account = imapInfo.source_account;
+        imap_node->map_account = map_account;
+        imap_node->full_category = g_strdup (key);
+        imap_node->match_string = g_strdup (std::get <1> (parsed_key).c_str ());
+        imap_node->category_head = g_strdup (category_head.c_str ());
+        imap_node->count = g_strdup_printf ("%" G_GINT64_FORMAT, count);
+        imapInfo.list = g_list_append (imapInfo.list, imap_node);
+    };
 }
 
 GList *
@@ -5498,7 +5477,6 @@ gnc_account_imap_get_info_bayes (Account *acc)
     return imapInfo.list;
 }
 
-
 GList *
 gnc_account_imap_get_info (Account *acc, const char *category)
 {
diff --git a/libgnucash/engine/qofinstance-p.h b/libgnucash/engine/qofinstance-p.h
index a54a581..bce6ead 100644
--- a/libgnucash/engine/qofinstance-p.h
+++ b/libgnucash/engine/qofinstance-p.h
@@ -173,8 +173,9 @@ bool qof_instance_has_path_slot (QofInstance const *, std::vector<std::string> c
 void qof_instance_slot_path_delete (QofInstance const *, std::vector<std::string> const &);
 
 void qof_instance_slot_path_delete_if_empty (QofInstance const *, std::vector<std::string> const &);
+
 /** Returns all keys that match the given prefix and their corresponding values.*/
-std::map<std::string, KvpValue*>
+std::vector <std::pair <std::string, KvpValue*>>
 qof_instance_get_slots_prefix (QofInstance const *, std::string const & prefix);
 
 /* Don't pass nullptr as the function */
diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp
index 64826d5..a222668 100644
--- a/libgnucash/engine/qofinstance.cpp
+++ b/libgnucash/engine/qofinstance.cpp
@@ -1353,6 +1353,17 @@ qof_instance_slot_delete_if_empty (const QofInstance *inst, const char *path)
     }
 }
 
+std::vector <std::pair <std::string, KvpValue*>>
+qof_instance_get_slots_prefix (QofInstance const * inst, std::string const & prefix)
+{
+    std::vector <std::pair <std::string, KvpValue*>> ret;
+    inst->kvp_data->for_each_slot_temp ([&prefix, &ret] (std::string const & key, KvpValue * val) {
+        if (key.find (prefix) == 0)
+            ret.emplace_back (key, val);
+    });
+    return ret;
+}
+
 namespace {
 struct wrap_param
 {
@@ -1360,6 +1371,7 @@ struct wrap_param
     void *user_data;
 };
 }
+
 static void
 wrap_gvalue_function (const char* key, KvpValue *val, wrap_param & param)
 {
diff --git a/libgnucash/engine/test/gtest-import-map.cpp b/libgnucash/engine/test/gtest-import-map.cpp
index 117fe59..1d5b693 100644
--- a/libgnucash/engine/test/gtest-import-map.cpp
+++ b/libgnucash/engine/test/gtest-import-map.cpp
@@ -381,11 +381,30 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
 TEST_F (ImapBayesTest, import_map_with_delimiters)
 {
     GList * tokens {nullptr};
-    tokens = g_list_prepend(tokens, const_cast<char*>("one/two/three"));
-    gnc_account_imap_add_account_bayes(t_imap, tokens, t_expense_account1);
-    gnc_account_imap_add_account_bayes(t_imap, tokens, t_expense_account1);
-    gnc_account_imap_add_account_bayes(t_imap, tokens, t_expense_account1);
+    tokens = g_list_prepend (tokens, const_cast<char*> ("one/two/three"));
+    gnc_account_imap_add_account_bayes (t_imap, tokens, t_expense_account1);
+    gnc_account_imap_add_account_bayes (t_imap, tokens, t_expense_account1);
+    gnc_account_imap_add_account_bayes (t_imap, tokens, t_expense_account1);
+    auto account = gnc_account_imap_find_account_bayes (t_imap, tokens);
+    EXPECT_EQ (account, t_expense_account1);
+}
 
+TEST_F (ImapBayesTest, get_bayes_info)
+{
+    GList * tokens {nullptr};
+    tokens = g_list_prepend (tokens, const_cast <char*> ("one/two/three"));
+    gnc_account_imap_add_account_bayes(t_imap, tokens, t_expense_account1);
     auto account = gnc_account_imap_find_account_bayes (t_imap, tokens);
     EXPECT_EQ (account, t_expense_account1);
+    auto infos = gnc_account_imap_get_info_bayes (t_bank_account);
+    EXPECT_EQ (g_list_first (infos), g_list_last (infos));
+    auto info = static_cast <imap_info*> (g_list_first (infos)->data);
+    EXPECT_EQ (info->source_account, t_bank_account);
+    EXPECT_EQ (info->map_account, t_expense_account1);
+    auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1)); //Food
+    EXPECT_STREQ (info->full_category, (std::string {IMAP_FRAME_BAYES} + "-one-two-three-" + acct1_guid).c_str ());
+    EXPECT_STREQ (info->match_string, "one-two-three");
+    EXPECT_STREQ (info->category_head, (std::string {IMAP_FRAME_BAYES} + "-one-two-three").c_str ());
+    EXPECT_STREQ (info->count, "1");
 }
+

commit 9d7ec35ce5ff59ffe5cf0bf65b1623bea5f52422
Author: lmat <dartme18 at gmail.com>
Date:   Thu Nov 2 15:42:22 2017 -0400

    Removed qof_instance_set_kvp, qof_instance_get_kvp
    
    And replaced them with versions that take lists of key path elements.
    This is in an effort to eliminate the parsing of kvp keys.

diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 8ce126d..b7aaef0 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -406,34 +406,27 @@ gnc_account_get_property (GObject         *object,
     case PROP_SORT_REVERSED:
         g_value_set_boolean(value, xaccAccountGetSortReversed(account));
     case PROP_LOT_NEXT_ID:
-        key = "lot-mgmt/next-id";
         /* Pre-set the value in case the frame is empty */
         g_value_set_int64 (value, 0);
-        qof_instance_get_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {"lot-mgmt", "next-id"});
         break;
     case PROP_ONLINE_ACCOUNT:
-        key = "online_id";
-        qof_instance_get_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {"online_id"});
         break;
     case PROP_OFX_INCOME_ACCOUNT:
-        key = KEY_ASSOC_INCOME_ACCOUNT;
-        qof_instance_get_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
         break;
     case PROP_AB_ACCOUNT_ID:
-        key = AB_KEY "/" AB_ACCOUNT_ID;
-        qof_instance_get_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_ID});
         break;
     case PROP_AB_ACCOUNT_UID:
-        key = AB_KEY "/" AB_ACCOUNT_UID;
-        qof_instance_get_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_UID});
         break;
     case PROP_AB_BANK_CODE:
-        key = AB_KEY "/" AB_BANK_CODE;
-        qof_instance_get_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_BANK_CODE});
         break;
     case PROP_AB_TRANS_RETRIEVAL:
-        key = AB_KEY "/" AB_TRANS_RETRIEVAL;
-        qof_instance_get_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_TRANS_RETRIEVAL});
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -449,10 +442,7 @@ gnc_account_set_property (GObject         *object,
 {
     Account *account;
     gnc_numeric *number;
-    const gchar *key = NULL;
-
     g_return_if_fail(GNC_IS_ACCOUNT(object));
-
     account = GNC_ACCOUNT(object);
     if (prop_id < PROP_RUNTIME_0)
         g_assert (qof_instance_get_editlevel(account));
@@ -540,32 +530,25 @@ gnc_account_set_property (GObject         *object,
     case PROP_SORT_REVERSED:
         xaccAccountSetSortReversed(account, g_value_get_boolean(value));
     case PROP_LOT_NEXT_ID:
-        key = "lot-mgmt/next-id";
-        qof_instance_set_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {"lot-mgmt", "next-id"});
         break;
     case PROP_ONLINE_ACCOUNT:
-        key = "online_id";
-        qof_instance_set_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {"online_id"});
         break;
     case PROP_OFX_INCOME_ACCOUNT:
-        key = KEY_ASSOC_INCOME_ACCOUNT;
-        qof_instance_set_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
         break;
     case PROP_AB_ACCOUNT_ID:
-        key = AB_KEY "/" AB_ACCOUNT_ID;
-        qof_instance_set_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_ID});
         break;
     case PROP_AB_ACCOUNT_UID:
-        key = AB_KEY "/" AB_ACCOUNT_UID;
-        qof_instance_set_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_UID});
         break;
     case PROP_AB_BANK_CODE:
-        key = AB_KEY "/" AB_BANK_CODE;
-        qof_instance_set_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_BANK_CODE});
         break;
     case PROP_AB_TRANS_RETRIEVAL:
-        key = AB_KEY "/" AB_TRANS_RETRIEVAL;
-        qof_instance_set_kvp (QOF_INSTANCE (account), key, value);
+        qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_TRANS_RETRIEVAL});
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -2310,20 +2293,20 @@ set_kvp_string_tag (Account *acc, const char *tag, const char *value)
     if (value)
     {
         gchar *tmp = g_strstrip(g_strdup(value));
-	if (strlen (tmp))
-	{
-	     GValue v = G_VALUE_INIT;
-	     g_value_init (&v, G_TYPE_STRING);
-	     g_value_set_string (&v, tmp);
-	     qof_instance_set_kvp (QOF_INSTANCE (acc), tag , &v);
-	}
-	else
-	     qof_instance_set_kvp (QOF_INSTANCE (acc), tag, NULL);
+        if (strlen (tmp))
+        {
+            GValue v = G_VALUE_INIT;
+            g_value_init (&v, G_TYPE_STRING);
+            g_value_set_string (&v, tmp);
+            qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {tag});
+        }
+        else
+            qof_instance_set_path_kvp (QOF_INSTANCE (acc), NULL, {tag});
         g_free(tmp);
     }
     else
     {
-	 qof_instance_set_kvp (QOF_INSTANCE (acc), tag, NULL);
+         qof_instance_set_path_kvp (QOF_INSTANCE (acc), NULL, {tag});
     }
     mark_account (acc);
     xaccAccountCommitEdit(acc);
@@ -2334,7 +2317,7 @@ get_kvp_string_tag (const Account *acc, const char *tag)
 {
     GValue v = G_VALUE_INIT;
     if (acc == NULL || tag == NULL) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (acc), tag, &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE (acc), &v, {tag});
     return G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : NULL;
 }
 
@@ -2359,7 +2342,7 @@ xaccAccountSetSortOrder (Account *acc, const char *str)
 void
 xaccAccountSetSortReversed (Account *acc, gboolean sortreversed)
 {
-     set_kvp_string_tag (acc, "sort-reversed", sortreversed ? "true" : NULL);
+    set_kvp_string_tag (acc, "sort-reversed", sortreversed ? "true" : NULL);
 }
 
 static void
@@ -2507,7 +2490,7 @@ DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
     if ((!acc) || (!currency)) return;
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, s);
-    qof_instance_set_kvp (QOF_INSTANCE (acc), "old-currency", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {"old-currency"});
     mark_account (acc);
     xaccAccountCommitEdit(acc);
 
@@ -3142,7 +3125,7 @@ DxaccAccountGetCurrency (const Account *acc)
     gnc_commodity_table *table;
 
     if (!acc) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE(acc), "old-currency", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {"old-currency"});
     if (G_VALUE_HOLDS_STRING (&v))
         s = g_value_get_string (&v);
     if (!s) return NULL;
@@ -3809,7 +3792,7 @@ xaccAccountForEachLot(const Account *acc,
 }
 
 static void
-set_boolean_key (Account *acc, const char* key, gboolean option)
+set_boolean_key (Account *acc, std::vector<std::string> const & path, gboolean option)
 {
     GValue v = G_VALUE_INIT;
     g_return_if_fail(GNC_IS_ACCOUNT(acc));
@@ -3817,17 +3800,17 @@ set_boolean_key (Account *acc, const char* key, gboolean option)
     g_value_init (&v, G_TYPE_BOOLEAN);
     g_value_set_boolean (&v, option);
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE (acc),key , &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, path);
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
 
 static gboolean
-boolean_from_key (const Account *acc, const char *key)
+boolean_from_key (const Account *acc, std::vector<std::string> const & path)
 {
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc), key, &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, path);
     if (G_VALUE_HOLDS_INT64 (&v))
         return g_value_get_int64 (&v) != 0;
     if (G_VALUE_HOLDS_BOOLEAN (&v))
@@ -3844,13 +3827,13 @@ boolean_from_key (const Account *acc, const char *key)
 gboolean
 xaccAccountGetTaxRelated (const Account *acc)
 {
-    return boolean_from_key(acc, "tax-related");
+    return boolean_from_key(acc, {"tax-related"});
 }
 
 void
 xaccAccountSetTaxRelated (Account *acc, gboolean tax_related)
 {
-    set_boolean_key(acc, "tax-related", tax_related);
+    set_boolean_key(acc, {"tax-related"}, tax_related);
 }
 
 const char *
@@ -3858,7 +3841,7 @@ xaccAccountGetTaxUSCode (const Account *acc)
 {
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc), "/tax-US/code", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {"tax-US", "code"});
     return G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : NULL;
 }
 
@@ -3871,7 +3854,7 @@ xaccAccountSetTaxUSCode (Account *acc, const char *code)
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, code);
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE (acc), "/tax-US/code", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {"tax-US", "code"});
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
@@ -3881,8 +3864,7 @@ xaccAccountGetTaxUSPayerNameSource (const Account *acc)
 {
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc),
-                          "/tax-US/payer-name-source", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {"tax-US", "payer-name-source"});
     return G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : NULL;
  }
 
@@ -3895,7 +3877,7 @@ xaccAccountSetTaxUSPayerNameSource (Account *acc, const char *source)
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, source);
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE (acc), "/tax-US/payer-name-source", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {"tax-US", "payer-name-source"});
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
@@ -3906,7 +3888,7 @@ xaccAccountGetTaxUSCopyNumber (const Account *acc)
     gint64 copy_number = 0;
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc), "/tax-US/copy-number", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {"tax-US", "copy-number"});
     if (G_VALUE_HOLDS_INT64 (&v))
         copy_number = g_value_get_int64 (&v);
 
@@ -3923,11 +3905,11 @@ xaccAccountSetTaxUSCopyNumber (Account *acc, gint64 copy_number)
         GValue v = G_VALUE_INIT;
         g_value_init (&v, G_TYPE_INT64);
         g_value_set_int64 (&v, copy_number);
-        qof_instance_set_kvp (QOF_INSTANCE (acc), "/tax-US/copy-number", &v);
+        qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {"tax-US", "copy-number"});
     }
     else
     {
-        qof_instance_set_kvp (QOF_INSTANCE (acc), "/tax-US/copy-number", NULL);
+        qof_instance_set_path_kvp (QOF_INSTANCE (acc), nullptr, {"tax-US", "copy-number"});
     }
     mark_account (acc);
     xaccAccountCommitEdit (acc);
@@ -3939,13 +3921,13 @@ xaccAccountSetTaxUSCopyNumber (Account *acc, gint64 copy_number)
 gboolean
 xaccAccountGetPlaceholder (const Account *acc)
 {
-    return boolean_from_key(acc, "placeholder");
+    return boolean_from_key(acc, {"placeholder"});
 }
 
 void
 xaccAccountSetPlaceholder (Account *acc, gboolean val)
 {
-    set_boolean_key(acc, "placeholder", val);
+    set_boolean_key(acc, {"placeholder"}, val);
 }
 
 GNCPlaceholderType
@@ -3975,13 +3957,13 @@ xaccAccountGetDescendantPlaceholder (const Account *acc)
 gboolean
 xaccAccountGetHidden (const Account *acc)
 {
-    return boolean_from_key (acc, "hidden");
+    return boolean_from_key (acc, {"hidden"});
 }
 
 void
 xaccAccountSetHidden (Account *acc, gboolean val)
 {
-    set_boolean_key (acc, "hidden", val);
+    set_boolean_key (acc, {"hidden"}, val);
 }
 
 gboolean
@@ -4312,7 +4294,7 @@ xaccAccountGetReconcileLastDate (const Account *acc, time64 *last_date)
     gint64 date = 0;
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc), "reconcile-info/last-date", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {"reconcile-info", "last-date"});
     if (G_VALUE_HOLDS_INT64 (&v))
         date = g_value_get_int64 (&v);
 
@@ -4337,7 +4319,7 @@ xaccAccountSetReconcileLastDate (Account *acc, time64 last_date)
     g_value_init (&v, G_TYPE_INT64);
     g_value_set_int64 (&v, last_date);
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE (acc), "/reconcile-info/last-date", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {"reconcile-info", "last-date"});
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
@@ -4354,10 +4336,10 @@ xaccAccountGetReconcileLastInterval (const Account *acc,
 
     if (!acc) return FALSE;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc),
-                          "reconcile-info/last-interval/months", &v1);
-    qof_instance_get_kvp (QOF_INSTANCE(acc),
-                          "reconcile-info/last-interval/days", &v2);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v1,
+            {"reconcile-info", "last-interval", "months"});
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v2,
+            {"reconcile-info", "last-interval", "days"});
     if (G_VALUE_HOLDS_INT64 (&v1))
         m = g_value_get_int64 (&v1);
     if (G_VALUE_HOLDS_INT64 (&v2))
@@ -4387,10 +4369,10 @@ xaccAccountSetReconcileLastInterval (Account *acc, int months, int days)
     g_value_init (&v2, G_TYPE_INT64);
     g_value_set_int64 (&v2, days);
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE (acc),
-                          "reconcile-info/last-interval/months", &v1);
-    qof_instance_set_kvp (QOF_INSTANCE (acc),
-                          "reconcile-info/last-interval/days", &v2);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v1,
+            {"reconcile-info", "last-interval", "months"});
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v2,
+            {"reconcile-info", "last-interval", "days"});
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
@@ -4404,8 +4386,8 @@ xaccAccountGetReconcilePostponeDate (const Account *acc, time64 *postpone_date)
     gint64 date = 0;
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc),
-                          "reconcile-info/postpone/date", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v,
+            {"reconcile-info", "postpone", "date"});
     if (G_VALUE_HOLDS_INT64 (&v))
         date = g_value_get_int64 (&v);
 
@@ -4430,8 +4412,8 @@ xaccAccountSetReconcilePostponeDate (Account *acc, time64 postpone_date)
     g_value_init (&v, G_TYPE_INT64);
     g_value_set_int64 (&v, postpone_date);
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE (acc),
-                          "/reconcile-info/postpone/date", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v,
+            {"reconcile-info", "postpone", "date"});
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
@@ -4446,8 +4428,8 @@ xaccAccountGetReconcilePostponeBalance (const Account *acc,
     gnc_numeric bal = gnc_numeric_zero ();
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc),
-                          "reconcile-info/postpone/balance", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v,
+            {"reconcile-info", "postpone", "balance"});
     if (!G_VALUE_HOLDS_INT64 (&v))
         return FALSE;
 
@@ -4473,8 +4455,8 @@ xaccAccountSetReconcilePostponeBalance (Account *acc, gnc_numeric balance)
     g_value_init (&v, GNC_TYPE_NUMERIC);
     g_value_set_boxed (&v, &balance);
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE (acc),
-                          "/reconcile-info/postpone/balance", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v,
+            {"reconcile-info", "postpone", "balance"});
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
@@ -4489,7 +4471,7 @@ xaccAccountClearReconcilePostpone (Account *acc)
     if (!acc) return;
 
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE(acc), "reconcile-info/postpone", NULL);
+    qof_instance_set_path_kvp (QOF_INSTANCE(acc), nullptr, {"reconcile-info", "postpone"});
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
@@ -4504,7 +4486,7 @@ xaccAccountClearReconcilePostpone (Account *acc)
 gboolean
 xaccAccountGetAutoInterestXfer (const Account *acc, gboolean default_value)
 {
-    return boolean_from_key (acc, "reconcile-info/auto-interest-transfer");
+    return boolean_from_key (acc, {"reconcile-info", "auto-interest-transfer"});
 }
 
 /********************************************************************\
@@ -4513,7 +4495,7 @@ xaccAccountGetAutoInterestXfer (const Account *acc, gboolean default_value)
 void
 xaccAccountSetAutoInterestXfer (Account *acc, gboolean option)
 {
-    set_boolean_key (acc, "reconcile-info/auto-interest-transfer", option);
+    set_boolean_key (acc, {"reconcile-info", "auto-interest-transfer"}, option);
 }
 
 /********************************************************************\
@@ -4524,7 +4506,7 @@ xaccAccountGetLastNum (const Account *acc)
 {
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
-    qof_instance_get_kvp (QOF_INSTANCE(acc), "last-num", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {"last-num"});
     return G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : NULL;
 }
 
@@ -4540,7 +4522,7 @@ xaccAccountSetLastNum (Account *acc, const char *num)
 
     g_value_set_string (&v, num);
     xaccAccountBeginEdit (acc);
-    qof_instance_set_kvp (QOF_INSTANCE (acc), "last-num", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {"last-num"});
     mark_account (acc);
     xaccAccountCommitEdit (acc);
 }
@@ -4594,13 +4576,13 @@ Account *
 xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
 {
     GValue v = G_VALUE_INIT;
-    gchar *curr_name = g_strdup_printf ("/lot-mgmt/gains-act/%s",
-                                      gnc_commodity_get_unique_name (curr));
+    std::vector<std::string> path {"lot-mgmt", "gains-acct",
+        gnc_commodity_get_unique_name (curr)};
     GncGUID *guid = NULL;
     Account *gains_account;
 
     g_return_val_if_fail (acc != NULL, NULL);
-    qof_instance_get_kvp (QOF_INSTANCE(acc), curr_name, &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, path);
     if (G_VALUE_HOLDS_BOXED (&v))
         guid = (GncGUID*)g_value_get_boxed (&v);
     if (guid == NULL) /* No gains account for this currency */
@@ -4613,7 +4595,7 @@ xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
              GValue vr = G_VALUE_INIT;
              g_value_init (&vr, GNC_TYPE_GUID);
              g_value_set_boxed (&vr, guid);
-             qof_instance_set_kvp (QOF_INSTANCE (acc), curr_name, &vr);
+             qof_instance_set_path_kvp (QOF_INSTANCE (acc), &vr, path);
              qof_instance_set_dirty (QOF_INSTANCE (acc));
         }
         xaccAccountCommitEdit (acc);
@@ -4622,7 +4604,6 @@ xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
         gains_account = xaccAccountLookup (guid,
                                            qof_instance_get_book(acc));
 
-    g_free (curr_name);
     return gains_account;
 }
 
@@ -4642,11 +4623,10 @@ dxaccAccountSetPriceSrc(Account *acc, const char *src)
             GValue v = G_VALUE_INIT;
             g_value_init (&v, G_TYPE_STRING);
             g_value_set_string (&v, src);
-            qof_instance_set_kvp (QOF_INSTANCE(acc),
-                                  "old-price-source", &v);
+            qof_instance_set_path_kvp (QOF_INSTANCE(acc), &v, {"old-price-source"});
         }
         else
-            qof_instance_set_kvp (QOF_INSTANCE(acc), "old-price-source", NULL);
+            qof_instance_set_path_kvp (QOF_INSTANCE(acc), nullptr, {"old-price-source"});
 
         mark_account (acc);
         xaccAccountCommitEdit(acc);
@@ -4664,7 +4644,7 @@ dxaccAccountGetPriceSrc(const Account *acc)
 
     if (!xaccAccountIsPriced(acc)) return NULL;
 
-    qof_instance_get_kvp (QOF_INSTANCE(acc), "old-price-source", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {"old-price-source"});
     return G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : NULL;
 }
 
@@ -4680,7 +4660,7 @@ dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
     xaccAccountBeginEdit(acc);
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, tz);
-    qof_instance_set_kvp (QOF_INSTANCE (acc), "old-quote-tz", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v, {"old-quote-tz"});
     mark_account (acc);
     xaccAccountCommitEdit(acc);
 }
@@ -4694,7 +4674,7 @@ dxaccAccountGetQuoteTZ(const Account *acc)
     GValue v = G_VALUE_INIT;
     if (!acc) return NULL;
     if (!xaccAccountIsPriced(acc)) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (acc), "old-quote-tz", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE (acc), &v, {"old-quote-tz"});
     return G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : NULL;
 }
 
@@ -4714,8 +4694,8 @@ xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
      */
     g_value_init (&v, G_TYPE_INT64);
     g_value_set_int64 (&v, status);
-    qof_instance_set_kvp (QOF_INSTANCE (acc),
-                          "/reconcile-info/include-children", &v);
+    qof_instance_set_path_kvp (QOF_INSTANCE (acc), &v,
+            {"reconcile-info", "include-children"});
     mark_account(acc);
     xaccAccountCommitEdit (acc);
 }
@@ -4732,8 +4712,8 @@ xaccAccountGetReconcileChildrenStatus(const Account *acc)
      */
     GValue v = G_VALUE_INIT;
     if (!acc) return FALSE;
-    qof_instance_get_kvp (QOF_INSTANCE (acc),
-                          "reconcile-info/include-children", &v);
+    qof_instance_get_path_kvp (QOF_INSTANCE (acc), &v,
+            {"reconcile-info", "include-children"});
     return G_VALUE_HOLDS_INT64 (&v) ? g_value_get_int64 (&v) : FALSE;
 }
 
@@ -5107,17 +5087,14 @@ gnc_account_imap_find_account (GncImportMatchMap *imap,
 {
     GValue v = G_VALUE_INIT;
     GncGUID * guid = NULL;
-    char *kvp_path;
-
     if (!imap || !key) return NULL;
-    if (!category)
-        kvp_path = g_strdup_printf (IMAP_FRAME "/%s", key);
-    else
-        kvp_path = g_strdup_printf (IMAP_FRAME "/%s/%s", category, key);
-    qof_instance_get_kvp (QOF_INSTANCE (imap->acc), kvp_path, &v);
+    std::vector<std::string> path {IMAP_FRAME};
+    if (category)
+        path.push_back (category);
+    path.push_back (key);
+    qof_instance_get_path_kvp (QOF_INSTANCE (imap->acc), &v, path);
     if (G_VALUE_HOLDS_BOXED (&v))
         guid = (GncGUID*)g_value_get_boxed (&v);
-    g_free (kvp_path);
     return xaccAccountLookup (guid, imap->book);
 }
 
@@ -5129,19 +5106,15 @@ gnc_account_imap_add_account (GncImportMatchMap *imap,
                               Account *acc)
 {
     GValue v = G_VALUE_INIT;
-    char *kvp_path;
-
     if (!imap || !key || !acc || (strlen (key) == 0)) return;
-    if (!category)
-        kvp_path = g_strdup_printf (IMAP_FRAME "/%s", key);
-    else
-        kvp_path = g_strdup_printf (IMAP_FRAME "/%s/%s", category, key);
-
+    std::vector<std::string> path {IMAP_FRAME};
+    if (category)
+        path.emplace_back (category);
+    path.emplace_back (key);
     g_value_init (&v, GNC_TYPE_GUID);
     g_value_set_boxed (&v, xaccAccountGetGUID (acc));
     xaccAccountBeginEdit (imap->acc);
-    qof_instance_set_kvp (QOF_INSTANCE (imap->acc), kvp_path, &v);
-    g_free (kvp_path);
+    qof_instance_set_path_kvp (QOF_INSTANCE (imap->acc), &v, path);
     qof_instance_set_dirty (QOF_INSTANCE (imap->acc));
     xaccAccountCommitEdit (imap->acc);
 }
@@ -5152,28 +5125,18 @@ gnc_account_imap_delete_account (GncImportMatchMap *imap,
                                  const char *category,
                                  const char *key)
 {
-    char *kvp_path;
-
     if (!imap || !key) return;
-    if (!category)
-        kvp_path = g_strdup_printf (IMAP_FRAME "/%s", key);
-    else
-        kvp_path = g_strdup_printf (IMAP_FRAME "/%s/%s", category, key);
-
+    std::vector<std::string> path {IMAP_FRAME};
+    if (category)
+        path.emplace_back (category);
+    path.emplace_back (key);
     xaccAccountBeginEdit (imap->acc);
-
-    if (qof_instance_has_slot (QOF_INSTANCE (imap->acc), kvp_path))
+    if (qof_instance_has_path_slot (QOF_INSTANCE (imap->acc), path))
     {
-        qof_instance_slot_delete (QOF_INSTANCE (imap->acc), kvp_path);
-        g_free (kvp_path);
-
+        qof_instance_slot_path_delete (QOF_INSTANCE (imap->acc), path);
         if (category)
-        {
-            kvp_path = g_strdup_printf (IMAP_FRAME "/%s", category);
-            qof_instance_slot_delete_if_empty (QOF_INSTANCE (imap->acc), kvp_path);
-            g_free (kvp_path);
-        }
-        qof_instance_slot_delete_if_empty (QOF_INSTANCE (imap->acc), IMAP_FRAME);
+            qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (imap->acc), {IMAP_FRAME, category});
+        qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (imap->acc), {IMAP_FRAME});
     }
     qof_instance_set_dirty (QOF_INSTANCE (imap->acc));
     xaccAccountCommitEdit (imap->acc);
@@ -5333,20 +5296,20 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
 }
 
 static void
-change_imap_entry (GncImportMatchMap *imap, gchar const * kvp_path, int64_t token_count)
+change_imap_entry (GncImportMatchMap *imap, std::string const & path, int64_t token_count)
 {
     GValue value = G_VALUE_INIT;
 
-    PINFO("Source Account is '%s', kvp_path is '%s', Count is '%" G_GINT64_FORMAT "'",
-           xaccAccountGetName (imap->acc), kvp_path, token_count);
+    PINFO("Source Account is '%s', Count is '%" G_GINT64_FORMAT "'",
+           xaccAccountGetName (imap->acc), token_count);
 
     // check for existing guid entry
-    if (qof_instance_has_slot (QOF_INSTANCE(imap->acc), kvp_path))
+    if (qof_instance_has_slot (QOF_INSTANCE(imap->acc), path.c_str ()))
     {
         int64_t  existing_token_count = 0;
 
         // get the existing_token_count value
-        qof_instance_get_kvp (QOF_INSTANCE (imap->acc), kvp_path, &value);
+        qof_instance_get_path_kvp (QOF_INSTANCE (imap->acc), &value, {path});
 
         if (G_VALUE_HOLDS_INT64 (&value))
             existing_token_count = g_value_get_int64 (&value);
@@ -5362,7 +5325,7 @@ change_imap_entry (GncImportMatchMap *imap, gchar const * kvp_path, int64_t toke
     g_value_set_int64 (&value, token_count);
 
     // Add or Update the entry based on guid
-    qof_instance_set_kvp (QOF_INSTANCE (imap->acc), kvp_path, &value);
+    qof_instance_set_path_kvp (QOF_INSTANCE (imap->acc), &value, {path});
 
     /* Set a feature flag in the book for the change to use guid.
      * This will prevent older GnuCash versions that don't support this feature
@@ -5410,11 +5373,11 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
         /* start off with one token for this account */
         token_count = 1;
         PINFO("adding token '%s'", (char*)current_token->data);
-        std::string translated_token {static_cast <char*> (current_token->data)};
-        std::replace (translated_token.begin (), translated_token.end (), '/', '-');
+        std::string translated_token {static_cast<char*>(current_token->data)};
+        std::replace(translated_token.begin(), translated_token.end(), '/', '-');
         auto path = std::string {IMAP_FRAME_BAYES} + '-' + translated_token + '-' + guid_string;
         /* change the imap entry for the account */
-        change_imap_entry (imap, path.c_str (), token_count);
+        change_imap_entry (imap, path, token_count);
     }
     /* free up the account fullname and guid string */
     qof_instance_set_dirty (QOF_INSTANCE (imap->acc));
@@ -5566,12 +5529,10 @@ gnc_account_get_map_entry (Account *acc, const char *full_category)
 {
     GValue v = G_VALUE_INIT;
     gchar *text = NULL;
-    gchar *kvp_path = g_strdup (full_category);
-
-    if (qof_instance_has_slot (QOF_INSTANCE(acc), kvp_path))
+    std::vector<std::string> path {full_category};
+    if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
     {
-        qof_instance_get_kvp (QOF_INSTANCE(acc), kvp_path, &v);
-
+        qof_instance_get_path_kvp (QOF_INSTANCE (acc), &v, path);
         if (G_VALUE_HOLDS_STRING (&v))
         {
             gchar const *string;
@@ -5579,7 +5540,6 @@ gnc_account_get_map_entry (Account *acc, const char *full_category)
             text = g_strdup (string);
         }
     }
-    g_free (kvp_path);
     return text;
 }
 
@@ -5729,7 +5689,7 @@ static bool
 run_once_key_set (char const * key, QofBook * book)
 {
     GValue value G_VALUE_INIT;
-    qof_instance_get_kvp (QOF_INSTANCE(book), key, &value);
+    qof_instance_get_path_kvp (QOF_INSTANCE(book), &value, {key});
     return G_VALUE_HOLDS_STRING(&value) && strcmp(g_value_get_string(&value), "true");
 }
 
@@ -5739,7 +5699,7 @@ set_run_once_key (char const * key, QofBook * book)
     GValue value G_VALUE_INIT;
     g_value_init(&value, G_TYPE_BOOLEAN);
     g_value_set_boolean(&value, TRUE);
-    qof_instance_set_kvp(QOF_INSTANCE (book), key, &value);
+    qof_instance_set_path_kvp(QOF_INSTANCE (book), &value, {key});
 }
 
 void
diff --git a/libgnucash/engine/Scrub.c b/libgnucash/engine/Scrub.c
index d27752e..a036fff 100644
--- a/libgnucash/engine/Scrub.c
+++ b/libgnucash/engine/Scrub.c
@@ -1228,10 +1228,10 @@ xaccAccountDeleteOldData (Account *account)
 {
     if (!account) return;
     xaccAccountBeginEdit (account);
-    qof_instance_set_kvp (QOF_INSTANCE (account), "old-currency", NULL);
-    qof_instance_set_kvp (QOF_INSTANCE (account), "old-security", NULL);
-    qof_instance_set_kvp (QOF_INSTANCE (account), "old-currency-scu", NULL);
-    qof_instance_set_kvp (QOF_INSTANCE (account), "old-security-scu", NULL);
+    qof_instance_set_var_kvp (QOF_INSTANCE (account), NULL, 1, "old-currency");
+    qof_instance_set_var_kvp (QOF_INSTANCE (account), NULL, 1, "old-security");
+    qof_instance_set_var_kvp (QOF_INSTANCE (account), NULL, 1, "old-currency-scu");
+    qof_instance_set_var_kvp (QOF_INSTANCE (account), NULL, 1, "old-security-scu");
     qof_instance_set_dirty (QOF_INSTANCE (account));
     xaccAccountCommitEdit (account);
 }
@@ -1337,22 +1337,22 @@ xaccAccountScrubKvp (Account *account)
 
     if (!account) return;
 
-    qof_instance_get_kvp (QOF_INSTANCE (account), "notes", &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (account), &v, 1, "notes");
     if (G_VALUE_HOLDS_STRING (&v))
     {
         str2 = g_strstrip(g_value_dup_string(&v));
         if (strlen(str2) == 0)
-            qof_instance_slot_delete (QOF_INSTANCE (account), "notes");
+            qof_instance_slot_var_delete (QOF_INSTANCE (account), 1, "notes");
         g_free(str2);
     }
 
-    qof_instance_get_kvp (QOF_INSTANCE (account), "placeholder", &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (account), &v, 1, "placeholder");
     if ((G_VALUE_HOLDS_STRING (&v) &&
         strcmp(g_value_get_string (&v), "false") == 0) ||
         (G_VALUE_HOLDS_BOOLEAN (&v) && ! g_value_get_boolean (&v)))
-        qof_instance_slot_delete (QOF_INSTANCE (account), "placeholder");
+        qof_instance_slot_var_delete (QOF_INSTANCE (account), 1, "placeholder");
 
-    qof_instance_slot_delete_if_empty (QOF_INSTANCE (account), "hbci");
+    qof_instance_slot_var_delete_if_empty (QOF_INSTANCE (account), 1, "hbci");
 }
 
 /* ================================================================ */
diff --git a/libgnucash/engine/Split.c b/libgnucash/engine/Split.c
index 86072d1..772849f 100644
--- a/libgnucash/engine/Split.c
+++ b/libgnucash/engine/Split.c
@@ -184,40 +184,31 @@ gnc_split_get_property(GObject         *object,
             g_value_take_object(value, split->lot);
             break;
         case PROP_SX_CREDIT_FORMULA:
-            key = GNC_SX_ID "/" GNC_SX_CREDIT_FORMULA;
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
             break;
         case PROP_SX_CREDIT_NUMERIC:
-            key = GNC_SX_ID "/" GNC_SX_CREDIT_NUMERIC;
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
             break;
         case PROP_SX_DEBIT_FORMULA:
-            key = GNC_SX_ID "/" GNC_SX_DEBIT_FORMULA;
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
             break;
         case PROP_SX_DEBIT_NUMERIC:
-            key = GNC_SX_ID "/" GNC_SX_DEBIT_NUMERIC;
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
             break;
         case PROP_SX_ACCOUNT:
-            key = GNC_SX_ID "/" GNC_SX_ACCOUNT;
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
             break;
         case PROP_SX_SHARES:
-            key = GNC_SX_ID "/" GNC_SX_SHARES;
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
             break;
         case PROP_ONLINE_ACCOUNT:
-            key = "online_id";
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 1, "online_id");
             break;
         case PROP_GAINS_SPLIT:
-            key = "gains-split";
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
             break;
         case PROP_GAINS_SOURCE:
-            key = "gains-source";
-            qof_instance_get_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_get_var_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
             break;
         default:
             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -270,40 +261,31 @@ gnc_split_set_property(GObject         *object,
             xaccSplitSetLot(split, g_value_get_object(value));
             break;
         case PROP_SX_CREDIT_FORMULA:
-            key = GNC_SX_ID "/" GNC_SX_CREDIT_FORMULA;
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_FORMULA);
             break;
         case PROP_SX_CREDIT_NUMERIC:
-            key = GNC_SX_ID "/" GNC_SX_CREDIT_NUMERIC;
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_CREDIT_NUMERIC);
             break;
         case PROP_SX_DEBIT_FORMULA:
-            key = GNC_SX_ID "/" GNC_SX_DEBIT_FORMULA;
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_FORMULA);
             break;
         case PROP_SX_DEBIT_NUMERIC:
-            key = GNC_SX_ID "/" GNC_SX_DEBIT_NUMERIC;
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_DEBIT_NUMERIC);
             break;
         case PROP_SX_ACCOUNT:
-            key = GNC_SX_ID "/" GNC_SX_ACCOUNT;
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_ACCOUNT);
             break;
         case PROP_SX_SHARES:
-            key = GNC_SX_ID "/" GNC_SX_SHARES;
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 2, GNC_SX_ID, GNC_SX_SHARES);
             break;
         case PROP_ONLINE_ACCOUNT:
-            key = "online_id";
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 1, "online_id");
             break;
         case PROP_GAINS_SPLIT:
-            key = "gains-split";
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 1, "gains-split");
             break;
         case PROP_GAINS_SOURCE:
-            key = "gains-source";
-            qof_instance_set_kvp (QOF_INSTANCE (split), key, value);
+            qof_instance_set_var_kvp (QOF_INSTANCE (split), value, 1, "gains-source");
             break;
         default:
             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -1097,7 +1079,7 @@ xaccSplitDetermineGainStatus (Split *split)
         return;
     }
 
-    qof_instance_get_kvp (QOF_INSTANCE (split), "gains-source", &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (split), &v, 1, "gains-source");
     if (G_VALUE_HOLDS_BOXED (&v))
         guid = (GncGUID*)g_value_get_boxed (&v);
     if (!guid)
@@ -1990,7 +1972,7 @@ xaccSplitGetType(const Split *s)
     const char *split_type = NULL;
 
     if (!s) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (s), "split-type", &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
     if (G_VALUE_HOLDS_STRING (&v))
         split_type = g_value_get_string (&v);
     return split_type ? split_type : "normal";
@@ -2007,7 +1989,7 @@ xaccSplitMakeStockSplit(Split *s)
     s->value = gnc_numeric_zero();
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, "stock-split");
-    qof_instance_set_kvp (QOF_INSTANCE (s), "split-type", &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (s), &v, 1, "split-type");
     SET_GAINS_VDIRTY(s);
     mark_split(s);
     qof_instance_set_dirty(QOF_INSTANCE(s));
@@ -2144,7 +2126,7 @@ xaccSplitVoidFormerAmount(const Split *split)
     GValue v = G_VALUE_INIT;
     gnc_numeric *num = NULL;
     g_return_val_if_fail(split, gnc_numeric_zero());
-    qof_instance_get_kvp (QOF_INSTANCE (split), void_former_amt_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (split), &v, 1, void_former_amt_str);
     if (G_VALUE_HOLDS_BOXED (&v))
         num = (gnc_numeric*)g_value_get_boxed (&v);
     return num ? *num : gnc_numeric_zero();
@@ -2156,7 +2138,7 @@ xaccSplitVoidFormerValue(const Split *split)
     GValue v = G_VALUE_INIT;
     gnc_numeric *num = NULL;
     g_return_val_if_fail(split, gnc_numeric_zero());
-    qof_instance_get_kvp (QOF_INSTANCE (split), void_former_val_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (split), &v, 1, void_former_val_str);
     if (G_VALUE_HOLDS_BOXED (&v))
         num = (gnc_numeric*)g_value_get_boxed (&v);
     return num ? *num : gnc_numeric_zero();
@@ -2171,10 +2153,10 @@ xaccSplitVoid(Split *split)
     g_value_init (&v, GNC_TYPE_NUMERIC);
     num =  xaccSplitGetAmount(split);
     g_value_set_boxed (&v, &num);
-    qof_instance_set_kvp (QOF_INSTANCE (split), void_former_amt_str, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (split), &v, 1, void_former_amt_str);
     num =  xaccSplitGetValue(split);
     g_value_set_boxed (&v, &num);
-    qof_instance_set_kvp (QOF_INSTANCE (split), void_former_val_str, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (split), &v, 1, void_former_val_str);
 
     /* Marking dirty handled by SetAmount etc. */
     xaccSplitSetAmount (split, zero);
@@ -2188,8 +2170,8 @@ xaccSplitUnvoid(Split *split)
     xaccSplitSetAmount (split, xaccSplitVoidFormerAmount(split));
     xaccSplitSetValue (split, xaccSplitVoidFormerValue(split));
     xaccSplitSetReconcile(split, NREC);
-    qof_instance_set_kvp (QOF_INSTANCE (split), void_former_amt_str, NULL);
-    qof_instance_set_kvp (QOF_INSTANCE (split), void_former_val_str, NULL);
+    qof_instance_set_var_kvp (QOF_INSTANCE (split), NULL, 1, void_former_amt_str);
+    qof_instance_set_var_kvp (QOF_INSTANCE (split), NULL, 1, void_former_val_str);
     qof_instance_set_dirty (QOF_INSTANCE (split));
 }
 
diff --git a/libgnucash/engine/Transaction.c b/libgnucash/engine/Transaction.c
index 811100d..cfa651a 100644
--- a/libgnucash/engine/Transaction.c
+++ b/libgnucash/engine/Transaction.c
@@ -338,17 +338,14 @@ gnc_transaction_get_property(GObject* object,
         g_value_set_boxed(value, &tx->date_entered);
         break;
     case PROP_INVOICE:
-	key = GNC_INVOICE_ID "/" GNC_INVOICE_GUID;
-	qof_instance_get_kvp (QOF_INSTANCE (tx), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
+        break;
     case PROP_SX_TXN:
-	key = GNC_SX_FROM;
-	qof_instance_get_kvp (QOF_INSTANCE (tx), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
+        break;
     case PROP_ONLINE_ACCOUNT:
-	key = "online_id";
-	qof_instance_get_kvp (QOF_INSTANCE (tx), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (tx), value, 1, "online_id");
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -387,17 +384,14 @@ gnc_transaction_set_property(GObject* object,
         xaccTransSetDateEnteredTS(tx, g_value_get_boxed(value));
         break;
     case PROP_INVOICE:
-	key = GNC_INVOICE_ID "/" GNC_INVOICE_GUID;
-	qof_instance_set_kvp (QOF_INSTANCE (tx), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (tx), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
+        break;
     case PROP_SX_TXN:
-	key = GNC_SX_FROM;
-	qof_instance_set_kvp (QOF_INSTANCE (tx), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (tx), value, 1, GNC_SX_FROM);
+        break;
     case PROP_ONLINE_ACCOUNT:
-	key = "online_id";
-	qof_instance_set_kvp (QOF_INSTANCE (tx), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (tx), value, 1, "online_id");
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -1993,7 +1987,7 @@ xaccTransSetDatePostedGDate (Transaction *trans, GDate date)
      * clearly be distinguished from the Timespec. */
     g_value_init (&v, G_TYPE_DATE);
     g_value_set_boxed (&v, &date);
-    qof_instance_set_kvp (QOF_INSTANCE(trans), TRANS_DATE_POSTED, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_DATE_POSTED);
     /* mark dirty and commit handled by SetDateInternal */
     xaccTransSetDateInternal(trans, &trans->date_posted,
                              gdate_to_timespec(date));
@@ -2069,7 +2063,7 @@ xaccTransSetDateDueTS (Transaction *trans, const Timespec *ts)
     g_value_init (&v, GNC_TYPE_TIMESPEC);
     g_value_set_boxed (&v, ts);
     xaccTransBeginEdit(trans);
-    qof_instance_set_kvp (QOF_INSTANCE (trans), TRANS_DATE_DUE_KVP, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_DUE_KVP);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2083,7 +2077,7 @@ xaccTransSetTxnType (Transaction *trans, char type)
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, s);
     xaccTransBeginEdit(trans);
-    qof_instance_set_kvp (QOF_INSTANCE (trans), TRANS_TXN_TYPE_KVP, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_TXN_TYPE_KVP);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2093,8 +2087,7 @@ void xaccTransClearReadOnly (Transaction *trans)
     if (trans)
     {
         xaccTransBeginEdit(trans);
-	qof_instance_set_kvp (QOF_INSTANCE (trans),
-			      TRANS_READ_ONLY_REASON, NULL);
+        qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, TRANS_READ_ONLY_REASON);
         qof_instance_set_dirty(QOF_INSTANCE(trans));
         xaccTransCommitEdit(trans);
     }
@@ -2105,11 +2098,11 @@ xaccTransSetReadOnly (Transaction *trans, const char *reason)
 {
     if (trans && reason)
     {
-	GValue v = G_VALUE_INIT;
-	g_value_init (&v, G_TYPE_STRING);
-	g_value_set_string (&v, reason);
+        GValue v = G_VALUE_INIT;
+        g_value_init (&v, G_TYPE_STRING);
+        g_value_set_string (&v, reason);
         xaccTransBeginEdit(trans);
-	qof_instance_set_kvp (QOF_INSTANCE (trans), TRANS_READ_ONLY_REASON, &v);
+        qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_READ_ONLY_REASON);
         qof_instance_set_dirty(QOF_INSTANCE(trans));
         xaccTransCommitEdit(trans);
     }
@@ -2167,7 +2160,7 @@ xaccTransSetAssociation (Transaction *trans, const char *assoc)
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, assoc);
     xaccTransBeginEdit(trans);
-    qof_instance_set_kvp (QOF_INSTANCE (trans), assoc_uri_str, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, assoc_uri_str);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2189,7 +2182,7 @@ xaccTransSetNotes (Transaction *trans, const char *notes)
     g_value_set_string (&v, notes);
     xaccTransBeginEdit(trans);
 
-    qof_instance_set_kvp (QOF_INSTANCE (trans), trans_notes_str, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2202,13 +2195,13 @@ xaccTransSetIsClosingTxn (Transaction *trans, gboolean is_closing)
 
     if (is_closing)
     {
-	GValue v = G_VALUE_INIT;
-	g_value_init (&v, G_TYPE_INT64);
-	g_value_set_int64 (&v, 1);
-        qof_instance_set_kvp (QOF_INSTANCE (trans), trans_is_closing_str, &v);
+        GValue v = G_VALUE_INIT;
+        g_value_init (&v, G_TYPE_INT64);
+        g_value_set_int64 (&v, 1);
+        qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_is_closing_str);
     }
     else
-	qof_instance_set_kvp (QOF_INSTANCE (trans), trans_is_closing_str, NULL);
+        qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, trans_is_closing_str);
     qof_instance_set_dirty(QOF_INSTANCE(trans));
     xaccTransCommitEdit(trans);
 }
@@ -2343,7 +2336,7 @@ xaccTransGetAssociation (const Transaction *trans)
 {
     GValue v = G_VALUE_INIT;
     if (!trans) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (trans), assoc_uri_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, assoc_uri_str);
     if (G_VALUE_HOLDS_STRING (&v))
          return g_value_get_string (&v);
     return NULL;
@@ -2354,7 +2347,7 @@ xaccTransGetNotes (const Transaction *trans)
 {
     GValue v = G_VALUE_INIT;
     if (!trans) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (trans), trans_notes_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     if (G_VALUE_HOLDS_STRING (&v))
          return g_value_get_string (&v);
     return NULL;
@@ -2365,7 +2358,7 @@ xaccTransGetIsClosingTxn (const Transaction *trans)
 {
     GValue v = G_VALUE_INIT;
     if (!trans) return FALSE;
-    qof_instance_get_kvp (QOF_INSTANCE (trans), trans_is_closing_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_is_closing_str);
     if (G_VALUE_HOLDS_INT64 (&v))
          return g_value_get_int64 (&v);
     return FALSE;
@@ -2419,11 +2412,11 @@ xaccTransGetDatePostedGDate (const Transaction *trans)
         /* Can we look up this value in the kvp slot? If yes, use it
          * from there because it doesn't suffer from time zone
          * shifts. */
-	GValue v = G_VALUE_INIT;
-	qof_instance_get_kvp (QOF_INSTANCE (trans), TRANS_DATE_POSTED, &v);
+        GValue v = G_VALUE_INIT;
+        qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_POSTED);
         if (G_VALUE_HOLDS_BOXED (&v))
              result = *(GDate*)g_value_get_boxed (&v);
-	if (! g_date_valid (&result))
+        if (! g_date_valid (&result))
         {
              /* Well, this txn doesn't have a GDate saved in a
               * slot. Avoid getting the date in the local TZ by
@@ -2455,7 +2448,7 @@ xaccTransGetDateDueTS (const Transaction *trans, Timespec *ts)
     GValue v = G_VALUE_INIT;
     if (!trans || !ts) return;
 
-    qof_instance_get_kvp (QOF_INSTANCE (trans), TRANS_DATE_DUE_KVP, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_DATE_DUE_KVP);
     if (G_VALUE_HOLDS_BOXED (&v))
          *ts = *(Timespec*)g_value_get_boxed (&v);
     if (ts->tv_sec == 0)
@@ -2477,11 +2470,11 @@ xaccTransGetTxnType (const Transaction *trans)
     GValue v = G_VALUE_INIT;
 
     if (!trans) return TXN_TYPE_NONE;
-    qof_instance_get_kvp (QOF_INSTANCE (trans), TRANS_TXN_TYPE_KVP, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, TRANS_TXN_TYPE_KVP);
     if (G_VALUE_HOLDS_STRING (&v))
          s = g_value_get_string (&v);
     if (s && strlen (s) == 1)
-	return *s;
+        return *s;
 
     return TXN_TYPE_NONE;
 }
@@ -2495,11 +2488,11 @@ xaccTransGetReadOnly (const Transaction *trans)
     GValue v = G_VALUE_INIT;
     const char *s = NULL;
     if (trans == NULL) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE(trans), TRANS_READ_ONLY_REASON, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_READ_ONLY_REASON);
     if (G_VALUE_HOLDS_STRING (&v))
          s = g_value_get_string (&v);
     if (s && strlen (s))
-	return s;
+        return s;
 
     return NULL;
 }
@@ -2693,20 +2686,20 @@ xaccTransVoid(Transaction *trans, const char *reason)
         return;
     }
     xaccTransBeginEdit(trans);
-    qof_instance_get_kvp (QOF_INSTANCE (trans), trans_notes_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     if (G_VALUE_HOLDS_STRING (&v))
-        qof_instance_set_kvp (QOF_INSTANCE (trans), void_former_notes_str, &v);
+        qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, void_former_notes_str);
     else
         g_value_init (&v, G_TYPE_STRING);
 
     g_value_set_string (&v, _("Voided transaction"));
-    qof_instance_set_kvp (QOF_INSTANCE (trans), trans_notes_str, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
     g_value_set_string (&v, reason);
-    qof_instance_set_kvp (QOF_INSTANCE (trans), void_reason_str, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
 
     gnc_timespec_to_iso8601_buff (timespec_now (), iso8601_str);
     g_value_set_string (&v, iso8601_str);
-    qof_instance_set_kvp (QOF_INSTANCE (trans), void_time_str, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, void_time_str);
 
     FOR_EACH_SPLIT(trans, xaccSplitVoid(s));
 
@@ -2722,7 +2715,7 @@ xaccTransGetVoidStatus(const Transaction *trans)
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(trans, FALSE);
 
-    qof_instance_get_kvp (QOF_INSTANCE (trans), void_reason_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
     if (G_VALUE_HOLDS_STRING (&v))
          s = g_value_get_string (&v);
     return s && strlen(s);
@@ -2734,7 +2727,7 @@ xaccTransGetVoidReason(const Transaction *trans)
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(trans, FALSE);
 
-    qof_instance_get_kvp (QOF_INSTANCE (trans), void_reason_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
     if (G_VALUE_HOLDS_STRING (&v))
          return g_value_get_string (&v);
     return NULL;
@@ -2748,11 +2741,11 @@ xaccTransGetVoidTime(const Transaction *tr)
     Timespec void_time = {0, 0};
 
     g_return_val_if_fail(tr, void_time);
-    qof_instance_get_kvp (QOF_INSTANCE (tr), void_time_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (tr), &v, 1, void_time_str);
     if (G_VALUE_HOLDS_STRING (&v))
         s = g_value_get_string (&v);
     if (s)
-	return gnc_iso8601_to_timespec_gmt (s);
+        return gnc_iso8601_to_timespec_gmt (s);
     return void_time;
 }
 
@@ -2763,18 +2756,18 @@ xaccTransUnvoid (Transaction *trans)
     const char *s = NULL;
     g_return_if_fail(trans);
 
-    qof_instance_get_kvp (QOF_INSTANCE (trans), void_reason_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, void_reason_str);
     if (G_VALUE_HOLDS_STRING (&v))
         s = g_value_get_string (&v);
     if (s == NULL) return; /* Transaction isn't voided. Bail. */
     xaccTransBeginEdit(trans);
 
-    qof_instance_get_kvp (QOF_INSTANCE (trans), void_former_notes_str, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (trans), &v, 1, void_former_notes_str);
     if (G_VALUE_HOLDS_STRING (&v))
-        qof_instance_set_kvp (QOF_INSTANCE (trans), trans_notes_str, &v);
-    qof_instance_set_kvp (QOF_INSTANCE (trans), void_former_notes_str, NULL);
-    qof_instance_set_kvp (QOF_INSTANCE (trans), void_reason_str, NULL);
-    qof_instance_set_kvp (QOF_INSTANCE (trans), void_time_str, NULL);
+        qof_instance_set_var_kvp (QOF_INSTANCE (trans), &v, 1, trans_notes_str);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, void_former_notes_str);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, void_reason_str);
+    qof_instance_set_var_kvp (QOF_INSTANCE (trans), NULL, 1, void_time_str);
 
     FOR_EACH_SPLIT(trans, xaccSplitUnvoid(s));
 
@@ -2804,7 +2797,7 @@ xaccTransReverse (Transaction *orig)
     /* Now update the original with a pointer to the new one */
     g_value_init (&v, GNC_TYPE_GUID);
     g_value_set_boxed (&v, xaccTransGetGUID(trans));
-    qof_instance_set_kvp (QOF_INSTANCE (orig), TRANS_REVERSED_BY, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (orig), &v, 1, TRANS_REVERSED_BY);
 
     /* Make sure the reverse transaction is not read-only */
     xaccTransClearReadOnly(trans);
@@ -2819,7 +2812,7 @@ xaccTransGetReversedBy(const Transaction *trans)
 {
     GValue v = G_VALUE_INIT;
     g_return_val_if_fail(trans, NULL);
-    qof_instance_get_kvp (QOF_INSTANCE(trans), TRANS_REVERSED_BY, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE(trans), &v, 1, TRANS_REVERSED_BY);
     if (G_VALUE_HOLDS_BOXED (&v))
         return xaccTransLookup((GncGUID*)g_value_get_boxed (&v),
                                qof_instance_get_book(trans));
diff --git a/libgnucash/engine/gnc-budget.c b/libgnucash/engine/gnc-budget.c
index c49af4e..16c121e 100644
--- a/libgnucash/engine/gnc-budget.c
+++ b/libgnucash/engine/gnc-budget.c
@@ -473,32 +473,31 @@ gnc_budget_get_num_periods(const GncBudget* budget)
     return GET_PRIVATE(budget)->num_periods;
 }
 
-#define BUF_SIZE (10 + GUID_ENCODING_LENGTH + \
-   GNC_BUDGET_MAX_NUM_PERIODS_DIGITS)
-
 static inline void
-make_period_path (const Account *account, guint period_num, char *path)
+make_period_path (const Account *account, guint period_num, char *path1, char *path2)
 {
     const GncGUID *guid;
     gchar *bufend;
-    guid = xaccAccountGetGUID(account);
-    bufend = guid_to_string_buff(guid, path);
-    g_sprintf(bufend, "/%d", period_num);
+    guid = xaccAccountGetGUID (account);
+    guid_to_string_buff (guid, path1);
+    g_sprintf (path2, "%d", period_num);
 }
+
 /* period_num is zero-based */
 /* What happens when account is deleted, after we have an entry for it? */
 void
 gnc_budget_unset_account_period_value(GncBudget *budget, const Account *account,
                                       guint period_num)
 {
-    gchar path[BUF_SIZE];
+    gchar path_part_one [GUID_ENCODING_LENGTH];
+    gchar path_part_two [GNC_BUDGET_MAX_NUM_PERIODS_DIGITS];
 
     g_return_if_fail (budget != NULL);
     g_return_if_fail (account != NULL);
-    make_period_path (account, period_num, path);
+    make_period_path (account, period_num, path_part_one, path_part_two);
 
     gnc_budget_begin_edit(budget);
-    qof_instance_set_kvp (QOF_INSTANCE (budget), path, NULL);
+    qof_instance_set_var_kvp (QOF_INSTANCE (budget), NULL, 2, path_part_one, path_part_two);
     qof_instance_set_dirty(&budget->inst);
     gnc_budget_commit_edit(budget);
 
@@ -512,7 +511,8 @@ void
 gnc_budget_set_account_period_value(GncBudget *budget, const Account *account,
                                     guint period_num, gnc_numeric val)
 {
-    gchar path[BUF_SIZE];
+    gchar path_part_one [GUID_ENCODING_LENGTH];
+    gchar path_part_two [GNC_BUDGET_MAX_NUM_PERIODS_DIGITS];
 
     /* Watch out for an off-by-one error here:
      * period_num starts from 0 while num_periods starts from 1 */
@@ -525,17 +525,17 @@ gnc_budget_set_account_period_value(GncBudget *budget, const Account *account,
     g_return_if_fail (budget != NULL);
     g_return_if_fail (account != NULL);
 
-    make_period_path (account, period_num, path);
+    make_period_path (account, period_num, path_part_one, path_part_two);
 
     gnc_budget_begin_edit(budget);
     if (gnc_numeric_check(val))
-        qof_instance_set_kvp (QOF_INSTANCE (budget), path, NULL);
+        qof_instance_set_var_kvp (QOF_INSTANCE (budget), NULL, 2, path_part_one, path_part_two);
     else
     {
         GValue v = G_VALUE_INIT;
         g_value_init (&v, GNC_TYPE_NUMERIC);
         g_value_set_boxed (&v, &val);
-        qof_instance_set_kvp (QOF_INSTANCE (budget), path, &v);
+        qof_instance_set_var_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
     }
     qof_instance_set_dirty(&budget->inst);
     gnc_budget_commit_edit(budget);
@@ -553,14 +553,15 @@ gnc_budget_is_account_period_value_set(const GncBudget *budget,
                                        guint period_num)
 {
     GValue v = G_VALUE_INIT;
-    gchar path[BUF_SIZE];
+    gchar path_part_one [GUID_ENCODING_LENGTH];
+    gchar path_part_two [GNC_BUDGET_MAX_NUM_PERIODS_DIGITS];
     gconstpointer ptr = NULL;
 
     g_return_val_if_fail(GNC_IS_BUDGET(budget), FALSE);
     g_return_val_if_fail(account, FALSE);
 
-    make_period_path (account, period_num, path);
-    qof_instance_get_kvp (QOF_INSTANCE (budget), path, &v);
+    make_period_path (account, period_num, path_part_one, path_part_two);
+    qof_instance_get_var_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
     if (G_VALUE_HOLDS_BOXED (&v))
         ptr = g_value_get_boxed (&v);
     return (ptr != NULL);
@@ -572,14 +573,15 @@ gnc_budget_get_account_period_value(const GncBudget *budget,
                                     guint period_num)
 {
     gnc_numeric *numeric = NULL;
-    gchar path[BUF_SIZE];
+    gchar path_part_one [GUID_ENCODING_LENGTH];
+    gchar path_part_two [GNC_BUDGET_MAX_NUM_PERIODS_DIGITS];
     GValue v = G_VALUE_INIT;
 
     g_return_val_if_fail(GNC_IS_BUDGET(budget), gnc_numeric_zero());
     g_return_val_if_fail(account, gnc_numeric_zero());
 
-    make_period_path (account, period_num, path);
-    qof_instance_get_kvp (QOF_INSTANCE (budget), path, &v);
+    make_period_path (account, period_num, path_part_one, path_part_two);
+    qof_instance_get_var_kvp (QOF_INSTANCE (budget), &v, 2, path_part_one, path_part_two);
     if (G_VALUE_HOLDS_BOXED (&v))
         numeric = (gnc_numeric*)g_value_get_boxed (&v);
 
diff --git a/libgnucash/engine/gnc-commodity.c b/libgnucash/engine/gnc-commodity.c
index f218f8f..85570fb 100644
--- a/libgnucash/engine/gnc-commodity.c
+++ b/libgnucash/engine/gnc-commodity.c
@@ -1083,7 +1083,7 @@ gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm)
     GValue v = G_VALUE_INIT;
 
     if (!cm) return FALSE;
-    qof_instance_get_kvp (QOF_INSTANCE (cm), "auto_quote_control", &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
     if (G_VALUE_HOLDS_STRING (&v) &&
         strcmp(g_value_get_string (&v), "false") == 0)
         return FALSE;
@@ -1145,7 +1145,7 @@ gnc_commodity_get_user_symbol(const gnc_commodity *cm)
 {
     GValue v = G_VALUE_INIT;
     if (!cm) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE(cm), "user_symbol", &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
     if (G_VALUE_HOLDS_STRING (&v))
         return g_value_get_string (&v);
     return NULL;
@@ -1316,12 +1316,12 @@ gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm,
     }
     gnc_commodity_begin_edit(cm);
     if (flag)
-        qof_instance_set_kvp (QOF_INSTANCE (cm), "auto_quote_control", NULL);
+        qof_instance_set_var_kvp (QOF_INSTANCE (cm), NULL, 1, "auto_quote_control");
     else
     {
         g_value_init (&v, G_TYPE_STRING);
         g_value_set_string (&v, "false");
-        qof_instance_set_kvp (QOF_INSTANCE (cm), "auto_quote_control", &v);
+        qof_instance_set_var_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
     }
     mark_commodity_dirty(cm);
     gnc_commodity_commit_edit(cm);
@@ -1456,10 +1456,10 @@ gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
     {
         g_value_init (&v, G_TYPE_STRING);
         g_value_set_string (&v, user_symbol);
-        qof_instance_set_kvp (QOF_INSTANCE(cm), "user_symbol", &v);
+        qof_instance_set_var_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
     }
     else
-        qof_instance_set_kvp (QOF_INSTANCE(cm), "user_symbol", NULL);
+        qof_instance_set_var_kvp (QOF_INSTANCE(cm), NULL, 1, "user_symbol");
 
     mark_commodity_dirty(cm);
     gnc_commodity_commit_edit(cm);
diff --git a/libgnucash/engine/gnc-lot.c b/libgnucash/engine/gnc-lot.c
index 77b3bec..896a39d 100644
--- a/libgnucash/engine/gnc-lot.c
+++ b/libgnucash/engine/gnc-lot.c
@@ -148,16 +148,13 @@ gnc_lot_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec*
         g_value_set_int(value, priv->marker);
         break;
     case PROP_INVOICE:
-        key = GNC_INVOICE_ID "/" GNC_INVOICE_GUID;
-        qof_instance_get_kvp (QOF_INSTANCE (lot), key, value);
+        qof_instance_get_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
         break;
     case PROP_OWNER_TYPE:
-        key = GNC_OWNER_ID"/" GNC_OWNER_TYPE;
-        qof_instance_get_kvp (QOF_INSTANCE (lot), key, value);
+        qof_instance_get_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
         break;
     case PROP_OWNER_GUID:
-        key = GNC_OWNER_ID "/" GNC_OWNER_GUID;
-        qof_instance_get_kvp (QOF_INSTANCE (lot), key, value);
+        qof_instance_get_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -191,16 +188,13 @@ gnc_lot_set_property (GObject* object,
         priv->marker = g_value_get_int(value);
         break;
     case PROP_INVOICE:
-        key = GNC_INVOICE_ID"/" GNC_INVOICE_GUID;
-        qof_instance_set_kvp (QOF_INSTANCE (lot), key, value);
+        qof_instance_set_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
         break;
     case PROP_OWNER_TYPE:
-        key = GNC_OWNER_ID "/" GNC_OWNER_TYPE;
-        qof_instance_set_kvp (QOF_INSTANCE (lot), key, value);
+        qof_instance_set_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
         break;
     case PROP_OWNER_GUID:
-        key = GNC_OWNER_ID "/" GNC_OWNER_GUID;
-        qof_instance_set_kvp (QOF_INSTANCE (lot), key, value);
+        qof_instance_set_var_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -433,7 +427,7 @@ gnc_lot_get_title (const GNCLot *lot)
 {
     GValue v = G_VALUE_INIT;
     if (!lot) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (lot), "/title", &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (lot), &v, 1, "title");
     if (G_VALUE_HOLDS_STRING (&v))
         return g_value_get_string (&v);
     return NULL;
@@ -444,7 +438,7 @@ gnc_lot_get_notes (const GNCLot *lot)
 {
     GValue v = G_VALUE_INIT;
     if (!lot) return NULL;
-    qof_instance_get_kvp (QOF_INSTANCE (lot), "/notes", &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
     if (G_VALUE_HOLDS_STRING (&v))
         return g_value_get_string (&v);
     return NULL;
@@ -458,7 +452,7 @@ gnc_lot_set_title (GNCLot *lot, const char *str)
     qof_begin_edit(QOF_INSTANCE(lot));
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, str);
-    qof_instance_set_kvp (QOF_INSTANCE (lot), "/title", &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (lot), &v, 1, "title");
     qof_instance_set_dirty(QOF_INSTANCE(lot));
     gnc_lot_commit_edit(lot);
 }
@@ -471,7 +465,7 @@ gnc_lot_set_notes (GNCLot *lot, const char *str)
     qof_begin_edit(QOF_INSTANCE(lot));
     g_value_init (&v, G_TYPE_STRING);
     g_value_set_string (&v, str);
-    qof_instance_set_kvp (QOF_INSTANCE (lot), "/notes", &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
     qof_instance_set_dirty(QOF_INSTANCE(lot));
     gnc_lot_commit_edit(lot);
 }
diff --git a/libgnucash/engine/gncCustomer.c b/libgnucash/engine/gncCustomer.c
index 9b4ed80..b318e71 100644
--- a/libgnucash/engine/gncCustomer.c
+++ b/libgnucash/engine/gncCustomer.c
@@ -140,17 +140,14 @@ gnc_customer_get_property (GObject         *object,
         g_value_set_string(value, cust->name);
         break;
     case PROP_PDF_DIRNAME:
-	key = OWNER_EXPORT_PDF_DIRNAME;
-	qof_instance_get_kvp (QOF_INSTANCE (cust), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        break;
     case PROP_LAST_POSTED:
-	key = LAST_POSTED_TO_ACCT;
-	qof_instance_get_kvp (QOF_INSTANCE (cust), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
+        break;
     case PROP_PAYMENT_LAST_ACCT:
-	key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT;
-	qof_instance_get_kvp (QOF_INSTANCE (cust), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -177,17 +174,14 @@ gnc_customer_set_property (GObject         *object,
         gncCustomerSetName(cust, g_value_get_string(value));
         break;
     case PROP_PDF_DIRNAME:
-	key = OWNER_EXPORT_PDF_DIRNAME;
-	qof_instance_set_kvp (QOF_INSTANCE (cust), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        break;
     case PROP_LAST_POSTED:
-	key = LAST_POSTED_TO_ACCT;
-	qof_instance_set_kvp (QOF_INSTANCE (cust), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
+        break;
     case PROP_PAYMENT_LAST_ACCT:
-	key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT;
-	qof_instance_set_kvp (QOF_INSTANCE (cust), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
diff --git a/libgnucash/engine/gncEmployee.c b/libgnucash/engine/gncEmployee.c
index ebfb518..f41c388 100644
--- a/libgnucash/engine/gncEmployee.c
+++ b/libgnucash/engine/gncEmployee.c
@@ -128,10 +128,7 @@ gnc_employee_get_property (GObject         *object,
                            GParamSpec      *pspec)
 {
     GncEmployee *emp;
-    gchar *key;
-
     g_return_if_fail(GNC_IS_EMPLOYEE(object));
-
     emp = GNC_EMPLOYEE(object);
     switch (prop_id)
     {
@@ -166,17 +163,14 @@ gnc_employee_get_property (GObject         *object,
         g_value_take_object(value, emp->ccard_acc);
         break;
     case PROP_PDF_DIRNAME:
-	key = OWNER_EXPORT_PDF_DIRNAME;
-	qof_instance_get_kvp (QOF_INSTANCE (emp), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (emp), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        break;
     case PROP_LAST_POSTED:
-	key = LAST_POSTED_TO_ACCT;
-	qof_instance_get_kvp (QOF_INSTANCE (emp), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (emp), value, 1, LAST_POSTED_TO_ACCT);
+        break;
     case PROP_PAYMENT_LAST_ACCT:
-	key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT;
-	qof_instance_get_kvp (QOF_INSTANCE (emp), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (emp), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -190,13 +184,9 @@ gnc_employee_set_property (GObject         *object,
                            GParamSpec      *pspec)
 {
     GncEmployee *emp;
-    gchar *key;
-
     g_return_if_fail(GNC_IS_EMPLOYEE(object));
-
     emp = GNC_EMPLOYEE(object);
     g_assert (qof_instance_get_editlevel(emp));
-
     switch (prop_id)
     {
     case PROP_USERNAME:
@@ -230,17 +220,14 @@ gnc_employee_set_property (GObject         *object,
         gncEmployeeSetCCard(emp, g_value_get_object(value));
         break;
     case PROP_PDF_DIRNAME:
-	key = OWNER_EXPORT_PDF_DIRNAME;
-	qof_instance_set_kvp (QOF_INSTANCE (emp), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (emp), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        break;
     case PROP_LAST_POSTED:
-	key = LAST_POSTED_TO_ACCT;
-	qof_instance_set_kvp (QOF_INSTANCE (emp), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (emp), value, 1, LAST_POSTED_TO_ACCT);
+        break;
     case PROP_PAYMENT_LAST_ACCT:
-	key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT;
-	qof_instance_set_kvp (QOF_INSTANCE (emp), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (emp), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
diff --git a/libgnucash/engine/gncInvoice.c b/libgnucash/engine/gncInvoice.c
index 651865b..074270d 100644
--- a/libgnucash/engine/gncInvoice.c
+++ b/libgnucash/engine/gncInvoice.c
@@ -354,9 +354,9 @@ GncInvoice *gncInvoiceCopy (const GncInvoice *from)
     invoice->billing_id = CACHE_INSERT (from->billing_id);
     invoice->active = from->active;
 
-    qof_instance_get_kvp (QOF_INSTANCE (from), GNC_INVOICE_IS_CN, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (from), &v, 1, GNC_INVOICE_IS_CN);
     if (G_VALUE_HOLDS_INT64 (&v))
-         qof_instance_set_kvp (QOF_INSTANCE (invoice), GNC_INVOICE_IS_CN, &v);
+         qof_instance_set_var_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
 
     invoice->terms = from->terms;
     gncBillTermIncRef (invoice->terms);
@@ -551,7 +551,7 @@ void gncInvoiceSetIsCreditNote (GncInvoice *invoice, gboolean credit_note)
     gncInvoiceBeginEdit (invoice);
     g_value_init (&v, G_TYPE_INT64);
     g_value_set_int64(&v, credit_note ? 1 : 0);
-    qof_instance_set_kvp (QOF_INSTANCE (invoice), GNC_INVOICE_IS_CN, &v);
+    qof_instance_set_var_kvp (QOF_INSTANCE (invoice), &v, 1, GNC_INVOICE_IS_CN);
     mark_invoice (invoice);
     gncInvoiceCommitEdit (invoice);
 
@@ -1040,7 +1040,7 @@ gboolean gncInvoiceGetIsCreditNote (const GncInvoice *invoice)
 {
     GValue v = G_VALUE_INIT;
     if (!invoice) return FALSE;
-    qof_instance_get_kvp (QOF_INSTANCE(invoice), GNC_INVOICE_IS_CN, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE(invoice), &v, 1, GNC_INVOICE_IS_CN);
     if (G_VALUE_HOLDS_INT64(&v) && g_value_get_int64(&v))
         return TRUE;
     else
diff --git a/libgnucash/engine/gncJob.c b/libgnucash/engine/gncJob.c
index bd4c672..5d76c29 100644
--- a/libgnucash/engine/gncJob.c
+++ b/libgnucash/engine/gncJob.c
@@ -120,8 +120,7 @@ gnc_job_get_property (GObject         *object,
         g_value_set_string(value, job->name);
         break;
     case PROP_PDF_DIRNAME:
-        key = OWNER_EXPORT_PDF_DIRNAME;
-        qof_instance_get_kvp (QOF_INSTANCE (job), key, value);
+        qof_instance_get_var_kvp (QOF_INSTANCE (job), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -149,8 +148,7 @@ gnc_job_set_property (GObject         *object,
         gncJobSetName(job, g_value_get_string(value));
         break;
     case PROP_PDF_DIRNAME:
-        key = OWNER_EXPORT_PDF_DIRNAME;
-        qof_instance_set_kvp (QOF_INSTANCE (job), key, value);
+        qof_instance_set_var_kvp (QOF_INSTANCE (job), value, 1, OWNER_EXPORT_PDF_DIRNAME);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -318,11 +316,11 @@ void gncJobSetRate (GncJob *job, gnc_numeric rate)
         GValue v = G_VALUE_INIT;
         g_value_init (&v, GNC_TYPE_NUMERIC);
         g_value_set_boxed (&v, &rate);
-        qof_instance_set_kvp (QOF_INSTANCE (job), GNC_JOB_RATE, &v);
+        qof_instance_set_var_kvp (QOF_INSTANCE (job), &v, 1, GNC_JOB_RATE);
     }
     else
     {
-        qof_instance_set_kvp (QOF_INSTANCE (job), GNC_JOB_RATE, NULL);
+        qof_instance_set_var_kvp (QOF_INSTANCE (job), NULL, 1, GNC_JOB_RATE);
     }
     mark_job (job);
     gncJobCommitEdit (job);
@@ -456,7 +454,7 @@ gnc_numeric gncJobGetRate (const GncJob *job)
     GValue v = G_VALUE_INIT;
     gnc_numeric *rate = NULL;
     if (!job) return gnc_numeric_zero ();
-    qof_instance_get_kvp (QOF_INSTANCE (job), GNC_JOB_RATE, &v);
+    qof_instance_get_var_kvp (QOF_INSTANCE (job), &v, 1, GNC_JOB_RATE);
     if (G_VALUE_HOLDS_BOXED (&v))
         rate = (gnc_numeric*)g_value_get_boxed (&v);
     if (rate)
diff --git a/libgnucash/engine/gncVendor.c b/libgnucash/engine/gncVendor.c
index 3ebdb2b..1df3fab 100644
--- a/libgnucash/engine/gncVendor.c
+++ b/libgnucash/engine/gncVendor.c
@@ -179,17 +179,14 @@ gnc_vendor_get_property (GObject         *object,
         g_value_set_string(value, qofVendorGetTaxIncluded(vendor));
         break;
     case PROP_PDF_DIRNAME:
-	key = OWNER_EXPORT_PDF_DIRNAME;
-	qof_instance_get_kvp (QOF_INSTANCE (vendor), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (vendor), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        break;
     case PROP_LAST_POSTED:
-	key = LAST_POSTED_TO_ACCT;
-	qof_instance_get_kvp (QOF_INSTANCE (vendor), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (vendor), value, 1, LAST_POSTED_TO_ACCT);
+        break;
     case PROP_PAYMENT_LAST_ACCT:
-	key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT;
-	qof_instance_get_kvp (QOF_INSTANCE (vendor), key, value);
-	break;
+        qof_instance_get_var_kvp (QOF_INSTANCE (vendor), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -246,17 +243,14 @@ gnc_vendor_set_property (GObject         *object,
         qofVendorSetTaxIncluded(vendor, g_value_get_string(value));
         break;
     case PROP_PDF_DIRNAME:
-	key = OWNER_EXPORT_PDF_DIRNAME;
-	qof_instance_set_kvp (QOF_INSTANCE (vendor), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (vendor), value, 1, OWNER_EXPORT_PDF_DIRNAME);
+        break;
     case PROP_LAST_POSTED:
-	key = LAST_POSTED_TO_ACCT;
-	qof_instance_set_kvp (QOF_INSTANCE (vendor), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (vendor), value, 1, LAST_POSTED_TO_ACCT);
+        break;
     case PROP_PAYMENT_LAST_ACCT:
-	key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT;
-	qof_instance_set_kvp (QOF_INSTANCE (vendor), key, value);
-	break;
+        qof_instance_set_var_kvp (QOF_INSTANCE (vendor), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
diff --git a/libgnucash/engine/kvp-frame.cpp b/libgnucash/engine/kvp-frame.cpp
index 29fe927..eff478a 100644
--- a/libgnucash/engine/kvp-frame.cpp
+++ b/libgnucash/engine/kvp-frame.cpp
@@ -84,6 +84,11 @@ make_vector(std::string key)
     return path;
 }
 
+/*
+ * If the key is delimited, calls set(path) with the parsed result
+ * otherwise, inserts the key and value locally and returns the
+ * old value if there was one.
+ */
 KvpValue*
 KvpFrameImpl::set(const char* key, KvpValue* value) noexcept
 {
@@ -123,6 +128,10 @@ walk_path_or_nullptr(const KvpFrameImpl* frame, Path& path)
     return cur_frame;
 }
 
+/*
+ * If the last path parameter has a delimiter, the path before that point is ignored,
+ * and set is called with only the last parameter with the delimiter as the key.
+ */
 KvpValue*
 KvpFrameImpl::set(Path path, KvpValue* value) noexcept
 {
diff --git a/libgnucash/engine/kvp-frame.hpp b/libgnucash/engine/kvp-frame.hpp
index 49572b8..6bdb16f 100644
--- a/libgnucash/engine/kvp-frame.hpp
+++ b/libgnucash/engine/kvp-frame.hpp
@@ -44,12 +44,8 @@
  * KVP modifications are written to the database. Two generic abstractions are
  * provided:
  *
- * * @ref qof_instance_set_kvp and @ref qof_instance_get_kvp provide single-item
-     access via GValues to support object properties.
-
  * * @ref qof_book_set_option and @ref qof_book_get_option provide similar
-     access for book options.
-
+ *   access for book options.
  *
  * @ref kvpvalues provides a catolog of KVP entries including what objects
  * they're part of and how they're used.
diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index 4a57af9..1694ec4 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -142,62 +142,40 @@ qof_book_get_property (GObject* object,
     switch (prop_id)
     {
     case PROP_OPT_TRADING_ACCOUNTS:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-			       OPTION_NAME_TRADING_ACCOUNTS);
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_TRADING_ACCOUNTS});
+        break;
     case PROP_OPT_BOOK_CURRENCY:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-                   OPTION_NAME_BOOK_CURRENCY);
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
+        break;
     case PROP_OPT_DEFAULT_GAINS_POLICY:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-                   OPTION_NAME_DEFAULT_GAINS_POLICY);
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
+        break;
     case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-                   OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID);
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
+        break;
     case PROP_OPT_AUTO_READONLY_DAYS:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-			       OPTION_NAME_AUTO_READONLY_DAYS);
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_AUTO_READONLY_DAYS});
+        break;
     case PROP_OPT_NUM_FIELD_SOURCE:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-			       OPTION_NAME_NUM_FIELD_SOURCE);
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_NUM_FIELD_SOURCE});
+        break;
     case PROP_OPT_DEFAULT_BUDGET:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-			       OPTION_NAME_DEFAULT_BUDGET);
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_BUDGET});
+        break;
     case PROP_OPT_FY_END:
-	key = const_cast<char*>("fy_end");
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	break;
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
+        break;
     case PROP_AB_TEMPLATES:
-	key = const_cast<char*>(AB_KEY "/" AB_TEMPLATES);
-	qof_instance_get_kvp (QOF_INSTANCE (book), key, value);
-	break;
+        key = const_cast<char*>(AB_KEY "/" AB_TEMPLATES);
+        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {"AB_KEY", "AB_TEMPLATES"});
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -220,62 +198,39 @@ qof_book_set_property (GObject      *object,
     switch (prop_id)
     {
     case PROP_OPT_TRADING_ACCOUNTS:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-			       OPTION_NAME_TRADING_ACCOUNTS);
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_TRADING_ACCOUNTS});
+        break;
     case PROP_OPT_BOOK_CURRENCY:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-                   OPTION_NAME_BOOK_CURRENCY);
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
+        break;
     case PROP_OPT_DEFAULT_GAINS_POLICY:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-                   OPTION_NAME_DEFAULT_GAINS_POLICY);
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
+        break;
     case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-                   OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID);
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
+        break;
     case PROP_OPT_AUTO_READONLY_DAYS:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-			       OPTION_NAME_AUTO_READONLY_DAYS);
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_AUTO_READONLY_DAYS});
+        break;
     case PROP_OPT_NUM_FIELD_SOURCE:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-			       OPTION_NAME_NUM_FIELD_SOURCE);
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_NUM_FIELD_SOURCE});
+        break;
     case PROP_OPT_DEFAULT_BUDGET:
-	key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH,
-			       OPTION_SECTION_ACCOUNTS,
-			       OPTION_NAME_DEFAULT_BUDGET);
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	g_free (key);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {KVP_OPTION_PATH,
+                OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_BUDGET});
+        break;
     case PROP_OPT_FY_END:
-	key = const_cast<char*>("fy_end");
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
+        break;
     case PROP_AB_TEMPLATES:
-	key = const_cast<char*>(AB_KEY "/" AB_TEMPLATES);
-	qof_instance_set_kvp (QOF_INSTANCE (book), key, value);
-	break;
+        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {AB_KEY, AB_TEMPLATES});
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
diff --git a/libgnucash/engine/qofinstance-p.h b/libgnucash/engine/qofinstance-p.h
index 5ffcd9f..a54a581 100644
--- a/libgnucash/engine/qofinstance-p.h
+++ b/libgnucash/engine/qofinstance-p.h
@@ -25,6 +25,7 @@
  *
  * Copyright (C) 2003 Linas Vepstas <linas at linas.org>
  * Copyright (c) 2007 David Hampton <hampton at employees.org>
+ * Copyright 2017 Aaron Laws <dartme18 at gmail.com>
  */
 
 #ifndef QOF_INSTANCE_P_H
@@ -115,23 +116,24 @@ void qof_instance_set_idata(gpointer inst, guint32 idata);
  * @return TRUE if Kvp isn't empty.
  */
 gboolean qof_instance_has_kvp (QofInstance *inst);
-/** Sets a KVP slot to a value from a GValue. The key can be a '/'-delimited
- * path, and intermediate container frames will be created if necessary.
- * Commits the change to the QofInstance.
+
+/** Sets a KVP slot to a value from a GValue. Intermediate container
+ * frames will be created if necessary. Commits the change to the QofInstance.
  * @param inst: The QofInstance on which to set the value.
- * @param key: The key for the slot or '/'-delimited path
+ * @param key: The path to the slot.
  * @param value: A GValue containing an item of a type which KvpValue knows
- * how to store.
+ *           how to store.
  */
-void qof_instance_set_kvp (QofInstance *inst, const gchar *key, const GValue *value);
+void qof_instance_set_var_kvp (QofInstance *, GValue const * value, unsigned count, ...);
+
 /** Retrieves the contents of a KVP slot into a provided GValue.
  * @param inst: The QofInstance
- * @param key: The key of or '/'-delimited path to the slot.
+ * @param key: The path to the slot.
  * @param value: A GValue into which to store the value of the slot. It will be
  *               set to the correct type.
  */
-void qof_instance_get_kvp (const QofInstance *inst, const gchar *key, GValue
-*value);
+void qof_instance_get_var_kvp (QofInstance *, GValue * value, unsigned count, ...);
+
 /** @} Close out the DOxygen ingroup */
 /* Functions to isolate the KVP mechanism inside QOF for cases where
 GValue * operations won't work.
@@ -151,7 +153,9 @@ gboolean qof_instance_kvp_has_guid (const QofInstance *inst, const char *path,
 void qof_instance_kvp_merge_guids (const QofInstance *target,
                                    const QofInstance *donor, const char* path);
 gboolean qof_instance_has_slot (const QofInstance *inst, const char *path);
+void qof_instance_slot_var_delete (const QofInstance *, unsigned count, ...);
 void qof_instance_slot_delete (const QofInstance *inst, const char *path);
+void qof_instance_slot_var_delete_if_empty (const QofInstance *, unsigned count, ...);
 void qof_instance_slot_delete_if_empty (const QofInstance *inst,
                                         const char *path);
 void qof_instance_foreach_slot (const QofInstance *inst, const char *path,
@@ -160,6 +164,15 @@ void qof_instance_foreach_slot (const QofInstance *inst, const char *path,
 #ifdef __cplusplus
 } /* extern "C" */
 
+void qof_instance_get_path_kvp (QofInstance *, GValue *, std::vector<std::string> const &);
+
+void qof_instance_set_path_kvp (QofInstance *, GValue const *, std::vector<std::string> const &);
+
+bool qof_instance_has_path_slot (QofInstance const *, std::vector<std::string> const &);
+
+void qof_instance_slot_path_delete (QofInstance const *, std::vector<std::string> const &);
+
+void qof_instance_slot_path_delete_if_empty (QofInstance const *, std::vector<std::string> const &);
 /** Returns all keys that match the given prefix and their corresponding values.*/
 std::map<std::string, KvpValue*>
 qof_instance_get_slots_prefix (QofInstance const *, std::string const & prefix);
diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp
index 7994fb9..64826d5 100644
--- a/libgnucash/engine/qofinstance.cpp
+++ b/libgnucash/engine/qofinstance.cpp
@@ -26,6 +26,7 @@
  *
  * Copyright (C) 2003 Linas Vepstas <linas at linas.org>
  * Copyright (c) 2007 David Hampton <hampton at employees.org>
+ * Copyright 2017 Aaron Laws <dartme18 at gmail.com>
  */
 
 #include "guid.hpp"
@@ -1059,23 +1060,53 @@ qof_instance_has_kvp (QofInstance *inst)
     return (inst->kvp_data != NULL && !inst->kvp_data->empty());
 }
 
-void
-qof_instance_set_kvp (QofInstance *inst, const gchar *key, const GValue *value)
+void qof_instance_set_path_kvp (QofInstance * inst, GValue const * value, std::vector<std::string> const & path)
 {
-    delete inst->kvp_data->set_path(key, kvp_value_from_gvalue(value));
+    delete inst->kvp_data->set_path (path, kvp_value_from_gvalue (value));
 }
 
 void
-qof_instance_get_kvp (const QofInstance *inst, const gchar *key, GValue *value)
+qof_instance_set_var_kvp (QofInstance * inst, GValue const * value, unsigned count, ...)
 {
-    auto temp = gvalue_from_kvp_value (inst->kvp_data->get_slot(key));
+    std::vector<std::string> path;
+    va_list args;
+    va_start (args, count);
+    for (unsigned i{0}; i < count; ++i)
+        path.push_back (va_arg (args, char const *));
+    va_end (args);
+    delete inst->kvp_data->set_path (path, kvp_value_from_gvalue (value));
+}
+
+void qof_instance_get_path_kvp (QofInstance * inst, GValue * value, std::vector<std::string> const & path)
+{
+    auto temp = gvalue_from_kvp_value (inst->kvp_data->get_slot (path));
     if (G_IS_VALUE (temp))
     {
         if (G_IS_VALUE (value))
             g_value_unset (value);
         g_value_init (value, G_VALUE_TYPE (temp));
-	g_value_copy (temp, value);
-	gnc_gvalue_free (temp);
+        g_value_copy (temp, value);
+        gnc_gvalue_free (temp);
+    }
+}
+
+void
+qof_instance_get_var_kvp (QofInstance * inst, GValue * value, unsigned count, ...)
+{
+    std::vector<std::string> path;
+    va_list args;
+    va_start (args, count);
+    for (unsigned i{0}; i < count; ++i)
+        path.push_back (va_arg (args, char const *));
+    va_end (args);
+    auto temp = gvalue_from_kvp_value (inst->kvp_data->get_slot (path));
+    if (G_IS_VALUE (temp))
+    {
+        if (G_IS_VALUE (value))
+            g_value_unset (value);
+        g_value_init (value, G_VALUE_TYPE (temp));
+        g_value_copy (temp, value);
+        gnc_gvalue_free (temp);
     }
 }
 
@@ -1115,16 +1146,16 @@ qof_instance_kvp_add_guid (const QofInstance *inst, const char* path,
     auto container = new KvpFrame;
     container->set(key, new KvpValue(const_cast<GncGUID*>(guid)));
     container->set("date", new KvpValue(time));
-    delete inst->kvp_data->set_path(path, new KvpValue(container));
+    delete inst->kvp_data->set_path({path}, new KvpValue(container));
 }
 
 inline static gboolean
-kvp_match_guid (KvpValue *v, const char *key, const GncGUID *guid)
+kvp_match_guid (KvpValue *v, std::vector<std::string> const & path, const GncGUID *guid)
 {
     if (v->get_type() != KvpValue::Type::FRAME)
         return FALSE;
     auto frame = v->get<KvpFrame*>();
-    auto val = frame->get_slot(key);
+    auto val = frame->get_slot(path);
     if (val == nullptr || val->get_type() != KvpValue::Type::GUID)
         return FALSE;
     auto this_guid = val->get<GncGUID*>();
@@ -1139,13 +1170,13 @@ qof_instance_kvp_has_guid (const QofInstance *inst, const char *path,
     g_return_val_if_fail (inst->kvp_data != NULL, FALSE);
     g_return_val_if_fail (guid != NULL, FALSE);
 
-    auto v = inst->kvp_data->get_slot(path);
+    auto v = inst->kvp_data->get_slot({path});
     if (v == nullptr) return FALSE;
 
     switch (v->get_type())
     {
     case KvpValue::Type::FRAME:
-        return kvp_match_guid (v, key, guid);
+        return kvp_match_guid (v, {key}, guid);
         break;
     case KvpValue::Type::GLIST:
     {
@@ -1153,7 +1184,7 @@ qof_instance_kvp_has_guid (const QofInstance *inst, const char *path,
         for (auto node = list; node != NULL; node = node->next)
         {
             auto val = static_cast<KvpValue*>(node->data);
-            if (kvp_match_guid (val, key, guid))
+            if (kvp_match_guid (val, {key}, guid))
             {
                 return TRUE;
             }
@@ -1174,15 +1205,15 @@ qof_instance_kvp_remove_guid (const QofInstance *inst, const char *path,
     g_return_if_fail (inst->kvp_data != NULL);
     g_return_if_fail (guid != NULL);
 
-    auto v = inst->kvp_data->get_slot(path);
+    auto v = inst->kvp_data->get_slot({path});
     if (v == NULL) return;
 
     switch (v->get_type())
     {
     case KvpValue::Type::FRAME:
-        if (kvp_match_guid (v, key, guid))
+        if (kvp_match_guid (v, {key}, guid))
         {
-            delete inst->kvp_data->set_path(path, nullptr);
+            delete inst->kvp_data->set_path({path}, nullptr);
             delete v;
         }
         break;
@@ -1192,7 +1223,7 @@ qof_instance_kvp_remove_guid (const QofInstance *inst, const char *path,
         for (auto node = list; node != nullptr; node = node->next)
         {
             auto val = static_cast<KvpValue*>(node->data);
-            if (kvp_match_guid (val, key, guid))
+            if (kvp_match_guid (val, {key}, guid))
             {
                 list = g_list_delete_link (list, node);
                 v->set(list);
@@ -1216,19 +1247,19 @@ qof_instance_kvp_merge_guids (const QofInstance *target,
     g_return_if_fail (target != NULL);
     g_return_if_fail (donor != NULL);
 
-    if (! qof_instance_has_slot (donor, path)) return;
-    auto v = donor->kvp_data->get_slot(path);
+    if (! qof_instance_has_slot (donor, {path})) return;
+    auto v = donor->kvp_data->get_slot({path});
     if (v == NULL) return;
 
-    auto target_val = target->kvp_data->get_slot(path);
+    auto target_val = target->kvp_data->get_slot({path});
     switch (v->get_type())
     {
     case KvpValue::Type::FRAME:
         if (target_val)
             target_val->add(v);
         else
-            target->kvp_data->set_path(path, v);
-        donor->kvp_data->set(path, nullptr); //Contents moved, Don't delete!
+            target->kvp_data->set_path({path}, v);
+        donor->kvp_data->set({path}, nullptr); //Contents moved, Don't delete!
         break;
     case KvpValue::Type::GLIST:
         if (target_val)
@@ -1238,8 +1269,8 @@ qof_instance_kvp_merge_guids (const QofInstance *target,
             target_val->set(list);
         }
         else
-            target->kvp_data->set(path, v);
-        donor->kvp_data->set(path, nullptr); //Contents moved, Don't delete!
+            target->kvp_data->set({path}, v);
+        donor->kvp_data->set({path}, nullptr); //Contents moved, Don't delete!
         break;
     default:
         PWARN ("Instance KVP on path %s contains the wrong type.", path);
@@ -1247,16 +1278,67 @@ qof_instance_kvp_merge_guids (const QofInstance *target,
     }
 }
 
+bool qof_instance_has_path_slot (QofInstance const * inst, std::vector<std::string> const & path)
+{
+    return inst->kvp_data->get_slot (path) != nullptr;
+}
+
 gboolean
 qof_instance_has_slot (const QofInstance *inst, const char *path)
 {
     return inst->kvp_data->get_slot(path) != NULL;
 }
 
+void qof_instance_slot_path_delete (QofInstance const * inst, std::vector<std::string> const & path)
+{
+    delete inst->kvp_data->set (path, nullptr);
+}
+
+void
+qof_instance_slot_var_delete (QofInstance const *inst, unsigned count, ...)
+{
+    std::vector<std::string> path;
+    va_list args;
+    va_start (args, count);
+    for (unsigned i{0}; i < count; ++i)
+        path.push_back (va_arg (args, char const *));
+    va_end (args);
+    delete inst->kvp_data->set (path, nullptr);
+}
+
 void
 qof_instance_slot_delete (const QofInstance *inst, const char *path)
 {
-    inst->kvp_data->set(path, nullptr);
+    delete inst->kvp_data->set(path, nullptr);
+}
+
+void qof_instance_slot_path_delete_if_empty (QofInstance const * inst, std::vector<std::string> const & path)
+{
+    auto slot = inst->kvp_data->get_slot (path);
+    if (slot)
+    {
+        auto frame = slot->get <KvpFrame*> ();
+        if (frame && frame->empty())
+            delete inst->kvp_data->set (path, nullptr);
+    }
+}
+
+void
+qof_instance_slot_var_delete_if_empty (QofInstance const *inst, unsigned count, ...)
+{
+    std::vector<std::string> path;
+    va_list args;
+    va_start (args, count);
+    for (unsigned i{0}; i < count; ++i)
+        path.push_back (va_arg (args, char const *));
+    va_end (args);
+    auto slot = inst->kvp_data->get_slot (path);
+    if (slot)
+    {
+        auto frame = slot->get <KvpFrame*> ();
+        if (frame && frame->empty ())
+            delete inst->kvp_data->set (path, nullptr);
+    }
 }
 
 void
@@ -1267,9 +1349,10 @@ qof_instance_slot_delete_if_empty (const QofInstance *inst, const char *path)
     {
         auto frame = slot->get<KvpFrame*>();
         if (frame && frame->empty())
-            inst->kvp_data->set(path, nullptr);
+            delete inst->kvp_data->set(path, nullptr);
     }
 }
+
 namespace {
 struct wrap_param
 {

commit eb6dad86e3dc48ceaebf586b280b1fc09d8087b7
Author: lmat <dartme18 at gmail.com>
Date:   Fri Oct 27 11:09:42 2017 -0400

    Fixed conversion problem
    
    The conversion assumed there were only three levels to bayes import
    map kvp: IMAP token, user-supplied token, GUID/account name. In
    actuality, since user-supplied tokens could have the delimiter in them,
    there could be several. This fix takes that into account like so:
    IMAP token, potentially several user-supplied tokens, GUID/account name.
    
    The import map is undergoing two conversions at the same time: account names
    to guids and an hierarchical representation to a flat representation in KVP.

diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c
index 96821e5..12d5e59 100644
--- a/gnucash/gnome-utils/gnc-file.c
+++ b/gnucash/gnome-utils/gnc-file.c
@@ -1026,7 +1026,6 @@ RESTART:
     // Convert imap mappings from account full name to guid strings
     qof_event_suspend();
     gnc_account_imap_convert_bayes (gnc_get_current_book());
-    gnc_account_imap_convert_flat (gnc_get_current_book());
     qof_event_resume();
 
     return TRUE;
diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index a3603e9..8ce126d 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -43,6 +43,8 @@
 #include "gnc-features.h"
 #include "guid.hpp"
 
+#include <numeric>
+
 static QofLogModule log_module = GNC_MOD_ACCOUNT;
 
 /* The Canonical Account Separator.  Pre-Initialized. */
@@ -5269,8 +5271,8 @@ get_first_pass_probabilities(GncImportMatchMap * imap, GList * tokens)
      * in the input tokens list. */
     for (auto current_token = tokens; current_token; current_token = current_token->next)
     {
-        auto translated_token = std::string{static_cast<char const *>(current_token->data)};
-        std::replace(translated_token.begin(), translated_token.end(), '/', '-');
+        auto translated_token = std::string {static_cast <char const *> (current_token->data)};
+        std::replace (translated_token.begin (), translated_token.end (), '/', '-');
         token_accounts_info tokenInfo{};
         auto path = std::string{IMAP_FRAME_BAYES "-"} + translated_token;
         qof_instance_foreach_slot_prefix (QOF_INSTANCE (imap->acc), path, &build_token_info, tokenInfo);
@@ -5331,7 +5333,7 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
 }
 
 static void
-change_imap_entry (GncImportMatchMap *imap, gchar *kvp_path, int64_t token_count)
+change_imap_entry (GncImportMatchMap *imap, gchar const * kvp_path, int64_t token_count)
 {
     GValue value = G_VALUE_INIT;
 
@@ -5377,7 +5379,7 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
 {
     GList *current_token;
     gint64 token_count;
-    char *account_fullname, *kvp_path;
+    char *account_fullname;
     char *guid_string;
 
     ENTER(" ");
@@ -5405,28 +5407,20 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
                  skip this case here. */
         if (!current_token->data || (*((char*)current_token->data) == '\0'))
             continue;
-
         /* start off with one token for this account */
         token_count = 1;
-
         PINFO("adding token '%s'", (char*)current_token->data);
-
-        std::string translated_token {static_cast<char*>(current_token->data)};
-        std::replace(translated_token.begin(), translated_token.end(), '/', '-');
-        kvp_path = g_strdup_printf (IMAP_FRAME_BAYES "-%s-%s",
-                                    translated_token.c_str(),
-                                    guid_string);
+        std::string translated_token {static_cast <char*> (current_token->data)};
+        std::replace (translated_token.begin (), translated_token.end (), '/', '-');
+        auto path = std::string {IMAP_FRAME_BAYES} + '-' + translated_token + '-' + guid_string;
         /* change the imap entry for the account */
-        change_imap_entry (imap, kvp_path, token_count);
-        g_free (kvp_path);
+        change_imap_entry (imap, path.c_str (), token_count);
     }
-
     /* free up the account fullname and guid string */
     qof_instance_set_dirty (QOF_INSTANCE (imap->acc));
     xaccAccountCommitEdit (imap->acc);
     g_free (account_fullname);
     g_free (guid_string);
-
     LEAVE(" ");
 }
 
@@ -5655,68 +5649,48 @@ look_for_old_separator_descendants (Account *root, gchar const *full_name, const
     return new_name;
 }
 
-static Account *
-look_for_old_mapping (GncImapInfo *imapInfo)
+static std::string
+get_guid_from_account_name (Account * root, std::string const & name)
 {
-    Account       *root, *map_account = NULL;
-    const gchar   *sep = gnc_get_account_separator_string ();
-    gchar         *full_name;
-
-    PINFO("Category Head is '%s', Full Category is '%s'", imapInfo->category_head, imapInfo->full_category);
-
-    // do we have a map_account all ready, implying a guid string
-    if (imapInfo->map_account != NULL)
-        return NULL;
-
-    root = gnc_account_get_root (imapInfo->source_account);
-
-    full_name = g_strdup (imapInfo->full_category + strlen (imapInfo->category_head) + 1);
-
-    // may be top level or match with existing separator
-    map_account = gnc_account_lookup_by_full_name (root, full_name);
-
-    // do we have a valid account, if not, look for old separator
-    if (map_account == NULL)
+    auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
+    if (!map_account)
     {
-        gchar * temp_name = look_for_old_separator_descendants (root, full_name, sep);
-        g_free(full_name);
-        full_name = temp_name;
-        map_account = gnc_account_lookup_by_full_name (root, full_name); // lets try again
+        auto temp_account_name = look_for_old_separator_descendants (root, name.c_str (),
+             gnc_get_account_separator_string ());
+        map_account = gnc_account_lookup_by_full_name (root, temp_account_name);
+        g_free (temp_account_name);
     }
+    auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
+    return temp_guid.to_string ();
+}
 
-    PINFO("Full account name is '%s'", full_name);
-
-    g_free (full_name);
-
-    return map_account;
+static std::pair <std::string, KvpValue*>
+convert_entry (std::pair <std::vector <std::string>, KvpValue*> entry, Account* root)
+{
+    auto const & account_name = entry.first.back();
+    entry.first.pop_back();
+    auto guid_str = get_guid_from_account_name (root, account_name);
+    entry.first.emplace_back ("/");
+    entry.first.emplace_back (guid_str);
+    std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
+    new_key = IMAP_FRAME_BAYES + new_key;
+    std::replace (new_key.begin(), new_key.end(), '/', '-');
+    return {new_key, entry.second};
 }
 
 static std::vector<std::pair<std::string, KvpValue*>>
 get_new_guid_imap (Account * acc)
 {
     auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
-    auto slot = frame->get_slot(IMAP_FRAME_BAYES);
+    auto slot = frame->get_slot (IMAP_FRAME_BAYES);
     if (!slot)
         return {};
-    std::string const imap_frame_str {IMAP_FRAME_BAYES};
-    std::vector<std::pair<std::string, KvpValue*>> ret;
+    auto imap_frame = slot->get<KvpFrame*> ();
+    auto flat_kvp = imap_frame->flatten_kvp ();
     auto root = gnc_account_get_root (acc);
-    auto imap_frame = slot->get<KvpFrame*>();
-    imap_frame->for_each_slot_temp ([&ret, root, &imap_frame_str] (char const * token, KvpValue* val) {
-        auto token_frame = val->get<KvpFrame*>();
-        token_frame->for_each_slot_temp ([&ret, root, &imap_frame_str, token] (char const * account_name, KvpValue* val) {
-            auto map_account = gnc_account_lookup_by_full_name (root, account_name);
-            if (!map_account)
-            {
-                auto temp_account_name = look_for_old_separator_descendants (root, account_name, gnc_get_account_separator_string());
-                map_account = gnc_account_lookup_by_full_name (root, temp_account_name);
-                g_free (temp_account_name);
-            }
-            auto temp_guid = gnc::GUID{*xaccAccountGetGUID (map_account)};
-            auto guid_str = temp_guid.to_string();
-            ret.push_back({imap_frame_str + "-" + token + "-" + guid_str, val});
-        });
-    });
+    std::vector <std::pair <std::string, KvpValue*>> ret;
+    for (auto const & flat_entry : flat_kvp)
+        ret.emplace_back (convert_entry (flat_entry, root));
     return ret;
 }
 
@@ -5737,7 +5711,6 @@ convert_imap_account_bayes_to_guid (Account *acc)
 }
 
 char const * run_once_key_to_guid {"changed-bayesian-to-guid"};
-char const * run_once_key_to_flat {"changed-bayesian-to-flat"};
 
 static void
 imap_convert_bayes_to_guid (QofBook * book)
@@ -5752,55 +5725,6 @@ imap_convert_bayes_to_guid (QofBook * book)
     g_list_free (accts);
 }
 
-static std::vector<std::pair<std::string, KvpValue*>>
-get_new_flat_imap (Account * acc)
-{
-    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
-    auto slot = frame->get_slot(IMAP_FRAME_BAYES);
-    if (!slot)
-        return {};
-    std::string const imap_frame_str {IMAP_FRAME_BAYES};
-    std::vector<std::pair<std::string, KvpValue*>> ret;
-    auto root = gnc_account_get_root (acc);
-    auto imap_frame = slot->get<KvpFrame*>();
-    imap_frame->for_each_slot_temp ([&ret, &imap_frame_str] (char const * token, KvpValue* val) {
-        auto token_frame = val->get<KvpFrame*>();
-        token_frame->for_each_slot_temp ([&ret, &imap_frame_str, token] (char const * account_guid, KvpValue* val) {
-            ret.push_back({imap_frame_str + "-" + token + "-" + account_guid, val});
-        });
-    });
-    return ret;
-}
-
-static void
-convert_imap_account_bayes_to_flat (Account * acc)
-{
-    auto flat_imap = get_new_flat_imap (acc);
-    if (!flat_imap.size())
-        return;
-    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
-    xaccAccountBeginEdit(acc);
-    frame->set(IMAP_FRAME_BAYES, nullptr);
-    std::for_each(flat_imap.begin(), flat_imap.end(), [&frame] (std::pair<std::string, KvpValue*> const & entry) {
-        frame->set(entry.first.c_str(), entry.second);
-    });
-    qof_instance_set_dirty (QOF_INSTANCE (acc));
-    xaccAccountCommitEdit(acc);
-}
-
-static void
-imap_convert_bayes_to_flat (QofBook * book)
-{
-    auto root = gnc_book_get_root_account (book);
-    auto accts = gnc_account_get_descendants_sorted (root);
-    for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
-    {
-        Account *acc = static_cast <Account*> (ptr->data);
-        convert_imap_account_bayes_to_flat (acc);
-    }
-    g_list_free (accts);
-}
-
 static bool
 run_once_key_set (char const * key, QofBook * book)
 {
@@ -5819,21 +5743,12 @@ set_run_once_key (char const * key, QofBook * book)
 }
 
 void
-gnc_account_imap_convert_flat (QofBook *book)
-{
-    if (run_once_key_set(run_once_key_to_flat, book))
-        return;
-    imap_convert_bayes_to_flat (book);
-    set_run_once_key(run_once_key_to_flat, book);
-}
-
-void
 gnc_account_imap_convert_bayes (QofBook *book)
 {
-    if (run_once_key_set(run_once_key_to_guid, book))
+    if (run_once_key_set (run_once_key_to_guid, book))
         return;
-    imap_convert_bayes_to_guid(book);
-    set_run_once_key(run_once_key_to_guid, book);
+    imap_convert_bayes_to_guid (book);
+    set_run_once_key (run_once_key_to_guid, book);
 }
 
 /* ================================================================ */
@@ -5843,7 +5758,6 @@ static void
 gnc_account_book_end(QofBook* book)
 {
     Account *root_account = gnc_book_get_root_account(book);
-
     xaccAccountBeginEdit(root_account);
     xaccAccountDestroy(root_account);
 }
diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h
index 7c93259..2f80632 100644
--- a/libgnucash/engine/Account.h
+++ b/libgnucash/engine/Account.h
@@ -1454,10 +1454,6 @@ void gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean e
  */
 void gnc_account_imap_convert_bayes (QofBook *book);
 
-/** Change the bayes imap entries from a nested representation to a flat representation.
- */
-void gnc_account_imap_convert_flat (QofBook *);
-
 /** @} */
 
 
diff --git a/libgnucash/engine/kvp-frame.cpp b/libgnucash/engine/kvp-frame.cpp
index 9461d01..29fe927 100644
--- a/libgnucash/engine/kvp-frame.cpp
+++ b/libgnucash/engine/kvp-frame.cpp
@@ -38,6 +38,7 @@ extern "C"
 #include <sstream>
 #include <algorithm>
 #include <vector>
+#include <numeric>
 
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = "qof.kvp";
@@ -473,7 +474,7 @@ gnc_value_list_get_type (void)
 }
 
 void
-KvpFrame::flatten_kvp_impl(std::vector<std::string> path, std::vector<std::pair<std::string, KvpValue*>> & entries) const
+KvpFrame::flatten_kvp_impl(std::vector <std::string> path, std::vector <std::pair <std::vector <std::string>, KvpValue*>> & entries) const
 {
     for (auto const & entry : m_valuemap)
     {
@@ -486,16 +487,17 @@ KvpFrame::flatten_kvp_impl(std::vector<std::string> path, std::vector<std::pair<
         }
         else
         {
-            std::string flat_path {std::accumulate(path.begin(), path.end(), std::string{})};
-            entries.emplace_back(flat_path + "/" + entry.first, entry.second);
+            std::vector <std::string> new_path {path};
+            new_path.emplace_back (entry.first);
+            entries.emplace_back (new_path, entry.second);
         }
     }
 }
 
-std::vector<std::pair<std::string, KvpValue*>>
+std::vector <std::pair <std::vector <std::string>, KvpValue*>>
 KvpFrame::flatten_kvp(void) const
 {
-    std::vector<std::pair<std::string, KvpValue*>> ret;
+    std::vector <std::pair <std::vector <std::string>, KvpValue*>> ret;
     flatten_kvp_impl({}, ret);
     return ret;
 }
diff --git a/libgnucash/engine/kvp-frame.hpp b/libgnucash/engine/kvp-frame.hpp
index 0cc206e..49572b8 100644
--- a/libgnucash/engine/kvp-frame.hpp
+++ b/libgnucash/engine/kvp-frame.hpp
@@ -213,6 +213,10 @@ struct KvpFrameImpl
      */
     KvpValue* get_slot(Path keys) const noexcept;
 
+    /**
+     * proc is called with each of the immediate contents of this frame, passing it the key,
+     * value, and specified data.
+     */
     void for_each_slot(void (*proc)(const char *key, KvpValue *, void *data), void* data) const noexcept;
 
     /** The function should be of the form:
@@ -239,7 +243,7 @@ struct KvpFrameImpl
      * Returns all keys and values of this frame recursively, flattening
      * the frame-containing values.
      */
-    std::vector<std::pair<std::string, KvpValue*>>
+    std::vector <std::pair <std::vector <std::string>, KvpValue*>>
     flatten_kvp(void) const;
 
     /** Test for emptiness
@@ -251,7 +255,7 @@ struct KvpFrameImpl
     private:
     map_type m_valuemap;
 
-    void flatten_kvp_impl(std::vector<std::string>, std::vector<std::pair<std::string, KvpValue*>> &) const;
+    void flatten_kvp_impl(std::vector <std::string>, std::vector <std::pair <std::vector <std::string>, KvpValue*>> &) const;
 };
 
 template<typename func_type>
diff --git a/libgnucash/engine/test/gtest-import-map.cpp b/libgnucash/engine/test/gtest-import-map.cpp
index 5938f40..117fe59 100644
--- a/libgnucash/engine/test/gtest-import-map.cpp
+++ b/libgnucash/engine/test/gtest-import-map.cpp
@@ -318,7 +318,6 @@ TEST_F(ImapBayesTest, AddAccountBayes)
     EXPECT_EQ(2, value->get<int64_t>());
 }
 
-
 TEST_F(ImapBayesTest, ConvertAccountBayes)
 {
     // prevent the embedded beginedit/committedit from doing anything
@@ -326,18 +325,15 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
     gnc_account_imap_add_account_bayes(t_imap, t_list1, t_expense_account1); //Food
     gnc_account_imap_add_account_bayes(t_imap, t_list2, t_expense_account2); //Drink
-
     auto root = qof_instance_get_slots(QOF_INSTANCE(t_bank_account));
     auto book = qof_instance_get_slots(QOF_INSTANCE(t_imap->book));
     auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1)); //Food
     auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2)); //Drink
     auto acct3_guid = guid_to_string (xaccAccountGetGUID(t_asset_account2)); //Asset-Bank
     auto acct4_guid = guid_to_string (xaccAccountGetGUID(t_sav_account)); //Sav Bank
-
     auto val1 = new KvpValue(static_cast<int64_t>(10));
     auto val2 = new KvpValue(static_cast<int64_t>(5));
     auto val3 = new KvpValue(static_cast<int64_t>(2));
-
     // Test for existing entries, all will be 1
     auto value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
@@ -347,84 +343,36 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     EXPECT_EQ(1, value->get<int64_t>());
     value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-
     // Set up some old entries
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pepper + "/" + "Asset-Bank").c_str(), val1);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/token/with/slashes/" + "Asset-Bank").c_str(), val1);
     root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + "Asset-Bank#Bank").c_str(), new KvpValue{*val1});
     root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + "Asset>Bank#Bank").c_str(), val2);
     root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pork + "/" + "Expense#Food").c_str(), new KvpValue{*val2});
     root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + sausage + "/" + "Expense#Drink").c_str(), val3);
     root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + "Expense#Food").c_str(), new KvpValue{*val2});
-
     EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
     EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
     qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
-
     // Start Convert
     gnc_account_imap_convert_bayes (t_imap->book);
-
     // convert from 'Asset-Bank' to 'Asset-Bank' guid
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct3_guid).c_str());
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-token-with-slashes-" + acct3_guid).c_str());
     EXPECT_EQ(10, value->get<int64_t>());
-
     // convert from 'Asset-Bank#Bank' to 'Sav Bank' guid
     value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct4_guid).c_str());
     EXPECT_EQ(10, value->get<int64_t>());
-
     // convert from 'Expense#Food' to 'Food' guid
     value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pork + "-" + acct1_guid).c_str());
     EXPECT_EQ(5, value->get<int64_t>());
-
     // convert from 'Expense#Drink' to 'Drink' guid
     value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + sausage + "-" + acct2_guid).c_str());
     EXPECT_EQ(2, value->get<int64_t>());
-
     // convert from 'Expense#Food' to 'Food' guid but add to original value
     value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str());
     EXPECT_EQ(5, value->get<int64_t>());
-
     // Check for run once flag
     auto vals = book->get_slot("changed-bayesian-to-guid");
     EXPECT_STREQ("true", vals->get<const char*>());
-
-    EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
-    EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
-}
-
-TEST_F (ImapBayesTest, convert_map_flat)
-{
-    // prevent the embedded beginedit/committedit from doing anything
-    qof_instance_increase_editlevel(QOF_INSTANCE(t_bank_account));
-    qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
-    //gnc_account_imap_add_account_bayes(t_imap, t_list1, t_expense_account1); //Food
-    //gnc_account_imap_add_account_bayes(t_imap, t_list2, t_expense_account2); //Drink
-    auto root = qof_instance_get_slots(QOF_INSTANCE(t_bank_account));
-    auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1)); //Food
-    auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2)); //Drink
-    auto acct3_guid = guid_to_string (xaccAccountGetGUID(t_asset_account2)); //Asset-Bank
-    auto acct4_guid = guid_to_string (xaccAccountGetGUID(t_sav_account)); //Sav Bank
-    auto val1 = new KvpValue(static_cast<int64_t>(10));
-    auto val2 = new KvpValue(static_cast<int64_t>(5));
-    auto val3 = new KvpValue(static_cast<int64_t>(2));
-    // Set up some old entries
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pepper + "/" + acct1_guid).c_str(), val1);
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + acct2_guid).c_str(), new KvpValue{*val1});
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + acct3_guid).c_str(), val2);
-    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pork + "/" + acct4_guid).c_str(), val3);
-    EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
-    qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
-    gnc_account_imap_convert_flat (t_imap->book);
-    auto value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid).c_str());
-    EXPECT_EQ(10, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid).c_str());
-    EXPECT_EQ(10, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct3_guid).c_str());
-    EXPECT_EQ(5, value->get<int64_t>());
-    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pork + "-" + acct4_guid).c_str());
-    EXPECT_EQ(2, value->get<int64_t>());
-    auto book = qof_instance_get_slots(QOF_INSTANCE(t_imap->book));
-    auto vals = book->get_slot("changed-bayesian-to-flat");
-    EXPECT_STREQ("true", vals->get<const char*>());
     EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
     EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
 }

commit b3667c76fcca6f374b0e7a52da1023e39713c5da
Author: lmat <dartme18 at gmail.com>
Date:   Thu Oct 19 15:42:32 2017 -0400

    Implement flat bayes kvp
    
    The bayes data are stored in the KVP store. Before this commit, they are
    stored under /import-map-bayes/<token>/<account guid>/count (where count
    is the datum that "matters" in bayes matching).
    
    The problem with this is that any token including the kvp delimiter
    (currently '/') gets divided, and is not found correctly during bayes
    kvp searching. The quickest solution to this is to replace all "/"
    characters with some other character. That has been done, along with a
    re-structuring of the bayes matching code to take advantage of c++
    features to make the code more concise and readable.
    
    Also modified some test functions to fix leaks and double-frees: the
    same kvp value can't be in the kvp tree twice.
    
    Also, when I added code to clean up after the tests, some things started
    breaking due to double-delete. Apparently const_cast was hiding some
    programming errors. Really? You don't say? When giving a GUID* to KvpValue,
    the latter takes ownership of the former.

diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c
index 12d5e59..96821e5 100644
--- a/gnucash/gnome-utils/gnc-file.c
+++ b/gnucash/gnome-utils/gnc-file.c
@@ -1026,6 +1026,7 @@ RESTART:
     // Convert imap mappings from account full name to guid strings
     qof_event_suspend();
     gnc_account_imap_convert_bayes (gnc_get_current_book());
+    gnc_account_imap_convert_flat (gnc_get_current_book());
     qof_event_resume();
 
     return TRUE;
diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp
index 1b32f9d..a3603e9 100644
--- a/libgnucash/engine/Account.cpp
+++ b/libgnucash/engine/Account.cpp
@@ -41,6 +41,7 @@
 #include "gnc-pricedb.h"
 #include "qofinstance-p.h"
 #include "gnc-features.h"
+#include "guid.hpp"
 
 static QofLogModule log_module = GNC_MOD_ACCOUNT;
 
@@ -5181,48 +5182,6 @@ gnc_account_imap_delete_account (GncImportMatchMap *imap,
 --------------------------------------------------------------------------*/
 
 
-struct account_token_count
-{
-    char* account_guid;
-    gint64 token_count; /**< occurrences of a given token for this account_guid */
-};
-
-/** total_count and the token_count for a given account let us calculate the
- * probability of a given account with any single token
- */
-struct token_accounts_info
-{
-    GList *accounts; /**< array of struct account_token_count */
-    gint64 total_count;
-};
-
-/** gpointer is a pointer to a struct token_accounts_info
- * \note Can always assume that keys are unique, reduces code in this function
- */
-static void
-buildTokenInfo(const char *key, const GValue *value, gpointer data)
-{
-    struct token_accounts_info *tokenInfo = (struct token_accounts_info*)data;
-    struct account_token_count* this_account;
-
-    //  PINFO("buildTokenInfo: account '%s', token_count: '%" G_GINT64_FORMAT "'", (char*)key,
-    //                  g_value_get_int64(value));
-
-    /* add the count to the total_count */
-    tokenInfo->total_count += g_value_get_int64(value);
-
-    /* allocate a new structure for this account and it's token count */
-    this_account = (struct account_token_count*)
-                   g_new0(struct account_token_count, 1);
-
-    /* fill in the account guid and number of tokens found for this account guid */
-    this_account->account_guid = (char*)key;
-    this_account->token_count = g_value_get_int64(value);
-
-    /* append onto the glist a pointer to the new account_token_count structure */
-    tokenInfo->accounts = g_list_prepend(tokenInfo->accounts, this_account);
-}
-
 /** intermediate values used to calculate the bayes probability of a given account
   where p(AB) = (a*b)/[a*b + (1-a)(1-b)], product is (a*b),
   product_difference is (1-a) * (1-b)
@@ -5233,245 +5192,144 @@ struct account_probability
     double product_difference; /* product of (1-probabilities) */
 };
 
-/** convert a hash table of account names and (struct account_probability*)
-  into a hash table of 100000x the percentage match value, ie. 10% would be
-  0.10 * 100000 = 10000
- */
-#define PROBABILITY_FACTOR 100000
-static void
-buildProbabilities(gpointer key, gpointer value, gpointer data)
+struct account_token_count
 {
-    GHashTable *final_probabilities = (GHashTable*)data;
-    struct account_probability *account_p = (struct account_probability*)value;
-
-    /* P(AB) = A*B / [A*B + (1-A)*(1-B)]
-     * NOTE: so we only keep track of a running product(A*B*C...)
-     * and product difference ((1-A)(1-B)...)
-     */
-    gint32 probability =
-        (account_p->product /
-         (account_p->product + account_p->product_difference))
-        * PROBABILITY_FACTOR;
-
-    PINFO("P('%s') = '%d'", (char*)key, probability);
-
-    g_hash_table_insert(final_probabilities, key, GINT_TO_POINTER(probability));
-}
+    std::string account_guid;
+    int64_t token_count; /** occurrences of a given token for this account_guid */
+};
 
-/** Frees an array of the same time that buildProperties built */
-static void
-freeProbabilities(gpointer key, gpointer value, gpointer data)
+/** total_count and the token_count for a given account let us calculate the
+ * probability of a given account with any single token
+ */
+struct token_accounts_info
 {
-    /* free up the struct account_probability that was allocated
-     * in gnc_account_find_account_bayes()
-     */
-    g_free(value);
-}
+    std::vector<account_token_count> accounts;
+    int64_t total_count;
+};
 
 /** holds an account guid and its corresponding integer probability
   the integer probability is some factor of 10
  */
 struct account_info
 {
-    char* account_guid;
-    gint32 probability;
+    std::string account_guid;
+    int32_t probability;
 };
 
-/** Find the highest probability and the corresponding account guid
-    store in data, a (struct account_info*)
-    NOTE: this is a g_hash_table_foreach() function for a hash table of entries
-    key is a  pointer to the account guid, value is a gint32, 100000x
-    the probability for this account
-*/
 static void
-highestProbability(gpointer key, gpointer value, gpointer data)
+build_token_info(char const * key, KvpValue * value, token_accounts_info & tokenInfo)
 {
-    struct account_info *account_i = (struct account_info*)data;
-
-    /* if the current probability is greater than the stored, store the current */
-    if (GPOINTER_TO_INT(value) > account_i->probability)
-    {
-        /* Save the new highest probability and the assoaciated account guid */
-        account_i->probability = GPOINTER_TO_INT(value);
-        account_i->account_guid = static_cast <char*> (key);
-    }
+    tokenInfo.total_count += value->get<int64_t>();
+    account_token_count this_account;
+    std::string account_guid {key};
+    /*By convention, the key ends with the account GUID.*/
+    this_account.account_guid = account_guid.substr(account_guid.size() - GUID_ENCODING_LENGTH);
+    this_account.token_count = value->get<int64_t>();
+    tokenInfo.accounts.push_back(this_account);
 }
 
+/** We scale the probability values by PROBABILITY_FACTOR.
+  ie. with PROBABILITY_FACTOR of 100000, 10% would be
+  0.10 * 100000 = 10000 */
+#define PROBABILITY_FACTOR 100000
 
-#define threshold (.90 * PROBABILITY_FACTOR) /* 90% */
-
-/** Look up an Account in the map */
-Account*
-gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
+static std::vector<std::pair<std::string, int32_t>>
+build_probabilities(std::vector<std::pair<std::string, account_probability>> const & first_pass)
 {
-    struct token_accounts_info tokenInfo; /**< holds the accounts and total
-                                           * token count for a single token */
-    GList *current_token;                 /**< pointer to the current
-                                           * token from the input GList
-                                           * tokens */
-    GList *current_account_token;         /**< pointer to the struct
-                                           * account_token_count */
-    struct account_token_count *account_c; /**< an account name and the number
-                                            * of times a token has appeared
-                                            * for the account */
-    struct account_probability *account_p; /**< intermediate storage of values
-                                            * to compute the bayes probability
-                                            * of an account */
-    GHashTable *running_probabilities = g_hash_table_new(g_str_hash,
-                                                         g_str_equal);
-    GHashTable *final_probabilities = g_hash_table_new(g_str_hash,
-                                                       g_str_equal);
-    struct account_info account_i;
-
-    ENTER(" ");
-
-    /* check to see if the imap is NULL */
-    if (!imap)
+    std::vector<std::pair<std::string, int32_t>> ret;
+    for (auto const & first_pass_prob : first_pass)
     {
-        PINFO("imap is null, returning null");
-        LEAVE(" ");
-        return NULL;
+        auto const & account_probability = first_pass_prob.second;
+        /* P(AB) = A*B / [A*B + (1-A)*(1-B)]
+         * NOTE: so we only keep track of a running product(A*B*C...)
+         * and product difference ((1-A)(1-B)...)
+         */
+        int32_t probability = (account_probability.product /
+                (account_probability.product + account_probability.product_difference)) * PROBABILITY_FACTOR;
+        ret.push_back({first_pass_prob.first, probability});
     }
+    return ret;
+}
+
+static account_info
+highest_probability(std::vector<std::pair<std::string, int32_t>> const & probabilities)
+{
+    account_info ret {"", std::numeric_limits<int32_t>::min()};
+    for (auto const & prob : probabilities)
+        if (prob.second > ret.probability)
+            ret = account_info{prob.first, prob.second};
+    return ret;
+}
 
+static std::vector<std::pair<std::string, account_probability>>
+get_first_pass_probabilities(GncImportMatchMap * imap, GList * tokens)
+{
+    std::vector<std::pair<std::string, account_probability>> ret;
     /* find the probability for each account that contains any of the tokens
-     * in the input tokens list
-     */
-    for (current_token = tokens; current_token;
-         current_token = current_token->next)
+     * in the input tokens list. */
+    for (auto current_token = tokens; current_token; current_token = current_token->next)
     {
-        char* path = g_strdup_printf (IMAP_FRAME_BAYES "/%s",
-                                            (char*)current_token->data);
-        /* zero out the token_accounts_info structure */
-        memset(&tokenInfo, 0, sizeof(struct token_accounts_info));
-
-        PINFO("token: '%s'", (char*)current_token->data);
-
-        /* process the accounts for this token, adding the account if it
-         * doesn't already exist or adding to the existing accounts token
-         * count if it does
-         */
-        qof_instance_foreach_slot(QOF_INSTANCE (imap->acc), path,
-                                  buildTokenInfo, &tokenInfo);
-        g_free (path);
-        /* for each account we have just found, see if the account
-         * already exists in the list of account probabilities, if not
-         * add it
-         */
-        for (current_account_token = tokenInfo.accounts; current_account_token;
-                current_account_token = current_account_token->next)
+        auto translated_token = std::string{static_cast<char const *>(current_token->data)};
+        std::replace(translated_token.begin(), translated_token.end(), '/', '-');
+        token_accounts_info tokenInfo{};
+        auto path = std::string{IMAP_FRAME_BAYES "-"} + translated_token;
+        qof_instance_foreach_slot_prefix (QOF_INSTANCE (imap->acc), path, &build_token_info, tokenInfo);
+        for (auto const & current_account_token : tokenInfo.accounts)
         {
-            /* get the account name and corresponding token count */
-            account_c = (struct account_token_count*)current_account_token->data;
-
-            PINFO("account_c->account_guid('%s'), "
-                  "account_c->token_count('%" G_GINT64_FORMAT
-                  "')/total_count('%" G_GINT64_FORMAT "')",
-                  account_c->account_guid, account_c->token_count,
-                  tokenInfo.total_count);
-
-            account_p = static_cast <account_probability*> (
-                    g_hash_table_lookup(running_probabilities, account_c->account_guid));
-
-            /* if the account exists in the list then continue
-             * the running probablities
-             */
-            if (account_p)
-            {
-                account_p->product = (((double)account_c->token_count /
-                                      (double)tokenInfo.total_count)
-                                      * account_p->product);
-                account_p->product_difference =
-                    ((double)1 - ((double)account_c->token_count /
-                                  (double)tokenInfo.total_count))
-                    * account_p->product_difference;
-                PINFO("product == %f, product_difference == %f",
-                      account_p->product, account_p->product_difference);
+            auto item = std::find_if(ret.begin(), ret.end(), [&current_account_token]
+                (std::pair<std::string, account_probability> const & a) {
+                    return current_account_token.account_guid == a.first;
+                });
+            if (item != ret.end())
+            {/* This account is already in the map */
+                item->second.product = ((double)current_account_token.token_count /
+                                      (double)tokenInfo.total_count) * item->second.product;
+                item->second.product_difference = ((double)1 - ((double)current_account_token.token_count /
+                                              (double)tokenInfo.total_count)) * item->second.product_difference;
             }
             else
             {
                 /* add a new entry */
-                PINFO("adding a new entry for this account");
-                account_p = (struct account_probability*)
-                            g_new0(struct account_probability, 1);
-
-                /* set the product and product difference values */
-                account_p->product = ((double)account_c->token_count /
+                account_probability new_probability;
+                new_probability.product = ((double)current_account_token.token_count /
                                       (double)tokenInfo.total_count);
-                account_p->product_difference =
-                    (double)1 - ((double)account_c->token_count /
-                                 (double)tokenInfo.total_count);
-
-                PINFO("product == %f, product_difference == %f",
-                      account_p->product, account_p->product_difference);
-
-                /* add the account guid and (struct account_probability*)
-                 * to the hash table */
-                g_hash_table_insert(running_probabilities,
-                                    account_c->account_guid, account_p);
+                new_probability.product_difference = 1 - (new_probability.product);
+                ret.push_back({current_account_token.account_guid, std::move(new_probability)});
             }
         } /* for all accounts in tokenInfo */
-
-        /* free the data in tokenInfo */
-        for (current_account_token = tokenInfo.accounts; current_account_token;
-                current_account_token = current_account_token->next)
-        {
-            /* free up each struct account_token_count we allocated */
-            g_free((struct account_token_count*)current_account_token->data);
-        }
-
-        g_list_free(tokenInfo.accounts); /* free the accounts GList */
     }
+    return ret;
+}
 
-    /* build a hash table of account names and their final probabilities
-     * from each entry in the running_probabilties hash table
-     */
-    g_hash_table_foreach(running_probabilities, buildProbabilities,
-                         final_probabilities);
-
-    /* find the highest probabilty and the corresponding account */
-    memset(&account_i, 0, sizeof(struct account_info));
-    g_hash_table_foreach(final_probabilities, highestProbability, &account_i);
-
-    /* free each element of the running_probabilities hash */
-    g_hash_table_foreach(running_probabilities, freeProbabilities, NULL);
-
-    /* free the hash tables */
-    g_hash_table_destroy(running_probabilities);
-    g_hash_table_destroy(final_probabilities);
-
-    PINFO("highest P('%s') = '%d'",
-          account_i.account_guid ? account_i.account_guid : "(null)",
-          account_i.probability);
-
-    /* has this probability met our threshold? */
-    if (account_i.probability >= threshold)
-    {
-        GncGUID *guid;
-        Account *account = NULL;
-
-        PINFO("Probability has met threshold");
-
-        guid = g_new (GncGUID, 1);
-
-        if (string_to_guid (account_i.account_guid, guid))
-            account = xaccAccountLookup (guid, imap->book);
-
-        g_free (guid);
-
-        if (account != NULL)
-            LEAVE("Return account is '%s'", xaccAccountGetName (account));
-        else
-            LEAVE("Return NULL, account for Guid '%s' can not be found", account_i.account_guid);
+#define threshold (.90 * PROBABILITY_FACTOR) /* 90% */
 
-        return account;
+/** Look up an Account in the map */
+Account*
+gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
+{
+    if (!imap)
+        return nullptr;
+    auto first_pass = get_first_pass_probabilities(imap, tokens);
+    if (!first_pass.size())
+        return nullptr;
+    auto final_probabilities = build_probabilities(first_pass);
+    if (!final_probabilities.size())
+        return nullptr;
+    auto best = highest_probability(final_probabilities);
+    if (best.account_guid == "")
+        return nullptr;
+    if (best.probability < threshold)
+        return nullptr;
+    gnc::GUID guid;
+    try {
+        guid = gnc::GUID::from_string(best.account_guid);
+    } catch (gnc::guid_syntax_exception) {
+        return nullptr;
     }
-    PINFO("Probability has not met threshold");
-    LEAVE("Return NULL");
-
-    return NULL; /* we didn't meet our threshold, return NULL for an account */
+    auto account = xaccAccountLookup (reinterpret_cast<GncGUID*>(&guid), imap->book);
+    return account;
 }
 
-
 static void
 change_imap_entry (GncImportMatchMap *imap, gchar *kvp_path, int64_t token_count)
 {
@@ -5553,13 +5411,13 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
 
         PINFO("adding token '%s'", (char*)current_token->data);
 
-        kvp_path = g_strdup_printf (IMAP_FRAME_BAYES "/%s/%s",
-                                    (char*)current_token->data,
+        std::string translated_token {static_cast<char*>(current_token->data)};
+        std::replace(translated_token.begin(), translated_token.end(), '/', '-');
+        kvp_path = g_strdup_printf (IMAP_FRAME_BAYES "-%s-%s",
+                                    translated_token.c_str(),
                                     guid_string);
-
         /* change the imap entry for the account */
         change_imap_entry (imap, kvp_path, token_count);
-
         g_free (kvp_path);
     }
 
@@ -5575,13 +5433,15 @@ gnc_account_imap_add_account_bayes (GncImportMatchMap *imap,
 /*******************************************************************************/
 
 static void
-build_bayes_layer_two (const char *key, const GValue *value, gpointer user_data)
+build_non_bayes (const char *key, const GValue *value, gpointer user_data)
 {
+    if (!G_VALUE_HOLDS_BOXED (value))
+        return;
+
     QofBook     *book;
-    Account     *map_account = NULL;
-    GncGUID     *guid;
+    GncGUID     *guid = NULL;
     gchar       *kvp_path;
-    gchar       *count;
+    gchar       *guid_string = NULL;
 
     struct imap_info *imapInfo_node;
 
@@ -5590,134 +5450,94 @@ build_bayes_layer_two (const char *key, const GValue *value, gpointer user_data)
     // Get the book
     book = qof_instance_get_book (imapInfo->source_account);
 
-    if (G_VALUE_HOLDS_INT64 (value))
-    {
-        PINFO("build_bayes_layer_two: account '%s', token_count: '%" G_GINT64_FORMAT "'",
-                                  (char*)key, g_value_get_int64(value));
+    guid = (GncGUID*)g_value_get_boxed (value);
+    guid_string = guid_to_string (guid);
 
-        count = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value));
-    }
-    else
-        count = g_strdup ("0");
+    PINFO("build_non_bayes: account '%s', match account guid: '%s'",
+                            (char*)key, guid_string);
 
     kvp_path = g_strconcat (imapInfo->category_head, "/", key, NULL);
 
-    PINFO("build_bayes_layer_two: kvp_path is '%s'", kvp_path);
-
-    guid = g_new (GncGUID, 1);
-
-    if (string_to_guid (key, guid))
-        map_account = xaccAccountLookup (guid, book);
-
-    g_free (guid);
+    PINFO("build_non_bayes: kvp_path is '%s'", kvp_path);
 
     imapInfo_node = static_cast <imap_info*> (g_malloc(sizeof(*imapInfo_node)));
 
     imapInfo_node->source_account = imapInfo->source_account;
-    imapInfo_node->map_account    = map_account;
+    imapInfo_node->map_account    = xaccAccountLookup (guid, book);
     imapInfo_node->full_category  = g_strdup (kvp_path);
-    imapInfo_node->match_string   = g_strdup (imapInfo->match_string);
+    imapInfo_node->match_string   = g_strdup (key);
     imapInfo_node->category_head  = g_strdup (imapInfo->category_head);
-    imapInfo_node->count          = g_strdup (count);
+    imapInfo_node->count          = g_strdup (" ");
 
     imapInfo->list = g_list_append (imapInfo->list, imapInfo_node);
 
     g_free (kvp_path);
-    g_free (count);
+    g_free (guid_string);
 }
 
 static void
-build_bayes (const char *key, const GValue *value, gpointer user_data)
+build_bayes_layer_two (const char *key, KvpValue * val, imap_info imapInfo)
 {
-    gchar *kvp_path;
-    struct imap_info *imapInfo = (struct imap_info*)user_data;
-    struct imap_info  imapInfol2;
-
-    PINFO("build_bayes: match string '%s'", (char*)key);
-
-    if (G_VALUE_HOLDS (value, G_TYPE_STRING) && g_value_get_string (value) == NULL)
+    QofBook     *book;
+    Account     *map_account = NULL;
+    GncGUID     *guid;
+    gchar       *kvp_path;
+    gchar       *count;
+    struct imap_info *imapInfo_node;
+    // Get the book
+    book = qof_instance_get_book (imapInfo.source_account);
+    if (val->get_type() == KvpValue::Type::INT64)
     {
-        kvp_path = g_strdup_printf (IMAP_FRAME_BAYES "/%s", key);
-
-        if (qof_instance_has_slot (QOF_INSTANCE(imapInfo->source_account), kvp_path))
-        {
-            PINFO("build_bayes: kvp_path is '%s', key '%s'", kvp_path, key);
-
-            imapInfol2.source_account = imapInfo->source_account;
-            imapInfol2.match_string   = g_strdup (key);
-            imapInfol2.category_head  = g_strdup (kvp_path);
-            imapInfol2.list           = imapInfo->list;
-
-            qof_instance_foreach_slot (QOF_INSTANCE(imapInfo->source_account), kvp_path,
-                                       build_bayes_layer_two, &imapInfol2);
-
-            imapInfo->list = imapInfol2.list;
-            g_free (imapInfol2.match_string);
-            g_free (imapInfol2.category_head);
-        }
-        g_free (kvp_path);
+        PINFO("build_bayes_layer_two: account '%s', token_count: '%" G_GINT64_FORMAT "'",
+                                  key, val->get<int64_t>());
+        count = g_strdup_printf ("%" G_GINT64_FORMAT, val->get<int64_t>());
     }
+    else
+        count = g_strdup ("0");
+    kvp_path = g_strconcat (imapInfo.category_head, "/", key, NULL);
+    PINFO("build_bayes_layer_two: kvp_path is '%s'", kvp_path);
+    guid = g_new (GncGUID, 1);
+    if (string_to_guid (key, guid))
+        map_account = xaccAccountLookup (guid, book);
+    g_free (guid);
+    imapInfo_node = static_cast <imap_info*> (g_malloc(sizeof(*imapInfo_node)));
+    imapInfo_node->source_account = imapInfo.source_account;
+    imapInfo_node->map_account    = map_account;
+    imapInfo_node->full_category  = g_strdup (kvp_path);
+    imapInfo_node->match_string   = g_strdup (imapInfo.match_string);
+    imapInfo_node->category_head  = g_strdup (imapInfo.category_head);
+    imapInfo_node->count          = g_strdup (count);
+    imapInfo.list = g_list_append (imapInfo.list, imapInfo_node);
+    g_free (kvp_path);
+    g_free (count);
 }
 
-
 static void
-build_non_bayes (const char *key, const GValue *value, gpointer user_data)
+build_bayes (const char *key, KvpValue * value, imap_info & imapInfo)
 {
-    if (G_VALUE_HOLDS_BOXED (value))
-    {
-        QofBook     *book;
-        GncGUID     *guid = NULL;
-        gchar       *kvp_path;
-        gchar       *guid_string = NULL;
-
-        struct imap_info *imapInfo_node;
-
-        struct imap_info *imapInfo = (struct imap_info*)user_data;
-
-        // Get the book
-        book = qof_instance_get_book (imapInfo->source_account);
-
-        guid = (GncGUID*)g_value_get_boxed (value);
-        guid_string = guid_to_string (guid);
-
-        PINFO("build_non_bayes: account '%s', match account guid: '%s'",
-                                (char*)key, guid_string);
-
-        kvp_path = g_strconcat (imapInfo->category_head, "/", key, NULL);
-
-        PINFO("build_non_bayes: kvp_path is '%s'", kvp_path);
-
-        imapInfo_node = static_cast <imap_info*> (g_malloc(sizeof(*imapInfo_node)));
-
-        imapInfo_node->source_account = imapInfo->source_account;
-        imapInfo_node->map_account    = xaccAccountLookup (guid, book);
-        imapInfo_node->full_category  = g_strdup (kvp_path);
-        imapInfo_node->match_string   = g_strdup (key);
-        imapInfo_node->category_head  = g_strdup (imapInfo->category_head);
-        imapInfo_node->count          = g_strdup (" ");
-
-        imapInfo->list = g_list_append (imapInfo->list, imapInfo_node);
+    struct imap_info imapInfol2;
+    PINFO("build_bayes: match string '%s'", (char*)key);
 
-        g_free (kvp_path);
-        g_free (guid_string);
-    }
+    std::string prefix {g_strdup_printf (IMAP_FRAME_BAYES "-%s", key)};
+    PINFO("build_bayes: prefix is '%s', key '%s'", prefix.c_str(), key);
+    imapInfol2.source_account = imapInfo.source_account;
+    imapInfol2.match_string   = g_strdup (key);
+    imapInfol2.category_head  = g_strdup (prefix.c_str());
+    imapInfol2.list           = imapInfo.list;
+    qof_instance_foreach_slot_prefix (QOF_INSTANCE(imapInfo.source_account), prefix,
+                               build_bayes_layer_two, imapInfol2);
+    imapInfo.list = imapInfol2.list;
+    g_free (imapInfol2.match_string);
+    g_free (imapInfol2.category_head);
 }
 
-
 GList *
 gnc_account_imap_get_info_bayes (Account *acc)
 {
-    GList *list = NULL;
-
-    GncImapInfo imapInfo;
-
-    imapInfo.source_account = acc;
-    imapInfo.list = list;
-
-    if (qof_instance_has_slot (QOF_INSTANCE(acc), IMAP_FRAME_BAYES))
-        qof_instance_foreach_slot (QOF_INSTANCE(acc), IMAP_FRAME_BAYES,
-                                   build_bayes, &imapInfo);
-
+    /* A dummy object which is used to hold the specified account, and the list
+     * of data about which we care. */
+    GncImapInfo imapInfo {acc, nullptr};
+    qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES, &build_bayes, imapInfo);
     return imapInfo.list;
 }
 
@@ -5774,18 +5594,14 @@ void
 gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty)
 {
     gchar *kvp_path = g_strdup (full_category);
-
     if ((acc != NULL) && qof_instance_has_slot (QOF_INSTANCE(acc), kvp_path))
     {
         xaccAccountBeginEdit (acc);
-
         if (empty)
             qof_instance_slot_delete_if_empty (QOF_INSTANCE(acc), kvp_path);
         else
             qof_instance_slot_delete (QOF_INSTANCE(acc), kvp_path);
-
         PINFO("Account is '%s', path is '%s'", xaccAccountGetName (acc), kvp_path);
-
         qof_instance_set_dirty (QOF_INSTANCE(acc));
         xaccAccountCommitEdit (acc);
     }
@@ -5796,11 +5612,12 @@ gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean empty)
 /*******************************************************************************/
 
 static gchar *
-look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar *separator)
+look_for_old_separator_descendants (Account *root, gchar const *full_name, const gchar *separator)
 {
     GList *top_accounts, *ptr;
     gint   found_len = 0;
     gchar  found_sep;
+    gchar * new_name = nullptr;
 
     top_accounts = gnc_account_get_descendants (root);
 
@@ -5828,60 +5645,16 @@ look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar
         }
     }
     g_list_free (top_accounts); // Free the List
+    new_name = g_strdup (full_name);
 
     if (found_len > 1)
-        full_name = g_strdelimit (full_name, &found_sep, *separator);
-
-    PINFO("Return full_name is '%s'", full_name);
-
-    return full_name;
-}
-
-
-static void
-convert_imap_entry (GncImapInfo *imapInfo, Account *map_account)
-{
-    GncImportMatchMap *imap;
-    gchar   *guid_string;
-    gchar   *kvp_path;
-    int64_t  token_count = 1;
-
-    GValue value = G_VALUE_INIT;
-
-    // Create an ImportMatchMap object
-    imap = gnc_account_imap_create_imap (imapInfo->source_account);
-
-    xaccAccountBeginEdit (imapInfo->source_account);
-
-    guid_string = guid_to_string (xaccAccountGetGUID (map_account));
-
-    PINFO("Map Account is '%s', GUID is '%s', Count is %s", xaccAccountGetName (map_account),
-               guid_string, imapInfo->count);
-
-    // save converting string, get the count value
-    qof_instance_get_kvp (QOF_INSTANCE (imapInfo->source_account), imapInfo->full_category, &value);
+        g_strdelimit (new_name, &found_sep, *separator);
 
-    if (G_VALUE_HOLDS_INT64 (&value))
-        token_count = g_value_get_int64 (&value);
+    PINFO("Return full_name is '%s'", new_name);
 
-    // Delete the old entry based on full account name
-    kvp_path = g_strdup (imapInfo->full_category);
-    gnc_account_delete_map_entry (imapInfo->source_account, kvp_path, FALSE);
-
-    // create path based on guid
-    kvp_path = g_strdup_printf ("/%s/%s", imapInfo->category_head, guid_string);
-
-    // change the imap entry of source_account
-    change_imap_entry (imap, kvp_path, token_count);
-
-    qof_instance_set_dirty (QOF_INSTANCE (imapInfo->source_account));
-    xaccAccountCommitEdit (imapInfo->source_account);
-
-    g_free (kvp_path);
-    g_free (guid_string);
+    return new_name;
 }
 
-
 static Account *
 look_for_old_mapping (GncImapInfo *imapInfo)
 {
@@ -5905,7 +5678,9 @@ look_for_old_mapping (GncImapInfo *imapInfo)
     // do we have a valid account, if not, look for old separator
     if (map_account == NULL)
     {
-        full_name = look_for_old_separator_descendants (root, full_name, sep);
+        gchar * temp_name = look_for_old_separator_descendants (root, full_name, sep);
+        g_free(full_name);
+        full_name = temp_name;
         map_account = gnc_account_lookup_by_full_name (root, full_name); // lets try again
     }
 
@@ -5916,84 +5691,150 @@ look_for_old_mapping (GncImapInfo *imapInfo)
     return map_account;
 }
 
+static std::vector<std::pair<std::string, KvpValue*>>
+get_new_guid_imap (Account * acc)
+{
+    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
+    auto slot = frame->get_slot(IMAP_FRAME_BAYES);
+    if (!slot)
+        return {};
+    std::string const imap_frame_str {IMAP_FRAME_BAYES};
+    std::vector<std::pair<std::string, KvpValue*>> ret;
+    auto root = gnc_account_get_root (acc);
+    auto imap_frame = slot->get<KvpFrame*>();
+    imap_frame->for_each_slot_temp ([&ret, root, &imap_frame_str] (char const * token, KvpValue* val) {
+        auto token_frame = val->get<KvpFrame*>();
+        token_frame->for_each_slot_temp ([&ret, root, &imap_frame_str, token] (char const * account_name, KvpValue* val) {
+            auto map_account = gnc_account_lookup_by_full_name (root, account_name);
+            if (!map_account)
+            {
+                auto temp_account_name = look_for_old_separator_descendants (root, account_name, gnc_get_account_separator_string());
+                map_account = gnc_account_lookup_by_full_name (root, temp_account_name);
+                g_free (temp_account_name);
+            }
+            auto temp_guid = gnc::GUID{*xaccAccountGetGUID (map_account)};
+            auto guid_str = temp_guid.to_string();
+            ret.push_back({imap_frame_str + "-" + token + "-" + guid_str, val});
+        });
+    });
+    return ret;
+}
+
 static void
-convert_imap_account (Account *acc)
+convert_imap_account_bayes_to_guid (Account *acc)
 {
-    GList *imap_list, *node;
-    gchar *acc_name = NULL;
-
-    acc_name = gnc_account_get_full_name (acc);
-    PINFO("Source Acc '%s'", acc_name);
+    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
+    if (!frame->get_keys().size())
+        return;
+    auto new_imap = get_new_guid_imap(acc);
+    xaccAccountBeginEdit(acc);
+    frame->set(IMAP_FRAME_BAYES, nullptr);
+    std::for_each(new_imap.begin(), new_imap.end(), [&frame] (std::pair<std::string, KvpValue*> const & entry) {
+        frame->set(entry.first.c_str(), entry.second);
+    });
+    qof_instance_set_dirty (QOF_INSTANCE (acc));
+    xaccAccountCommitEdit(acc);
+}
 
-    imap_list = gnc_account_imap_get_info_bayes (acc);
+char const * run_once_key_to_guid {"changed-bayesian-to-guid"};
+char const * run_once_key_to_flat {"changed-bayesian-to-flat"};
 
-    if (g_list_length (imap_list) > 0) // we have mappings
+static void
+imap_convert_bayes_to_guid (QofBook * book)
+{
+    auto root = gnc_book_get_root_account (book);
+    auto accts = gnc_account_get_descendants_sorted (root);
+    for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
     {
-        PINFO("List length is %d", g_list_length (imap_list));
-        xaccAccountBeginEdit(acc);
-        for (node = imap_list;  node; node = g_list_next (node))
-        {
-            Account *map_account = NULL;
-            GncImapInfo *imapInfo = static_cast <GncImapInfo *> (node->data);
-
-            // Lets start doing stuff
-            map_account = look_for_old_mapping (imapInfo);
-
-            if (map_account != NULL) // we have an account, try and update it
-                convert_imap_entry (imapInfo, map_account);
-            // Free the members and structure
-            g_free (imapInfo->category_head);
-            g_free (imapInfo->full_category);
-            g_free (imapInfo->match_string);
-            g_free (imapInfo->count);
-            g_free (imapInfo);
-        }
-        xaccAccountCommitEdit(acc);
+        Account *acc = static_cast <Account*> (ptr->data);
+        convert_imap_account_bayes_to_guid (acc);
     }
-    g_free (acc_name);
-    g_list_free (imap_list); // Free the List
+    g_list_free (accts);
+}
+
+static std::vector<std::pair<std::string, KvpValue*>>
+get_new_flat_imap (Account * acc)
+{
+    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
+    auto slot = frame->get_slot(IMAP_FRAME_BAYES);
+    if (!slot)
+        return {};
+    std::string const imap_frame_str {IMAP_FRAME_BAYES};
+    std::vector<std::pair<std::string, KvpValue*>> ret;
+    auto root = gnc_account_get_root (acc);
+    auto imap_frame = slot->get<KvpFrame*>();
+    imap_frame->for_each_slot_temp ([&ret, &imap_frame_str] (char const * token, KvpValue* val) {
+        auto token_frame = val->get<KvpFrame*>();
+        token_frame->for_each_slot_temp ([&ret, &imap_frame_str, token] (char const * account_guid, KvpValue* val) {
+            ret.push_back({imap_frame_str + "-" + token + "-" + account_guid, val});
+        });
+    });
+    return ret;
 }
 
-void
-gnc_account_imap_convert_bayes (QofBook *book)
+static void
+convert_imap_account_bayes_to_flat (Account * acc)
 {
-    Account      *root;
-    GList        *accts, *ptr;
-    gboolean      run_once = FALSE;
-    GValue        value_s = G_VALUE_INIT;
-
-    // get the run-once value
-    qof_instance_get_kvp (QOF_INSTANCE (book), "changed-bayesian-to-guid", &value_s);
-
-    if (G_VALUE_HOLDS_STRING (&value_s) && (strcmp(g_value_get_string (&value_s), "true") == 0))
-        run_once = TRUE;
+    auto flat_imap = get_new_flat_imap (acc);
+    if (!flat_imap.size())
+        return;
+    auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
+    xaccAccountBeginEdit(acc);
+    frame->set(IMAP_FRAME_BAYES, nullptr);
+    std::for_each(flat_imap.begin(), flat_imap.end(), [&frame] (std::pair<std::string, KvpValue*> const & entry) {
+        frame->set(entry.first.c_str(), entry.second);
+    });
+    qof_instance_set_dirty (QOF_INSTANCE (acc));
+    xaccAccountCommitEdit(acc);
+}
 
-    if (run_once == FALSE)
+static void
+imap_convert_bayes_to_flat (QofBook * book)
+{
+    auto root = gnc_book_get_root_account (book);
+    auto accts = gnc_account_get_descendants_sorted (root);
+    for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
     {
-        GValue value_b = G_VALUE_INIT;
-
-        /* Get list of Accounts */
-        root = gnc_book_get_root_account (book);
-        accts = gnc_account_get_descendants_sorted (root);
-
-        /* Go through list of accounts */
-        for (ptr = accts; ptr; ptr = g_list_next (ptr))
-        {
-            Account *acc = static_cast <Account*> (ptr->data);
-
-            convert_imap_account (acc);
-        }
-        g_list_free (accts);
+        Account *acc = static_cast <Account*> (ptr->data);
+        convert_imap_account_bayes_to_flat (acc);
+    }
+    g_list_free (accts);
+}
 
-        g_value_init (&value_b, G_TYPE_BOOLEAN);
+static bool
+run_once_key_set (char const * key, QofBook * book)
+{
+    GValue value G_VALUE_INIT;
+    qof_instance_get_kvp (QOF_INSTANCE(book), key, &value);
+    return G_VALUE_HOLDS_STRING(&value) && strcmp(g_value_get_string(&value), "true");
+}
 
-        g_value_set_boolean (&value_b, TRUE);
+static void
+set_run_once_key (char const * key, QofBook * book)
+{
+    GValue value G_VALUE_INIT;
+    g_value_init(&value, G_TYPE_BOOLEAN);
+    g_value_set_boolean(&value, TRUE);
+    qof_instance_set_kvp(QOF_INSTANCE (book), key, &value);
+}
 
-        // set the run-once value
-        qof_instance_set_kvp (QOF_INSTANCE (book), "changed-bayesian-to-guid", &value_b);
-    }
+void
+gnc_account_imap_convert_flat (QofBook *book)
+{
+    if (run_once_key_set(run_once_key_to_flat, book))
+        return;
+    imap_convert_bayes_to_flat (book);
+    set_run_once_key(run_once_key_to_flat, book);
 }
 
+void
+gnc_account_imap_convert_bayes (QofBook *book)
+{
+    if (run_once_key_set(run_once_key_to_guid, book))
+        return;
+    imap_convert_bayes_to_guid(book);
+    set_run_once_key(run_once_key_to_guid, book);
+}
 
 /* ================================================================ */
 /* QofObject function implementation and registration */
diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h
index 2f80632..7c93259 100644
--- a/libgnucash/engine/Account.h
+++ b/libgnucash/engine/Account.h
@@ -1454,6 +1454,10 @@ void gnc_account_delete_map_entry (Account *acc, char *full_category, gboolean e
  */
 void gnc_account_imap_convert_bayes (QofBook *book);
 
+/** Change the bayes imap entries from a nested representation to a flat representation.
+ */
+void gnc_account_imap_convert_flat (QofBook *);
+
 /** @} */
 
 
diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index e939ac8..5706802 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -356,6 +356,21 @@ GUID::from_string (std::string const & str)
     }
 }
 
+bool
+GUID::is_valid_guid (std::string const & str)
+{
+    try
+    {
+        static boost::uuids::string_generator strgen;
+        auto a = strgen (str);
+        return true;
+    }
+    catch (...)
+    {
+        return false;
+    }
+}
+
 guid_syntax_exception::guid_syntax_exception () noexcept
     : invalid_argument {"Invalid syntax for guid."}
 {
diff --git a/libgnucash/engine/guid.hpp b/libgnucash/engine/guid.hpp
index db6e4dc..837d509 100644
--- a/libgnucash/engine/guid.hpp
+++ b/libgnucash/engine/guid.hpp
@@ -50,6 +50,7 @@ struct GUID
     static GUID create_random () noexcept;
     static GUID const & null_guid () noexcept;
     static GUID from_string (std::string const &);
+    static bool is_valid_guid (std::string const &);
     std::string to_string () const noexcept;
     auto begin () const noexcept -> decltype (implementation.begin ());
     auto end () const noexcept -> decltype (implementation.end ());
diff --git a/libgnucash/engine/kvp-frame.cpp b/libgnucash/engine/kvp-frame.cpp
index 13a0e4e..9461d01 100644
--- a/libgnucash/engine/kvp-frame.cpp
+++ b/libgnucash/engine/kvp-frame.cpp
@@ -472,4 +472,30 @@ gnc_value_list_get_type (void)
     return type;
 }
 
-/* ========================== END OF FILE ======================= */
+void
+KvpFrame::flatten_kvp_impl(std::vector<std::string> path, std::vector<std::pair<std::string, KvpValue*>> & entries) const
+{
+    for (auto const & entry : m_valuemap)
+    {
+        if (entry.second->get_type() == KvpValue::Type::FRAME)
+        {
+            std::vector<std::string> send_path {path};
+            send_path.push_back("/");
+            send_path.push_back(entry.first);
+            entry.second->get<KvpFrame*>()->flatten_kvp_impl(send_path, entries);
+        }
+        else
+        {
+            std::string flat_path {std::accumulate(path.begin(), path.end(), std::string{})};
+            entries.emplace_back(flat_path + "/" + entry.first, entry.second);
+        }
+    }
+}
+
+std::vector<std::pair<std::string, KvpValue*>>
+KvpFrame::flatten_kvp(void) const
+{
+    std::vector<std::pair<std::string, KvpValue*>> ret;
+    flatten_kvp_impl({}, ret);
+    return ret;
+}
diff --git a/libgnucash/engine/kvp-frame.hpp b/libgnucash/engine/kvp-frame.hpp
index 1af4217..0cc206e 100644
--- a/libgnucash/engine/kvp-frame.hpp
+++ b/libgnucash/engine/kvp-frame.hpp
@@ -93,6 +93,8 @@
 #include <string>
 #include <vector>
 #include <cstring>
+#include <algorithm>
+#include <iostream>
 using Path = std::vector<std::string>;
 
 /** Implements KvpFrame.
@@ -192,7 +194,7 @@ struct KvpFrameImpl
      * prefixed to every item in the frame.
      * @return A std::string representing all the children of the frame.
      */
-    std::string to_string(std::string const & prefix) const noexcept;
+    std::string to_string(std::string const &) const noexcept;
     /**
      * Report the keys in the immediate frame. Be sensible about using this, it
      * isn't a very efficient way to iterate.
@@ -211,14 +213,34 @@ struct KvpFrameImpl
      */
     KvpValue* get_slot(Path keys) const noexcept;
 
-    void for_each_slot(void (*)(char const *key, KvpValue*, void*data), void*data) const noexcept;
+    void for_each_slot(void (*proc)(const char *key, KvpValue *, void *data), void* data) const noexcept;
 
     /** The function should be of the form:
      * <anything> func (char const *, KvpValue *, data_type &);
-     * Do not pass nullptr for the function.
+     * Do not pass nullptr as the function.
      */
     template <typename func_type, typename data_type>
-    void for_each_slot_temp (func_type const &, data_type &) const noexcept;
+    void for_each_slot_temp(func_type const &, data_type &) const noexcept;
+
+    template <typename func_type>
+    void for_each_slot_temp(func_type const &) const noexcept;
+
+    /**
+     * Like for_each_slot, but doesn't traverse nested values. This will only loop
+     * over root-level values whose keys match the specified prefix.
+     */
+    template <typename func_type, typename data_type>
+    void for_each_slot_prefix(std::string const & prefix, func_type const &, data_type &) const noexcept;
+
+    template <typename func_type>
+    void for_each_slot_prefix(std::string const & prefix, func_type const &) const noexcept;
+
+    /**
+     * Returns all keys and values of this frame recursively, flattening
+     * the frame-containing values.
+     */
+    std::vector<std::pair<std::string, KvpValue*>>
+    flatten_kvp(void) const;
 
     /** Test for emptiness
      * @return true if the frame contains nothing.
@@ -228,8 +250,55 @@ struct KvpFrameImpl
 
     private:
     map_type m_valuemap;
+
+    void flatten_kvp_impl(std::vector<std::string>, std::vector<std::pair<std::string, KvpValue*>> &) const;
 };
 
+template<typename func_type>
+void KvpFrame::for_each_slot_prefix(std::string const & prefix,
+        func_type const & func) const noexcept
+{
+    std::for_each (m_valuemap.begin(), m_valuemap.end(),
+        [&prefix,&func](const KvpFrameImpl::map_type::value_type & a)
+        {
+            std::string temp_key {a.first};
+            if (temp_key.size() < prefix.size())
+                return;
+            /* Testing for prefix matching */
+            if (std::mismatch(prefix.begin(), prefix.end(), temp_key.begin()).first == prefix.end())
+                func (a.first, a.second);
+        }
+    );
+}
+
+template<typename func_type, typename data_type>
+void KvpFrame::for_each_slot_prefix(std::string const & prefix,
+        func_type const & func, data_type & data) const noexcept
+{
+    std::for_each (m_valuemap.begin(), m_valuemap.end(),
+        [&prefix,&func,&data](const KvpFrameImpl::map_type::value_type & a)
+        {
+            std::string temp_key {a.first};
+            if (temp_key.size() < prefix.size())
+                return;
+            /* Testing for prefix matching */
+            if (std::mismatch(prefix.begin(), prefix.end(), temp_key.begin()).first == prefix.end())
+                func (a.first, a.second, data);
+        }
+    );
+}
+
+template <typename func_type>
+void KvpFrame::for_each_slot_temp(func_type const & func) const noexcept
+{
+    std::for_each (m_valuemap.begin(), m_valuemap.end(),
+        [&func](const KvpFrameImpl::map_type::value_type & a)
+        {
+            func (a.first, a.second);
+        }
+    );
+}
+
 template <typename func_type, typename data_type>
 void KvpFrame::for_each_slot_temp(func_type const & func, data_type & data) const noexcept
 {
@@ -241,7 +310,6 @@ void KvpFrame::for_each_slot_temp(func_type const & func, data_type & data) cons
     );
 }
 
-
 int compare (const KvpFrameImpl &, const KvpFrameImpl &) noexcept;
 int compare (const KvpFrameImpl *, const KvpFrameImpl *) noexcept;
 /** @} Doxygen Group */
diff --git a/libgnucash/engine/qofinstance-p.h b/libgnucash/engine/qofinstance-p.h
index ca6a4c5..5ffcd9f 100644
--- a/libgnucash/engine/qofinstance-p.h
+++ b/libgnucash/engine/qofinstance-p.h
@@ -160,6 +160,10 @@ void qof_instance_foreach_slot (const QofInstance *inst, const char *path,
 #ifdef __cplusplus
 } /* extern "C" */
 
+/** Returns all keys that match the given prefix and their corresponding values.*/
+std::map<std::string, KvpValue*>
+qof_instance_get_slots_prefix (QofInstance const *, std::string const & prefix);
+
 /* Don't pass nullptr as the function */
 template<typename func_type, typename data_type>
 void qof_instance_foreach_slot_temp (QofInstance const * inst, std::string const & path,
@@ -171,6 +175,18 @@ void qof_instance_foreach_slot_temp (QofInstance const * inst, std::string const
     auto frame = slot->get<KvpFrame*>();
     frame->for_each_slot(func, data);
 }
+
+/**
+ * Similar to qof_instance_foreach_slot, but we don't traverse the depth of the key value frame,
+ * we only check the root level for keys that match the specified prefix.
+ */
+template<typename func_type, typename data_type>
+void qof_instance_foreach_slot_prefix(QofInstance const * inst, std::string const & path_prefix,
+        func_type const & func, data_type & data)
+{
+    inst->kvp_data->for_each_slot_prefix(path_prefix, func, data);
+}
+
 #endif
 
 #endif /* QOF_INSTANCE_P_H */
diff --git a/libgnucash/engine/test/gtest-import-map.cpp b/libgnucash/engine/test/gtest-import-map.cpp
index f5f3cf2..5938f40 100644
--- a/libgnucash/engine/test/gtest-import-map.cpp
+++ b/libgnucash/engine/test/gtest-import-map.cpp
@@ -30,6 +30,7 @@ extern "C"
 #include <qofinstance-p.h>
 #include <kvp-frame.hpp>
 #include <gtest/gtest.h>
+#include <string>
 
 class ImapTest : public testing::Test
 {
@@ -67,7 +68,11 @@ protected:
         gnc_account_append_child(t_expense_account, t_expense_account2);
     }
     void TearDown() {
-        qof_book_destroy (gnc_account_get_book (t_bank_account));
+        auto root = gnc_account_get_root (t_bank_account);
+        auto book = gnc_account_get_book (root);
+        xaccAccountBeginEdit (root);
+        xaccAccountDestroy (root);
+        qof_book_destroy (book);
     }
     Account *t_bank_account {};
     Account *t_sav_account {};
@@ -110,18 +115,16 @@ protected:
 TEST_F(ImapPlainTest, FindAccount)
 {
     auto root = qof_instance_get_slots(QOF_INSTANCE(t_bank_account));
-    auto acc1_val = new KvpValue(const_cast<GncGUID*>(xaccAccountGetGUID(t_expense_account1)));
-    auto acc2_val = new KvpValue(const_cast<GncGUID*>(xaccAccountGetGUID(t_expense_account2)));
+    auto acc1_val = new KvpValue(guid_copy(xaccAccountGetGUID(t_expense_account1)));
+    auto acc2_val = new KvpValue(guid_copy(xaccAccountGetGUID(t_expense_account2)));
     root->set_path({IMAP_FRAME, "foo", "bar"}, acc1_val);
     root->set_path({IMAP_FRAME, "baz", "waldo"}, acc2_val);
-    root->set_path({IMAP_FRAME, "pepper"}, acc1_val);
-    root->set_path({IMAP_FRAME, "salt"}, acc2_val);
+    root->set_path({IMAP_FRAME, "pepper"}, new KvpValue{*acc1_val});
+    root->set_path({IMAP_FRAME, "salt"}, new KvpValue{*acc2_val});
 
     EXPECT_EQ(t_expense_account1, gnc_account_imap_find_account(t_imap, "foo", "bar"));
-    EXPECT_EQ(t_expense_account2,
-              gnc_account_imap_find_account(t_imap, "baz", "waldo"));
-    EXPECT_EQ(t_expense_account1,
-              gnc_account_imap_find_account(t_imap, NULL, "pepper"));
+    EXPECT_EQ(t_expense_account2, gnc_account_imap_find_account(t_imap, "baz", "waldo"));
+    EXPECT_EQ(t_expense_account1, gnc_account_imap_find_account(t_imap, NULL, "pepper"));
     EXPECT_EQ(t_expense_account2, gnc_account_imap_find_account(t_imap, NULL, "salt"));
     EXPECT_EQ(nullptr, gnc_account_imap_find_account(t_imap, "salt", NULL));
 }
@@ -251,12 +254,12 @@ TEST_F(ImapBayesTest, FindAccountBayes)
     auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2));
     auto value = new KvpValue(INT64_C(42));
 
-    root->set_path({IMAP_FRAME_BAYES, foo, acct1_guid}, value);
-    root->set_path({IMAP_FRAME_BAYES, bar, acct1_guid}, value);
-    root->set_path({IMAP_FRAME_BAYES, baz, acct2_guid}, value);
-    root->set_path({IMAP_FRAME_BAYES, waldo, acct2_guid}, value);
-    root->set_path({IMAP_FRAME_BAYES, pepper, acct1_guid}, value);
-    root->set_path({IMAP_FRAME_BAYES, salt, acct2_guid}, value);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str(), value);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid).c_str(), new KvpValue{*value});
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid).c_str(), new KvpValue{*value});
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid).c_str(), new KvpValue{*value});
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid).c_str(), new KvpValue{*value});
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid).c_str(), new KvpValue{*value});
 
     auto account = gnc_account_imap_find_account_bayes(t_imap, t_list1);
     EXPECT_EQ(t_expense_account1, account);
@@ -289,29 +292,29 @@ TEST_F(ImapBayesTest, AddAccountBayes)
     auto root = qof_instance_get_slots(QOF_INSTANCE(t_bank_account));
     auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1));
     auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2));
-    auto value = root->get_slot({IMAP_FRAME_BAYES, "foo", "bar"});
+    auto value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-foo-bar").c_str());
     auto check_account = [this](KvpValue* v) {
         return (v->get<const char*>(), this->t_imap->book); };
-    value = root->get_slot({IMAP_FRAME_BAYES, foo, acct1_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, bar, acct1_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, baz, acct2_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, waldo, acct2_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, pepper, acct1_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, salt, acct2_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, baz, acct1_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct1_guid).c_str());
     EXPECT_EQ(nullptr, value);
 
     qof_instance_increase_editlevel(QOF_INSTANCE(t_bank_account));
     gnc_account_imap_add_account_bayes(t_imap, t_list2, t_expense_account2);
     qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
     qof_instance_reset_editlevel(QOF_INSTANCE(t_bank_account));
-    value = root->get_slot({IMAP_FRAME_BAYES, baz, acct2_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid).c_str());
     EXPECT_EQ(2, value->get<int64_t>());
 }
 
@@ -336,22 +339,22 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     auto val3 = new KvpValue(static_cast<int64_t>(2));
 
     // Test for existing entries, all will be 1
-    auto value = root->get_slot({IMAP_FRAME_BAYES, foo, acct1_guid});
+    auto value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, bar, acct1_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + bar + "-" + acct1_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, baz, acct2_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + baz + "-" + acct2_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
-    value = root->get_slot({IMAP_FRAME_BAYES, waldo, acct2_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + waldo + "-" + acct2_guid).c_str());
     EXPECT_EQ(1, value->get<int64_t>());
 
     // Set up some old entries
-    root->set_path({IMAP_FRAME_BAYES, pepper, "Asset-Bank"}, val1);
-    root->set_path({IMAP_FRAME_BAYES, salt, "Asset-Bank#Bank"}, val1);
-    root->set_path({IMAP_FRAME_BAYES, salt, "Asset>Bank#Bank"}, val2);
-    root->set_path({IMAP_FRAME_BAYES, pork, "Expense#Food"}, val2);
-    root->set_path({IMAP_FRAME_BAYES, sausage, "Expense#Drink"}, val3);
-    root->set_path({IMAP_FRAME_BAYES, foo, "Expense#Food"}, val2);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pepper + "/" + "Asset-Bank").c_str(), val1);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + "Asset-Bank#Bank").c_str(), new KvpValue{*val1});
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + "Asset>Bank#Bank").c_str(), val2);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pork + "/" + "Expense#Food").c_str(), new KvpValue{*val2});
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + sausage + "/" + "Expense#Drink").c_str(), val3);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + "Expense#Food").c_str(), new KvpValue{*val2});
 
     EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
     EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
@@ -361,24 +364,24 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     gnc_account_imap_convert_bayes (t_imap->book);
 
     // convert from 'Asset-Bank' to 'Asset-Bank' guid
-    value = root->get_slot({IMAP_FRAME_BAYES, pepper, acct3_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct3_guid).c_str());
     EXPECT_EQ(10, value->get<int64_t>());
 
     // convert from 'Asset-Bank#Bank' to 'Sav Bank' guid
-    value = root->get_slot({IMAP_FRAME_BAYES, salt, acct4_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct4_guid).c_str());
     EXPECT_EQ(10, value->get<int64_t>());
 
     // convert from 'Expense#Food' to 'Food' guid
-    value = root->get_slot({IMAP_FRAME_BAYES, pork, acct1_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pork + "-" + acct1_guid).c_str());
     EXPECT_EQ(5, value->get<int64_t>());
 
     // convert from 'Expense#Drink' to 'Drink' guid
-    value = root->get_slot({IMAP_FRAME_BAYES, sausage, acct2_guid});
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + sausage + "-" + acct2_guid).c_str());
     EXPECT_EQ(2, value->get<int64_t>());
 
     // convert from 'Expense#Food' to 'Food' guid but add to original value
-    value = root->get_slot({IMAP_FRAME_BAYES, foo, acct1_guid});
-    EXPECT_EQ(6, value->get<int64_t>());
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct1_guid).c_str());
+    EXPECT_EQ(5, value->get<int64_t>());
 
     // Check for run once flag
     auto vals = book->get_slot("changed-bayesian-to-guid");
@@ -388,4 +391,53 @@ TEST_F(ImapBayesTest, ConvertAccountBayes)
     EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
 }
 
+TEST_F (ImapBayesTest, convert_map_flat)
+{
+    // prevent the embedded beginedit/committedit from doing anything
+    qof_instance_increase_editlevel(QOF_INSTANCE(t_bank_account));
+    qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
+    //gnc_account_imap_add_account_bayes(t_imap, t_list1, t_expense_account1); //Food
+    //gnc_account_imap_add_account_bayes(t_imap, t_list2, t_expense_account2); //Drink
+    auto root = qof_instance_get_slots(QOF_INSTANCE(t_bank_account));
+    auto acct1_guid = guid_to_string (xaccAccountGetGUID(t_expense_account1)); //Food
+    auto acct2_guid = guid_to_string (xaccAccountGetGUID(t_expense_account2)); //Drink
+    auto acct3_guid = guid_to_string (xaccAccountGetGUID(t_asset_account2)); //Asset-Bank
+    auto acct4_guid = guid_to_string (xaccAccountGetGUID(t_sav_account)); //Sav Bank
+    auto val1 = new KvpValue(static_cast<int64_t>(10));
+    auto val2 = new KvpValue(static_cast<int64_t>(5));
+    auto val3 = new KvpValue(static_cast<int64_t>(2));
+    // Set up some old entries
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pepper + "/" + acct1_guid).c_str(), val1);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + salt + "/" + acct2_guid).c_str(), new KvpValue{*val1});
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + foo + "/" + acct3_guid).c_str(), val2);
+    root->set_path((std::string{IMAP_FRAME_BAYES} + "/" + pork + "/" + acct4_guid).c_str(), val3);
+    EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
+    qof_instance_mark_clean(QOF_INSTANCE(t_bank_account));
+    gnc_account_imap_convert_flat (t_imap->book);
+    auto value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pepper + "-" + acct1_guid).c_str());
+    EXPECT_EQ(10, value->get<int64_t>());
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + salt + "-" + acct2_guid).c_str());
+    EXPECT_EQ(10, value->get<int64_t>());
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + foo + "-" + acct3_guid).c_str());
+    EXPECT_EQ(5, value->get<int64_t>());
+    value = root->get_slot((std::string{IMAP_FRAME_BAYES} + "-" + pork + "-" + acct4_guid).c_str());
+    EXPECT_EQ(2, value->get<int64_t>());
+    auto book = qof_instance_get_slots(QOF_INSTANCE(t_imap->book));
+    auto vals = book->get_slot("changed-bayesian-to-flat");
+    EXPECT_STREQ("true", vals->get<const char*>());
+    EXPECT_EQ(1, qof_instance_get_editlevel(QOF_INSTANCE(t_bank_account)));
+    EXPECT_TRUE(qof_instance_get_dirty_flag(QOF_INSTANCE(t_bank_account)));
+}
 
+/* Tests the import map's handling of KVP delimiters */
+TEST_F (ImapBayesTest, import_map_with_delimiters)
+{
+    GList * tokens {nullptr};
+    tokens = g_list_prepend(tokens, const_cast<char*>("one/two/three"));
+    gnc_account_imap_add_account_bayes(t_imap, tokens, t_expense_account1);
+    gnc_account_imap_add_account_bayes(t_imap, tokens, t_expense_account1);
+    gnc_account_imap_add_account_bayes(t_imap, tokens, t_expense_account1);
+
+    auto account = gnc_account_imap_find_account_bayes (t_imap, tokens);
+    EXPECT_EQ (account, t_expense_account1);
+}
diff --git a/libgnucash/engine/test/test-kvp-frame.cpp b/libgnucash/engine/test/test-kvp-frame.cpp
index 3df1c75..d67639f 100644
--- a/libgnucash/engine/test/test-kvp-frame.cpp
+++ b/libgnucash/engine/test/test-kvp-frame.cpp
@@ -202,3 +202,41 @@ TEST_F (KvpFrameTest, Empty)
     EXPECT_TRUE(f1.empty());
     EXPECT_FALSE(f2.empty());
 }
+
+TEST (KvpFrameTestForEachPrefix, for_each_prefix_1)
+{
+    KvpFrame fr;
+    fr.set("one", new KvpValue{new KvpFrame});
+    fr.set("one/two", new KvpValue{new KvpFrame});
+    fr.set("top/two/three", new KvpValue {15.0});
+    fr.set("onetwo", new KvpValue{new KvpFrame});
+    fr.set("onetwo/three", new KvpValue {15.0});
+    fr.set("onetwothree", new KvpValue {(int64_t)52});
+    unsigned count {};
+    auto counter = [] (char const *, KvpValue*, unsigned & count) { ++count; };
+    fr.for_each_slot_prefix("one", counter, count);
+    EXPECT_EQ(count, 3);
+    count = 0;
+    fr.for_each_slot_prefix("onetwo", counter, count);
+    EXPECT_EQ(count, 2);
+    count = 0;
+    fr.for_each_slot_prefix("onetwothree", counter, count);
+    EXPECT_EQ(count, 1);
+    count = 0;
+    fr.for_each_slot_prefix("two", counter, count);
+    EXPECT_EQ(count, 0);
+}
+
+TEST (KvpFrameTestForEachPrefix, for_each_prefix_2)
+{
+    KvpFrame fr;
+    fr.set("onetwo/three", new KvpValue {15.0});
+    fr.set("onethree", new KvpValue {(int64_t)52});
+    unsigned count;
+    fr.for_each_slot_prefix("onetwo", [](char const *, KvpValue * value, unsigned) {
+            EXPECT_EQ(value->get_type(), KvpValue::Type::FRAME);
+        }, count);
+    fr.for_each_slot_prefix("onetwo", [](char const *, KvpValue * value, unsigned) {
+            EXPECT_EQ(value->get_type(), KvpValue::Type::INT64);
+        }, count);
+}
diff --git a/libgnucash/engine/test/utest-Split.cpp b/libgnucash/engine/test/utest-Split.cpp
index 09234a6..d60fbe1 100644
--- a/libgnucash/engine/test/utest-Split.cpp
+++ b/libgnucash/engine/test/utest-Split.cpp
@@ -740,7 +740,7 @@ test_xaccSplitDetermineGainStatus (Fixture *fixture, gconstpointer pData)
     g_assert (fixture->split->gains_split == NULL);
     g_assert_cmpint (fixture->split->gains, ==, GAINS_STATUS_A_VDIRTY | GAINS_STATUS_DATE_DIRTY);
 
-    fixture->split->inst.kvp_data->set("gains-source", new KvpValue(const_cast<GncGUID*>(guid_copy(g_guid))));
+    fixture->split->inst.kvp_data->set("gains-source", new KvpValue(guid_copy(g_guid)));
     g_assert (fixture->split->gains_split == NULL);
     fixture->split->gains = GAINS_STATUS_UNKNOWN;
     xaccSplitDetermineGainStatus (fixture->split);

commit 08aa0104ef3f439b0bc9a9708d97440b4bf8b221
Author: lmat <dartme18 at gmail.com>
Date:   Thu Oct 19 15:23:16 2017 -0400

    Change kvp string representation
    
    The nested representation was very noisy. Now, the string representation
    shows one line per value with the full prefix which is also more
    expressive than the old version.

diff --git a/libgnucash/engine/kvp-frame.cpp b/libgnucash/engine/kvp-frame.cpp
index 0610135..13a0e4e 100644
--- a/libgnucash/engine/kvp-frame.cpp
+++ b/libgnucash/engine/kvp-frame.cpp
@@ -181,23 +181,30 @@ KvpFrameImpl::set_path(Path path, KvpValue* value) noexcept
 std::string
 KvpFrameImpl::to_string() const noexcept
 {
-    std::ostringstream ret;
-    ret << "{\n";
+    return to_string("");
+}
 
+std::string
+KvpFrameImpl::to_string(std::string const & prefix) const noexcept
+{
+    if (!m_valuemap.size())
+        return prefix;
+    std::ostringstream ret;
     std::for_each(m_valuemap.begin(), m_valuemap.end(),
-        [this,&ret](const map_type::value_type &a)
+        [this,&ret,&prefix](const map_type::value_type &a)
         {
-            ret << "    ";
+            std::string new_prefix {prefix};
             if (a.first)
-                ret << a.first;
-            ret << " => ";
+            {
+                new_prefix += a.first;
+                new_prefix += "/";
+            }
             if (a.second)
-                ret << a.second->to_string();
-            ret << ",\n";
+                ret << a.second->to_string(new_prefix) << "\n";
+            else
+                ret << new_prefix << "(null)\n";
         }
     );
-
-    ret << "}\n";
     return ret.str();
 }
 
diff --git a/libgnucash/engine/kvp-frame.hpp b/libgnucash/engine/kvp-frame.hpp
index e5308ec..1af4217 100644
--- a/libgnucash/engine/kvp-frame.hpp
+++ b/libgnucash/engine/kvp-frame.hpp
@@ -188,6 +188,12 @@ struct KvpFrameImpl
      */
     std::string to_string() const noexcept;
     /**
+     * Make a string representation of the frame with the specified string
+     * prefixed to every item in the frame.
+     * @return A std::string representing all the children of the frame.
+     */
+    std::string to_string(std::string const & prefix) const noexcept;
+    /**
      * Report the keys in the immediate frame. Be sensible about using this, it
      * isn't a very efficient way to iterate.
      * @return std::vector of keys as std::strings.
diff --git a/libgnucash/engine/kvp-value.cpp b/libgnucash/engine/kvp-value.cpp
index 55e0e6e..a0329d2 100644
--- a/libgnucash/engine/kvp-value.cpp
+++ b/libgnucash/engine/kvp-value.cpp
@@ -125,34 +125,32 @@ struct to_string_visitor : boost::static_visitor<void>
 
     void operator()(int64_t val)
     {
-        output << "KVP_VALUE_GINT64(" << val << ")";
+        output << val << " (64-bit int)";
     }
 
-    void operator()(KvpFrame * val)
+    void operator()(KvpFrame* val)
     {
-        output << "KVP_VALUE_FRAME(" << val->to_string() << ")";
+        output << val->to_string();
     }
 
     void operator()(GDate val)
     {
-        output << "KVP_VALUE_GDATE(";
         output << std::setw(4) << g_date_get_year(&val) << '-';
         output << std::setw(2) << g_date_get_month(&val) << '-';
-        output << std::setw(2) << g_date_get_day(&val) << ')';
+        output << std::setw(2) << g_date_get_day(&val);
+        output << " (gdate)";
     }
 
     void operator()(GList * val)
     {
         output << "KVP_VALUE_GLIST(";
         output << "[ ";
-
         /*Since val is passed by value, we can modify it*/
         for (;val; val = val->next)
         {
             auto realvalue = static_cast<const KvpValue *>(val->data);
             output << ' ' << realvalue->to_string() << ',';
         }
-
         output << " ]";
         output << ")";
     }
@@ -161,53 +159,66 @@ struct to_string_visitor : boost::static_visitor<void>
     {
         char tmp1[40] {};
         gnc_timespec_to_iso8601_buff (val, tmp1);
-        output << "KVP_VALUE_TIMESPEC(" << tmp1 << ")";
+        output << tmp1 << " (timespec)";
     }
 
     void operator()(gnc_numeric val)
     {
         auto tmp1 = gnc_numeric_to_string(val);
-        output << "KVP_VALUE_NUMERIC(";
         if (tmp1)
         {
             output << tmp1;
             g_free(tmp1);
         }
-        output << ")";
+        else
+        {
+            output << "(null)";
+        }
+        output << " (timespec)";
     }
 
     void operator()(GncGUID * val)
     {
         char guidstr[GUID_ENCODING_LENGTH+1];
-        output << "KVP_VALUE_GUID(";
         if (val)
         {
             guid_to_string_buff(val,guidstr);
             output << guidstr;
         }
-        output << ")";
+        else
+        {
+            output << "(null)";
+        }
+        output << " (guid)";
     }
 
     void operator()(const char * val)
     {
-        output << "KVP_VALUE_STRING(" << val << ")";
+        output << val << " (char *)";
     }
 
     void operator()(double val)
     {
-        output << "KVP_VALUE_DOUBLE(" << val << ")";
+        output << val << " (double)";
     }
 };
 
 std::string
-KvpValueImpl::to_string() const noexcept
+KvpValueImpl::to_string(std::string const & prefix) const noexcept
 {
+    if (this->datastore.type() == typeid(KvpFrame*))
+        return this->get<KvpFrame*>()->to_string(prefix);
     std::ostringstream ret;
     to_string_visitor visitor {ret};
     boost::apply_visitor(visitor, datastore);
-
     /*We still use g_strdup since the return value will be freed by g_free*/
-    return ret.str();
+    return prefix + ret.str();
+}
+
+std::string
+KvpValueImpl::to_string() const noexcept
+{
+    return to_string("");
 }
 
 static int
diff --git a/libgnucash/engine/kvp-value.hpp b/libgnucash/engine/kvp-value.hpp
index 003ee7d..78a8eb7 100644
--- a/libgnucash/engine/kvp-value.hpp
+++ b/libgnucash/engine/kvp-value.hpp
@@ -138,6 +138,7 @@ struct KvpValueImpl
     KvpValueImpl::Type get_type() const noexcept;
 
     std::string to_string() const noexcept;
+    std::string to_string(std::string const & prefix) const noexcept;
 
     template <typename T>
     T get() const noexcept;
diff --git a/libgnucash/engine/test/utest-Transaction.cpp b/libgnucash/engine/test/utest-Transaction.cpp
index dde0a48..a95a5ac 100644
--- a/libgnucash/engine/test/utest-Transaction.cpp
+++ b/libgnucash/engine/test/utest-Transaction.cpp
@@ -885,7 +885,7 @@ test_xaccTransEqual (Fixture *fixture, gconstpointer pData)
     xaccTransCommitEdit (clone);
     g_free (cleanup->msg);
     g_free (check->msg);
-    check->msg = g_strdup ("[xaccTransEqual] kvp frames differ:\n{\n    notes => KVP_VALUE_STRING(Salt pork sausage),\n    qux => KVP_VALUE_FRAME({\n    quux => KVP_VALUE_FRAME({\n    corge => KVP_VALUE_DOUBLE(654.321),\n}\n),\n}\n),\n}\n\n\nvs\n\n{\n    notes => KVP_VALUE_STRING(Salt pork sausage),\n    qux => KVP_VALUE_FRAME({\n    quux => KVP_VALUE_FRAME({\n    corge => KVP_VALUE_DOUBLE(123.456),\n}\n),\n}\n),\n}\n");
+    check->msg = g_strdup ("[xaccTransEqual] kvp frames differ:\nnotes/Salt pork sausage (char *)\nqux/quux/corge/654.321 (double)\n\n\n\n\nvs\n\nnotes/Salt pork sausage (char *)\nqux/quux/corge/123.456 (double)\n\n\n");
 
     g_assert (!xaccTransEqual (clone, txn0, TRUE, FALSE, TRUE, TRUE));
 

commit 34e0d6cfa06648e38d0771363803f2468efdc01c
Author: lmat <dartme18 at gmail.com>
Date:   Thu Oct 5 12:48:37 2017 -0400

    kvp frame to template and correcting failure macro
    
    The template avoids the need to cast to and from void*, and adds flexibility to
    the targeted function's signature.
    
    test-stuff.h defines a macro, "failure" which is used as an identifier
    in the standard IO library, so I moved any inclusion of test-stuff.h to
    the last include position so that "failure" wouldn't be defined before
    the IO library was included.

diff --git a/common/test-core/test-stuff.h b/common/test-core/test-stuff.h
index 8fcd506..6da2adb 100644
--- a/common/test-core/test-stuff.h
+++ b/common/test-core/test-stuff.h
@@ -57,7 +57,9 @@ Otherwise, only failures are printed out.
 #include <glib.h>
 #include <stdlib.h>
 
-
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 /* Privately used to indicate a test result. You may use these if you
  * wish, but it's easier to use the do_test macro above.
@@ -150,5 +152,8 @@ gint64 get_random_gint64(void);
 double get_random_double(void);
 const char* get_random_string_in_array(const char* str_list[]);
 
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
 
 #endif /* TEST_STUFF_H */
diff --git a/libgnucash/backend/sql/gnc-slots-sql.cpp b/libgnucash/backend/sql/gnc-slots-sql.cpp
index 8b35b27..30563a5 100644
--- a/libgnucash/backend/sql/gnc-slots-sql.cpp
+++ b/libgnucash/backend/sql/gnc-slots-sql.cpp
@@ -581,81 +581,78 @@ slot_info_copy (slot_info_t* pInfo, GncGUID* guid)
 }
 
 static void
-save_slot (const char* key, KvpValue* value, gpointer data)
+save_slot (const char* key, KvpValue* value, slot_info_t & slot_info)
 {
-    slot_info_t* pSlot_info = (slot_info_t*)data;
-
     g_return_if_fail (value != NULL);
-    g_return_if_fail (data != NULL);
 
     // Ignore if we've already run into a failure
-    if (!pSlot_info->is_ok)
+    if (!slot_info.is_ok)
     {
         return;
     }
-    auto curlen = pSlot_info->path.length();
-    pSlot_info->pKvpValue = value;
+    auto curlen = slot_info.path.length();
+    slot_info.pKvpValue = value;
     if (curlen != 0)
-        pSlot_info->path += "/";
+        slot_info.path += "/";
 
-    pSlot_info->path += key;
-    pSlot_info->value_type = value->get_type ();
+    slot_info.path += key;
+    slot_info.value_type = value->get_type ();
 
-    switch (pSlot_info->value_type)
+    switch (slot_info.value_type)
     {
     case KvpValue::Type::FRAME:
     {
         auto pKvpFrame = value->get<KvpFrame*> ();
         auto guid = guid_new ();
-        slot_info_t* pNewInfo = slot_info_copy (pSlot_info, guid);
-        KvpValue* oldValue = pSlot_info->pKvpValue;
-        pSlot_info->pKvpValue = new KvpValue {guid};
-        pSlot_info->is_ok = pSlot_info->be->do_db_operation(OP_DB_INSERT,
+        slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
+        KvpValue* oldValue = slot_info.pKvpValue;
+        slot_info.pKvpValue = new KvpValue {guid};
+        slot_info.is_ok = slot_info.be->do_db_operation(OP_DB_INSERT,
                                                             TABLE_NAME,
                                                             TABLE_NAME,
-                                                            pSlot_info,
+                                                            &slot_info,
                                                             col_table);
-        g_return_if_fail (pSlot_info->is_ok);
-        pKvpFrame->for_each_slot (save_slot, pNewInfo);
-        delete pSlot_info->pKvpValue;
-        pSlot_info->pKvpValue = oldValue;
+        g_return_if_fail (slot_info.is_ok);
+        pKvpFrame->for_each_slot_temp (save_slot, *pNewInfo);
+        delete slot_info.pKvpValue;
+        slot_info.pKvpValue = oldValue;
         delete pNewInfo;
     }
     break;
     case KvpValue::Type::GLIST:
     {
         GncGUID* guid = guid_new ();
-        slot_info_t* pNewInfo = slot_info_copy (pSlot_info, guid);
-        KvpValue* oldValue = pSlot_info->pKvpValue;
-        pSlot_info->pKvpValue = new KvpValue {guid};  // Transfer ownership!
-        pSlot_info->is_ok = pSlot_info->be->do_db_operation(OP_DB_INSERT,
+        slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
+        KvpValue* oldValue = slot_info.pKvpValue;
+        slot_info.pKvpValue = new KvpValue {guid};  // Transfer ownership!
+        slot_info.is_ok = slot_info.be->do_db_operation(OP_DB_INSERT,
                                                             TABLE_NAME,
                                                             TABLE_NAME,
-                                                            pSlot_info,
+                                                            &slot_info,
                                                             col_table);
-        g_return_if_fail (pSlot_info->is_ok);
+        g_return_if_fail (slot_info.is_ok);
         for (auto cursor = value->get<GList*> (); cursor; cursor = cursor->next)
         {
             auto val = static_cast<KvpValue*> (cursor->data);
-            save_slot ("", val, pNewInfo);
+            save_slot ("", val, *pNewInfo);
         }
-        delete pSlot_info->pKvpValue;
-        pSlot_info->pKvpValue = oldValue;
+        delete slot_info.pKvpValue;
+        slot_info.pKvpValue = oldValue;
         delete pNewInfo;
     }
     break;
     default:
     {
-        pSlot_info->is_ok = pSlot_info->be->do_db_operation (OP_DB_INSERT,
+        slot_info.is_ok = slot_info.be->do_db_operation (OP_DB_INSERT,
                                                              TABLE_NAME,
                                                              TABLE_NAME,
-                                                             pSlot_info,
+                                                             &slot_info,
                                                              col_table);
     }
     break;
     }
 
-    pSlot_info->path.erase(curlen);
+    slot_info.path.erase(curlen);
 }
 
 gboolean
@@ -678,7 +675,7 @@ gnc_sql_slots_save (GncSqlBackend* sql_be, const GncGUID* guid, gboolean is_infa
 
     slot_info.be = sql_be;
     slot_info.guid = guid;
-    pFrame->for_each_slot (save_slot, &slot_info);
+    pFrame->for_each_slot_temp (save_slot, slot_info);
 
     return slot_info.is_ok;
 }
diff --git a/libgnucash/backend/xml/sixtp-dom-generators.cpp b/libgnucash/backend/xml/sixtp-dom-generators.cpp
index 22a3f7e..25504dd 100644
--- a/libgnucash/backend/xml/sixtp-dom-generators.cpp
+++ b/libgnucash/backend/xml/sixtp-dom-generators.cpp
@@ -334,7 +334,7 @@ add_kvp_value_node (xmlNodePtr node, const gchar* tag, KvpValue* val)
         auto frame = val->get<KvpFrame*> ();
         if (!frame)
             break;
-        frame->for_each_slot (add_kvp_slot, static_cast<void*> (val_node));
+        frame->for_each_slot_temp (&add_kvp_slot, val_node);
         break;
     }
     default:
@@ -366,6 +366,6 @@ qof_instance_slots_to_dom_tree (const char* tag, const QofInstance* inst)
         return nullptr;
 
     ret = xmlNewNode (nullptr, BAD_CAST tag);
-    frame->for_each_slot (add_kvp_slot, static_cast<void*> (ret));
+    frame->for_each_slot_temp (&add_kvp_slot, ret);
     return ret;
 }
diff --git a/libgnucash/backend/xml/test/test-date-converting.cpp b/libgnucash/backend/xml/test/test-date-converting.cpp
index 992ee85..cae7c19 100644
--- a/libgnucash/backend/xml/test/test-date-converting.cpp
+++ b/libgnucash/backend/xml/test/test-date-converting.cpp
@@ -21,7 +21,6 @@ extern "C"
 {
 #include <config.h>
 
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
 
 #include <stdlib.h>
@@ -30,6 +29,7 @@ extern "C"
 #include "test-file-stuff.h"
 #include "sixtp-utils.h"
 #include "sixtp-dom-generators.h"
+#include "test-stuff.h"
 
 #define GNC_V2_STRING "gnc-v2"
 const gchar* gnc_v2_xml_version_string = GNC_V2_STRING;
diff --git a/libgnucash/backend/xml/test/test-dom-converters1.cpp b/libgnucash/backend/xml/test/test-dom-converters1.cpp
index d5499d9..1035c6e 100644
--- a/libgnucash/backend/xml/test/test-dom-converters1.cpp
+++ b/libgnucash/backend/xml/test/test-dom-converters1.cpp
@@ -30,7 +30,6 @@ extern "C"
 
 #include <glib.h>
 
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
 #include "cashobjects.h"
 #include "gnc-engine.h"
@@ -44,6 +43,7 @@ extern "C"
 #include "sixtp-utils.h"
 #include "sixtp-dom-parsers.h"
 #include "sixtp-dom-generators.h"
+#include "test-stuff.h"
 
 #define GNC_V2_STRING "gnc-v2"
 const gchar* gnc_v2_xml_version_string = GNC_V2_STRING;
diff --git a/libgnucash/backend/xml/test/test-load-example-account.cpp b/libgnucash/backend/xml/test/test-load-example-account.cpp
index 576ddd1..8193ba3 100644
--- a/libgnucash/backend/xml/test/test-load-example-account.cpp
+++ b/libgnucash/backend/xml/test/test-load-example-account.cpp
@@ -34,7 +34,6 @@ extern "C"
 
 #include "gnc-module.h"
 #include "gnc-engine.h"
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
 }
 
@@ -43,6 +42,7 @@ extern "C"
 #include "io-gncxml-v2.h"
 
 #include "io-example-account.h"
+#include "test-stuff.h"
 
 static const gchar* da_ending = ".gnucash-xea";
 
diff --git a/libgnucash/backend/xml/test/test-load-xml2.cpp b/libgnucash/backend/xml/test/test-load-xml2.cpp
index 7731f88..f417fd6 100644
--- a/libgnucash/backend/xml/test/test-load-xml2.cpp
+++ b/libgnucash/backend/xml/test/test-load-xml2.cpp
@@ -45,7 +45,6 @@ extern "C"
 #include <gnc-engine.h>
 #include <gnc-prefs.h>
 
-#include <test-stuff.h>
 #include <unittest-support.h>
 #include <test-engine-stuff.h>
 }
@@ -53,6 +52,7 @@ extern "C"
 #include "../gnc-backend-xml.h"
 #include "../io-gncxml-v2.h"
 #include "test-file-stuff.h"
+#include <test-stuff.h>
 
 #define GNC_LIB_NAME "gncmod-backend-xml"
 #define GNC_LIB_REL_PATH "xml"
diff --git a/libgnucash/backend/xml/test/test-save-in-lang.cpp b/libgnucash/backend/xml/test/test-save-in-lang.cpp
index f7c0fb7..6705348 100644
--- a/libgnucash/backend/xml/test/test-save-in-lang.cpp
+++ b/libgnucash/backend/xml/test/test-save-in-lang.cpp
@@ -30,7 +30,6 @@ extern "C"
 #include <stdlib.h>
 #include <string.h>
 
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
 
 #include "gnc-engine.h"
@@ -39,6 +38,7 @@ extern "C"
 
 #include "test-file-stuff.h"
 #include "io-gncxml-v2.h"
+#include "test-stuff.h"
 
 const char* possible_envs[] =
 {
diff --git a/libgnucash/backend/xml/test/test-string-converters.cpp b/libgnucash/backend/xml/test/test-string-converters.cpp
index a8ad66a..72f74ba 100644
--- a/libgnucash/backend/xml/test/test-string-converters.cpp
+++ b/libgnucash/backend/xml/test/test-string-converters.cpp
@@ -24,13 +24,13 @@ extern "C"
 #include <stdlib.h>
 #include "gnc-engine.h"
 
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
 }
 
 #include "test-file-stuff.h"
 #include "sixtp-dom-parsers.h"
 #include "sixtp-dom-generators.h"
+#include "test-stuff.h"
 
 
 #define GNC_V2_STRING "gnc-v2"
diff --git a/libgnucash/backend/xml/test/test-xml-account.cpp b/libgnucash/backend/xml/test/test-xml-account.cpp
index 8a1eb87..5022a91 100644
--- a/libgnucash/backend/xml/test/test-xml-account.cpp
+++ b/libgnucash/backend/xml/test/test-xml-account.cpp
@@ -32,7 +32,6 @@ extern "C"
 #include <gnc-engine.h>
 #include <cashobjects.h>
 
-#include <test-stuff.h>
 #include <test-engine-stuff.h>
 #include <unittest-support.h>
 
@@ -45,6 +44,7 @@ extern "C"
 #include "../sixtp-parsers.h"
 #include "../sixtp-dom-parsers.h"
 #include "test-file-stuff.h"
+#include <test-stuff.h>
 
 static QofBook* sixbook;
 
diff --git a/libgnucash/backend/xml/test/test-xml-commodity.cpp b/libgnucash/backend/xml/test/test-xml-commodity.cpp
index 07f8f43..71a3060 100644
--- a/libgnucash/backend/xml/test/test-xml-commodity.cpp
+++ b/libgnucash/backend/xml/test/test-xml-commodity.cpp
@@ -28,7 +28,6 @@ extern "C"
 
 #include "gnc-module.h"
 #include "qof.h"
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
 
 #include "Account.h"
@@ -41,6 +40,7 @@ extern "C"
 #include "sixtp-dom-parsers.h"
 #include "io-gncxml-gen.h"
 #include "test-file-stuff.h"
+#include "test-stuff.h"
 
 static QofBook* book;
 
diff --git a/libgnucash/backend/xml/test/test-xml-pricedb.cpp b/libgnucash/backend/xml/test/test-xml-pricedb.cpp
index 4ab584d..18d22ef 100644
--- a/libgnucash/backend/xml/test/test-xml-pricedb.cpp
+++ b/libgnucash/backend/xml/test/test-xml-pricedb.cpp
@@ -34,7 +34,6 @@ extern "C"
 #include "gnc-engine.h"
 #include "gnc-pricedb.h"
 
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
 }
 
@@ -45,6 +44,7 @@ extern "C"
 #include "sixtp-dom-parsers.h"
 #include "io-gncxml-v2.h"
 #include "test-file-stuff.h"
+#include "test-stuff.h"
 
 static QofSession* session;
 static int iter;
diff --git a/libgnucash/backend/xml/test/test-xml-transaction.cpp b/libgnucash/backend/xml/test/test-xml-transaction.cpp
index 5828b43..1154b3b 100644
--- a/libgnucash/backend/xml/test/test-xml-transaction.cpp
+++ b/libgnucash/backend/xml/test/test-xml-transaction.cpp
@@ -38,7 +38,6 @@ extern "C"
 #include <cashobjects.h>
 #include <TransLog.h>
 
-#include <test-stuff.h>
 #include <test-engine-stuff.h>
 #include <unittest-support.h>
 
@@ -53,7 +52,7 @@ extern "C"
 #include "../sixtp-dom-parsers.h"
 #include "../io-gncxml-gen.h"
 #include "test-file-stuff.h"
-
+#include <test-stuff.h>
 static QofBook* book;
 
 extern gboolean gnc_transaction_xml_v2_testing;
diff --git a/libgnucash/backend/xml/test/test-xml2-is-file.cpp b/libgnucash/backend/xml/test/test-xml2-is-file.cpp
index f4234e6..8b698f4 100644
--- a/libgnucash/backend/xml/test/test-xml2-is-file.cpp
+++ b/libgnucash/backend/xml/test/test-xml2-is-file.cpp
@@ -23,12 +23,12 @@ extern "C"
 #include <stdlib.h>
 #include <string.h>
 
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
 }
 
 #include "io-gncxml-v2.h"
 #include "test-file-stuff.h"
+#include "test-stuff.h"
 
 #define FILENAME "Money95bank_fr.gml2"
 
diff --git a/libgnucash/engine/gnc-aqbanking-templates.cpp b/libgnucash/engine/gnc-aqbanking-templates.cpp
index eb8d6e7..fe2c37d 100644
--- a/libgnucash/engine/gnc-aqbanking-templates.cpp
+++ b/libgnucash/engine/gnc-aqbanking-templates.cpp
@@ -30,9 +30,9 @@
 extern "C"
 {
 #include "gnc-aqbanking-templates.h"
-#include "qofinstance-p.h"
 }
 
+#include "qofinstance-p.h"
 #include "kvp-frame.hpp"
 #include "gnc-rational.hpp"
 
diff --git a/libgnucash/engine/kvp-frame.cpp b/libgnucash/engine/kvp-frame.cpp
index f1368bc..0610135 100644
--- a/libgnucash/engine/kvp-frame.cpp
+++ b/libgnucash/engine/kvp-frame.cpp
@@ -214,20 +214,6 @@ KvpFrameImpl::get_keys() const noexcept
     return ret;
 }
 
-void
-KvpFrameImpl::for_each_slot(void (*proc)(const char *key, KvpValue *value,
-                                         void * data),
-                            void *data) const noexcept
-{
-    if (!proc) return;
-    std::for_each (m_valuemap.begin(), m_valuemap.end(),
-        [proc,data](const KvpFrameImpl::map_type::value_type & a)
-        {
-            proc (a.first, a.second, data);
-        }
-    );
-}
-
 KvpValueImpl *
 KvpFrameImpl::get_slot(const char * key) const noexcept
 {
diff --git a/libgnucash/engine/kvp-frame.hpp b/libgnucash/engine/kvp-frame.hpp
index 52908cd..e5308ec 100644
--- a/libgnucash/engine/kvp-frame.hpp
+++ b/libgnucash/engine/kvp-frame.hpp
@@ -204,11 +204,15 @@ struct KvpFrameImpl
      * @return The value at the key or nullptr.
      */
     KvpValue* get_slot(Path keys) const noexcept;
-    /** Convenience wrapper for std::for_each, which should be preferred.
+
+    void for_each_slot(void (*)(char const *key, KvpValue*, void*data), void*data) const noexcept;
+
+    /** The function should be of the form:
+     * <anything> func (char const *, KvpValue *, data_type &);
+     * Do not pass nullptr for the function.
      */
-    void for_each_slot(void (*proc)(const char *key, KvpValue *value,
-                                    void * data),
-                       void *data) const noexcept;
+    template <typename func_type, typename data_type>
+    void for_each_slot_temp (func_type const &, data_type &) const noexcept;
 
     /** Test for emptiness
      * @return true if the frame contains nothing.
@@ -220,6 +224,18 @@ struct KvpFrameImpl
     map_type m_valuemap;
 };
 
+template <typename func_type, typename data_type>
+void KvpFrame::for_each_slot_temp(func_type const & func, data_type & data) const noexcept
+{
+    std::for_each (m_valuemap.begin(), m_valuemap.end(),
+        [&func,&data](const KvpFrameImpl::map_type::value_type & a)
+        {
+            func (a.first, a.second, data);
+        }
+    );
+}
+
+
 int compare (const KvpFrameImpl &, const KvpFrameImpl &) noexcept;
 int compare (const KvpFrameImpl *, const KvpFrameImpl *) noexcept;
 /** @} Doxygen Group */
diff --git a/libgnucash/engine/qof-backend.hpp b/libgnucash/engine/qof-backend.hpp
index 871fec8..ce0df97 100644
--- a/libgnucash/engine/qof-backend.hpp
+++ b/libgnucash/engine/qof-backend.hpp
@@ -45,12 +45,12 @@ extern "C"
 {
 #include "qofbackend.h"
 #include "qofbook.h"
-#include "qofinstance-p.h"
 #include "qofquery.h"
 #include "qofsession.h"
 #include <gmodule.h>
 }
 
+#include "qofinstance-p.h"
 #include <string>
 #include <algorithm>
 #include <vector>
diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index 2bcbebc..4a57af9 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -1118,7 +1118,7 @@ static void commit_err (G_GNUC_UNUSED QofInstance *inst, QofBackendError errcode
 
 #define GNC_FEATURES "features"
 static void
-add_feature_to_hash (const gchar *key, KvpValue *value, gpointer user_data)
+add_feature_to_hash (const gchar *key, KvpValue *value, GHashTable * user_data)
 {
     gchar *descr = g_strdup(value->get<const char*>());
     g_hash_table_insert (*(GHashTable**)user_data, (gchar*)key, descr);
@@ -1134,8 +1134,8 @@ qof_book_get_features (QofBook *book)
     auto slot = frame->get_slot(GNC_FEATURES);
     if (slot != nullptr)
     {
-	frame = slot->get<KvpFrame*>();
-	frame->for_each_slot(&add_feature_to_hash, &features);
+        frame = slot->get<KvpFrame*>();
+        frame->for_each_slot_temp(&add_feature_to_hash, features);
     }
     return features;
 }
diff --git a/libgnucash/engine/qofinstance-p.h b/libgnucash/engine/qofinstance-p.h
index 0c0d4fd..ca6a4c5 100644
--- a/libgnucash/engine/qofinstance-p.h
+++ b/libgnucash/engine/qofinstance-p.h
@@ -33,6 +33,8 @@
 #include "qofinstance.h"
 
 #ifdef __cplusplus
+#include "kvp-frame.hpp"
+#include <string>
 extern "C"
 {
 #endif
@@ -156,8 +158,19 @@ void qof_instance_foreach_slot (const QofInstance *inst, const char *path,
                                 void(*proc)(const char*, const GValue*, void*),
                                 void* data);
 #ifdef __cplusplus
+} /* extern "C" */
+
+/* Don't pass nullptr as the function */
+template<typename func_type, typename data_type>
+void qof_instance_foreach_slot_temp (QofInstance const * inst, std::string const & path,
+        func_type const & func, data_type & data)
+{
+    auto slot = inst->kvp_data->get_slot(path.c_str());
+    if (slot == nullptr || slot->get_type() != KvpValue::Type::FRAME)
+        return;
+    auto frame = slot->get<KvpFrame*>();
+    frame->for_each_slot(func, data);
 }
 #endif
 
-
 #endif /* QOF_INSTANCE_P_H */
diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp
index c307aee..7994fb9 100644
--- a/libgnucash/engine/qofinstance.cpp
+++ b/libgnucash/engine/qofinstance.cpp
@@ -1278,10 +1278,9 @@ struct wrap_param
 };
 }
 static void
-wrap_gvalue_function (const char* key, KvpValue *val, gpointer data)
+wrap_gvalue_function (const char* key, KvpValue *val, wrap_param & param)
 {
     GValue *gv;
-    auto param = static_cast<wrap_param*>(data);
     if (val->get_type() != KvpValue::Type::FRAME)
         gv = gvalue_from_kvp_value(val);
     else
@@ -1290,7 +1289,7 @@ wrap_gvalue_function (const char* key, KvpValue *val, gpointer data)
         g_value_init (gv, G_TYPE_STRING);
         g_value_set_string (gv, nullptr);
     }
-    param->proc(key, gv, param->user_data);
+    param.proc(key, gv, param.user_data);
     g_slice_free (GValue, gv);
 }
 
@@ -1304,7 +1303,7 @@ qof_instance_foreach_slot (const QofInstance *inst, const char* path,
         return;
     auto frame = slot->get<KvpFrame*>();
     wrap_param new_data {proc, data};
-    frame->for_each_slot(wrap_gvalue_function, &new_data);
+    frame->for_each_slot_temp(&wrap_gvalue_function, new_data);
 }
 
 /* ========================== END OF FILE ======================= */
diff --git a/libgnucash/engine/qofsession.cpp b/libgnucash/engine/qofsession.cpp
index 45729ea..2049b5b 100644
--- a/libgnucash/engine/qofsession.cpp
+++ b/libgnucash/engine/qofsession.cpp
@@ -50,12 +50,12 @@ extern "C"
 
 #include <glib.h>
 #include "qof.h"
-#include "qofbook-p.h"
 #include "qofobject-p.h"
 
 static QofLogModule log_module = QOF_MOD_SESSION;
 } //extern 'C'
 
+#include "qofbook-p.h"
 #include "qof-backend.hpp"
 #include "qofsession.hpp"
 #include "gnc-backend-prov.hpp"
diff --git a/libgnucash/engine/test-core/test-engine-stuff.cpp b/libgnucash/engine/test-core/test-engine-stuff.cpp
index b95c14d..c4c4170 100644
--- a/libgnucash/engine/test-core/test-engine-stuff.cpp
+++ b/libgnucash/engine/test-core/test-engine-stuff.cpp
@@ -55,7 +55,6 @@ extern "C"
 #include <string.h>
 #include <sys/stat.h>
 #include <qof.h>
-#include <qofinstance-p.h>
 
 #include "Account.h"
 #include "AccountP.h"
@@ -71,6 +70,7 @@ extern "C"
 #include "test-stuff.h"
 #include "test-engine-strings.h"
 }
+#include <qofinstance-p.h>
 
 static gboolean glist_strings_only = FALSE;
 
diff --git a/libgnucash/engine/test/gtest-import-map.cpp b/libgnucash/engine/test/gtest-import-map.cpp
index 47746d6..f5f3cf2 100644
--- a/libgnucash/engine/test/gtest-import-map.cpp
+++ b/libgnucash/engine/test/gtest-import-map.cpp
@@ -25,9 +25,9 @@ extern "C"
 #include <config.h>
 #include "../Account.h"
 #include <qof.h>
-#include <qofinstance-p.h>
 }
 
+#include <qofinstance-p.h>
 #include <kvp-frame.hpp>
 #include <gtest/gtest.h>
 
diff --git a/libgnucash/engine/test/test-account-object.cpp b/libgnucash/engine/test/test-account-object.cpp
index 2872f85..32f612e 100644
--- a/libgnucash/engine/test/test-account-object.cpp
+++ b/libgnucash/engine/test/test-account-object.cpp
@@ -32,10 +32,10 @@ extern "C"
 #include "qof.h"
 #include "Account.h"
 #include "cashobjects.h"
-#include "test-stuff.h"
 #include "test-engine-stuff.h"
-#include <qofinstance-p.h>
 }
+#include <qofinstance-p.h>
+#include "test-stuff.h"
 
 static void
 run_test (void)
diff --git a/libgnucash/engine/test/utest-Account.cpp b/libgnucash/engine/test/utest-Account.cpp
index 3007907..4e59778 100644
--- a/libgnucash/engine/test/utest-Account.cpp
+++ b/libgnucash/engine/test/utest-Account.cpp
@@ -27,7 +27,6 @@ extern "C"
 #include <unittest-support.h>
 #include <gnc-event.h>
 #include <gnc-date.h>
-#include <qofinstance-p.h>
 /* Add specific headers for this class */
 #include "../Account.h"
 #include "../AccountP.h"
@@ -42,6 +41,7 @@ static const gchar *suitename = "/engine/Account";
 void test_suite_account (void);
 }
 
+#include <qofinstance-p.h>
 #include <kvp-frame.hpp>
 
 typedef struct
diff --git a/libgnucash/engine/test/utest-Split.cpp b/libgnucash/engine/test/utest-Split.cpp
index 7609242..09234a6 100644
--- a/libgnucash/engine/test/utest-Split.cpp
+++ b/libgnucash/engine/test/utest-Split.cpp
@@ -35,7 +35,6 @@ extern "C"
 #include <TransactionP.h>
 #include <gnc-lot.h>
 #include <gnc-event.h>
-#include <qofinstance-p.h>
 
 #if defined(__clang__) && (__clang_major__ == 5 || (__clang_major__ == 3 && __clang_minor__ < 5))
 #define USE_CLANG_FUNC_SIG 1
@@ -45,6 +44,7 @@ static const gchar *suitename = "/engine/Split";
 void test_suite_split ( void );
 }
 
+#include <qofinstance-p.h>
 #include <kvp-frame.hpp>
 
 typedef struct

commit eb6c741bf91f18404007a5d8f2859bd2ecf7caf7
Author: lmat <dartme18 at gmail.com>
Date:   Mon Aug 14 14:32:45 2017 -0400

    Account.c to Account.cpp
    
    Since Account.c is now Account.cpp, the function signatures look a bit
    different internally. The tests rely on function signatures in error
    messages. Instead of trying to figure out what the exact
    function signature might be, I use a substring matching strategy to
    ensure that the correct error was issued.

diff --git a/common/test-core/unittest-support.c b/common/test-core/unittest-support.c
index 86cbe44..dddcef8 100644
--- a/common/test-core/unittest-support.c
+++ b/common/test-core/unittest-support.c
@@ -143,6 +143,29 @@ test_clear_error_list (void)
     message_queue = NULL;
 }
 
+
+gboolean
+test_list_substring_handler (const char *log_domain, GLogLevelFlags log_level,
+                      const gchar *msg, gpointer user_data)
+{
+    GList *list = g_list_first (message_queue);
+    const guint fatal = G_LOG_FLAG_FATAL;
+    while (list)
+    {
+        TestErrorStruct *error = (TestErrorStruct*)list->data;
+        if (!g_strcmp0 (log_domain, error->log_domain)
+                && ((log_level | fatal) == (error->log_level | fatal))
+                && g_strrstr (msg, error->msg))
+        {
+            ++(error->hits);
+            return FALSE;
+        }
+        list = g_list_next (list);
+    }
+    /* No list or no matches, fall through */
+    return test_checked_substring_handler (log_domain, log_level, msg, user_data);
+}
+
 static gboolean
 do_test_list_handler (const char *log_domain, GLogLevelFlags log_level,
                       const gchar *msg, gpointer user_data, gboolean hits)
@@ -206,6 +229,27 @@ do_test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
 }
 
 gboolean
+test_checked_substring_handler (const char *log_domain, GLogLevelFlags log_level,
+                                         const gchar *msg, gpointer user_data)
+{
+    TestErrorStruct *tdata = (TestErrorStruct*)user_data;
+    if ((tdata == NULL)
+            || (tdata->log_domain != NULL
+                && g_strcmp0 (log_domain, tdata->log_domain))
+            || (tdata->log_level && tdata->log_level != log_level)
+            || (tdata->msg && !g_strrstr (msg, tdata->msg)))
+    {
+        gchar *level = test_log_level (log_level);
+        g_printf ( "<%s> (%s) %s\n", level, log_domain, msg);
+        g_free (level);
+        g_assert (log_level ^ G_LOG_FLAG_FATAL);
+        return FALSE;
+    }
+    ++(tdata->hits);
+    return FALSE;
+}
+
+gboolean
 test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
                       const gchar *msg, gpointer user_data )
 {
diff --git a/common/test-core/unittest-support.h b/common/test-core/unittest-support.h
index 61503db..f9e4849 100644
--- a/common/test-core/unittest-support.h
+++ b/common/test-core/unittest-support.h
@@ -179,6 +179,14 @@ GSList *test_log_set_fatal_handler (GSList *list, TestErrorStruct *error,
 void test_free_log_handler (gpointer item);
 
 /**
+ * Check that the user_data error message is a substring of the
+ * actual error otherwise assert.  Displays the error (and asserts
+ * if G_LOG_FLAG_FATAL is TRUE) if NULL is passed as user_data,
+ * but a NULL or 0 value member matches anything.
+ */
+gboolean test_checked_substring_handler (const char *log_domain, GLogLevelFlags log_level,
+                                         const gchar *msg, gpointer user_data);
+/**
  * Check the user_data against the actual error and assert on any
  * differences.  Displays the error (and asserts if G_LOG_FLAG_FATAL
  * is TRUE) if NULL is passed as user_data, but a NULL or 0 value
@@ -215,6 +223,18 @@ void test_clear_error_list (void);
 
 /**
  * Checks received errors against the list created by
+ * test_add_error. Rather than checking for an exact match, this function
+ * checks using a substring match. If the list is empty or nothing
+ * matches, passes control on to test_checked_substring_handler, giving
+ * the opportunity for an additional check that's not in the list
+ * (set user_data to NULL if you want test_checked_handler to
+ * immediately print the error).
+ */
+gboolean test_list_substring_handler (const char *log_domain, GLogLevelFlags log_level,
+                      const gchar *msg, gpointer user_data);
+
+/**
+ * Checks received errors against the list created by
  * test_add_error. If the list is empty or nothing matches, passes
  * control on to test_checked_handler, giving the opportunity for an
  * additional check that's not in the list (set user_data to NULL if
diff --git a/libgnucash/core-utils/gnc-glib-utils.h b/libgnucash/core-utils/gnc-glib-utils.h
index 2999894..a381825 100644
--- a/libgnucash/core-utils/gnc-glib-utils.h
+++ b/libgnucash/core-utils/gnc-glib-utils.h
@@ -39,6 +39,10 @@
 
 #include <glib.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /** @name Character Sets
  @{
 */
@@ -186,6 +190,10 @@ void gnc_gpid_kill(GPid pid);
 
 /** @} */
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* GNC_GLIB_UTILS_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/Account.c b/libgnucash/engine/Account.cpp
similarity index 96%
rename from libgnucash/engine/Account.c
rename to libgnucash/engine/Account.cpp
index bcf5d0a..1b32f9d 100644
--- a/libgnucash/engine/Account.c
+++ b/libgnucash/engine/Account.cpp
@@ -171,7 +171,7 @@ gchar *gnc_account_name_violations_errmsg (const gchar *separator, GList* invali
     for ( node = invalid_account_names;  node; node = g_list_next(node))
     {
         if ( !account_list )
-            account_list = node->data;
+            account_list = static_cast<gchar *>(node->data);
         else
         {
             gchar *tmp_list = NULL;
@@ -251,9 +251,9 @@ gnc_account_init(Account* acc)
     priv->parent   = NULL;
     priv->children = NULL;
 
-    priv->accountName = CACHE_INSERT("");
-    priv->accountCode = CACHE_INSERT("");
-    priv->description = CACHE_INSERT("");
+    priv->accountName = static_cast<char*>(qof_string_cache_insert(""));
+    priv->accountCode = static_cast<char*>(qof_string_cache_insert(""));
+    priv->description = static_cast<char*>(qof_string_cache_insert(""));
 
     priv->type = ACCT_TYPE_NONE;
 
@@ -473,10 +473,10 @@ gnc_account_set_property (GObject         *object,
         break;
     case PROP_TYPE:
         // NEED TO BE CONVERTED TO A G_TYPE_ENUM
-        xaccAccountSetType(account, g_value_get_int(value));
+        xaccAccountSetType(account, static_cast<GNCAccountType>(g_value_get_int(value)));
         break;
     case PROP_COMMODITY:
-        xaccAccountSetCommodity(account, g_value_get_object(value));
+        xaccAccountSetCommodity(account, static_cast<gnc_commodity*>(g_value_get_object(value)));
         break;
     case PROP_COMMODITY_SCU:
         xaccAccountSetCommoditySCU(account, g_value_get_int(value));
@@ -491,19 +491,19 @@ gnc_account_set_property (GObject         *object,
         gnc_account_set_balance_dirty(account);
         break;
     case PROP_START_BALANCE:
-        number = g_value_get_boxed(value);
+        number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
         gnc_account_set_start_balance(account, *number);
         break;
     case PROP_START_CLEARED_BALANCE:
-        number = g_value_get_boxed(value);
+        number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
         gnc_account_set_start_cleared_balance(account, *number);
         break;
     case PROP_START_RECONCILED_BALANCE:
-        number = g_value_get_boxed(value);
+        number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
         gnc_account_set_start_reconciled_balance(account, *number);
         break;
     case PROP_POLICY:
-        gnc_account_set_policy(account, g_value_get_pointer(value));
+        gnc_account_set_policy(account, static_cast<GNCPolicy*>(g_value_get_pointer(value)));
         break;
     case PROP_MARK:
         xaccAccountSetMark(account, g_value_get_int(value));
@@ -595,7 +595,7 @@ gnc_account_class_init (AccountClass *klass)
                           "repeated. but no two accounts that share "
                           "a parent may have the same name.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -606,7 +606,7 @@ gnc_account_class_init (AccountClass *klass)
                           "all its parent account names to indicate "
                           "a unique account.",
                           NULL,
-                          G_PARAM_READABLE));
+                          static_cast<GParamFlags>(G_PARAM_READABLE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -618,7 +618,7 @@ gnc_account_class_init (AccountClass *klass)
                           "be reporting code that is a synonym for "
                           "the accountName.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -630,7 +630,7 @@ gnc_account_class_init (AccountClass *klass)
                           "to be a longer, 1-5 sentence description of "
                           "what this account is all about.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -641,7 +641,7 @@ gnc_account_class_init (AccountClass *klass)
                           "by the user. It is intended to highlight the "
                           "account based on the users wishes.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -652,7 +652,7 @@ gnc_account_class_init (AccountClass *klass)
                           "for the user to attach any other text that "
                           "they would like to associate with the account.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -665,7 +665,7 @@ gnc_account_class_init (AccountClass *klass)
                        ACCT_TYPE_NONE,
                        NUM_ACCOUNT_TYPES - 1,
                        ACCT_TYPE_BANK,
-                       G_PARAM_READWRITE));
+                       static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -676,7 +676,7 @@ gnc_account_class_init (AccountClass *klass)
                           "'stuff' stored  in this account, whether "
                           "it is USD, gold, stock, etc.",
                           GNC_TYPE_COMMODITY,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -691,7 +691,7 @@ gnc_account_class_init (AccountClass *klass)
                        0,
                        G_MAXINT32,
                        1000000,
-                       G_PARAM_READWRITE));
+                       static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -704,7 +704,7 @@ gnc_account_class_init (AccountClass *klass)
                            "mismatched values in older versions of "
                            "GnuCash.",
                            FALSE,
-                           G_PARAM_READWRITE));
+                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -719,7 +719,7 @@ gnc_account_class_init (AccountClass *klass)
                           "affect the sort order of the account. Note: "
                           "This value can only be set to TRUE.",
                           FALSE,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -733,7 +733,7 @@ gnc_account_class_init (AccountClass *klass)
                           "the engine to say a split has been modified. "
                           "Note: This value can only be set to TRUE.",
                           FALSE,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -749,7 +749,7 @@ gnc_account_class_init (AccountClass *klass)
                         "the 'starting balance' will represent the "
                         "summation of the splits up to that date.",
                         GNC_TYPE_NUMERIC,
-                        G_PARAM_READWRITE));
+                        static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -766,7 +766,7 @@ gnc_account_class_init (AccountClass *klass)
                         "balance' will represent the summation of the "
                         "splits up to that date.",
                         GNC_TYPE_NUMERIC,
-                        G_PARAM_READWRITE));
+                        static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -783,7 +783,7 @@ gnc_account_class_init (AccountClass *klass)
                         "balance' will represent the summation of the "
                         "splits up to that date.",
                         GNC_TYPE_NUMERIC,
-                        G_PARAM_READWRITE));
+                        static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -818,7 +818,7 @@ gnc_account_class_init (AccountClass *klass)
                         "the starting balance and all reconciled splits "
                         "in the account.",
                         GNC_TYPE_NUMERIC,
-                        G_PARAM_READABLE));
+                        static_cast<GParamFlags>(G_PARAM_READABLE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -826,7 +826,7 @@ gnc_account_class_init (AccountClass *klass)
      g_param_spec_pointer ("policy",
                            "Policy",
                            "The account lots policy.",
-                           G_PARAM_READWRITE));
+                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -837,7 +837,7 @@ gnc_account_class_init (AccountClass *klass)
                        0,
                        G_MAXINT16,
                        0,
-                       G_PARAM_READWRITE));
+                       static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -847,7 +847,7 @@ gnc_account_class_init (AccountClass *klass)
                            "Whether the account maps to an entry on an "
                            "income tax document.",
                            FALSE,
-                           G_PARAM_READWRITE));
+                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -859,7 +859,7 @@ gnc_account_class_init (AccountClass *klass)
                           "United States it is used to transfer totals "
                           "into tax preparation software.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -868,7 +868,7 @@ gnc_account_class_init (AccountClass *klass)
                           "Tax Source",
                           "This specifies where exported name comes from.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -880,7 +880,7 @@ gnc_account_class_init (AccountClass *klass)
                          (gint64)1,
                          G_MAXINT64,
                          (gint64)1,
-                         G_PARAM_READWRITE));
+                         static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -890,7 +890,7 @@ gnc_account_class_init (AccountClass *klass)
                            "Whether the account should be hidden in the  "
                            "account tree.",
                            FALSE,
-                           G_PARAM_READWRITE));
+                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -900,7 +900,7 @@ gnc_account_class_init (AccountClass *klass)
                            "Whether the account is a placeholder account which does not "
                            "allow transactions to be created, edited or deleted.",
                            FALSE,
-                           G_PARAM_READWRITE));
+                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -910,7 +910,7 @@ gnc_account_class_init (AccountClass *klass)
                           "The account filter is a value saved to allow "
                           "filters to be recalled.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -920,7 +920,7 @@ gnc_account_class_init (AccountClass *klass)
                           "The account sort order is a value saved to allow "
                           "the sort order to be recalled.",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -929,7 +929,7 @@ gnc_account_class_init (AccountClass *klass)
                           "Account Sort Reversed",
                           "Parameter to store whether the sort order is reversed or not.",
                           FALSE,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -940,7 +940,7 @@ gnc_account_class_init (AccountClass *klass)
                          (gint64)1,
                          G_MAXINT64,
                          (gint64)1,
-                         G_PARAM_READWRITE));
+                         static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -950,7 +950,7 @@ gnc_account_class_init (AccountClass *klass)
                           "The online account which corresponds to this "
                           "account for OFX import",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
      g_object_class_install_property(
        gobject_class,
@@ -959,7 +959,7 @@ gnc_account_class_init (AccountClass *klass)
                            "Associated income account",
                            "Used by the OFX importer.",
                            GNC_TYPE_GUID,
-                           G_PARAM_READWRITE));
+                           static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -969,7 +969,7 @@ gnc_account_class_init (AccountClass *klass)
                           "The AqBanking account which corresponds to this "
                           "account for AQBanking import",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
     g_object_class_install_property
     (gobject_class,
      PROP_AB_BANK_CODE,
@@ -978,7 +978,7 @@ gnc_account_class_init (AccountClass *klass)
                           "The online account which corresponds to this "
                           "account for AQBanking import",
                           NULL,
-                          G_PARAM_READWRITE));
+                          static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -989,7 +989,7 @@ gnc_account_class_init (AccountClass *klass)
                          (gint64)1,
                          G_MAXINT64,
                          (gint64)1,
-                         G_PARAM_READWRITE));
+                         static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
     g_object_class_install_property
     (gobject_class,
@@ -999,7 +999,7 @@ gnc_account_class_init (AccountClass *klass)
                         "The time of the last transaction retrieval for this "
                         "account.",
                         GNC_TYPE_TIMESPEC,
-                        G_PARAM_READWRITE));
+                        static_cast<GParamFlags>(G_PARAM_READWRITE)));
 
 }
 
@@ -1028,7 +1028,7 @@ static Account *
 gnc_coll_get_root_account (QofCollection *col)
 {
     if (!col) return NULL;
-    return qof_collection_get_data (col);
+    return static_cast<Account*>(qof_collection_get_data (col));
 }
 
 static void
@@ -1101,7 +1101,7 @@ xaccMallocAccount (QofBook *book)
 
     g_return_val_if_fail (book, NULL);
 
-    acc = g_object_new (GNC_TYPE_ACCOUNT, NULL);
+    acc = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, NULL));
     xaccInitAccount (acc, book);
     qof_event_gen (&acc->inst, QOF_EVENT_CREATE, NULL);
 
@@ -1118,7 +1118,7 @@ gnc_account_create_root (QofBook *book)
     rpriv = GET_PRIVATE(root);
     xaccAccountBeginEdit(root);
     rpriv->type = ACCT_TYPE_ROOT;
-    CACHE_REPLACE(rpriv->accountName, "Root Account");
+    qof_string_cache_replace((void const **)(&rpriv->accountName), "Root Account");
     mark_account (root);
     xaccAccountCommitEdit(root);
     gnc_book_set_root_account(book, root);
@@ -1135,7 +1135,7 @@ xaccCloneAccount(const Account *from, QofBook *book)
     g_return_val_if_fail(QOF_IS_BOOK(book), NULL);
 
     ENTER (" ");
-    ret = g_object_new (GNC_TYPE_ACCOUNT, NULL);
+    ret = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, NULL));
     g_return_val_if_fail (ret, NULL);
 
     from_priv = GET_PRIVATE(from);
@@ -1147,9 +1147,9 @@ xaccCloneAccount(const Account *from, QofBook *book)
      * Also let caller issue the generate_event (EVENT_CREATE) */
     priv->type = from_priv->type;
 
-    priv->accountName = CACHE_INSERT(from_priv->accountName);
-    priv->accountCode = CACHE_INSERT(from_priv->accountCode);
-    priv->description = CACHE_INSERT(from_priv->description);
+    priv->accountName = static_cast<char*>(qof_string_cache_insert(from_priv->accountName));
+    priv->accountCode = static_cast<char*>(qof_string_cache_insert(from_priv->accountCode));
+    priv->description = static_cast<char*>(qof_string_cache_insert(from_priv->description));
 
     qof_instance_copy_kvp (QOF_INSTANCE (ret), QOF_INSTANCE (from));
 
@@ -1229,7 +1229,7 @@ xaccFreeAccount (Account *acc)
 
         for (lp = priv->lots; lp; lp = lp->next)
         {
-            GNCLot *lot = lp->data;
+            GNCLot *lot = static_cast<GNCLot*>(lp->data);
             gnc_lot_destroy (lot);
         }
         g_list_free (priv->lots);
@@ -1261,15 +1261,16 @@ xaccFreeAccount (Account *acc)
 */
     }
 
-    CACHE_REPLACE(priv->accountName, NULL);
-    CACHE_REPLACE(priv->accountCode, NULL);
-    CACHE_REPLACE(priv->description, NULL);
+    qof_string_cache_remove(priv->accountName);
+    qof_string_cache_remove(priv->accountCode);
+    qof_string_cache_remove(priv->description);
+    priv->accountName = priv->accountCode = priv->description = nullptr;
 
     /* zero out values, just in case stray
      * pointers are pointing here. */
 
-    priv->parent = NULL;
-    priv->children = NULL;
+    priv->parent = nullptr;
+    priv->children = nullptr;
 
     priv->balance  = gnc_numeric_zero();
     priv->cleared_balance = gnc_numeric_zero();
@@ -1327,7 +1328,7 @@ destroy_pending_splits_for_account(QofInstance *ent, gpointer acc)
     Split *split;
 
     if (xaccTransIsOpen(trans))
-        while ((split = xaccTransFindSplitByAccount(trans, acc)))
+        while ((split = xaccTransFindSplitByAccount(trans, static_cast<Account*>(acc))))
             xaccSplitDestroy(split);
 }
 
@@ -1365,7 +1366,7 @@ xaccAccountCommitEdit (Account *acc)
             slist = g_list_copy(priv->splits);
             for (lp = slist; lp; lp = lp->next)
             {
-                Split *s = lp->data;
+                Split *s = static_cast<Split *>(lp->data);
                 xaccSplitDestroy (s);
             }
             g_list_free(slist);
@@ -1392,7 +1393,7 @@ xaccAccountCommitEdit (Account *acc)
             /* the lots should be empty by now */
             for (lp = priv->lots; lp; lp = lp->next)
             {
-                GNCLot *lot = lp->data;
+                GNCLot *lot = static_cast<GNCLot*>(lp->data);
                 gnc_lot_destroy (lot);
             }
         }
@@ -1455,7 +1456,7 @@ xaccAcctChildrenEqual(const GList *na,
 
     while (na)
     {
-        Account *aa = na->data;
+        Account *aa = static_cast<Account*>(na->data);
         Account *ab;
         GList *node = g_list_find_custom ((GList*)nb, aa,
                                           (GCompareFunc)compare_account_by_name);
@@ -1465,7 +1466,7 @@ xaccAcctChildrenEqual(const GList *na,
             PINFO ("Unable to find matching child account.");
             return FALSE;
         }
-        ab = node->data;
+        ab = static_cast<Account*>(node->data);
         if (!xaccAccountEqual(aa, ab, check_guids))
         {
             char sa[GUID_ENCODING_LENGTH + 1];
@@ -1883,7 +1884,7 @@ xaccClearMarkDown (Account *acc, short val)
     priv->mark = val;
     for (node = priv->children; node; node = node->next)
     {
-        xaccClearMarkDown(node->data, val);
+        xaccClearMarkDown(static_cast<Account*>(node->data), val);
     }
 }
 
@@ -2254,7 +2255,7 @@ xaccAccountSetName (Account *acc, const char *str)
         return;
 
     xaccAccountBeginEdit(acc);
-    CACHE_REPLACE(priv->accountName, str);
+    qof_string_cache_replace((void const **)(&priv->accountName), str);
     mark_account (acc);
     xaccAccountCommitEdit(acc);
 }
@@ -2273,7 +2274,7 @@ xaccAccountSetCode (Account *acc, const char *str)
         return;
 
     xaccAccountBeginEdit(acc);
-    CACHE_REPLACE(priv->accountCode, str ? str : "");
+    qof_string_cache_replace((void const **)(&priv->accountCode), str ? str : "");
     mark_account (acc);
     xaccAccountCommitEdit(acc);
 }
@@ -2292,7 +2293,7 @@ xaccAccountSetDescription (Account *acc, const char *str)
         return;
 
     xaccAccountBeginEdit(acc);
-    CACHE_REPLACE(priv->description, str ? str : "");
+    qof_string_cache_replace((void const **)(&priv->description), str ? str : "");
     mark_account (acc);
     xaccAccountCommitEdit(acc);
 }
@@ -2691,7 +2692,7 @@ Account *
 gnc_account_nth_child (const Account *parent, gint num)
 {
     g_return_val_if_fail(GNC_IS_ACCOUNT(parent), NULL);
-    return g_list_nth_data(GET_PRIVATE(parent)->children, num);
+    return static_cast<Account*>(g_list_nth_data(GET_PRIVATE(parent)->children, num));
 }
 
 gint
@@ -2706,7 +2707,7 @@ gnc_account_n_descendants (const Account *account)
     priv = GET_PRIVATE(account);
     for (node = priv->children; node; node = g_list_next(node))
     {
-        count += gnc_account_n_descendants(node->data) + 1;
+        count += gnc_account_n_descendants(static_cast<Account*>(node->data)) + 1;
     }
     return count;
 }
@@ -2745,7 +2746,7 @@ gnc_account_get_tree_depth (const Account *account)
 
     for (node = priv->children; node; node = g_list_next(node))
     {
-        child_depth = gnc_account_get_tree_depth(node->data);
+        child_depth = gnc_account_get_tree_depth(static_cast<Account const *>(node->data));
         depth = MAX(depth, child_depth);
     }
     return depth + 1;
@@ -2768,7 +2769,7 @@ gnc_account_get_descendants (const Account *account)
     {
         descendants = g_list_append(descendants, child->data);
         descendants = g_list_concat(descendants,
-                                    gnc_account_get_descendants(child->data));
+                gnc_account_get_descendants(static_cast<Account const *>(child->data)));
     }
     return descendants;
 }
@@ -2793,7 +2794,7 @@ gnc_account_get_descendants_sorted (const Account *account)
     {
         descendants = g_list_append(descendants, child->data);
         descendants = g_list_concat(descendants,
-                                    gnc_account_get_descendants_sorted(child->data));
+                gnc_account_get_descendants_sorted(static_cast<Account const *>(child->data)));
     }
     g_list_free(children);
     return descendants;
@@ -2813,7 +2814,7 @@ gnc_account_lookup_by_name (const Account *parent, const char * name)
     ppriv = GET_PRIVATE(parent);
     for (node = ppriv->children; node; node = node->next)
     {
-        child = node->data;
+        child = static_cast<Account*>(node->data);
         cpriv = GET_PRIVATE(child);
         if (g_strcmp0(cpriv->accountName, name) == 0)
             return child;
@@ -2823,7 +2824,7 @@ gnc_account_lookup_by_name (const Account *parent, const char * name)
      * Recursively search each of the child accounts next */
     for (node = ppriv->children; node; node = node->next)
     {
-        child = node->data;
+        child = static_cast<Account*>(node->data);
         result = gnc_account_lookup_by_name (child, name);
         if (result)
             return result;
@@ -2846,7 +2847,7 @@ gnc_account_lookup_by_code (const Account *parent, const char * code)
     ppriv = GET_PRIVATE(parent);
     for (node = ppriv->children; node; node = node->next)
     {
-        child = node->data;
+        child = static_cast<Account*>(node->data);
         cpriv = GET_PRIVATE(child);
         if (g_strcmp0(cpriv->accountCode, code) == 0)
             return child;
@@ -2856,7 +2857,7 @@ gnc_account_lookup_by_code (const Account *parent, const char * code)
      * Recursively search each of the child accounts next */
     for (node = ppriv->children; node; node = node->next)
     {
-        child = node->data;
+        child = static_cast<Account*>(node->data);
         result = gnc_account_lookup_by_code (child, code);
         if (result)
             return result;
@@ -2884,7 +2885,7 @@ gnc_account_lookup_by_full_name_helper (const Account *parent,
     ppriv = GET_PRIVATE(parent);
     for (node = ppriv->children; node; node = node->next)
     {
-        Account *account = node->data;
+        Account *account = static_cast<Account*>(node->data);
 
         priv = GET_PRIVATE(account);
         if (g_strcmp0(priv->accountName, names[0]) == 0)
@@ -2950,7 +2951,7 @@ gnc_account_foreach_child (const Account *acc,
     priv = GET_PRIVATE(acc);
     for (node = priv->children; node; node = node->next)
     {
-        thunk (node->data, user_data);
+        thunk (static_cast<Account*>(node->data), user_data);
     }
 }
 
@@ -2969,7 +2970,7 @@ gnc_account_foreach_descendant (const Account *acc,
     priv = GET_PRIVATE(acc);
     for (node = priv->children; node; node = node->next)
     {
-        child = node->data;
+        child = static_cast<Account*>(node->data);
         thunk(child, user_data);
         gnc_account_foreach_descendant(child, thunk, user_data);
     }
@@ -2991,7 +2992,7 @@ gnc_account_foreach_descendant_until (const Account *acc,
     priv = GET_PRIVATE(acc);
     for (node = priv->children; node; node = node->next)
     {
-        child = node->data;
+        child = static_cast<Account*>(node->data);
         result = thunk(child, user_data);
         if (result)
             return(result);
@@ -3040,7 +3041,6 @@ gnc_account_get_full_name(const Account *account)
     AccountPrivate *priv;
     const Account *a;
     char *fullname;
-    gchar **names;
     int level;
 
     /* So much for hardening the API. Too many callers to this function don't
@@ -3067,7 +3067,7 @@ gnc_account_get_full_name(const Account *account)
 
     /* Get all the pointers in the right order. The root node "entry"
      * becomes the terminating NULL pointer for the array of strings. */
-    names = g_malloc(level * sizeof(gchar *));
+    gchar* names[level*sizeof(gchar*)];
     names[--level] = NULL;
     for (a = account; level > 0; a = priv->parent)
     {
@@ -3077,7 +3077,6 @@ gnc_account_get_full_name(const Account *account)
 
     /* Build the full name */
     fullname =  g_strjoinv(account_separator, names);
-    g_free(names);
 
     return fullname;
 }
@@ -3267,7 +3266,7 @@ xaccAccountGetProjectedMinimumBalance (const Account *acc)
     today = gnc_time64_get_today_end();
     for (node = g_list_last(priv->splits); node; node = node->prev)
     {
-        Split *split = node->data;
+        Split *split = static_cast<Split*>(node->data);
 
         if (!seen_a_transaction)
         {
@@ -3383,7 +3382,7 @@ xaccAccountGetPresentBalance (const Account *acc)
     today = gnc_time64_get_today_end();
     for (node = g_list_last(priv->splits); node; node = node->prev)
     {
-        Split *split = node->data;
+        Split *split = static_cast<Split*>(node->data);
 
         if (xaccTransGetDate (xaccSplitGetParent (split)) <= today)
             return xaccSplitGetBalance (split);
@@ -3517,7 +3516,7 @@ typedef struct
 static void
 xaccAccountBalanceHelper (Account *acc, gpointer data)
 {
-    CurrencyBalance *cb = data;
+    CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
     gnc_numeric balance;
 
     if (!cb->fn || !cb->currency)
@@ -3531,7 +3530,7 @@ xaccAccountBalanceHelper (Account *acc, gpointer data)
 static void
 xaccAccountBalanceAsOfDateHelper (Account *acc, gpointer data)
 {
-    CurrencyBalance *cb = data;
+    CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
     gnc_numeric balance;
 
     g_return_if_fail (cb->asOfDateFn && cb->currency);
@@ -3768,7 +3767,7 @@ xaccAccountFindOpenLots (const Account *acc,
     priv = GET_PRIVATE(acc);
     for (lot_list = priv->lots; lot_list; lot_list = lot_list->next)
     {
-        GNCLot *lot = lot_list->data;
+        GNCLot *lot = static_cast<GNCLot*>(lot_list->data);
 
         /* If this lot is closed, then ignore it */
         if (gnc_lot_is_closed (lot))
@@ -4111,7 +4110,7 @@ xaccAccountStringToEnum(const char* str)
 /********************************************************************\
 \********************************************************************/
 
-static char *
+static char const *
 account_type_name[NUM_ACCOUNT_TYPES] =
 {
     N_("Bank"),
@@ -4763,7 +4762,7 @@ finder_help_function(const Account *acc, const char *description,
     priv = GET_PRIVATE(acc);
     for (slp = g_list_last(priv->splits); slp; slp = slp->prev)
     {
-        Split *lsplit = slp->data;
+        Split *lsplit = static_cast<Split*>(slp->data);
         Transaction *ltrans = xaccSplitGetParent(lsplit);
 
         if (g_strcmp0 (description, xaccTransGetDescription (ltrans)) == 0)
@@ -4821,7 +4820,7 @@ gnc_account_join_children (Account *to_parent, Account *from_parent)
     ENTER (" ");
     children = g_list_copy(from_priv->children);
     for (node = children; node; node = g_list_next(node))
-        gnc_account_append_child(to_parent, node->data);
+        gnc_account_append_child(to_parent, static_cast <Account*> (node->data));
     g_list_free(children);
     LEAVE (" ");
 }
@@ -4839,12 +4838,12 @@ gnc_account_merge_children (Account *parent)
     ppriv = GET_PRIVATE(parent);
     for (node_a = ppriv->children; node_a; node_a = node_a->next)
     {
-        Account *acc_a = node_a->data;
+        Account *acc_a = static_cast <Account*> (node_a->data);
 
         priv_a = GET_PRIVATE(acc_a);
         for (node_b = node_a->next; node_b; node_b = g_list_next(node_b))
         {
-            Account *acc_b = node_b->data;
+            Account *acc_b = static_cast <Account*> (node_b->data);
 
             priv_b = GET_PRIVATE(acc_b);
             if (0 != null_strcmp(priv_a->accountName, priv_b->accountName))
@@ -4881,7 +4880,7 @@ gnc_account_merge_children (Account *parent)
 
             /* consolidate transactions */
             while (priv_b->splits)
-                xaccSplitSetAccount (priv_b->splits->data, acc_a);
+                xaccSplitSetAccount (static_cast <Split*> (priv_b->splits->data), acc_a);
 
             /* move back one before removal. next iteration around the loop
              * will get the node after node_b */
@@ -4906,7 +4905,7 @@ xaccSplitsBeginStagedTransactionTraversals (GList *splits)
 
     for (lp = splits; lp; lp = lp->next)
     {
-        Split *s = lp->data;
+        Split *s = static_cast <Split*> (lp->data);
         Transaction *trans = s->parent;
 
         if (trans)
@@ -4987,7 +4986,7 @@ xaccAccountStagedTransactionTraversal (const Account *acc,
          * a thunk removes splits from this account. */
         next = g_list_next(split_p);
 
-        s = split_p->data;
+        s = static_cast <Split*> (split_p->data);
         trans = s->parent;
         if (trans && (trans->marker < stage))
         {
@@ -5021,15 +5020,15 @@ gnc_account_tree_staged_transaction_traversal (const Account *acc,
     priv = GET_PRIVATE(acc);
     for (acc_p = priv->children; acc_p; acc_p = g_list_next(acc_p))
     {
-        retval = gnc_account_tree_staged_transaction_traversal(acc_p->data, stage,
-                 thunk, cb_data);
+        retval = gnc_account_tree_staged_transaction_traversal(static_cast <Account*> (acc_p->data),
+                stage, thunk, cb_data);
         if (retval) return retval;
     }
 
     /* Now this account */
     for (split_p = priv->splits; split_p; split_p = g_list_next(split_p))
     {
-        s = split_p->data;
+        s = static_cast <Split*> (split_p->data);
         trans = s->parent;
         if (trans && (trans->marker < stage))
         {
@@ -5294,7 +5293,7 @@ highestProbability(gpointer key, gpointer value, gpointer data)
     {
         /* Save the new highest probability and the assoaciated account guid */
         account_i->probability = GPOINTER_TO_INT(value);
-        account_i->account_guid = key;
+        account_i->account_guid = static_cast <char*> (key);
     }
 }
 
@@ -5370,8 +5369,8 @@ gnc_account_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens)
                   account_c->account_guid, account_c->token_count,
                   tokenInfo.total_count);
 
-            account_p = g_hash_table_lookup(running_probabilities,
-                                            account_c->account_guid);
+            account_p = static_cast <account_probability*> (
+                    g_hash_table_lookup(running_probabilities, account_c->account_guid));
 
             /* if the account exists in the list then continue
              * the running probablities
@@ -5612,7 +5611,7 @@ build_bayes_layer_two (const char *key, const GValue *value, gpointer user_data)
 
     g_free (guid);
 
-    imapInfo_node = g_malloc(sizeof(*imapInfo_node));
+    imapInfo_node = static_cast <imap_info*> (g_malloc(sizeof(*imapInfo_node)));
 
     imapInfo_node->source_account = imapInfo->source_account;
     imapInfo_node->map_account    = map_account;
@@ -5688,7 +5687,7 @@ build_non_bayes (const char *key, const GValue *value, gpointer user_data)
 
         PINFO("build_non_bayes: kvp_path is '%s'", kvp_path);
 
-        imapInfo_node = g_malloc(sizeof(*imapInfo_node));
+        imapInfo_node = static_cast <imap_info*> (g_malloc(sizeof(*imapInfo_node)));
 
         imapInfo_node->source_account = imapInfo->source_account;
         imapInfo_node->map_account    = xaccAccountLookup (guid, book);
@@ -5810,7 +5809,7 @@ look_for_old_separator_descendants (Account *root, gchar *full_name, const gchar
     /* Go through list of top level accounts */
     for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
     {
-        const gchar *name = xaccAccountGetName (ptr->data);
+        const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
 
         // we are looking for the longest top level account that matches
         if (g_str_has_prefix (full_name, name))
@@ -5935,7 +5934,7 @@ convert_imap_account (Account *acc)
         for (node = imap_list;  node; node = g_list_next (node))
         {
             Account *map_account = NULL;
-            GncImapInfo *imapInfo = node->data;
+            GncImapInfo *imapInfo = static_cast <GncImapInfo *> (node->data);
 
             // Lets start doing stuff
             map_account = look_for_old_mapping (imapInfo);
@@ -5980,7 +5979,7 @@ gnc_account_imap_convert_bayes (QofBook *book)
         /* Go through list of accounts */
         for (ptr = accts; ptr; ptr = g_list_next (ptr))
         {
-            Account *acc = ptr->data;
+            Account *acc = static_cast <Account*> (ptr->data);
 
             convert_imap_account (acc);
         }
@@ -6020,7 +6019,7 @@ static QofObject account_object_def =
     DI(.interface_version = ) QOF_OBJECT_VERSION,
     DI(.e_type            = ) GNC_ID_ACCOUNT,
     DI(.type_label        = ) "Account",
-    DI(.create            = ) (gpointer)xaccMallocAccount,
+    DI(.create            = ) (void*(*)(QofBook*)) xaccMallocAccount,
     DI(.book_begin        = ) NULL,
     DI(.book_end          = ) gnc_account_book_end,
     DI(.is_dirty          = ) qof_collection_is_dirty,
diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h
index 13f265c..2f80632 100644
--- a/libgnucash/engine/Account.h
+++ b/libgnucash/engine/Account.h
@@ -48,6 +48,9 @@
 #include "gnc-engine.h"
 #include "policy.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
 typedef gnc_numeric (*xaccGetBalanceFn)( const Account *account );
 
 typedef gnc_numeric (*xaccGetBalanceInCurrencyFn) (
@@ -1517,6 +1520,10 @@ const char * dxaccAccountGetQuoteTZ (const Account *account);
  * in the gnome-search parameter list.  Be careful when you use this. */
 #define ACCOUNT_MATCH_ALL_TYPE	"account-match-all"
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* XACC_ACCOUNT_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/AccountP.h b/libgnucash/engine/AccountP.h
index a3a2bdd..a1ab6a0 100644
--- a/libgnucash/engine/AccountP.h
+++ b/libgnucash/engine/AccountP.h
@@ -41,6 +41,10 @@
 
 #include "Account.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #define GNC_ID_ROOT_ACCOUNT        "RootAccount"
 
 /** STRUCTS *********************************************************/
@@ -149,5 +153,8 @@ typedef struct
 
 AccountTestFunctions* _utest_account_fill_functions(void);
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
 
 #endif /* XACC_ACCOUNT_P_H */
diff --git a/libgnucash/engine/CMakeLists.txt b/libgnucash/engine/CMakeLists.txt
index eeeb7f9..993eb5e 100644
--- a/libgnucash/engine/CMakeLists.txt
+++ b/libgnucash/engine/CMakeLists.txt
@@ -136,7 +136,7 @@ ADD_CUSTOM_COMMAND (
 ADD_CUSTOM_TARGET(iso-4217-c DEPENDS ${ISO_4217_C})
 
 SET (engine_SOURCES
-  Account.c
+  Account.cpp
   Recurrence.c
   Query.c
   SchedXaction.c
diff --git a/libgnucash/engine/Makefile.am b/libgnucash/engine/Makefile.am
index 49c70cc..088b9b4 100644
--- a/libgnucash/engine/Makefile.am
+++ b/libgnucash/engine/Makefile.am
@@ -18,7 +18,7 @@ AM_CPPFLAGS = \
 
 
 libgncmod_engine_la_SOURCES = \
-  Account.c \
+  Account.cpp \
   Recurrence.c \
   Query.c \
   SchedXaction.c \
diff --git a/libgnucash/engine/Split.h b/libgnucash/engine/Split.h
index 1c45080..4d7f417 100644
--- a/libgnucash/engine/Split.h
+++ b/libgnucash/engine/Split.h
@@ -41,6 +41,10 @@ typedef struct _SplitClass SplitClass;
 #include "gnc-commodity.h"
 #include "gnc-engine.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* --- type macros --- */
 #define GNC_TYPE_SPLIT            (gnc_split_get_type ())
 #define GNC_SPLIT(o)              \
@@ -550,6 +554,10 @@ gnc_numeric xaccSplitVoidFormerValue(const Split *split);
 /** \deprecated */
 #define xaccSplitReturnGUID(X) (X ? *(qof_entity_get_guid(QOF_INSTANCE(X))) : *(guid_null()))
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* XACC_SPLIT_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/Transaction.h b/libgnucash/engine/Transaction.h
index a196e89..2d14389 100644
--- a/libgnucash/engine/Transaction.h
+++ b/libgnucash/engine/Transaction.h
@@ -94,6 +94,10 @@ typedef struct _TransactionClass TransactionClass;
 #include "gnc-engine.h"
 #include "Split.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* --- type macros --- */
 #define GNC_TYPE_TRANSACTION            (gnc_transaction_get_type ())
 #define GNC_TRANSACTION(o)              \
@@ -789,6 +793,10 @@ void xaccTransDump (const Transaction *trans, const char *tag);
 /** \deprecated */
 #define xaccTransReturnGUID(X) (X ? *(qof_entity_get_guid(QOF_INSTANCE(X))) : *(guid_null()))
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* XACC_TRANSACTION_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/gnc-commodity.h b/libgnucash/engine/gnc-commodity.h
index 8daa83b..b5dcc93 100644
--- a/libgnucash/engine/gnc-commodity.h
+++ b/libgnucash/engine/gnc-commodity.h
@@ -53,6 +53,10 @@ typedef struct _GncCommodityNamespaceClass gnc_commodity_namespaceClass;
 #include <glib/gi18n.h>
 #include "gnc-engine.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* --- type macros --- */
 #define GNC_TYPE_COMMODITY            (gnc_commodity_get_type ())
 #define GNC_COMMODITY(o)              \
@@ -1056,6 +1060,10 @@ void gnc_monetary_list_free(MonetaryList *list);
 
 /** @} */
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* GNC_COMMODITY_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/gnc-engine.h b/libgnucash/engine/gnc-engine.h
index f8f1c98..651cf44 100644
--- a/libgnucash/engine/gnc-engine.h
+++ b/libgnucash/engine/gnc-engine.h
@@ -39,6 +39,10 @@
 #include <glib.h>
 #include "qof.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /** \name QofLogModule identifiers */
 // @{
 #define GNC_MOD_ROOT      "gnc"
@@ -257,6 +261,9 @@ void gnc_engine_signal_commit_error( QofBackendError errcode );
 #define GNC_OWNER_GUID    "owner-guid"
 #define GNC_SX_ID         "sched-xaction"
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
 
 #endif
 /** @} */
diff --git a/libgnucash/engine/gnc-features.h b/libgnucash/engine/gnc-features.h
index 39f1c03..feefe8d 100644
--- a/libgnucash/engine/gnc-features.h
+++ b/libgnucash/engine/gnc-features.h
@@ -38,6 +38,10 @@
 
 #include "qof.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /** @name Defined features
 @{
  */
@@ -64,6 +68,10 @@ gchar *gnc_features_test_unknown (QofBook *book);
  */
 void gnc_features_set_used (QofBook *book, const gchar *feature);
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* GNC_FEATURES_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/gnc-lot.h b/libgnucash/engine/gnc-lot.h
index 64e1dfa..c725b6f 100644
--- a/libgnucash/engine/gnc-lot.h
+++ b/libgnucash/engine/gnc-lot.h
@@ -65,6 +65,10 @@
 #include "gnc-engine.h"
 /*#include "gnc-lot-p.h"*/
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct
 {
     QofInstanceClass parent_class;
@@ -174,6 +178,11 @@ GNCLot * gnc_lot_make_default (Account * acc);
 #define LOT_BALANCE     "balance"
 #define LOT_TITLE       "lot-title"
 #define LOT_NOTES       "notes"
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* GNC_LOT_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/gnc-pricedb.h b/libgnucash/engine/gnc-pricedb.h
index f7eefa3..671c87e 100644
--- a/libgnucash/engine/gnc-pricedb.h
+++ b/libgnucash/engine/gnc-pricedb.h
@@ -31,6 +31,10 @@ typedef struct _GncPriceDBClass GNCPriceDBClass;
 #include "gnc-commodity.h"
 #include "gnc-engine.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* --- type macros --- */
 #define GNC_TYPE_PRICE            (gnc_price_get_type ())
 #define GNC_PRICE(o)              \
@@ -662,6 +666,10 @@ void gnc_pricedb_print_contents(GNCPriceDB *db, FILE *f);
 
 /** @} */
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* GNC_PRICEDB_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/policy.h b/libgnucash/engine/policy.h
index 42dc812..71e4c0ed 100644
--- a/libgnucash/engine/policy.h
+++ b/libgnucash/engine/policy.h
@@ -37,6 +37,10 @@
 #ifndef XACC_POLICY_H
 #define XACC_POLICY_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct gncpolicy_s GNCPolicy;
 
 /** Valid Policy List
@@ -83,6 +87,10 @@ GNCPolicy *xaccGetFIFOPolicy (void);
  */
 GNCPolicy *xaccGetLIFOPolicy (void);
 
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
 #endif /* XACC_POLICY_H */
 /** @} */
 /** @} */
diff --git a/libgnucash/engine/qof-string-cache.cpp b/libgnucash/engine/qof-string-cache.cpp
index 70e79d7..7390a63 100644
--- a/libgnucash/engine/qof-string-cache.cpp
+++ b/libgnucash/engine/qof-string-cache.cpp
@@ -132,4 +132,11 @@ qof_string_cache_insert(gconstpointer key)
     return NULL;
 }
 
+void
+qof_string_cache_replace(gconstpointer * dst, gconstpointer src)
+{
+    gpointer tmp {qof_string_cache_insert(src)};
+    qof_string_cache_remove(&dst);
+    *dst = tmp;
+}
 /* ************************ END OF FILE ***************************** */
diff --git a/libgnucash/engine/qof-string-cache.h b/libgnucash/engine/qof-string-cache.h
index 4aaa485..2fd5274 100644
--- a/libgnucash/engine/qof-string-cache.h
+++ b/libgnucash/engine/qof-string-cache.h
@@ -86,6 +86,10 @@ void qof_string_cache_remove(gconstpointer key);
 */
 gpointer qof_string_cache_insert(gconstpointer key);
 
+/** Same as CACHE_REPLACE below, but safe to call from C++.
+ */
+void qof_string_cache_replace(gconstpointer * dst, gconstpointer src);
+
 #define CACHE_INSERT(str) qof_string_cache_insert((gconstpointer)(str))
 #define CACHE_REMOVE(str) qof_string_cache_remove((str))
 
diff --git a/libgnucash/engine/test/utest-Account.cpp b/libgnucash/engine/test/utest-Account.cpp
index 06dae62..3007907 100644
--- a/libgnucash/engine/test/utest-Account.cpp
+++ b/libgnucash/engine/test/utest-Account.cpp
@@ -467,13 +467,7 @@ test_gnc_account_list_name_violations (Fixture *fixture, gconstpointer pData)
 {
     auto log_level = static_cast<GLogLevelFlags>(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL);
     auto log_domain = "gnc.engine";
-#ifdef USE_CLANG_FUNC_SIG
-#define _func "GList *gnc_account_list_name_violations(QofBook *, const gchar *)"
-#else
-#define _func "gnc_account_list_name_violations"
-#endif
-    auto msg = _func ": assertion 'separator != NULL' failed";
-#undef _func
+    auto msg = ": assertion 'separator != NULL' failed";
     auto check = test_error_struct_new(log_domain, log_level, msg);
     GList *results, *res_iter;
     auto sep = ":";
@@ -482,7 +476,7 @@ test_gnc_account_list_name_violations (Fixture *fixture, gconstpointer pData)
      * affect the test_log_fatal_handler
      */
     GLogFunc oldlogger = g_log_set_default_handler ((GLogFunc)test_null_handler, check);
-    g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_checked_handler, check);
+    g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_checked_substring_handler, check);
     g_assert (gnc_account_list_name_violations (NULL, NULL) == NULL);
     g_assert_cmpint (check->hits, ==, 1);
     g_assert (gnc_account_list_name_violations (book, NULL) == NULL);
@@ -759,19 +753,13 @@ test_xaccCloneAccount (Fixture *fixture, gconstpointer pData)
     Account *clone;
     QofBook *book = gnc_account_get_book (fixture->acct);
     auto loglevel = static_cast<GLogLevelFlags>(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL);
-#ifdef USE_CLANG_FUNC_SIG
-#define _func "Account *xaccCloneAccount(const Account *, QofBook *)"
-#else
-#define _func "xaccCloneAccount"
-#endif
-    auto msg1 = _func ": assertion 'GNC_IS_ACCOUNT(from)' failed";
-    auto msg2 = _func ": assertion 'QOF_IS_BOOK(book)' failed";
-#undef _func
+    auto msg1 = ": assertion 'GNC_IS_ACCOUNT(from)' failed";
+    auto msg2 = ": assertion 'QOF_IS_BOOK(book)' failed";
     auto check = test_error_struct_new("gnc.engine", loglevel, msg1);
     AccountPrivate *acct_p, *clone_p;
     auto oldlogger = g_log_set_default_handler ((GLogFunc)test_null_handler,
                                                 check);
-    g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_checked_handler, check);
+    g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_checked_substring_handler, check);
     clone = xaccCloneAccount (NULL, book);
     g_assert (clone == NULL);
     g_assert_cmpint (check->hits, ==, 1);
@@ -1095,14 +1083,8 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
     Split *split3 = xaccMallocSplit (book);
     TestSignal sig1, sig2, sig3;
     AccountPrivate *priv = fixture->func->get_private (fixture->acct);
-#ifdef USE_CLANG_FUNC_SIG
-#define _func "gboolean gnc_account_insert_split(Account *, Split *)"
-#else
-#define _func "gnc_account_insert_split"
-#endif
-    auto msg1 = _func ": assertion 'GNC_IS_ACCOUNT(acc)' failed";
-    auto msg2 = _func ": assertion 'GNC_IS_SPLIT(s)' failed";
-#undef _func
+    auto msg1 = ": assertion 'GNC_IS_ACCOUNT(acc)' failed";
+    auto msg2 = ": assertion 'GNC_IS_SPLIT(s)' failed";
     auto loglevel = static_cast<GLogLevelFlags>(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL);
 //    auto log_domain = "gnc.engine";
     auto check1 = test_error_struct_new("gnc.engine", loglevel, msg1);
@@ -1116,7 +1098,7 @@ test_gnc_account_insert_remove_split (Fixture *fixture, gconstpointer pData)
     test_add_error (check2);
     logger = g_log_set_handler ("gnc.engine", loglevel,
                                 (GLogFunc)test_null_handler, check3);
-    g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_list_handler, NULL);
+    g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_list_substring_handler, NULL);
 
     /* Check that the call fails with invalid account and split (throws) */
     g_assert (!gnc_account_insert_split (NULL, split1));
diff --git a/po/POTFILES.in b/po/POTFILES.in
index bced61f..190bd21 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -608,7 +608,7 @@ libgnucash/core-utils/gnc-locale-utils.c
 libgnucash/core-utils/gnc-path.c
 libgnucash/core-utils/gnc-prefs.c
 libgnucash/doc/doxygen_main_page.c
-libgnucash/engine/Account.c
+libgnucash/engine/Account.cpp
 libgnucash/engine/business-core.scm
 libgnucash/engine/cap-gains.c
 libgnucash/engine/cashobjects.c

commit 318f7ebc4fd97d727c8b49bc12758c6c7a9520f8
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Wed Dec 20 14:34:35 2017 +0100

    Force build order on report system support files
    
    Guile 2.2 is more picky about this than guile 2.0 was.

diff --git a/gnucash/report/report-system/CMakeLists.txt b/gnucash/report/report-system/CMakeLists.txt
index 72eeb83..d97f5be 100644
--- a/gnucash/report/report-system/CMakeLists.txt
+++ b/gnucash/report/report-system/CMakeLists.txt
@@ -52,9 +52,12 @@ SET (report_system_SCHEME
     eguile-html-utilities.scm
 )
 
-SET (report_system_SCHEME_2
+SET (report_system_SCHEME_2a
     collectors.scm
     list-extras.scm
+)
+
+SET (report_system_SCHEME_2b
     report-collectors.scm
 )
 
@@ -87,17 +90,24 @@ GNC_ADD_SCHEME_TARGETS(scm-report-system
   FALSE
 )
 
-GNC_ADD_SCHEME_TARGETS(scm-report-system-2
-  "${report_system_SCHEME_2}"
+GNC_ADD_SCHEME_TARGETS(scm-report-system-2a
+  "${report_system_SCHEME_2a}"
   "gnucash/report/report-system"
   scm-report-system
   FALSE
 )
 
+GNC_ADD_SCHEME_TARGETS(scm-report-system-2b
+  "${report_system_SCHEME_2b}"
+  "gnucash/report/report-system"
+  scm-report-system-2a
+  FALSE
+)
+
 GNC_ADD_SCHEME_TARGETS(scm-report-system-3
   "${report_system_SCHEME_3}"
   ""
-  scm-report-system-2
+  scm-report-system-2b
   FALSE
 )
 

commit 723b51a06d7e8028349d1a3f81badc32ea0befa3
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Tue Dec 19 23:28:47 2017 +0100

    Add unit test for rewritten scheme error handlers

diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index faa1e68..c455423 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -28,6 +28,7 @@ GNC_ADD_TEST_WITH_GUILE(test-scm-query-string test-scm-query-string.cpp
 ADD_APP_UTILS_TEST(test-sx test-sx.cpp)
 
 SET(GUILE_DEPENDS
+  scm-test-engine
   scm-app-utils
   gnc-core-utils
   gnc-module
@@ -36,6 +37,11 @@ SET(GUILE_DEPENDS
   gncmod-backend-xml
 )
 
+set(test_app_utils_scheme_SOURCES
+  test-c-interface.scm
+  test-load-app-utils-module.scm
+)
+
 GNC_ADD_SCHEME_TARGETS(scm-test-load-app-utils-module
   "test-load-app-utils-module.scm"
   "gnucash/reports"
@@ -43,7 +49,14 @@ GNC_ADD_SCHEME_TARGETS(scm-test-load-app-utils-module
   FALSE
 )
 
-GNC_ADD_SCHEME_TESTS("test-load-app-utils-module.scm")
+GNC_ADD_SCHEME_TARGETS(scm-test-c-interface
+  "test-c-interface.scm"
+  ""
+  "${GUILE_DEPENDS}"
+  FALSE
+)
+
+GNC_ADD_SCHEME_TESTS(${test_app_utils_scheme_SOURCES})
 # Doesn't work yet:
 GNC_ADD_TEST_WITH_GUILE(test-app-utils "${test_app_utils_SOURCES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS)
 
@@ -56,6 +69,7 @@ SET_DIST_LIST(test_app_utils_DIST
   test-print-queries.cpp
   test-scm-query-string.cpp
   test-sx.cpp
-  test-load-app-utils-module.scm
+  test-c-interface.scm
+  ${test_app_utils_scheme_SOURCES}
   ${test_app_utils_SOURCES}
 )
diff --git a/libgnucash/app-utils/test/Makefile.am b/libgnucash/app-utils/test/Makefile.am
index 5380cc3..3ebb930 100644
--- a/libgnucash/app-utils/test/Makefile.am
+++ b/libgnucash/app-utils/test/Makefile.am
@@ -16,11 +16,14 @@ test_scm_query_string_SOURCES = test-scm-query-string.cpp
 test_sx_SOURCES = test-sx.cpp
 test_print_parse_amount_SOURCES = test-print-parse-amount.cpp
 
-GNC_TEST_DEPS = --gnc-module-dir ${top_builddir}/libgnucash/engine \
+GNC_TEST_DEPS = \
+  --gnc-module-dir ${top_builddir}/libgnucash/engine \
+  --gnc-module-dir ${top_builddir}/libgnucash/engine/test \
   --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
   --guile-load-dir ${top_builddir}/libgnucash/core-utils \
   --guile-load-dir ${top_builddir}/libgnucash/gnc-module \
   --guile-load-dir ${top_builddir}/libgnucash/engine \
+  --guile-load-dir ${top_builddir}/libgnucash/engine/test \
   --guile-load-dir ${top_builddir}/libgnucash/scm \
   --guile-load-dir ${top_builddir}/libgnucash/app-utils \
   --library-dir    ${top_builddir}/libgnucash/core-utils \
@@ -74,7 +77,7 @@ test_app_utils_CXXFLAGS = \
 	-DTESTPROG=test_app_utils \
 	${GLIB_CFLAGS}
 
-SCM_TESTS =   test-load-app-utils-module
+SCM_TESTS =   test-load-app-utils-module test-c-interface
 SCM_TEST_SRCS = $(SCM_TESTS:%=%.scm)
 
 $(SCM_TESTS): %: $(srcdir)/%.scm Makefile
diff --git a/libgnucash/app-utils/test/test-c-interface.scm b/libgnucash/app-utils/test/test-c-interface.scm
new file mode 100644
index 0000000..f12cb1c
--- /dev/null
+++ b/libgnucash/app-utils/test/test-c-interface.scm
@@ -0,0 +1,45 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 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
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setenv "GNC_UNINSTALLED" "1")
+(debug-set! stack 50000)
+(load-from-path "c-interface")
+(use-modules (gnucash engine test test-extras))
+
+(define (test-func a b)
+    (list (/ a b) 6))
+
+(define (run-test)
+    (and (test test-call-with-error-handling)
+         (test test-eval-string-with-error-handling)
+         (test test-apply-with-error-handling)))
+
+(define (test-call-with-error-handling)
+  (and (eq? #f (cadr (gnc:call-with-error-handling test-func (list 4 5))))
+       (eq? #f (cadr (gnc:call-with-error-handling "(test-func 4 5)" '())))
+       (eq? #f (car (gnc:call-with-error-handling test-func (list 4 0))))
+       (eq? #f (car (gnc:call-with-error-handling "(test-func 4 0)" '())))))
+
+(define (test-eval-string-with-error-handling)
+  (and (eq? #f (cadr (gnc:eval-string-with-error-handling "(test-func 4 5)")))
+       (eq? #f (car (gnc:eval-string-with-error-handling "(test-func 4 0)")))))
+
+(define (test-apply-with-error-handling)
+  (and (eq? #f (cadr (gnc:apply-with-error-handling test-func (list 4 5))))
+       (eq? #f (car (gnc:apply-with-error-handling test-func (list 4 0))))))

commit 3d910ad2b1c6bde3948e9fe7e22265408c4a1c02
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Tue Dec 19 23:13:01 2017 +0100

    Drop guile 1.8 support
    
    And with it all quirks we still had in the code to support that version.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c679753..05aff73 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -269,7 +269,7 @@ IF(BUILDING_FROM_VCS)
 ENDIF()
 
 # Find Guile and determine which version we are using.
-# Look for guile versions in this order: 2.2 > 2.0 > 1.8
+# Look for guile versions in this order: 2.2 > 2.0
 
 # guile library and include dir
 GNC_PKG_CHECK_MODULES (GUILE22 guile-2.2 QUIET)
@@ -302,19 +302,7 @@ ELSE(GUILE22_FOUND)
     MESSAGE(STATUS "Using guile-2.0.x")
     FIND_PROGRAM (GUILE_EXECUTABLE NAMES guile2.0 guile)
   ELSE(GUILE2_FOUND)
-
-    # look for guile 1.8
-    GNC_PKG_CHECK_MODULES (GUILE1 guile-1.8>=1.8.8 QUIET)
-    IF (NOT GUILE1_FOUND)
-      MESSAGE (FATAL_ERROR "Neither guile 1.8 nor guile 2.0 were found GnuCash can't run without one of them. Ensure that one is installed and can be found with pgk-config.")
-    ENDIF(NOT GUILE1_FOUND)
-
-    SET(HAVE_GUILE1 TRUE)
-    SET(GUILE_EFFECTIVE_VERSION 1.8)
-    SET(GUILE_INCLUDE_DIRS ${GUILE1_INCLUDE_DIRS})
-    SET(GUILE_LDFLAGS ${GUILE1_LDFLAGS})
-    MESSAGE(STATUS "Using guile-1.8.x")
-    FIND_PROGRAM (GUILE_EXECUTABLE NAMES guile1.8 guile)
+    MESSAGE (FATAL_ERROR "Neither guile 2.2 nor guile 2.0 were found GnuCash can't run without one of them. Ensure that one is installed and can be found with pgk-config.")
   ENDIF(GUILE2_FOUND)
 ENDIF(GUILE22_FOUND)
 
diff --git a/Makefile.am b/Makefile.am
index fc2aa8b..2823a0a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,7 +57,6 @@ dist_doc_DATA = \
 # CVS dirs.
 
 EXTRA_DIST = \
-  $(SWIG_DIST_FAIL) \
   CMakeLists.txt \
   cmake/cmake_uninstall.cmake.in \
   cmake/README_CMAKE.txt \
@@ -174,14 +173,6 @@ distuninstallcheck_listfiles = \
 
 #dist-hook: po/POTFILES.in
 
-# Make "make dist" fail if we have the wrong version of swig
-if SWIG_DIST_FAIL
-SWIG_DIST_FAIL = swig-dist-fail
-swig-dist-fail:
-	@echo "You cannot build the dist with your version of swig"
-	@exit 1
-endif
-
 distcheck-hook:
 	@e=''; \
 	for X in `grep -v \# ${distdir}/po/POTFILES.in | sed 's/\[type:.*\]//'` ; do \
diff --git a/README b/README
index 642365f..3f479df 100644
--- a/README
+++ b/README
@@ -237,9 +237,8 @@ you'll need for the systems we know about:
     current:
       libgnome-dev
       libwebkit-dev
-      guile1.8
+      guile
       libguile9-dev
-      libguile9-slib
 
 
   SuSE:
diff --git a/README.dependencies b/README.dependencies
index 56753b1..e2699ce 100644
--- a/README.dependencies
+++ b/README.dependencies
@@ -64,7 +64,7 @@ Libraries/Deps
   --------		_______
 glib2			2.40.0
 gtk+3			3.14.0
-guile			1.8.5 or 2.0.0
+guile			2.2.0 or 2.0.0
 libxml2			2.5.10
 libxslt
 ICU                                             International Compnents for
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 84ca246..c63e54f 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -10,8 +10,7 @@ SET(common_EXTRA_DIST
         config.h.cmake.in
         gnc-test-env.pl
         guile-mappings.h
-        platform.h
-	swig-utf8.patch)
+        platform.h)
 
 IF (BUILDING_FROM_VCS)
   SET (SWIG_RUNTIME_H ${CMAKE_CURRENT_BINARY_DIR}/swig-runtime.h PARENT_SCOPE)
diff --git a/common/Makefile.am b/common/Makefile.am
index 07c4e9e..29c5b8e 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -39,5 +39,4 @@ EXTRA_DIST = \
   gnc-test-env.pl \
   guile-mappings.h \
   platform.h \
-  swig-utf8.patch \
   CMakeLists.txt
diff --git a/common/cmake_modules/GncAddSchemeTargets.cmake b/common/cmake_modules/GncAddSchemeTargets.cmake
index ec6fc6f..befc367 100644
--- a/common/cmake_modules/GncAddSchemeTargets.cmake
+++ b/common/cmake_modules/GncAddSchemeTargets.cmake
@@ -54,8 +54,8 @@ FUNCTION(GNC_ADD_SCHEME_TARGETS _TARGET _SOURCE_FILES _OUTPUT_DIR _GUILE_DEPENDS
     MAKE_UNIX_PATH(CMAKE_SOURCE_DIR)
   ENDIF(MINGW64)
 
-  # For guile 1, we simple link (or copy, for Windows) each source file to the dest directory
-  IF(HAVE_GUILE1 OR MAKE_LINKS)
+  # If links are requested, we simple link (or copy, for Windows) each source file to the dest directory
+  IF(MAKE_LINKS)
     SET(_LINK_DIR ${DATADIR_BUILD}/gnucash/scm/${_OUTPUT_DIR})
     FILE(MAKE_DIRECTORY ${_LINK_DIR})
     SET(_SCHEME_LINKS "")
@@ -74,30 +74,25 @@ FUNCTION(GNC_ADD_SCHEME_TARGETS _TARGET _SOURCE_FILES _OUTPUT_DIR _GUILE_DEPENDS
         )
       ENDIF()
     ENDFOREACH(scheme_file)
-    IF(HAVE_GUILE1)
-      ADD_CUSTOM_TARGET(${_TARGET} ALL DEPENDS ${_SCHEME_LINKS})
-    ELSE()
-      ADD_CUSTOM_TARGET(${_TARGET}-links ALL DEPENDS ${_SCHEME_LINKS})
-    ENDIF()
-  ENDIF(HAVE_GUILE1 OR MAKE_LINKS)
+    ADD_CUSTOM_TARGET(${_TARGET}-links ALL DEPENDS ${_SCHEME_LINKS})
+  ENDIF(MAKE_LINKS)
 
-  IF(HAVE_GUILE2)
-    # Construct the guile source and compiled load paths
+  # Construct the guile source and compiled load paths
 
-    SET(_GUILE_LOAD_PATH "${current_srcdir}"
-        "${current_bindir}" "${CMAKE_BINARY_DIR}/libgnucash/scm")  # to pick up generated build-config.scm
-    SET(_GUILE_LOAD_COMPILED_PATH "${current_bindir}")
+  SET(_GUILE_LOAD_PATH "${current_srcdir}"
+      "${current_bindir}" "${CMAKE_BINARY_DIR}/libgnucash/scm")  # to pick up generated build-config.scm
+  SET(_GUILE_LOAD_COMPILED_PATH "${current_bindir}")
 
-    SET(_GUILE_CACHE_DIR ${LIBDIR_BUILD}/gnucash/scm/ccache/${GUILE_EFFECTIVE_VERSION})
-    SET(_GUILE_LOAD_PATH "${current_srcdir}")
-    IF (MAKE_LINKS)
+  SET(_GUILE_CACHE_DIR ${LIBDIR_BUILD}/gnucash/scm/ccache/${GUILE_EFFECTIVE_VERSION})
+  SET(_GUILE_LOAD_PATH "${current_srcdir}")
+  IF (MAKE_LINKS)
       LIST(APPEND _GUILE_LOAD_PATH "${build_datadir}/gnucash/scm")
-    ENDIF()
-    SET(_GUILE_LOAD_COMPILED_PATH ${build_libdir}/gnucash/scm/ccache/${GUILE_EFFECTIVE_VERSION})
+  ENDIF()
+  SET(_GUILE_LOAD_COMPILED_PATH ${build_libdir}/gnucash/scm/ccache/${GUILE_EFFECTIVE_VERSION})
 
-    SET(_TARGET_FILES "")
+  SET(_TARGET_FILES "")
 
-    FOREACH(source_file ${_SOURCE_FILES})
+  FOREACH(source_file ${_SOURCE_FILES})
       SET(guile_depends ${_GUILE_DEPENDS})
       GET_FILENAME_COMPONENT(basename ${source_file} NAME_WE)
 
@@ -121,11 +116,11 @@ FUNCTION(GNC_ADD_SCHEME_TARGETS _TARGET _SOURCE_FILES _OUTPUT_DIR _GUILE_DEPENDS
         SET(CMAKE_COMMAND_TMP ${CMAKE_COMMAND} -E env)
       ENDIF()
       IF (MINGW64)
-	set(fpath "")
-	foreach(dir $ENV{PATH})
-	  MAKE_UNIX_PATH(dir)
-	  set(fpath "${fpath}${dir}:")
-	endforeach(dir)
+        set(fpath "")
+        foreach(dir $ENV{PATH})
+            MAKE_UNIX_PATH(dir)
+            set(fpath "${fpath}${dir}:")
+        endforeach(dir)
         SET(LIBRARY_PATH "PATH=\"${build_bindir}:${fpath}\"")
       ELSE (MINGW64)
         SET (LIBRARY_PATH "LD_LIBRARY_PATH=${LIBDIR_BUILD}:${LIBDIR_BUILD}/gnucash:${_GUILE_LD_LIBRARY_PATH}")
@@ -135,7 +130,7 @@ FUNCTION(GNC_ADD_SCHEME_TARGETS _TARGET _SOURCE_FILES _OUTPUT_DIR _GUILE_DEPENDS
       ENDIF (APPLE)
       SET(_GNC_MODULE_PATH "")
       IF(MINGW64)
-	SET(_GNC_MODULE_PATH "${build_bindir}")
+        SET(_GNC_MODULE_PATH "${build_bindir}")
       ELSE(MINGW64)
         SET(_GNC_MODULE_PATH "${LIBDIR_BUILD}" "${LIBDIR_BUILD}/gnucash" "${GNC_MODULE_PATH}")
       ENDIF(MINGW64)
@@ -144,31 +139,30 @@ FUNCTION(GNC_ADD_SCHEME_TARGETS _TARGET _SOURCE_FILES _OUTPUT_DIR _GUILE_DEPENDS
       MAKE_UNIX_PATH_LIST(_GUILE_LD_LIBRARY_PATH)
       MAKE_UNIX_PATH_LIST(_GNC_MODULE_PATH)
       IF (__DEBUG)
-	MESSAGE("  ")
-	MESSAGE("   LIBRARY_PATH: ${LIBRARY_PATH}")
-	MESSAGE("   GUILE_LOAD_PATH: ${_GUILE_LOAD_PATH}")
-	MESSAGE("   GUILE_LOAD_COMPILED_PATH: ${_GUILE_LOAD_COMPILED_PATH}")
-	MESSAGE("   GNC_MODULE_PATH: ${_GNC_MODULE_PATH}")
+        MESSAGE("  ")
+        MESSAGE("   LIBRARY_PATH: ${LIBRARY_PATH}")
+        MESSAGE("   GUILE_LOAD_PATH: ${_GUILE_LOAD_PATH}")
+        MESSAGE("   GUILE_LOAD_COMPILED_PATH: ${_GUILE_LOAD_COMPILED_PATH}")
+        MESSAGE("   GNC_MODULE_PATH: ${_GNC_MODULE_PATH}")
       ENDIF(__DEBUG)
       ADD_CUSTOM_COMMAND(
         OUTPUT ${output_file}
         COMMAND ${CMAKE_COMMAND_TMP}
-	   ${LIBRARY_PATH}
-           GNC_UNINSTALLED=YES
-           GNC_BUILDDIR=${CMAKE_BINARY_DIR}
-           GUILE_LOAD_PATH=${_GUILE_LOAD_PATH}
-           GUILE_LOAD_COMPILED_PATH=${_GUILE_LOAD_COMPILED_PATH}
-           GNC_MODULE_PATH=${_GNC_MODULE_PATH}
-           ${GUILE_EXECUTABLE} -e '\(@@ \(guild\) main\)' -s ${GUILD_EXECUTABLE} compile -o ${output_file} ${source_file_abs_path}
+            ${LIBRARY_PATH}
+            GNC_UNINSTALLED=YES
+            GNC_BUILDDIR=${CMAKE_BINARY_DIR}
+            GUILE_LOAD_PATH=${_GUILE_LOAD_PATH}
+            GUILE_LOAD_COMPILED_PATH=${_GUILE_LOAD_COMPILED_PATH}
+            GNC_MODULE_PATH=${_GNC_MODULE_PATH}
+            ${GUILE_EXECUTABLE} -e '\(@@ \(guild\) main\)' -s ${GUILD_EXECUTABLE} compile -o ${output_file} ${source_file_abs_path}
         DEPENDS ${guile_depends}
         MAIN_DEPENDENCY ${source_file_abs_path}
         )
-    ENDFOREACH(source_file)
-    IF (__DEBUG)
-      MESSAGE("TARGET_FILES are ${_TARGET_FILES}")
-    ENDIF(__DEBUG)
-    ADD_CUSTOM_TARGET(${_TARGET} ALL DEPENDS ${_TARGET_FILES})
-    INSTALL(FILES ${_TARGET_FILES} DESTINATION ${SCHEME_INSTALLED_CACHE_DIR}/${_OUTPUT_DIR})
-  ENDIF(HAVE_GUILE2)
+  ENDFOREACH(source_file)
+  IF (__DEBUG)
+    MESSAGE("TARGET_FILES are ${_TARGET_FILES}")
+  ENDIF(__DEBUG)
+  ADD_CUSTOM_TARGET(${_TARGET} ALL DEPENDS ${_TARGET_FILES})
+  INSTALL(FILES ${_TARGET_FILES} DESTINATION ${SCHEME_INSTALLED_CACHE_DIR}/${_OUTPUT_DIR})
   INSTALL(FILES ${_SOURCE_FILES} DESTINATION ${SCHEME_INSTALLED_SOURCE_DIR}/${_OUTPUT_DIR})
 ENDFUNCTION(GNC_ADD_SCHEME_TARGETS)
diff --git a/common/guile-mappings.h b/common/guile-mappings.h
index 9ec30b9..386b24b 100644
--- a/common/guile-mappings.h
+++ b/common/guile-mappings.h
@@ -19,15 +19,6 @@
 
 #include <libguile.h> /* for SCM_MAJOR_VERSION etc */
 
-/* Give Guile 1.8 a 2.0-like interface */
-#if (SCM_MAJOR_VERSION < 2)
-# define scm_c_string_length scm_i_string_length
-#endif
-#ifndef scm_from_utf8_string
-# define scm_from_utf8_string scm_from_locale_string
-# define scm_to_utf8_string scm_to_locale_string
-#endif
-
 /* Convenience macros */
 
 #define scm_is_equal(obj1,obj2)	scm_is_true(scm_equal_p(obj1,obj2))
diff --git a/common/swig-utf8.patch b/common/swig-utf8.patch
deleted file mode 100644
index 86a222a..0000000
--- a/common/swig-utf8.patch
+++ /dev/null
@@ -1,60 +0,0 @@
---- guile_scm_run.swg
-+++ guile_scm_run.swg
-@@ -41,10 +41,14 @@ typedef struct swig_guile_clientdata {
-   SCM goops_class;
- } swig_guile_clientdata;
- 
-+#if SCM_MAJOR_VERSION <= 2
-+#define scm_to_utf8_string scm_to_locale_string
-+#define scm_from_utf8_string scm_from_locale_string
-+#endif
- #define SWIG_scm2str(s) \
-   SWIG_Guile_scm2newstr(s, NULL)
- #define SWIG_str02scm(str) \
--  str ? scm_from_locale_string(str) : SCM_BOOL_F 
-+  str ? scm_from_utf8_string(str) : SCM_BOOL_F 
- # define SWIG_malloc(size) \
-   scm_malloc(size)
- # define SWIG_free(mem) \
-@@ -84,21 +88,13 @@ SWIGINTERN char *
- SWIG_Guile_scm2newstr(SCM str, size_t *len) {
- #define FUNC_NAME "SWIG_Guile_scm2newstr"
-   char *ret;
--  char *tmp;
--  size_t l;
- 
-   SCM_ASSERT (scm_is_string(str), str, 1, FUNC_NAME);
--  l = scm_c_string_length(str);
- 
--  ret = (char *) SWIG_malloc( (l + 1) * sizeof(char));
-+  ret = scm_to_utf8_string(str);
-   if (!ret) return NULL;
- 
--  tmp = scm_to_locale_string(str);
--  memcpy(ret, tmp, l);
--  free(tmp);
--
--  ret[l] = '\0';
--  if (len) *len = l;
-+  if (len) *len = strlen(ret) - 1;
-   return ret;
- #undef FUNC_NAME
- }
-@@ -473,7 +469,7 @@ SWIG_Guile_GetArgs (SCM *dest, SCM rest,
-   int num_args_passed = 0;
-   for (i = 0; i<reqargs; i++) {
-     if (!SCM_CONSP(rest))
--      scm_wrong_num_args(scm_from_locale_string(procname ? (char *) procname : "unknown procedure"));
-+      scm_wrong_num_args(scm_from_utf8_string(procname ? (char *) procname : "unknown procedure"));
-     *dest++ = SCM_CAR(rest);
-     rest = SCM_CDR(rest);
-     num_args_passed++;
-@@ -486,7 +482,7 @@ SWIG_Guile_GetArgs (SCM *dest, SCM rest,
-   for (; i<optargs; i++)
-     *dest++ = SCM_UNDEFINED;
-   if (!SCM_NULLP(rest))
--      scm_wrong_num_args(scm_from_locale_string(procname ? (char *) procname : "unknown procedure"));
-+      scm_wrong_num_args(scm_from_utf8_string(procname ? (char *) procname : "unknown procedure"));
-   return num_args_passed;
- }
- 
diff --git a/common/test-core/Makefile.am b/common/test-core/Makefile.am
index 30d5a98..b30af7d 100644
--- a/common/test-core/Makefile.am
+++ b/common/test-core/Makefile.am
@@ -28,13 +28,6 @@ swig-unittest-support-guile.c: unittest-support.i $(top_srcdir)/common/base-type
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common \
 	${AM_CPPFLAGS} -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 
 swig-unittest-support-python.c: unittest-support.i $(top_srcdir)/common/base-typemaps.i
 	$(SWIG) -python  -Wall -Werror \
@@ -110,7 +103,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --library-dir    ${top_builddir}/common/test-core \
   --library-dir    ${top_builddir}/libgnucash/engine \
@@ -125,7 +117,6 @@ GUILE_COMPILE_ENV = \
 
 gncscmmodcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash
 gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash
diff --git a/common/test-core/unittest-support.scm b/common/test-core/unittest-support.scm
index 80c9a3c..0525471 100644
--- a/common/test-core/unittest-support.scm
+++ b/common/test-core/unittest-support.scm
@@ -18,13 +18,9 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (define-module (gnucash unittest-support))
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libtest-core-guile" "scm_init_unittest_support_module")))
-  (else
-    (load-extension "libtest-core-guile" "scm_init_unittest_support_module")))
+      (load-extension "libtest-core-guile" "scm_init_unittest_support_module"))
 (use-modules (unittest_support))
 
 (re-export TestErrorStruct-log-level-set)
diff --git a/configure.ac b/configure.ac
index cdc0a5d..b3f1884 100644
--- a/configure.ac
+++ b/configure.ac
@@ -504,40 +504,38 @@ GUILE_EFFECTIVE_VERSION=0
 # - determine GUILE_CFLAGS and GUILE_LIBS
 
 AC_ARG_WITH([guile],
-    AS_HELP_STRING([--with-guile=1.8|2.0|auto],
+    AS_HELP_STRING([--with-guile=2.2|2.0|auto],
                    [which guile version to compile against @<:@default: auto@:>@]),
     [],
     [with_guile=auto]
 )
 
-AS_IF([test "$with_guile" = "2.0"],
+AS_IF([test "$with_guile" = "2.2"],
+      [PKG_CHECK_MODULES(GUILE, [guile-2.2 >= 2.2.0],
+                         [GUILE_EFFECTIVE_VERSION=2.2])],
+      [test "$with_guile" = "2.0"],
       [PKG_CHECK_MODULES(GUILE, [guile-2.0 >= 2.0.0],
-                         [GUILE_EFFECTIVE_VERSION=2.0
-                          AC_PATH_PROG([GUILD], guild)])],
-      [test "$with_guile" = "1.8"],
-      [PKG_CHECK_MODULES(GUILE, [guile-1.8 >= 1.8.5],
-                         [GUILE_EFFECTIVE_VERSION=1.8])],
+                         [GUILE_EFFECTIVE_VERSION=2.0])],
       [test "$with_guile" = "auto"],
-      [PKG_CHECK_MODULES(GUILE, [guile-2.0 >= 2.0.0],
-          [GUILE_EFFECTIVE_VERSION=2.0
-           AC_PATH_PROG([GUILD], guild)],
-          [PKG_CHECK_MODULES(GUILE, [guile-1.8 >= 1.8.5],
-                             [GUILE_EFFECTIVE_VERSION=1.8],
+      [PKG_CHECK_MODULES(GUILE, [guile-2.2 >= 2.2.0],
+          [GUILE_EFFECTIVE_VERSION=2.2],
+          [PKG_CHECK_MODULES(GUILE, [guile-2.0 >= 2.0.0],
+                             [GUILE_EFFECTIVE_VERSION=2.0],
                              [GUILE_EFFECTIVE_VERSION=0])
           ])],
       # else
       [AC_MSG_ERROR([invalid guile version specified])]
 )
 
+AC_PATH_PROG([GUILD], guild)
 AS_IF([test "$GUILE_EFFECTIVE_VERSION" = "0"],
       [AC_MSG_ERROR([
        guile does not appear to be installed correctly, or is not in the
        correct version range.  Perhaps you have not installed the guile
-       development packages?  Gnucash requires at least version 1.8.5 to build.
+       development packages?  Gnucash requires at least version 2.0.0 to build.
        ])]
 )
 
-AM_CONDITIONAL(GNC_HAVE_GUILE_2, [test "$GUILE_EFFECTIVE_VERSION" = "2.0"])
 AC_SUBST(GUILE_EFFECTIVE_VERSION)
 AC_SUBST(GUILE, [`pwd`/gnc-guile])
 
@@ -553,48 +551,13 @@ the libtool(-ltdl) development package?])])
 # version 2.0.10 of SWIG, because that's the first version that supports
 # guile 2.
 #
-# For a guile 1.8 build we accept an older version, but won't allow you
-# to build the dist because we need to apply a patch to the swig
-# generated files that needs to go into the release tarball. At that
-# point we don't know yet which version of guile will be used together
-# with that tarball so we have to prepare the tarball for guile 2
-# compatibility.
-#
 if test "${BUILDING_FROM_VCS}" = yes
 then
-    AX_PKG_SWIG(2.0.10, [gnc_have_swig_2_0_10=yes], [gnc_have_swig_2_0_10=no])
-
-    if test "${GUILE_EFFECTIVE_VERSION}" = "2.0"
-    then
-        if test "${gnc_have_swig_2_0_10}" = no
-        then
-            AC_MSG_ERROR([
-    You are building from ${VCS_TYPE} but swig was not found or too old.
-    To build gnucash you need at least swig version 2.0.10.
-            ])
-        fi
-    else  # using guile 1.8
-        if test "${gnc_have_swig_2_0_10}" = no
-        then
-            AX_PKG_SWIG(1.3.31, [],
+    AX_PKG_SWIG(2.0.10, ,
                 [AC_MSG_ERROR([
     You are building from ${VCS_TYPE} but swig was not found or too old.
-    To build gnucash with guile 1.8 you need at least swig version 1.3.31.
-            ])])
-        fi
-    fi
-
-    # If we get here then we have an okay version to build locally.
-    # Check if we can build dist (which always requires swig 2.0.10)
-    if test "${gnc_have_swig_2_0_10}" = no
-    then
-        AC_MSG_WARN([You don't have SWIG 2.0.10 so you will not be able to "make dist"])
-    fi
-    AM_CONDITIONAL([SWIG_DIST_FAIL], test "${gnc_have_swig_2_0_10}" != yes)
-else
-    # When building from tarball, all the prerequisites to build dist are already
-    # met so make sure it's allowed
-    AM_CONDITIONAL([SWIG_DIST_FAIL], test yes != yes)
+    To build gnucash you need at least swig version 2.0.10.
+                ])])
 fi
 
 ### --------------------------------------------------------------------------
diff --git a/gnucash/gnome-utils/Makefile.am b/gnucash/gnome-utils/Makefile.am
index 0692968..2ed2bc2 100644
--- a/gnucash/gnome-utils/Makefile.am
+++ b/gnucash/gnome-utils/Makefile.am
@@ -222,13 +222,6 @@ swig-gnome-utils.c: gnome-utils.i \
                     ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 gncscmmoddir = ${GNC_SCM_INSTALL_DIR}/gnucash
@@ -267,7 +260,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --guile-load-dir ${top_builddir}/libgnucash/core-utils \
   --guile-load-dir ${top_builddir}/libgnucash/gnc-module \
@@ -290,7 +282,6 @@ gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 
 gncscmcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@
 gncscmcache_DATA = $(gncscm_DATA:.scm=.go)
-endif
 
 noinst_DATA = .scm-links
 clean-local:
diff --git a/gnucash/gnome-utils/gnome-utils.scm b/gnucash/gnome-utils/gnome-utils.scm
index a5a8c73..e165efc 100644
--- a/gnucash/gnome-utils/gnome-utils.scm
+++ b/gnucash/gnome-utils/gnome-utils.scm
@@ -22,12 +22,9 @@
 (use-modules (gnucash main)) ;; FIXME: delete after we finish modularizing.
 (use-modules (gnucash gnc-module))
 
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgncmod-gnome-utils" "scm_init_sw_gnome_utils_module")))
-  (else ))
+      (load-extension "libgncmod-gnome-utils" "scm_init_sw_gnome_utils_module"))
 (use-modules (sw_gnome_utils))
 (gnc:module-load "gnucash/app-utils" 0)
 
diff --git a/gnucash/gnome/Makefile.am b/gnucash/gnome/Makefile.am
index 8fe2b65..b57eb2a 100644
--- a/gnucash/gnome/Makefile.am
+++ b/gnucash/gnome/Makefile.am
@@ -152,13 +152,6 @@ if BUILDING_FROM_VCS
 swig-gnome.c: gnome.i dialog-progress.h ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 EXTRA_DIST = \
diff --git a/gnucash/html/Makefile.am b/gnucash/html/Makefile.am
index db57fd7..3fa8f68 100644
--- a/gnucash/html/Makefile.am
+++ b/gnucash/html/Makefile.am
@@ -57,13 +57,6 @@ swig-gnc-html.c: gnc-html.i gnc-html.h \
                     ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 EXTRA_DIST = \
diff --git a/gnucash/import-export/qif-imp/Makefile.am b/gnucash/import-export/qif-imp/Makefile.am
index 6226701..894dff4 100644
--- a/gnucash/import-export/qif-imp/Makefile.am
+++ b/gnucash/import-export/qif-imp/Makefile.am
@@ -98,7 +98,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
   --guile-load-dir ${top_builddir}/libgnucash/app-utils \
@@ -134,7 +133,6 @@ gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 
 gncscmcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/qif-import
 gncscmcache_DATA = $(gncscm_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash qif-import
diff --git a/gnucash/import-export/qif-imp/qif-import.scm b/gnucash/import-export/qif-imp/qif-import.scm
index ae4cf71..62b31d0 100644
--- a/gnucash/import-export/qif-imp/qif-import.scm
+++ b/gnucash/import-export/qif-imp/qif-import.scm
@@ -30,13 +30,9 @@
 
 ;; We do this initialization here because src/gnome isn't a real module.
 ;; Note: Guile 2 needs to find the symbols from the extension at compile time already
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgnc-gnome" "scm_init_sw_gnome_module")))
-  (else
-    (load-extension "libgnc-gnome" "scm_init_sw_gnome_module")))
+      (load-extension "libgnc-gnome" "scm_init_sw_gnome_module"))
 
 (use-modules (sw_gnome))
 
diff --git a/gnucash/report/business-reports/Makefile.am b/gnucash/report/business-reports/Makefile.am
index 3c08cf2..07bc62b 100644
--- a/gnucash/report/business-reports/Makefile.am
+++ b/gnucash/report/business-reports/Makefile.am
@@ -46,7 +46,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
@@ -81,7 +80,6 @@ GUILE_COMPILE_ENV = \
 
 gncscmmodcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash/report
 gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash
diff --git a/gnucash/report/business-reports/balsheet-eg.scm b/gnucash/report/business-reports/balsheet-eg.scm
index 0050fa3..5df41b6 100644
--- a/gnucash/report/business-reports/balsheet-eg.scm
+++ b/gnucash/report/business-reports/balsheet-eg.scm
@@ -39,10 +39,7 @@
 (use-modules (gnucash report eguile-utilities))
 
 (use-modules (ice-9 regex))  ; for regular expressions
-(cond-expand
-  (guile-2
-      (use-modules (ice-9 local-eval)))  ; for the-environment
-  (else ))
+(use-modules (ice-9 local-eval))  ; for the-environment
 (use-modules (srfi srfi-13)) ; for extra string functions
 
 (gnc:module-load "gnucash/report/report-system" 0)
diff --git a/gnucash/report/business-reports/receipt.scm b/gnucash/report/business-reports/receipt.scm
index 4e69995..890510b 100644
--- a/gnucash/report/business-reports/receipt.scm
+++ b/gnucash/report/business-reports/receipt.scm
@@ -16,10 +16,7 @@
 
 (define-module (gnucash report receipt))
 
-(cond-expand
-  (guile-2
-      (use-modules (ice-9 local-eval)))  ; for the-environment
-  (else ))
+(use-modules (ice-9 local-eval))  ; for the-environment
 (use-modules (gnucash main))
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
diff --git a/gnucash/report/business-reports/taxinvoice.scm b/gnucash/report/business-reports/taxinvoice.scm
index dc864db..2417c6b 100644
--- a/gnucash/report/business-reports/taxinvoice.scm
+++ b/gnucash/report/business-reports/taxinvoice.scm
@@ -24,10 +24,7 @@
 ; (see http://wiki.gnucash.org/wiki/Custom_Reports )
 (define-module (gnucash report taxinvoice))
 
-(cond-expand
-  (guile-2
-      (use-modules (ice-9 local-eval)))  ; for the-environment
-  (else ))
+(use-modules (ice-9 local-eval))  ; for the-environment
 (use-modules (gnucash main))
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
diff --git a/gnucash/report/locale-specific/us/Makefile.am b/gnucash/report/locale-specific/us/Makefile.am
index 474e662..bcf84f6 100644
--- a/gnucash/report/locale-specific/us/Makefile.am
+++ b/gnucash/report/locale-specific/us/Makefile.am
@@ -51,7 +51,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
@@ -91,7 +90,6 @@ gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 
 gncscmrptcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash/report/locale-specific
 gncscmrptcache_DATA = $(gncscmrpt_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash
diff --git a/gnucash/report/locale-specific/us/taxtxf.scm b/gnucash/report/locale-specific/us/taxtxf.scm
index e339f2c..fe17aa9 100644
--- a/gnucash/report/locale-specific/us/taxtxf.scm
+++ b/gnucash/report/locale-specific/us/taxtxf.scm
@@ -97,12 +97,9 @@
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
 
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgncmod-gnome-utils" "scm_init_sw_gnome_utils_module")))
-  (else ))
+      (load-extension "libgncmod-gnome-utils" "scm_init_sw_gnome_utils_module"))
 (use-modules (sw_gnome_utils)) ;; to get to gnc-error-dialog
 
 (use-modules (gnucash printf))
diff --git a/gnucash/report/report-gnome/Makefile.am b/gnucash/report/report-gnome/Makefile.am
index 38c11d7..88b945f 100644
--- a/gnucash/report/report-gnome/Makefile.am
+++ b/gnucash/report/report-gnome/Makefile.am
@@ -51,13 +51,6 @@ if BUILDING_FROM_VCS
 swig-report-gnome.c: report-gnome.i ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 gncscmmoddir = ${GNC_SCM_INSTALL_DIR}/gnucash/report
@@ -103,7 +96,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
@@ -136,7 +128,6 @@ GUILE_COMPILE_ENV = \
 
 gncscmmodcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash/report
 gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
-endif
 
 clean-local:
 	rm -rf gnucash
diff --git a/gnucash/report/report-gnome/report-gnome.scm b/gnucash/report/report-gnome/report-gnome.scm
index 5f07220..48ecf45 100644
--- a/gnucash/report/report-gnome/report-gnome.scm
+++ b/gnucash/report/report-gnome/report-gnome.scm
@@ -33,13 +33,10 @@
 
 (use-modules (gnucash printf))
 
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
       (load-extension "libgncmod-gnome-utils" "scm_init_sw_gnome_utils_module")
-      (load-extension "libgncmod-report-gnome" "scm_init_sw_report_gnome_module")))
-  (else ))
+      (load-extension "libgncmod-report-gnome" "scm_init_sw_report_gnome_module"))
 (use-modules (sw_report_gnome))
 
 (gnc:module-load "gnucash/gnome-utils" 0)
diff --git a/gnucash/report/report-gnome/test/test-load-report-gnome-module.scm b/gnucash/report/report-gnome/test/test-load-report-gnome-module.scm
index 5273082..85f07cd 100755
--- a/gnucash/report/report-gnome/test/test-load-report-gnome-module.scm
+++ b/gnucash/report/report-gnome/test/test-load-report-gnome-module.scm
@@ -9,13 +9,7 @@ exec ${GUILE} -s "$0"
     (debug-set! maxdepth 100000))
 
 (display "  testing report module load ... ")
-(cond-expand
-  (guile-2 )
-  (else
-    ;; Syncase is deprecated and redundant in guile 2
-    (use-modules (ice-9 syncase))))
 (use-modules (gnucash gnc-module))
-
 (gnc:module-system-init)
 
 (setenv "GNC_UNINSTALLED" "1")
diff --git a/gnucash/report/report-system/Makefile.am b/gnucash/report/report-system/Makefile.am
index 0cdeeef..d92f372 100644
--- a/gnucash/report/report-system/Makefile.am
+++ b/gnucash/report/report-system/Makefile.am
@@ -27,13 +27,6 @@ if BUILDING_FROM_VCS
 swig-report-system.c: report-system.i ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 AM_CPPFLAGS = \
@@ -105,7 +98,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
@@ -143,7 +135,6 @@ gncmodscmcache_DATA = $(gncmodscm_DATA:.scm=.go)
 
 gncscmcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@
 gncscmcache_DATA = $(gncscm_DATA:.scm=.go)
-endif
 
 noinst_DATA = .scm-links
 
diff --git a/gnucash/report/report-system/eguile-gnc.scm b/gnucash/report/report-system/eguile-gnc.scm
index 38ea3c0..1f8a614 100644
--- a/gnucash/report/report-system/eguile-gnc.scm
+++ b/gnucash/report/report-system/eguile-gnc.scm
@@ -85,10 +85,7 @@
 
 (use-modules (ice-9 regex))       ; for regular expressions
 (use-modules (ice-9 rdelim))      ; for read-line
-(cond-expand
-  (guile-2
-      (use-modules (ice-9 local-eval)))  ; for the-environment
-  (else ))
+(use-modules (ice-9 local-eval))  ; for the-environment
 (use-modules (gnucash printf))
 (use-modules (gnucash app-utils)) ; for _
 
diff --git a/gnucash/report/report-system/eguile-utilities.scm b/gnucash/report/report-system/eguile-utilities.scm
index 5eb12e1..becf77b 100644
--- a/gnucash/report/report-system/eguile-utilities.scm
+++ b/gnucash/report/report-system/eguile-utilities.scm
@@ -32,15 +32,8 @@
 (use-modules (gnucash core-utils))
 (gnc:module-load "gnucash/report/report-system" 0)
 (gnc:module-load "gnucash/app-utils" 0)
-; Syncase is deprecated and redundant in guile 2
-(cond-expand
-  (guile-2 )
-  (else
-    (use-modules (ice-9 syncase)))) ; for define-syntax
 
 
-;(use-modules (srfi srfi-13)) ; for extra string functions
-
 (define-public (fmtnumber n)
   ;; Format a number (integer or real) into something printable
   (number->string (if (integer? n) 
diff --git a/gnucash/report/report-system/report.scm b/gnucash/report/report-system/report.scm
index 6ec25d5..81dc90f 100644
--- a/gnucash/report/report-system/report.scm
+++ b/gnucash/report/report-system/report.scm
@@ -24,12 +24,9 @@
 (use-modules (gnucash app-utils))
 (use-modules (gnucash printf))
 (use-modules (gnucash gettext))
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgncmod-report-system" "scm_init_sw_report_system_module")))
-  (else ))
+      (load-extension "libgncmod-report-system" "scm_init_sw_report_system_module"))
 (use-modules (sw_report_system))
 
 ;; Terminology in this file:
diff --git a/gnucash/report/report-system/test/test-load-report-system-module.scm b/gnucash/report/report-system/test/test-load-report-system-module.scm
index 0695190..b2158de 100755
--- a/gnucash/report/report-system/test/test-load-report-system-module.scm
+++ b/gnucash/report/report-system/test/test-load-report-system-module.scm
@@ -6,11 +6,6 @@
 
 (display "  testing report module load ... ")
 (setenv "GNC_UNINSTALLED" "1")
-(cond-expand
-  (guile-2 )
-  (else
-    ;; Syncase is deprecated and redundant in guile 2
-    (use-modules (ice-9 syncase))))
 (use-modules (gnucash gnc-module))
 
 (gnc:module-system-init)
diff --git a/gnucash/report/standard-reports/Makefile.am b/gnucash/report/standard-reports/Makefile.am
index 7040d88..da43206 100644
--- a/gnucash/report/standard-reports/Makefile.am
+++ b/gnucash/report/standard-reports/Makefile.am
@@ -58,7 +58,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
@@ -94,7 +93,6 @@ gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 
 gncscmrptcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash/report/standard-reports
 gncscmrptcache_DATA = $(gncscmrpt_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash
diff --git a/gnucash/report/standard-reports/test/test-standard-net-linechart.scm b/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
index 549ef11..d74387b 100644
--- a/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
+++ b/gnucash/report/standard-reports/test/test-standard-net-linechart.scm
@@ -26,12 +26,8 @@
 ;; otherwise the N_ syntax-rule won't be found at compile time
 ;; causing the test to fail
 ;; That's what the wrapper below is meant for:
-(cond-expand
-   (guile-2
-    (define-syntax-rule (begin-for-syntax form ...)
-      (eval-when (load compile eval expand) (begin form ...))))
-   (else
-    (define begin-for-syntax begin)))
+(define-syntax-rule (begin-for-syntax form ...)
+      (eval-when (load compile eval expand) (begin form ...)))
 
 (begin-for-syntax (gnc:module-load "gnucash/report/report-system" 0))
 (use-modules (gnucash engine))
diff --git a/gnucash/report/stylesheets/Makefile.am b/gnucash/report/stylesheets/Makefile.am
index 1de87a7..a33bc4f 100644
--- a/gnucash/report/stylesheets/Makefile.am
+++ b/gnucash/report/stylesheets/Makefile.am
@@ -62,7 +62,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
@@ -97,7 +96,6 @@ GUILE_COMPILE_ENV = \
 
 gncscmmodcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash/report
 gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash
diff --git a/gnucash/report/utility-reports/Makefile.am b/gnucash/report/utility-reports/Makefile.am
index 289ef72..f5df74d 100644
--- a/gnucash/report/utility-reports/Makefile.am
+++ b/gnucash/report/utility-reports/Makefile.am
@@ -28,7 +28,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/app-utils \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
@@ -61,7 +60,6 @@ GUILE_COMPILE_ENV = \
 
 gncscmmodcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash/report
 gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash
diff --git a/gnucash/report/utility-reports/view-column.scm b/gnucash/report/utility-reports/view-column.scm
index ba4be79..dd521a1 100644
--- a/gnucash/report/utility-reports/view-column.scm
+++ b/gnucash/report/utility-reports/view-column.scm
@@ -31,12 +31,9 @@
 (use-modules (gnucash app-utils))
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgncmod-report-system" "scm_init_sw_report_system_module")))
-  (else ))
+      (load-extension "libgncmod-report-system" "scm_init_sw_report_system_module"))
 (use-modules (sw_report_system))
 
 (use-modules (gnucash printf))
diff --git a/gnucash/report/utility-reports/welcome-to-gnucash.scm b/gnucash/report/utility-reports/welcome-to-gnucash.scm
index 9537a4e..7f689bb 100644
--- a/gnucash/report/utility-reports/welcome-to-gnucash.scm
+++ b/gnucash/report/utility-reports/welcome-to-gnucash.scm
@@ -27,12 +27,9 @@
 (use-modules (gnucash core-utils)) ; for gnc:version
 (use-modules (gnucash gettext))
 (use-modules (gnucash gnc-module))
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgncmod-report-system" "scm_init_sw_report_system_module")))
-  (else ))
+      (load-extension "libgncmod-report-system" "scm_init_sw_report_system_module"))
 (use-modules (sw_report_system))
 
 (gnc:module-load "gnucash/report/report-system" 0)
diff --git a/libgnucash/app-utils/Makefile.am b/libgnucash/app-utils/Makefile.am
index 84e7f8b..87cef70 100644
--- a/libgnucash/app-utils/Makefile.am
+++ b/libgnucash/app-utils/Makefile.am
@@ -105,13 +105,7 @@ if BUILDING_FROM_VCS
 swig-app-utils-guile.c: app-utils.i ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
+
 swig-app-utils-python.c: app-utils.i ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -python -Wall -Werror \
 	-I${GLIB_CFLAGS} -I${top_srcdir}/common -o $@ $<
@@ -199,7 +193,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
   --guile-load-dir ${top_builddir}/libgnucash/app-utils \
@@ -223,7 +216,6 @@ gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 
 gncscmcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@
 gncscmcache_DATA = $(gncscm_DATA:.scm=.go)
-endif
 
 clean-local:
 	rm -rf gnucash
diff --git a/libgnucash/app-utils/app-utils.scm b/libgnucash/app-utils/app-utils.scm
index fa3f5f0..cc21aa2 100644
--- a/libgnucash/app-utils/app-utils.scm
+++ b/libgnucash/app-utils/app-utils.scm
@@ -16,12 +16,9 @@
 ;; Boston, MA  02110-1301,  USA       gnu at gnu.org
 
 (define-module (gnucash app-utils))
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgncmod-app-utils" "scm_init_sw_app_utils_module")))
-  (else ))
+      (load-extension "libgncmod-app-utils" "scm_init_sw_app_utils_module"))
 (use-modules (sw_app_utils))
 (use-modules (srfi srfi-1))
 (use-modules (gnucash main)) ;; FIXME: delete after we finish modularizing.
@@ -29,13 +26,9 @@
 (use-modules (gnucash gettext))
 
 ;; Guile 2 needs to find the symbols from the c module at compile time already
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (gnc:module-load "gnucash/engine" 0)))
-  (else
-    (gnc:module-load "gnucash/engine" 0)))
+      (gnc:module-load "gnucash/engine" 0))
 
 ;; gettext.scm
 (re-export gnc:gettext)
diff --git a/libgnucash/app-utils/gettext.scm b/libgnucash/app-utils/gettext.scm
index eeacb81..bfef856 100644
--- a/libgnucash/app-utils/gettext.scm
+++ b/libgnucash/app-utils/gettext.scm
@@ -18,16 +18,11 @@
 (define-module (gnucash gettext))
 
 ;; Load a few different modules depending on the version of guile
-(cond-expand
-  (guile-2
-    ;; Our app-utils gnc module must be evaluated at compile time
-    ;; Without it sw_app_utils can't be evaluated below
-    (eval-when
+;; Our app-utils gnc module must be evaluated at compile time
+;; Without it sw_app_utils can't be evaluated below
+(eval-when
       (compile load eval expand)
-      (load-extension "libgncmod-app-utils" "scm_init_sw_app_utils_module")))
-  (else
-    ;; Syncase is deprecated and redundant in guile 2
-    (use-modules (ice-9 syncase))))
+      (load-extension "libgncmod-app-utils" "scm_init_sw_app_utils_module"))
 (use-modules (sw_app_utils))
 
 ;; gettext functions
@@ -43,4 +38,4 @@
 
 (export gnc:gettext)
 (export _)
-(export N_)
\ No newline at end of file
+(export N_)
diff --git a/libgnucash/app-utils/make-prefs-migration-script.xsl b/libgnucash/app-utils/make-prefs-migration-script.xsl
index f9a3350..8fccf5c 100644
--- a/libgnucash/app-utils/make-prefs-migration-script.xsl
+++ b/libgnucash/app-utils/make-prefs-migration-script.xsl
@@ -25,13 +25,9 @@
 (use-modules (gnucash core-utils))
 ;(use-modules (gnucash gnc-module))
 ;; Guile 2 needs to find the symbols from the c module at compile time already
-;(cond-expand
-;  (guile-2
-;    (eval-when
-;      (compile load eval) 
-;      (load-extension "libgnc-core-utils" "scm_init_sw_core_utils_module")))
-;  (else
-;    (load-extension "libgnc-core-utils" "scm_init_sw_core_utils_module")))
+;(eval-when
+;      (compile load eval)
+;      (load-extension "libgnc-core-utils" "scm_init_sw_core_utils_module"))
 ;(use-modules (sw_core_utils))
 
 (define (run-migration-internal)
@@ -217,4 +213,4 @@
   <df:dateformat><df:name>locale</df:name><df:index>4</df:index></df:dateformat>
 </df:dateformats>
 
-</xsl:stylesheet>
\ No newline at end of file
+</xsl:stylesheet>
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index a0ced62..b0e7b0e 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -199,13 +199,6 @@
 (define (gnc:value->string value)
   (let ((result (call-with-output-string
                  (lambda (port) (write value port)))))
-       ;; Guile 1.8 has a bug that it serializes a space in a symbol to "\ "
-       ;; but can't deserialize this back to a symbol afterwards. Stripping the
-       ;; "\" appears to work around this (see gnucash bug  721654 which lead to this issue)
-       (cond-expand
-         (guile-2 )
-         (else (set! result (regexp-substitute/global #f "\\\\ " result 'pre " " 'post))))
-
        result))
 
 (define (gnc:make-string-option
diff --git a/libgnucash/app-utils/test/test-load-app-utils-module.scm b/libgnucash/app-utils/test/test-load-app-utils-module.scm
index a3bd729..b9fac73 100755
--- a/libgnucash/app-utils/test/test-load-app-utils-module.scm
+++ b/libgnucash/app-utils/test/test-load-app-utils-module.scm
@@ -7,12 +7,8 @@
 ;; otherwise the N_ syntax-rule won't be found at compile time
 ;; causing the test to fail
 ;; That's what the wrapper below is meant for:
-(cond-expand
-   (guile-2
-    (define-syntax-rule (begin-for-syntax form ...)
-      (eval-when (load compile eval) (begin form ...))))
-   (else
-    (define begin-for-syntax begin)))
+(define-syntax-rule (begin-for-syntax form ...)
+      (eval-when (load compile eval) (begin form ...)))
 
 (begin-for-syntax (define loaded-module (gnc:module-load "gnucash/app-utils" 0)))
 (if loaded-module
diff --git a/libgnucash/app-utils/test/test-scm-query-string.cpp b/libgnucash/app-utils/test/test-scm-query-string.cpp
index 8031a9a..b9f2a82 100644
--- a/libgnucash/app-utils/test/test-scm-query-string.cpp
+++ b/libgnucash/app-utils/test/test-scm-query-string.cpp
@@ -132,11 +132,6 @@ int
 main (int argc, char **argv)
 {
     g_setenv ("GNC_UNINSTALLED", "1", TRUE);
-/* When built with clang, guile-1.8.8's scm_c_eval_string truncates all
- * integer values to int32, which causes this test to fail.
- */
-#if !(defined(__clang__)) || defined(HAVE_GUILE20) || defined(HAVE_GUILE22)
     scm_boot_guile (argc, argv, main_helper, NULL);
-#endif
     return 0;
 }
diff --git a/libgnucash/core-utils/Makefile.am b/libgnucash/core-utils/Makefile.am
index da63a34..51f07fa 100644
--- a/libgnucash/core-utils/Makefile.am
+++ b/libgnucash/core-utils/Makefile.am
@@ -42,13 +42,7 @@ if BUILDING_FROM_VCS
 swig-core-utils-guile.c: core-utils.i ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
+
 swig-core-utils-python.c: core-utils.i ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -python -Wall -Werror \
 	-I${GLIB_CFLAGS} -I${top_srcdir}/common -I${srcdir} -o $@ $<
@@ -107,7 +101,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --library-dir    ${top_builddir}/libgnucash/core-utils
 
@@ -119,7 +112,6 @@ GUILE_COMPILE_ENV = \
 
 gncscmmodcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash
 gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
-endif
 
 noinst_DATA = .scm-links
 BUILT_SOURCES = gncla-dir.h gnc-version.h
diff --git a/libgnucash/core-utils/core-utils.scm b/libgnucash/core-utils/core-utils.scm
index 645228b..d02dce1 100644
--- a/libgnucash/core-utils/core-utils.scm
+++ b/libgnucash/core-utils/core-utils.scm
@@ -27,13 +27,9 @@
 (define-module (gnucash core-utils))
 
 ;; Guile 2 needs to find the symbols from the extension at compile time already
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgnc-core-utils" "scm_init_sw_core_utils_module")))
-  (else
-    (load-extension "libgnc-core-utils" "scm_init_sw_core_utils_module")))
+      (load-extension "libgnc-core-utils" "scm_init_sw_core_utils_module"))
 
 (use-modules (sw_core_utils))
 
diff --git a/libgnucash/core-utils/gnc-guile-utils.c b/libgnucash/core-utils/gnc-guile-utils.c
index 8b99fb4..e86fe9f 100644
--- a/libgnucash/core-utils/gnc-guile-utils.c
+++ b/libgnucash/core-utils/gnc-guile-utils.c
@@ -297,33 +297,12 @@ gchar *gnc_scm_strip_comments (SCM scm_text)
     splits = g_strsplit(raw_text, "\n", -1);
     for (i = j = 0; splits[i]; i++)
     {
-        gchar *haystack, *needle;
         if ((splits[i][0] == ';') || (splits[i][0] == '\0'))
         {
             g_free(splits[i]);
             continue;
         }
-
-        /* Work around a bug in guile 1.8 that escapes spaces
-         * in a symbol printed on a string port. We don't
-         * want this, because this string can't be properly
-         * converted back into a symbol later on. */
-
-        haystack = splits [i];
-        needle = g_strstr_len (haystack, -1, "\\ ");
-        while (needle)
-        {
-            gchar *new_haystack = NULL;
-            gsize prefix_size = needle - haystack;
-            gchar *prefix = g_strndup (haystack, prefix_size);
-            needle++;
-            new_haystack = g_strconcat (prefix, needle, NULL);
-            g_free (prefix);
-            g_free (haystack);
-            haystack = new_haystack;
-            needle = g_strstr_len (haystack, -1, "\\ ");
-        }
-        splits[j++] = haystack;
+        splits[j++] = splits [i];
     }
     splits[j] = NULL;
 
diff --git a/libgnucash/engine/Makefile.am b/libgnucash/engine/Makefile.am
index 5b8ae45..49c70cc 100644
--- a/libgnucash/engine/Makefile.am
+++ b/libgnucash/engine/Makefile.am
@@ -275,7 +275,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --guile-load-dir ${top_builddir}/libgnucash/gnc-module \
   --library-dir    ${top_builddir}/libgnucash/engine \
@@ -293,7 +292,6 @@ gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 
 gncscmcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@
 gncscmcache_DATA = $(gncscm_DATA:.scm=.go)
-endif
 
 noinst_DATA = .scm-links
 
@@ -303,13 +301,6 @@ swig-engine.c: engine.i $(top_srcdir)/common/base-typemaps.i \
                $(gncinclude_HEADERS) $(noinst_HEADERS)
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 
diff --git a/libgnucash/engine/engine.scm b/libgnucash/engine/engine.scm
index 00bd61c..fdb0b40 100644
--- a/libgnucash/engine/engine.scm
+++ b/libgnucash/engine/engine.scm
@@ -19,12 +19,9 @@
 
 (define-module (gnucash engine))
 
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgncmod-engine" "scm_init_sw_engine_module")))
-  (else ))
+      (load-extension "libgncmod-engine" "scm_init_sw_engine_module"))
 (use-modules (sw_engine))
 
 (export GNC-RND-FLOOR)
diff --git a/libgnucash/gnc-module/Makefile.am b/libgnucash/gnc-module/Makefile.am
index 9246715..1d99001 100644
--- a/libgnucash/gnc-module/Makefile.am
+++ b/libgnucash/gnc-module/Makefile.am
@@ -33,13 +33,6 @@ if BUILDING_FROM_VCS
 swig-gnc-module.c: gnc-module.i ${top_srcdir}/common/base-typemaps.i
 	$(SWIG) -guile -Linkage module \
 	-I${top_srcdir}/common -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 EXAMPLE_EXTRA_DIST = \
@@ -79,7 +72,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --library-dir    ${top_builddir}/libgnucash/gnc-module \
   --library-dir    ${top_builddir}/libgnucash/core-utils
@@ -92,7 +84,6 @@ GUILE_COMPILE_ENV = \
 
 gncscmmodcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash
 gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
-endif
 
 clean-local:
 	rm -rf gnucash
diff --git a/libgnucash/gnc-module/gnc-module.scm b/libgnucash/gnc-module/gnc-module.scm
index 544f37c..236f02f 100644
--- a/libgnucash/gnc-module/gnc-module.scm
+++ b/libgnucash/gnc-module/gnc-module.scm
@@ -28,13 +28,9 @@
 (define-module (gnucash gnc-module))
 
 ;; Guile 2 needs to find the symbols from the extension at compile time already
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
-      (load-extension "libgnc-module" "scm_init_sw_gnc_module_module")))
-  (else
-    (load-extension "libgnc-module" "scm_init_sw_gnc_module_module")))
+      (load-extension "libgnc-module" "scm_init_sw_gnc_module_module"))
 
 (use-modules (sw_gnc_module))
 
@@ -52,9 +48,5 @@
 (export gnc:module-begin-syntax)
 
 ;; Guile 2 needs to load external modules at compile time
-(cond-expand
-   (guile-2
-    (define-syntax-rule (gnc:module-begin-syntax form ...)
-      (eval-when (load compile eval expand) (begin form ...))))
-   (else
-    (define gnc:module-begin-syntax begin)))
+(define-syntax-rule (gnc:module-begin-syntax form ...)
+      (eval-when (load compile eval expand) (begin form ...)))
diff --git a/libgnucash/gnc-module/test/mod-bar/Makefile.am b/libgnucash/gnc-module/test/mod-bar/Makefile.am
index 76b158c..008d543 100644
--- a/libgnucash/gnc-module/test/mod-bar/Makefile.am
+++ b/libgnucash/gnc-module/test/mod-bar/Makefile.am
@@ -25,13 +25,6 @@ libgncmodbar_la_LIBADD=libbar.la \
 if BUILDING_FROM_VCS
 swig-bar.c: bar.i
 	$(SWIG) -guile -Linkage module -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 EXTRA_DIST = \
diff --git a/libgnucash/gnc-module/test/mod-baz/Makefile.am b/libgnucash/gnc-module/test/mod-baz/Makefile.am
index b5d1064..5873a5e 100644
--- a/libgnucash/gnc-module/test/mod-baz/Makefile.am
+++ b/libgnucash/gnc-module/test/mod-baz/Makefile.am
@@ -29,13 +29,6 @@ libgncmodbaz_la_LIBADD = \
 if BUILDING_FROM_VCS
 swig-baz.c: baz.i
 	$(SWIG) -guile -Linkage module -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 EXTRA_DIST = \
diff --git a/libgnucash/gnc-module/test/mod-foo/Makefile.am b/libgnucash/gnc-module/test/mod-foo/Makefile.am
index a1536f4..2e63b54 100644
--- a/libgnucash/gnc-module/test/mod-foo/Makefile.am
+++ b/libgnucash/gnc-module/test/mod-foo/Makefile.am
@@ -31,13 +31,6 @@ EXTRA_DIST = \
 if BUILDING_FROM_VCS
 swig-foo.c: foo.i
 	$(SWIG) -guile -Linkage module -o $@ $<
-if ! OS_WIN32
-if ! SWIG_DIST_FAIL
-	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
-	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
-	fi
-endif
-endif
 endif
 
 BUILT_SOURCES = swig-foo.c
diff --git a/libgnucash/scm/CMakeLists.txt b/libgnucash/scm/CMakeLists.txt
index 22165f8..f11cc81 100644
--- a/libgnucash/scm/CMakeLists.txt
+++ b/libgnucash/scm/CMakeLists.txt
@@ -14,11 +14,7 @@ SET (scm_SCHEME_4
 
 # FIXME: ${BUILD_CONFIG_SCM} needs a rule to cause it to be generated, I think.
 
-IF (HAVE_GUILE1)
-  GNC_CONFIGURE(build-config.scm.in build-config.scm)
-ELSE()
-  GNC_CONFIGURE2(build-config-scm build-config.scm.in build-config.scm)
-ENDIF()
+GNC_CONFIGURE2(build-config-scm build-config.scm.in build-config.scm)
 
 # CONFIGURE_FILE(build-config.scm.in ${BUILD_CONFIG_SCM})
 
diff --git a/libgnucash/scm/Makefile.am b/libgnucash/scm/Makefile.am
index d6647c7..2d69f08 100644
--- a/libgnucash/scm/Makefile.am
+++ b/libgnucash/scm/Makefile.am
@@ -41,7 +41,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --guile-load-dir ${top_builddir}/libgnucash/core-utils \
   --guile-load-dir ${top_builddir}/libgnucash/gnc-module \
@@ -59,7 +58,6 @@ gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 
 gncscmcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@
 gncscmcache_DATA = $(gncscm_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash
diff --git a/libgnucash/scm/gnumeric/Makefile.am b/libgnucash/scm/gnumeric/Makefile.am
index 2e12cf6..476557f 100644
--- a/libgnucash/scm/gnumeric/Makefile.am
+++ b/libgnucash/scm/gnumeric/Makefile.am
@@ -19,14 +19,12 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 %.go : %.scm .scm-links
 	$(GUILD) compile \
 	-o $@ $<
 
 gncscmcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnumeric
 gncscmcache_DATA = $(gncscm_DATA:.scm=.go)
-endif
 
 noinst_DATA = .scm-links
 
diff --git a/libgnucash/scm/main.scm b/libgnucash/scm/main.scm
index 107a109..178f33d 100644
--- a/libgnucash/scm/main.scm
+++ b/libgnucash/scm/main.scm
@@ -18,11 +18,6 @@
 (define-module (gnucash main)
   #:use-module (gnucash printf))
 
-;; This is to silence warnings with guile-1.8:
-(if (and (>= (string->number (major-version)) 1) 
-         (>= (string->number (minor-version)) 8))
-    (default-duplicate-binding-handler 'last))
-
 ;; Turn off the scheme compiler's "possibly unbound variable" warnings.
 ;; In guile 2.0 we get nearly 7500 of them loading the scheme files.
 ;; This is the default value for auto-compilation-options without "unbound-variable".
diff --git a/libgnucash/tax/us/Makefile.am b/libgnucash/tax/us/Makefile.am
index ac7d2d5..136f760 100644
--- a/libgnucash/tax/us/Makefile.am
+++ b/libgnucash/tax/us/Makefile.am
@@ -44,7 +44,6 @@ if ! OS_WIN32
 	touch .scm-links
 endif
 
-if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
   --gnc-module-dir ${top_builddir}/libgnucash/engine \
   --guile-load-dir ${top_builddir}/libgnucash/app-utils \
@@ -68,7 +67,6 @@ gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 
 gncscmcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@
 gncscmcache_DATA = $(gncscm_DATA:.scm=.go)
-endif
 
 clean-local:
 	$(RM) -rf gnucash
diff --git a/libgnucash/tax/us/de_DE.scm b/libgnucash/tax/us/de_DE.scm
index 0ae5699..54bb9f7 100644
--- a/libgnucash/tax/us/de_DE.scm
+++ b/libgnucash/tax/us/de_DE.scm
@@ -20,13 +20,10 @@
 (define-module (gnucash tax de_DE))
 
 (use-modules (gnucash gnc-module))
-(cond-expand
-  (guile-2
-    (eval-when
+(eval-when
       (compile load eval expand)
       (load-extension "libgncmod-engine" "scm_init_sw_engine_module")
-      (load-extension "libgncmod-app-utils" "scm_init_sw_app_utils_module")))
-  (else ))
+      (load-extension "libgncmod-app-utils" "scm_init_sw_app_utils_module"))
 (use-modules (sw_app_utils))
 (use-modules (sw_engine))
 (use-modules (gnucash app-utils))

commit a784dd57848e0d52ad17108f624722adfeeb2867
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Tue Dec 19 16:53:40 2017 +0100

    Add support for guile 2.2

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 964bb9b..c679753 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -269,37 +269,54 @@ IF(BUILDING_FROM_VCS)
 ENDIF()
 
 # Find Guile and determine which version we are using.
-# First look for guile-2.0. If not found, try to locate guile-1.8
+# Look for guile versions in this order: 2.2 > 2.0 > 1.8
 
 # guile library and include dir
-GNC_PKG_CHECK_MODULES (GUILE2 guile-2.0>=2.0.9 QUIET)
-IF (GUILE2_FOUND) # found guile-2.0
-  ADD_DEFINITIONS (-DHAVE_GUILE20)
+GNC_PKG_CHECK_MODULES (GUILE22 guile-2.2 QUIET)
+IF (GUILE22_FOUND) # found guile-2.2
+  ADD_DEFINITIONS (-DHAVE_GUILE22)
   SET(HAVE_GUILE2 TRUE)
-  SET(GUILE_EFFECTIVE_VERSION 2.0)
-  SET(GUILE_INCLUDE_DIRS ${GUILE2_INCLUDE_DIRS})
-  SET(GUILE_LDFLAGS ${GUILE2_LDFLAGS})
+  SET(GUILE_EFFECTIVE_VERSION 2.2)
+  SET(GUILE_INCLUDE_DIRS ${GUILE22_INCLUDE_DIRS})
+  SET(GUILE_LDFLAGS ${GUILE22_LDFLAGS})
 
-  FIND_PROGRAM (GUILD_EXECUTABLE NAMES guild2.0 guild)
+  FIND_PROGRAM (GUILD_EXECUTABLE NAMES guild2.2 guild)
   IF (NOT GUILD_EXECUTABLE)
     MESSAGE (SEND_ERROR "The guild executable was not found, but is required. Please set GUILD_EXECUTABLE.")
   ENDIF (NOT GUILD_EXECUTABLE)
-  MESSAGE(STATUS "Using guile-2.0.x")
-  FIND_PROGRAM (GUILE_EXECUTABLE NAMES guile2.0 guile)
-ELSE()
-  # look for guile 1.8
-  GNC_PKG_CHECK_MODULES (GUILE1 guile-1.8>=1.8.8 QUIET)
-  IF (NOT GUILE1_FOUND)
-    MESSAGE (FATAL_ERROR "Neither guile 1.8 nor guile 2.0 were found GnuCash can't run without one of them. Ensure that one is installed and can be found with pgk-config.")
-  ENDIF(NOT GUILE1_FOUND)
-
-  SET(HAVE_GUILE1 TRUE)
-  SET(GUILE_EFFECTIVE_VERSION 1.8)
-  SET(GUILE_INCLUDE_DIRS ${GUILE1_INCLUDE_DIRS})
-  SET(GUILE_LDFLAGS ${GUILE1_LDFLAGS})
-  MESSAGE(STATUS "Using guile-1.8.x")
-  FIND_PROGRAM (GUILE_EXECUTABLE NAMES guile1.8 guile)
-ENDIF()
+  MESSAGE(STATUS "Using guile-2.2.x")
+  FIND_PROGRAM (GUILE_EXECUTABLE NAMES guile2.2 guile)
+ELSE(GUILE22_FOUND)
+  GNC_PKG_CHECK_MODULES (GUILE2 guile-2.0>=2.0.9 QUIET)
+  IF (GUILE2_FOUND) # found guile-2.0
+    ADD_DEFINITIONS (-DHAVE_GUILE20)
+    SET(HAVE_GUILE2 TRUE)
+    SET(GUILE_EFFECTIVE_VERSION 2.0)
+    SET(GUILE_INCLUDE_DIRS ${GUILE2_INCLUDE_DIRS})
+    SET(GUILE_LDFLAGS ${GUILE2_LDFLAGS})
+
+    FIND_PROGRAM (GUILD_EXECUTABLE NAMES guild2.0 guild)
+    IF (NOT GUILD_EXECUTABLE)
+      MESSAGE (SEND_ERROR "The guild executable was not found, but is required. Please set GUILD_EXECUTABLE.")
+    ENDIF (NOT GUILD_EXECUTABLE)
+    MESSAGE(STATUS "Using guile-2.0.x")
+    FIND_PROGRAM (GUILE_EXECUTABLE NAMES guile2.0 guile)
+  ELSE(GUILE2_FOUND)
+
+    # look for guile 1.8
+    GNC_PKG_CHECK_MODULES (GUILE1 guile-1.8>=1.8.8 QUIET)
+    IF (NOT GUILE1_FOUND)
+      MESSAGE (FATAL_ERROR "Neither guile 1.8 nor guile 2.0 were found GnuCash can't run without one of them. Ensure that one is installed and can be found with pgk-config.")
+    ENDIF(NOT GUILE1_FOUND)
+
+    SET(HAVE_GUILE1 TRUE)
+    SET(GUILE_EFFECTIVE_VERSION 1.8)
+    SET(GUILE_INCLUDE_DIRS ${GUILE1_INCLUDE_DIRS})
+    SET(GUILE_LDFLAGS ${GUILE1_LDFLAGS})
+    MESSAGE(STATUS "Using guile-1.8.x")
+    FIND_PROGRAM (GUILE_EXECUTABLE NAMES guile1.8 guile)
+  ENDIF(GUILE2_FOUND)
+ENDIF(GUILE22_FOUND)
 
 IF (NOT GUILE_EXECUTABLE)
   MESSAGE (SEND_ERROR "The guile executable was not found, but is required. Please set GUILE_EXECUTABLE.")
diff --git a/common/cmake_modules/GncAddSchemeTargets.cmake b/common/cmake_modules/GncAddSchemeTargets.cmake
index ffe31f1..ec6fc6f 100644
--- a/common/cmake_modules/GncAddSchemeTargets.cmake
+++ b/common/cmake_modules/GncAddSchemeTargets.cmake
@@ -88,12 +88,12 @@ FUNCTION(GNC_ADD_SCHEME_TARGETS _TARGET _SOURCE_FILES _OUTPUT_DIR _GUILE_DEPENDS
         "${current_bindir}" "${CMAKE_BINARY_DIR}/libgnucash/scm")  # to pick up generated build-config.scm
     SET(_GUILE_LOAD_COMPILED_PATH "${current_bindir}")
 
-    SET(_GUILE_CACHE_DIR ${LIBDIR_BUILD}/gnucash/scm/ccache/2.0)
+    SET(_GUILE_CACHE_DIR ${LIBDIR_BUILD}/gnucash/scm/ccache/${GUILE_EFFECTIVE_VERSION})
     SET(_GUILE_LOAD_PATH "${current_srcdir}")
     IF (MAKE_LINKS)
       LIST(APPEND _GUILE_LOAD_PATH "${build_datadir}/gnucash/scm")
     ENDIF()
-    SET(_GUILE_LOAD_COMPILED_PATH ${build_libdir}/gnucash/scm/ccache/2.0)
+    SET(_GUILE_LOAD_COMPILED_PATH ${build_libdir}/gnucash/scm/ccache/${GUILE_EFFECTIVE_VERSION})
 
     SET(_TARGET_FILES "")
 
diff --git a/common/cmake_modules/GncAddTest.cmake b/common/cmake_modules/GncAddTest.cmake
index 0f11a63..6435f22 100644
--- a/common/cmake_modules/GncAddTest.cmake
+++ b/common/cmake_modules/GncAddTest.cmake
@@ -25,7 +25,7 @@ FUNCTION(GET_GUILE_ENV)
       set(fpath "${fpath}${dir}:")
     endforeach(dir)
     LIST(APPEND env "PATH=${fpath}")
-    set(compiled_path "${LIBDIR_BUILD}/gnucash/scm/ccache/2.0")
+    set(compiled_path "${LIBDIR_BUILD}/gnucash/scm/ccache/${GUILE_EFFECTIVE_VERSION}")
     string(REGEX REPLACE "^([A-Za-z]):" "/\\1" compiled_path ${compiled_path})
     LIST(APPEND env GUILE_LOAD_COMPILED_PATH=${compiled_path})
   ENDIF(MINGW64)
@@ -33,7 +33,7 @@ FUNCTION(GET_GUILE_ENV)
   LIST(APPEND env "GUILE=${GUILE_EXECUTABLE}")
 
   IF (NOT WIN32)
-    LIST(APPEND env "GUILE_LOAD_COMPILED_PATH=${LIBDIR_BUILD}/gnucash/scm/ccache/2.0")
+    LIST(APPEND env "GUILE_LOAD_COMPILED_PATH=${LIBDIR_BUILD}/gnucash/scm/ccache/${GUILE_EFFECTIVE_VERSION}")
   ENDIF()
   SET(guile_load_paths "")
   LIST(APPEND guile_load_paths ${CMAKE_CURRENT_SOURCE_DIR}/mod-foo)
diff --git a/gnucash/environment.in b/gnucash/environment.in
index 0ef4568..eb582e2 100644
--- a/gnucash/environment.in
+++ b/gnucash/environment.in
@@ -1,7 +1,5 @@
 @-NOTE If you make any changes here, you should probably    -@
- at -NOTE also change the equivalent sections in:              -@
- at -NOTE - src/bin/overrides/gnucash-env.in                   -@
- at -NOTE Check as well that modifications performed in        -@
+ at -NOTE also verify that modifications performed in          -@
 @-NOTE - gnucash-on-windows.git:gnucash.iss don't conflict. -@
 # environment
 #
@@ -66,7 +64,7 @@ GUILE_LOAD_PATH={GNC_DATA}/scm;{GUILE_LIBS};{GUILE_LOAD_PATH}
 
 # On Windows {GNC_LIB} points to {GNC_HOME}/bin because that's where the DLLs
 # are. It's not where the compiled scheme files are so we use {SYS_LIB} here.
-GUILE_LOAD_COMPILED_PATH={SYS_LIB}/guile/2.0/ccache;{SYS_LIB}/gnucash/scm/ccache/@-GUILE_EFFECTIVE_VERSION-@;{GUILE_COMPILED_LIBS};{GUILE_LOAD_COMPILED_PATH}
+GUILE_LOAD_COMPILED_PATH={SYS_LIB}/guile/@-GUILE_EFFECTIVE_VERSION-@/ccache;{SYS_LIB}/gnucash/scm/ccache/@-GUILE_EFFECTIVE_VERSION-@;{GUILE_COMPILED_LIBS};{GUILE_LOAD_COMPILED_PATH}
 
 # Tell Guile where to find GnuCash specific shared libraries
 GNC_LIBRARY_PATH={SYS_LIB};{GNC_LIB}
diff --git a/gnucash/import-export/qif-imp/Makefile.am b/gnucash/import-export/qif-imp/Makefile.am
index e23cd32..6226701 100644
--- a/gnucash/import-export/qif-imp/Makefile.am
+++ b/gnucash/import-export/qif-imp/Makefile.am
@@ -100,7 +100,10 @@ endif
 
 if GNC_HAVE_GUILE_2
 GUILE_COMPILE_ENV = \
+  --gnc-module-dir ${top_builddir}/libgnucash/engine \
+  --guile-load-dir ${top_builddir}/libgnucash/app-utils \
   --guile-load-dir ${top_builddir}/libgnucash/core-utils \
+  --guile-load-dir ${top_builddir}/libgnucash/engine \
   --guile-load-dir ${top_builddir}/libgnucash/gnc-module \
   --guile-load-dir ${top_builddir}/libgnucash/scm \
   --library-dir    ${top_builddir}/libgnucash/engine \
diff --git a/gnucash/import-export/qif-imp/qif-import.scm b/gnucash/import-export/qif-imp/qif-import.scm
index 54443fb..ae4cf71 100644
--- a/gnucash/import-export/qif-imp/qif-import.scm
+++ b/gnucash/import-export/qif-imp/qif-import.scm
@@ -26,6 +26,7 @@
 
 (define-module (gnucash import-export qif-import))
 (use-modules (gnucash main)) ;; FIXME: delete after we finish modularizing.
+(use-modules (gnucash app-utils))
 
 ;; We do this initialization here because src/gnome isn't a real module.
 ;; Note: Guile 2 needs to find the symbols from the extension at compile time already
@@ -43,7 +44,6 @@
 (use-modules (ice-9 regex))
 (use-modules (srfi srfi-1))
 
-(debug-enable 'debug)
 (debug-enable 'backtrace)
 
 (gnc:module-load "gnucash/engine" 0)
diff --git a/gnucash/report/report-gnome/dialog-report-column-view.c b/gnucash/report/report-gnome/dialog-report-column-view.c
index d351ca4..8c508f3 100644
--- a/gnucash/report/report-gnome/dialog-report-column-view.c
+++ b/gnucash/report/report-gnome/dialog-report-column-view.c
@@ -432,7 +432,7 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data)
                 oldlist = SCM_CDR(oldlist);
             }
             newlist = scm_append
-                      (scm_listify(scm_reverse(scm_cons(SCM_LIST4(new_report,
+                      (scm_list_n (scm_reverse(scm_cons(SCM_LIST4(new_report,
                                                scm_from_int (1),
                                                scm_from_int (1),
                                                SCM_BOOL_F),
@@ -443,7 +443,7 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data)
         else
         {
             newlist = scm_append
-                      (scm_listify(oldlist,
+                      (scm_list_n (oldlist,
                                    SCM_LIST1(SCM_LIST4(new_report,
                                              scm_from_int (1),
                                              scm_from_int (1),
@@ -485,7 +485,7 @@ gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data)
             }
             if (count <= oldlength)
             {
-                newlist = scm_append(scm_listify(scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED));
+                newlist = scm_append(scm_list_n (scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED));
             }
         }
 
@@ -528,7 +528,7 @@ gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data)
         temp = SCM_CAR(oldlist);
         oldlist = SCM_CDR(oldlist);
         newlist = scm_cons(temp, scm_cons(SCM_CAR(oldlist), newlist));
-        newlist = scm_append(scm_listify(scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED));
+        newlist = scm_append(scm_list_n (scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED));
 
         scm_gc_unprotect_object(r->contents_list);
         r->contents_list = newlist;
@@ -566,7 +566,7 @@ gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data)
         temp = SCM_CAR(oldlist);
         oldlist = SCM_CDR(oldlist);
         newlist = scm_cons(temp, scm_cons(SCM_CAR(oldlist), newlist));
-        newlist = scm_append(scm_listify(scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED));
+        newlist = scm_append(scm_list_n (scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED));
 
         scm_gc_unprotect_object(r->contents_list);
         r->contents_list = newlist;
diff --git a/gnucash/report/report-gnome/test/test-load-report-gnome-module.scm b/gnucash/report/report-gnome/test/test-load-report-gnome-module.scm
index 054ada4..5273082 100755
--- a/gnucash/report/report-gnome/test/test-load-report-gnome-module.scm
+++ b/gnucash/report/report-gnome/test/test-load-report-gnome-module.scm
@@ -2,7 +2,6 @@
 exec ${GUILE} -s "$0"
 !#
 
-(debug-enable 'debug)
 (debug-enable 'backtrace)
 
 (debug-set! stack 500000)
@@ -10,7 +9,11 @@ exec ${GUILE} -s "$0"
     (debug-set! maxdepth 100000))
 
 (display "  testing report module load ... ")
-(use-modules (ice-9 syncase))
+(cond-expand
+  (guile-2 )
+  (else
+    ;; Syncase is deprecated and redundant in guile 2
+    (use-modules (ice-9 syncase))))
 (use-modules (gnucash gnc-module))
 
 (gnc:module-system-init)
diff --git a/gnucash/report/report-system/report.scm b/gnucash/report/report-system/report.scm
index ae8ae00..6ec25d5 100644
--- a/gnucash/report/report-system/report.scm
+++ b/gnucash/report/report-system/report.scm
@@ -21,6 +21,7 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (use-modules (gnucash main))
+(use-modules (gnucash app-utils))
 (use-modules (gnucash printf))
 (use-modules (gnucash gettext))
 (cond-expand
diff --git a/gnucash/report/report-system/test/test-load-report-system-module.scm b/gnucash/report/report-system/test/test-load-report-system-module.scm
index ea77251..0695190 100755
--- a/gnucash/report/report-system/test/test-load-report-system-module.scm
+++ b/gnucash/report/report-system/test/test-load-report-system-module.scm
@@ -1,4 +1,3 @@
-(debug-enable 'debug)
 (debug-enable 'backtrace)
 
 (debug-set! stack 500000)
@@ -7,7 +6,11 @@
 
 (display "  testing report module load ... ")
 (setenv "GNC_UNINSTALLED" "1")
-(use-modules (ice-9 syncase))
+(cond-expand
+  (guile-2 )
+  (else
+    ;; Syncase is deprecated and redundant in guile 2
+    (use-modules (ice-9 syncase))))
 (use-modules (gnucash gnc-module))
 
 (gnc:module-system-init)
diff --git a/gnucash/report/utility-reports/hello-world.scm b/gnucash/report/utility-reports/hello-world.scm
index bbabfdd..5337aa9 100644
--- a/gnucash/report/utility-reports/hello-world.scm
+++ b/gnucash/report/utility-reports/hello-world.scm
@@ -28,11 +28,6 @@
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
 
-;; 'debug is deprecated and unused since guile 2
-(cond-expand
-  (guile-2 )
-  (else
-    (debug-enable 'debug)))
 (debug-enable 'backtrace)
 
 (gnc:module-load "gnucash/report/report-system" 0)
diff --git a/gnucash/report/utility-reports/test-graphing.scm b/gnucash/report/utility-reports/test-graphing.scm
index c053e48..94437ef 100644
--- a/gnucash/report/utility-reports/test-graphing.scm
+++ b/gnucash/report/utility-reports/test-graphing.scm
@@ -27,7 +27,6 @@
 (use-modules (gnucash main)) ;; FIXME: delete after we finish modularizing.
 (use-modules (gnucash gnc-module))
 
-(debug-enable 'debug)
 (debug-enable 'backtrace)
 
 (gnc:module-load "gnucash/report/report-system" 0)
diff --git a/gnucash/report/utility-reports/view-column.scm b/gnucash/report/utility-reports/view-column.scm
index 780a521..ba4be79 100644
--- a/gnucash/report/utility-reports/view-column.scm
+++ b/gnucash/report/utility-reports/view-column.scm
@@ -28,6 +28,7 @@
 
 (define-module (gnucash report view-column))
 (use-modules (gnucash main)) ;; FIXME: delete after we finish modularizing.
+(use-modules (gnucash app-utils))
 (use-modules (gnucash gnc-module))
 (use-modules (gnucash gettext))
 (cond-expand
diff --git a/libgnucash/app-utils/app-utils.scm b/libgnucash/app-utils/app-utils.scm
index 22008d2..fa3f5f0 100644
--- a/libgnucash/app-utils/app-utils.scm
+++ b/libgnucash/app-utils/app-utils.scm
@@ -43,7 +43,9 @@
 (re-export N_)
 
 ;; c-interface.scm
-(export gnc:error->string)
+(export gnc:apply-with-error-handling)
+(export gnc:eval-string-with-error-handling)
+(export gnc:backtrace-if-exception)
 (export gnc:make-string-database)
 
 ;; options.scm
diff --git a/libgnucash/app-utils/c-interface.scm b/libgnucash/app-utils/c-interface.scm
index e6243d7..559bc32 100644
--- a/libgnucash/app-utils/c-interface.scm
+++ b/libgnucash/app-utils/c-interface.scm
@@ -15,25 +15,67 @@
 ;; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
 ;; Boston, MA  02110-1301,  USA       gnu at gnu.org
 
-(define (gnc:error->string tag args)
-  (define (write-error port)
-    (if (and (list? args) (not (null? args)))
-        (let ((func (car args)))
-          (if func
-              (begin
-                (display "Function: " port)
-                (display func port)
-                (display ", " port)
-                (display tag port)
-                (display "\n\n" port)))))
-    (false-if-exception
-     (apply display-error (fluid-ref the-last-stack) port args))
-    (display-backtrace (fluid-ref the-last-stack) port)
-    (force-output port))
-  
-  (false-if-exception
-   (call-with-output-string write-error)))
+(define (gnc:call-with-error-handling cmd args)
+  (let ((captured-stack #f)
+        (captured-error #f)
+        (result #f))
+    (catch #t
+        (lambda ()
+            ;; Execute the code in which
+            ;; you want to catch errors here.
+            (if (procedure? cmd)
+                (set! result (apply cmd args)))
+            (if (string? cmd)
+                (set! result (eval-string cmd)))
+            )
+        (lambda (key . parameters)
+            ;; Put the code which you want
+            ;; to handle an error after the
+            ;; stack has been unwound here.
+            (let* ((str-port (open-output-string)))
+                    (display-backtrace captured-stack str-port)
+                    (display "\n" str-port)
+                    (print-exception str-port #f key parameters)
+                    (set! captured-error (get-output-string str-port))))
+        (lambda (key . parameters)
+            ;; Capture the stack here, cut the last 3 frames which are
+            ;; make-stack, this one, and the throw handler.
+                (set! captured-stack (make-stack #t 3))))
 
+    (list result captured-error)
+))
+
+;; gnc:eval-string-with-error-handling will evaluate the input string (cmd)
+;; an captures any exception that would be generated. It returns
+;; a list with 2 elements: the output of the evaluation and a backtrace.
+;; The first may be set if string evaluation did generate
+;; output, the latter is set when an exception was caught.
+;; We'll use this to wrap guile calls in C(++), allowing
+;; the C(++) code to decide how to handle the errors.
+(define (gnc:eval-string-with-error-handling cmd)
+    (gnc:call-with-error-handling cmd '()))
+
+;; gnc:apply-with-error-handling will call guile's apply to run func with args
+;; an captures any exception that would be generated. It returns
+;; a list with 2 elements: the output of the evaluation and a backtrace.
+;; The first may be set if the result of the apply did generate
+;; output, the latter is set when an exception was caught.
+;; We'll use this to wrap guile calls in C(++), allowing
+;; the C(++) code to decide how to handle the errors.
+(define (gnc:apply-with-error-handling func args)
+    (gnc:call-with-error-handling func args))
+
+
+(define (gnc:backtrace-if-exception proc . args)
+  (let* ((apply-result (gnc:apply-with-error-handling proc args))
+         (result (car apply-result))
+         (error (cadr apply-result)))
+        (if error
+            (begin
+                (display error (current-error-port))
+                (if (defined? 'gnc:warn)
+                    (gnc:warn error)))
+            result)))
 
 ;; This database can be used to store and retrieve translatable
 ;; strings. Strings that are returned by the lookup function are
@@ -56,5 +98,5 @@
       (if func
           (apply func args)
           (gnc:warn "string-database: bad message" message "\n"))))
-  
+
   dispatch)
diff --git a/libgnucash/app-utils/gfec.c b/libgnucash/app-utils/gfec.c
index b640e2d..8e5f70a 100644
--- a/libgnucash/app-utils/gfec.c
+++ b/libgnucash/app-utils/gfec.c
@@ -18,134 +18,31 @@
 # define strdup _strdup
 #endif
 
-typedef struct {
-    char **msg;
-    SCM *scm_string;
-} helper_data_t;
-
-static SCM helper_scm_to_string(void *ptr_void)
-{
-    helper_data_t* ptr = ptr_void;
-    g_assert(ptr);
-    *(ptr->msg) = gnc_scm_to_utf8_string(*ptr->scm_string);
-    return SCM_UNDEFINED;
-}
-
-
-static int gfec_catcher_recursion_level = 0;
-
-/* We assume that data is actually a char**. The way we return results
- * from this function is to malloc a fresh string, and store it in
- * this pointer. It is the caller's responsibility to do something
- * smart with this freshly allocated storage. the caller can determine
- * whether there was an error by initializing the char* passed in to
- * NULL. If there is an error, the char string will not be NULL on
- * return.
- *
- * This function might call itself recursively: The conversion of the error
- * object to a string might itself throw an exception, hence the scm_to_string
- * function must be wrapped into a stack_catch block as well. To avoid infinite
- * recursion, we check the recursion level by gfec_catcher_recursion_level.
- */
-static SCM
-gfec_catcher(void *data, SCM tag, SCM throw_args)
-{
-    SCM func;
-    SCM result;
-    char *msg = NULL;
-
-    // To much recursion? Better jump out of here quickly.
-    if (gfec_catcher_recursion_level > 2)
-    {
-        *(char**)data = strdup("Guile error: Too many recursions in error catch handler.");
-        return SCM_UNDEFINED;
-    }
-
-    gfec_catcher_recursion_level++;
-
-    func = scm_c_eval_string("gnc:error->string");
-    if (scm_is_procedure(func))
-    {
-        result = scm_call_2(func, tag, throw_args);
-        if (scm_is_string(result))
-        {
-            char *internal_err_msg = NULL;
-            helper_data_t helper_data;
-
-            helper_data.msg = &msg;
-            helper_data.scm_string = &result;
-
-            // The conversion to string can itself throw as well
-            scm_internal_stack_catch(SCM_BOOL_T,
-                                     helper_scm_to_string,
-                                     (void *) &helper_data,
-                                     gfec_catcher,
-                                     &internal_err_msg);
-            // Previously: msg = gnc_scm_to_utf8_string (result);
-
-            // Did we run into an exception? Then the output argument msg is
-            // not set (due to the exception), but err_msg is set and contains
-            // that error message. We thus pass the err_msg instead of msg to
-            // our caller.
-            if (internal_err_msg)
-            {
-                msg = internal_err_msg;
-            }
-        }
-    }
-
-    if (msg == NULL)
-    {
-        *(char**)data = strdup("Error running guile function.");
-    }
-    else
-    {
-        *(char**)data = strdup(msg);
-        g_free(msg);
-    }
-
-    --gfec_catcher_recursion_level;
-    return SCM_UNDEFINED;
-}
-
-
-/* The arguments to scm_internal_stack_catch:
-   ------------------------------------------
-   SCM tag                     : this should be SCM_BOOL_T to catch all errors.
-   scm_catch_body_t body       : the function to run.
-   void *body_data             : a pointer to pass to body
-   scm_catch_handler_t handler : the hander function
-   void *handler_data          : a pointer to pass to the handler
-*/
-
-static SCM
-gfec_string_helper(void *data)
-{
-    char *string = data;
-
-    return scm_c_eval_string(string);
-}
 
 SCM
 gfec_eval_string(const char *str, gfec_error_handler error_handler)
 {
-    char *err_msg = NULL;
-    SCM result;
-
-    result = scm_internal_stack_catch(SCM_BOOL_T,
-                                      gfec_string_helper,
-                                      (void *) str,
-                                      gfec_catcher,
-                                      &err_msg);
-
-    if (err_msg != NULL)
+    SCM result = SCM_UNDEFINED;
+    SCM func = scm_c_eval_string("gnc:eval-string-with-error-handling");
+    if (scm_is_procedure(func))
     {
-        if (error_handler)
-            error_handler(err_msg);
+        char *err_msg = NULL;
+        SCM call_result, error = SCM_UNDEFINED;
+        call_result = scm_call_1 (func, scm_from_utf8_string (str));
 
-        free(err_msg);
+        error = scm_list_ref (call_result, scm_from_uint (1));
+        if (scm_is_true (error))
+            err_msg = gnc_scm_to_utf8_string (error);
+        else
+            result = scm_list_ref (call_result, scm_from_uint (0));
 
-        return SCM_UNDEFINED;
+        if (err_msg != NULL)
+        {
+            if (error_handler)
+                error_handler (err_msg);
+
+            free(err_msg);
+        }
     }
 
     return result;
@@ -175,44 +72,30 @@ gfec_eval_file(const char *file, gfec_error_handler error_handler)
     return result;
 }
 
-struct gfec_apply_rec
-{
-    SCM proc;
-    SCM arglist;
-};
-
-static SCM
-gfec_apply_helper(void *data)
-{
-    struct gfec_apply_rec *apply_rec = (struct gfec_apply_rec *)data;
-
-    return scm_apply(apply_rec->proc, apply_rec->arglist, SCM_EOL);
-}
-
 SCM
 gfec_apply(SCM proc, SCM arglist, gfec_error_handler error_handler)
 {
-    char *err_msg = NULL;
-    struct gfec_apply_rec apply_rec;
-    SCM result;
-
-    apply_rec.proc = proc;
-    apply_rec.arglist = arglist;
-
-    result = scm_internal_stack_catch(SCM_BOOL_T,
-                                      gfec_apply_helper,
-                                      &apply_rec,
-                                      gfec_catcher,
-                                      &err_msg);
-
-    if (err_msg != NULL)
+    SCM result = SCM_UNDEFINED;
+    SCM func = scm_c_eval_string("gnc:apply-with-error-handling");
+    if (scm_is_procedure(func))
     {
-        if (error_handler)
-            error_handler(err_msg);
+        char *err_msg = NULL;
+        SCM call_result, error;
+        call_result = scm_call_2 (func, proc, arglist);
 
-        free(err_msg);
+        error = scm_list_ref (call_result, scm_from_uint (1));
+        if (scm_is_true (error))
+            err_msg = gnc_scm_to_utf8_string (error);
+        else
+            result = scm_list_ref (call_result, scm_from_uint (0));
 
-        return SCM_UNDEFINED;
+        if (err_msg != NULL)
+        {
+            if (error_handler)
+                error_handler (err_msg);
+
+            free(err_msg);
+        }
     }
 
     return result;
diff --git a/libgnucash/app-utils/gnc-exp-parser.c b/libgnucash/app-utils/gnc-exp-parser.c
index 0f90281..23b787c 100644
--- a/libgnucash/app-utils/gnc-exp-parser.c
+++ b/libgnucash/app-utils/gnc-exp-parser.c
@@ -302,7 +302,7 @@ func_op(const char *fname, int argc, void **argv)
         printf( "gnc:\"%s\" is not a scm procedure\n", fname );
         return NULL;
     }
-    scmArgs = scm_listify( SCM_UNDEFINED );
+    scmArgs = scm_list_n (SCM_UNDEFINED);
     for ( i = 0; i < argc; i++ )
     {
         /* cons together back-to-front. */
diff --git a/libgnucash/app-utils/test/test-exp-parser.c b/libgnucash/app-utils/test/test-exp-parser.c
index 94be10a..6c46c7f 100644
--- a/libgnucash/app-utils/test/test-exp-parser.c
+++ b/libgnucash/app-utils/test/test-exp-parser.c
@@ -189,9 +189,9 @@ test_parser (void)
                    "- 42.72 + 13.32 + 15.48 + 23.4 + 115.4",
                    gnc_numeric_create(35897, 100) );
 
-    /* This must be defined for the function-parsing to work. */
-    scm_c_eval_string("(define (gnc:error->string tag args)   (define (write-error port)     (if (and (list? args) (not (null? args)))         (let ((func (car args)))           (if func               (begin                 (display \"Function: \" port)                 (display func port)                 (display \", \" port)                 (display tag port)                 (display \"\n\n\" port)))))     (false-if-exception      (apply display-error (fluid-ref the-last-stack) port args))     (display-backtrace (fluid-ref the-last-stack) port)     (force-output port))   (false-if-exception    (call-with-output-string write-error)))");
-
+    /* gnc:apply-with-error-handling must be defined because it's used
+     * indirectly through gfec_apply by the expression parser */
+    scm_c_eval_string("(define (gnc:apply-with-error-handling cmd args)  (let ((captured-stack #f)  (captured-error #f)  (result #f))  (catch #t  (lambda ()  (if (procedure? cmd)  (set! result (apply cmd args)))  (if (string? cmd)  (set! result (eval-string cmd))))  (lambda (key . parameters)  (let* ((str-port (open-output-string)))  (display-backtrace captured-stack str-port)  (display \"\n\" str-port)  (print-exception str-port #f key parameters)  (set! captured-error (get-output-string str-port))))  (lambda (key . parameters)  (set! captured-stack (make-stack #t 3))))  (list result captured-error)))");
     scm_c_eval_string( "(define (gnc:plus a b) (+ a b))" );
     add_pass_test("plus(2 : 1)", NULL, gnc_numeric_create(3, 1));
     add_fail_test("plus(1:2) plus(3:4)", NULL, 15);
diff --git a/libgnucash/app-utils/test/test-load-app-utils-module.scm b/libgnucash/app-utils/test/test-load-app-utils-module.scm
index 6bf102f..a3bd729 100755
--- a/libgnucash/app-utils/test/test-load-app-utils-module.scm
+++ b/libgnucash/app-utils/test/test-load-app-utils-module.scm
@@ -21,10 +21,10 @@
       (display "Failed - module gnucash/app-utils not loaded successfully\n")
       (set! exit-code -1)))
 
-(if (procedure? gnc:error->string)
-    (display "Procedure gnc:error->string found\n")
+(if (procedure? gnc:apply-with-error-handling)
+    (display "Procedure gnc:apply-with-error-handling found\n")
     (begin
-      (display "Failed - procedure gnc:error->string not found\n")
+      (display "Failed - procedure gnc:apply-with-error-handling not found\n")
       (set! exit-code -1)))
 
 (if (procedure? gnc-default-currency)
diff --git a/libgnucash/app-utils/test/test-scm-query-string.cpp b/libgnucash/app-utils/test/test-scm-query-string.cpp
index 2d08da3..8031a9a 100644
--- a/libgnucash/app-utils/test/test-scm-query-string.cpp
+++ b/libgnucash/app-utils/test/test-scm-query-string.cpp
@@ -135,7 +135,7 @@ main (int argc, char **argv)
 /* When built with clang, guile-1.8.8's scm_c_eval_string truncates all
  * integer values to int32, which causes this test to fail.
  */
-#if !(defined(__clang__)) || defined(HAVE_GUILE20)
+#if !(defined(__clang__)) || defined(HAVE_GUILE20) || defined(HAVE_GUILE22)
     scm_boot_guile (argc, argv, main_helper, NULL);
 #endif
     return 0;
diff --git a/libgnucash/scm/main.scm b/libgnucash/scm/main.scm
index 040f289..107a109 100644
--- a/libgnucash/scm/main.scm
+++ b/libgnucash/scm/main.scm
@@ -53,7 +53,6 @@
 (export gnc:error)
 (export gnc:msg)
 (export gnc:debug)
-(export gnc:backtrace-if-exception)
 (export gnc:safe-strcmp) ;; only used by aging.scm atm...
 
 ;; Get the Makefile.am/configure.in generated variables.
@@ -64,15 +63,6 @@
 ;; These are needed for a guile 1.3.4 bug
 (debug-enable 'backtrace)
 (read-enable 'positions)
-
-;; These options should only be set for guile < 2.0
-;; 'debug (deprecated and unused since guile 2)
-;; maxdepth (removed since guile 2)
-(cond-expand
-  (guile-2 )
-  (else
-    (debug-enable 'debug)
-    (debug-set! maxdepth 100000)))
 (debug-set! stack    200000)
 
 ;; Initalialize localization, otherwise reports may output
@@ -94,32 +84,6 @@
        (b -1)
        (else 0))))
 
-(define (gnc:backtrace-if-exception proc . args)
-  (define (dumper key . args)
-    (let ((stack (make-stack #t dumper)))
-      ;; Send debugging output to the console.
-      (display-backtrace stack (current-error-port))
-      (apply display-error stack (current-error-port) args)
-
-      ;; Send debugging output to the log.
-      (if (defined? 'gnc:warn)
-          (let ((string-port (open-output-string)))
-            (display-backtrace stack string-port)
-            (apply display-error stack string-port args)
-            (gnc:warn (get-output-string string-port))
-            (close-output-port string-port)))
-
-      (throw 'ignore)))
-  
-  (catch 
-      'ignore
-    (lambda () 
-      (lazy-catch #t 
-                  (lambda () (apply proc args))
-                  dumper))
-    (lambda (key . args)
-      #f)))
-
 ;;;; Status output functions.
 
 (define (strify items)

commit a28bcdf19b1005b2c2937a25e10a21164f741228
Merge: 98fcf1b a847d44
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 19 22:04:18 2017 -0800

    Merge Chris Lam's Bug 790526 fix into maint.


commit a847d441f14cf66255a8e52f6447b43d9aac447c
Author: christopherlam <christopher.lck at gmail.com>
Date:   Wed Dec 20 11:57:35 2017 +0800

    fix silly mistake

diff --git a/src/app-utils/test/test-date-utilities.scm b/src/app-utils/test/test-date-utilities.scm
index e62610f..6fca517 100644
--- a/src/app-utils/test/test-date-utilities.scm
+++ b/src/app-utils/test/test-date-utilities.scm
@@ -7,12 +7,12 @@
 
 (define (create-time64 l)
   (let ((now (gnc-localtime (current-time))))
-    (set-tm:sec now (list-ref l 0))
-    (set-tm:min now (list-ref l 1))
-    (set-tm:hour now (list-ref l 2))
-    (set-tm:mday now (list-ref l 3))
-    (set-tm:mon now (list-ref l 4))
-    (set-tm:year now (list-ref l 5))
+    (set-tm:sec now (list-ref l 5))
+    (set-tm:min now (list-ref l 4))
+    (set-tm:hour now (list-ref l 3))
+    (set-tm:mday now (list-ref l 2))
+    (set-tm:mon now (list-ref l 1))
+    (set-tm:year now (list-ref l 0))
     (set-tm:isdst now -1)
     (gnc-mktime now)))
 

commit 9af6f184efde5941d09d5d289b6d41a886745a52
Author: christopherlam <christopher.lck at gmail.com>
Date:   Wed Dec 20 10:01:32 2017 +0800

    fix unit test again

diff --git a/src/app-utils/test/test-date-utilities.scm b/src/app-utils/test/test-date-utilities.scm
index 380253a..e62610f 100644
--- a/src/app-utils/test/test-date-utilities.scm
+++ b/src/app-utils/test/test-date-utilities.scm
@@ -16,15 +16,11 @@
     (set-tm:isdst now -1)
     (gnc-mktime now)))
 
-(define (list->time64 l)
-  (create-time64 (list-ref l 0) (list-ref l 1) (list-ref l 2)
-                 (list-ref l 3) (list-ref l 4) (list-ref l 5)))
-
 (define (weeknums-equal? pair-of-dates)
   (let ((d1 (car pair-of-dates))
         (d2 (cdr pair-of-dates)))
-    (equal? (gnc:date-to-week (list->time64 d1))
-            (gnc:date-to-week (list->time64 d2)))))
+    (equal? (gnc:date-to-week (create-time64 d1))
+            (gnc:date-to-week (create-time64 d2)))))
 
 (define (test-weeknum-calculator)
   (and (weeknums-equal? (cons '(1970 1 1 0 0 0)

commit 302d1e73e850b92f5b8076b0af5741fc4afbaa2d
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 19 17:39:39 2017 -0800

    Add test-date-utilities to CMakeLists.txt and Makefile.am.

diff --git a/src/app-utils/test/CMakeLists.txt b/src/app-utils/test/CMakeLists.txt
index b525b80..dc47478 100644
--- a/src/app-utils/test/CMakeLists.txt
+++ b/src/app-utils/test/CMakeLists.txt
@@ -29,5 +29,7 @@ GNC_ADD_SCHEME_TEST(scm-test-load-module test-load-module.in)
 
 CONFIGURE_FILE(test-load-module.in test-load-module @ONLY)
 
+GNC_ADD_SCHEME_TEST(scm-test-date-utilities test-date-utilities.scm)
+
 SET_DIST_LIST(test_app_utils_DIST CMakeLists.txt Makefile.am test-exp-parser.c test-link-module.c test-load-module.in
-        test-print-parse-amount.c test-print-queries.c test-scm-query-string.c test-sx.c)
\ No newline at end of file
+        test-print-parse-amount.c test-print-queries.c test-scm-query-string.c test-sx.c test-date-utilities.scm)
diff --git a/src/app-utils/test/Makefile.am b/src/app-utils/test/Makefile.am
index 6fb6a95..b78f046 100644
--- a/src/app-utils/test/Makefile.am
+++ b/src/app-utils/test/Makefile.am
@@ -4,6 +4,7 @@ TESTS = \
   test-exp-parser \
   test-scm-query-string \
   test-print-parse-amount \
+  test-date-utilities \
   test-sx
 
 test_exp_parser_SOURCES = \

commit 98fcf1b08efbe4de7a896637d6394aeed8d824c2
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 19 16:54:01 2017 -0800

    Fix duplicate test-case name.

diff --git a/src/import-export/test/test-flat-bayes.c b/src/import-export/test/test-flat-bayes.c
index 22872ca..b00608b 100644
--- a/src/import-export/test/test-flat-bayes.c
+++ b/src/import-export/test/test-flat-bayes.c
@@ -279,7 +279,7 @@ main (int argc, char *argv[])
     GNC_TEST_ADD (suitename, "guid_bayes_entry", Fixture, NULL, setup, &write_guid_bayes_entry, teardown);
     GNC_TEST_ADD (suitename, "normal_bayes_find", Fixture, NULL, setup, &find_normal_bayes_account, teardown);
     GNC_TEST_ADD (suitename, "guid_bayes_find", Fixture, NULL, setup, &find_guid_bayes_account, teardown);
-    GNC_TEST_ADD (suitename, "normal_bayes_find", Fixture, NULL, setup, &find_normal_bayes_when_non_exists, teardown);
+    GNC_TEST_ADD (suitename, "normal_bayes_find_none", Fixture, NULL, setup, &find_normal_bayes_when_non_exists, teardown);
     GNC_TEST_ADD (suitename, "Mixed case", Fixture, NULL, setup, &group_account_name_and_guid_in_mixed_case, teardown);
     GNC_TEST_ADD (suitename, "Read flat bayes", Fixture, NULL, setup, &read_flat_bayes, teardown);
     GNC_TEST_ADD (suitename, "Write flat bayes", Fixture, NULL, setup, &write_flat_bayes, teardown);

commit 3f408a885bc5d88fbb555a78e019494b7518d69b
Author: christopherlam <christopher.lck at gmail.com>
Date:   Wed Dec 20 08:15:26 2017 +0800

    typo

diff --git a/src/app-utils/test/test-date-utilities.scm b/src/app-utils/test/test-date-utilities.scm
index 393c0f4..380253a 100644
--- a/src/app-utils/test/test-date-utilities.scm
+++ b/src/app-utils/test/test-date-utilities.scm
@@ -5,7 +5,7 @@
 (define (run-test)
   (and (test test-weeknum-calculator)))
 
-(define (create->time64 l)
+(define (create-time64 l)
   (let ((now (gnc-localtime (current-time))))
     (set-tm:sec now (list-ref l 0))
     (set-tm:min now (list-ref l 1))

commit b6ec61fa45c386b641c516782b4501d333a8ea0e
Author: christopherlam <christopher.lck at gmail.com>
Date:   Wed Dec 20 07:58:03 2017 +0800

    add unittest for bugzilla 790526

diff --git a/src/app-utils/test/test-date-utilities.scm b/src/app-utils/test/test-date-utilities.scm
new file mode 100644
index 0000000..393c0f4
--- /dev/null
+++ b/src/app-utils/test/test-date-utilities.scm
@@ -0,0 +1,42 @@
+(use-modules (gnucash gnc-module))
+(gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
+(use-modules (gnucash engine test test-extras))
+
+(define (run-test)
+  (and (test test-weeknum-calculator)))
+
+(define (create->time64 l)
+  (let ((now (gnc-localtime (current-time))))
+    (set-tm:sec now (list-ref l 0))
+    (set-tm:min now (list-ref l 1))
+    (set-tm:hour now (list-ref l 2))
+    (set-tm:mday now (list-ref l 3))
+    (set-tm:mon now (list-ref l 4))
+    (set-tm:year now (list-ref l 5))
+    (set-tm:isdst now -1)
+    (gnc-mktime now)))
+
+(define (list->time64 l)
+  (create-time64 (list-ref l 0) (list-ref l 1) (list-ref l 2)
+                 (list-ref l 3) (list-ref l 4) (list-ref l 5)))
+
+(define (weeknums-equal? pair-of-dates)
+  (let ((d1 (car pair-of-dates))
+        (d2 (cdr pair-of-dates)))
+    (equal? (gnc:date-to-week (list->time64 d1))
+            (gnc:date-to-week (list->time64 d2)))))
+
+(define (test-weeknum-calculator)
+  (and (weeknums-equal? (cons '(1970 1 1 0 0 0)
+                              '(1970 1 1 23 59 59)))
+       (weeknums-equal? (cons '(1969 12 31 0 0 0)
+                              '(1969 12 31 23 59 59)))
+       (weeknums-equal? (cons '(1969 12 31 0 0 0)
+                              '(1970 1 1 0 0 1)))
+       (weeknums-equal? (cons '(2001 1 1 0 0 0)
+                              '(2001 1 1 23 59 59)))
+       (not (weeknums-equal? (cons '(1970 1 1 0 0 0)
+                                   '(1970 1 10 0 0 1))))
+       (not (weeknums-equal? (cons '(1969 12 28 0 0 1)
+                                   '(1970 1 5 0 0 1))))
+       ))

commit 76921b5e2843023512c324535fa667efe0b5a98f
Author: christopherlam <christopher.lck at gmail.com>
Date:   Tue Dec 19 15:39:57 2017 -0800

    Fix -DWITH_SQL=OFF Build.

diff --git a/gnucash/gnome/gnc-plugin-basic-commands.c b/gnucash/gnome/gnc-plugin-basic-commands.c
index 248c494..3142c71 100644
--- a/gnucash/gnome/gnc-plugin-basic-commands.c
+++ b/gnucash/gnome/gnc-plugin-basic-commands.c
@@ -528,7 +528,7 @@ gnc_main_window_cmd_file_export_accounts (GtkAction *action, GncMainWindowAction
 #ifdef HAVE_DBI_DBI_H
     gnc_ui_file_access_for_export (GTK_WINDOW (data->window));
 #else
-    gnc_file_export ();
+    gnc_file_export (GTK_WINDOW (data->window));
 #endif
     gnc_window_set_progressbar_window (NULL);
 }

commit 0186bc1c0c32db749485dc1d48402715f69337dc
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 8 14:45:02 2017 +0000

    Change the relative path to a full one for rpath

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 38515d6..964bb9b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -526,7 +526,7 @@ IF (APPLE AND WITH_GNUCASH)
 ENDIF (APPLE AND WITH_GNUCASH)
 
 IF (UNIX)
-  SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}:${PKGLIBDIR}")
+  SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}:${CMAKE_INSTALL_FULL_LIBDIR}/gnucash")
 ENDIF()
 
 SET(BUILD_SHARED_LIBS ON)

commit 7c57ad18cd5dd4566cc99e959da396c757aac88a
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 19 14:34:10 2017 -0800

    Fix date offset error in datetime test.

diff --git a/libgnucash/engine/test/gtest-gnc-datetime.cpp b/libgnucash/engine/test/gtest-gnc-datetime.cpp
index 3f1712d..364a7dc 100644
--- a/libgnucash/engine/test/gtest-gnc-datetime.cpp
+++ b/libgnucash/engine/test/gtest-gnc-datetime.cpp
@@ -335,7 +335,7 @@ TEST(gnc_datetime_constructors, test_gncdate_neutral_constructor)
 TEST(gnc_datetime_functions, test_format)
 {
     GncDateTime atime(2394187200); //2045-11-13 12:00:00 Z
-    if ((atime.offset() / 3600) > 11 || (atime.offset() / 3600) < -11)
+    if ((atime.offset() / 3600) > 11)
         EXPECT_EQ(atime.format("%d-%m-%Y"), "14-11-2045");
     else
         EXPECT_EQ(atime.format("%d-%m-%Y"), "13-11-2045");

commit baad2097a709f90a63f9608542fd701fcc64746e
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Dec 18 10:15:05 2017 -0800

    Add tests for GMT and GMT+7 timezones.

diff --git a/libgnucash/engine/test/gtest-gnc-timezone.cpp b/libgnucash/engine/test/gtest-gnc-timezone.cpp
index 0703333..d499544 100644
--- a/libgnucash/engine/test/gtest-gnc-timezone.cpp
+++ b/libgnucash/engine/test/gtest-gnc-timezone.cpp
@@ -67,10 +67,35 @@ TEST(gnc_timezone_constructors, test_posix_timezone)
     TZ_Ptr tz = tzp.get(2006);
     EXPECT_EQ(tz->std_zone_abbrev(), "FST");
     EXPECT_EQ(tz->dst_zone_abbrev(), "FDT");
+    EXPECT_TRUE(tz->has_dst());
     EXPECT_EQ(tz->base_utc_offset().hours(), 8L);
     EXPECT_EQ(tz->dst_offset().hours(), 7L);
 }
 
+TEST(gnc_timezone_constructors, test_gmt_timezone)
+{
+    std::string timezone("GMT");
+    TimeZoneProvider tzp(timezone);
+    TZ_Ptr tz = tzp.get(2006);
+    EXPECT_EQ(tz->std_zone_abbrev(), "GMT");
+    EXPECT_FALSE(tz->has_dst());
+    EXPECT_EQ(tz->dst_zone_abbrev(), "");
+    EXPECT_EQ(tz->base_utc_offset().hours(), 0L);
+    EXPECT_EQ(tz->dst_offset().hours(), 0L);
+}
+
+TEST(gnc_timezone_constructors, test_GMT_plus_7_timezone)
+{
+    std::string timezone("Etc/GMT+7");
+    TimeZoneProvider tzp(timezone);
+    TZ_Ptr tz = tzp.get(2006);
+    EXPECT_EQ(tz->std_zone_abbrev(), "-07");
+    EXPECT_EQ(tz->dst_zone_abbrev(), "");
+    EXPECT_FALSE(tz->has_dst());
+    EXPECT_EQ(tz->base_utc_offset().hours(), -7);
+    EXPECT_EQ(tz->dst_offset().hours(), 0L);
+}
+
 TEST(gnc_timezone_constructors, test_IANA_Belize_tz)
 {
     TimeZoneProvider tzp("America/Belize");

commit 5af21dfad816a2a41594c02512b1dfc97818f60a
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Dec 18 09:48:17 2017 -0800

    Fix timezone constructor crash when zone file has no transitions.

diff --git a/libgnucash/engine/gnc-timezone.cpp b/libgnucash/engine/gnc-timezone.cpp
index 0a26b48..96f164c 100644
--- a/libgnucash/engine/gnc-timezone.cpp
+++ b/libgnucash/engine/gnc-timezone.cpp
@@ -677,8 +677,9 @@ TimeZoneProvider::parse_file(const std::string& tzname)
 /* if the transitions end before the end of the zoneinfo coverage
  * period then the zone rescinded DST and we need a final no-dstzone.
  */
-    if (last_time.is_not_a_date_time() ||
-        last_time.date().year() < parser.last_year)
+    if (last_time.is_not_a_date_time())
+        zone_vector.push_back(zone_no_dst(max_year, last_info));
+    else if (last_time.date().year() < parser.last_year)
         zone_vector.push_back(zone_no_dst(last_time.date().year(), last_info));
 }
 

commit 5aa84e13d610525410b031031960a7830d806e59
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Dec 14 20:37:12 2017 +0000

    Change the way the import settings are handled

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index 65082be..03c6195 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -18,6 +18,8 @@ SET(csv_import_SOURCES
   gnc-csv-gnumeric-popup.c
   gnc-csv-tokenizer.cpp
   gnc-csv-import-settings.cpp
+  gnc-csv-price-import-settings.cpp
+  gnc-csv-trans-import-settings.cpp
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
   gnc-price-import.cpp
@@ -46,6 +48,8 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-gnumeric-popup.h
   gnc-csv-tokenizer.hpp
   gnc-csv-import-settings.hpp
+  gnc-csv-price-import-settings.hpp
+  gnc-csv-trans-import-settings.hpp
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
   gnc-price-import.hpp
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index f1b501f..b4ab800 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -19,7 +19,9 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
   gnc-trans-props.cpp \
-  gnc-csv-import-settings.cpp
+  gnc-csv-import-settings.cpp \
+  gnc-csv-price-import-settings.cpp \
+  gnc-csv-trans-import-settings.cpp
 
 noinst_HEADERS = \
   assistant-csv-account-import.h \
@@ -37,7 +39,9 @@ noinst_HEADERS = \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
   gnc-trans-props.hpp \
-  gnc-csv-import-settings.hpp
+  gnc-csv-import-settings.hpp \
+  gnc-csv-price-import-settings.hpp \
+  gnc-csv-trans-import-settings.hpp
 
 libgncmod_csv_import_la_LDFLAGS = -avoid-version
 
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 8bb371b..0911233 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -52,7 +52,7 @@ extern "C"
 #include "go-charmap-sel.h"
 }
 
-#include "gnc-csv-import-settings.hpp"
+#include "gnc-csv-price-import-settings.hpp"
 #include "gnc-price-import.hpp"
 #include "gnc-fw-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -64,8 +64,6 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
-const std::string settings_type = "PRICE";
-
 class  CsvImpPriceAssist
 {
 public:
@@ -738,7 +736,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo()
     gtk_list_store_clear (GTK_LIST_STORE(model));
 
     // Append the default entry
-    auto presets = get_import_presets (settings_type);
+    auto presets = get_import_presets_price ();
     for (auto preset : presets)
     {
         GtkTreeIter iter;
@@ -766,7 +764,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
     /* Handle sensitivity of the delete and save button */
     if (gtk_combo_box_get_active_iter (combo, &iter))
     {
-        CsvImportSettings *preset;
+        CsvPriceImpSettings *preset;
         GtkTreeModel *model = gtk_combo_box_get_model (combo);
         gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -809,7 +807,7 @@ CsvImpPriceAssist::preview_settings_load ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvImportSettings *preset = nullptr;
+    CsvPriceImpSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -836,7 +834,7 @@ CsvImpPriceAssist::preview_settings_delete ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvImportSettings *preset = nullptr;
+    CsvPriceImpSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -870,7 +868,7 @@ CsvImpPriceAssist::preview_settings_save ()
         while (valid)
         {
             // Walk through the list, reading each row
-            CsvImportSettings *preset;
+            CsvPriceImpSettings *preset;
             gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
             if (preset && (preset->m_name == std::string(new_name)))
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index 41779c6..5347393 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -58,7 +58,7 @@ extern "C"
 #include "go-charmap-sel.h"
 }
 
-#include "gnc-csv-import-settings.hpp"
+#include "gnc-csv-trans-import-settings.hpp"
 #include "gnc-tx-import.hpp"
 #include "gnc-fw-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -70,8 +70,6 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
-const std::string settings_type = "TRANS";
-
 class  CsvImpTransAssist
 {
 public:
@@ -678,8 +676,7 @@ void CsvImpTransAssist::preview_populate_settings_combo()
     gtk_list_store_clear (GTK_LIST_STORE(model));
 
     // Append the default entry
-
-    auto presets = get_import_presets (settings_type);
+    auto presets = get_import_presets_trans ();
     for (auto preset : presets)
     {
         GtkTreeIter iter;
@@ -707,7 +704,7 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
     /* Handle sensitivity of the delete and save button */
     if (gtk_combo_box_get_active_iter (combo, &iter))
     {
-        CsvImportSettings *preset;
+        CsvTransImpSettings *preset;
         GtkTreeModel *model = gtk_combo_box_get_model (combo);
         gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -736,7 +733,7 @@ CsvImpTransAssist::preview_settings_name (GtkEntry* entry)
 
     auto box = gtk_widget_get_parent (GTK_WIDGET(entry));
     auto combo = gtk_widget_get_parent (GTK_WIDGET(box));
-    
+
     preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo));
 }
 
@@ -752,7 +749,7 @@ CsvImpTransAssist::preview_settings_load ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvImportSettings *preset = nullptr;
+    CsvTransImpSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -779,7 +776,7 @@ CsvImpTransAssist::preview_settings_delete ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvImportSettings *preset = nullptr;
+    CsvTransImpSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -812,7 +809,7 @@ CsvImpTransAssist::preview_settings_save ()
         while (valid)
         {
             // Walk through the list, reading each row
-            CsvImportSettings *preset;
+            CsvTransImpSettings *preset;
             gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
             if (preset && (preset->m_name == std::string(new_name)))
diff --git a/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
index 0096c53..7c5e362 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
@@ -1,5 +1,5 @@
 /*******************************************************************\
- * gnc-csv-import-settings.c -- Save and Load CSV Import Settings   *
+ * gnc-csv-import-settings.cpp -- Save and Load CSV Import Settings *
  *                                                                  *
  * Copyright (C) 2014 Robert Fewell                                 *
  *                                                                  *
@@ -20,7 +20,7 @@
  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
-/** @file gnc-csv-import-settings.c
+/** @file gnc-csv-import-settings.cpp
     @brief CSV Import Settings
     @author Copyright (c) 2014 Robert Fewell
     @author Copyright (c) 2016 Geert Janssens
@@ -44,12 +44,12 @@ 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_SKIP_ALT     "SkipAltLines"
 #define CSV_SKIP_START   "SkipStartLines"
 #define CSV_SKIP_END     "SkipEndLines"
-#define CSV_MULTI_SPLIT  "MultiSplit"
 
 #define CSV_SEP          "Separators"
 
@@ -60,121 +60,10 @@ const std::string gnc_exp{N_("GnuCash Export Format")};
 #define CSV_CURRENCY     "CurrencyFormat"
 
 #define CSV_ENCODING     "Encoding"
-#define CSV_COL_TYPES    "ColumnTypes"
 #define CSV_COL_WIDTHS   "ColumnWidths"
-#define CSV_ACCOUNT      "BaseAccount"
-#define CSV_TO_CURR      "PriceToCurrency"
-#define CSV_FROM_COMM    "PriceFromCommodity"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
-preset_vec presets;
-
-static std::shared_ptr<CsvImportSettings> create_int_no_preset(const std::string& set_type)
-{
-    auto preset = std::make_shared<CsvImportSettings>();
-    preset->m_name = no_settings;
-    preset->m_settings_type = set_type;
-
-    return preset;
-}
-
-static std::shared_ptr<CsvImportSettings> create_int_gnc_exp_preset(void)
-{
-    auto preset = std::make_shared<CsvImportSettings>();
-    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
-     * had set as global preference.
-    preset->date_active = 0;
-    preset->currency_active = 0;
-    */
-    preset->m_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
- * based on settings type.
- **************************************************/
-const preset_vec& get_import_presets (const std::string& set_type)
-{
-
-    // Search all Groups in the state key file for ones starting with prefix
-    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++)
-    {
-        auto group = std::string(groups[i]);
-        auto gp = csv_group_prefix + set_type + " - ";
-        auto pos = group.find(gp);
-        if (pos == std::string::npos)
-            continue;
-
-        preset_names.push_back(group.substr(gp.size()));
-    }
-    // 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(set_type));
-
-    if (set_type.compare("TRANS") == 0)
-        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<CsvImportSettings>();
-        preset->m_settings_type = set_type;
-        preset->m_name = preset_name;
-        preset->load();
-        presets.push_back(preset);
-    }
-    return presets;
-}
-
-bool 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())));
-}
-
-
 /**************************************************
  * handle_load_error
  *
@@ -182,7 +71,7 @@ bool preset_is_reserved_name (const std::string& name)
  * ignore key-not-found errors though. We'll just
  * use a default value and go on.
  **************************************************/
-static bool
+bool
 handle_load_error (GError **key_error, const std::string& group)
 {
     if (!*key_error)
@@ -199,17 +88,37 @@ handle_load_error (GError **key_error, const std::string& group)
     return true;
 }
 
+bool 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())));
+}
+
+std::string get_no_settings (void)
+{
+    return no_settings;
+}
+
+std::string get_gnc_exp (void)
+{
+    return gnc_exp;
+}
+
+std::string get_prefix (void)
+{
+    return csv_group_prefix;
+}
+
 /**************************************************
- * load
+ * load_common
  *
  * load the settings from a state key file
  **************************************************/
 bool
-CsvImportSettings::load (void)
+CsvImportSettings::load_common (void)
 {
-    if (preset_is_reserved_name (m_name))
-        return true;
-
     GError *key_error = nullptr;
     m_load_error = false;
     auto group = csv_group_prefix + m_settings_type + " - " + m_name;
@@ -224,9 +133,6 @@ CsvImportSettings::load (void)
     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);
 
-    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
     m_load_error |= handle_load_error (&key_error, group);
@@ -257,82 +163,8 @@ CsvImportSettings::load (void)
     if (key_char)
         g_free (key_char);
 
-    gsize list_len;
-
-    // Transactions
-    if (m_settings_type.compare("TRANS") == 0)
-    {
-        key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
-        if (key_char && *key_char != '\0')
-            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);
-
-        m_column_types.clear();
-        gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
-                &list_len, &key_error);
-        for (uint32_t i = 0; i < list_len; i++)
-        {
-            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())
-            {
-                /* 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, 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, m_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);
-    }
-
-    // Price
-    if (m_settings_type.compare("PRICE") == 0)
-    {
-        key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
-        if (key_char && *key_char != '\0')
-            m_to_currency = parse_commodity_price_comm (key_char);
-        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_FROM_COMM, &key_error);
-        if (key_char && *key_char != '\0')
-            m_from_commodity = parse_commodity_price_comm (key_char);
-        m_load_error |= handle_load_error (&key_error, group);
-        if (key_char)
-            g_free (key_char);
-
-        m_column_types.clear();
-        gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
-                &list_len, &key_error);
-        for (uint32_t i = 0; i < list_len; i++)
-        {
-            auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(),
-                    gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i]));
-            if (col_types_it != gnc_price_col_type_strs.end())
-            {
-                // Found a valid column type
-                m_column_types_price.push_back(col_types_it->first);
-            }
-            else
-                PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
-                        col_types_str_price[i]);
-        }
-        if (col_types_str_price)
-            g_strfreev (col_types_str_price);
-    }
-
     // Widths
+    gsize list_len;
     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);
@@ -348,35 +180,18 @@ CsvImportSettings::load (void)
     return m_load_error;
 }
 
-
 /**************************************************
- * save
+ * save_common
  *
  * save settings to a key file
  **************************************************/
 bool
-CsvImportSettings::save (void)
+CsvImportSettings::save_common (void)
 {
-    if (preset_is_reserved_name (m_name))
-    {
-        PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
-        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_settings_type + " - " + m_name;
 
-    // Drop previous saved settings with this name
-    g_key_file_remove_group (keyfile, group.c_str(), nullptr);
-
-    // Start Saving the settings
-    // Common
+    // Start Saving the Common settings
     g_key_file_set_string (keyfile, group.c_str(), CSV_NAME, m_name.c_str());
 
     g_key_file_set_integer (keyfile, group.c_str(), CSV_SKIP_START, m_skip_start_lines);
@@ -394,8 +209,7 @@ CsvImportSettings::save (void)
                     [&cmt_ss, &fmt_num](const GncDateFormat& fmt)
                         { cmt_ss << fmt_num++ << ": '" << fmt.m_fmt << "', "; });
     auto cmt = cmt_ss.str().substr(0, static_cast<long>(cmt_ss.tellp()) - 2);
-    g_key_file_set_comment (keyfile, group.c_str(), CSV_DATE,
-                            cmt.c_str(), nullptr);
+    g_key_file_set_comment (keyfile, group.c_str(), CSV_DATE, cmt.c_str(), nullptr);
     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());
 
@@ -403,51 +217,6 @@ CsvImportSettings::save (void)
         g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
                 (gint*)(m_column_widths.data()), m_column_widths.size());
 
-    // Transaction
-    if (m_settings_type.compare("TRANS") == 0)
-    {
-        g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split);
-
-        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 : 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());
-    }
-
-    // Price
-    if (m_settings_type.compare("PRICE") == 0)
-    {
-        if (m_to_currency)
-        {
-            auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::",
-                               gnc_commodity_get_mnemonic (m_to_currency), nullptr);
-            g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name);
-            g_free (unique_name);
-        }
-
-        if (m_from_commodity)
-        {
-            auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::",
-                               gnc_commodity_get_mnemonic (m_from_commodity), nullptr);
-            g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name);
-            g_free (unique_name);
-        }
-
-        std::vector<const char*> col_types_str_price;
-        for (auto col_type : m_column_types_price)
-            col_types_str_price.push_back(gnc_price_col_type_strs[col_type]);
-
-        if (!col_types_str_price.empty())
-            g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
-                    col_types_str_price.data(), col_types_str_price.size());
-    }
-
     // Do a test read of encoding
     GError *key_error = nullptr;
     bool error = false;
@@ -460,33 +229,20 @@ CsvImportSettings::save (void)
     {
         if (key_error)
         {
-            g_warning ("Error reading group %s key %s: %s", group.c_str(), CSV_COL_TYPES, key_error->message);
+            g_warning ("Error reading group %s key %s: %s", group.c_str(), CSV_ENCODING, 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, enc_str.c_str(), group.c_str());
+            g_warning ("Error comparing group %s key %s: '%s' and '%s'", group.c_str(), CSV_ENCODING, enc_str.c_str(), group.c_str());
         error = true;
     }
     return error;
 }
 
 void
-CsvImportSettings::remove (void)
+CsvImportSettings::remove_common (void)
 {
-    if (preset_is_reserved_name (m_name))
-        return;
-
     auto keyfile = gnc_state_get_current ();
     auto group = csv_group_prefix + m_settings_type + " - " + m_name;
     g_key_file_remove_group (keyfile, group.c_str(), nullptr);
 }
-
-
-bool
-CsvImportSettings::read_only (void)
-{
-    return ((m_name == no_settings) ||
-            (m_name == _(no_settings.c_str())) ||
-            (m_name == gnc_exp) ||
-            (m_name == _(gnc_exp.c_str())));
-}
diff --git a/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
index e6ce4d4..6102fed 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
@@ -1,5 +1,5 @@
 /*******************************************************************\
- * gnc-csv-import-settings.h  -- Save and Load CSV Import Settings  *
+ * gnc-csv-import-settings.hpp -- Save and Load CSV Import Settings *
  *                                                                  *
  * Copyright (C) 2014 Robert Fewell                                 *
  *                                                                  *
@@ -20,7 +20,7 @@
  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
-/** @file gnc-csv-import-settings.h
+/** @file gnc-csv-import-settings.hpp
     @brief CSV Import Settings
     @author Copyright (c) 2014 Robert Fewell
     @author Copyright (c) 2016 Geert Janssens
@@ -36,8 +36,8 @@ extern "C" {
 
 #include <string>
 #include <vector>
-#include "gnc-trans-props.hpp"
-#include "gnc-price-props.hpp"
+#include <boost/optional.hpp>
+#include <gnc-datetime.hpp>
 #include "gnc-tokenizer.hpp"
 
 /** Enumeration for separator checkbutton types. These are the
@@ -52,34 +52,25 @@ enum SETTINGS_COL {SET_GROUP, SET_NAME};
 struct CsvImportSettings
 {
     CsvImportSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"},
-            m_multi_split (false), m_date_format {0}, m_currency_format {0},
+            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_load_error {false}, m_base_account {nullptr},
-            m_from_commodity {nullptr}, m_to_currency {nullptr} { }
+            m_separators {","}, m_load_error {false} { }
 
 /** Save the gathered widget properties to a key File.
  *
  *  @return true if there was a problem in saving.
  */
-bool save (void);
+bool save_common (void);
 
 /** Load the widget properties from a key File.
  *
  *  @return true if there was a problem.
  */
-bool load (void);
+bool load_common (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.
- *
- *  @return true if there was a problem.
- */
-bool read_only (void);
+void remove_common (void);
 
 std::string   m_settings_type;                // Settings Type, TRANS, PRICE etc.
 
@@ -87,7 +78,6 @@ std::string   m_settings_type;                // Settings Type, TRANS, PRICE etc
 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
 uint32_t      m_skip_start_lines;             // Number of header rows to skip
@@ -96,28 +86,11 @@ bool          m_skip_alt_lines;               // Skip alternate rows
 std::string   m_separators;                   // Separators for csv format
 bool          m_load_error;                   // Was there an error while parsing the state file ?
 std::vector<uint32_t> m_column_widths;        // The Column widths
-
-// Transaction Settings
-Account      *m_base_account;                 // Base account
-std::vector<GncTransPropType> m_column_types; // The Column types in order
-
-// Price Settings
-gnc_commodity *m_from_commodity;              //  Price From Commodity
-gnc_commodity *m_to_currency;                 //  Price To Currency
-std::vector<GncPricePropType> m_column_types_price; // The Price Column types in order
 };
 
-using preset_vec = std::vector<std::shared_ptr<CsvImportSettings>>;
-/** Creates a vector of CsvImportSettings which combines
- *  - one or more internally defined presets
- *  - all preset found in the state key file.
- *
- *  @param set_type The type of setting stored in the
- *  key file, TRANS, PRICE, etc.
- *
- *  @return a reference to the populated vector.
- */
-const preset_vec& get_import_presets (const std::string& set_type);
+std::string get_no_settings (void);
+std::string get_gnc_exp (void);
+std::string get_prefix (void);
 
 /** Check whether name can be used as a preset name.
  *  The names of the internal presets are considered reserved.
@@ -125,4 +98,14 @@ const preset_vec& get_import_presets (const std::string& set_type);
  */
 bool preset_is_reserved_name (const std::string& name);
 
+/**************************************************
+ * 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.
+ **************************************************/
+bool
+handle_load_error (GError **key_error, const std::string& group);
+
 #endif
diff --git a/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp
new file mode 100644
index 0000000..8ebe9eb
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp
@@ -0,0 +1,258 @@
+/*******************************************************************\
+ * gnc-csv-price-import-settings.cpp -- Price CSV Import Settings   *
+ *                                                                  *
+ * Copyright (C) 2017 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-price-import-settings.cpp
+    @brief CSV Import Settings
+    @author Copyright (c) 2014 Robert Fewell
+    @author Copyright (c) 2016 Geert Janssens
+*/
+
+#include "gnc-csv-import-settings.hpp"
+#include "gnc-csv-price-import-settings.hpp"
+#include <sstream>
+
+extern "C"
+{
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "Account.h"
+#include "gnc-state.h"
+#include "gnc-ui-util.h"
+}
+
+const std::string settings_type{"PRICE"};
+
+#define CSV_COL_TYPES    "ColumnTypes"
+
+#define CSV_TO_CURR      "PriceToCurrency"
+#define CSV_FROM_COMM    "PriceFromCommodity"
+
+G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
+
+preset_vec_price presets_price;
+
+static std::shared_ptr<CsvPriceImpSettings> create_int_no_preset(void)
+{
+    auto preset = std::make_shared<CsvPriceImpSettings>();
+    preset->m_name = get_no_settings();
+    preset->m_settings_type = settings_type;
+
+    return preset;
+}
+
+static std::shared_ptr<CsvPriceImpSettings> create_int_gnc_exp_preset(void)
+{
+    auto preset = std::make_shared<CsvPriceImpSettings>();
+    preset->m_name = get_gnc_exp();
+    preset->m_skip_start_lines = 1;
+
+    /* 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 global preference.
+    preset->date_active = 0;
+    preset->currency_active = 0;
+    */
+    preset->m_column_types_price = {
+            GncPricePropType::DATE,
+            GncPricePropType::AMOUNT,
+            GncPricePropType::FROM_COMMODITY,
+            GncPricePropType::TO_CURRENCY
+    };
+    return preset;
+}
+
+/**************************************************
+ * find
+ *
+ * find all settings entries in the state key file
+ * based on settings type.
+ **************************************************/
+const preset_vec_price& get_import_presets_price (void)
+{
+    // Search all Groups in the state key file for ones starting with prefix
+    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++)
+    {
+        auto group = std::string(groups[i]);
+        auto gp = get_prefix() + settings_type + " - ";
+        auto pos = group.find(gp);
+        if (pos == std::string::npos)
+            continue;
+
+        preset_names.push_back(group.substr(gp.size()));
+    }
+    // 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_price.clear();
+
+    /* Start with the internally generated ones */
+    presets_price.push_back(create_int_no_preset());
+    //presets_price.push_back(create_int_gnc_exp_preset()); // Not Required
+
+    /* Then add all the ones we found in the state file */
+    for (auto preset_name : preset_names)
+    {
+        auto preset = std::make_shared<CsvPriceImpSettings>();
+        preset->m_settings_type = settings_type;
+        preset->m_name = preset_name;
+        preset->load();
+        presets_price.push_back(preset);
+    }
+    return presets_price;
+}
+
+/**************************************************
+ * load
+ *
+ * load the settings from a state key file
+ **************************************************/
+bool
+CsvPriceImpSettings::load (void)
+{
+    if (preset_is_reserved_name (m_name))
+        return true;
+
+    GError *key_error = nullptr;
+    m_load_error = false;
+    auto keyfile = gnc_state_get_current ();
+    auto group = get_prefix() + m_settings_type + " - " + m_name;
+
+    // Start Loading the settings
+    m_load_error = load_common(); // load the common settings
+
+    gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
+    if (key_char && *key_char != '\0')
+        m_to_currency = parse_commodity_price_comm (key_char);
+    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_FROM_COMM, &key_error);
+    if (key_char && *key_char != '\0')
+        m_from_commodity = parse_commodity_price_comm (key_char);
+    m_load_error |= handle_load_error (&key_error, group);
+    if (key_char)
+        g_free (key_char);
+
+    gsize list_len;
+    m_column_types_price.clear();
+    gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+            &list_len, &key_error);
+    for (uint32_t i = 0; i < list_len; i++)
+    {
+        auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(),
+                gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i]));
+        if (col_types_it != gnc_price_col_type_strs.end())
+        {
+            // Found a valid column type
+            m_column_types_price.push_back(col_types_it->first);
+        }
+        else
+            PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
+                    col_types_str_price[i]);
+    }
+    if (col_types_str_price)
+        g_strfreev (col_types_str_price);
+
+    return m_load_error;
+}
+
+/**************************************************
+ * save
+ *
+ * save settings to a key file
+ **************************************************/
+bool
+CsvPriceImpSettings::save (void)
+{
+    if (preset_is_reserved_name (m_name))
+    {
+        PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
+        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 = get_prefix() + m_settings_type + " - " + m_name;
+
+    // Drop previous saved settings with this name
+    g_key_file_remove_group (keyfile, group.c_str(), nullptr);
+
+    // Start Saving the settings
+    bool error = save_common(); // save the common settings
+
+    if (error)
+        return error;
+
+    if (m_to_currency)
+    {
+        auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::",
+                           gnc_commodity_get_mnemonic (m_to_currency), nullptr);
+        g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name);
+        g_free (unique_name);
+    }
+
+    if (m_from_commodity)
+    {
+        auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::",
+                           gnc_commodity_get_mnemonic (m_from_commodity), nullptr);
+        g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name);
+        g_free (unique_name);
+    }
+
+    std::vector<const char*> col_types_str_price;
+    for (auto col_type : m_column_types_price)
+        col_types_str_price.push_back(gnc_price_col_type_strs[col_type]);
+
+    if (!col_types_str_price.empty())
+        g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+                col_types_str_price.data(), col_types_str_price.size());
+
+    return error;
+}
+
+void
+CsvPriceImpSettings::remove (void)
+{
+    if (preset_is_reserved_name (m_name))
+        return;
+
+    remove_common();
+}
diff --git a/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.hpp
new file mode 100644
index 0000000..e0d52b4
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-csv-price-import-settings.hpp
@@ -0,0 +1,79 @@
+/*******************************************************************\
+ * gnc-csv-price-import-settings.hpp  -- Price CSV Import Settings  *
+ *                                                                  *
+ * Copyright (C) 2017 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-price-import-settings.hpp
+    @brief CSV Import Settings
+    @author Copyright (c) 2014 Robert Fewell
+    @author Copyright (c) 2016 Geert Janssens
+*/
+#ifndef GNC_CSV_PRICE_IMPORT_SETTINGS_H
+#define GNC_CSV_PRICE_IMPORT_SETTINGS_H
+
+extern "C" {
+#include <config.h>
+#include "Account.h"
+#include "gnc-commodity.h"
+}
+
+#include <string>
+#include <vector>
+#include "gnc-price-props.hpp"
+#include "gnc-tokenizer.hpp"
+#include "gnc-csv-import-settings.hpp"
+
+struct CsvPriceImpSettings : public CsvImportSettings
+{
+    CsvPriceImpSettings() : m_from_commodity {nullptr}, m_to_currency {nullptr} { }
+
+/** Save the gathered widget properties to a key File.
+ *
+ *  @return true if there was a problem in saving.
+ */
+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);
+
+// Price Settings
+gnc_commodity *m_from_commodity;                    //  Price From Commodity
+gnc_commodity *m_to_currency;                       //  Price To Currency
+std::vector<GncPricePropType> m_column_types_price; // The Price Column types in order
+};
+
+using preset_vec_price = std::vector<std::shared_ptr<CsvPriceImpSettings>>;
+
+/** Creates a vector of CsvPriceImpSettings 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_price& get_import_presets_price (void);
+
+#endif
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp
new file mode 100644
index 0000000..17ac2b7
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp
@@ -0,0 +1,262 @@
+/*******************************************************************\
+ * gnc-csv-trans-import-settings.cpp -- Trans 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-import-settings.cpp
+    @brief CSV Import Settings
+    @author Copyright (c) 2014 Robert Fewell
+    @author Copyright (c) 2016 Geert Janssens
+*/
+
+#include "gnc-csv-import-settings.hpp"
+#include "gnc-csv-trans-import-settings.hpp"
+#include <sstream>
+
+extern "C"
+{
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "Account.h"
+#include "gnc-state.h"
+#include "gnc-ui-util.h"
+}
+
+const std::string settings_type{"TRANS"};
+
+#define CSV_COL_TYPES    "ColumnTypes"
+
+#define CSV_ACCOUNT      "BaseAccount"
+#define CSV_MULTI_SPLIT  "MultiSplit"
+
+G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
+
+preset_vec_trans presets_trans;
+
+static std::shared_ptr<CsvTransImpSettings> create_int_no_preset(void)
+{
+    auto preset = std::make_shared<CsvTransImpSettings>();
+    preset->m_name = get_no_settings();
+    preset->m_settings_type = settings_type;
+
+    return preset;
+}
+
+static std::shared_ptr<CsvTransImpSettings> create_int_gnc_exp_preset(void)
+{
+    auto preset = std::make_shared<CsvTransImpSettings>();
+    preset->m_name = get_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
+     * had set as global preference.
+    preset->date_active = 0;
+    preset->currency_active = 0;
+    */
+    preset->m_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
+ * based on settings type.
+ **************************************************/
+const preset_vec_trans& get_import_presets_trans (void)
+{
+    // Search all Groups in the state key file for ones starting with prefix
+    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++)
+    {
+        auto group = std::string(groups[i]);
+        auto gp = get_prefix() + settings_type + " - ";
+        auto pos = group.find(gp);
+        if (pos == std::string::npos)
+            continue;
+
+        preset_names.push_back(group.substr(gp.size()));
+    }
+    // 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_trans.clear();
+
+    /* Start with the internally generated ones */
+    presets_trans.push_back(create_int_no_preset());
+    presets_trans.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<CsvTransImpSettings>();
+        preset->m_settings_type = settings_type;
+        preset->m_name = preset_name;
+        preset->load();
+        presets_trans.push_back(preset);
+    }
+    return presets_trans;
+}
+
+/**************************************************
+ * load
+ *
+ * load the settings from a state key file
+ **************************************************/
+bool
+CsvTransImpSettings::load (void)
+{
+    if (preset_is_reserved_name (m_name))
+        return true;
+
+    GError *key_error = nullptr;
+    m_load_error = false;
+    auto keyfile = gnc_state_get_current ();
+    auto group = get_prefix() + m_settings_type + " - " + m_name;
+
+    // Start Loading the settings
+    m_load_error = load_common(); // load the common settings
+
+    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);
+
+    gchar *key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
+    if (key_char && *key_char != '\0')
+        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);
+
+    gsize list_len;
+    m_column_types.clear();
+    gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+            &list_len, &key_error);
+    for (uint32_t i = 0; i < list_len; i++)
+    {
+        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())
+        {
+            /* 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, 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, m_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);
+
+    return m_load_error;
+}
+
+/**************************************************
+ * save
+ *
+ * save settings to a key file
+ **************************************************/
+bool
+CsvTransImpSettings::save (void)
+{
+    if (preset_is_reserved_name (m_name))
+    {
+        PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
+        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 = get_prefix() + m_settings_type + " - " + m_name;
+
+    // Drop previous saved settings with this name
+    g_key_file_remove_group (keyfile, group.c_str(), nullptr);
+
+    // Start Saving the settings
+    bool error = save_common(); // save the common settings
+
+    if (error)
+        return error;
+
+    g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split);
+
+    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 : 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());
+
+    return error;
+}
+
+void
+CsvTransImpSettings::remove (void)
+{
+    if (preset_is_reserved_name (m_name))
+        return;
+
+    remove_common();
+}
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.hpp
new file mode 100644
index 0000000..c93ef3a
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.hpp
@@ -0,0 +1,79 @@
+/*******************************************************************\
+ * gnc-csv-trans-import-settings.hpp  -- Trans CSV Import Settings  *
+ *                                                                  *
+ * Copyright (C) 2017 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-import-settings.hpp
+    @brief CSV Import Settings
+    @author Copyright (c) 2014 Robert Fewell
+    @author Copyright (c) 2016 Geert Janssens
+*/
+#ifndef GNC_CSV_TRANS_IMPORT_SETTINGS_H
+#define GNC_CSV_TRANS_IMPORT_SETTINGS_H
+
+extern "C" {
+#include <config.h>
+#include "Account.h"
+#include "gnc-commodity.h"
+}
+
+#include <string>
+#include <vector>
+#include "gnc-trans-props.hpp"
+#include "gnc-tokenizer.hpp"
+#include "gnc-csv-import-settings.hpp"
+
+struct CsvTransImpSettings : public CsvImportSettings
+{
+    CsvTransImpSettings() : m_base_account {nullptr}, m_multi_split (false) { }
+
+/** Save the gathered widget properties to a key File.
+ *
+ *  @return true if there was a problem in saving.
+ */
+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);
+
+// Transaction Settings
+Account      *m_base_account;                 // Base account
+bool          m_multi_split;                  // Assume multiple lines per transaction
+std::vector<GncTransPropType> m_column_types; // The Column types in order
+};
+
+using preset_vec_trans = std::vector<std::shared_ptr<CsvTransImpSettings>>;
+
+/** Creates a vector of CsvTransImpSettings 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_trans& get_import_presets_trans (void);
+
+#endif
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index df91261..0bc1ead 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -42,7 +42,7 @@ extern "C" {
 #include "gnc-price-props.hpp"
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
-#include "gnc-csv-import-settings.hpp"
+#include "gnc-csv-price-import-settings.hpp"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -272,7 +272,7 @@ void GncPriceImport::separators (std::string separators)
 }
 std::string GncPriceImport::separators () { return m_settings.m_separators; }
 
-void GncPriceImport::settings (const CsvImportSettings& settings)
+void GncPriceImport::settings (const CsvPriceImpSettings& settings)
 {
     /* First apply file format as this may recreate the tokenizer */
     file_format (settings.m_file_format);
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index c91943f..7d181ec 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -42,7 +42,7 @@ extern "C" {
 
 #include "gnc-tokenizer.hpp"
 #include "gnc-price-props.hpp"
-#include "gnc-csv-import-settings.hpp"
+#include "gnc-csv-price-import-settings.hpp"
 #include <boost/optional.hpp>
 
 /* A set of currency formats that the user sees. */
@@ -118,7 +118,7 @@ public:
     void separators (std::string separators);
     std::string separators ();
 
-    void settings (const CsvImportSettings& settings);
+    void settings (const CsvPriceImpSettings& settings);
     bool save_settings ();
 
     void settings_name (std::string name);
@@ -164,7 +164,7 @@ private:
      */
     void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type);
 
-    CsvImportSettings m_settings;
+    CsvPriceImpSettings m_settings;
     bool m_skip_errors;
     bool m_over_write;
 };
diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.cpp b/gnucash/import-export/csv-imp/gnc-tx-import.cpp
index eddbd75..a43f29c 100644
--- a/gnucash/import-export/csv-imp/gnc-tx-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-tx-import.cpp
@@ -39,7 +39,7 @@ extern "C" {
 #include "gnc-trans-props.hpp"
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
-#include "gnc-csv-import-settings.hpp"
+#include "gnc-csv-trans-import-settings.hpp"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -293,7 +293,7 @@ void GncTxImport::separators (std::string separators)
 }
 std::string GncTxImport::separators () { return m_settings.m_separators; }
 
-void GncTxImport::settings (const CsvImportSettings& settings)
+void GncTxImport::settings (const CsvTransImpSettings& settings)
 {
     /* First apply file format as this may recreate the tokenizer */
     file_format (settings.m_file_format);
diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.hpp b/gnucash/import-export/csv-imp/gnc-tx-import.hpp
index 87e4ea4..4fc339c 100644
--- a/gnucash/import-export/csv-imp/gnc-tx-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-tx-import.hpp
@@ -43,7 +43,7 @@ extern "C" {
 
 #include "gnc-tokenizer.hpp"
 #include "gnc-trans-props.hpp"
-#include "gnc-csv-import-settings.hpp"
+#include "gnc-csv-trans-import-settings.hpp"
 #include <boost/optional.hpp>
 
 
@@ -136,7 +136,7 @@ public:
     void separators (std::string separators);
     std::string separators ();
 
-    void settings (const CsvImportSettings& settings);
+    void settings (const CsvTransImpSettings& settings);
     bool save_settings ();
 
     void settings_name (std::string name);
@@ -189,8 +189,8 @@ private:
     void update_pre_trans_props (uint32_t row, uint32_t col, GncTransPropType prop_type);
     void update_pre_split_props (uint32_t row, uint32_t col, GncTransPropType prop_type);
 
-    struct CsvTranSettings;
-    CsvImportSettings m_settings;
+    struct CsvTranImpSettings; //FIXME do we need this line
+    CsvTransImpSettings m_settings;
     bool m_skip_errors;
     bool m_req_mapped_accts;
 

commit ba5ca5bd551a6bf4f8066a4533a44e792fbe09d1
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon Dec 11 09:53:20 2017 +0000

    Remove surplus statement

diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 360859d..c91943f 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -164,7 +164,6 @@ private:
      */
     void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type);
 
-    struct CsvTranSettings;
     CsvImportSettings m_settings;
     bool m_skip_errors;
     bool m_over_write;

commit 0534ba4f8b9a69d39a524be282b2bcd737594f33
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sun Dec 10 11:59:40 2017 +0000

    Update file with changes for transient dialog changes

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 398a52c..8bb371b 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -704,13 +704,13 @@ CsvImpPriceAssist::file_confirm_cb ()
     catch (std::ifstream::failure& e)
     {
         /* File loading failed ... */
-        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what());
+        gnc_error_dialog (GTK_WINDOW(csv_imp_asst), "%s", e.what());
         return;
     }
     catch (std::range_error &e)
     {
         /* Parsing failed ... */
-        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what());
+        gnc_error_dialog (GTK_WINDOW(csv_imp_asst), "%s", e.what());
         return;
     }
     /* Get settings store and populate */
@@ -818,7 +818,7 @@ CsvImpPriceAssist::preview_settings_load ()
 
     price_imp->settings (*preset);
     if (preset->m_load_error)
-        gnc_error_dialog (GTK_WIDGET(csv_imp_asst),
+        gnc_error_dialog (GTK_WINDOW(csv_imp_asst),
             "%s", _("There were problems reading some saved settings, continuing to load.\n"
                     "Please review and save again."));
 
@@ -840,7 +840,7 @@ CsvImpPriceAssist::preview_settings_delete ()
     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(csv_imp_asst),
+    auto response = gnc_ok_cancel_dialog (GTK_WINDOW(csv_imp_asst),
                                 GTK_RESPONSE_CANCEL,
                                 "%s", _("Delete the Import Settings."));
     if (response == GTK_RESPONSE_OK)
@@ -875,7 +875,7 @@ CsvImpPriceAssist::preview_settings_save ()
 
             if (preset && (preset->m_name == std::string(new_name)))
             {
-                auto response = gnc_ok_cancel_dialog (GTK_WIDGET(csv_imp_asst),
+                auto response = gnc_ok_cancel_dialog (GTK_WINDOW(csv_imp_asst),
                         GTK_RESPONSE_OK,
                         "%s", _("Setting name already exists, over write?"));
                 if (response != GTK_RESPONSE_OK)
@@ -890,7 +890,7 @@ CsvImpPriceAssist::preview_settings_save ()
     /* All checks passed, let's save this preset */
     if (!price_imp->save_settings())
     {
-        gnc_info_dialog (GTK_WIDGET(csv_imp_asst),
+        gnc_info_dialog (GTK_WINDOW(csv_imp_asst),
             "%s", _("The settings have been saved."));
 
         // Update the settings store
@@ -915,7 +915,7 @@ CsvImpPriceAssist::preview_settings_save ()
         }
     }
     else
-        gnc_error_dialog (GTK_WIDGET(csv_imp_asst),
+        gnc_error_dialog (GTK_WINDOW(csv_imp_asst),
             "%s", _("There was a problem saving the settings, please try again."));
 }
 
@@ -996,7 +996,7 @@ void CsvImpPriceAssist::preview_update_separators (GtkWidget* widget)
         /* 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 (GTK_WIDGET(csv_imp_asst), "Error in parsing");
+        gnc_error_dialog (GTK_WINDOW(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)
@@ -1046,7 +1046,7 @@ void CsvImpPriceAssist::preview_update_file_format ()
     catch (std::range_error &e)
     {
         /* Parsing failed ... */
-        gnc_error_dialog (nullptr, "%s", e.what());
+        gnc_error_dialog (GTK_WINDOW (csv_imp_asst), "%s", e.what());
         return;
     }
     catch (...)
@@ -1082,7 +1082,7 @@ CsvImpPriceAssist::preview_update_encoding (const char* encoding)
         catch (...)
         {
             /* If it fails, change back to the old encoding. */
-            gnc_error_dialog (nullptr, "%s", _("Invalid encoding selected"));
+            gnc_error_dialog (GTK_WINDOW (csv_imp_asst), "%s", _("Invalid encoding selected"));
             go_charmap_sel_set_encoding (encselector, previous_encoding.c_str());
         }
     }
@@ -1319,7 +1319,7 @@ fixed_context_menu_handler_price (GnumericPopupMenuElement const *element,
     }
     catch(std::range_error& e)
     {
-        gnc_error_dialog (nullptr, "%s", e.what());
+        gnc_error_dialog (GTK_WINDOW (info->csv_imp_asst), "%s", e.what());
         return false;
     }
     info->preview_refresh_table ();
@@ -1364,7 +1364,7 @@ CsvImpPriceAssist::preview_split_column (int col, int offset)
     }
     catch (std::range_error& e)
     {
-        gnc_error_dialog (nullptr, "%s", e.what());
+        gnc_error_dialog (GTK_WINDOW (csv_imp_asst), "%s", e.what());
         return;
     }
     preview_refresh_table();
@@ -1799,7 +1799,7 @@ CsvImpPriceAssist::assist_finish ()
         /* 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),
+        gnc_error_dialog (GTK_WINDOW(csv_imp_asst),
             _("An unexpected error has occurred while creating prices. Please report this as a bug.\n\n"
               "Error message:\n%s"), err.what());
         gtk_assistant_set_current_page (csv_imp_asst, 2);

commit 8ee6783b4b3e65cfaace37a5a4f9d4b31f574495
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sun Dec 10 11:58:09 2017 +0000

    Replace magic numbers used in std::get... with values from enum

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index fbc563c..398a52c 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -1564,12 +1564,12 @@ void CsvImpPriceAssist::preview_refresh_table ()
         GtkTreeIter iter;
         gtk_list_store_append (store, &iter);
         preview_row_fill_state_cells (store, &iter,
-                std::get<1>(parse_line), std::get<3>(parse_line));
+                std::get<PL_ERROR>(parse_line), std::get<PL_SKIP>(parse_line));
 
         /* 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++)
+        for (auto cell_str_it = std::get<PL_INPUT>(parse_line).cbegin(); cell_str_it != std::get<PL_INPUT>(parse_line).cend(); cell_str_it++)
         {
-            uint32_t pos = PREV_N_FIXED_COLS + cell_str_it - std::get<0>(parse_line).cbegin();
+            uint32_t pos = PREV_N_FIXED_COLS + cell_str_it - std::get<PL_INPUT>(parse_line).cbegin();
             gtk_list_store_set (store, &iter, pos, cell_str_it->c_str(), -1);
         }
     }
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 61c876d..df91261 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -54,7 +54,6 @@ const gchar* currency_format_user_price[] = {N_("Locale"),
 
 
 /** Constructor for GncPriceImport.
- * @return Pointer to a new GncCsvParseData
  */
 GncPriceImport::GncPriceImport(GncImpFileFormat format)
 {
@@ -247,12 +246,12 @@ void GncPriceImport::update_skipped_lines(boost::optional<uint32_t> start, boost
 
     for (uint32_t i = 0; i < m_parsed_lines.size(); i++)
     {
-        std::get<3>(m_parsed_lines[i]) =
+        std::get<PL_SKIP>(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
+             (m_skip_errors && !std::get<PL_ERROR>(m_parsed_lines[i]).empty())); // skip lines with errors
     }
 }
 
@@ -493,7 +492,7 @@ std::string GncPriceImport::verify ()
     auto have_line_errors = false;
     for (auto line : m_parsed_lines)
     {
-        if (!std::get<3>(line) && !std::get<1>(line).empty())
+        if (!std::get<PL_SKIP>(line) && !std::get<PL_ERROR>(line).empty())
         {
             have_line_errors = true;
             break;
@@ -624,7 +623,7 @@ void GncPriceImport::create_prices ()
             ++parsed_lines_it)
     {
         /* Skip current line if the user specified so */
-        if ((std::get<3>(*parsed_lines_it)))
+        if ((std::get<PL_SKIP>(*parsed_lines_it)))
             continue;
 
         /* Should not throw anymore, otherwise verify needs revision */
@@ -648,13 +647,13 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro
     if (prop_type == GncPricePropType::NONE)
         return; /* Only deal with price related properties. */
 
-    auto price_props = std::make_shared<GncImportPrice> (*(std::get<2>(m_parsed_lines[row])).get());
+    auto price_props = std::make_shared<GncImportPrice> (*(std::get<PL_PREPRICE>(m_parsed_lines[row])).get());
 
-    if (col >= std::get<0>(m_parsed_lines[row]).size())
+    if (col >= std::get<PL_INPUT>(m_parsed_lines[row]).size())
         price_props->reset (prop_type); //reset errors
     else
     {
-        auto value = std::get<0>(m_parsed_lines[row]).at(col);
+        auto value = std::get<PL_INPUT>(m_parsed_lines[row]).at(col);
         bool enable_test_empty = true;
         try
         {
@@ -683,12 +682,12 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro
             /* 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<3>(m_parsed_lines[row]))
+            if (!std::get<PL_SKIP>(m_parsed_lines[row]))
                 PINFO("User warning: %s", e.what());
         }
     }
     /* Store the result */
-    std::get<2>(m_parsed_lines[row]) = price_props;
+    std::get<PL_PREPRICE>(m_parsed_lines[row]) = price_props;
 }
 
 void
@@ -723,8 +722,8 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type,
         /* Reset date and currency formats for each price 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<2>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format);
+        std::get<PL_PREPRICE>(*parsed_lines_it)->set_date_format (m_settings.m_date_format);
+        std::get<PL_PREPRICE>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format);
 
         uint32_t row = parsed_lines_it - m_parsed_lines.begin();
 
@@ -733,7 +732,7 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type,
          */
         if (old_type != type)
         {
-            auto old_col = std::get<0>(*parsed_lines_it).size(); // Deliberately out of bounds to trigger a reset!
+            auto old_col = std::get<PL_INPUT>(*parsed_lines_it).size(); // Deliberately out of bounds to trigger a reset!
             if ((old_type > GncPricePropType::NONE)
                     && (old_type <= GncPricePropType::PRICE_PROPS))
                 update_price_props (row, old_col, old_type);
@@ -744,8 +743,8 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type,
             update_price_props (row, position, type);
 
         /* Report errors if there are any */
-        auto price_errors = std::get<2>(*parsed_lines_it)->errors();
-        std::get<1>(*parsed_lines_it) =
+        auto price_errors = std::get<PL_PREPRICE>(*parsed_lines_it)->errors();
+        std::get<PL_ERROR>(*parsed_lines_it) =
                 price_errors +
                 (price_errors.empty() ? std::string() : "\n");
     }
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index fd8ebed..360859d 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -49,6 +49,18 @@ extern "C" {
 extern const int num_currency_formats_price;
 extern const gchar* currency_format_user_price[];
 
+/** An enum describing the columns found in a parse_line_t. Currently these are:
+ *  - a tokenized line of input
+ *  - an optional error string
+ *  - a struct to hold user selected properties for a price
+ *  - a boolean to mark the line as skipped by error and/or user or not */
+enum parse_line_cols {
+    PL_INPUT,
+    PL_ERROR,
+    PL_PREPRICE,
+    PL_SKIP
+};
+
 /** Tuple to hold
  *  - a tokenized line of input
  *  - an optional error string

commit 43f1b2fde04187f137447a9419a8938bb51e58ca
Merge: 66da4ae 288563c
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Sun Dec 10 10:24:07 2017 +0000

    Merge branch 'prices-in' of /mygit/gnucash into prices-in


commit 288563c25e39be4c5a87f924d928be6689e00357
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Dec 7 11:19:18 2017 +0000

    Add a test for empty values
    
    Some csv values are allowed to be empty based on options selected so
    add a test for this otherwise all values are required.

diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 705d37c..61c876d 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -649,15 +649,13 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro
         return; /* Only deal with price related properties. */
 
     auto price_props = std::make_shared<GncImportPrice> (*(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())
-        price_props->reset (prop_type);
+    if (col >= std::get<0>(m_parsed_lines[row]).size())
+        price_props->reset (prop_type); //reset errors
     else
     {
+        auto value = std::get<0>(m_parsed_lines[row]).at(col);
+        bool enable_test_empty = true;
         try
         {
             // set the from_commodity based on combo so we can test for same.
@@ -665,14 +663,20 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro
             {
                 if (m_settings.m_from_commodity)
                     price_props->set_from_commodity (m_settings.m_from_commodity);
+
+                if (m_settings.m_to_currency)
+                    enable_test_empty = false;
             }
             // set the to_currency based on combo so we can test for same.
             if (prop_type == GncPricePropType::FROM_COMMODITY)
             {
                 if (m_settings.m_to_currency)
                     price_props->set_to_currency (m_settings.m_to_currency);
+
+                if (m_settings.m_from_commodity)
+                    enable_test_empty = false;
             }
-            price_props->set(prop_type, value);
+            price_props->set(prop_type, value, enable_test_empty);
         }
         catch (const std::exception& e)
         {
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 1ccd6e5..151bd38 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -135,13 +135,17 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
         return comm;
 }
 
-void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
+void GncImportPrice::set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty)
 {
     try
     {
         // Drop any existing error for the prop_type we're about to set
         m_errors.erase(prop_type);
 
+        // conditional test for empty values
+        if (value.empty() && enable_test_empty)
+            throw std::invalid_argument (_("Column value can not be empty."));
+
         gnc_commodity *comm = nullptr;
         switch (prop_type)
         {
@@ -207,7 +211,8 @@ void GncImportPrice::reset (GncPricePropType prop_type)
 {
     try
     {
-        set (prop_type, std::string());
+        // set enable_test_empty to false to allow empty values
+        set (prop_type, std::string(), false);
     }
     catch (...)
     {
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 77e39dc..3a35861 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -86,7 +86,7 @@ public:
     GncImportPrice (int date_format, int currency_format) : m_date_format{date_format},
         m_currency_format{currency_format}{};
 
-    void set (GncPricePropType prop_type, const std::string& value);
+    void set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty);
     void set_date_format (int date_format) { m_date_format = date_format ;}
     void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
     void reset (GncPricePropType prop_type);

commit 16714a8c5b7e08eb727c905fce1582f68fffd5c2
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Dec 7 11:17:14 2017 +0000

    Replace date parse function with one from gnc_datetime

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index af810cb..fbc563c 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -608,10 +608,8 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
 
         /* 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_price; i++)
-        {
-            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user_price[i]));
-        }
+        for (auto& date_fmt : GncDate::c_formats)
+            gtk_combo_box_text_append_text (date_format_combo, _(date_fmt.m_fmt.c_str()));
         gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0);
         g_signal_connect (G_OBJECT(date_format_combo), "changed",
                          G_CALLBACK(csv_price_imp_preview_date_fmt_sel_cb), this);
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 838c947..705d37c 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -46,14 +46,6 @@ extern "C" {
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
-const int num_date_formats_price = 5;
-const gchar* date_format_user_price[] = {N_("y-m-d"),
-                                   N_("d-m-y"),
-                                   N_("m-d-y"),
-                                   N_("d-m"),
-                                   N_("m-d")
-                                  };
-
 const int num_currency_formats_price = 3;
 const gchar* currency_format_user_price[] = {N_("Locale"),
                                        N_("Period: 123,456.78"),
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 9959aaa..fd8ebed 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -49,10 +49,6 @@ extern "C" {
 extern const int num_currency_formats_price;
 extern const gchar* currency_format_user_price[];
 
-/* A set of date formats that the user sees. */
-extern const int num_date_formats_price;
-extern const gchar* date_format_user_price[];
-
 /** Tuple to hold
  *  - a tokenized line of input
  *  - an optional error string
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 539f316..1ccd6e5 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -50,116 +50,6 @@ std::map<GncPricePropType, const char*> gnc_price_col_type_strs = {
         { GncPricePropType::TO_CURRENCY, N_("Currency To") },
 };
 
-/* Regular expressions used to parse dates per date format */
-const char* date_regex_price[] = {
-                             "(?:"                                   // 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_price (const std::string &date_str, int format)
-{
-    boost::regex r(date_regex_price[format]);
-    boost::smatch what;
-    if(!boost::regex_search(date_str, what, r))
-        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 (_("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"));
-
-    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);
-    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;
-}
-
-
 /** Convert str into a GncNumeric using the user-specified (import) currency format.
  * @param str The string to be parsed
  * @param currency_format The currency format to use.
@@ -257,7 +147,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
         {
             case GncPricePropType::DATE:
                 m_date = boost::none;
-                m_date = parse_date_price (value, m_date_format); // Throws if parsing fails
+                m_date = GncDate(value, GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails
                 break;
 
             case GncPricePropType::AMOUNT:
@@ -357,7 +247,7 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
     }
 
     Timespec date;
-    timespecFromTime64 (&date, *m_date);
+    timespecFromTime64 (&date, static_cast<time64>(GncDateTime(*m_date, DayPart::neutral)));
     date.tv_nsec = 0;
 
     bool rev = false;
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 9ae4853..77e39dc 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -39,6 +39,7 @@ extern "C" {
 #include <map>
 #include <memory>
 #include <boost/optional.hpp>
+#include <gnc-datetime.hpp>
 #include <gnc-numeric.hpp>
 
 /** Enumeration for column types. These are the different types of
@@ -76,7 +77,6 @@ private:
     const char *m_name;
 };
 
-time64 parse_date_price (const std::string &date_str, int format);
 gnc_commodity* parse_commodity_price_comm (const std::string& comm_str);
 GncNumeric parse_amount_price (const std::string &str, int currency_format);
 
@@ -104,7 +104,7 @@ public:
 private:
     int m_date_format;
     int m_currency_format;
-    boost::optional<time64> m_date;
+    boost::optional<GncDate> m_date;
     boost::optional<GncNumeric> m_amount;
     boost::optional<gnc_commodity*> m_from_commodity;
     boost::optional<gnc_commodity*> m_to_currency;

commit ee2f301789b34d289743a1dd5bad4d1d07b09ba5
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon Dec 4 14:25:02 2017 +0000

    Make changes for Gtk3 compatibility

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 0fa6273..af810cb 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -503,10 +503,12 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
     file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
     g_signal_connect (G_OBJECT(file_chooser), "file-activated",
                       G_CALLBACK(csv_price_imp_file_confirm_cb), this);
-    auto button = gtk_button_new_from_stock (GTK_STOCK_OK);
+    auto button = gtk_button_new_with_label (_("OK"));
     gtk_widget_set_size_request (button, 100, -1);
     gtk_widget_show (button);
-    auto h_box = gtk_hbox_new (TRUE, 0);
+    auto h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+    gtk_box_set_homogeneous (GTK_BOX (h_box), TRUE);
+    gtk_widget_set_hexpand (GTK_WIDGET(h_box), TRUE);
     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",
@@ -792,7 +794,9 @@ CsvImpPriceAssist::preview_settings_name (GtkEntry* entry)
     if (text)
         price_imp->settings_name(text);
 
-    auto combo = gtk_widget_get_parent (GTK_WIDGET(entry));
+    auto box = gtk_widget_get_parent (GTK_WIDGET(entry));
+    auto combo = gtk_widget_get_parent (GTK_WIDGET(box));
+
     preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo));
 }
 
@@ -1239,11 +1243,11 @@ enum
 static GnumericPopupMenuElement const popup_elements[] =
 {
     {
-        N_("Merge with column on _left"), GTK_STOCK_REMOVE,
+        N_("Merge with column on _left"), "list-remove",
         0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT
     },
     {
-        N_("Merge with column on _right"), GTK_STOCK_REMOVE,
+        N_("Merge with column on _right"), "list-remove",
         0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT
     },
     { "", nullptr, 0, 0, 0 },
@@ -1253,11 +1257,11 @@ static GnumericPopupMenuElement const popup_elements[] =
     },
     { "", nullptr, 0, 0, 0 },
     {
-        N_("_Widen this column"), GTK_STOCK_GO_FORWARD,
+        N_("_Widen this column"), "go-next",
         0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN
     },
     {
-        N_("_Narrow this column"), GTK_STOCK_GO_BACK,
+        N_("_Narrow this column"), "go-previous",
         0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW
     },
     { nullptr, nullptr, 0, 0, 0 },
@@ -1429,7 +1433,7 @@ CsvImpPriceAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte
         fcolor = "black";
         bcolor = "pink";
         c_err_msg = err_msg.c_str();
-        icon_name = GTK_STOCK_DIALOG_ERROR;
+        icon_name = "dialog-error";
     }
     gtk_list_store_set (store, iter,
             PREV_COL_FCOLOR, fcolor,
@@ -1480,13 +1484,13 @@ void
 CsvImpPriceAssist::preview_style_column (uint32_t 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);
+    auto renderer = static_cast<GtkCellRenderer*>(gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(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,
+                "icon-name", 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,
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
index 33db266..b1ba9c0 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
 <interface>
-  <requires lib="gtk+" version="2.24"/>
-  <!-- interface-naming-policy project-wide -->
+  <requires lib="gtk+" version="3.10"/>
   <object class="GtkAdjustment" id="end_row_adj">
     <property name="upper">1000</property>
     <property name="step_increment">1</property>
@@ -38,11 +38,11 @@
     <property name="title" translatable="yes">CSV Price Import</property>
     <property name="default_width">400</property>
     <property name="default_height">500</property>
+    <signal name="apply" handler="csv_price_imp_assist_finish_cb" swapped="no"/>
+    <signal name="cancel" handler="csv_price_imp_assist_cancel_cb" swapped="no"/>
     <signal name="close" handler="csv_price_imp_assist_close_cb" swapped="no"/>
     <signal name="destroy" handler="csv_price_imp_assist_destroy_cb" swapped="no"/>
-    <signal name="apply" handler="csv_price_imp_assist_finish_cb" swapped="no"/>
     <signal name="prepare" handler="csv_price_imp_assist_prepare_cb" swapped="no"/>
-    <signal name="cancel" handler="csv_price_imp_assist_cancel_cb" swapped="no"/>
     <child>
       <placeholder/>
     </child>
@@ -76,10 +76,11 @@ Click on 'Forward' to proceed or 'Cancel' to Abort Import.</property>
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="file_page">
+      <object class="GtkBox" id="file_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="border_width">12</property>
+        <property name="orientation">vertical</property>
         <child>
           <object class="GtkLabel" id="label7">
             <property name="visible">True</property>
@@ -101,20 +102,19 @@ Select location and file name for the Import, then click 'OK'...
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="preview_page">
+      <object class="GtkBox" id="preview_page">
         <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="border_width">12</property>
+        <property name="orientation">vertical</property>
         <property name="spacing">2</property>
         <child>
-          <object class="GtkTable" id="table1">
+          <object class="GtkGrid" id="table1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="n_rows">2</property>
-            <property name="n_columns">2</property>
-            <property name="column_spacing">5</property>
             <property name="row_spacing">5</property>
+            <property name="column_spacing">5</property>
             <child>
               <object class="GtkFrame" id="frame6">
                 <property name="visible">True</property>
@@ -129,7 +129,7 @@ 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="GtkHBox" id="combo_hbox">
+                      <object class="GtkBox" id="combo_hbox">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <child>
@@ -143,7 +143,7 @@ Select location and file name for the Import, then click 'OK'...
                               <object class="GtkImage" id="image2">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="stock">gtk-delete</property>
+                                <property name="icon_name">edit-delete</property>
                               </object>
                             </child>
                           </object>
@@ -165,7 +165,7 @@ Select location and file name for the Import, then click 'OK'...
                               <object class="GtkImage" id="image1">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="stock">gtk-save</property>
+                                <property name="icon_name">document-save</property>
                               </object>
                             </child>
                           </object>
@@ -184,6 +184,7 @@ Select location and file name for the Import, then click 'OK'...
                   <object class="GtkLabel" id="label12">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="halign">start</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>
@@ -191,8 +192,8 @@ Select location and file name for the Import, then click 'OK'...
                 </child>
               </object>
               <packing>
-                <property name="x_options">GTK_FILL</property>
-                <property name="y_options">GTK_FILL</property>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
               </packing>
             </child>
             <child>
@@ -210,15 +211,14 @@ 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="GtkVBox" id="vbox1">
+                      <object class="GtkBox" id="vbox1">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
                         <child>
-                          <object class="GtkTable" id="table4">
+                          <object class="GtkGrid" id="table4">
                             <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="GtkRadioButton" id="csv_button">
                                 <property name="label" translatable="yes">Separators</property>
@@ -226,10 +226,15 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="active">True</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_fixed_sel_cb" swapped="no"/>
                               </object>
+                              <packing>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
+                              </packing>
                             </child>
                             <child>
                               <object class="GtkRadioButton" id="fixed_button">
@@ -238,24 +243,24 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</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>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
-                              <object class="GtkHSeparator" id="hseparator1">
+                              <object class="GtkSeparator" id="hseparator1">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                               </object>
                               <packing>
-                                <property name="right_attach">2</property>
+                                <property name="left_attach">0</property>
                                 <property name="top_attach">1</property>
-                                <property name="bottom_attach">2</property>
-                                <property name="y_options">GTK_FILL</property>
+                                <property name="width">2</property>
                               </packing>
                             </child>
                           </object>
@@ -266,12 +271,10 @@ Select location and file name for the Import, then click 'OK'...
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkTable" id="separator_table">
+                          <object class="GtkGrid" id="separator_table">
                             <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>
                             <child>
                               <object class="GtkCheckButton" id="space_cbutton">
@@ -280,12 +283,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
-                                <property name="x_options">GTK_FILL</property>
-                                <property name="y_options">GTK_FILL</property>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -295,14 +299,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_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>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -312,15 +315,14 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="active">True</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_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>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -330,14 +332,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
+                                <property name="left_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>
@@ -347,16 +348,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
-                                <property name="left_attach">1</property>
-                                <property name="right_attach">2</property>
+                                <property name="left_attach">0</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>
@@ -366,16 +364,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
-                                <property name="left_attach">2</property>
-                                <property name="right_attach">3</property>
+                                <property name="left_attach">1</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>
@@ -385,14 +380,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
+                                <property name="left_attach">0</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>
@@ -401,22 +395,18 @@ Select location and file name for the Import, then click 'OK'...
                                 <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_price_imp_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>
+                            <child>
+                              <placeholder/>
+                            </child>
                           </object>
                           <packing>
                             <property name="expand">False</property>
@@ -425,7 +415,7 @@ Select location and file name for the Import, then click 'OK'...
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkHBox" id="fw_instructions_hbox">
+                          <object class="GtkBox" id="fw_instructions_hbox">
                             <property name="can_focus">False</property>
                             <property name="no_show_all">True</property>
                             <child>
@@ -433,8 +423,7 @@ Select location and file name for the Import, then click 'OK'...
                                 <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>
+                                <property name="icon_name">dialog-information</property>
                               </object>
                               <packing>
                                 <property name="expand">False</property>
@@ -444,67 +433,61 @@ Select location and file name for the Import, then click 'OK'...
                               </packing>
                             </child>
                             <child>
-                              <object class="GtkTable" id="table2">
+                              <object class="GtkGrid" 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>
+                                  <packing>
+                                    <property name="left_attach">0</property>
+                                    <property name="top_attach">0</property>
+                                  </packing>
                                 </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="halign">start</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"/>
+                                    <property name="top_attach">0</property>
                                   </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="left_attach">0</property>
                                     <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="halign">start</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>
@@ -522,17 +505,17 @@ Select location and file name for the Import, then click 'OK'...
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkTable" id="table5">
+                          <object class="GtkGrid" id="table5">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="n_rows">2</property>
                             <child>
-                              <object class="GtkHSeparator" id="hseparator4">
+                              <object class="GtkSeparator" id="hseparator4">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                               </object>
                               <packing>
-                                <property name="y_options">GTK_FILL</property>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -540,15 +523,15 @@ Select location and file name for the Import, then click 'OK'...
                                 <property name="label" translatable="yes">Allow existing prices to be over written.</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
+                                <property name="focus_on_click">False</property>
                                 <property name="receives_default">False</property>
                                 <property name="tooltip_text" translatable="yes">Normally prices are not over written, select this to change that. This setting is not saved.</property>
-                                <property name="focus_on_click">False</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_overwrite_cb" swapped="no"/>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <property name="top_attach">1</property>
-                                <property name="bottom_attach">2</property>
                               </packing>
                             </child>
                           </object>
@@ -567,16 +550,15 @@ Select location and file name for the Import, then click 'OK'...
                     <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="halign">start</property>
                     <property name="label" translatable="yes"><b>File Format</b></property>
                     <property name="use_markup">True</property>
                   </object>
                 </child>
               </object>
               <packing>
+                <property name="left_attach">0</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>
@@ -592,18 +574,17 @@ 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="GtkVBox" id="vbox6">
+                      <object class="GtkBox" 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>
+                        <property name="orientation">vertical</property>
                         <child>
-                          <object class="GtkTable" id="table3">
+                          <object class="GtkGrid" id="table3">
                             <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>
+                            <property name="column_spacing">5</property>
                             <child>
                               <object class="GtkAlignment" id="date_format_container">
                                 <property name="visible">True</property>
@@ -613,23 +594,19 @@ Select location and file name for the Import, then click 'OK'...
                               </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="halign">start</property>
                                 <property name="label" translatable="yes">Date Format</property>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <property name="top_attach">1</property>
-                                <property name="bottom_attach">2</property>
-                                <property name="x_options">GTK_SHRINK | GTK_FILL</property>
                               </packing>
                             </child>
                             <child>
@@ -640,34 +617,31 @@ Select location and file name for the Import, then click 'OK'...
                               </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="halign">start</property>
                                 <property name="label" translatable="yes">Currency Format</property>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <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="halign">start</property>
                                 <property name="label" translatable="yes">Encoding</property>
                               </object>
                               <packing>
-                                <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -678,38 +652,35 @@ Select location and file name for the Import, then click 'OK'...
                               </object>
                               <packing>
                                 <property name="left_attach">1</property>
-                                <property name="right_attach">2</property>
-                                <property name="x_options">GTK_FILL</property>
+                                <property name="top_attach">0</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="halign">start</property>
                                 <property name="label" translatable="yes">Leading Lines to Skip</property>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <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="halign">start</property>
                                 <property name="label" translatable="yes">Trailing Lines to Skip</property>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <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">
+                              <object class="GtkBox" id="hbox2">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <child>
@@ -717,11 +688,8 @@ Select location and file name for the Import, then click 'OK'...
                                     <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_price_imp_preview_srow_cb" swapped="no"/>
@@ -735,13 +703,11 @@ Select location and file name for the Import, then click 'OK'...
                               </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">
+                              <object class="GtkBox" id="hbox3">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <child>
@@ -749,11 +715,8 @@ Select location and file name for the Import, then click 'OK'...
                                     <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_price_imp_preview_erow_cb" swapped="no"/>
@@ -767,20 +730,18 @@ Select location and file name for the Import, then click 'OK'...
                               </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">
+                              <object class="GtkSeparator" id="hseparator2">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                               </object>
                               <packing>
-                                <property name="right_attach">2</property>
+                                <property name="left_attach">0</property>
                                 <property name="top_attach">3</property>
-                                <property name="bottom_attach">4</property>
+                                <property name="width">2</property>
                               </packing>
                             </child>
                           </object>
@@ -800,6 +761,7 @@ Select location and file name for the Import, then click 'OK'...
 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="halign">start</property>
                             <property name="draw_indicator">True</property>
                             <signal name="toggled" handler="csv_price_imp_preview_skiprows_cb" swapped="no"/>
                           </object>
@@ -817,6 +779,7 @@ For example
                   <object class="GtkLabel" id="label13">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="halign">start</property>
                     <property name="label" translatable="yes"><b>Miscellaneous</b></property>
                     <property name="use_markup">True</property>
                   </object>
@@ -824,11 +787,7 @@ For example
               </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>
@@ -842,7 +801,7 @@ For example
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox1">
+          <object class="GtkBox" id="hbox1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
@@ -859,7 +818,7 @@ For example
                     <property name="left_padding">5</property>
                     <property name="right_padding">5</property>
                     <child>
-                      <object class="GtkHBox" id="commodity_hbox">
+                      <object class="GtkBox" id="commodity_hbox">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <child>
@@ -913,7 +872,7 @@ For example
                     <property name="left_padding">5</property>
                     <property name="right_padding">5</property>
                     <child>
-                      <object class="GtkHBox" id="currency_hbox">
+                      <object class="GtkBox" id="currency_hbox">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <child>
@@ -964,16 +923,15 @@ For example
           <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="viewport2">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <child>
-                  <object class="GtkVBox" id="vbox8">
+                  <object class="GtkBox" id="vbox8">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="orientation">vertical</property>
                     <child>
                       <object class="GtkTreeView" id="ctreeview">
                         <property name="visible">True</property>
@@ -981,6 +939,9 @@ For example
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                         <property name="headers_visible">False</property>
                         <property name="enable_grid_lines">both</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection"/>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">False</property>
@@ -994,6 +955,9 @@ For example
                         <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="enable_grid_lines">both</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection"/>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">True</property>
@@ -1013,7 +977,7 @@ For example
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox13">
+          <object class="GtkBox" 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>
@@ -1022,8 +986,7 @@ For example
                 <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>
+                <property name="icon_name">dialog-information</property>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -1037,7 +1000,7 @@ For example
                 <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="halign">start</property>
                 <property name="label" translatable="yes">Select the type of each column to import.</property>
               </object>
               <packing>
@@ -1055,7 +1018,7 @@ For example
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox14">
+          <object class="GtkBox" id="hbox14">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
@@ -1063,7 +1026,6 @@ For example
                 <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>
                 <property name="image_position">right</property>
                 <property name="draw_indicator">True</property>
                 <signal name="toggled" handler="csv_price_imp_preview_skiperrors_cb" swapped="no"/>
@@ -1089,10 +1051,11 @@ For example
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="confirm_page">
+      <object class="GtkBox" id="confirm_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="border_width">12</property>
+        <property name="orientation">vertical</property>
         <child>
           <object class="GtkAlignment" id="alignment2">
             <property name="visible">True</property>
@@ -1122,10 +1085,11 @@ Cancel to abort.</b></property>
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="summary_page">
+      <object class="GtkBox" id="summary_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="border_width">12</property>
+        <property name="orientation">vertical</property>
         <child>
           <object class="GtkLabel" id="summary_label">
             <property name="visible">True</property>
@@ -1147,5 +1111,12 @@ Cancel to abort.</b></property>
         <property name="complete">True</property>
       </packing>
     </child>
+    <child internal-child="action_area">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+      </object>
+      <packing>
+      </packing>
+    </child>
   </object>
 </interface>
diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
index c14092d..3cecf66 100644
--- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
+++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
@@ -57,7 +57,7 @@ static GtkActionEntry gnc_plugin_actions [] =
         G_CALLBACK (gnc_plugin_csv_import_trans_cmd)
     },
     {
-        "CsvImportPriceAction", GTK_STOCK_CONVERT, N_("Import _Prices from a CSV file..."), NULL,
+        "CsvImportPriceAction", "go-previous", N_("Import _Prices from a CSV file..."), NULL,
         N_("Import Prices from a CSV file"),
         G_CALLBACK (gnc_plugin_csv_import_price_cmd)
     },

commit 1aa3601e01cc07748d7f4a70e3f9be06af3a18e3
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 12:04:51 2017 +0000

    Pot file changes for new files and settings rename

diff --git a/po/POTFILES.in b/po/POTFILES.in
index bced61f..7f74af4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -287,17 +287,21 @@ gnucash/import-export/csv-exp/gnc-plugin-csv-export.c
 [type: gettext/gsettings]gnucash/import-export/csv-exp/gschemas/org.gnucash.dialogs.export.csv.gschema.xml.in.in
 gnucash/import-export/csv-imp/assistant-csv-account-import.c
 gnucash/import-export/csv-imp/assistant-csv-account-import.glade
+gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+gnucash/import-export/csv-imp/assistant-csv-price-import.glade
 gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
 gnucash/import-export/csv-imp/assistant-csv-trans-import.glade
 gnucash/import-export/csv-imp/csv-account-import.c
 gnucash/import-export/csv-imp/gnc-csv-account-map.c
 gnucash/import-export/csv-imp/gnc-csv-gnumeric-popup.c
 gnucash/import-export/csv-imp/gnc-csv-tokenizer.cpp
-gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
 gnucash/import-export/csv-imp/gnc-dummy-tokenizer.cpp
 gnucash/import-export/csv-imp/gnc-fw-tokenizer.cpp
 gnucash/import-export/csv-imp/gncmod-csv-import.c
 gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
+gnucash/import-export/csv-imp/gnc-price-import.cpp
+gnucash/import-export/csv-imp/gnc-price-props.cpp
 gnucash/import-export/csv-imp/gnc-tokenizer.cpp
 gnucash/import-export/csv-imp/gnc-trans-props.cpp
 gnucash/import-export/csv-imp/gnc-tx-import.cpp

commit 71bf7d01fdd1b044723daec5cfbba2cb17c061bf
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 12:03:14 2017 +0000

    Rename gnc-csv-trans-settings.* to gnc-csv-import-settings.*
    
    Change the name of the import settings files as they do not just deal
    with transactions and all associated required changes.

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index 44a14ca..65082be 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -17,7 +17,7 @@ SET(csv_import_SOURCES
   gnc-csv-account-map.c
   gnc-csv-gnumeric-popup.c
   gnc-csv-tokenizer.cpp
-  gnc-csv-trans-settings.cpp
+  gnc-csv-import-settings.cpp
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
   gnc-price-import.cpp
@@ -45,7 +45,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-account-map.h
   gnc-csv-gnumeric-popup.h
   gnc-csv-tokenizer.hpp
-  gnc-csv-trans-settings.hpp
+  gnc-csv-import-settings.hpp
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
   gnc-price-import.hpp
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index 4b473de..f1b501f 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -19,7 +19,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
   gnc-trans-props.cpp \
-  gnc-csv-trans-settings.cpp
+  gnc-csv-import-settings.cpp
 
 noinst_HEADERS = \
   assistant-csv-account-import.h \
@@ -37,7 +37,7 @@ noinst_HEADERS = \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
   gnc-trans-props.hpp \
-  gnc-csv-trans-settings.hpp
+  gnc-csv-import-settings.hpp
 
 libgncmod_csv_import_la_LDFLAGS = -avoid-version
 
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 21126ea..0fa6273 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -52,7 +52,7 @@ extern "C"
 #include "go-charmap-sel.h"
 }
 
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include "gnc-price-import.hpp"
 #include "gnc-fw-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -738,7 +738,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo()
     gtk_list_store_clear (GTK_LIST_STORE(model));
 
     // Append the default entry
-    auto presets = get_trans_presets (settings_type);
+    auto presets = get_import_presets (settings_type);
     for (auto preset : presets)
     {
         GtkTreeIter iter;
@@ -766,11 +766,11 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
     /* Handle sensitivity of the delete and save button */
     if (gtk_combo_box_get_active_iter (combo, &iter))
     {
-        CsvTransSettings *preset;
+        CsvImportSettings *preset;
         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->m_name))
+        if (preset && !preset_is_reserved_name (preset->m_name))
         {
             /* Current preset is not read_only, so buttons can be enabled */
             can_delete = true;
@@ -778,7 +778,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
         }
     }
     else if (entry_text && (strlen (entry_text) > 0) &&
-            !trans_preset_is_reserved_name (std::string(entry_text)))
+            !preset_is_reserved_name (std::string(entry_text)))
         can_save = true;
 
     gtk_widget_set_sensitive (save_button, can_save);
@@ -807,7 +807,7 @@ CsvImpPriceAssist::preview_settings_load ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvTransSettings *preset = nullptr;
+    CsvImportSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -834,7 +834,7 @@ CsvImpPriceAssist::preview_settings_delete ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvTransSettings *preset = nullptr;
+    CsvImportSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -868,7 +868,7 @@ CsvImpPriceAssist::preview_settings_save ()
         while (valid)
         {
             // Walk through the list, reading each row
-            CsvTransSettings *preset;
+            CsvImportSettings *preset;
             gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
             if (preset && (preset->m_name == std::string(new_name)))
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index 07091f0..41779c6 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -58,7 +58,7 @@ extern "C"
 #include "go-charmap-sel.h"
 }
 
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include "gnc-tx-import.hpp"
 #include "gnc-fw-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -679,7 +679,7 @@ void CsvImpTransAssist::preview_populate_settings_combo()
 
     // Append the default entry
 
-    auto presets = get_trans_presets (settings_type);
+    auto presets = get_import_presets (settings_type);
     for (auto preset : presets)
     {
         GtkTreeIter iter;
@@ -707,11 +707,11 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
     /* Handle sensitivity of the delete and save button */
     if (gtk_combo_box_get_active_iter (combo, &iter))
     {
-        CsvTransSettings *preset;
+        CsvImportSettings *preset;
         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->m_name))
+        if (preset && !preset_is_reserved_name (preset->m_name))
         {
             /* Current preset is not read_only, so buttons can be enabled */
             can_delete = true;
@@ -719,7 +719,7 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
         }
     }
     else if (entry_text && (strlen (entry_text) > 0) &&
-            !trans_preset_is_reserved_name (std::string(entry_text)))
+            !preset_is_reserved_name (std::string(entry_text)))
         can_save = true;
 
     gtk_widget_set_sensitive (save_button, can_save);
@@ -752,7 +752,7 @@ CsvImpTransAssist::preview_settings_load ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvTransSettings *preset = nullptr;
+    CsvImportSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -779,7 +779,7 @@ CsvImpTransAssist::preview_settings_delete ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvTransSettings *preset = nullptr;
+    CsvImportSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -812,7 +812,7 @@ CsvImpTransAssist::preview_settings_save ()
         while (valid)
         {
             // Walk through the list, reading each row
-            CsvTransSettings *preset;
+            CsvImportSettings *preset;
             gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
             if (preset && (preset->m_name == std::string(new_name)))
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
similarity index 95%
rename from gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
rename to gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
index 3bbc984..0096c53 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
@@ -1,5 +1,5 @@
 /*******************************************************************\
- * gnc-csv-trans-settings.c -- Save and Load CSV Import Settings    *
+ * gnc-csv-import-settings.c -- Save and Load CSV Import Settings   *
  *                                                                  *
  * Copyright (C) 2014 Robert Fewell                                 *
  *                                                                  *
@@ -20,13 +20,13 @@
  * 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
+/** @file gnc-csv-import-settings.c
     @brief CSV Import Settings
     @author Copyright (c) 2014 Robert Fewell
     @author Copyright (c) 2016 Geert Janssens
 */
 
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include <sstream>
 
 extern "C"
@@ -70,18 +70,18 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
 preset_vec presets;
 
-static std::shared_ptr<CsvTransSettings> create_int_no_preset(const std::string& set_type)
+static std::shared_ptr<CsvImportSettings> create_int_no_preset(const std::string& set_type)
 {
-    auto preset = std::make_shared<CsvTransSettings>();
+    auto preset = std::make_shared<CsvImportSettings>();
     preset->m_name = no_settings;
     preset->m_settings_type = set_type;
 
     return preset;
 }
 
-static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
+static std::shared_ptr<CsvImportSettings> create_int_gnc_exp_preset(void)
 {
-    auto preset = std::make_shared<CsvTransSettings>();
+    auto preset = std::make_shared<CsvImportSettings>();
     preset->m_name = gnc_exp;
     preset->m_skip_start_lines = 1;
     preset->m_multi_split = true;
@@ -119,7 +119,7 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
  * find all settings entries in the state key file
  * based on settings type.
  **************************************************/
-const preset_vec& get_trans_presets (const std::string& set_type)
+const preset_vec& get_import_presets (const std::string& set_type)
 {
 
     // Search all Groups in the state key file for ones starting with prefix
@@ -157,7 +157,7 @@ const preset_vec& get_trans_presets (const std::string& set_type)
     /* Then add all the ones we found in the state file */
     for (auto preset_name : preset_names)
     {
-        auto preset = std::make_shared<CsvTransSettings>();
+        auto preset = std::make_shared<CsvImportSettings>();
         preset->m_settings_type = set_type;
         preset->m_name = preset_name;
         preset->load();
@@ -166,7 +166,7 @@ const preset_vec& get_trans_presets (const std::string& set_type)
     return presets;
 }
 
-bool trans_preset_is_reserved_name (const std::string& name)
+bool preset_is_reserved_name (const std::string& name)
 {
     return ((name == no_settings) ||
             (name == _(no_settings.c_str())) ||
@@ -205,9 +205,9 @@ handle_load_error (GError **key_error, const std::string& group)
  * load the settings from a state key file
  **************************************************/
 bool
-CsvTransSettings::load (void)
+CsvImportSettings::load (void)
 {
-    if (trans_preset_is_reserved_name (m_name))
+    if (preset_is_reserved_name (m_name))
         return true;
 
     GError *key_error = nullptr;
@@ -355,9 +355,9 @@ CsvTransSettings::load (void)
  * save settings to a key file
  **************************************************/
 bool
-CsvTransSettings::save (void)
+CsvImportSettings::save (void)
 {
-    if (trans_preset_is_reserved_name (m_name))
+    if (preset_is_reserved_name (m_name))
     {
         PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
         return true;
@@ -471,9 +471,9 @@ CsvTransSettings::save (void)
 }
 
 void
-CsvTransSettings::remove (void)
+CsvImportSettings::remove (void)
 {
-    if (trans_preset_is_reserved_name (m_name))
+    if (preset_is_reserved_name (m_name))
         return;
 
     auto keyfile = gnc_state_get_current ();
@@ -483,7 +483,7 @@ CsvTransSettings::remove (void)
 
 
 bool
-CsvTransSettings::read_only (void)
+CsvImportSettings::read_only (void)
 {
     return ((m_name == no_settings) ||
             (m_name == _(no_settings.c_str())) ||
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
similarity index 90%
rename from gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
rename to gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
index 7df293c..e6ce4d4 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
@@ -1,5 +1,5 @@
 /*******************************************************************\
- * gnc-csv-trans-settings.h   -- Save and Load CSV Import Settings  *
+ * gnc-csv-import-settings.h  -- Save and Load CSV Import Settings  *
  *                                                                  *
  * Copyright (C) 2014 Robert Fewell                                 *
  *                                                                  *
@@ -20,13 +20,13 @@
  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
-/** @file gnc-csv-trans-settings.h
+/** @file gnc-csv-import-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
+#ifndef GNC_CSV_IMPORT_SETTINGS_H
+#define GNC_CSV_IMPORT_SETTINGS_H
 
 extern "C" {
 #include <config.h>
@@ -49,9 +49,9 @@ 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};
 
-struct CsvTransSettings
+struct CsvImportSettings
 {
-    CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"},
+    CsvImportSettings() : 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_load_error {false}, m_base_account {nullptr},
@@ -107,8 +107,8 @@ gnc_commodity *m_to_currency;                 //  Price To Currency
 std::vector<GncPricePropType> m_column_types_price; // The Price Column types in order
 };
 
-using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
-/** Creates a vector of CsvTransSettings which combines
+using preset_vec = std::vector<std::shared_ptr<CsvImportSettings>>;
+/** Creates a vector of CsvImportSettings which combines
  *  - one or more internally defined presets
  *  - all preset found in the state key file.
  *
@@ -117,12 +117,12 @@ using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
  *
  *  @return a reference to the populated vector.
  */
-const preset_vec& get_trans_presets (const std::string& set_type);
+const preset_vec& get_import_presets (const std::string& set_type);
 
 /** 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);
+bool preset_is_reserved_name (const std::string& name);
 
 #endif
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 44a17b9..838c947 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -42,7 +42,7 @@ extern "C" {
 #include "gnc-price-props.hpp"
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -281,7 +281,7 @@ void GncPriceImport::separators (std::string separators)
 }
 std::string GncPriceImport::separators () { return m_settings.m_separators; }
 
-void GncPriceImport::settings (const CsvTransSettings& settings)
+void GncPriceImport::settings (const CsvImportSettings& settings)
 {
     /* First apply file format as this may recreate the tokenizer */
     file_format (settings.m_file_format);
@@ -315,7 +315,7 @@ void GncPriceImport::settings (const CsvTransSettings& settings)
 
 bool GncPriceImport::save_settings ()
 {
-    if (trans_preset_is_reserved_name (m_settings.m_name))
+    if (preset_is_reserved_name (m_settings.m_name))
         return true;
 
     /* separators are already copied to m_settings in the separators
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index be91941..9959aaa 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -42,7 +42,7 @@ extern "C" {
 
 #include "gnc-tokenizer.hpp"
 #include "gnc-price-props.hpp"
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include <boost/optional.hpp>
 
 /* A set of currency formats that the user sees. */
@@ -110,7 +110,7 @@ public:
     void separators (std::string separators);
     std::string separators ();
 
-    void settings (const CsvTransSettings& settings);
+    void settings (const CsvImportSettings& settings);
     bool save_settings ();
 
     void settings_name (std::string name);
@@ -157,7 +157,7 @@ private:
     void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type);
 
     struct CsvTranSettings;
-    CsvTransSettings m_settings;
+    CsvImportSettings m_settings;
     bool m_skip_errors;
     bool m_over_write;
 };
diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.cpp b/gnucash/import-export/csv-imp/gnc-tx-import.cpp
index daa85b4..eddbd75 100644
--- a/gnucash/import-export/csv-imp/gnc-tx-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-tx-import.cpp
@@ -39,7 +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"
+#include "gnc-csv-import-settings.hpp"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -293,7 +293,7 @@ void GncTxImport::separators (std::string separators)
 }
 std::string GncTxImport::separators () { return m_settings.m_separators; }
 
-void GncTxImport::settings (const CsvTransSettings& settings)
+void GncTxImport::settings (const CsvImportSettings& settings)
 {
     /* First apply file format as this may recreate the tokenizer */
     file_format (settings.m_file_format);
@@ -329,7 +329,7 @@ void GncTxImport::settings (const CsvTransSettings& settings)
 bool GncTxImport::save_settings ()
 {
 
-    if (trans_preset_is_reserved_name (m_settings.m_name))
+    if (preset_is_reserved_name (m_settings.m_name))
         return true;
 
     /* separators are already copied to m_settings in the separators
diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.hpp b/gnucash/import-export/csv-imp/gnc-tx-import.hpp
index 0c14a74..87e4ea4 100644
--- a/gnucash/import-export/csv-imp/gnc-tx-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-tx-import.hpp
@@ -43,7 +43,7 @@ extern "C" {
 
 #include "gnc-tokenizer.hpp"
 #include "gnc-trans-props.hpp"
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include <boost/optional.hpp>
 
 
@@ -136,7 +136,7 @@ public:
     void separators (std::string separators);
     std::string separators ();
 
-    void settings (const CsvTransSettings& settings);
+    void settings (const CsvImportSettings& settings);
     bool save_settings ();
 
     void settings_name (std::string name);
@@ -190,7 +190,7 @@ private:
     void update_pre_split_props (uint32_t row, uint32_t col, GncTransPropType prop_type);
 
     struct CsvTranSettings;
-    CsvTransSettings m_settings;
+    CsvImportSettings m_settings;
     bool m_skip_errors;
     bool m_req_mapped_accts;
 

commit 644a0aa06f09c49a99dd013f25c74af999f4fc46
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:56:25 2017 +0000

    Reorder the create price procedure.

diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 0ea21f9..539f316 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -362,9 +362,20 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
 
     bool rev = false;
     auto amount = *m_amount;
+    Result ret_val = ADDED;
 
     GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date);
 
+    // Should old price be over writen
+    if ((old_price != nullptr) && (over == true))
+    {
+        DEBUG("Over write");
+        gnc_pricedb_remove_price (pdb, old_price);
+        gnc_price_unref (old_price);
+        old_price = nullptr;
+        ret_val = REPLACED;
+    }
+
     if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import
     {
         // Check for currency in reverse direction.
@@ -373,9 +384,8 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
             // Check for price in reverse direction.
             if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity))
                 rev = true;
-
-            DEBUG("Commodity from is a Currency");
         }
+        DEBUG("Commodity from is a Currency");
 
         // Check for price less than 1, reverse if so.
         if (*m_amount < GncNumeric(1,1))
@@ -386,18 +396,6 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
         rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency),
         amount.to_string().c_str());
 
-    Result ret_val = ADDED;
-
-    // Should old price be over writen
-    if ((old_price != nullptr) && (over == true))
-    {
-        DEBUG("Over write");
-        gnc_pricedb_remove_price (pdb, old_price);
-        gnc_price_unref (old_price);
-        old_price = nullptr;
-        ret_val = REPLACED;
-    }
-
     // Create the new price
     if (old_price == nullptr)
     {

commit 62bbe4f951dbc8dbb9a1ac0b522e0fd33276e4b8
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:52:48 2017 +0000

    Add the ability to test from_commodity and to_currency being the same.
    
    To cover all combinations we need to test across the combo's and also
    the table entries when appropriate columns are set. Also need to force
    a reparse if any of the options change.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 14a8ffa..21126ea 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -94,6 +94,7 @@ public:
     void preview_update_currency_format ();
     void preview_update_currency ();
     void preview_update_commodity ();
+    void preview_reparse_col_type (GncPricePropType type);
     void preview_update_col_type (GtkComboBox* cbox);
     void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
 
@@ -1138,6 +1139,22 @@ enum PreviewDataTableCols {
     PREV_COL_ERR_ICON,
     PREV_N_FIXED_COLS };
 
+
+void
+CsvImpPriceAssist::preview_reparse_col_type (GncPricePropType type)
+{
+    auto column_types = price_imp->column_types_price();
+
+    // look for column type and force a reparse
+    auto col_type = std::find (column_types.begin(),
+                column_types.end(), type);
+    if (col_type != column_types.end())
+    {
+        price_imp->set_column_type_price (col_type -column_types.begin(),
+                        type, true);
+    }
+}
+
 /** 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 the selection, and any other columns containing that selection
@@ -1157,8 +1174,26 @@ void CsvImpPriceAssist::preview_update_col_type (GtkComboBox* cbox)
     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"));
+
+    auto column_types = price_imp->column_types_price();
+    auto old_col_type = column_types.at(col_num);
+
     price_imp->set_column_type_price (col_num, new_col_type);
 
+    // if old_col_type is TO_CURRENCY, force a reparse of commodity
+    if (old_col_type == GncPricePropType::TO_CURRENCY)
+    {
+        // look for a from_commodity column to reparse
+        preview_reparse_col_type (GncPricePropType::FROM_COMMODITY);
+    }
+
+    // if old_col_type is FROM_COMMODITY, force a reparse of currency
+    if (old_col_type == GncPricePropType::FROM_COMMODITY)
+    {
+        // look for a to_currency column to reparse
+        preview_reparse_col_type (GncPricePropType::TO_CURRENCY);
+    }
+
     /* Delay rebuilding our data table to avoid critical warnings due to
      * pending events still acting on them after this event is processed.
      */
@@ -1580,7 +1615,7 @@ void CsvImpPriceAssist::preview_refresh_table ()
 
     auto column_types = price_imp->column_types_price();
 
-    // look for a commodity column, clear the commdoity combo
+    // look for a commodity column, clear the commodity combo
     auto col_type_comm = std::find (column_types.begin(),
                 column_types.end(), GncPricePropType::FROM_COMMODITY);
     if (col_type_comm != column_types.end())
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index d5d3862..44a17b9 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -136,7 +136,6 @@ void GncPriceImport::over_write (bool over)
 {
     m_over_write = over;
 }
-
 bool GncPriceImport::over_write () { return m_over_write; }
 
 /** Sets a from commodity. This is the commodity all import data relates to.
@@ -147,18 +146,20 @@ bool GncPriceImport::over_write () { return m_over_write; }
 void GncPriceImport::from_commodity (gnc_commodity* from_commodity)
 {
     m_settings.m_from_commodity = from_commodity;
-
     if (m_settings.m_from_commodity)
     {
-        auto col_type = std::find (m_settings.m_column_types_price.begin(),
+        auto col_type_comm = std::find (m_settings.m_column_types_price.begin(),
                 m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY);
 
-        if (col_type != m_settings.m_column_types_price.end())
-            set_column_type_price (col_type -m_settings.m_column_types_price.begin(),
+        if (col_type_comm != m_settings.m_column_types_price.end())
+            set_column_type_price (col_type_comm -m_settings.m_column_types_price.begin(),
                             GncPricePropType::NONE);
+
+        // force a refresh of the to_currency if the from_commodity is changed
+        std::vector<GncPricePropType> commodities = { GncPricePropType::TO_CURRENCY };
+        reset_formatted_column (commodities);
     }
 }
-
 gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; }
 
 /** Sets a to currency. This is the to currency all import data relates to.
@@ -169,18 +170,20 @@ gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_comm
 void GncPriceImport::to_currency (gnc_commodity* to_currency)
 {
     m_settings.m_to_currency = to_currency;
-
     if (m_settings.m_to_currency)
     {
-        auto col_type = std::find (m_settings.m_column_types_price.begin(),
+        auto col_type_currency = std::find (m_settings.m_column_types_price.begin(),
                 m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY);
 
-        if (col_type != m_settings.m_column_types_price.end())
-            set_column_type_price (col_type -m_settings.m_column_types_price.begin(),
+        if (col_type_currency != m_settings.m_column_types_price.end())
+            set_column_type_price (col_type_currency -m_settings.m_column_types_price.begin(),
                             GncPricePropType::NONE);
+
+        // force a refresh of the from_commodity if the to_currency is changed
+        std::vector<GncPricePropType> commodities = { GncPricePropType::FROM_COMMODITY };
+        reset_formatted_column (commodities);
     }
 }
-
 gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; }
 
 void GncPriceImport::reset_formatted_column (std::vector<GncPricePropType>& col_types)
@@ -453,6 +456,14 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
         if (!m_settings.m_from_commodity)
             error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field."));
     }
+
+    /* Verify a 'Commodity from' does not equal 'Currency to'.
+     */
+    if ((m_settings.m_to_currency) && (m_settings.m_from_commodity))
+    {
+        if (gnc_commodity_equal (m_settings.m_to_currency, m_settings.m_from_commodity))
+            error_msg.add_error( _("'Commodity From' can not be the same as 'Currency To'."));
+    }
 }
 
 /* Check whether the chosen settings can successfully parse
@@ -539,7 +550,7 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
 
     error_message.clear();
 
-    // Add a CURRENCY_TO property with the selected 'currency to' if no 'currency to' column was set by the user
+    // Add a TO_CURRENCY property with the selected 'currency to' if no 'currency to' column was set by the user
     auto line_to_currency = price_props->get_to_currency();
     if (!line_to_currency)
     {
@@ -556,7 +567,7 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
         }
     }
 
-    // Add a COMMODITY_FROM property with the selected 'commodity from' if no 'commodity from' column was set by the user
+    // Add a FROM_COMMODITY property with the selected 'commodity from' if no 'commodity from' column was set by the user
     auto line_from_commodity = price_props->get_from_commodity();
     if (!line_from_commodity)
     {
@@ -657,6 +668,18 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro
     {
         try
         {
+            // set the from_commodity based on combo so we can test for same.
+            if (prop_type == GncPricePropType::TO_CURRENCY)
+            {
+                if (m_settings.m_from_commodity)
+                    price_props->set_from_commodity (m_settings.m_from_commodity);
+            }
+            // set the to_currency based on combo so we can test for same.
+            if (prop_type == GncPricePropType::FROM_COMMODITY)
+            {
+                if (m_settings.m_to_currency)
+                    price_props->set_to_currency (m_settings.m_to_currency);
+            }
             price_props->set(prop_type, value);
         }
         catch (const std::exception& e)

commit aff1c0c5a66e3d6861b25216066157754e4300d1
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:51:07 2017 +0000

    Add a test for from_commodity not being the same as to_currency

diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 2deffb3..0ea21f9 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -269,7 +269,11 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
                 m_from_commodity = boost::none;
                 comm = parse_commodity_price_comm (value); // Throws if parsing fails
                 if (comm)
+                {
+                    if (m_to_currency == comm)
+                        throw std::invalid_argument (_("'Commodity From' can not be the same as 'Currency To' column type."));
                     m_from_commodity = comm;
+                }
                 break;
 
             case GncPricePropType::TO_CURRENCY:
@@ -277,6 +281,8 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
                 comm = parse_commodity_price_comm (value); // Throws if parsing fails
                 if (comm)
                 {
+                    if (m_from_commodity == comm)
+                        throw std::invalid_argument (_("'Currency To' can not be the same as 'Commodity From' column type."));
                     if (gnc_commodity_is_currency (comm) != true)
                         throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type."));
                     m_to_currency = comm;
@@ -332,6 +338,8 @@ std::string GncImportPrice::verify_essentials (void)
         return _("No 'Currency to' column.");
     else if (m_from_commodity == boost::none)
         return _("No 'Commodity from' column.");
+    else if (gnc_commodity_equal (*m_from_commodity, *m_to_currency))
+        return _("'Commodity from' can not be the same as 'Currency to'.");
     else
         return std::string();
 }

commit 61f860bcfcfb793a61d75fc48b7b1be47b6ae056
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:49:28 2017 +0000

    Various changes to comments in source files and displayed text.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 075f463..14a8ffa 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -434,7 +434,7 @@ GtkTreeModel *get_model (bool all_commodity)
         /* Hide the template entry */
         if (g_utf8_collate (tmp_namespace, "template" ) != 0)
         {
-            if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) 
+            if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true))
             {
                 commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
                 commodity_list  = g_list_first (commodity_list);
@@ -1727,10 +1727,10 @@ CsvImpPriceAssist::assist_summary_page_prepare ()
 {
     auto text = std::string("<span size=\"medium\"><b>");
     text += _("The prices were imported from the file '") + m_file_name + "'.";
-    text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added);
-    text += _(", duplicated was ") + std::to_string(price_imp->m_prices_duplicated);
-    text += _(" and replaced was ") + std::to_string(price_imp->m_prices_replaced);
-    text += ".</b></span>";
+    text += _("\n\nThere were ") + std::to_string(price_imp->m_prices_added);
+    text += _(" Prices added, ") + std::to_string(price_imp->m_prices_duplicated);
+    text += _(" duplicated and ") + std::to_string(price_imp->m_prices_replaced);
+    text += _(" replaced.</b></span>");
 
     gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str());
 }
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
index 764b196..33db266 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
@@ -44,20 +44,26 @@
     <signal name="prepare" handler="csv_price_imp_assist_prepare_cb" swapped="no"/>
     <signal name="cancel" handler="csv_price_imp_assist_cancel_cb" swapped="no"/>
     <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <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 Prices from a CSV file.
 
-There is a minimum number of columns that have to be present for a successful import, for Stock prices these are Date, Amount, Symbol From and for Currency they are Date, Amount, Currency From and Currency To.
+There is a minimum number of columns that have to be present for a successful import, these are Date, Amount, Commodity From and Currency To. If all entries are for the same Commodity / Currency then you can select them and then the columns will be Date and Amount.
 
-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.
+Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the table of rows displayed to set a column width, then right mouse to change if required.
 
 Examples are "RR.L","21/11/2016",5.345,"GBP" and "USD","2016-11-21",1.56,"GBP"
 
 There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row which can be used if you have some header text. Also there is an option to over write existing prices for that day if required.
 
-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.
+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.
 
 This operation is not reversable, so make sure you have a working backup.
 
@@ -536,6 +542,7 @@ Select location and file name for the Import, then click 'OK'...
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
                                 <property name="tooltip_text" translatable="yes">Normally prices are not over written, select this to change that. This setting is not saved.</property>
+                                <property name="focus_on_click">False</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_overwrite_cb" swapped="no"/>
                               </object>
@@ -1094,8 +1101,9 @@ For example
               <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 add Prices.
-Cancel to abort.</property>
+                <property name="label" translatable="yes"><b>Press Apply to add the Prices.
+Cancel to abort.</b></property>
+                <property name="use_markup">True</property>
                 <property name="justify">center</property>
                 <property name="wrap">True</property>
               </object>
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 38a3cbc..d5d3862 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -62,7 +62,7 @@ const gchar* currency_format_user_price[] = {N_("Locale"),
 
 
 /** Constructor for GncPriceImport.
- * @return Pointer to a new GncCSvParseData
+ * @return Pointer to a new GncCsvParseData
  */
 GncPriceImport::GncPriceImport(GncImpFileFormat format)
 {
@@ -142,7 +142,7 @@ bool GncPriceImport::over_write () { return m_over_write; }
 /** Sets a from commodity. This is the commodity all import data relates to.
  *  When a from commodity is set, there can't be any from columns selected
  *  in the import data.
- * @param from_commodity Pointer to a commodity or NULL.
+ * @param from_commodity pointer to a commodity or NULL.
  */
 void GncPriceImport::from_commodity (gnc_commodity* from_commodity)
 {
@@ -164,7 +164,7 @@ gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_comm
 /** Sets a to currency. This is the to currency all import data relates to.
  *  When a to currency is set, there can't be any to currency columns selected
  *  in the import data.
- * @param to_currency Pointer to a commodity or NULL.
+ * @param to_currency pointer to a commodity or NULL.
  */
 void GncPriceImport::to_currency (gnc_commodity* to_currency)
 {
@@ -221,7 +221,6 @@ int GncPriceImport::date_format () { return m_settings.m_date_format; }
  */
 void GncPriceImport::encoding (const std::string& encoding)
 {
-
     // TODO investigate if we can catch conversion errors and report them
     if (m_tokenizer)
     {
@@ -309,12 +308,10 @@ void GncPriceImport::settings (const CsvTransSettings& settings)
     std::copy_n (settings.m_column_types_price.begin(),
             std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()),
             m_settings.m_column_types_price.begin());
-
 }
 
 bool GncPriceImport::save_settings ()
 {
-
     if (trans_preset_is_reserved_name (m_settings.m_name))
         return true;
 
@@ -327,7 +324,6 @@ bool GncPriceImport::save_settings ()
         auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
         m_settings.m_column_widths = fwtok->get_columns();
     }
-
     return m_settings.save();
 }
 
@@ -342,7 +338,6 @@ std::string GncPriceImport::settings_name () { return m_settings.m_name; }
  */
 void GncPriceImport::load_file (const std::string& filename)
 {
-
     /* Get the raw data first and handle an error if one occurs. */
     try
     {
@@ -407,7 +402,6 @@ void GncPriceImport::tokenize (bool guessColTypes)
     }
 }
 
-
 struct ErrorListPrice
 {
 public:
@@ -428,7 +422,6 @@ std::string ErrorListPrice::str()
     return m_error.substr(0, m_error.size() - 1);
 }
 
-
 /* Test for the required minimum number of columns selected and
  * the selection is consistent.
  * @param An ErrorListPrice object to which all found issues are added.
@@ -450,7 +443,7 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
     if (!check_for_column_type(GncPricePropType::TO_CURRENCY))
     {
         if (!m_settings.m_to_currency)
-            error_msg.add_error( _("Please select a Currency to column or set a Currency in the Currency To field."));
+            error_msg.add_error( _("Please select a 'Currency to' column or set a Currency in the 'Currency To' field."));
     }
 
     /* Verify a Commodity from column is selected.
@@ -458,11 +451,10 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
     if (!check_for_column_type(GncPricePropType::FROM_COMMODITY))
     {
         if (!m_settings.m_from_commodity)
-            error_msg.add_error( _("Please select a Commodity from column or set a Commodity in the Commodity From field."));
+            error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field."));
     }
 }
 
-
 /* Check whether the chosen settings can successfully parse
  * the import data. This will check:
  * - there's at least one line selected for import
@@ -547,7 +539,7 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
 
     error_message.clear();
 
-    // Add a CURRENCY_TO property with the default currency to if no currency to column was set by the user
+    // Add a CURRENCY_TO property with the selected 'currency to' if no 'currency to' column was set by the user
     auto line_to_currency = price_props->get_to_currency();
     if (!line_to_currency)
     {
@@ -555,16 +547,16 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
             price_props->set_to_currency(m_settings.m_to_currency);
         else
         {
-            // Oops - the user didn't select an Account column *and* we didn't get a default value either!
+            // Oops - the user didn't select a 'currency to' column *and* we didn't get a selected value either!
             // Note if you get here this suggests a bug in the code!
-            error_message = _("No Currency to column selected and no default Currency specified either.\n"
+            error_message = _("No 'Currency to' column selected and no selected Currency 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);
         }
     }
 
-    // Add a COMMODITY_FROM property with the default commodity from if no commodity from column was set by the user
+    // Add a COMMODITY_FROM property with the selected 'commodity from' if no 'commodity from' column was set by the user
     auto line_from_commodity = price_props->get_from_commodity();
     if (!line_from_commodity)
     {
@@ -572,9 +564,9 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
             price_props->set_from_commodity(m_settings.m_from_commodity);
         else
         {
-            // Oops - the user didn't select an Account column *and* we didn't get a default value either!
+            // Oops - the user didn't select a 'commodity from' column *and* we didn't get a selected value either!
             // Note if you get here this suggests a bug in the code!
-            error_message = _("No Commodity from column selected and no default Commodity specified either.\n"
+            error_message = _("No 'Commodity from' column selected and no selected Commodity 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);
@@ -605,7 +597,6 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
     }
 }
 
-
 /** Creates a list of prices from parsed data. The parsed data
  * will first be validated. If any errors are found in lines that are marked
  * for processing (ie not marked to skip) this function will
@@ -697,11 +688,11 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type,
 
     m_settings.m_column_types_price.at (position) = type;
 
-    // If the user has set a Commodity from column, we can't have a commodity from default set
+    // If the user has set a 'commodity from' column, we can't have a commodity from selected
     if (type == GncPricePropType::FROM_COMMODITY)
         from_commodity (nullptr);
 
-    // If the user has set a Currency to column, we can't have a currency to default set
+    // If the user has set a 'currency to' column, we can't have a currency to selected
     if (type == GncPricePropType::TO_CURRENCY)
         to_currency (nullptr);
 
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index d9293e1..2deffb3 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -160,7 +160,7 @@ time64 parse_date_price (const std::string &date_str, int format)
 }
 
 
-/** Convert str into a GncRational using the user-specified (import) currency format.
+/** Convert str into a GncNumeric using the user-specified (import) currency format.
  * @param str The string to be parsed
  * @param currency_format The currency format to use.
  * @return a GncNumeric
@@ -200,6 +200,11 @@ GncNumeric parse_amount_price (const std::string &str, int currency_format)
     return GncNumeric(val);
 }
 
+/** Convert comm_str into a gnc_commodity.
+ * @param comm_str The string to be parsed
+ * @return a gnc_commodity
+ * @exception May throw std::invalid argument if string can't be parsed properly
+ */
 gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
 {
     if (comm_str.empty())
@@ -324,9 +329,9 @@ std::string GncImportPrice::verify_essentials (void)
     else if (m_amount == boost::none)
         return _("No amount column.");
     else if (m_to_currency == boost::none)
-        return _("No Currency to column.");
+        return _("No 'Currency to' column.");
     else if (m_from_commodity == boost::none)
-        return _("No Commodity from column.");
+        return _("No 'Commodity from' column.");
     else
         return std::string();
 }
@@ -417,7 +422,6 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
 
         if (perr == false)
             throw std::invalid_argument (_("Failed to create price from selected columns."));
-//FIXME Not sure about this, should this be a PWARN
     }
     else
     {
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 1d44ff6..9ae4853 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -90,6 +90,7 @@ public:
     void set_date_format (int date_format) { m_date_format = date_format ;}
     void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
     void reset (GncPricePropType prop_type);
+    std::string verify_essentials (void);
     Result create_price (QofBook* book, GNCPriceDB *pdb, bool over);
 
     gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; }

commit 1bb2d1dc3890236aaa9f996e84723555f5c334e9
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:47:12 2017 +0000

    Change the way commodity and currency combo's are shown.
    
    Use commodity print name to show in the combo's and use a hidden field
    to sort the list grouping by namespace. Also alter the way these
    settings are saved.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 43f2d8a..075f463 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -419,11 +419,11 @@ GtkTreeModel *get_model (bool all_commodity)
 
     store = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER));
     model = gtk_tree_model_sort_new_with_model (store);
-    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
-                                        0, GTK_SORT_ASCENDING);
+    // set sort to sort on second string, first string will be shown
+    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), 1, GTK_SORT_ASCENDING);
 
     gtk_list_store_append (GTK_LIST_STORE(store), &iter);
-    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, nullptr, -1);
+    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, " ", 2, nullptr, -1);
 
     namespace_list = g_list_first (namespace_list);
     while (namespace_list != nullptr)
@@ -440,23 +440,20 @@ GtkTreeModel *get_model (bool all_commodity)
                 commodity_list  = g_list_first (commodity_list);
                 while (commodity_list != nullptr)
                 {
-                    gchar *name_str;
-                    gchar *save_str;
-                    gchar *settings_str;
+                    const gchar *name_str;
+                    gchar *sort_str;
                     tmp_commodity = (gnc_commodity*)commodity_list->data;
                     DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity));
 
-                    name_str = g_strconcat (tmp_namespace, " : (", gnc_commodity_get_mnemonic (tmp_commodity),
-                                            ") ", gnc_commodity_get_fullname (tmp_commodity), nullptr);
+                    name_str = gnc_commodity_get_printname (tmp_commodity);
 
-                    settings_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr);
-                    DEBUG("Name string is %s, Save string is %s", name_str, settings_str);
+                    sort_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr);
+                    DEBUG("Name string is %s, Sort string is %s", name_str, sort_str);
 
                     gtk_list_store_append (GTK_LIST_STORE(store), &iter);
-                    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, settings_str, 2, tmp_commodity, -1);
+                    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, sort_str, 2, tmp_commodity, -1);
 
-                    g_free (name_str);
-                    g_free (settings_str);
+                    g_free (sort_str);
                     commodity_list = g_list_next (commodity_list);
                 }
             }
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 73ab2a5..3bbc984 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -294,7 +294,7 @@ CsvTransSettings::load (void)
         if (col_types_str)
             g_strfreev (col_types_str);
     }
- 
+
     // Price
     if (m_settings_type.compare("PRICE") == 0)
     {
@@ -424,10 +424,20 @@ CsvTransSettings::save (void)
     if (m_settings_type.compare("PRICE") == 0)
     {
         if (m_to_currency)
-            g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_mnemonic(m_to_currency));
+        {
+            auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::",
+                               gnc_commodity_get_mnemonic (m_to_currency), nullptr);
+            g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name);
+            g_free (unique_name);
+        }
 
         if (m_from_commodity)
-            g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_mnemonic(m_from_commodity));
+        {
+            auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::",
+                               gnc_commodity_get_mnemonic (m_from_commodity), nullptr);
+            g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name);
+            g_free (unique_name);
+        }
 
         std::vector<const char*> col_types_str_price;
         for (auto col_type : m_column_types_price)

commit caba8c433021673d630b4f7459ada4290c2798ef
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:44:38 2017 +0000

    Minor changes and tidy up

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index e3a5ae3..43f2d8a 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -76,7 +76,7 @@ public:
     void assist_preview_page_prepare ();
     void assist_confirm_page_prepare ();
     void assist_summary_page_prepare ();
-    void assist_finish (bool canceled);
+    void assist_finish ();
     void assist_compmgr_close ();
 
     void file_confirm_cb ();
@@ -213,7 +213,6 @@ csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info)
 void
 csv_price_imp_assist_cancel_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
 {
-    info->assist_finish (true);
     gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info);
 }
 
@@ -226,7 +225,7 @@ csv_price_imp_assist_close_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
 void
 csv_price_imp_assist_finish_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
 {
-    info->assist_finish (false);
+    info->assist_finish ();
 }
 
 void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info)
@@ -356,7 +355,7 @@ gnc_commodity *get_commodity_from_combo (GtkComboBox *combo)
     GtkTreeModel *model, *sort_model;
     GtkTreeIter  iter, siter;
     gchar *string;
-   gnc_commodity *comm;
+    gnc_commodity *comm;
 
     if (!gtk_combo_box_get_active_iter (combo, &siter))
         return nullptr;
@@ -741,7 +740,6 @@ void CsvImpPriceAssist::preview_populate_settings_combo()
     gtk_list_store_clear (GTK_LIST_STORE(model));
 
     // Append the default entry
-//FIXME get_trans_presets ????
     auto presets = get_trans_presets (settings_type);
     for (auto preset : presets)
     {
@@ -773,7 +771,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
         CsvTransSettings *preset;
         GtkTreeModel *model = gtk_combo_box_get_model (combo);
         gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
-//FIXME
+
         if (preset && !trans_preset_is_reserved_name (preset->m_name))
         {
             /* Current preset is not read_only, so buttons can be enabled */
@@ -781,7 +779,6 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
             can_save = true;
         }
     }
-//FIXME
     else if (entry_text && (strlen (entry_text) > 0) &&
             !trans_preset_is_reserved_name (std::string(entry_text)))
         can_save = true;
@@ -944,6 +941,8 @@ void CsvImpPriceAssist::preview_update_skipped_rows ()
     preview_refresh_table ();
 }
 
+/* Callback triggered when user clicks on Over Write option
+ */
 void CsvImpPriceAssist::preview_over_write (bool over)
 {
     price_imp->over_write (over);
@@ -1723,6 +1722,7 @@ CsvImpPriceAssist::assist_preview_page_prepare ()
 void
 CsvImpPriceAssist::assist_confirm_page_prepare ()
 {
+    /* Confirm Page */
 }
 
 void
@@ -1752,19 +1752,9 @@ CsvImpPriceAssist::assist_prepare_cb (GtkWidget *page)
 }
 
 void
-CsvImpPriceAssist::assist_finish (bool canceled)
+CsvImpPriceAssist::assist_finish ()
 {
     /* Start the import */
-//FIXME Apply button
-g_print("Finish\n");
-//    if (canceled || price_imp->m_transactions.empty())
-//        gnc_gen_trans_list_delete (gnc_csv_importer_gui);
-//    else
-//        gnc_gen_trans_assist_start (gnc_csv_importer_gui);
-
-
-//FIXME Cancel comes here to, check when nothing set, goes to catch below also
-
     /* Create prices from the parsed data */
     try
     {

commit 3a3c2cba9c55df5cb8ac0dfd9ee34801d353243f
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:43:14 2017 +0000

    Change the settings file to save and load price settings.
    
    Added a setting type to distinguish between TRANS and PRICE settings so
    it can load a specific settings type and added the price save and load
    options.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 2d2cfdd..e3a5ae3 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -64,6 +64,8 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
+const std::string settings_type = "PRICE";
+
 class  CsvImpPriceAssist
 {
 public:
@@ -740,7 +742,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo()
 
     // Append the default entry
 //FIXME get_trans_presets ????
-    auto presets = get_trans_presets ();
+    auto presets = get_trans_presets (settings_type);
     for (auto preset : presets)
     {
         GtkTreeIter iter;
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index 9402f38..07091f0 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -70,6 +70,8 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
+const std::string settings_type = "TRANS";
+
 class  CsvImpTransAssist
 {
 public:
@@ -677,7 +679,7 @@ void CsvImpTransAssist::preview_populate_settings_combo()
 
     // Append the default entry
 
-    auto presets = get_trans_presets ();
+    auto presets = get_trans_presets (settings_type);
     for (auto preset : presets)
     {
         GtkTreeIter iter;
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 5235a4c..73ab2a5 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -41,7 +41,7 @@ extern "C"
 #include "gnc-ui-util.h"
 }
 
-const std::string csv_group_prefix{"CSV - "};
+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"
@@ -70,10 +70,11 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
 preset_vec presets;
 
-static std::shared_ptr<CsvTransSettings> create_int_no_preset(void)
+static std::shared_ptr<CsvTransSettings> create_int_no_preset(const std::string& set_type)
 {
     auto preset = std::make_shared<CsvTransSettings>();
     preset->m_name = no_settings;
+    preset->m_settings_type = set_type;
 
     return preset;
 }
@@ -109,14 +110,6 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
             GncTransPropType::REC_DATE,
             GncTransPropType::PRICE
     };
-
-    preset->m_column_types_price = {
-            GncPricePropType::DATE,
-            GncPricePropType::AMOUNT,
-            GncPricePropType::FROM_COMMODITY,
-            GncPricePropType::TO_CURRENCY,
-    };
-
     return preset;
 }
 
@@ -124,8 +117,9 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
  * find
  *
  * find all settings entries in the state key file
+ * based on settings type.
  **************************************************/
-const preset_vec& get_trans_presets (void)
+const preset_vec& get_trans_presets (const std::string& set_type)
 {
 
     // Search all Groups in the state key file for ones starting with prefix
@@ -138,11 +132,12 @@ const preset_vec& get_trans_presets (void)
     for (gsize i=0; i < grouplength; i++)
     {
         auto group = std::string(groups[i]);
-        auto pos = group.find(csv_group_prefix);
+        auto gp = csv_group_prefix + set_type + " - ";
+        auto pos = group.find(gp);
         if (pos == std::string::npos)
             continue;
 
-        preset_names.push_back(group.substr(csv_group_prefix.size()));
+        preset_names.push_back(group.substr(gp.size()));
     }
     // string array from the state file is no longer needed now.
     g_strfreev (groups);
@@ -154,18 +149,20 @@ const preset_vec& get_trans_presets (void)
     presets.clear();
 
     /* Start with the internally generated ones */
-    presets.push_back(create_int_no_preset());
-    presets.push_back(create_int_gnc_exp_preset());
+    presets.push_back(create_int_no_preset(set_type));
+
+    if (set_type.compare("TRANS") == 0)
+        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->m_settings_type = set_type;
         preset->m_name = preset_name;
         preset->load();
         presets.push_back(preset);
     }
-
     return presets;
 }
 
@@ -215,7 +212,7 @@ CsvTransSettings::load (void)
 
     GError *key_error = nullptr;
     m_load_error = false;
-    auto group = csv_group_prefix + m_name;
+    auto group = csv_group_prefix + m_settings_type + " - " + m_name;
     auto keyfile = gnc_state_get_current ();
 
     m_skip_start_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
@@ -260,56 +257,82 @@ 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')
-        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);
-
-    key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
-    if (key_char && *key_char != '\0')
-//FIXME        m_to_currency = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
-        m_to_currency = nullptr;
-    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_FROM_COMM, &key_error);
-    if (key_char && *key_char != '\0')
-//FIXME        m_from_commodity = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
-        m_from_commodity = nullptr;
-    m_load_error |= handle_load_error (&key_error, group);
-    if (key_char)
-        g_free (key_char);
-
-    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);
-    for (uint32_t i = 0; i < list_len; i++)
+
+    // Transactions
+    if (m_settings_type.compare("TRANS") == 0)
     {
-        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())
+        key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
+        if (key_char && *key_char != '\0')
+            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);
+
+        m_column_types.clear();
+        gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+                &list_len, &key_error);
+        for (uint32_t i = 0; i < list_len; i++)
         {
-            /* 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, 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, m_multi_split ? "enabled" : "disabled");
+            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())
+            {
+                /* 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, 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, m_multi_split ? "enabled" : "disabled");
+            }
+            else
+                PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
+                        col_types_str[i]);
         }
-        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);
+    }
+ 
+    // Price
+    if (m_settings_type.compare("PRICE") == 0)
+    {
+        key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
+        if (key_char && *key_char != '\0')
+            m_to_currency = parse_commodity_price_comm (key_char);
+        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_FROM_COMM, &key_error);
+        if (key_char && *key_char != '\0')
+            m_from_commodity = parse_commodity_price_comm (key_char);
+        m_load_error |= handle_load_error (&key_error, group);
+        if (key_char)
+            g_free (key_char);
+
+        m_column_types.clear();
+        gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+                &list_len, &key_error);
+        for (uint32_t i = 0; i < list_len; i++)
+        {
+            auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(),
+                    gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i]));
+            if (col_types_it != gnc_price_col_type_strs.end())
+            {
+                // Found a valid column type
+                m_column_types_price.push_back(col_types_it->first);
+            }
+            else
+                PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
+                        col_types_str_price[i]);
+        }
+        if (col_types_str_price)
+            g_strfreev (col_types_str_price);
     }
-    if (col_types_str)
-        g_strfreev (col_types_str);
 
+    // Widths
     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);
@@ -347,14 +370,15 @@ CsvTransSettings::save (void)
     }
 
     auto keyfile = gnc_state_get_current ();
-    auto group = csv_group_prefix + m_name;
+    auto group = csv_group_prefix + m_settings_type + " - " + m_name;
 
     // Drop previous saved settings with this name
     g_key_file_remove_group (keyfile, group.c_str(), nullptr);
 
     // Start Saving the settings
+    // Common
     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);
@@ -375,26 +399,44 @@ CsvTransSettings::save (void)
     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 (m_base_account)
-        g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account));
+    if (!m_column_widths.empty())
+        g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
+                (gint*)(m_column_widths.data()), m_column_widths.size());
 
-    if (m_to_currency)
-        g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_fullname(m_to_currency));
+    // Transaction
+    if (m_settings_type.compare("TRANS") == 0)
+    {
+        g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split);
 
-    if (m_from_commodity)
-        g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_fullname(m_from_commodity));
+        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 : m_column_types)
-        col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
+        std::vector<const char*> col_types_str;
+        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 (!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 (!m_column_widths.empty())
-        g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
-                (gint*)(m_column_widths.data()), m_column_widths.size());
+    // Price
+    if (m_settings_type.compare("PRICE") == 0)
+    {
+        if (m_to_currency)
+            g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_mnemonic(m_to_currency));
+
+        if (m_from_commodity)
+            g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_mnemonic(m_from_commodity));
+
+        std::vector<const char*> col_types_str_price;
+        for (auto col_type : m_column_types_price)
+            col_types_str_price.push_back(gnc_price_col_type_strs[col_type]);
+
+        if (!col_types_str_price.empty())
+            g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+                    col_types_str_price.data(), col_types_str_price.size());
+    }
 
     // Do a test read of encoding
     GError *key_error = nullptr;
@@ -425,7 +467,7 @@ CsvTransSettings::remove (void)
         return;
 
     auto keyfile = gnc_state_get_current ();
-    auto group = csv_group_prefix + m_name;
+    auto group = csv_group_prefix + m_settings_type + " - " + m_name;
     g_key_file_remove_group (keyfile, group.c_str(), nullptr);
 }
 
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
index dae837d..7df293c 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -54,8 +54,8 @@ struct CsvTransSettings
     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_to_currency {nullptr},
-            m_from_commodity {nullptr}, m_load_error {false} { }
+            m_separators {","}, m_load_error {false}, m_base_account {nullptr},
+            m_from_commodity {nullptr}, m_to_currency {nullptr} { }
 
 /** Save the gathered widget properties to a key File.
  *
@@ -81,7 +81,9 @@ void remove (void);
  */
 bool read_only (void);
 
+std::string   m_settings_type;                // Settings Type, TRANS, PRICE etc.
 
+// Common Settings
 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
@@ -92,16 +94,17 @@ uint32_t      m_skip_start_lines;             // Number of header rows to skip
 uint32_t      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
+bool          m_load_error;                   // Was there an error while parsing the state file ?
+std::vector<uint32_t> m_column_widths;        // The Column widths
 
+// Transaction Settings
 Account      *m_base_account;                 // Base account
 std::vector<GncTransPropType> m_column_types; // The Column types in order
-std::vector<GncPricePropType> m_column_types_price; // The Column Price types in order
-std::vector<uint32_t> m_column_widths;        // The Column widths
 
-gnc_commodity *m_to_currency;                 //  Price To Currency
+// Price Settings
 gnc_commodity *m_from_commodity;              //  Price From Commodity
-
-bool          m_load_error;                   // Was there an error while parsing the state file ?
+gnc_commodity *m_to_currency;                 //  Price To Currency
+std::vector<GncPricePropType> m_column_types_price; // The Price Column types in order
 };
 
 using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
@@ -109,9 +112,12 @@ using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
  *  - one or more internally defined presets
  *  - all preset found in the state key file.
  *
+ *  @param set_type The type of setting stored in the
+ *  key file, TRANS, PRICE, etc.
+ *
  *  @return a reference to the populated vector.
  */
-const preset_vec& get_trans_presets (void);
+const preset_vec& get_trans_presets (const std::string& set_type);
 
 /** Check whether name can be used as a preset name.
  *  The names of the internal presets are considered reserved.

commit 3c18b8063459a2ef2634c854c065764cbaa50bf7
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:34:21 2017 +0000

    Fix some errors in conversion of some function names
    
    Some function names did not get converted to a price equivalent and
    reorder some statements.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 120367a..2d2cfdd 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -607,9 +607,9 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
 
         /* 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++)
+        for (int i = 0; i < num_date_formats_price; i++)
         {
-            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i]));
+            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user_price[i]));
         }
         gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0);
         g_signal_connect (G_OBJECT(date_format_combo), "changed",
@@ -622,9 +622,9 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
 
         /* 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++)
+        for (int i = 0; i < num_currency_formats_price; i++)
         {
-            gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i]));
+            gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user_price[i]));
         }
         /* Default will the locale */
         gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0);
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index e7bb72a..38a3cbc 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -284,9 +284,9 @@ void GncPriceImport::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;
     from_commodity (m_settings.m_from_commodity);
     to_currency (m_settings.m_to_currency);
-    m_settings = settings;
     encoding (m_settings.m_encoding);
 
     if (file_format() == GncImpFileFormat::CSV)
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 809464e..be91941 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -46,12 +46,12 @@ extern "C" {
 #include <boost/optional.hpp>
 
 /* A set of currency formats that the user sees. */
-extern const int num_currency_formats;
-extern const gchar* currency_format_user[];
+extern const int num_currency_formats_price;
+extern const gchar* currency_format_user_price[];
 
 /* A set of date formats that the user sees. */
-extern const int num_date_formats;
-extern const gchar* date_format_user[];
+extern const int num_date_formats_price;
+extern const gchar* date_format_user_price[];
 
 /** Tuple to hold
  *  - a tokenized line of input
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 9ae4853..1d44ff6 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -90,7 +90,6 @@ public:
     void set_date_format (int date_format) { m_date_format = date_format ;}
     void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
     void reset (GncPricePropType prop_type);
-    std::string verify_essentials (void);
     Result create_price (QofBook* book, GNCPriceDB *pdb, bool over);
 
     gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; }

commit 16845c3a2096906bf1a72eb30eae997745b39c3a
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:32:48 2017 +0000

    Remove duplicated function

diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index e6f983e..d9293e1 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -240,56 +240,6 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
         return comm;
 }
 
-//FIXME can we change above to do below
-gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency)
-{
-    if (sym_str.empty())
-        return nullptr;
-
-    auto commodity_table = gnc_get_current_commodities ();
-    GList         *namespaces;
-    gnc_commodity *retval = nullptr;
-    gnc_commodity *tmp_commodity = nullptr;
-    char  *tmp_namespace = nullptr;
-    GList *commodity_list = NULL;
-    GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
-
-    namespace_list = g_list_first (namespace_list);
-    while (namespace_list != NULL && retval == NULL)
-    {
-        tmp_namespace = (char*)namespace_list->data;
-        DEBUG("Looking at namespace %s", tmp_namespace);
-        commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
-        commodity_list  = g_list_first (commodity_list);
-        while (commodity_list != NULL && retval == NULL)
-        {
-            const char* tmp_mnemonic = NULL;
-            tmp_commodity = (gnc_commodity*)commodity_list->data;
-            DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity));
-            tmp_mnemonic = gnc_commodity_get_mnemonic (tmp_commodity);
-            if (g_strcmp0 (tmp_mnemonic, sym_str.c_str()) == 0)
-            {
-                retval = tmp_commodity;
-                DEBUG("Commodity %s%s", gnc_commodity_get_fullname (retval), " matches.");
-            }
-            commodity_list = g_list_next (commodity_list);
-        }
-        namespace_list = g_list_next (namespace_list);
-    }
-    g_list_free (commodity_list);
-    g_list_free (namespace_list);
-
-    if (!retval)
-        throw std::invalid_argument (_("Value can't be parsed into a valid commodity."));
-    else
-    {
-        if ((is_currency == true) && (gnc_commodity_is_currency (retval) != true))
-                throw std::invalid_argument (_("Value parsed into an invalid currency for currency column type."));
-        else
-            return retval;
-    }
-}
-
 void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
 {
     try
@@ -307,21 +257,25 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
 
             case GncPricePropType::AMOUNT:
                 m_amount = boost::none;
-                m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails
+                m_amount = parse_amount_price (value, m_currency_format); // Throws if parsing fails
                 break;
 
             case GncPricePropType::FROM_COMMODITY:
                 m_from_commodity = boost::none;
-                comm = parse_commodity_price_sym (value, false); // Throws if parsing fails
+                comm = parse_commodity_price_comm (value); // Throws if parsing fails
                 if (comm)
                     m_from_commodity = comm;
                 break;
 
             case GncPricePropType::TO_CURRENCY:
                 m_to_currency = boost::none;
-                comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
+                comm = parse_commodity_price_comm (value); // Throws if parsing fails
                 if (comm)
+                {
+                    if (gnc_commodity_is_currency (comm) != true)
+                        throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type."));
                     m_to_currency = comm;
+                }
                 break;
 
             default:
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index a8550ea..9ae4853 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -78,7 +78,6 @@ private:
 
 time64 parse_date_price (const std::string &date_str, int format);
 gnc_commodity* parse_commodity_price_comm (const std::string& comm_str);
-gnc_commodity* parse_commodity_price_sym (const std::string& comm_str, bool is_currency);
 GncNumeric parse_amount_price (const std::string &str, int currency_format);
 
 struct GncImportPrice

commit 1e31db74d1143b3a7cd7fe39f1e42a2f935c04d7
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:30:16 2017 +0000

    Made changes to preset column types to align with other changes
    
    These changes are to align with the changes to column types and also the
     basic setup of the new commodity from and currency to combo's. More
     changes will follow to make the saving and loading work properly.

diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 48cfdce..5235a4c 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -63,6 +63,8 @@ const std::string gnc_exp{N_("GnuCash Export Format")};
 #define CSV_COL_TYPES    "ColumnTypes"
 #define CSV_COL_WIDTHS   "ColumnWidths"
 #define CSV_ACCOUNT      "BaseAccount"
+#define CSV_TO_CURR      "PriceToCurrency"
+#define CSV_FROM_COMM    "PriceFromCommodity"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -111,9 +113,8 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
     preset->m_column_types_price = {
             GncPricePropType::DATE,
             GncPricePropType::AMOUNT,
-            GncPricePropType::CURRENCY_FROM,
-            GncPricePropType::CURRENCY_TO,
-            GncPricePropType::SYMBOL_FROM
+            GncPricePropType::FROM_COMMODITY,
+            GncPricePropType::TO_CURRENCY,
     };
 
     return preset;
@@ -266,6 +267,22 @@ CsvTransSettings::load (void)
     if (key_char)
         g_free (key_char);
 
+    key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
+    if (key_char && *key_char != '\0')
+//FIXME        m_to_currency = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
+        m_to_currency = nullptr;
+    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_FROM_COMM, &key_error);
+    if (key_char && *key_char != '\0')
+//FIXME        m_from_commodity = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
+        m_from_commodity = nullptr;
+    m_load_error |= handle_load_error (&key_error, group);
+    if (key_char)
+        g_free (key_char);
+
     m_column_types.clear();
     gsize list_len;
     gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
@@ -361,6 +378,12 @@ CsvTransSettings::save (void)
     if (m_base_account)
         g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account));
 
+    if (m_to_currency)
+        g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_fullname(m_to_currency));
+
+    if (m_from_commodity)
+        g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_fullname(m_from_commodity));
+
     std::vector<const char*> col_types_str;
     for (auto col_type : m_column_types)
         col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 93275f2..dae837d 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -31,6 +31,7 @@
 extern "C" {
 #include <config.h>
 #include "Account.h"
+#include "gnc-commodity.h"
 }
 
 #include <string>
@@ -53,8 +54,8 @@ struct CsvTransSettings
     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} { }
+            m_separators {","}, m_base_account {nullptr}, m_to_currency {nullptr},
+            m_from_commodity {nullptr}, m_load_error {false} { }
 
 /** Save the gathered widget properties to a key File.
  *
@@ -97,6 +98,9 @@ std::vector<GncTransPropType> m_column_types; // The Column types in order
 std::vector<GncPricePropType> m_column_types_price; // The Column Price types in order
 std::vector<uint32_t> m_column_widths;        // The Column widths
 
+gnc_commodity *m_to_currency;                 //  Price To Currency
+gnc_commodity *m_from_commodity;              //  Price From Commodity
+
 bool          m_load_error;                   // Was there an error while parsing the state file ?
 };
 

commit 8f3e175fb2d3b665e597527e52cbe1cf2e242528
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:25:06 2017 +0000

    Add option to specify Commodity from and Currency to for whole file
    
    Added two combo's to allow user to specify a Commodity from and Currency
     to for the whole file. Also reduced the property types to four and
     aligned all the commodity and currency variables.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 6c4279a..120367a 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -90,6 +90,8 @@ public:
     void preview_update_encoding (const char* encoding);
     void preview_update_date_format ();
     void preview_update_currency_format ();
+    void preview_update_currency ();
+    void preview_update_commodity ();
     void preview_update_col_type (GtkComboBox* cbox);
     void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
 
@@ -134,6 +136,8 @@ private:
     GtkWidget       *csv_button;                    /**< The widget for the CSV button */
     GtkWidget       *fixed_button;                  /**< The widget for the Fixed Width button */
     GtkWidget       *over_write_cbutton;            /**< The widget for Price Over Write */
+    GtkWidget       *commodity_selector;            /**< The widget for commodity combo box */
+    GtkWidget       *currency_selector;             /**< The widget for currency combo box */
     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 */
@@ -321,6 +325,16 @@ static void csv_price_imp_preview_currency_fmt_sel_cb (GtkComboBox* format_selec
     info->preview_update_currency_format();
 }
 
+static void csv_price_imp_preview_currency_sel_cb (GtkComboBox* currency_selector, CsvImpPriceAssist* info)
+{
+    info->preview_update_currency();
+}
+
+static void csv_price_imp_preview_commodity_sel_cb (GtkComboBox* commodity_selector, CsvImpPriceAssist* info)
+{
+    info->preview_update_commodity();
+}
+
 void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info)
 {
     info->preview_update_col_type (cbox);
@@ -334,6 +348,126 @@ csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton
     return false;
 }
 
+static
+gnc_commodity *get_commodity_from_combo (GtkComboBox *combo)
+{
+    GtkTreeModel *model, *sort_model;
+    GtkTreeIter  iter, siter;
+    gchar *string;
+   gnc_commodity *comm;
+
+    if (!gtk_combo_box_get_active_iter (combo, &siter))
+        return nullptr;
+
+    sort_model = gtk_combo_box_get_model (combo);
+    model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model));
+
+    gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(sort_model),
+                                                    &iter, &siter);
+
+    gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &string, 2, &comm, -1);
+
+    PINFO("Commodity string is %s", string);
+
+    g_free (string);
+    return comm;
+}
+
+static void
+set_commodity_for_combo (GtkComboBox *combo, gnc_commodity *comm)
+{
+    GtkTreeModel *model, *sort_model;
+    GtkTreeIter  iter, siter;
+    gnc_commodity *model_comm;
+    gboolean valid;
+
+    sort_model = gtk_combo_box_get_model (combo);
+    model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model));
+    valid = gtk_tree_model_get_iter_first (model, &iter);
+
+    while (valid)
+    {
+        gtk_tree_model_get (model, &iter, 2, &model_comm, -1);
+        if (model_comm == comm)
+        {
+            if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(sort_model), &siter, &iter))
+            {
+                gtk_combo_box_set_active_iter (combo, &siter);
+                return;
+            }
+        }
+        /* Make iter point to the next row in the list store */
+        valid = gtk_tree_model_iter_next (model, &iter);
+    }
+    // Not found, set it to first iter
+    valid = gtk_tree_model_get_iter_first (model, &iter);
+    if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(sort_model), &siter, &iter))
+        gtk_combo_box_set_active_iter (combo, &siter);
+}
+
+static
+GtkTreeModel *get_model (bool all_commodity)
+{
+    GtkTreeModel *store, *model;
+    const gnc_commodity_table *commodity_table = gnc_get_current_commodities ();
+    gnc_commodity *tmp_commodity = nullptr;
+    char  *tmp_namespace = nullptr;
+    GList *commodity_list = nullptr;
+    GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
+    GtkTreeIter iter;
+
+    store = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER));
+    model = gtk_tree_model_sort_new_with_model (store);
+    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
+                                        0, GTK_SORT_ASCENDING);
+
+    gtk_list_store_append (GTK_LIST_STORE(store), &iter);
+    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, nullptr, -1);
+
+    namespace_list = g_list_first (namespace_list);
+    while (namespace_list != nullptr)
+    {
+        tmp_namespace = (char*)namespace_list->data;
+        DEBUG("Looking at namespace %s", tmp_namespace);
+
+        /* Hide the template entry */
+        if (g_utf8_collate (tmp_namespace, "template" ) != 0)
+        {
+            if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) 
+            {
+                commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
+                commodity_list  = g_list_first (commodity_list);
+                while (commodity_list != nullptr)
+                {
+                    gchar *name_str;
+                    gchar *save_str;
+                    gchar *settings_str;
+                    tmp_commodity = (gnc_commodity*)commodity_list->data;
+                    DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity));
+
+                    name_str = g_strconcat (tmp_namespace, " : (", gnc_commodity_get_mnemonic (tmp_commodity),
+                                            ") ", gnc_commodity_get_fullname (tmp_commodity), nullptr);
+
+                    settings_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr);
+                    DEBUG("Name string is %s, Save string is %s", name_str, settings_str);
+
+                    gtk_list_store_append (GTK_LIST_STORE(store), &iter);
+                    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, settings_str, 2, tmp_commodity, -1);
+
+                    g_free (name_str);
+                    g_free (settings_str);
+                    commodity_list = g_list_next (commodity_list);
+                }
+            }
+        }
+        namespace_list = g_list_next (namespace_list);
+    }
+    g_list_free (commodity_list);
+    g_list_free (namespace_list);
+
+    return model;
+}
+
 
 /*******************************************************
  * Assistant Constructor
@@ -343,6 +477,8 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
     auto builder = gtk_builder_new();
     gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "start_row_adj");
     gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "end_row_adj");
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "liststore1");
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "liststore2");
     gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "CSV Price Assistant");
     csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Price Assistant"));
 
@@ -453,6 +589,18 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
         gtk_container_add (encoding_container, GTK_WIDGET(encselector));
         gtk_widget_show_all (GTK_WIDGET(encoding_container));
 
+        /* Add commodity selection widget */
+        commodity_selector = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_cbox"));
+        gtk_combo_box_set_model (GTK_COMBO_BOX(commodity_selector), get_model (true));
+        g_signal_connect(G_OBJECT(commodity_selector), "changed",
+                         G_CALLBACK(csv_price_imp_preview_commodity_sel_cb), this);
+
+        /* Add currency selection widget */
+        currency_selector = GTK_WIDGET(gtk_builder_get_object (builder, "currency_cbox"));
+        gtk_combo_box_set_model (GTK_COMBO_BOX(currency_selector), get_model (false));
+        g_signal_connect(G_OBJECT(currency_selector), "changed",
+                         G_CALLBACK(csv_price_imp_preview_currency_sel_cb), this);
+
         /* 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"));
@@ -570,6 +718,9 @@ CsvImpPriceAssist::file_confirm_cb ()
     preview_populate_settings_combo();
     gtk_combo_box_set_active (settings_combo, 0);
 
+    // set over_write to false as default
+    price_imp->over_write (false);
+
     auto num = gtk_assistant_get_current_page (csv_imp_asst);
     gtk_assistant_set_current_page (csv_imp_asst, num + 1);
 }
@@ -951,6 +1102,22 @@ CsvImpPriceAssist::preview_update_currency_format ()
     preview_refresh_table ();
 }
 
+void
+CsvImpPriceAssist::preview_update_currency ()
+{
+    gnc_commodity *comm = get_commodity_from_combo (GTK_COMBO_BOX(currency_selector));
+    price_imp->to_currency (comm);
+    preview_refresh_table ();
+}
+
+void
+CsvImpPriceAssist::preview_update_commodity ()
+{
+    gnc_commodity *comm = get_commodity_from_combo (GTK_COMBO_BOX(commodity_selector));
+    price_imp->from_commodity (comm);
+    preview_refresh_table ();
+}
+
 gboolean
 csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist)
 {
@@ -1413,6 +1580,28 @@ void CsvImpPriceAssist::preview_refresh_table ()
     for (uint32_t i = 0; i < ntcols; i++)
         preview_style_column (i, combostore);
 
+    auto column_types = price_imp->column_types_price();
+
+    // look for a commodity column, clear the commdoity combo
+    auto col_type_comm = std::find (column_types.begin(),
+                column_types.end(), GncPricePropType::FROM_COMMODITY);
+    if (col_type_comm != column_types.end())
+    {
+        g_signal_handlers_block_by_func (commodity_selector, (gpointer) csv_price_imp_preview_commodity_sel_cb, this);
+        set_commodity_for_combo (GTK_COMBO_BOX(commodity_selector), nullptr);
+        g_signal_handlers_unblock_by_func (commodity_selector, (gpointer) csv_price_imp_preview_commodity_sel_cb, this);
+    }
+
+    // look for a currency column, clear the currency combo
+    auto col_type_curr = std::find (column_types.begin(),
+                column_types.end(), GncPricePropType::TO_CURRENCY);
+    if (col_type_curr != column_types.end())
+    {
+        g_signal_handlers_block_by_func (currency_selector, (gpointer) csv_price_imp_preview_currency_sel_cb, this);
+        set_commodity_for_combo (GTK_COMBO_BOX(currency_selector), nullptr);
+        g_signal_handlers_unblock_by_func (currency_selector, (gpointer) csv_price_imp_preview_currency_sel_cb, this);
+    }
+
     /* Release our reference for the stores to allow proper memory management. */
     g_object_unref (store);
     g_object_unref (combostore);
@@ -1460,6 +1649,13 @@ CsvImpPriceAssist::preview_refresh ()
             price_imp->currency_format());
     go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str());
 
+    // Set the commodity and currency combos
+    set_commodity_for_combo(GTK_COMBO_BOX(commodity_selector),
+            price_imp->from_commodity());
+
+    set_commodity_for_combo(GTK_COMBO_BOX(currency_selector),
+            price_imp->to_currency());
+
     // Handle separator checkboxes and custom field, only relevant if the file format is csv
     if (price_imp->file_format() == GncImpFileFormat::CSV)
     {
@@ -1533,9 +1729,9 @@ CsvImpPriceAssist::assist_summary_page_prepare ()
     auto text = std::string("<span size=\"medium\"><b>");
     text += _("The prices were imported from the file '") + m_file_name + "'.";
     text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added);
-    text += _(" and ") + std::to_string(price_imp->m_prices_duplicated);
-    text += _(" were duplicated.");
-    text += "</b></span>";
+    text += _(", duplicated was ") + std::to_string(price_imp->m_prices_duplicated);
+    text += _(" and replaced was ") + std::to_string(price_imp->m_prices_replaced);
+    text += ".</b></span>";
 
     gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str());
 }
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
index 705e827..764b196 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
@@ -7,6 +7,26 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkListStore" id="liststore1">
+    <columns>
+      <!-- column-name string -->
+      <column type="gchararray"/>
+      <!-- column-name save -->
+      <column type="gchararray"/>
+      <!-- column-name commodity -->
+      <column type="gpointer"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="liststore2">
+    <columns>
+      <!-- column-name string -->
+      <column type="gchararray"/>
+      <!-- column-name save -->
+      <column type="gchararray"/>
+      <!-- column-name currency -->
+      <column type="gpointer"/>
+    </columns>
+  </object>
   <object class="GtkAdjustment" id="start_row_adj">
     <property name="upper">1000</property>
     <property name="step_increment">1</property>
@@ -515,7 +535,7 @@ Select location and file name for the Import, then click 'OK'...
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="tooltip_text" translatable="yes">Normally prices are not over written, select this to change that.</property>
+                                <property name="tooltip_text" translatable="yes">Normally prices are not over written, select this to change that. This setting is not saved.</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_overwrite_cb" swapped="no"/>
                               </object>
@@ -804,6 +824,9 @@ For example
                 <property name="y_options">GTK_FILL</property>
               </packing>
             </child>
+            <child>
+              <placeholder/>
+            </child>
           </object>
           <packing>
             <property name="expand">False</property>
@@ -812,6 +835,125 @@ For example
           </packing>
         </child>
         <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</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="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="commodity_hbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkComboBox" id="commodity_cbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="model">liststore1</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                              <attributes>
+                                <attribute name="text">0</attribute>
+                              </attributes>
+                            </child>
+                          </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="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes"><b>Commodity From</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>
+              </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="alignment5">
+                    <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="currency_hbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkComboBox" id="currency_cbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="model">liststore1</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="cellrenderertext3"/>
+                              <attributes>
+                                <attribute name="text">0</attribute>
+                              </attributes>
+                            </child>
+                          </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="label8">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes"><b>Currency To</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">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</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>
@@ -860,7 +1002,7 @@ For example
           <packing>
             <property name="expand">True</property>
             <property name="fill">True</property>
-            <property name="position">1</property>
+            <property name="position">2</property>
           </packing>
         </child>
         <child>
@@ -902,7 +1044,7 @@ For example
             <property name="expand">False</property>
             <property name="fill">False</property>
             <property name="padding">5</property>
-            <property name="position">2</property>
+            <property name="position">3</property>
           </packing>
         </child>
         <child>
@@ -930,7 +1072,7 @@ For example
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
-            <property name="position">3</property>
+            <property name="position">4</property>
           </packing>
         </child>
       </object>
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 5437237..e7bb72a 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -125,7 +125,6 @@ void GncPriceImport::file_format(GncImpFileFormat format)
         auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
         fwtok->columns (m_settings.m_column_widths);
     }
-
 }
 
 GncImpFileFormat GncPriceImport::file_format()
@@ -140,6 +139,50 @@ void GncPriceImport::over_write (bool over)
 
 bool GncPriceImport::over_write () { return m_over_write; }
 
+/** Sets a from commodity. This is the commodity all import data relates to.
+ *  When a from commodity is set, there can't be any from columns selected
+ *  in the import data.
+ * @param from_commodity Pointer to a commodity or NULL.
+ */
+void GncPriceImport::from_commodity (gnc_commodity* from_commodity)
+{
+    m_settings.m_from_commodity = from_commodity;
+
+    if (m_settings.m_from_commodity)
+    {
+        auto col_type = std::find (m_settings.m_column_types_price.begin(),
+                m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY);
+
+        if (col_type != m_settings.m_column_types_price.end())
+            set_column_type_price (col_type -m_settings.m_column_types_price.begin(),
+                            GncPricePropType::NONE);
+    }
+}
+
+gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; }
+
+/** Sets a to currency. This is the to currency all import data relates to.
+ *  When a to currency is set, there can't be any to currency columns selected
+ *  in the import data.
+ * @param to_currency Pointer to a commodity or NULL.
+ */
+void GncPriceImport::to_currency (gnc_commodity* to_currency)
+{
+    m_settings.m_to_currency = to_currency;
+
+    if (m_settings.m_to_currency)
+    {
+        auto col_type = std::find (m_settings.m_column_types_price.begin(),
+                m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY);
+
+        if (col_type != m_settings.m_column_types_price.end())
+            set_column_type_price (col_type -m_settings.m_column_types_price.begin(),
+                            GncPricePropType::NONE);
+    }
+}
+
+gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; }
+
 void GncPriceImport::reset_formatted_column (std::vector<GncPricePropType>& col_types)
 {
     for (auto col_type: col_types)
@@ -241,6 +284,8 @@ void GncPriceImport::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 */
+    from_commodity (m_settings.m_from_commodity);
+    to_currency (m_settings.m_to_currency);
     m_settings = settings;
     encoding (m_settings.m_encoding);
 
@@ -402,14 +447,19 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
 
     /* Verify a Currency to column is selected.
      */
-    if (!check_for_column_type(GncPricePropType::CURRENCY_TO))
-        error_msg.add_error( _("Please select a Currency to column."));
+    if (!check_for_column_type(GncPricePropType::TO_CURRENCY))
+    {
+        if (!m_settings.m_to_currency)
+            error_msg.add_error( _("Please select a Currency to column or set a Currency in the Currency To field."));
+    }
 
-    /* Verify at least one from column (symbol_from or currency_from) column is selected.
+    /* Verify a Commodity from column is selected.
      */
-    if (!check_for_column_type(GncPricePropType::SYMBOL_FROM) &&
-        !check_for_column_type(GncPricePropType::CURRENCY_FROM))
-        error_msg.add_error( _("Please select a symbol or currency from column."));
+    if (!check_for_column_type(GncPricePropType::FROM_COMMODITY))
+    {
+        if (!m_settings.m_from_commodity)
+            error_msg.add_error( _("Please select a Commodity from column or set a Commodity in the Commodity From field."));
+    }
 }
 
 
@@ -497,6 +547,40 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
 
     error_message.clear();
 
+    // Add a CURRENCY_TO property with the default currency to if no currency to column was set by the user
+    auto line_to_currency = price_props->get_to_currency();
+    if (!line_to_currency)
+    {
+        if (m_settings.m_to_currency)
+            price_props->set_to_currency(m_settings.m_to_currency);
+        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!
+            error_message = _("No Currency to column selected and no default Currency 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);
+        }
+    }
+
+    // Add a COMMODITY_FROM property with the default commodity from if no commodity from column was set by the user
+    auto line_from_commodity = price_props->get_from_commodity();
+    if (!line_from_commodity)
+    {
+        if (m_settings.m_from_commodity)
+            price_props->set_from_commodity(m_settings.m_from_commodity);
+        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!
+            error_message = _("No Commodity from column selected and no default Commodity 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 price properties into a price. */
     try
     {
@@ -507,11 +591,12 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
 
         /* If all went well, add this price to the list. */
         auto price_created = price_props->create_price (book, pdb, m_over_write);
-//FIXME Need to look at this
-        if (price_created)
+        if (price_created == ADDED)
             m_prices_added++;
-        else
+        else if (price_created == DUPLICATED)
             m_prices_duplicated++;
+        else if (price_created == REPLACED)
+            m_prices_replaced++;
     }
     catch (const std::invalid_argument& e)
     {
@@ -537,6 +622,7 @@ void GncPriceImport::create_prices ()
 
     m_prices_added = 0;
     m_prices_duplicated = 0;
+    m_prices_replaced = 0;
 
     /* Iterate over all parsed lines */
     for (auto parsed_lines_it = m_parsed_lines.begin();
@@ -550,8 +636,8 @@ void GncPriceImport::create_prices ()
         /* Should not throw anymore, otherwise verify needs revision */
         create_price (parsed_lines_it);
     }
-    PINFO("Number of lines is %d, added is %d, duplicates is %d",
-         (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated);
+    PINFO("Number of lines is %d, added %d, duplicated %d, replaced %d",
+         (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated, m_prices_replaced);
 }
 
 bool
@@ -611,6 +697,14 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type,
 
     m_settings.m_column_types_price.at (position) = type;
 
+    // If the user has set a Commodity from column, we can't have a commodity from default set
+    if (type == GncPricePropType::FROM_COMMODITY)
+        from_commodity (nullptr);
+
+    // If the user has set a Currency to column, we can't have a currency to default set
+    if (type == GncPricePropType::TO_CURRENCY)
+        to_currency (nullptr);
+
     /* Update the preparsed data */
     for (auto parsed_lines_it = m_parsed_lines.begin();
             parsed_lines_it != m_parsed_lines.end();
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 60cdc27..809464e 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -32,7 +32,7 @@
 
 extern "C" {
 #include "config.h"
-
+#include "gnc-commodity.h"
 }
 
 #include <vector>
@@ -85,6 +85,12 @@ public:
     void over_write (bool over);
     bool over_write ();
 
+    void from_commodity (gnc_commodity *from_commodity);
+    gnc_commodity *from_commodity ();
+
+    void to_currency (gnc_commodity *to_currency);
+    gnc_commodity *to_currency ();
+
     void currency_format (int currency_format);
     int currency_format ();
 
@@ -131,6 +137,7 @@ public:
                                                      price properties. */
     int  m_prices_added;
     int  m_prices_duplicated;
+    int  m_prices_replaced;
 
 private:
     /** A helper function used by create_prices. It will attempt
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index a39f178..e6f983e 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -32,8 +32,6 @@ extern "C" {
 
 #include "engine-helpers.h"
 #include "gnc-ui-util.h"
-#include "gnc-pricedb.h"
-
 }
 
 #include <string>
@@ -48,9 +46,8 @@ std::map<GncPricePropType, const char*> gnc_price_col_type_strs = {
         { GncPricePropType::NONE, N_("None") },
         { GncPricePropType::DATE, N_("Date") },
         { GncPricePropType::AMOUNT, N_("Amount") },
-        { GncPricePropType::CURRENCY_FROM, N_("Currency From") },
-        { GncPricePropType::CURRENCY_TO, N_("Currency To") },
-        { GncPricePropType::SYMBOL_FROM, N_("Symbol From") },
+        { GncPricePropType::FROM_COMMODITY, N_("Commodity From") },
+        { GncPricePropType::TO_CURRENCY, N_("Currency To") },
 };
 
 /* Regular expressions used to parse dates per date format */
@@ -243,6 +240,7 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
         return comm;
 }
 
+//FIXME can we change above to do below
 gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency)
 {
     if (sym_str.empty())
@@ -285,8 +283,8 @@ gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_c
         throw std::invalid_argument (_("Value can't be parsed into a valid commodity."));
     else
     {
-        if (gnc_commodity_is_currency (retval) != is_currency)
-            throw std::invalid_argument (_("Value parsed into an invalid commodity for column type."));
+        if ((is_currency == true) && (gnc_commodity_is_currency (retval) != true))
+                throw std::invalid_argument (_("Value parsed into an invalid currency for currency column type."));
         else
             return retval;
     }
@@ -312,25 +310,18 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
                 m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails
                 break;
 
-            case GncPricePropType::CURRENCY_FROM:
-                m_currency_from = boost::none;
-                comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
+            case GncPricePropType::FROM_COMMODITY:
+                m_from_commodity = boost::none;
+                comm = parse_commodity_price_sym (value, false); // Throws if parsing fails
                 if (comm)
-                    m_currency_from = comm;
+                    m_from_commodity = comm;
                 break;
 
-            case GncPricePropType::CURRENCY_TO:
-                m_currency_to = boost::none;
+            case GncPricePropType::TO_CURRENCY:
+                m_to_currency = boost::none;
                 comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
                 if (comm)
-                    m_currency_to = comm;
-                break;
-
-            case GncPricePropType::SYMBOL_FROM:
-                m_symbol_from = boost::none;
-                comm = parse_commodity_price_sym (value, false); // Throws if parsing fails
-                if (comm)
-                    m_symbol_from = comm;
+                    m_to_currency = comm;
                 break;
 
             default:
@@ -378,15 +369,15 @@ std::string GncImportPrice::verify_essentials (void)
         return _("No date column.");
     else if (m_amount == boost::none)
         return _("No amount column.");
-    else if (m_currency_to == boost::none)
+    else if (m_to_currency == boost::none)
         return _("No Currency to column.");
-    else if ((m_symbol_from == boost::none) && (m_currency_from == boost::none))
-        return _("No from column.");
+    else if (m_from_commodity == boost::none)
+        return _("No Commodity from column.");
     else
         return std::string();
 }
 
-bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
+Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
 {
     /* Gently refuse to create the price if the basics are not set correctly
      * This should have been tested before calling this function though!
@@ -395,47 +386,40 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
     if (!check.empty())
     {
         PWARN ("Refusing to create price because essentials not set properly: %s", check.c_str());
-        return false;
+        return FAILED;
     }
 
     Timespec date;
     timespecFromTime64 (&date, *m_date);
     date.tv_nsec = 0;
 
-#ifdef skip
-//FIXME Numeric needs changing, copied from old version...
     bool rev = false;
-    gnc_commodity *comm_from = nullptr;
+    auto amount = *m_amount;
 
-    if (m_currency_from != boost::none) // Currency Import
+    GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date);
+
+    if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import
     {
         // Check for currency in reverse direction.
-        GNCPrice *rev_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, *m_currency_from, date);
-        if (rev_price != nullptr)
-            rev = true;
-        gnc_price_unref (rev_price);
+        if (old_price != nullptr)
+        {
+            // Check for price in reverse direction.
+            if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity))
+                rev = true;
+
+            DEBUG("Commodity from is a Currency");
+        }
 
         // Check for price less than 1, reverse if so.
-        if (gnc_numeric_compare (*m_amount, gnc_numeric_create (1, 1)) != 1)
+        if (*m_amount < GncNumeric(1,1))
             rev = true;
 
-        comm_from = *m_currency_from;
-        DEBUG("Commodity from is a Currency");
     }
-    else
-        comm_from = *m_symbol_from;
-
     DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date),
-          rev, gnc_commodity_get_fullname (comm_from), gnc_commodity_get_fullname (*m_currency_to),
-          gnc_num_dbg_to_string (*m_amount)           );
-
-    GNCPrice *old_price = nullptr;
+        rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency),
+        amount.to_string().c_str());
 
-    // Should the commodities be reversed
-    if (rev)
-        old_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, comm_from, date);
-    else
-        old_price = gnc_pricedb_lookup_day (pdb, comm_from, *m_currency_to, date);
+    Result ret_val = ADDED;
 
     // Should old price be over writen
     if ((old_price != nullptr) && (over == true))
@@ -444,31 +428,29 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
         gnc_pricedb_remove_price (pdb, old_price);
         gnc_price_unref (old_price);
         old_price = nullptr;
+        ret_val = REPLACED;
     }
-#endif
-    bool ret_val = true;
-#ifdef skip
+
     // Create the new price
     if (old_price == nullptr)
     {
         DEBUG("Create");
         GNCPrice *price = gnc_price_create (book);
         gnc_price_begin_edit (price);
-
         if (rev)
         {
-            gnc_price_set_commodity (price, *m_currency_to);
-            gnc_price_set_currency (price, comm_from);
-            *m_amount = gnc_numeric_convert (gnc_numeric_invert (*m_amount),
-                                          CURRENCY_DENOM, GNC_HOW_RND_ROUND_HALF_UP);
-            gnc_price_set_value (price, *m_amount);
+            amount = amount.inv(); //invert the amount
+            gnc_price_set_commodity (price, *m_to_currency);
+            gnc_price_set_currency (price, *m_from_commodity);
         }
         else
         {
-            gnc_price_set_commodity (price, comm_from);
-            gnc_price_set_currency (price, *m_currency_to);
-            gnc_price_set_value (price, *m_amount);
+            gnc_price_set_commodity (price, *m_from_commodity);
+            gnc_price_set_currency (price, *m_to_currency);
         }
+        auto amount_conv = amount.convert<RoundType::half_up>(CURRENCY_DENOM);
+        gnc_price_set_value (price, static_cast<gnc_numeric>(amount_conv));
+
         gnc_price_set_time (price, date);
         gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE);
 //FIXME Not sure which one        gnc_price_set_source (price, PRICE_SOURCE_FQ);
@@ -479,15 +461,15 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
 
         gnc_price_unref (price);
 
-         if (perr == false)
+        if (perr == false)
             throw std::invalid_argument (_("Failed to create price from selected columns."));
 //FIXME Not sure about this, should this be a PWARN
     }
     else
-
-#endif
-        ret_val = false;
-
+    {
+        gnc_price_unref (old_price);
+        ret_val = DUPLICATED;
+    }
     return ret_val;
 }
 
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 57083e9..a8550ea 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -49,12 +49,13 @@ enum class GncPricePropType {
     NONE,
     DATE,
     AMOUNT,
-    CURRENCY_FROM,
-    CURRENCY_TO,
-    SYMBOL_FROM,
-    PRICE_PROPS = SYMBOL_FROM
+    FROM_COMMODITY,
+    TO_CURRENCY,
+    PRICE_PROPS = TO_CURRENCY
 };
 
+enum Result { FAILED, ADDED, DUPLICATED, REPLACED };
+
 /** Maps all column types to a string representation.
  *  The actual definition is in gnc-price-props.cpp.
  *  Attention: that definition should be adjusted for any
@@ -91,7 +92,14 @@ public:
     void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
     void reset (GncPricePropType prop_type);
     std::string verify_essentials (void);
-    bool create_price (QofBook* book, GNCPriceDB *pdb, bool over);
+    Result create_price (QofBook* book, GNCPriceDB *pdb, bool over);
+
+    gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; }
+    void set_from_commodity (gnc_commodity* comm) { if (comm) m_from_commodity = comm; else m_from_commodity = boost::none; }
+
+    gnc_commodity* get_to_currency () { if (m_to_currency) return *m_to_currency; else return nullptr; }
+    void set_to_currency (gnc_commodity* curr) { if (curr) m_to_currency = curr; else m_to_currency = boost::none; }
+
     std::string errors();
 
 private:
@@ -99,9 +107,8 @@ private:
     int m_currency_format;
     boost::optional<time64> m_date;
     boost::optional<GncNumeric> m_amount;
-    boost::optional<gnc_commodity*> m_currency_from;
-    boost::optional<gnc_commodity*> m_currency_to;
-    boost::optional<gnc_commodity*> m_symbol_from;
+    boost::optional<gnc_commodity*> m_from_commodity;
+    boost::optional<gnc_commodity*> m_to_currency;
     bool created = false;
 
     std::map<GncPricePropType, std::string> m_errors;

commit 3279973329885cc63a6c7b8fb1060aacd4f2cfec
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:04:49 2017 +0000

    Some text changes

diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 09deb93..5437237 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -400,7 +400,7 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
     if (!check_for_column_type(GncPricePropType::AMOUNT))
         error_msg.add_error( _("Please select an amount column."));
 
-    /* Verify an Currency to column is selected.
+    /* Verify a Currency to column is selected.
      */
     if (!check_for_column_type(GncPricePropType::CURRENCY_TO))
         error_msg.add_error( _("Please select a Currency to column."));
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 0898215..60cdc27 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -68,11 +68,10 @@ struct ErrorListPrice;
  * - 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
+ * - parse the file into lines, which in turn are split up in to 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 */
+ * - last step is convert the mapped columns into a list of prices to add */
 class GncPriceImport
 {
 public:

commit f2c78102e09c55ed1bb3243c35c87bf3293e3808
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:03:07 2017 +0000

    Remove not required account update

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index a6ba349..6c4279a 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -87,7 +87,6 @@ public:
     void preview_over_write (bool over);
     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 ();
@@ -306,11 +305,6 @@ void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImp
     info->preview_update_file_format();
 }
 
-void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info)
-{
-    info->preview_update_account();
-}
-
 void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
                               CsvImpPriceAssist* info)
 {

commit 3cfa9d05bad517db632679ffa6127dd34066d58e
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:01:16 2017 +0000

    Add CSV Price importer assistant files
    
    These file are largely based on the csv transaction importer.
    They are just the start for subsequent changes.

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index 0bc7d29..44a14ca 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -10,6 +10,7 @@ SET(csv_import_remote_SOURCES
 SET(csv_import_SOURCES
   gncmod-csv-import.c
   assistant-csv-account-import.c
+  assistant-csv-price-import.cpp
   assistant-csv-trans-import.cpp
   gnc-plugin-csv-import.c
   csv-account-import.c
@@ -37,6 +38,7 @@ SET(csv_import_remote_HEADERS
 
 SET(csv_import_noinst_HEADERS
   assistant-csv-account-import.h
+  assistant-csv-price-import.h
   assistant-csv-trans-import.h
   gnc-plugin-csv-import.h
   csv-account-import.h
@@ -88,7 +90,7 @@ INSTALL(TARGETS gncmod-csv-import
 # No headers to install
 
 SET(csv_import_GLADE assistant-csv-account-import.glade
-      assistant-csv-trans-import.glade)
+      assistant-csv-price-import.glade assistant-csv-trans-import.glade)
 
 INSTALL(FILES ${csv_import_GLADE} DESTINATION ${CMAKE_INSTALL_DATADIR}/gnucash/gtkbuilder)
 
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index 4083963..4b473de 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -5,6 +5,7 @@ pkglib_LTLIBRARIES=libgncmod-csv-import.la
 libgncmod_csv_import_la_SOURCES = \
   gncmod-csv-import.c \
   assistant-csv-account-import.c \
+  assistant-csv-price-import.cpp \
   assistant-csv-trans-import.cpp \
   gnc-plugin-csv-import.c \
   csv-account-import.c \
@@ -22,6 +23,7 @@ libgncmod_csv_import_la_SOURCES = \
 
 noinst_HEADERS = \
   assistant-csv-account-import.h \
+  assistant-csv-price-import.h \
   assistant-csv-trans-import.h \
   gnc-plugin-csv-import.h \
   csv-account-import.h \
@@ -79,6 +81,7 @@ ui_DATA = \
 gtkbuilderdir = ${GNC_GTKBUILDER_DIR}
 gtkbuilder_DATA = \
 	assistant-csv-account-import.glade \
+	assistant-csv-price-import.glade \
 	assistant-csv-trans-import.glade
 
 EXTRA_DIST = $(ui_DATA) $(gtkbuilder_DATA) CMakeLists.txt
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
new file mode 100644
index 0000000..a6ba349
--- /dev/null
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -0,0 +1,1621 @@
+/*******************************************************************\
+ * assistant-csv-price-import.c -- An assistant for importing       *
+ *                                     Prices from a file.          *
+ *                                                                  *
+ * Copyright (C) 2017 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-price-import.cpp
+    @brief CSV Import Assistant
+    @author Copyright (c) 2016 Geert Janssens
+    @author Copyright (c) 2017 Robert Fewell
+*/
+
+#include <guid.hpp>
+
+extern "C"
+{
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <stdlib.h>
+
+#include "gnc-ui.h"
+#include "gnc-uri-utils.h"
+#include "gnc-ui-util.h"
+#include "dialog-utils.h"
+
+#include "gnc-component-manager.h"
+
+#include "gnc-state.h"
+
+#include "assistant-csv-price-import.h"
+
+#include "gnc-csv-gnumeric-popup.h"
+#include "go-charmap-sel.h"
+}
+
+#include "gnc-csv-trans-settings.hpp"
+#include "gnc-price-import.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_PRICE_CM_CLASS "assistant-csv-price-import"
+
+/* This static indicates the debugging module that this .o belongs to.  */
+static QofLogModule log_module = GNC_MOD_ASSISTANT;
+
+class  CsvImpPriceAssist
+{
+public:
+    CsvImpPriceAssist ();
+
+    void assist_prepare_cb (GtkWidget *page);
+    void assist_file_page_prepare ();
+    void assist_preview_page_prepare ();
+    void assist_confirm_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_over_write (bool over);
+    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 (GtkComboBox* cbox);
+    void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
+
+    void preview_populate_settings_combo();
+    void preview_handle_save_del_sensitivity (GtkComboBox* combo);
+    void preview_split_column (int col, int offset);
+    void preview_refresh_table ();
+    void preview_refresh ();
+    void preview_validate_settings ();
+
+    friend gboolean
+    fixed_context_menu_handler_price (GnumericPopupMenuElement const *element,
+            gpointer userdata);
+private:
+    /* helper functions to manage the context menu for fixed with columns */
+    uint32_t get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx);
+    void fixed_context_menu (GdkEventButton *event, int col, int dx);
+    /* 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, uint32_t colnum);
+    /* helper function to set rendering parameters for preview data columns */
+    void preview_style_column (uint32_t col_num, GtkTreeModel* model);
+
+    GtkAssistant    *csv_imp_asst;
+
+    GtkWidget       *file_page;                     /**< Assistant file page widget */
+    GtkWidget       *file_chooser;                  /**< The widget for the file chooser */
+    std::string      m_file_name;                   /**< The import file name */
+
+    GtkWidget       *preview_page;                  /**< Assistant preview page widget */
+    GtkComboBox     *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 */
+    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       *over_write_cbutton;            /**< The widget for Price Over Write */
+    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 */
+    GtkComboBoxText *currency_format_combo;         /**< The Combo Text widget for selecting the currency format */
+    GtkTreeView     *treeview;                      /**< The treeview containing the data */
+    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 the user has clicked */
+    int              fixed_context_offset;          /**< The offset (in characters) in the column
+                                                       * the user has clicked */
+
+    GtkWidget       *confirm_page;                  /**< Assistant confirm page widget */
+
+    GtkWidget       *summary_page;                  /**< Assistant summary page widget */
+    GtkWidget       *summary_label;                 /**< The summary text */
+
+    std::unique_ptr<GncPriceImport> price_imp;      /**< The actual data we are previewing */
+};
+
+
+/*******************************************************
+ * Assistant call back functions
+ *******************************************************/
+
+extern "C"
+{
+void csv_price_imp_assist_prepare_cb (GtkAssistant  *assistant, GtkWidget *page, CsvImpPriceAssist* info);
+void csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info);
+void csv_price_imp_assist_cancel_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info);
+void csv_price_imp_assist_close_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info);
+void csv_price_imp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info);
+void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info);
+void csv_price_imp_preview_del_settings_cb (GtkWidget *button, CsvImpPriceAssist *info);
+void csv_price_imp_preview_save_settings_cb (GtkWidget *button, CsvImpPriceAssist *info);
+void csv_price_imp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpPriceAssist *info);
+void csv_price_imp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text,
+        gint new_text_length, gint *position, CsvImpPriceAssist *info);
+void csv_price_imp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpPriceAssist *info);
+void csv_price_imp_preview_srow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info);
+void csv_price_imp_preview_erow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info);
+void csv_price_imp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info);
+void csv_price_imp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info);
+void csv_price_imp_preview_overwrite_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info);
+void csv_price_imp_preview_sep_button_cb (GtkWidget* widget, CsvImpPriceAssist* info);
+void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpPriceAssist* info);
+void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info);
+void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
+                              CsvImpPriceAssist* info);
+}
+
+void
+csv_price_imp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
+        CsvImpPriceAssist* info)
+{
+    info->assist_prepare_cb(page);
+}
+
+void
+csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info)
+{
+    gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info);
+    delete info;
+}
+
+void
+csv_price_imp_assist_cancel_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
+{
+    info->assist_finish (true);
+    gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info);
+}
+
+void
+csv_price_imp_assist_close_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
+{
+    gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info);
+}
+
+void
+csv_price_imp_assist_finish_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
+{
+    info->assist_finish (false);
+}
+
+void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info)
+{
+    info->file_confirm_cb();
+}
+
+void csv_price_imp_preview_del_settings_cb (GtkWidget *button, CsvImpPriceAssist *info)
+{
+    info->preview_settings_delete();
+}
+
+void csv_price_imp_preview_save_settings_cb (GtkWidget *button, CsvImpPriceAssist *info)
+{
+    info->preview_settings_save();
+}
+
+void csv_price_imp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpPriceAssist *info)
+{
+    info->preview_settings_load();
+}
+
+void
+csv_price_imp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text,
+        gint new_text_length, gint *position, CsvImpPriceAssist *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_price_imp_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_price_imp_preview_settings_text_inserted_cb, info);
+
+    g_signal_stop_emission_by_name (entry, "insert_text");
+}
+
+void
+csv_price_imp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpPriceAssist *info)
+{
+    info->preview_settings_name(entry);
+}
+
+void csv_price_imp_preview_srow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_price_imp_preview_erow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_price_imp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_price_imp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_price_imp_preview_overwrite_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info)
+{
+    info->preview_over_write (gtk_toggle_button_get_active (checkbox));
+}
+
+void csv_price_imp_preview_sep_button_cb (GtkWidget* widget, CsvImpPriceAssist* info)
+{
+    info->preview_update_separators(widget);
+}
+
+void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpPriceAssist* info)
+{
+    info->preview_update_file_format();
+}
+
+void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info)
+{
+    info->preview_update_account();
+}
+
+void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
+                              CsvImpPriceAssist* info)
+{
+    info->preview_update_encoding(encoding);
+}
+
+static void csv_price_imp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImpPriceAssist* info)
+{
+    info->preview_update_date_format();
+}
+
+static void csv_price_imp_preview_currency_fmt_sel_cb (GtkComboBox* format_selector, CsvImpPriceAssist* info)
+{
+    info->preview_update_currency_format();
+}
+
+void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info)
+{
+    info->preview_update_col_type (cbox);
+}
+
+gboolean
+csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton* event,
+                                        CsvImpPriceAssist* info)
+{
+    info->preview_update_fw_columns(treeview, event);
+    return false;
+}
+
+
+/*******************************************************
+ * Assistant Constructor
+ *******************************************************/
+CsvImpPriceAssist::CsvImpPriceAssist ()
+{
+    auto builder = gtk_builder_new();
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "start_row_adj");
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "end_row_adj");
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "CSV Price Assistant");
+    csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Price 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, "confirm_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_price_imp_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_price_imp_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_price_imp_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_price_imp_preview_settings_text_changed_cb), this);
+        g_signal_connect (G_OBJECT(emb_entry), "insert-text",
+                         G_CALLBACK(csv_price_imp_preview_settings_text_inserted_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"));
+        over_write_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "over_write_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. */
+        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"));
+
+        /* 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_price_imp_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_price_imp_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_price_imp_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"));
+        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. */
+        encoding_selected_called = false;
+    }
+
+    /* Confirm Page */
+    confirm_page = GTK_WIDGET(gtk_builder_get_object (builder, "confirm_page"));
+
+    /* 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));
+}
+
+/**************************************************
+ * Code related to the file chooser page
+ **************************************************/
+
+/* csv_price_imp_file_confirm_cb
+ *
+ * call back for ok button in file chooser widget
+ */
+void
+CsvImpPriceAssist::file_confirm_cb ()
+{
+    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);
+
+    m_file_name = file_name;
+    gnc_set_default_directory (GNC_PREFS_GROUP, starting_dir);
+
+    DEBUG("file_name selected is %s", m_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. */
+    price_imp = std::unique_ptr<GncPriceImport>(new GncPriceImport);
+    /* Assume data is CSV. User can later override to Fixed Width if needed */
+    try
+    {
+        price_imp->file_format (GncImpFileFormat::CSV);
+        price_imp->load_file (m_file_name);
+        price_imp->tokenize (true);
+    }
+    catch (std::ifstream::failure& e)
+    {
+        /* File loading failed ... */
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what());
+        return;
+    }
+    catch (std::range_error &e)
+    {
+        /* Parsing failed ... */
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what());
+        return;
+    }
+    /* Get settings store and populate */
+    preview_populate_settings_combo();
+    gtk_combo_box_set_active (settings_combo, 0);
+
+    auto num = gtk_assistant_get_current_page (csv_imp_asst);
+    gtk_assistant_set_current_page (csv_imp_asst, num + 1);
+}
+
+
+/**************************************************
+ * Code related to the preview page
+ **************************************************/
+
+/* Set the available presets in the settings combo box
+ */
+void CsvImpPriceAssist::preview_populate_settings_combo()
+{
+    // Clear the list store
+    auto model = gtk_combo_box_get_model (settings_combo);
+    gtk_list_store_clear (GTK_LIST_STORE(model));
+
+    // Append the default entry
+//FIXME get_trans_presets ????
+    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->m_name.c_str(), -1);
+    }
+}
+
+/* Enable or disable the save and delete settings buttons
+ * depending on what is selected and entered as settings name
+ */
+void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
+{
+    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 and save button */
+    if (gtk_combo_box_get_active_iter (combo, &iter))
+    {
+        CsvTransSettings *preset;
+        GtkTreeModel *model = gtk_combo_box_get_model (combo);
+        gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+//FIXME
+        if (preset && !trans_preset_is_reserved_name (preset->m_name))
+        {
+            /* Current preset is not read_only, so buttons can be enabled */
+            can_delete = true;
+            can_save = true;
+        }
+    }
+//FIXME
+    else if (entry_text && (strlen (entry_text) > 0) &&
+            !trans_preset_is_reserved_name (std::string(entry_text)))
+        can_save = true;
+
+    gtk_widget_set_sensitive (save_button, can_save);
+    gtk_widget_set_sensitive (del_button, can_delete);
+}
+
+void
+CsvImpPriceAssist::preview_settings_name (GtkEntry* entry)
+{
+    auto text = gtk_entry_get_text (entry);
+    if (text)
+        price_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
+CsvImpPriceAssist::preview_settings_load ()
+{
+    // Get the Active Selection
+    GtkTreeIter iter;
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
+        return;
+
+    CsvTransSettings *preset = nullptr;
+    auto model = gtk_combo_box_get_model (settings_combo);
+    gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+
+    if (!preset)
+        return;
+
+    price_imp->settings (*preset);
+    if (preset->m_load_error)
+        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."));
+
+    preview_refresh ();
+    preview_handle_save_del_sensitivity (settings_combo);
+}
+
+/* Callback to delete a settings entry
+ */
+void
+CsvImpPriceAssist::preview_settings_delete ()
+{
+    // Get the Active Selection
+    GtkTreeIter iter;
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
+        return;
+
+    CsvTransSettings *preset = nullptr;
+    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(csv_imp_asst),
+                                GTK_RESPONSE_CANCEL,
+                                "%s", _("Delete the Import Settings."));
+    if (response == GTK_RESPONSE_OK)
+    {
+        preset->remove();
+        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
+CsvImpPriceAssist::preview_settings_save ()
+{
+    auto title = _("Save the Import Settings.");
+    auto new_name = price_imp->settings_name();
+
+    /* Check if the entry text matches an already existing preset */
+    GtkTreeIter iter;
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
+    {
+
+        auto model = gtk_combo_box_get_model (settings_combo);
+        bool valid = gtk_tree_model_get_iter_first (model, &iter);
+        while (valid)
+        {
+            // Walk through the list, reading each row
+            CsvTransSettings *preset;
+            gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+
+            if (preset && (preset->m_name == std::string(new_name)))
+            {
+                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)
+                    return;
+
+                break;
+            }
+            valid = gtk_tree_model_iter_next (model, &iter);
+        }
+    }
+
+    /* All checks passed, let's save this preset */
+    if (!price_imp->save_settings())
+    {
+        gnc_info_dialog (GTK_WIDGET(csv_imp_asst),
+            "%s", _("The settings have been saved."));
+
+        // Update the settings store
+        preview_populate_settings_combo();
+        auto model = gtk_combo_box_get_model (settings_combo);
+
+        // Get the first entry in model
+        GtkTreeIter   iter;
+        bool valid = gtk_tree_model_get_iter_first (model, &iter);
+        while (valid)
+        {
+            // 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 (settings_combo, &iter);
+
+            g_free (name);
+
+            valid = gtk_tree_model_iter_next (model, &iter);
+        }
+    }
+    else
+        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 CsvImpPriceAssist::preview_update_skipped_rows ()
+{
+    /* Update skip rows in the parser */
+    price_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)));
+
+    /* 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, price_imp->m_parsed_lines.size()
+            - price_imp->skip_start_lines() -1);
+
+    adj = gtk_spin_button_get_adjustment (start_row_spin);
+    gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size()
+            - price_imp->skip_end_lines() - 1);
+
+    preview_refresh_table ();
+}
+
+void CsvImpPriceAssist::preview_over_write (bool over)
+{
+    price_imp->over_write (over);
+}
+
+/** 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 CsvImpPriceAssist::preview_update_separators (GtkWidget* widget)
+{
+    /* Only manipulate separator characters if the currently open file is
+     * csv separated. */
+    if (price_imp->file_format() != GncImpFileFormat::CSV)
+        return;
+
+    /* Add the corresponding characters to checked_separators for each
+     * button that is checked. */
+    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(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(custom_cbutton)))
+    {
+        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. */
+    price_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
+    {
+        price_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 (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(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;
+    }
+}
+
+/** 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 CsvImpPriceAssist::preview_update_file_format ()
+{
+    /* Set the parsing type correctly. */
+    try
+    {
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(csv_button)))
+        {
+            price_imp->file_format (GncImpFileFormat::CSV);
+            g_signal_handlers_disconnect_by_func(G_OBJECT(treeview),
+                    (gpointer)csv_price_imp_preview_treeview_clicked_cb, (gpointer)this);
+            gtk_widget_set_visible (separator_table, true);
+            gtk_widget_set_visible (fw_instructions_hbox, false);
+        }
+        else
+        {
+            price_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_price_imp_preview_treeview_clicked_cb), (gpointer)this);
+            gtk_widget_set_visible (separator_table, false);
+            gtk_widget_set_visible (fw_instructions_hbox, true);
+
+        }
+        price_imp->tokenize (false);
+        preview_refresh_table ();
+    }
+    catch (std::range_error &e)
+    {
+        /* Parsing failed ... */
+        gnc_error_dialog (nullptr, "%s", e.what());
+        return;
+    }
+    catch (...)
+    {
+        // FIXME Handle file loading errors (possibly thrown by file_format above)
+        PWARN("Got an error during file loading");
+    }
+}
+
+/** 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
+ */
+void
+CsvImpPriceAssist::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 (encoding_selected_called)
+    {
+        std::string previous_encoding = price_imp->m_tokenizer->encoding();
+        /* Try converting the new encoding and reparsing. */
+        try
+        {
+            price_imp->encoding (encoding);
+            preview_refresh_table ();
+        }
+        catch (...)
+        {
+            /* If it fails, change back to the old encoding. */
+            gnc_error_dialog (nullptr, "%s", _("Invalid encoding selected"));
+            go_charmap_sel_set_encoding (encselector, previous_encoding.c_str());
+        }
+    }
+    encoding_selected_called = !encoding_selected_called;
+}
+
+void
+CsvImpPriceAssist::preview_update_date_format ()
+{
+    price_imp->date_format (gtk_combo_box_get_active (GTK_COMBO_BOX(date_format_combo)));
+    preview_refresh_table ();
+}
+
+void
+CsvImpPriceAssist::preview_update_currency_format ()
+{
+    price_imp->currency_format (gtk_combo_box_get_active (GTK_COMBO_BOX(currency_format_combo)));
+    preview_refresh_table ();
+}
+
+gboolean
+csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist)
+{
+    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_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
+ * 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.
+ * @param new_text The text the user selected
+ * @param info The display of the data being imported
+ */
+void CsvImpPriceAssist::preview_update_col_type (GtkComboBox* cbox)
+{
+    /* Get the new text */
+    GtkTreeIter iter;
+    auto model = gtk_combo_box_get_model (cbox);
+    gtk_combo_box_get_active_iter (cbox, &iter);
+    auto new_col_type = GncPricePropType::NONE;
+    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"));
+    price_imp->set_column_type_price (col_num, new_col_type);
+
+    /* 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);
+}
+
+/*======================================================================*/
+/*================== Beginning of Gnumeric Code ========================*/
+
+/* The following is code copied from Gnumeric 1.7.8 licensed under the
+ * GNU General Public License version 2 and/or version 3. It is from the file
+ * gnumeric/gnucash/dialogs/dialog-stf-fixed-page.c, and it has been
+ * modified slightly to work within GnuCash. */
+
+/*
+ * Copyright 2001 Almer S. Tigelaar <almer at gnome.org>
+ * Copyright 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.
+ */
+
+enum
+{
+    CONTEXT_STF_IMPORT_MERGE_LEFT = 1,
+    CONTEXT_STF_IMPORT_MERGE_RIGHT = 2,
+    CONTEXT_STF_IMPORT_SPLIT = 3,
+    CONTEXT_STF_IMPORT_WIDEN = 4,
+    CONTEXT_STF_IMPORT_NARROW = 5
+};
+
+static GnumericPopupMenuElement const popup_elements[] =
+{
+    {
+        N_("Merge with column on _left"), GTK_STOCK_REMOVE,
+        0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT
+    },
+    {
+        N_("Merge with column on _right"), GTK_STOCK_REMOVE,
+        0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT
+    },
+    { "", nullptr, 0, 0, 0 },
+    {
+        N_("_Split this column"), nullptr,
+        0, 1 << CONTEXT_STF_IMPORT_SPLIT, CONTEXT_STF_IMPORT_SPLIT
+    },
+    { "", nullptr, 0, 0, 0 },
+    {
+        N_("_Widen this column"), GTK_STOCK_GO_FORWARD,
+        0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN
+    },
+    {
+        N_("_Narrow this column"), GTK_STOCK_GO_BACK,
+        0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW
+    },
+    { nullptr, nullptr, 0, 0, 0 },
+};
+
+uint32_t CsvImpPriceAssist::get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx)
+{
+    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;
+    g_object_get (G_OBJECT(cell), "font_desc", &font_desc, nullptr);
+
+    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);
+    if (width < 1) width = 1;
+    uint32_t charindex = (dx + width / 2) / width;
+    g_object_unref (layout);
+    pango_font_description_free (font_desc);
+
+    return charindex;
+}
+
+gboolean
+fixed_context_menu_handler_price (GnumericPopupMenuElement const *element,
+        gpointer userdata)
+{
+    auto info = (CsvImpPriceAssist*)userdata;
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(info->price_imp->m_tokenizer.get());
+
+    switch (element->index)
+    {
+    case CONTEXT_STF_IMPORT_MERGE_LEFT:
+        fwtok->col_delete (info->fixed_context_col - 1);
+        break;
+    case CONTEXT_STF_IMPORT_MERGE_RIGHT:
+        fwtok->col_delete (info->fixed_context_col);
+        break;
+    case CONTEXT_STF_IMPORT_SPLIT:
+        fwtok->col_split (info->fixed_context_col, info->fixed_context_offset);
+        break;
+    case CONTEXT_STF_IMPORT_WIDEN:
+        fwtok->col_widen (info->fixed_context_col);
+        break;
+    case CONTEXT_STF_IMPORT_NARROW:
+        fwtok->col_narrow (info->fixed_context_col);
+        break;
+    default:
+        ; /* Nothing */
+    }
+
+    try
+    {
+        info->price_imp->tokenize (false);
+    }
+    catch(std::range_error& e)
+    {
+        gnc_error_dialog (nullptr, "%s", e.what());
+        return false;
+    }
+    info->preview_refresh_table ();
+    return true;
+}
+
+void
+CsvImpPriceAssist::fixed_context_menu (GdkEventButton *event,
+                    int col, int offset)
+{
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(price_imp->m_tokenizer.get());
+    fixed_context_col = col;
+    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, 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);
+
+    gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler_price,
+                                this, 0,
+                                sensitivity_filter, event);
+}
+
+/*===================== End of Gnumeric Code ===========================*/
+/*======================================================================*/
+void
+CsvImpPriceAssist::preview_split_column (int col, int offset)
+{
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(price_imp->m_tokenizer.get());
+    fwtok->col_split (col, offset);
+    try
+    {
+        price_imp->tokenize (false);
+    }
+    catch (std::range_error& e)
+    {
+        gnc_error_dialog (nullptr, "%s", e.what());
+        return;
+    }
+    preview_refresh_table();
+}
+
+/** Event handler for clicking on column headers. This function is
+ * called whenever the user clicks on column headers in
+ * preview->treeview to modify columns when in fixed-width mode.
+ * @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
+CsvImpPriceAssist::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;
+
+    /* Find the column that was clicked. */
+    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;
+
+    /* 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;
+
+    /* 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 (dcol, offset);
+    else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
+        /* Right clicking brings up a context menu. */
+        fixed_context_menu (event, dcol, offset);
+}
+
+/* Convert state info (errors/skipped) in visual feedback to decorate the preview table */
+void
+CsvImpPriceAssist::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())
+    {
+        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
+ * with valid column types and selects the given column type
+ */
+GtkWidget*
+CsvImpPriceAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum)
+{
+    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>( price_imp->column_types_price()[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_price_imp_preview_col_type_changed_cb), (gpointer)this);
+
+    gtk_widget_show (cbox);
+    return cbox;
+}
+
+void
+CsvImpPriceAssist::preview_style_column (uint32_t 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_price (void)
+{
+    auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+    for (auto col_type : gnc_price_col_type_strs)
+    {
+        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 CsvImpPriceAssist::preview_refresh_table ()
+{
+    preview_validate_settings ();
+
+    /* 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 + price_imp->column_types_price().size();
+    auto model_col_types = g_new (GType, ncols);
+    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 importer object. */
+    for (auto parse_line : price_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<3>(parse_line));
+
+        /* 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++)
+        {
+            uint32_t 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);
+
+    /* 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.
+     */
+
+    /* 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)
+    {
+        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 if the model has more data columns than the treeview. */
+    while (ntcols < ncols - PREV_N_FIXED_COLS + 1)
+    {
+        /* Default cell renderer is text, except for the first (error) column */
+        auto renderer = gtk_cell_renderer_text_new();
+        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);
+    }
+
+    /* Reset column attributes as they are undefined after recreating the model */
+    auto combostore = make_column_header_model_price ();
+    for (uint32_t 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);
+    g_object_unref (combostore);
+
+    /* Make the things actually appear. */
+    gtk_widget_show_all (GTK_WIDGET(treeview));
+}
+
+/* Update the preview page based on the current state of the importer.
+ * Should be called when settings are changed.
+ */
+void
+CsvImpPriceAssist::preview_refresh ()
+{
+    // Set start row
+    auto adj = gtk_spin_button_get_adjustment (start_row_spin);
+    gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (start_row_spin,
+            price_imp->skip_start_lines());
+
+    // Set end row
+    adj = gtk_spin_button_get_adjustment (end_row_spin);
+    gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (end_row_spin,
+            price_imp->skip_end_lines());
+
+    // Set Alternate rows
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button),
+            price_imp->skip_alt_lines());
+
+    // Set over-write indicator
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(over_write_cbutton),
+            price_imp->over_write());
+
+    // Set Import Format
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(csv_button),
+            (price_imp->file_format() == GncImpFileFormat::CSV));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(fixed_button),
+            (price_imp->file_format() != GncImpFileFormat::CSV));
+
+    // This section deals with the combo's and character encoding
+    gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo),
+            price_imp->date_format());
+    gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo),
+            price_imp->currency_format());
+    go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str());
+
+    // Handle separator checkboxes and custom field, only relevant if the file format is csv
+    if (price_imp->file_format() == GncImpFileFormat::CSV)
+    {
+        auto separators = price_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(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(custom_cbutton),
+                !separators.empty());
+        gtk_entry_set_text (GTK_ENTRY(custom_entry), separators.c_str());
+    }
+    // Repopulate the parsed data table
+    g_idle_add ((GSourceFunc)csv_imp_preview_queue_rebuild_table, this);
+}
+
+/* Check if all selected data can be parsed sufficiently to continue
+ */
+void CsvImpPriceAssist::preview_validate_settings ()
+{
+    /* Allow the user to proceed only if there are no inconsistencies in the settings */
+    auto error_msg = price_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());
+}
+
+/*******************************************************
+ * Assistant page prepare functions
+ *******************************************************/
+
+void
+CsvImpPriceAssist::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(file_chooser), starting_dir);
+        g_free (starting_dir);
+    }
+}
+
+void
+CsvImpPriceAssist::assist_preview_page_prepare ()
+{
+    /* Disable the Forward Assistant Button */
+    gtk_assistant_set_page_complete (csv_imp_asst, preview_page, false);
+
+    /* Load the data into the treeview. */
+    preview_refresh_table ();
+}
+
+void
+CsvImpPriceAssist::assist_confirm_page_prepare ()
+{
+}
+
+void
+CsvImpPriceAssist::assist_summary_page_prepare ()
+{
+    auto text = std::string("<span size=\"medium\"><b>");
+    text += _("The prices were imported from the file '") + m_file_name + "'.";
+    text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added);
+    text += _(" and ") + std::to_string(price_imp->m_prices_duplicated);
+    text += _(" were duplicated.");
+    text += "</b></span>";
+
+    gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str());
+}
+
+void
+CsvImpPriceAssist::assist_prepare_cb (GtkWidget *page)
+{
+    if (page == file_page)
+        assist_file_page_prepare ();
+    else if (page == preview_page)
+        assist_preview_page_prepare ();
+    else if (page == confirm_page)
+        assist_confirm_page_prepare ();
+    else if (page == summary_page)
+        assist_summary_page_prepare ();
+}
+
+void
+CsvImpPriceAssist::assist_finish (bool canceled)
+{
+    /* Start the import */
+//FIXME Apply button
+g_print("Finish\n");
+//    if (canceled || price_imp->m_transactions.empty())
+//        gnc_gen_trans_list_delete (gnc_csv_importer_gui);
+//    else
+//        gnc_gen_trans_assist_start (gnc_csv_importer_gui);
+
+
+//FIXME Cancel comes here to, check when nothing set, goes to catch below also
+
+    /* Create prices from the parsed data */
+    try
+    {
+        price_imp->create_prices ();
+    }
+    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 creating prices. Please report this as a bug.\n\n"
+              "Error message:\n%s"), err.what());
+        gtk_assistant_set_current_page (csv_imp_asst, 2);
+    }
+}
+
+void
+CsvImpPriceAssist::assist_compmgr_close ()
+{
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst));
+    gtk_widget_destroy (GTK_WIDGET(csv_imp_asst));
+}
+
+static void
+csv_price_imp_close_handler (gpointer user_data)
+{
+    auto info = (CsvImpPriceAssist*)user_data;
+    info->assist_compmgr_close();
+}
+
+/********************************************************************\
+ * gnc_file_csv_price_import                                        *
+ * opens up a assistant to import prices.                           *
+ *                                                                  *
+ * Args:   none                                                     *
+ * Return: nothing                                                  *
+\********************************************************************/
+void
+gnc_file_csv_price_import(void)
+{
+    auto info = new CsvImpPriceAssist;
+    gnc_register_gui_component (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS,
+                                nullptr, csv_price_imp_close_handler,
+                                info);
+}
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
new file mode 100644
index 0000000..705e827
--- /dev/null
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
@@ -0,0 +1,1001 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="2.24"/>
+  <!-- interface-naming-policy project-wide -->
+  <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 Price Assistant">
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <property name="title" translatable="yes">CSV Price Import</property>
+    <property name="default_width">400</property>
+    <property name="default_height">500</property>
+    <signal name="close" handler="csv_price_imp_assist_close_cb" swapped="no"/>
+    <signal name="destroy" handler="csv_price_imp_assist_destroy_cb" swapped="no"/>
+    <signal name="apply" handler="csv_price_imp_assist_finish_cb" swapped="no"/>
+    <signal name="prepare" handler="csv_price_imp_assist_prepare_cb" swapped="no"/>
+    <signal name="cancel" handler="csv_price_imp_assist_cancel_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 Prices from a CSV file.
+
+There is a minimum number of columns that have to be present for a successful import, for Stock prices these are Date, Amount, Symbol From and for Currency they are Date, Amount, Currency From and Currency To.
+
+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.
+
+Examples are "RR.L","21/11/2016",5.345,"GBP" and "USD","2016-11-21",1.56,"GBP"
+
+There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row which can be used if you have some header text. Also there is an option to over write existing prices for that day if required.
+
+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.
+
+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="wrap">True</property>
+      </object>
+      <packing>
+        <property name="page_type">intro</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="label7">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">
+Select location and file name for the Import, then click 'OK'...
+</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">Select File for Import</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="preview_page">
+        <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="border_width">12</property>
+        <property name="spacing">2</property>
+        <child>
+          <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">2</property>
+            <property name="column_spacing">5</property>
+            <property name="row_spacing">5</property>
+            <child>
+              <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>
+                          <object class="GtkButton" id="delete_settings">
+                            <property name="visible">True</property>
+                            <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_price_imp_preview_del_settings_cb" swapped="no"/>
+                            <child>
+                              <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>
+                          <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_price_imp_preview_save_settings_cb" swapped="no"/>
+                            <child>
+                              <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>
+                  </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="GtkVBox" id="vbox1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkTable" id="table4">
+                            <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="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_price_imp_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="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkTable" id="separator_table">
+                            <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>
+                            <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_price_imp_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_price_imp_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_price_imp_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_price_imp_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_price_imp_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_price_imp_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_price_imp_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_price_imp_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="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <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="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkTable" id="table5">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <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="over_write_button">
+                                <property name="label" translatable="yes">Allow existing prices to be over written.</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 prices are not over written, select this to change that.</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_price_imp_preview_overwrite_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </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="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="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">
+                        <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">
+                            <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_price_imp_preview_srow_cb" swapped="no"/>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </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_price_imp_preview_erow_cb" swapped="no"/>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </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>
+                          <object class="GtkCheckButton" id="skip_rows">
+                            <property name="label" translatable="yes">Skip alternate lines</property>
+                            <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_price_imp_preview_skiprows_cb" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </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="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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <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="viewport2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkVBox" id="vbox8">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkTreeView" id="ctreeview">
+                        <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="headers_visible">False</property>
+                        <property name="enable_grid_lines">both</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkTreeView" id="treeview">
+                        <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="enable_grid_lines">both</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <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>
+        <child>
+          <object class="GtkHBox" id="hbox14">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <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>
+                <property name="xalign">1</property>
+                <property name="image_position">right</property>
+                <property name="draw_indicator">True</property>
+                <signal name="toggled" handler="csv_price_imp_preview_skiperrors_cb" swapped="no"/>
+              </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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="page_type">intro</property>
+        <property name="complete">True</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="confirm_page">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="border_width">12</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 add Prices.
+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>
+      </object>
+      <packing>
+        <property name="page_type">confirm</property>
+        <property name="title" translatable="yes">Import Prices Now</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="wrap">True</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</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/gnucash/import-export/csv-imp/assistant-csv-price-import.h b/gnucash/import-export/csv-imp/assistant-csv-price-import.h
new file mode 100644
index 0000000..213b622
--- /dev/null
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.h
@@ -0,0 +1,36 @@
+/*******************************************************************\
+ * assistant-csv-price-import.h -- An assistant for importing       *
+ *                                     Prices from a file.          *
+ *                                                                  *
+ * Copyright (C) 2017 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-price-import.h
+    @brief CSV Import Assistant
+    @author Copyright (c) 2017 Robert Fewell
+*/
+#ifndef GNC_ASSISTANT_CSV_IMPORT_PRICE_H
+#define GNC_ASSISTANT_CSV_IMPORT_PRICE_H
+
+
+/** The gnc_file_csv_price_import() will let the user import the
+ *  commodity prices from a file.
+ */
+void gnc_file_csv_price_import (void);
+#endif
diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml b/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
index c119a64..bdd3043 100644
--- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
+++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
@@ -5,6 +5,7 @@
       	<placeholder name="FileImportPlaceholder">
       	   <menuitem name="FileCsvImportAccounts" action="CsvImportAccountAction"/>
       	   <menuitem name="FileCsvImportTrans" action="CsvImportTransAction"/>
+           <menuitem name="FileCsvImportPrice" action="CsvImportPriceAction"/>
       	</placeholder>
       </menu>
     </menu>
diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
index 25954be..c14092d 100644
--- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
+++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
@@ -30,6 +30,7 @@
 
 #include "assistant-csv-account-import.h"
 #include "assistant-csv-trans-import.h"
+#include "assistant-csv-price-import.h"
 
 static void gnc_plugin_csv_import_class_init (GncPluginCsvImportClass *klass);
 static void gnc_plugin_csv_import_init (GncPluginCsvImport *plugin);
@@ -38,6 +39,7 @@ 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_price_cmd (GtkAction *action, GncMainWindowActionData *data);
 
 #define PLUGIN_ACTIONS_NAME "gnc-plugin-csv-import-actions"
 #define PLUGIN_UI_FILENAME  "gnc-plugin-csv-import-ui.xml"
@@ -54,6 +56,11 @@ static GtkActionEntry gnc_plugin_actions [] =
         N_("Import Transactions from a CSV file"),
         G_CALLBACK (gnc_plugin_csv_import_trans_cmd)
     },
+    {
+        "CsvImportPriceAction", GTK_STOCK_CONVERT, N_("Import _Prices from a CSV file..."), NULL,
+        N_("Import Prices from a CSV file"),
+        G_CALLBACK (gnc_plugin_csv_import_price_cmd)
+    },
 };
 static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions);
 
@@ -157,6 +164,13 @@ gnc_plugin_csv_import_trans_cmd (GtkAction *action,
     gnc_file_csv_trans_import ();
 }
 
+static void
+gnc_plugin_csv_import_price_cmd (GtkAction *action,
+                                 GncMainWindowActionData *data)
+{
+    gnc_file_csv_price_import ();
+}
+
 /************************************************************
  *                    Plugin Bootstrapping                   *
  ************************************************************/

commit 404bc1e329bddb6726cab3577f98c49ef7294ffa
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 10:55:29 2017 +0000

    Rename function gnc_csv_price_col_type_strs to gnc_price_col_type_strs

diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 1fdcaa53..a39f178 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -44,7 +44,7 @@ extern "C" {
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
 /* This map contains a set of strings representing the different column types. */
-std::map<GncPricePropType, const char*> gnc_csv_price_col_type_strs = {
+std::map<GncPricePropType, const char*> gnc_price_col_type_strs = {
         { GncPricePropType::NONE, N_("None") },
         { GncPricePropType::DATE, N_("Date") },
         { GncPricePropType::AMOUNT, N_("Amount") },
@@ -341,7 +341,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
     }
     catch (const std::invalid_argument& e)
     {
-        auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) +
+        auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) +
                        std::string(_(" could not be understood.\n")) +
                        e.what();
         m_errors.emplace(prop_type, err_str);
@@ -349,7 +349,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
     }
     catch (const std::out_of_range& e)
     {
-        auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) +
+        auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) +
                        std::string(_(" could not be understood.\n")) +
                        e.what();
         m_errors.emplace(prop_type, err_str);
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index d101280..57083e9 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -56,10 +56,10 @@ enum class GncPricePropType {
 };
 
 /** Maps all column types to a string representation.
- *  The actual definition is in gnc-csv-imp-prices.cpp.
+ *  The actual definition is in gnc-price-props.cpp.
  *  Attention: that definition should be adjusted for any
  *  changes to enum class GncPricePropType ! */
-extern std::map<GncPricePropType, const char*> gnc_csv_price_col_type_strs;
+extern std::map<GncPricePropType, const char*> gnc_price_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.

commit bec1fbd1a08cbd1c0c408df5e38a6343aa9dccd8
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 10:53:41 2017 +0000

    Add price import files for the csv price importer
    
    These files are largely based on the csv transaction importer and with
    minimum of changes to settings files.

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index e2fa97f..0bc7d29 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -19,6 +19,7 @@ SET(csv_import_SOURCES
   gnc-csv-trans-settings.cpp
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
+  gnc-price-import.cpp
   gnc-price-props.cpp
   gnc-tokenizer.cpp
   gnc-trans-props.cpp
@@ -45,6 +46,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-trans-settings.hpp
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
+  gnc-price-import.hpp
   gnc-price-props.hpp
   gnc-tokenizer.hpp
   gnc-trans-props.hpp
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index 8b13d01..4083963 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-csv-gnumeric-popup.c \
   gnc-dummy-tokenizer.cpp \
   gnc-fw-tokenizer.cpp \
+  gnc-price-import.cpp \
   gnc-price-props.cpp \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
@@ -29,6 +30,7 @@ noinst_HEADERS = \
   gnc-csv-gnumeric-popup.h \
   gnc-dummy-tokenizer.hpp \
   gnc-fw-tokenizer.hpp \
+  gnc-price-import.hpp \
   gnc-price-props.hpp \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 6611875..48cfdce 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -108,6 +108,14 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
             GncTransPropType::PRICE
     };
 
+    preset->m_column_types_price = {
+            GncPricePropType::DATE,
+            GncPricePropType::AMOUNT,
+            GncPricePropType::CURRENCY_FROM,
+            GncPricePropType::CURRENCY_TO,
+            GncPricePropType::SYMBOL_FROM
+    };
+
     return preset;
 }
 
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 379c660..93275f2 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -36,6 +36,7 @@ extern "C" {
 #include <string>
 #include <vector>
 #include "gnc-trans-props.hpp"
+#include "gnc-price-props.hpp"
 #include "gnc-tokenizer.hpp"
 
 /** Enumeration for separator checkbutton types. These are the
@@ -93,7 +94,8 @@ std::string   m_separators;                   // Separators for csv format
 
 Account      *m_base_account;                 // Base account
 std::vector<GncTransPropType> m_column_types; // The Column types in order
-std::vector<uint32_t> m_column_widths;            // The Column widths
+std::vector<GncPricePropType> m_column_types_price; // The Column Price types in order
+std::vector<uint32_t> m_column_widths;        // The Column widths
 
 bool          m_load_error;                   // Was there an error while parsing the state file ?
 };
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
new file mode 100644
index 0000000..09deb93
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -0,0 +1,654 @@
+/********************************************************************\
+ * gnc-price-import.cpp - import prices 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                   *
+ *                                                                  *
+\********************************************************************/
+
+#include <guid.hpp>
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "gnc-ui-util.h" //get book
+#include "gnc-commodity.h"
+#include "gnc-pricedb.h"
+}
+
+#include <boost/regex.hpp>
+#include <boost/regex/icu.hpp>
+
+#include "gnc-price-import.hpp"
+#include "gnc-price-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;
+
+const int num_date_formats_price = 5;
+const gchar* date_format_user_price[] = {N_("y-m-d"),
+                                   N_("d-m-y"),
+                                   N_("m-d-y"),
+                                   N_("d-m"),
+                                   N_("m-d")
+                                  };
+
+const int num_currency_formats_price = 3;
+const gchar* currency_format_user_price[] = {N_("Locale"),
+                                       N_("Period: 123,456.78"),
+                                       N_("Comma: 123.456,78")
+                                      };
+
+
+/** Constructor for GncPriceImport.
+ * @return Pointer to a new GncCSvParseData
+ */
+GncPriceImport::GncPriceImport(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_skip_errors = false;
+    file_format(m_settings.m_file_format = format);
+}
+
+/** Destructor for GncPriceImport.
+ */
+GncPriceImport::~GncPriceImport()
+{
+}
+
+/** 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 std::ifstream::failure if file reloading fails
+ */
+void GncPriceImport::file_format(GncImpFileFormat 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 (m_tokenizer)
+    {
+        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;
+    m_tokenizer = gnc_tokenizer_factory(m_settings.m_file_format);
+
+    // Set up new tokenizer with common settings
+    // 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 GncPriceImport::file_format()
+{
+    return m_settings.m_file_format;
+}
+
+void GncPriceImport::over_write (bool over)
+{
+    m_over_write = over;
+}
+
+bool GncPriceImport::over_write () { return m_over_write; }
+
+void GncPriceImport::reset_formatted_column (std::vector<GncPricePropType>& col_types)
+{
+    for (auto col_type: col_types)
+    {
+        auto col = std::find (m_settings.m_column_types_price.begin(),
+                m_settings.m_column_types_price.end(), col_type);
+        if (col != m_settings.m_column_types_price.end())
+            set_column_type_price (col - m_settings.m_column_types_price.begin(), col_type, true);
+    }
+}
+
+void GncPriceImport::currency_format (int currency_format)
+{
+    m_settings.m_currency_format = currency_format;
+
+    /* Reparse all currency related columns */
+    std::vector<GncPricePropType> commodities = { GncPricePropType::AMOUNT };
+    reset_formatted_column (commodities);
+}
+int GncPriceImport::currency_format () { return m_settings.m_currency_format; }
+
+void GncPriceImport::date_format (int date_format)
+{
+    m_settings.m_date_format = date_format;
+
+    /* Reparse all date related columns */
+    std::vector<GncPricePropType> dates = { GncPricePropType::DATE };
+    reset_formatted_column (dates);
+}
+int GncPriceImport::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
+ * the wrong encoding.
+ * @param encoding Encoding that data should be translated using
+ */
+void GncPriceImport::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;
+}
+
+std::string GncPriceImport::encoding () { return m_settings.m_encoding; }
+
+void GncPriceImport::update_skipped_lines(boost::optional<uint32_t> start, boost::optional<uint32_t> 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 (uint32_t i = 0; i < m_parsed_lines.size(); i++)
+    {
+        std::get<3>(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
+    }
+}
+
+uint32_t GncPriceImport::skip_start_lines () { return m_settings.m_skip_start_lines; }
+uint32_t GncPriceImport::skip_end_lines () { return m_settings.m_skip_end_lines; }
+bool GncPriceImport::skip_alt_lines () { return m_settings.m_skip_alt_lines; }
+bool GncPriceImport::skip_err_lines () { return m_skip_errors; }
+
+void GncPriceImport::separators (std::string separators)
+{
+    if (file_format() != GncImpFileFormat::CSV)
+        return;
+
+    m_settings.m_separators = separators;
+    auto csvtok = dynamic_cast<GncCsvTokenizer*>(m_tokenizer.get());
+    csvtok->set_separators (separators);
+
+}
+std::string GncPriceImport::separators () { return m_settings.m_separators; }
+
+void GncPriceImport::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;
+    encoding (m_settings.m_encoding);
+
+    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);
+    }
+    catch (...)
+    { };
+
+    /* Tokenizing will clear column types, reset them here
+     * based on the loaded settings.
+     */
+    std::copy_n (settings.m_column_types_price.begin(),
+            std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()),
+            m_settings.m_column_types_price.begin());
+
+}
+
+bool GncPriceImport::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();
+}
+
+void GncPriceImport::settings_name (std::string name) { m_settings.m_name = name; }
+std::string GncPriceImport::settings_name () { return m_settings.m_name; }
+
+/** Loads a file into a GncPriceImport. This is the first function
+ * that must be called after creating a new GncPriceImport. 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
+ */
+void GncPriceImport::load_file (const std::string& filename)
+{
+
+    /* Get the raw data first and handle an error if one occurs. */
+    try
+    {
+        m_tokenizer->load_file (filename);
+        return;
+    }
+    catch (std::ifstream::failure& ios_err)
+    {
+        // Just log the error and pass it on the call stack for proper handling
+        PWARN ("Error: %s", ios_err.what());
+        throw;
+    }
+}
+
+/** Splits a file into cells. This requires having an encoding that
+ * works (see GncPriceImport::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
+ *          GncPricePropType::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 GncPriceImport::tokenize (bool guessColTypes)
+{
+    if (!m_tokenizer)
+        return;
+
+    uint32_t max_cols = 0;
+    m_tokenizer->tokenize();
+    m_parsed_lines.clear();
+    for (auto tokenized_line : m_tokenizer->get_tokens())
+    {
+        m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
+                std::make_shared<GncImportPrice>(date_format(), currency_format()),
+                false));
+        auto length = tokenized_line.size();
+        if (length > max_cols)
+            max_cols = length;
+    }
+
+    /* If it failed, generate an error. */
+    if (m_parsed_lines.size() == 0)
+    {
+        throw (std::range_error ("Tokenizing failed."));
+        return;
+    }
+
+    m_settings.m_column_types_price.resize(max_cols, GncPricePropType::NONE);
+
+    /* Force reinterpretation of already set columns and/or base_account */
+    for (uint32_t i = 0; i < m_settings.m_column_types_price.size(); i++)
+        set_column_type_price (i, m_settings.m_column_types_price[i], true);
+
+    if (guessColTypes)
+    {
+        /* Guess column_types based
+         * on the contents of each column. */
+        /* TODO Make it actually guess. */
+    }
+}
+
+
+struct ErrorListPrice
+{
+public:
+    void add_error (std::string msg);
+    std::string str();
+    bool empty() { return m_error.empty(); }
+private:
+    std::string m_error;
+};
+
+void ErrorListPrice::add_error (std::string msg)
+{
+    m_error += "- " + msg + "\n";
+}
+
+std::string ErrorListPrice::str()
+{
+    return m_error.substr(0, m_error.size() - 1);
+}
+
+
+/* Test for the required minimum number of columns selected and
+ * the selection is consistent.
+ * @param An ErrorListPrice object to which all found issues are added.
+ */
+void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
+{
+    /* Verify if a date column is selected and it's parsable.
+     */
+    if (!check_for_column_type(GncPricePropType::DATE))
+        error_msg.add_error( _("Please select a date column."));
+
+    /* Verify an amount column is selected.
+     */
+    if (!check_for_column_type(GncPricePropType::AMOUNT))
+        error_msg.add_error( _("Please select an amount column."));
+
+    /* Verify an Currency to column is selected.
+     */
+    if (!check_for_column_type(GncPricePropType::CURRENCY_TO))
+        error_msg.add_error( _("Please select a Currency to column."));
+
+    /* Verify at least one from column (symbol_from or currency_from) column is selected.
+     */
+    if (!check_for_column_type(GncPricePropType::SYMBOL_FROM) &&
+        !check_for_column_type(GncPricePropType::CURRENCY_FROM))
+        error_msg.add_error( _("Please select a symbol or currency from column."));
+}
+
+
+/* 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.
+ */
+std::string GncPriceImport::verify ()
+{
+    auto newline = std::string();
+    auto error_msg = ErrorListPrice();
+
+    /* 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_msg.add_error(_("No lines are selected for importing. Please reduce the number of lines to skip."));
+        return error_msg.str();
+    }
+
+    verify_column_selections (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<3>(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();
+}
+
+/** Checks whether the parsed line contains all essential properties.
+ * @param parsed_line The line we are checking
+ * @exception std::invalid_argument in an essential property is missing
+ */
+static void price_properties_verify_essentials (std::vector<parse_line_t>::iterator& parsed_line)
+{
+    std::string error_message;
+    std::shared_ptr<GncImportPrice> price_props;
+    std::tie(std::ignore, error_message, price_props, std::ignore) = *parsed_line;
+
+    auto price_error = price_props->verify_essentials();
+
+    error_message.clear();
+    if (!price_error.empty())
+    {
+        error_message += price_error;
+        error_message += "\n";
+    }
+
+    if (!error_message.empty())
+        throw std::invalid_argument(error_message);
+}
+
+void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_line)
+{
+    StrVec line;
+    std::string error_message;
+    std::shared_ptr<GncImportPrice> price_props = nullptr;
+    bool skip_line = false;
+    std::tie(line, error_message, price_props, skip_line) = *parsed_line;
+
+    if (skip_line)
+        return;
+
+    error_message.clear();
+
+    /* If column parsing was successful, convert price properties into a price. */
+    try
+    {
+        price_properties_verify_essentials (parsed_line);
+
+        QofBook* book = gnc_get_current_book();
+        GNCPriceDB *pdb = gnc_pricedb_get_db (book);
+
+        /* If all went well, add this price to the list. */
+        auto price_created = price_props->create_price (book, pdb, m_over_write);
+//FIXME Need to look at this
+        if (price_created)
+            m_prices_added++;
+        else
+            m_prices_duplicated++;
+    }
+    catch (const std::invalid_argument& e)
+    {
+        error_message = e.what();
+        PINFO("User warning: %s", error_message.c_str());
+    }
+}
+
+
+/** Creates a list of prices from parsed data. The parsed data
+ * 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 or processing fails.
+ */
+void GncPriceImport::create_prices ()
+{
+    /* Start with verifying the current data. */
+    auto verify_result = verify();
+    if (!verify_result.empty())
+        throw std::invalid_argument (verify_result);
+
+    m_prices_added = 0;
+    m_prices_duplicated = 0;
+
+    /* Iterate over all parsed lines */
+    for (auto parsed_lines_it = m_parsed_lines.begin();
+            parsed_lines_it != m_parsed_lines.end();
+            ++parsed_lines_it)
+    {
+        /* Skip current line if the user specified so */
+        if ((std::get<3>(*parsed_lines_it)))
+            continue;
+
+        /* Should not throw anymore, otherwise verify needs revision */
+        create_price (parsed_lines_it);
+    }
+    PINFO("Number of lines is %d, added is %d, duplicates is %d",
+         (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated);
+}
+
+bool
+GncPriceImport::check_for_column_type (GncPricePropType type)
+{
+    return (std::find (m_settings.m_column_types_price.begin(),
+                       m_settings.m_column_types_price.end(), type)
+                        != m_settings.m_column_types_price.end());
+}
+
+/* A helper function intended to be called only from set_column_type_price */
+void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type)
+{
+    if (prop_type == GncPricePropType::NONE)
+        return; /* Only deal with price related properties. */
+
+    auto price_props = std::make_shared<GncImportPrice> (*(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())
+        price_props->reset (prop_type);
+    else
+    {
+        try
+        {
+            price_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<3>(m_parsed_lines[row]))
+                PINFO("User warning: %s", e.what());
+        }
+    }
+    /* Store the result */
+    std::get<2>(m_parsed_lines[row]) = price_props;
+}
+
+void
+GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, bool force)
+{
+    if (position >= m_settings.m_column_types_price.size())
+        return;
+
+    auto old_type = m_settings.m_column_types_price[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_price.begin(), m_settings.m_column_types_price.end(),
+            type, GncPricePropType::NONE);
+
+    m_settings.m_column_types_price.at (position) = type;
+
+    /* Update the preparsed data */
+    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 price 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<2>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format);
+
+        uint32_t 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 > GncPricePropType::NONE)
+                    && (old_type <= GncPricePropType::PRICE_PROPS))
+                update_price_props (row, old_col, old_type);
+        }
+        /* Then set the property represented by the new column type */
+        if ((type > GncPricePropType::NONE)
+                && (type <= GncPricePropType::PRICE_PROPS))
+            update_price_props (row, position, type);
+
+        /* Report errors if there are any */
+        auto price_errors = std::get<2>(*parsed_lines_it)->errors();
+        std::get<1>(*parsed_lines_it) =
+                price_errors +
+                (price_errors.empty() ? std::string() : "\n");
+    }
+}
+
+std::vector<GncPricePropType> GncPriceImport::column_types_price ()
+{
+    return m_settings.m_column_types_price;
+}
+
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
new file mode 100644
index 0000000..0898215
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -0,0 +1,160 @@
+/********************************************************************\
+ * gnc-price-import.hpp - import prices 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 prices from CSV or fixed width files
+     *
+     gnc-price-import.hpp
+     @author Copyright (c) 2015 Geert Janssens <geert at kobaltwit.be>
+     @author Copyright (c) 2017 Robert Fewell
+ */
+
+#ifndef GNC_PRICE_IMPORT_HPP
+#define GNC_PRICE_IMPORT_HPP
+
+extern "C" {
+#include "config.h"
+
+}
+
+#include <vector>
+#include <set>
+#include <map>
+#include <memory>
+
+#include "gnc-tokenizer.hpp"
+#include "gnc-price-props.hpp"
+#include "gnc-csv-trans-settings.hpp"
+#include <boost/optional.hpp>
+
+/* 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[];
+
+/** Tuple to hold
+ *  - a tokenized line of input
+ *  - an optional error string
+ *  - a struct to hold user selected properties for a price */
+using parse_line_t = std::tuple<StrVec,
+                                std::string,
+                                std::shared_ptr<GncImportPrice>,
+                                bool>;
+struct ErrorListPrice;
+
+/** The actual PriceImport 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 GncPriceImport
+{
+public:
+    // Constructor - Destructor
+    GncPriceImport(GncImpFileFormat format = GncImpFileFormat::UNKNOWN);
+    ~GncPriceImport();
+
+    void file_format(GncImpFileFormat format);
+    GncImpFileFormat file_format();
+
+    void over_write (bool over);
+    bool over_write ();
+
+    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 update_skipped_lines (boost::optional<uint32_t> start, boost::optional<uint32_t> end,
+                               boost::optional<bool> alt, boost::optional<bool> errors);
+    uint32_t skip_start_lines ();
+    uint32_t skip_end_lines ();
+    bool skip_alt_lines ();
+    bool skip_err_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();
+
+    /** This function will attempt to convert all tokenized lines into
+     *  prices using the column types the user has set.
+     */
+    void create_prices ();
+    bool check_for_column_type (GncPricePropType type);
+    void set_column_type_price (uint32_t position, GncPricePropType type, bool force = false);
+    std::vector<GncPricePropType> column_types_price ();
+
+    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
+                                                     price properties. */
+    int  m_prices_added;
+    int  m_prices_duplicated;
+
+private:
+    /** A helper function used by create_prices. It will attempt
+     *  to convert a single tokenized line into a price using
+     *  the column types the user has set.
+     */
+    void create_price (std::vector<parse_line_t>::iterator& parsed_line);
+
+    void verify_column_selections (ErrorListPrice& error_msg);
+
+    /* Internal helper function to force reparsing of columns subject to format changes */
+    void reset_formatted_column (std::vector<GncPricePropType>& col_types);
+
+    /* Two internal helper functions that should only be called from within
+     * set_column_type_price for consistency (otherwise error messages may not be (re)set)
+     */
+    void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type);
+
+    struct CsvTranSettings;
+    CsvTransSettings m_settings;
+    bool m_skip_errors;
+    bool m_over_write;
+};
+
+
+#endif
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 6f1189b..d101280 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -31,7 +31,7 @@ extern "C" {
 #endif
 
 #include <glib/gi18n.h>
-
+#include "gnc-pricedb.h"
 #include "gnc-commodity.h"
 }
 

commit 17d8d424a0a6266a9790797d88452f2c356f3e0d
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 10:44:15 2017 +0000

    Add property files for the csv price importer
    
    These files are largely based on the csv transaction importer

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index c2a77b8..e2fa97f 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -19,6 +19,7 @@ SET(csv_import_SOURCES
   gnc-csv-trans-settings.cpp
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
+  gnc-price-props.cpp
   gnc-tokenizer.cpp
   gnc-trans-props.cpp
   gnc-tx-import.cpp
@@ -44,6 +45,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-trans-settings.hpp
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
+  gnc-price-props.hpp
   gnc-tokenizer.hpp
   gnc-trans-props.hpp
   gnc-tx-import.hpp
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index 06ef43f..8b13d01 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-csv-gnumeric-popup.c \
   gnc-dummy-tokenizer.cpp \
   gnc-fw-tokenizer.cpp \
+  gnc-price-props.cpp \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
   gnc-trans-props.cpp \
@@ -28,6 +29,7 @@ noinst_HEADERS = \
   gnc-csv-gnumeric-popup.h \
   gnc-dummy-tokenizer.hpp \
   gnc-fw-tokenizer.hpp \
+  gnc-price-props.hpp \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
   gnc-trans-props.hpp \
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
new file mode 100644
index 0000000..1fdcaa53
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -0,0 +1,508 @@
+/********************************************************************\
+ * gnc-price-props.cpp - encapsulate price 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                   *
+ *                                                                  *
+\********************************************************************/
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "engine-helpers.h"
+#include "gnc-ui-util.h"
+#include "gnc-pricedb.h"
+
+}
+
+#include <string>
+#include <boost/regex.hpp>
+#include <boost/regex/icu.hpp>
+#include "gnc-price-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<GncPricePropType, const char*> gnc_csv_price_col_type_strs = {
+        { GncPricePropType::NONE, N_("None") },
+        { GncPricePropType::DATE, N_("Date") },
+        { GncPricePropType::AMOUNT, N_("Amount") },
+        { GncPricePropType::CURRENCY_FROM, N_("Currency From") },
+        { GncPricePropType::CURRENCY_TO, N_("Currency To") },
+        { GncPricePropType::SYMBOL_FROM, N_("Symbol From") },
+};
+
+/* Regular expressions used to parse dates per date format */
+const char* date_regex_price[] = {
+                             "(?:"                                   // 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_price (const std::string &date_str, int format)
+{
+    boost::regex r(date_regex_price[format]);
+    boost::smatch what;
+    if(!boost::regex_search(date_str, what, r))
+        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 (_("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"));
+
+    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);
+    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;
+}
+
+
+/** 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 GncNumeric
+ * @exception May throw std::invalid argument if string can't be parsed properly
+ */
+GncNumeric parse_amount_price (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 (_("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, "");
+
+    /* 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 (_("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 (_("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 (_("Value can't be parsed into a number using the selected currency format."));
+        break;
+    }
+
+    return GncNumeric(val);
+}
+
+gnc_commodity* parse_commodity_price_comm (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 (_("Value can't be parsed into a valid commodity."));
+    else
+        return comm;
+}
+
+gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency)
+{
+    if (sym_str.empty())
+        return nullptr;
+
+    auto commodity_table = gnc_get_current_commodities ();
+    GList         *namespaces;
+    gnc_commodity *retval = nullptr;
+    gnc_commodity *tmp_commodity = nullptr;
+    char  *tmp_namespace = nullptr;
+    GList *commodity_list = NULL;
+    GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
+
+    namespace_list = g_list_first (namespace_list);
+    while (namespace_list != NULL && retval == NULL)
+    {
+        tmp_namespace = (char*)namespace_list->data;
+        DEBUG("Looking at namespace %s", tmp_namespace);
+        commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
+        commodity_list  = g_list_first (commodity_list);
+        while (commodity_list != NULL && retval == NULL)
+        {
+            const char* tmp_mnemonic = NULL;
+            tmp_commodity = (gnc_commodity*)commodity_list->data;
+            DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity));
+            tmp_mnemonic = gnc_commodity_get_mnemonic (tmp_commodity);
+            if (g_strcmp0 (tmp_mnemonic, sym_str.c_str()) == 0)
+            {
+                retval = tmp_commodity;
+                DEBUG("Commodity %s%s", gnc_commodity_get_fullname (retval), " matches.");
+            }
+            commodity_list = g_list_next (commodity_list);
+        }
+        namespace_list = g_list_next (namespace_list);
+    }
+    g_list_free (commodity_list);
+    g_list_free (namespace_list);
+
+    if (!retval)
+        throw std::invalid_argument (_("Value can't be parsed into a valid commodity."));
+    else
+    {
+        if (gnc_commodity_is_currency (retval) != is_currency)
+            throw std::invalid_argument (_("Value parsed into an invalid commodity for column type."));
+        else
+            return retval;
+    }
+}
+
+void GncImportPrice::set (GncPricePropType 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);
+
+        gnc_commodity *comm = nullptr;
+        switch (prop_type)
+        {
+            case GncPricePropType::DATE:
+                m_date = boost::none;
+                m_date = parse_date_price (value, m_date_format); // Throws if parsing fails
+                break;
+
+            case GncPricePropType::AMOUNT:
+                m_amount = boost::none;
+                m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails
+                break;
+
+            case GncPricePropType::CURRENCY_FROM:
+                m_currency_from = boost::none;
+                comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
+                if (comm)
+                    m_currency_from = comm;
+                break;
+
+            case GncPricePropType::CURRENCY_TO:
+                m_currency_to = boost::none;
+                comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
+                if (comm)
+                    m_currency_to = comm;
+                break;
+
+            case GncPricePropType::SYMBOL_FROM:
+                m_symbol_from = boost::none;
+                comm = parse_commodity_price_sym (value, false); // Throws if parsing fails
+                if (comm)
+                    m_symbol_from = comm;
+                break;
+
+            default:
+                /* Issue a warning for all other prop_types. */
+                PWARN ("%d is an invalid property for a Price", static_cast<int>(prop_type));
+                break;
+        }
+    }
+    catch (const std::invalid_argument& e)
+    {
+        auto err_str = std::string(_(gnc_csv_price_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_price_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 GncImportPrice::reset (GncPricePropType prop_type)
+{
+    try
+    {
+        set (prop_type, std::string());
+    }
+    catch (...)
+    {
+        // Set with an empty string will effectively clear the property
+        // but can also set an error for the property. Clear that error here.
+        m_errors.erase(prop_type);
+    }
+}
+
+std::string GncImportPrice::verify_essentials (void)
+{
+    /* Make sure this price has the minimum required set of properties defined */
+    if (m_date == boost::none)
+        return _("No date column.");
+    else if (m_amount == boost::none)
+        return _("No amount column.");
+    else if (m_currency_to == boost::none)
+        return _("No Currency to column.");
+    else if ((m_symbol_from == boost::none) && (m_currency_from == boost::none))
+        return _("No from column.");
+    else
+        return std::string();
+}
+
+bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
+{
+    /* Gently refuse to create the price 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 price because essentials not set properly: %s", check.c_str());
+        return false;
+    }
+
+    Timespec date;
+    timespecFromTime64 (&date, *m_date);
+    date.tv_nsec = 0;
+
+#ifdef skip
+//FIXME Numeric needs changing, copied from old version...
+    bool rev = false;
+    gnc_commodity *comm_from = nullptr;
+
+    if (m_currency_from != boost::none) // Currency Import
+    {
+        // Check for currency in reverse direction.
+        GNCPrice *rev_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, *m_currency_from, date);
+        if (rev_price != nullptr)
+            rev = true;
+        gnc_price_unref (rev_price);
+
+        // Check for price less than 1, reverse if so.
+        if (gnc_numeric_compare (*m_amount, gnc_numeric_create (1, 1)) != 1)
+            rev = true;
+
+        comm_from = *m_currency_from;
+        DEBUG("Commodity from is a Currency");
+    }
+    else
+        comm_from = *m_symbol_from;
+
+    DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date),
+          rev, gnc_commodity_get_fullname (comm_from), gnc_commodity_get_fullname (*m_currency_to),
+          gnc_num_dbg_to_string (*m_amount)           );
+
+    GNCPrice *old_price = nullptr;
+
+    // Should the commodities be reversed
+    if (rev)
+        old_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, comm_from, date);
+    else
+        old_price = gnc_pricedb_lookup_day (pdb, comm_from, *m_currency_to, date);
+
+    // Should old price be over writen
+    if ((old_price != nullptr) && (over == true))
+    {
+        DEBUG("Over write");
+        gnc_pricedb_remove_price (pdb, old_price);
+        gnc_price_unref (old_price);
+        old_price = nullptr;
+    }
+#endif
+    bool ret_val = true;
+#ifdef skip
+    // Create the new price
+    if (old_price == nullptr)
+    {
+        DEBUG("Create");
+        GNCPrice *price = gnc_price_create (book);
+        gnc_price_begin_edit (price);
+
+        if (rev)
+        {
+            gnc_price_set_commodity (price, *m_currency_to);
+            gnc_price_set_currency (price, comm_from);
+            *m_amount = gnc_numeric_convert (gnc_numeric_invert (*m_amount),
+                                          CURRENCY_DENOM, GNC_HOW_RND_ROUND_HALF_UP);
+            gnc_price_set_value (price, *m_amount);
+        }
+        else
+        {
+            gnc_price_set_commodity (price, comm_from);
+            gnc_price_set_currency (price, *m_currency_to);
+            gnc_price_set_value (price, *m_amount);
+        }
+        gnc_price_set_time (price, date);
+        gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE);
+//FIXME Not sure which one        gnc_price_set_source (price, PRICE_SOURCE_FQ);
+        gnc_price_set_typestr (price, PRICE_TYPE_LAST);
+        gnc_price_commit_edit (price);
+
+        bool perr = gnc_pricedb_add_price (pdb, price);
+
+        gnc_price_unref (price);
+
+         if (perr == false)
+            throw std::invalid_argument (_("Failed to create price from selected columns."));
+//FIXME Not sure about this, should this be a PWARN
+    }
+    else
+
+#endif
+        ret_val = false;
+
+    return ret_val;
+}
+
+static std::string gen_err_str (std::map<GncPricePropType, std::string>& errors)
+{
+    auto full_error = std::string();
+    for (auto error : errors)
+    {
+        full_error += (full_error.empty() ? "" : "\n") + error.second;
+    }
+    return full_error;
+}
+
+std::string GncImportPrice::errors ()
+{
+    return gen_err_str (m_errors);
+}
+
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
new file mode 100644
index 0000000..6f1189b
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -0,0 +1,110 @@
+/********************************************************************\
+ * gnc-price-props.hpp - encapsulate price 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_PRICE_PROPS_HPP
+#define GNC_PRICE_PROPS_HPP
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "gnc-commodity.h"
+}
+
+#include <string>
+#include <map>
+#include <memory>
+#include <boost/optional.hpp>
+#include <gnc-numeric.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 GncPricePropType::NONE
+ * type. */
+enum class GncPricePropType {
+    NONE,
+    DATE,
+    AMOUNT,
+    CURRENCY_FROM,
+    CURRENCY_TO,
+    SYMBOL_FROM,
+    PRICE_PROPS = SYMBOL_FROM
+};
+
+/** Maps all column types to a string representation.
+ *  The actual definition is in gnc-csv-imp-prices.cpp.
+ *  Attention: that definition should be adjusted for any
+ *  changes to enum class GncPricePropType ! */
+extern std::map<GncPricePropType, const char*> gnc_csv_price_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_price_prop_type_str
+{
+    test_price_prop_type_str( const char* name ) : m_name(name) {}
+    bool operator()( const std::pair<GncPricePropType, const char*>& v ) const
+    {
+        return !g_strcmp0(v.second, m_name);
+    }
+private:
+    const char *m_name;
+};
+
+time64 parse_date_price (const std::string &date_str, int format);
+gnc_commodity* parse_commodity_price_comm (const std::string& comm_str);
+gnc_commodity* parse_commodity_price_sym (const std::string& comm_str, bool is_currency);
+GncNumeric parse_amount_price (const std::string &str, int currency_format);
+
+struct GncImportPrice
+{
+public:
+    GncImportPrice (int date_format, int currency_format) : m_date_format{date_format},
+        m_currency_format{currency_format}{};
+
+    void set (GncPricePropType prop_type, const std::string& value);
+    void set_date_format (int date_format) { m_date_format = date_format ;}
+    void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
+    void reset (GncPricePropType prop_type);
+    std::string verify_essentials (void);
+    bool create_price (QofBook* book, GNCPriceDB *pdb, bool over);
+    std::string errors();
+
+private:
+    int m_date_format;
+    int m_currency_format;
+    boost::optional<time64> m_date;
+    boost::optional<GncNumeric> m_amount;
+    boost::optional<gnc_commodity*> m_currency_from;
+    boost::optional<gnc_commodity*> m_currency_to;
+    boost::optional<gnc_commodity*> m_symbol_from;
+    bool created = false;
+
+    std::map<GncPricePropType, std::string> m_errors;
+};
+
+#endif

commit 66da4ae37404dc46a6ee1a81289cd2b8fd506383
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Dec 7 11:19:18 2017 +0000

    Add a test for empty values
    
    Some csv values are allowed to be empty based on options selected so
    add a test for this otherwise all values are required.

diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 705d37c..61c876d 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -649,15 +649,13 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro
         return; /* Only deal with price related properties. */
 
     auto price_props = std::make_shared<GncImportPrice> (*(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())
-        price_props->reset (prop_type);
+    if (col >= std::get<0>(m_parsed_lines[row]).size())
+        price_props->reset (prop_type); //reset errors
     else
     {
+        auto value = std::get<0>(m_parsed_lines[row]).at(col);
+        bool enable_test_empty = true;
         try
         {
             // set the from_commodity based on combo so we can test for same.
@@ -665,14 +663,20 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro
             {
                 if (m_settings.m_from_commodity)
                     price_props->set_from_commodity (m_settings.m_from_commodity);
+
+                if (m_settings.m_to_currency)
+                    enable_test_empty = false;
             }
             // set the to_currency based on combo so we can test for same.
             if (prop_type == GncPricePropType::FROM_COMMODITY)
             {
                 if (m_settings.m_to_currency)
                     price_props->set_to_currency (m_settings.m_to_currency);
+
+                if (m_settings.m_from_commodity)
+                    enable_test_empty = false;
             }
-            price_props->set(prop_type, value);
+            price_props->set(prop_type, value, enable_test_empty);
         }
         catch (const std::exception& e)
         {
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 1ccd6e5..151bd38 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -135,13 +135,17 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
         return comm;
 }
 
-void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
+void GncImportPrice::set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty)
 {
     try
     {
         // Drop any existing error for the prop_type we're about to set
         m_errors.erase(prop_type);
 
+        // conditional test for empty values
+        if (value.empty() && enable_test_empty)
+            throw std::invalid_argument (_("Column value can not be empty."));
+
         gnc_commodity *comm = nullptr;
         switch (prop_type)
         {
@@ -207,7 +211,8 @@ void GncImportPrice::reset (GncPricePropType prop_type)
 {
     try
     {
-        set (prop_type, std::string());
+        // set enable_test_empty to false to allow empty values
+        set (prop_type, std::string(), false);
     }
     catch (...)
     {
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 77e39dc..3a35861 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -86,7 +86,7 @@ public:
     GncImportPrice (int date_format, int currency_format) : m_date_format{date_format},
         m_currency_format{currency_format}{};
 
-    void set (GncPricePropType prop_type, const std::string& value);
+    void set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty);
     void set_date_format (int date_format) { m_date_format = date_format ;}
     void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
     void reset (GncPricePropType prop_type);

commit db079b55404dfa1c04533b28d6d0ecd38da10689
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Dec 7 11:17:14 2017 +0000

    Replace date parse function with one from gnc_datetime

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index af810cb..fbc563c 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -608,10 +608,8 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
 
         /* 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_price; i++)
-        {
-            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user_price[i]));
-        }
+        for (auto& date_fmt : GncDate::c_formats)
+            gtk_combo_box_text_append_text (date_format_combo, _(date_fmt.m_fmt.c_str()));
         gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0);
         g_signal_connect (G_OBJECT(date_format_combo), "changed",
                          G_CALLBACK(csv_price_imp_preview_date_fmt_sel_cb), this);
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 838c947..705d37c 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -46,14 +46,6 @@ extern "C" {
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
-const int num_date_formats_price = 5;
-const gchar* date_format_user_price[] = {N_("y-m-d"),
-                                   N_("d-m-y"),
-                                   N_("m-d-y"),
-                                   N_("d-m"),
-                                   N_("m-d")
-                                  };
-
 const int num_currency_formats_price = 3;
 const gchar* currency_format_user_price[] = {N_("Locale"),
                                        N_("Period: 123,456.78"),
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 9959aaa..fd8ebed 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -49,10 +49,6 @@ extern "C" {
 extern const int num_currency_formats_price;
 extern const gchar* currency_format_user_price[];
 
-/* A set of date formats that the user sees. */
-extern const int num_date_formats_price;
-extern const gchar* date_format_user_price[];
-
 /** Tuple to hold
  *  - a tokenized line of input
  *  - an optional error string
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 539f316..1ccd6e5 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -50,116 +50,6 @@ std::map<GncPricePropType, const char*> gnc_price_col_type_strs = {
         { GncPricePropType::TO_CURRENCY, N_("Currency To") },
 };
 
-/* Regular expressions used to parse dates per date format */
-const char* date_regex_price[] = {
-                             "(?:"                                   // 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_price (const std::string &date_str, int format)
-{
-    boost::regex r(date_regex_price[format]);
-    boost::smatch what;
-    if(!boost::regex_search(date_str, what, r))
-        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 (_("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"));
-
-    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);
-    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;
-}
-
-
 /** Convert str into a GncNumeric using the user-specified (import) currency format.
  * @param str The string to be parsed
  * @param currency_format The currency format to use.
@@ -257,7 +147,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
         {
             case GncPricePropType::DATE:
                 m_date = boost::none;
-                m_date = parse_date_price (value, m_date_format); // Throws if parsing fails
+                m_date = GncDate(value, GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails
                 break;
 
             case GncPricePropType::AMOUNT:
@@ -357,7 +247,7 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
     }
 
     Timespec date;
-    timespecFromTime64 (&date, *m_date);
+    timespecFromTime64 (&date, static_cast<time64>(GncDateTime(*m_date, DayPart::neutral)));
     date.tv_nsec = 0;
 
     bool rev = false;
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 9ae4853..77e39dc 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -39,6 +39,7 @@ extern "C" {
 #include <map>
 #include <memory>
 #include <boost/optional.hpp>
+#include <gnc-datetime.hpp>
 #include <gnc-numeric.hpp>
 
 /** Enumeration for column types. These are the different types of
@@ -76,7 +77,6 @@ private:
     const char *m_name;
 };
 
-time64 parse_date_price (const std::string &date_str, int format);
 gnc_commodity* parse_commodity_price_comm (const std::string& comm_str);
 GncNumeric parse_amount_price (const std::string &str, int currency_format);
 
@@ -104,7 +104,7 @@ public:
 private:
     int m_date_format;
     int m_currency_format;
-    boost::optional<time64> m_date;
+    boost::optional<GncDate> m_date;
     boost::optional<GncNumeric> m_amount;
     boost::optional<gnc_commodity*> m_from_commodity;
     boost::optional<gnc_commodity*> m_to_currency;

commit 5b02021550dd71dd3de643a387b4c3034e6758a7
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Mon Dec 4 14:25:02 2017 +0000

    Make changes for Gtk3 compatibility

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 0fa6273..af810cb 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -503,10 +503,12 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
     file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
     g_signal_connect (G_OBJECT(file_chooser), "file-activated",
                       G_CALLBACK(csv_price_imp_file_confirm_cb), this);
-    auto button = gtk_button_new_from_stock (GTK_STOCK_OK);
+    auto button = gtk_button_new_with_label (_("OK"));
     gtk_widget_set_size_request (button, 100, -1);
     gtk_widget_show (button);
-    auto h_box = gtk_hbox_new (TRUE, 0);
+    auto h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+    gtk_box_set_homogeneous (GTK_BOX (h_box), TRUE);
+    gtk_widget_set_hexpand (GTK_WIDGET(h_box), TRUE);
     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",
@@ -792,7 +794,9 @@ CsvImpPriceAssist::preview_settings_name (GtkEntry* entry)
     if (text)
         price_imp->settings_name(text);
 
-    auto combo = gtk_widget_get_parent (GTK_WIDGET(entry));
+    auto box = gtk_widget_get_parent (GTK_WIDGET(entry));
+    auto combo = gtk_widget_get_parent (GTK_WIDGET(box));
+
     preview_handle_save_del_sensitivity (GTK_COMBO_BOX(combo));
 }
 
@@ -1239,11 +1243,11 @@ enum
 static GnumericPopupMenuElement const popup_elements[] =
 {
     {
-        N_("Merge with column on _left"), GTK_STOCK_REMOVE,
+        N_("Merge with column on _left"), "list-remove",
         0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT
     },
     {
-        N_("Merge with column on _right"), GTK_STOCK_REMOVE,
+        N_("Merge with column on _right"), "list-remove",
         0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT
     },
     { "", nullptr, 0, 0, 0 },
@@ -1253,11 +1257,11 @@ static GnumericPopupMenuElement const popup_elements[] =
     },
     { "", nullptr, 0, 0, 0 },
     {
-        N_("_Widen this column"), GTK_STOCK_GO_FORWARD,
+        N_("_Widen this column"), "go-next",
         0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN
     },
     {
-        N_("_Narrow this column"), GTK_STOCK_GO_BACK,
+        N_("_Narrow this column"), "go-previous",
         0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW
     },
     { nullptr, nullptr, 0, 0, 0 },
@@ -1429,7 +1433,7 @@ CsvImpPriceAssist::preview_row_fill_state_cells (GtkListStore *store, GtkTreeIte
         fcolor = "black";
         bcolor = "pink";
         c_err_msg = err_msg.c_str();
-        icon_name = GTK_STOCK_DIALOG_ERROR;
+        icon_name = "dialog-error";
     }
     gtk_list_store_set (store, iter,
             PREV_COL_FCOLOR, fcolor,
@@ -1480,13 +1484,13 @@ void
 CsvImpPriceAssist::preview_style_column (uint32_t 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);
+    auto renderer = static_cast<GtkCellRenderer*>(gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(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,
+                "icon-name", 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,
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
index 33db266..b1ba9c0 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
 <interface>
-  <requires lib="gtk+" version="2.24"/>
-  <!-- interface-naming-policy project-wide -->
+  <requires lib="gtk+" version="3.10"/>
   <object class="GtkAdjustment" id="end_row_adj">
     <property name="upper">1000</property>
     <property name="step_increment">1</property>
@@ -38,11 +38,11 @@
     <property name="title" translatable="yes">CSV Price Import</property>
     <property name="default_width">400</property>
     <property name="default_height">500</property>
+    <signal name="apply" handler="csv_price_imp_assist_finish_cb" swapped="no"/>
+    <signal name="cancel" handler="csv_price_imp_assist_cancel_cb" swapped="no"/>
     <signal name="close" handler="csv_price_imp_assist_close_cb" swapped="no"/>
     <signal name="destroy" handler="csv_price_imp_assist_destroy_cb" swapped="no"/>
-    <signal name="apply" handler="csv_price_imp_assist_finish_cb" swapped="no"/>
     <signal name="prepare" handler="csv_price_imp_assist_prepare_cb" swapped="no"/>
-    <signal name="cancel" handler="csv_price_imp_assist_cancel_cb" swapped="no"/>
     <child>
       <placeholder/>
     </child>
@@ -76,10 +76,11 @@ Click on 'Forward' to proceed or 'Cancel' to Abort Import.</property>
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="file_page">
+      <object class="GtkBox" id="file_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="border_width">12</property>
+        <property name="orientation">vertical</property>
         <child>
           <object class="GtkLabel" id="label7">
             <property name="visible">True</property>
@@ -101,20 +102,19 @@ Select location and file name for the Import, then click 'OK'...
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="preview_page">
+      <object class="GtkBox" id="preview_page">
         <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="border_width">12</property>
+        <property name="orientation">vertical</property>
         <property name="spacing">2</property>
         <child>
-          <object class="GtkTable" id="table1">
+          <object class="GtkGrid" id="table1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="n_rows">2</property>
-            <property name="n_columns">2</property>
-            <property name="column_spacing">5</property>
             <property name="row_spacing">5</property>
+            <property name="column_spacing">5</property>
             <child>
               <object class="GtkFrame" id="frame6">
                 <property name="visible">True</property>
@@ -129,7 +129,7 @@ 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="GtkHBox" id="combo_hbox">
+                      <object class="GtkBox" id="combo_hbox">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <child>
@@ -143,7 +143,7 @@ Select location and file name for the Import, then click 'OK'...
                               <object class="GtkImage" id="image2">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="stock">gtk-delete</property>
+                                <property name="icon_name">edit-delete</property>
                               </object>
                             </child>
                           </object>
@@ -165,7 +165,7 @@ Select location and file name for the Import, then click 'OK'...
                               <object class="GtkImage" id="image1">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="stock">gtk-save</property>
+                                <property name="icon_name">document-save</property>
                               </object>
                             </child>
                           </object>
@@ -184,6 +184,7 @@ Select location and file name for the Import, then click 'OK'...
                   <object class="GtkLabel" id="label12">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="halign">start</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>
@@ -191,8 +192,8 @@ Select location and file name for the Import, then click 'OK'...
                 </child>
               </object>
               <packing>
-                <property name="x_options">GTK_FILL</property>
-                <property name="y_options">GTK_FILL</property>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
               </packing>
             </child>
             <child>
@@ -210,15 +211,14 @@ 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="GtkVBox" id="vbox1">
+                      <object class="GtkBox" id="vbox1">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
                         <child>
-                          <object class="GtkTable" id="table4">
+                          <object class="GtkGrid" id="table4">
                             <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="GtkRadioButton" id="csv_button">
                                 <property name="label" translatable="yes">Separators</property>
@@ -226,10 +226,15 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="active">True</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_fixed_sel_cb" swapped="no"/>
                               </object>
+                              <packing>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
+                              </packing>
                             </child>
                             <child>
                               <object class="GtkRadioButton" id="fixed_button">
@@ -238,24 +243,24 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</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>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
-                              <object class="GtkHSeparator" id="hseparator1">
+                              <object class="GtkSeparator" id="hseparator1">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                               </object>
                               <packing>
-                                <property name="right_attach">2</property>
+                                <property name="left_attach">0</property>
                                 <property name="top_attach">1</property>
-                                <property name="bottom_attach">2</property>
-                                <property name="y_options">GTK_FILL</property>
+                                <property name="width">2</property>
                               </packing>
                             </child>
                           </object>
@@ -266,12 +271,10 @@ Select location and file name for the Import, then click 'OK'...
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkTable" id="separator_table">
+                          <object class="GtkGrid" id="separator_table">
                             <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>
                             <child>
                               <object class="GtkCheckButton" id="space_cbutton">
@@ -280,12 +283,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
-                                <property name="x_options">GTK_FILL</property>
-                                <property name="y_options">GTK_FILL</property>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -295,14 +299,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_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>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -312,15 +315,14 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="active">True</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_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>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -330,14 +332,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
+                                <property name="left_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>
@@ -347,16 +348,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
-                                <property name="left_attach">1</property>
-                                <property name="right_attach">2</property>
+                                <property name="left_attach">0</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>
@@ -366,16 +364,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
-                                <property name="left_attach">2</property>
-                                <property name="right_attach">3</property>
+                                <property name="left_attach">1</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>
@@ -385,14 +380,13 @@ Select location and file name for the Import, then click 'OK'...
                                 <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="halign">start</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_sep_button_cb" swapped="no"/>
                               </object>
                               <packing>
+                                <property name="left_attach">0</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>
@@ -401,22 +395,18 @@ Select location and file name for the Import, then click 'OK'...
                                 <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_price_imp_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>
+                            <child>
+                              <placeholder/>
+                            </child>
                           </object>
                           <packing>
                             <property name="expand">False</property>
@@ -425,7 +415,7 @@ Select location and file name for the Import, then click 'OK'...
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkHBox" id="fw_instructions_hbox">
+                          <object class="GtkBox" id="fw_instructions_hbox">
                             <property name="can_focus">False</property>
                             <property name="no_show_all">True</property>
                             <child>
@@ -433,8 +423,7 @@ Select location and file name for the Import, then click 'OK'...
                                 <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>
+                                <property name="icon_name">dialog-information</property>
                               </object>
                               <packing>
                                 <property name="expand">False</property>
@@ -444,67 +433,61 @@ Select location and file name for the Import, then click 'OK'...
                               </packing>
                             </child>
                             <child>
-                              <object class="GtkTable" id="table2">
+                              <object class="GtkGrid" 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>
+                                  <packing>
+                                    <property name="left_attach">0</property>
+                                    <property name="top_attach">0</property>
+                                  </packing>
                                 </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="halign">start</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"/>
+                                    <property name="top_attach">0</property>
                                   </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="left_attach">0</property>
                                     <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="halign">start</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>
@@ -522,17 +505,17 @@ Select location and file name for the Import, then click 'OK'...
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkTable" id="table5">
+                          <object class="GtkGrid" id="table5">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="n_rows">2</property>
                             <child>
-                              <object class="GtkHSeparator" id="hseparator4">
+                              <object class="GtkSeparator" id="hseparator4">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                               </object>
                               <packing>
-                                <property name="y_options">GTK_FILL</property>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -540,15 +523,15 @@ Select location and file name for the Import, then click 'OK'...
                                 <property name="label" translatable="yes">Allow existing prices to be over written.</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
+                                <property name="focus_on_click">False</property>
                                 <property name="receives_default">False</property>
                                 <property name="tooltip_text" translatable="yes">Normally prices are not over written, select this to change that. This setting is not saved.</property>
-                                <property name="focus_on_click">False</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_overwrite_cb" swapped="no"/>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <property name="top_attach">1</property>
-                                <property name="bottom_attach">2</property>
                               </packing>
                             </child>
                           </object>
@@ -567,16 +550,15 @@ Select location and file name for the Import, then click 'OK'...
                     <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="halign">start</property>
                     <property name="label" translatable="yes"><b>File Format</b></property>
                     <property name="use_markup">True</property>
                   </object>
                 </child>
               </object>
               <packing>
+                <property name="left_attach">0</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>
@@ -592,18 +574,17 @@ 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="GtkVBox" id="vbox6">
+                      <object class="GtkBox" 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>
+                        <property name="orientation">vertical</property>
                         <child>
-                          <object class="GtkTable" id="table3">
+                          <object class="GtkGrid" id="table3">
                             <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>
+                            <property name="column_spacing">5</property>
                             <child>
                               <object class="GtkAlignment" id="date_format_container">
                                 <property name="visible">True</property>
@@ -613,23 +594,19 @@ Select location and file name for the Import, then click 'OK'...
                               </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="halign">start</property>
                                 <property name="label" translatable="yes">Date Format</property>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <property name="top_attach">1</property>
-                                <property name="bottom_attach">2</property>
-                                <property name="x_options">GTK_SHRINK | GTK_FILL</property>
                               </packing>
                             </child>
                             <child>
@@ -640,34 +617,31 @@ Select location and file name for the Import, then click 'OK'...
                               </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="halign">start</property>
                                 <property name="label" translatable="yes">Currency Format</property>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <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="halign">start</property>
                                 <property name="label" translatable="yes">Encoding</property>
                               </object>
                               <packing>
-                                <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
                               </packing>
                             </child>
                             <child>
@@ -678,38 +652,35 @@ Select location and file name for the Import, then click 'OK'...
                               </object>
                               <packing>
                                 <property name="left_attach">1</property>
-                                <property name="right_attach">2</property>
-                                <property name="x_options">GTK_FILL</property>
+                                <property name="top_attach">0</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="halign">start</property>
                                 <property name="label" translatable="yes">Leading Lines to Skip</property>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <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="halign">start</property>
                                 <property name="label" translatable="yes">Trailing Lines to Skip</property>
                               </object>
                               <packing>
+                                <property name="left_attach">0</property>
                                 <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">
+                              <object class="GtkBox" id="hbox2">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <child>
@@ -717,11 +688,8 @@ Select location and file name for the Import, then click 'OK'...
                                     <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_price_imp_preview_srow_cb" swapped="no"/>
@@ -735,13 +703,11 @@ Select location and file name for the Import, then click 'OK'...
                               </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">
+                              <object class="GtkBox" id="hbox3">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                                 <child>
@@ -749,11 +715,8 @@ Select location and file name for the Import, then click 'OK'...
                                     <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_price_imp_preview_erow_cb" swapped="no"/>
@@ -767,20 +730,18 @@ Select location and file name for the Import, then click 'OK'...
                               </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">
+                              <object class="GtkSeparator" id="hseparator2">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
                               </object>
                               <packing>
-                                <property name="right_attach">2</property>
+                                <property name="left_attach">0</property>
                                 <property name="top_attach">3</property>
-                                <property name="bottom_attach">4</property>
+                                <property name="width">2</property>
                               </packing>
                             </child>
                           </object>
@@ -800,6 +761,7 @@ Select location and file name for the Import, then click 'OK'...
 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="halign">start</property>
                             <property name="draw_indicator">True</property>
                             <signal name="toggled" handler="csv_price_imp_preview_skiprows_cb" swapped="no"/>
                           </object>
@@ -817,6 +779,7 @@ For example
                   <object class="GtkLabel" id="label13">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="halign">start</property>
                     <property name="label" translatable="yes"><b>Miscellaneous</b></property>
                     <property name="use_markup">True</property>
                   </object>
@@ -824,11 +787,7 @@ For example
               </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>
@@ -842,7 +801,7 @@ For example
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox1">
+          <object class="GtkBox" id="hbox1">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
@@ -859,7 +818,7 @@ For example
                     <property name="left_padding">5</property>
                     <property name="right_padding">5</property>
                     <child>
-                      <object class="GtkHBox" id="commodity_hbox">
+                      <object class="GtkBox" id="commodity_hbox">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <child>
@@ -913,7 +872,7 @@ For example
                     <property name="left_padding">5</property>
                     <property name="right_padding">5</property>
                     <child>
-                      <object class="GtkHBox" id="currency_hbox">
+                      <object class="GtkBox" id="currency_hbox">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <child>
@@ -964,16 +923,15 @@ For example
           <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="viewport2">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <child>
-                  <object class="GtkVBox" id="vbox8">
+                  <object class="GtkBox" id="vbox8">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="orientation">vertical</property>
                     <child>
                       <object class="GtkTreeView" id="ctreeview">
                         <property name="visible">True</property>
@@ -981,6 +939,9 @@ For example
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                         <property name="headers_visible">False</property>
                         <property name="enable_grid_lines">both</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection"/>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">False</property>
@@ -994,6 +955,9 @@ For example
                         <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="enable_grid_lines">both</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection"/>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">True</property>
@@ -1013,7 +977,7 @@ For example
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox13">
+          <object class="GtkBox" 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>
@@ -1022,8 +986,7 @@ For example
                 <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>
+                <property name="icon_name">dialog-information</property>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -1037,7 +1000,7 @@ For example
                 <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="halign">start</property>
                 <property name="label" translatable="yes">Select the type of each column to import.</property>
               </object>
               <packing>
@@ -1055,7 +1018,7 @@ For example
           </packing>
         </child>
         <child>
-          <object class="GtkHBox" id="hbox14">
+          <object class="GtkBox" id="hbox14">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
@@ -1063,7 +1026,6 @@ For example
                 <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>
                 <property name="image_position">right</property>
                 <property name="draw_indicator">True</property>
                 <signal name="toggled" handler="csv_price_imp_preview_skiperrors_cb" swapped="no"/>
@@ -1089,10 +1051,11 @@ For example
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="confirm_page">
+      <object class="GtkBox" id="confirm_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="border_width">12</property>
+        <property name="orientation">vertical</property>
         <child>
           <object class="GtkAlignment" id="alignment2">
             <property name="visible">True</property>
@@ -1122,10 +1085,11 @@ Cancel to abort.</b></property>
       </packing>
     </child>
     <child>
-      <object class="GtkVBox" id="summary_page">
+      <object class="GtkBox" id="summary_page">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="border_width">12</property>
+        <property name="orientation">vertical</property>
         <child>
           <object class="GtkLabel" id="summary_label">
             <property name="visible">True</property>
@@ -1147,5 +1111,12 @@ Cancel to abort.</b></property>
         <property name="complete">True</property>
       </packing>
     </child>
+    <child internal-child="action_area">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+      </object>
+      <packing>
+      </packing>
+    </child>
   </object>
 </interface>
diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
index c14092d..3cecf66 100644
--- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
+++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
@@ -57,7 +57,7 @@ static GtkActionEntry gnc_plugin_actions [] =
         G_CALLBACK (gnc_plugin_csv_import_trans_cmd)
     },
     {
-        "CsvImportPriceAction", GTK_STOCK_CONVERT, N_("Import _Prices from a CSV file..."), NULL,
+        "CsvImportPriceAction", "go-previous", N_("Import _Prices from a CSV file..."), NULL,
         N_("Import Prices from a CSV file"),
         G_CALLBACK (gnc_plugin_csv_import_price_cmd)
     },

commit b94b2f8ac25926f5afdc6988f952b52b998d31a8
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 12:04:51 2017 +0000

    Pot file changes for new files and settings rename

diff --git a/po/POTFILES.in b/po/POTFILES.in
index bced61f..7f74af4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -287,17 +287,21 @@ gnucash/import-export/csv-exp/gnc-plugin-csv-export.c
 [type: gettext/gsettings]gnucash/import-export/csv-exp/gschemas/org.gnucash.dialogs.export.csv.gschema.xml.in.in
 gnucash/import-export/csv-imp/assistant-csv-account-import.c
 gnucash/import-export/csv-imp/assistant-csv-account-import.glade
+gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+gnucash/import-export/csv-imp/assistant-csv-price-import.glade
 gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
 gnucash/import-export/csv-imp/assistant-csv-trans-import.glade
 gnucash/import-export/csv-imp/csv-account-import.c
 gnucash/import-export/csv-imp/gnc-csv-account-map.c
 gnucash/import-export/csv-imp/gnc-csv-gnumeric-popup.c
 gnucash/import-export/csv-imp/gnc-csv-tokenizer.cpp
-gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
 gnucash/import-export/csv-imp/gnc-dummy-tokenizer.cpp
 gnucash/import-export/csv-imp/gnc-fw-tokenizer.cpp
 gnucash/import-export/csv-imp/gncmod-csv-import.c
 gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
+gnucash/import-export/csv-imp/gnc-price-import.cpp
+gnucash/import-export/csv-imp/gnc-price-props.cpp
 gnucash/import-export/csv-imp/gnc-tokenizer.cpp
 gnucash/import-export/csv-imp/gnc-trans-props.cpp
 gnucash/import-export/csv-imp/gnc-tx-import.cpp

commit c3b54ab05493a04621d1068dce57cc92daff5645
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 12:03:14 2017 +0000

    Rename gnc-csv-trans-settings.* to gnc-csv-import-settings.*
    
    Change the name of the import settings files as they do not just deal
    with transactions and all associated required changes.

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index 44a14ca..65082be 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -17,7 +17,7 @@ SET(csv_import_SOURCES
   gnc-csv-account-map.c
   gnc-csv-gnumeric-popup.c
   gnc-csv-tokenizer.cpp
-  gnc-csv-trans-settings.cpp
+  gnc-csv-import-settings.cpp
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
   gnc-price-import.cpp
@@ -45,7 +45,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-account-map.h
   gnc-csv-gnumeric-popup.h
   gnc-csv-tokenizer.hpp
-  gnc-csv-trans-settings.hpp
+  gnc-csv-import-settings.hpp
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
   gnc-price-import.hpp
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index 4b473de..f1b501f 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -19,7 +19,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
   gnc-trans-props.cpp \
-  gnc-csv-trans-settings.cpp
+  gnc-csv-import-settings.cpp
 
 noinst_HEADERS = \
   assistant-csv-account-import.h \
@@ -37,7 +37,7 @@ noinst_HEADERS = \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
   gnc-trans-props.hpp \
-  gnc-csv-trans-settings.hpp
+  gnc-csv-import-settings.hpp
 
 libgncmod_csv_import_la_LDFLAGS = -avoid-version
 
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 21126ea..0fa6273 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -52,7 +52,7 @@ extern "C"
 #include "go-charmap-sel.h"
 }
 
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include "gnc-price-import.hpp"
 #include "gnc-fw-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -738,7 +738,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo()
     gtk_list_store_clear (GTK_LIST_STORE(model));
 
     // Append the default entry
-    auto presets = get_trans_presets (settings_type);
+    auto presets = get_import_presets (settings_type);
     for (auto preset : presets)
     {
         GtkTreeIter iter;
@@ -766,11 +766,11 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
     /* Handle sensitivity of the delete and save button */
     if (gtk_combo_box_get_active_iter (combo, &iter))
     {
-        CsvTransSettings *preset;
+        CsvImportSettings *preset;
         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->m_name))
+        if (preset && !preset_is_reserved_name (preset->m_name))
         {
             /* Current preset is not read_only, so buttons can be enabled */
             can_delete = true;
@@ -778,7 +778,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
         }
     }
     else if (entry_text && (strlen (entry_text) > 0) &&
-            !trans_preset_is_reserved_name (std::string(entry_text)))
+            !preset_is_reserved_name (std::string(entry_text)))
         can_save = true;
 
     gtk_widget_set_sensitive (save_button, can_save);
@@ -807,7 +807,7 @@ CsvImpPriceAssist::preview_settings_load ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvTransSettings *preset = nullptr;
+    CsvImportSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -834,7 +834,7 @@ CsvImpPriceAssist::preview_settings_delete ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvTransSettings *preset = nullptr;
+    CsvImportSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -868,7 +868,7 @@ CsvImpPriceAssist::preview_settings_save ()
         while (valid)
         {
             // Walk through the list, reading each row
-            CsvTransSettings *preset;
+            CsvImportSettings *preset;
             gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
             if (preset && (preset->m_name == std::string(new_name)))
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index e3d10cc..3512aba 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -58,7 +58,7 @@ extern "C"
 #include "go-charmap-sel.h"
 }
 
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include "gnc-tx-import.hpp"
 #include "gnc-fw-tokenizer.hpp"
 #include "gnc-csv-tokenizer.hpp"
@@ -679,7 +679,7 @@ void CsvImpTransAssist::preview_populate_settings_combo()
 
     // Append the default entry
 
-    auto presets = get_trans_presets (settings_type);
+    auto presets = get_import_presets (settings_type);
     for (auto preset : presets)
     {
         GtkTreeIter iter;
@@ -707,11 +707,11 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
     /* Handle sensitivity of the delete and save button */
     if (gtk_combo_box_get_active_iter (combo, &iter))
     {
-        CsvTransSettings *preset;
+        CsvImportSettings *preset;
         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->m_name))
+        if (preset && !preset_is_reserved_name (preset->m_name))
         {
             /* Current preset is not read_only, so buttons can be enabled */
             can_delete = true;
@@ -719,7 +719,7 @@ void CsvImpTransAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
         }
     }
     else if (entry_text && (strlen (entry_text) > 0) &&
-            !trans_preset_is_reserved_name (std::string(entry_text)))
+            !preset_is_reserved_name (std::string(entry_text)))
         can_save = true;
 
     gtk_widget_set_sensitive (save_button, can_save);
@@ -752,7 +752,7 @@ CsvImpTransAssist::preview_settings_load ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvTransSettings *preset = nullptr;
+    CsvImportSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -779,7 +779,7 @@ CsvImpTransAssist::preview_settings_delete ()
     if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
         return;
 
-    CsvTransSettings *preset = nullptr;
+    CsvImportSettings *preset = nullptr;
     auto model = gtk_combo_box_get_model (settings_combo);
     gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
@@ -812,7 +812,7 @@ CsvImpTransAssist::preview_settings_save ()
         while (valid)
         {
             // Walk through the list, reading each row
-            CsvTransSettings *preset;
+            CsvImportSettings *preset;
             gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
 
             if (preset && (preset->m_name == std::string(new_name)))
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
similarity index 95%
rename from gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
rename to gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
index 3bbc984..0096c53 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.cpp
@@ -1,5 +1,5 @@
 /*******************************************************************\
- * gnc-csv-trans-settings.c -- Save and Load CSV Import Settings    *
+ * gnc-csv-import-settings.c -- Save and Load CSV Import Settings   *
  *                                                                  *
  * Copyright (C) 2014 Robert Fewell                                 *
  *                                                                  *
@@ -20,13 +20,13 @@
  * 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
+/** @file gnc-csv-import-settings.c
     @brief CSV Import Settings
     @author Copyright (c) 2014 Robert Fewell
     @author Copyright (c) 2016 Geert Janssens
 */
 
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include <sstream>
 
 extern "C"
@@ -70,18 +70,18 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
 preset_vec presets;
 
-static std::shared_ptr<CsvTransSettings> create_int_no_preset(const std::string& set_type)
+static std::shared_ptr<CsvImportSettings> create_int_no_preset(const std::string& set_type)
 {
-    auto preset = std::make_shared<CsvTransSettings>();
+    auto preset = std::make_shared<CsvImportSettings>();
     preset->m_name = no_settings;
     preset->m_settings_type = set_type;
 
     return preset;
 }
 
-static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
+static std::shared_ptr<CsvImportSettings> create_int_gnc_exp_preset(void)
 {
-    auto preset = std::make_shared<CsvTransSettings>();
+    auto preset = std::make_shared<CsvImportSettings>();
     preset->m_name = gnc_exp;
     preset->m_skip_start_lines = 1;
     preset->m_multi_split = true;
@@ -119,7 +119,7 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
  * find all settings entries in the state key file
  * based on settings type.
  **************************************************/
-const preset_vec& get_trans_presets (const std::string& set_type)
+const preset_vec& get_import_presets (const std::string& set_type)
 {
 
     // Search all Groups in the state key file for ones starting with prefix
@@ -157,7 +157,7 @@ const preset_vec& get_trans_presets (const std::string& set_type)
     /* Then add all the ones we found in the state file */
     for (auto preset_name : preset_names)
     {
-        auto preset = std::make_shared<CsvTransSettings>();
+        auto preset = std::make_shared<CsvImportSettings>();
         preset->m_settings_type = set_type;
         preset->m_name = preset_name;
         preset->load();
@@ -166,7 +166,7 @@ const preset_vec& get_trans_presets (const std::string& set_type)
     return presets;
 }
 
-bool trans_preset_is_reserved_name (const std::string& name)
+bool preset_is_reserved_name (const std::string& name)
 {
     return ((name == no_settings) ||
             (name == _(no_settings.c_str())) ||
@@ -205,9 +205,9 @@ handle_load_error (GError **key_error, const std::string& group)
  * load the settings from a state key file
  **************************************************/
 bool
-CsvTransSettings::load (void)
+CsvImportSettings::load (void)
 {
-    if (trans_preset_is_reserved_name (m_name))
+    if (preset_is_reserved_name (m_name))
         return true;
 
     GError *key_error = nullptr;
@@ -355,9 +355,9 @@ CsvTransSettings::load (void)
  * save settings to a key file
  **************************************************/
 bool
-CsvTransSettings::save (void)
+CsvImportSettings::save (void)
 {
-    if (trans_preset_is_reserved_name (m_name))
+    if (preset_is_reserved_name (m_name))
     {
         PWARN ("Ignoring attempt to save to reserved name '%s'", m_name.c_str());
         return true;
@@ -471,9 +471,9 @@ CsvTransSettings::save (void)
 }
 
 void
-CsvTransSettings::remove (void)
+CsvImportSettings::remove (void)
 {
-    if (trans_preset_is_reserved_name (m_name))
+    if (preset_is_reserved_name (m_name))
         return;
 
     auto keyfile = gnc_state_get_current ();
@@ -483,7 +483,7 @@ CsvTransSettings::remove (void)
 
 
 bool
-CsvTransSettings::read_only (void)
+CsvImportSettings::read_only (void)
 {
     return ((m_name == no_settings) ||
             (m_name == _(no_settings.c_str())) ||
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
similarity index 90%
rename from gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
rename to gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
index 7df293c..e6ce4d4 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-import-settings.hpp
@@ -1,5 +1,5 @@
 /*******************************************************************\
- * gnc-csv-trans-settings.h   -- Save and Load CSV Import Settings  *
+ * gnc-csv-import-settings.h  -- Save and Load CSV Import Settings  *
  *                                                                  *
  * Copyright (C) 2014 Robert Fewell                                 *
  *                                                                  *
@@ -20,13 +20,13 @@
  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
-/** @file gnc-csv-trans-settings.h
+/** @file gnc-csv-import-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
+#ifndef GNC_CSV_IMPORT_SETTINGS_H
+#define GNC_CSV_IMPORT_SETTINGS_H
 
 extern "C" {
 #include <config.h>
@@ -49,9 +49,9 @@ 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};
 
-struct CsvTransSettings
+struct CsvImportSettings
 {
-    CsvTransSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"},
+    CsvImportSettings() : 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_load_error {false}, m_base_account {nullptr},
@@ -107,8 +107,8 @@ gnc_commodity *m_to_currency;                 //  Price To Currency
 std::vector<GncPricePropType> m_column_types_price; // The Price Column types in order
 };
 
-using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
-/** Creates a vector of CsvTransSettings which combines
+using preset_vec = std::vector<std::shared_ptr<CsvImportSettings>>;
+/** Creates a vector of CsvImportSettings which combines
  *  - one or more internally defined presets
  *  - all preset found in the state key file.
  *
@@ -117,12 +117,12 @@ using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
  *
  *  @return a reference to the populated vector.
  */
-const preset_vec& get_trans_presets (const std::string& set_type);
+const preset_vec& get_import_presets (const std::string& set_type);
 
 /** 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);
+bool preset_is_reserved_name (const std::string& name);
 
 #endif
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 44a17b9..838c947 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -42,7 +42,7 @@ extern "C" {
 #include "gnc-price-props.hpp"
 #include "gnc-csv-tokenizer.hpp"
 #include "gnc-fw-tokenizer.hpp"
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -281,7 +281,7 @@ void GncPriceImport::separators (std::string separators)
 }
 std::string GncPriceImport::separators () { return m_settings.m_separators; }
 
-void GncPriceImport::settings (const CsvTransSettings& settings)
+void GncPriceImport::settings (const CsvImportSettings& settings)
 {
     /* First apply file format as this may recreate the tokenizer */
     file_format (settings.m_file_format);
@@ -315,7 +315,7 @@ void GncPriceImport::settings (const CsvTransSettings& settings)
 
 bool GncPriceImport::save_settings ()
 {
-    if (trans_preset_is_reserved_name (m_settings.m_name))
+    if (preset_is_reserved_name (m_settings.m_name))
         return true;
 
     /* separators are already copied to m_settings in the separators
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index be91941..9959aaa 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -42,7 +42,7 @@ extern "C" {
 
 #include "gnc-tokenizer.hpp"
 #include "gnc-price-props.hpp"
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include <boost/optional.hpp>
 
 /* A set of currency formats that the user sees. */
@@ -110,7 +110,7 @@ public:
     void separators (std::string separators);
     std::string separators ();
 
-    void settings (const CsvTransSettings& settings);
+    void settings (const CsvImportSettings& settings);
     bool save_settings ();
 
     void settings_name (std::string name);
@@ -157,7 +157,7 @@ private:
     void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type);
 
     struct CsvTranSettings;
-    CsvTransSettings m_settings;
+    CsvImportSettings m_settings;
     bool m_skip_errors;
     bool m_over_write;
 };
diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.cpp b/gnucash/import-export/csv-imp/gnc-tx-import.cpp
index daa85b4..eddbd75 100644
--- a/gnucash/import-export/csv-imp/gnc-tx-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-tx-import.cpp
@@ -39,7 +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"
+#include "gnc-csv-import-settings.hpp"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -293,7 +293,7 @@ void GncTxImport::separators (std::string separators)
 }
 std::string GncTxImport::separators () { return m_settings.m_separators; }
 
-void GncTxImport::settings (const CsvTransSettings& settings)
+void GncTxImport::settings (const CsvImportSettings& settings)
 {
     /* First apply file format as this may recreate the tokenizer */
     file_format (settings.m_file_format);
@@ -329,7 +329,7 @@ void GncTxImport::settings (const CsvTransSettings& settings)
 bool GncTxImport::save_settings ()
 {
 
-    if (trans_preset_is_reserved_name (m_settings.m_name))
+    if (preset_is_reserved_name (m_settings.m_name))
         return true;
 
     /* separators are already copied to m_settings in the separators
diff --git a/gnucash/import-export/csv-imp/gnc-tx-import.hpp b/gnucash/import-export/csv-imp/gnc-tx-import.hpp
index 0c14a74..87e4ea4 100644
--- a/gnucash/import-export/csv-imp/gnc-tx-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-tx-import.hpp
@@ -43,7 +43,7 @@ extern "C" {
 
 #include "gnc-tokenizer.hpp"
 #include "gnc-trans-props.hpp"
-#include "gnc-csv-trans-settings.hpp"
+#include "gnc-csv-import-settings.hpp"
 #include <boost/optional.hpp>
 
 
@@ -136,7 +136,7 @@ public:
     void separators (std::string separators);
     std::string separators ();
 
-    void settings (const CsvTransSettings& settings);
+    void settings (const CsvImportSettings& settings);
     bool save_settings ();
 
     void settings_name (std::string name);
@@ -190,7 +190,7 @@ private:
     void update_pre_split_props (uint32_t row, uint32_t col, GncTransPropType prop_type);
 
     struct CsvTranSettings;
-    CsvTransSettings m_settings;
+    CsvImportSettings m_settings;
     bool m_skip_errors;
     bool m_req_mapped_accts;
 

commit c1a9464511b4bc0587a9038a214be3b8390da49f
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:56:25 2017 +0000

    Reorder the create price procedure.

diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 0ea21f9..539f316 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -362,9 +362,20 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
 
     bool rev = false;
     auto amount = *m_amount;
+    Result ret_val = ADDED;
 
     GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date);
 
+    // Should old price be over writen
+    if ((old_price != nullptr) && (over == true))
+    {
+        DEBUG("Over write");
+        gnc_pricedb_remove_price (pdb, old_price);
+        gnc_price_unref (old_price);
+        old_price = nullptr;
+        ret_val = REPLACED;
+    }
+
     if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import
     {
         // Check for currency in reverse direction.
@@ -373,9 +384,8 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
             // Check for price in reverse direction.
             if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity))
                 rev = true;
-
-            DEBUG("Commodity from is a Currency");
         }
+        DEBUG("Commodity from is a Currency");
 
         // Check for price less than 1, reverse if so.
         if (*m_amount < GncNumeric(1,1))
@@ -386,18 +396,6 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
         rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency),
         amount.to_string().c_str());
 
-    Result ret_val = ADDED;
-
-    // Should old price be over writen
-    if ((old_price != nullptr) && (over == true))
-    {
-        DEBUG("Over write");
-        gnc_pricedb_remove_price (pdb, old_price);
-        gnc_price_unref (old_price);
-        old_price = nullptr;
-        ret_val = REPLACED;
-    }
-
     // Create the new price
     if (old_price == nullptr)
     {

commit 6c11cfad852efe67aa8863685e15b9db1079036a
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:52:48 2017 +0000

    Add the ability to test from_commodity and to_currency being the same.
    
    To cover all combinations we need to test across the combo's and also
    the table entries when appropriate columns are set. Also need to force
    a reparse if any of the options change.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 14a8ffa..21126ea 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -94,6 +94,7 @@ public:
     void preview_update_currency_format ();
     void preview_update_currency ();
     void preview_update_commodity ();
+    void preview_reparse_col_type (GncPricePropType type);
     void preview_update_col_type (GtkComboBox* cbox);
     void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
 
@@ -1138,6 +1139,22 @@ enum PreviewDataTableCols {
     PREV_COL_ERR_ICON,
     PREV_N_FIXED_COLS };
 
+
+void
+CsvImpPriceAssist::preview_reparse_col_type (GncPricePropType type)
+{
+    auto column_types = price_imp->column_types_price();
+
+    // look for column type and force a reparse
+    auto col_type = std::find (column_types.begin(),
+                column_types.end(), type);
+    if (col_type != column_types.end())
+    {
+        price_imp->set_column_type_price (col_type -column_types.begin(),
+                        type, true);
+    }
+}
+
 /** 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 the selection, and any other columns containing that selection
@@ -1157,8 +1174,26 @@ void CsvImpPriceAssist::preview_update_col_type (GtkComboBox* cbox)
     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"));
+
+    auto column_types = price_imp->column_types_price();
+    auto old_col_type = column_types.at(col_num);
+
     price_imp->set_column_type_price (col_num, new_col_type);
 
+    // if old_col_type is TO_CURRENCY, force a reparse of commodity
+    if (old_col_type == GncPricePropType::TO_CURRENCY)
+    {
+        // look for a from_commodity column to reparse
+        preview_reparse_col_type (GncPricePropType::FROM_COMMODITY);
+    }
+
+    // if old_col_type is FROM_COMMODITY, force a reparse of currency
+    if (old_col_type == GncPricePropType::FROM_COMMODITY)
+    {
+        // look for a to_currency column to reparse
+        preview_reparse_col_type (GncPricePropType::TO_CURRENCY);
+    }
+
     /* Delay rebuilding our data table to avoid critical warnings due to
      * pending events still acting on them after this event is processed.
      */
@@ -1580,7 +1615,7 @@ void CsvImpPriceAssist::preview_refresh_table ()
 
     auto column_types = price_imp->column_types_price();
 
-    // look for a commodity column, clear the commdoity combo
+    // look for a commodity column, clear the commodity combo
     auto col_type_comm = std::find (column_types.begin(),
                 column_types.end(), GncPricePropType::FROM_COMMODITY);
     if (col_type_comm != column_types.end())
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index d5d3862..44a17b9 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -136,7 +136,6 @@ void GncPriceImport::over_write (bool over)
 {
     m_over_write = over;
 }
-
 bool GncPriceImport::over_write () { return m_over_write; }
 
 /** Sets a from commodity. This is the commodity all import data relates to.
@@ -147,18 +146,20 @@ bool GncPriceImport::over_write () { return m_over_write; }
 void GncPriceImport::from_commodity (gnc_commodity* from_commodity)
 {
     m_settings.m_from_commodity = from_commodity;
-
     if (m_settings.m_from_commodity)
     {
-        auto col_type = std::find (m_settings.m_column_types_price.begin(),
+        auto col_type_comm = std::find (m_settings.m_column_types_price.begin(),
                 m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY);
 
-        if (col_type != m_settings.m_column_types_price.end())
-            set_column_type_price (col_type -m_settings.m_column_types_price.begin(),
+        if (col_type_comm != m_settings.m_column_types_price.end())
+            set_column_type_price (col_type_comm -m_settings.m_column_types_price.begin(),
                             GncPricePropType::NONE);
+
+        // force a refresh of the to_currency if the from_commodity is changed
+        std::vector<GncPricePropType> commodities = { GncPricePropType::TO_CURRENCY };
+        reset_formatted_column (commodities);
     }
 }
-
 gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; }
 
 /** Sets a to currency. This is the to currency all import data relates to.
@@ -169,18 +170,20 @@ gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_comm
 void GncPriceImport::to_currency (gnc_commodity* to_currency)
 {
     m_settings.m_to_currency = to_currency;
-
     if (m_settings.m_to_currency)
     {
-        auto col_type = std::find (m_settings.m_column_types_price.begin(),
+        auto col_type_currency = std::find (m_settings.m_column_types_price.begin(),
                 m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY);
 
-        if (col_type != m_settings.m_column_types_price.end())
-            set_column_type_price (col_type -m_settings.m_column_types_price.begin(),
+        if (col_type_currency != m_settings.m_column_types_price.end())
+            set_column_type_price (col_type_currency -m_settings.m_column_types_price.begin(),
                             GncPricePropType::NONE);
+
+        // force a refresh of the from_commodity if the to_currency is changed
+        std::vector<GncPricePropType> commodities = { GncPricePropType::FROM_COMMODITY };
+        reset_formatted_column (commodities);
     }
 }
-
 gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; }
 
 void GncPriceImport::reset_formatted_column (std::vector<GncPricePropType>& col_types)
@@ -453,6 +456,14 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
         if (!m_settings.m_from_commodity)
             error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field."));
     }
+
+    /* Verify a 'Commodity from' does not equal 'Currency to'.
+     */
+    if ((m_settings.m_to_currency) && (m_settings.m_from_commodity))
+    {
+        if (gnc_commodity_equal (m_settings.m_to_currency, m_settings.m_from_commodity))
+            error_msg.add_error( _("'Commodity From' can not be the same as 'Currency To'."));
+    }
 }
 
 /* Check whether the chosen settings can successfully parse
@@ -539,7 +550,7 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
 
     error_message.clear();
 
-    // Add a CURRENCY_TO property with the selected 'currency to' if no 'currency to' column was set by the user
+    // Add a TO_CURRENCY property with the selected 'currency to' if no 'currency to' column was set by the user
     auto line_to_currency = price_props->get_to_currency();
     if (!line_to_currency)
     {
@@ -556,7 +567,7 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
         }
     }
 
-    // Add a COMMODITY_FROM property with the selected 'commodity from' if no 'commodity from' column was set by the user
+    // Add a FROM_COMMODITY property with the selected 'commodity from' if no 'commodity from' column was set by the user
     auto line_from_commodity = price_props->get_from_commodity();
     if (!line_from_commodity)
     {
@@ -657,6 +668,18 @@ void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePro
     {
         try
         {
+            // set the from_commodity based on combo so we can test for same.
+            if (prop_type == GncPricePropType::TO_CURRENCY)
+            {
+                if (m_settings.m_from_commodity)
+                    price_props->set_from_commodity (m_settings.m_from_commodity);
+            }
+            // set the to_currency based on combo so we can test for same.
+            if (prop_type == GncPricePropType::FROM_COMMODITY)
+            {
+                if (m_settings.m_to_currency)
+                    price_props->set_to_currency (m_settings.m_to_currency);
+            }
             price_props->set(prop_type, value);
         }
         catch (const std::exception& e)

commit b1becf3dd4548f6244ce21bf62a1dcaf34b75518
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:51:07 2017 +0000

    Add a test for from_commodity not being the same as to_currency

diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 2deffb3..0ea21f9 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -269,7 +269,11 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
                 m_from_commodity = boost::none;
                 comm = parse_commodity_price_comm (value); // Throws if parsing fails
                 if (comm)
+                {
+                    if (m_to_currency == comm)
+                        throw std::invalid_argument (_("'Commodity From' can not be the same as 'Currency To' column type."));
                     m_from_commodity = comm;
+                }
                 break;
 
             case GncPricePropType::TO_CURRENCY:
@@ -277,6 +281,8 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
                 comm = parse_commodity_price_comm (value); // Throws if parsing fails
                 if (comm)
                 {
+                    if (m_from_commodity == comm)
+                        throw std::invalid_argument (_("'Currency To' can not be the same as 'Commodity From' column type."));
                     if (gnc_commodity_is_currency (comm) != true)
                         throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type."));
                     m_to_currency = comm;
@@ -332,6 +338,8 @@ std::string GncImportPrice::verify_essentials (void)
         return _("No 'Currency to' column.");
     else if (m_from_commodity == boost::none)
         return _("No 'Commodity from' column.");
+    else if (gnc_commodity_equal (*m_from_commodity, *m_to_currency))
+        return _("'Commodity from' can not be the same as 'Currency to'.");
     else
         return std::string();
 }

commit 339fbaa587f49c91ed329b0f3a558a4c9ecc2bbb
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:49:28 2017 +0000

    Various changes to comments in source files and displayed text.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 075f463..14a8ffa 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -434,7 +434,7 @@ GtkTreeModel *get_model (bool all_commodity)
         /* Hide the template entry */
         if (g_utf8_collate (tmp_namespace, "template" ) != 0)
         {
-            if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) 
+            if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true))
             {
                 commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
                 commodity_list  = g_list_first (commodity_list);
@@ -1727,10 +1727,10 @@ CsvImpPriceAssist::assist_summary_page_prepare ()
 {
     auto text = std::string("<span size=\"medium\"><b>");
     text += _("The prices were imported from the file '") + m_file_name + "'.";
-    text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added);
-    text += _(", duplicated was ") + std::to_string(price_imp->m_prices_duplicated);
-    text += _(" and replaced was ") + std::to_string(price_imp->m_prices_replaced);
-    text += ".</b></span>";
+    text += _("\n\nThere were ") + std::to_string(price_imp->m_prices_added);
+    text += _(" Prices added, ") + std::to_string(price_imp->m_prices_duplicated);
+    text += _(" duplicated and ") + std::to_string(price_imp->m_prices_replaced);
+    text += _(" replaced.</b></span>");
 
     gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str());
 }
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
index 764b196..33db266 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
@@ -44,20 +44,26 @@
     <signal name="prepare" handler="csv_price_imp_assist_prepare_cb" swapped="no"/>
     <signal name="cancel" handler="csv_price_imp_assist_cancel_cb" swapped="no"/>
     <child>
+      <placeholder/>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+    <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 Prices from a CSV file.
 
-There is a minimum number of columns that have to be present for a successful import, for Stock prices these are Date, Amount, Symbol From and for Currency they are Date, Amount, Currency From and Currency To.
+There is a minimum number of columns that have to be present for a successful import, these are Date, Amount, Commodity From and Currency To. If all entries are for the same Commodity / Currency then you can select them and then the columns will be Date and Amount.
 
-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.
+Various options exist for specifying the delimiter as well as a fixed width option. With the fixed width option, double click on the table of rows displayed to set a column width, then right mouse to change if required.
 
 Examples are "RR.L","21/11/2016",5.345,"GBP" and "USD","2016-11-21",1.56,"GBP"
 
 There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row which can be used if you have some header text. Also there is an option to over write existing prices for that day if required.
 
-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.
+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.
 
 This operation is not reversable, so make sure you have a working backup.
 
@@ -536,6 +542,7 @@ Select location and file name for the Import, then click 'OK'...
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
                                 <property name="tooltip_text" translatable="yes">Normally prices are not over written, select this to change that. This setting is not saved.</property>
+                                <property name="focus_on_click">False</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_overwrite_cb" swapped="no"/>
                               </object>
@@ -1094,8 +1101,9 @@ For example
               <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 add Prices.
-Cancel to abort.</property>
+                <property name="label" translatable="yes"><b>Press Apply to add the Prices.
+Cancel to abort.</b></property>
+                <property name="use_markup">True</property>
                 <property name="justify">center</property>
                 <property name="wrap">True</property>
               </object>
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 38a3cbc..d5d3862 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -62,7 +62,7 @@ const gchar* currency_format_user_price[] = {N_("Locale"),
 
 
 /** Constructor for GncPriceImport.
- * @return Pointer to a new GncCSvParseData
+ * @return Pointer to a new GncCsvParseData
  */
 GncPriceImport::GncPriceImport(GncImpFileFormat format)
 {
@@ -142,7 +142,7 @@ bool GncPriceImport::over_write () { return m_over_write; }
 /** Sets a from commodity. This is the commodity all import data relates to.
  *  When a from commodity is set, there can't be any from columns selected
  *  in the import data.
- * @param from_commodity Pointer to a commodity or NULL.
+ * @param from_commodity pointer to a commodity or NULL.
  */
 void GncPriceImport::from_commodity (gnc_commodity* from_commodity)
 {
@@ -164,7 +164,7 @@ gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_comm
 /** Sets a to currency. This is the to currency all import data relates to.
  *  When a to currency is set, there can't be any to currency columns selected
  *  in the import data.
- * @param to_currency Pointer to a commodity or NULL.
+ * @param to_currency pointer to a commodity or NULL.
  */
 void GncPriceImport::to_currency (gnc_commodity* to_currency)
 {
@@ -221,7 +221,6 @@ int GncPriceImport::date_format () { return m_settings.m_date_format; }
  */
 void GncPriceImport::encoding (const std::string& encoding)
 {
-
     // TODO investigate if we can catch conversion errors and report them
     if (m_tokenizer)
     {
@@ -309,12 +308,10 @@ void GncPriceImport::settings (const CsvTransSettings& settings)
     std::copy_n (settings.m_column_types_price.begin(),
             std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()),
             m_settings.m_column_types_price.begin());
-
 }
 
 bool GncPriceImport::save_settings ()
 {
-
     if (trans_preset_is_reserved_name (m_settings.m_name))
         return true;
 
@@ -327,7 +324,6 @@ bool GncPriceImport::save_settings ()
         auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
         m_settings.m_column_widths = fwtok->get_columns();
     }
-
     return m_settings.save();
 }
 
@@ -342,7 +338,6 @@ std::string GncPriceImport::settings_name () { return m_settings.m_name; }
  */
 void GncPriceImport::load_file (const std::string& filename)
 {
-
     /* Get the raw data first and handle an error if one occurs. */
     try
     {
@@ -407,7 +402,6 @@ void GncPriceImport::tokenize (bool guessColTypes)
     }
 }
 
-
 struct ErrorListPrice
 {
 public:
@@ -428,7 +422,6 @@ std::string ErrorListPrice::str()
     return m_error.substr(0, m_error.size() - 1);
 }
 
-
 /* Test for the required minimum number of columns selected and
  * the selection is consistent.
  * @param An ErrorListPrice object to which all found issues are added.
@@ -450,7 +443,7 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
     if (!check_for_column_type(GncPricePropType::TO_CURRENCY))
     {
         if (!m_settings.m_to_currency)
-            error_msg.add_error( _("Please select a Currency to column or set a Currency in the Currency To field."));
+            error_msg.add_error( _("Please select a 'Currency to' column or set a Currency in the 'Currency To' field."));
     }
 
     /* Verify a Commodity from column is selected.
@@ -458,11 +451,10 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
     if (!check_for_column_type(GncPricePropType::FROM_COMMODITY))
     {
         if (!m_settings.m_from_commodity)
-            error_msg.add_error( _("Please select a Commodity from column or set a Commodity in the Commodity From field."));
+            error_msg.add_error( _("Please select a 'Commodity from' column or set a Commodity in the 'Commodity From' field."));
     }
 }
 
-
 /* Check whether the chosen settings can successfully parse
  * the import data. This will check:
  * - there's at least one line selected for import
@@ -547,7 +539,7 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
 
     error_message.clear();
 
-    // Add a CURRENCY_TO property with the default currency to if no currency to column was set by the user
+    // Add a CURRENCY_TO property with the selected 'currency to' if no 'currency to' column was set by the user
     auto line_to_currency = price_props->get_to_currency();
     if (!line_to_currency)
     {
@@ -555,16 +547,16 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
             price_props->set_to_currency(m_settings.m_to_currency);
         else
         {
-            // Oops - the user didn't select an Account column *and* we didn't get a default value either!
+            // Oops - the user didn't select a 'currency to' column *and* we didn't get a selected value either!
             // Note if you get here this suggests a bug in the code!
-            error_message = _("No Currency to column selected and no default Currency specified either.\n"
+            error_message = _("No 'Currency to' column selected and no selected Currency 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);
         }
     }
 
-    // Add a COMMODITY_FROM property with the default commodity from if no commodity from column was set by the user
+    // Add a COMMODITY_FROM property with the selected 'commodity from' if no 'commodity from' column was set by the user
     auto line_from_commodity = price_props->get_from_commodity();
     if (!line_from_commodity)
     {
@@ -572,9 +564,9 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
             price_props->set_from_commodity(m_settings.m_from_commodity);
         else
         {
-            // Oops - the user didn't select an Account column *and* we didn't get a default value either!
+            // Oops - the user didn't select a 'commodity from' column *and* we didn't get a selected value either!
             // Note if you get here this suggests a bug in the code!
-            error_message = _("No Commodity from column selected and no default Commodity specified either.\n"
+            error_message = _("No 'Commodity from' column selected and no selected Commodity 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);
@@ -605,7 +597,6 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
     }
 }
 
-
 /** Creates a list of prices from parsed data. The parsed data
  * will first be validated. If any errors are found in lines that are marked
  * for processing (ie not marked to skip) this function will
@@ -697,11 +688,11 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type,
 
     m_settings.m_column_types_price.at (position) = type;
 
-    // If the user has set a Commodity from column, we can't have a commodity from default set
+    // If the user has set a 'commodity from' column, we can't have a commodity from selected
     if (type == GncPricePropType::FROM_COMMODITY)
         from_commodity (nullptr);
 
-    // If the user has set a Currency to column, we can't have a currency to default set
+    // If the user has set a 'currency to' column, we can't have a currency to selected
     if (type == GncPricePropType::TO_CURRENCY)
         to_currency (nullptr);
 
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index d9293e1..2deffb3 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -160,7 +160,7 @@ time64 parse_date_price (const std::string &date_str, int format)
 }
 
 
-/** Convert str into a GncRational using the user-specified (import) currency format.
+/** Convert str into a GncNumeric using the user-specified (import) currency format.
  * @param str The string to be parsed
  * @param currency_format The currency format to use.
  * @return a GncNumeric
@@ -200,6 +200,11 @@ GncNumeric parse_amount_price (const std::string &str, int currency_format)
     return GncNumeric(val);
 }
 
+/** Convert comm_str into a gnc_commodity.
+ * @param comm_str The string to be parsed
+ * @return a gnc_commodity
+ * @exception May throw std::invalid argument if string can't be parsed properly
+ */
 gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
 {
     if (comm_str.empty())
@@ -324,9 +329,9 @@ std::string GncImportPrice::verify_essentials (void)
     else if (m_amount == boost::none)
         return _("No amount column.");
     else if (m_to_currency == boost::none)
-        return _("No Currency to column.");
+        return _("No 'Currency to' column.");
     else if (m_from_commodity == boost::none)
-        return _("No Commodity from column.");
+        return _("No 'Commodity from' column.");
     else
         return std::string();
 }
@@ -417,7 +422,6 @@ Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
 
         if (perr == false)
             throw std::invalid_argument (_("Failed to create price from selected columns."));
-//FIXME Not sure about this, should this be a PWARN
     }
     else
     {
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 1d44ff6..9ae4853 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -90,6 +90,7 @@ public:
     void set_date_format (int date_format) { m_date_format = date_format ;}
     void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
     void reset (GncPricePropType prop_type);
+    std::string verify_essentials (void);
     Result create_price (QofBook* book, GNCPriceDB *pdb, bool over);
 
     gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; }

commit 9debe91e990ba00c68a09449d6a4c22646faae66
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:47:12 2017 +0000

    Change the way commodity and currency combo's are shown.
    
    Use commodity print name to show in the combo's and use a hidden field
    to sort the list grouping by namespace. Also alter the way these
    settings are saved.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 43f2d8a..075f463 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -419,11 +419,11 @@ GtkTreeModel *get_model (bool all_commodity)
 
     store = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER));
     model = gtk_tree_model_sort_new_with_model (store);
-    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
-                                        0, GTK_SORT_ASCENDING);
+    // set sort to sort on second string, first string will be shown
+    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), 1, GTK_SORT_ASCENDING);
 
     gtk_list_store_append (GTK_LIST_STORE(store), &iter);
-    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, nullptr, -1);
+    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, " ", 2, nullptr, -1);
 
     namespace_list = g_list_first (namespace_list);
     while (namespace_list != nullptr)
@@ -440,23 +440,20 @@ GtkTreeModel *get_model (bool all_commodity)
                 commodity_list  = g_list_first (commodity_list);
                 while (commodity_list != nullptr)
                 {
-                    gchar *name_str;
-                    gchar *save_str;
-                    gchar *settings_str;
+                    const gchar *name_str;
+                    gchar *sort_str;
                     tmp_commodity = (gnc_commodity*)commodity_list->data;
                     DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity));
 
-                    name_str = g_strconcat (tmp_namespace, " : (", gnc_commodity_get_mnemonic (tmp_commodity),
-                                            ") ", gnc_commodity_get_fullname (tmp_commodity), nullptr);
+                    name_str = gnc_commodity_get_printname (tmp_commodity);
 
-                    settings_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr);
-                    DEBUG("Name string is %s, Save string is %s", name_str, settings_str);
+                    sort_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr);
+                    DEBUG("Name string is %s, Sort string is %s", name_str, sort_str);
 
                     gtk_list_store_append (GTK_LIST_STORE(store), &iter);
-                    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, settings_str, 2, tmp_commodity, -1);
+                    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, sort_str, 2, tmp_commodity, -1);
 
-                    g_free (name_str);
-                    g_free (settings_str);
+                    g_free (sort_str);
                     commodity_list = g_list_next (commodity_list);
                 }
             }
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 73ab2a5..3bbc984 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -294,7 +294,7 @@ CsvTransSettings::load (void)
         if (col_types_str)
             g_strfreev (col_types_str);
     }
- 
+
     // Price
     if (m_settings_type.compare("PRICE") == 0)
     {
@@ -424,10 +424,20 @@ CsvTransSettings::save (void)
     if (m_settings_type.compare("PRICE") == 0)
     {
         if (m_to_currency)
-            g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_mnemonic(m_to_currency));
+        {
+            auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_to_currency), "::",
+                               gnc_commodity_get_mnemonic (m_to_currency), nullptr);
+            g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, unique_name);
+            g_free (unique_name);
+        }
 
         if (m_from_commodity)
-            g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_mnemonic(m_from_commodity));
+        {
+            auto unique_name = g_strconcat (gnc_commodity_get_namespace (m_from_commodity), "::",
+                               gnc_commodity_get_mnemonic (m_from_commodity), nullptr);
+            g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, unique_name);
+            g_free (unique_name);
+        }
 
         std::vector<const char*> col_types_str_price;
         for (auto col_type : m_column_types_price)

commit b8bbdb2ad54e2eaf55b774b9954ab4f99775eea6
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:44:38 2017 +0000

    Minor changes and tidy up

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index e3a5ae3..43f2d8a 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -76,7 +76,7 @@ public:
     void assist_preview_page_prepare ();
     void assist_confirm_page_prepare ();
     void assist_summary_page_prepare ();
-    void assist_finish (bool canceled);
+    void assist_finish ();
     void assist_compmgr_close ();
 
     void file_confirm_cb ();
@@ -213,7 +213,6 @@ csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info)
 void
 csv_price_imp_assist_cancel_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
 {
-    info->assist_finish (true);
     gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info);
 }
 
@@ -226,7 +225,7 @@ csv_price_imp_assist_close_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
 void
 csv_price_imp_assist_finish_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
 {
-    info->assist_finish (false);
+    info->assist_finish ();
 }
 
 void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info)
@@ -356,7 +355,7 @@ gnc_commodity *get_commodity_from_combo (GtkComboBox *combo)
     GtkTreeModel *model, *sort_model;
     GtkTreeIter  iter, siter;
     gchar *string;
-   gnc_commodity *comm;
+    gnc_commodity *comm;
 
     if (!gtk_combo_box_get_active_iter (combo, &siter))
         return nullptr;
@@ -741,7 +740,6 @@ void CsvImpPriceAssist::preview_populate_settings_combo()
     gtk_list_store_clear (GTK_LIST_STORE(model));
 
     // Append the default entry
-//FIXME get_trans_presets ????
     auto presets = get_trans_presets (settings_type);
     for (auto preset : presets)
     {
@@ -773,7 +771,7 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
         CsvTransSettings *preset;
         GtkTreeModel *model = gtk_combo_box_get_model (combo);
         gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
-//FIXME
+
         if (preset && !trans_preset_is_reserved_name (preset->m_name))
         {
             /* Current preset is not read_only, so buttons can be enabled */
@@ -781,7 +779,6 @@ void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
             can_save = true;
         }
     }
-//FIXME
     else if (entry_text && (strlen (entry_text) > 0) &&
             !trans_preset_is_reserved_name (std::string(entry_text)))
         can_save = true;
@@ -944,6 +941,8 @@ void CsvImpPriceAssist::preview_update_skipped_rows ()
     preview_refresh_table ();
 }
 
+/* Callback triggered when user clicks on Over Write option
+ */
 void CsvImpPriceAssist::preview_over_write (bool over)
 {
     price_imp->over_write (over);
@@ -1723,6 +1722,7 @@ CsvImpPriceAssist::assist_preview_page_prepare ()
 void
 CsvImpPriceAssist::assist_confirm_page_prepare ()
 {
+    /* Confirm Page */
 }
 
 void
@@ -1752,19 +1752,9 @@ CsvImpPriceAssist::assist_prepare_cb (GtkWidget *page)
 }
 
 void
-CsvImpPriceAssist::assist_finish (bool canceled)
+CsvImpPriceAssist::assist_finish ()
 {
     /* Start the import */
-//FIXME Apply button
-g_print("Finish\n");
-//    if (canceled || price_imp->m_transactions.empty())
-//        gnc_gen_trans_list_delete (gnc_csv_importer_gui);
-//    else
-//        gnc_gen_trans_assist_start (gnc_csv_importer_gui);
-
-
-//FIXME Cancel comes here to, check when nothing set, goes to catch below also
-
     /* Create prices from the parsed data */
     try
     {

commit a996c02ef7da224a0a5962cb50985c1dd887df50
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:43:14 2017 +0000

    Change the settings file to save and load price settings.
    
    Added a setting type to distinguish between TRANS and PRICE settings so
    it can load a specific settings type and added the price save and load
    options.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 2d2cfdd..e3a5ae3 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -64,6 +64,8 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
+const std::string settings_type = "PRICE";
+
 class  CsvImpPriceAssist
 {
 public:
@@ -740,7 +742,7 @@ void CsvImpPriceAssist::preview_populate_settings_combo()
 
     // Append the default entry
 //FIXME get_trans_presets ????
-    auto presets = get_trans_presets ();
+    auto presets = get_trans_presets (settings_type);
     for (auto preset : presets)
     {
         GtkTreeIter iter;
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index d52b676..e3d10cc 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -70,6 +70,8 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_ASSISTANT;
 
+const std::string settings_type = "TRANS";
+
 class  CsvImpTransAssist
 {
 public:
@@ -677,7 +679,7 @@ void CsvImpTransAssist::preview_populate_settings_combo()
 
     // Append the default entry
 
-    auto presets = get_trans_presets ();
+    auto presets = get_trans_presets (settings_type);
     for (auto preset : presets)
     {
         GtkTreeIter iter;
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 5235a4c..73ab2a5 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -41,7 +41,7 @@ extern "C"
 #include "gnc-ui-util.h"
 }
 
-const std::string csv_group_prefix{"CSV - "};
+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"
@@ -70,10 +70,11 @@ G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
 preset_vec presets;
 
-static std::shared_ptr<CsvTransSettings> create_int_no_preset(void)
+static std::shared_ptr<CsvTransSettings> create_int_no_preset(const std::string& set_type)
 {
     auto preset = std::make_shared<CsvTransSettings>();
     preset->m_name = no_settings;
+    preset->m_settings_type = set_type;
 
     return preset;
 }
@@ -109,14 +110,6 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
             GncTransPropType::REC_DATE,
             GncTransPropType::PRICE
     };
-
-    preset->m_column_types_price = {
-            GncPricePropType::DATE,
-            GncPricePropType::AMOUNT,
-            GncPricePropType::FROM_COMMODITY,
-            GncPricePropType::TO_CURRENCY,
-    };
-
     return preset;
 }
 
@@ -124,8 +117,9 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
  * find
  *
  * find all settings entries in the state key file
+ * based on settings type.
  **************************************************/
-const preset_vec& get_trans_presets (void)
+const preset_vec& get_trans_presets (const std::string& set_type)
 {
 
     // Search all Groups in the state key file for ones starting with prefix
@@ -138,11 +132,12 @@ const preset_vec& get_trans_presets (void)
     for (gsize i=0; i < grouplength; i++)
     {
         auto group = std::string(groups[i]);
-        auto pos = group.find(csv_group_prefix);
+        auto gp = csv_group_prefix + set_type + " - ";
+        auto pos = group.find(gp);
         if (pos == std::string::npos)
             continue;
 
-        preset_names.push_back(group.substr(csv_group_prefix.size()));
+        preset_names.push_back(group.substr(gp.size()));
     }
     // string array from the state file is no longer needed now.
     g_strfreev (groups);
@@ -154,18 +149,20 @@ const preset_vec& get_trans_presets (void)
     presets.clear();
 
     /* Start with the internally generated ones */
-    presets.push_back(create_int_no_preset());
-    presets.push_back(create_int_gnc_exp_preset());
+    presets.push_back(create_int_no_preset(set_type));
+
+    if (set_type.compare("TRANS") == 0)
+        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->m_settings_type = set_type;
         preset->m_name = preset_name;
         preset->load();
         presets.push_back(preset);
     }
-
     return presets;
 }
 
@@ -215,7 +212,7 @@ CsvTransSettings::load (void)
 
     GError *key_error = nullptr;
     m_load_error = false;
-    auto group = csv_group_prefix + m_name;
+    auto group = csv_group_prefix + m_settings_type + " - " + m_name;
     auto keyfile = gnc_state_get_current ();
 
     m_skip_start_lines = g_key_file_get_integer (keyfile, group.c_str(), CSV_SKIP_START, &key_error);
@@ -260,56 +257,82 @@ 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')
-        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);
-
-    key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
-    if (key_char && *key_char != '\0')
-//FIXME        m_to_currency = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
-        m_to_currency = nullptr;
-    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_FROM_COMM, &key_error);
-    if (key_char && *key_char != '\0')
-//FIXME        m_from_commodity = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
-        m_from_commodity = nullptr;
-    m_load_error |= handle_load_error (&key_error, group);
-    if (key_char)
-        g_free (key_char);
-
-    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);
-    for (uint32_t i = 0; i < list_len; i++)
+
+    // Transactions
+    if (m_settings_type.compare("TRANS") == 0)
     {
-        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())
+        key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_ACCOUNT, &key_error);
+        if (key_char && *key_char != '\0')
+            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);
+
+        m_column_types.clear();
+        gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+                &list_len, &key_error);
+        for (uint32_t i = 0; i < list_len; i++)
         {
-            /* 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, 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, m_multi_split ? "enabled" : "disabled");
+            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())
+            {
+                /* 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, 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, m_multi_split ? "enabled" : "disabled");
+            }
+            else
+                PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
+                        col_types_str[i]);
         }
-        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);
+    }
+ 
+    // Price
+    if (m_settings_type.compare("PRICE") == 0)
+    {
+        key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
+        if (key_char && *key_char != '\0')
+            m_to_currency = parse_commodity_price_comm (key_char);
+        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_FROM_COMM, &key_error);
+        if (key_char && *key_char != '\0')
+            m_from_commodity = parse_commodity_price_comm (key_char);
+        m_load_error |= handle_load_error (&key_error, group);
+        if (key_char)
+            g_free (key_char);
+
+        m_column_types.clear();
+        gchar** col_types_str_price = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+                &list_len, &key_error);
+        for (uint32_t i = 0; i < list_len; i++)
+        {
+            auto col_types_it = std::find_if (gnc_price_col_type_strs.begin(),
+                    gnc_price_col_type_strs.end(), test_price_prop_type_str (col_types_str_price[i]));
+            if (col_types_it != gnc_price_col_type_strs.end())
+            {
+                // Found a valid column type
+                m_column_types_price.push_back(col_types_it->first);
+            }
+            else
+                PWARN("Found invalid column type '%s'. Inserting column type 'NONE' instead'.",
+                        col_types_str_price[i]);
+        }
+        if (col_types_str_price)
+            g_strfreev (col_types_str_price);
     }
-    if (col_types_str)
-        g_strfreev (col_types_str);
 
+    // Widths
     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);
@@ -347,14 +370,15 @@ CsvTransSettings::save (void)
     }
 
     auto keyfile = gnc_state_get_current ();
-    auto group = csv_group_prefix + m_name;
+    auto group = csv_group_prefix + m_settings_type + " - " + m_name;
 
     // Drop previous saved settings with this name
     g_key_file_remove_group (keyfile, group.c_str(), nullptr);
 
     // Start Saving the settings
+    // Common
     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);
@@ -375,26 +399,44 @@ CsvTransSettings::save (void)
     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 (m_base_account)
-        g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account));
+    if (!m_column_widths.empty())
+        g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
+                (gint*)(m_column_widths.data()), m_column_widths.size());
 
-    if (m_to_currency)
-        g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_fullname(m_to_currency));
+    // Transaction
+    if (m_settings_type.compare("TRANS") == 0)
+    {
+        g_key_file_set_boolean (keyfile, group.c_str(), CSV_MULTI_SPLIT, m_multi_split);
 
-    if (m_from_commodity)
-        g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_fullname(m_from_commodity));
+        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 : m_column_types)
-        col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
+        std::vector<const char*> col_types_str;
+        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 (!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 (!m_column_widths.empty())
-        g_key_file_set_integer_list (keyfile, group.c_str(), CSV_COL_WIDTHS,
-                (gint*)(m_column_widths.data()), m_column_widths.size());
+    // Price
+    if (m_settings_type.compare("PRICE") == 0)
+    {
+        if (m_to_currency)
+            g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_mnemonic(m_to_currency));
+
+        if (m_from_commodity)
+            g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_mnemonic(m_from_commodity));
+
+        std::vector<const char*> col_types_str_price;
+        for (auto col_type : m_column_types_price)
+            col_types_str_price.push_back(gnc_price_col_type_strs[col_type]);
+
+        if (!col_types_str_price.empty())
+            g_key_file_set_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
+                    col_types_str_price.data(), col_types_str_price.size());
+    }
 
     // Do a test read of encoding
     GError *key_error = nullptr;
@@ -425,7 +467,7 @@ CsvTransSettings::remove (void)
         return;
 
     auto keyfile = gnc_state_get_current ();
-    auto group = csv_group_prefix + m_name;
+    auto group = csv_group_prefix + m_settings_type + " - " + m_name;
     g_key_file_remove_group (keyfile, group.c_str(), nullptr);
 }
 
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
index dae837d..7df293c 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -54,8 +54,8 @@ struct CsvTransSettings
     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_to_currency {nullptr},
-            m_from_commodity {nullptr}, m_load_error {false} { }
+            m_separators {","}, m_load_error {false}, m_base_account {nullptr},
+            m_from_commodity {nullptr}, m_to_currency {nullptr} { }
 
 /** Save the gathered widget properties to a key File.
  *
@@ -81,7 +81,9 @@ void remove (void);
  */
 bool read_only (void);
 
+std::string   m_settings_type;                // Settings Type, TRANS, PRICE etc.
 
+// Common Settings
 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
@@ -92,16 +94,17 @@ uint32_t      m_skip_start_lines;             // Number of header rows to skip
 uint32_t      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
+bool          m_load_error;                   // Was there an error while parsing the state file ?
+std::vector<uint32_t> m_column_widths;        // The Column widths
 
+// Transaction Settings
 Account      *m_base_account;                 // Base account
 std::vector<GncTransPropType> m_column_types; // The Column types in order
-std::vector<GncPricePropType> m_column_types_price; // The Column Price types in order
-std::vector<uint32_t> m_column_widths;        // The Column widths
 
-gnc_commodity *m_to_currency;                 //  Price To Currency
+// Price Settings
 gnc_commodity *m_from_commodity;              //  Price From Commodity
-
-bool          m_load_error;                   // Was there an error while parsing the state file ?
+gnc_commodity *m_to_currency;                 //  Price To Currency
+std::vector<GncPricePropType> m_column_types_price; // The Price Column types in order
 };
 
 using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
@@ -109,9 +112,12 @@ using preset_vec = std::vector<std::shared_ptr<CsvTransSettings>>;
  *  - one or more internally defined presets
  *  - all preset found in the state key file.
  *
+ *  @param set_type The type of setting stored in the
+ *  key file, TRANS, PRICE, etc.
+ *
  *  @return a reference to the populated vector.
  */
-const preset_vec& get_trans_presets (void);
+const preset_vec& get_trans_presets (const std::string& set_type);
 
 /** Check whether name can be used as a preset name.
  *  The names of the internal presets are considered reserved.

commit 5578da11860737109f678443b9bf8b4e7084b2dd
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:34:21 2017 +0000

    Fix some errors in conversion of some function names
    
    Some function names did not get converted to a price equivalent and
    reorder some statements.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 120367a..2d2cfdd 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -607,9 +607,9 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
 
         /* 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++)
+        for (int i = 0; i < num_date_formats_price; i++)
         {
-            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user[i]));
+            gtk_combo_box_text_append_text (date_format_combo, _(date_format_user_price[i]));
         }
         gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0);
         g_signal_connect (G_OBJECT(date_format_combo), "changed",
@@ -622,9 +622,9 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
 
         /* 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++)
+        for (int i = 0; i < num_currency_formats_price; i++)
         {
-            gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user[i]));
+            gtk_combo_box_text_append_text (currency_format_combo, _(currency_format_user_price[i]));
         }
         /* Default will the locale */
         gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), 0);
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index e7bb72a..38a3cbc 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -284,9 +284,9 @@ void GncPriceImport::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;
     from_commodity (m_settings.m_from_commodity);
     to_currency (m_settings.m_to_currency);
-    m_settings = settings;
     encoding (m_settings.m_encoding);
 
     if (file_format() == GncImpFileFormat::CSV)
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 809464e..be91941 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -46,12 +46,12 @@ extern "C" {
 #include <boost/optional.hpp>
 
 /* A set of currency formats that the user sees. */
-extern const int num_currency_formats;
-extern const gchar* currency_format_user[];
+extern const int num_currency_formats_price;
+extern const gchar* currency_format_user_price[];
 
 /* A set of date formats that the user sees. */
-extern const int num_date_formats;
-extern const gchar* date_format_user[];
+extern const int num_date_formats_price;
+extern const gchar* date_format_user_price[];
 
 /** Tuple to hold
  *  - a tokenized line of input
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 9ae4853..1d44ff6 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -90,7 +90,6 @@ public:
     void set_date_format (int date_format) { m_date_format = date_format ;}
     void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
     void reset (GncPricePropType prop_type);
-    std::string verify_essentials (void);
     Result create_price (QofBook* book, GNCPriceDB *pdb, bool over);
 
     gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; }

commit 4d75259cb41902581bcffaf71ca2742164e635c2
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:32:48 2017 +0000

    Remove duplicated function

diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index e6f983e..d9293e1 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -240,56 +240,6 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
         return comm;
 }
 
-//FIXME can we change above to do below
-gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency)
-{
-    if (sym_str.empty())
-        return nullptr;
-
-    auto commodity_table = gnc_get_current_commodities ();
-    GList         *namespaces;
-    gnc_commodity *retval = nullptr;
-    gnc_commodity *tmp_commodity = nullptr;
-    char  *tmp_namespace = nullptr;
-    GList *commodity_list = NULL;
-    GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
-
-    namespace_list = g_list_first (namespace_list);
-    while (namespace_list != NULL && retval == NULL)
-    {
-        tmp_namespace = (char*)namespace_list->data;
-        DEBUG("Looking at namespace %s", tmp_namespace);
-        commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
-        commodity_list  = g_list_first (commodity_list);
-        while (commodity_list != NULL && retval == NULL)
-        {
-            const char* tmp_mnemonic = NULL;
-            tmp_commodity = (gnc_commodity*)commodity_list->data;
-            DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity));
-            tmp_mnemonic = gnc_commodity_get_mnemonic (tmp_commodity);
-            if (g_strcmp0 (tmp_mnemonic, sym_str.c_str()) == 0)
-            {
-                retval = tmp_commodity;
-                DEBUG("Commodity %s%s", gnc_commodity_get_fullname (retval), " matches.");
-            }
-            commodity_list = g_list_next (commodity_list);
-        }
-        namespace_list = g_list_next (namespace_list);
-    }
-    g_list_free (commodity_list);
-    g_list_free (namespace_list);
-
-    if (!retval)
-        throw std::invalid_argument (_("Value can't be parsed into a valid commodity."));
-    else
-    {
-        if ((is_currency == true) && (gnc_commodity_is_currency (retval) != true))
-                throw std::invalid_argument (_("Value parsed into an invalid currency for currency column type."));
-        else
-            return retval;
-    }
-}
-
 void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
 {
     try
@@ -307,21 +257,25 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
 
             case GncPricePropType::AMOUNT:
                 m_amount = boost::none;
-                m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails
+                m_amount = parse_amount_price (value, m_currency_format); // Throws if parsing fails
                 break;
 
             case GncPricePropType::FROM_COMMODITY:
                 m_from_commodity = boost::none;
-                comm = parse_commodity_price_sym (value, false); // Throws if parsing fails
+                comm = parse_commodity_price_comm (value); // Throws if parsing fails
                 if (comm)
                     m_from_commodity = comm;
                 break;
 
             case GncPricePropType::TO_CURRENCY:
                 m_to_currency = boost::none;
-                comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
+                comm = parse_commodity_price_comm (value); // Throws if parsing fails
                 if (comm)
+                {
+                    if (gnc_commodity_is_currency (comm) != true)
+                        throw std::invalid_argument (_("Value parsed into an invalid currency for a currency column type."));
                     m_to_currency = comm;
+                }
                 break;
 
             default:
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index a8550ea..9ae4853 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -78,7 +78,6 @@ private:
 
 time64 parse_date_price (const std::string &date_str, int format);
 gnc_commodity* parse_commodity_price_comm (const std::string& comm_str);
-gnc_commodity* parse_commodity_price_sym (const std::string& comm_str, bool is_currency);
 GncNumeric parse_amount_price (const std::string &str, int currency_format);
 
 struct GncImportPrice

commit cd4b5a31006a36d2fdbd8f2f1ef8839a4c83bfea
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:30:16 2017 +0000

    Made changes to preset column types to align with other changes
    
    These changes are to align with the changes to column types and also the
     basic setup of the new commodity from and currency to combo's. More
     changes will follow to make the saving and loading work properly.

diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 48cfdce..5235a4c 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -63,6 +63,8 @@ const std::string gnc_exp{N_("GnuCash Export Format")};
 #define CSV_COL_TYPES    "ColumnTypes"
 #define CSV_COL_WIDTHS   "ColumnWidths"
 #define CSV_ACCOUNT      "BaseAccount"
+#define CSV_TO_CURR      "PriceToCurrency"
+#define CSV_FROM_COMM    "PriceFromCommodity"
 
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
@@ -111,9 +113,8 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
     preset->m_column_types_price = {
             GncPricePropType::DATE,
             GncPricePropType::AMOUNT,
-            GncPricePropType::CURRENCY_FROM,
-            GncPricePropType::CURRENCY_TO,
-            GncPricePropType::SYMBOL_FROM
+            GncPricePropType::FROM_COMMODITY,
+            GncPricePropType::TO_CURRENCY,
     };
 
     return preset;
@@ -266,6 +267,22 @@ CsvTransSettings::load (void)
     if (key_char)
         g_free (key_char);
 
+    key_char = g_key_file_get_string (keyfile, group.c_str(), CSV_TO_CURR, &key_error);
+    if (key_char && *key_char != '\0')
+//FIXME        m_to_currency = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
+        m_to_currency = nullptr;
+    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_FROM_COMM, &key_error);
+    if (key_char && *key_char != '\0')
+//FIXME        m_from_commodity = gnc_account_lookup_by_full_name (gnc_get_current_root_account(), key_char);
+        m_from_commodity = nullptr;
+    m_load_error |= handle_load_error (&key_error, group);
+    if (key_char)
+        g_free (key_char);
+
     m_column_types.clear();
     gsize list_len;
     gchar** col_types_str = g_key_file_get_string_list (keyfile, group.c_str(), CSV_COL_TYPES,
@@ -361,6 +378,12 @@ CsvTransSettings::save (void)
     if (m_base_account)
         g_key_file_set_string (keyfile, group.c_str(), CSV_ACCOUNT, gnc_account_get_full_name(m_base_account));
 
+    if (m_to_currency)
+        g_key_file_set_string (keyfile, group.c_str(), CSV_TO_CURR, gnc_commodity_get_fullname(m_to_currency));
+
+    if (m_from_commodity)
+        g_key_file_set_string (keyfile, group.c_str(), CSV_FROM_COMM, gnc_commodity_get_fullname(m_from_commodity));
+
     std::vector<const char*> col_types_str;
     for (auto col_type : m_column_types)
         col_types_str.push_back(gnc_csv_col_type_strs[col_type]);
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 93275f2..dae837d 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -31,6 +31,7 @@
 extern "C" {
 #include <config.h>
 #include "Account.h"
+#include "gnc-commodity.h"
 }
 
 #include <string>
@@ -53,8 +54,8 @@ struct CsvTransSettings
     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} { }
+            m_separators {","}, m_base_account {nullptr}, m_to_currency {nullptr},
+            m_from_commodity {nullptr}, m_load_error {false} { }
 
 /** Save the gathered widget properties to a key File.
  *
@@ -97,6 +98,9 @@ std::vector<GncTransPropType> m_column_types; // The Column types in order
 std::vector<GncPricePropType> m_column_types_price; // The Column Price types in order
 std::vector<uint32_t> m_column_widths;        // The Column widths
 
+gnc_commodity *m_to_currency;                 //  Price To Currency
+gnc_commodity *m_from_commodity;              //  Price From Commodity
+
 bool          m_load_error;                   // Was there an error while parsing the state file ?
 };
 

commit bf0c3853ac0d9a2f57acfd5dac61c69a583ff23b
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:25:06 2017 +0000

    Add option to specify Commodity from and Currency to for whole file
    
    Added two combo's to allow user to specify a Commodity from and Currency
     to for the whole file. Also reduced the property types to four and
     aligned all the commodity and currency variables.

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index 6c4279a..120367a 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -90,6 +90,8 @@ public:
     void preview_update_encoding (const char* encoding);
     void preview_update_date_format ();
     void preview_update_currency_format ();
+    void preview_update_currency ();
+    void preview_update_commodity ();
     void preview_update_col_type (GtkComboBox* cbox);
     void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
 
@@ -134,6 +136,8 @@ private:
     GtkWidget       *csv_button;                    /**< The widget for the CSV button */
     GtkWidget       *fixed_button;                  /**< The widget for the Fixed Width button */
     GtkWidget       *over_write_cbutton;            /**< The widget for Price Over Write */
+    GtkWidget       *commodity_selector;            /**< The widget for commodity combo box */
+    GtkWidget       *currency_selector;             /**< The widget for currency combo box */
     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 */
@@ -321,6 +325,16 @@ static void csv_price_imp_preview_currency_fmt_sel_cb (GtkComboBox* format_selec
     info->preview_update_currency_format();
 }
 
+static void csv_price_imp_preview_currency_sel_cb (GtkComboBox* currency_selector, CsvImpPriceAssist* info)
+{
+    info->preview_update_currency();
+}
+
+static void csv_price_imp_preview_commodity_sel_cb (GtkComboBox* commodity_selector, CsvImpPriceAssist* info)
+{
+    info->preview_update_commodity();
+}
+
 void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info)
 {
     info->preview_update_col_type (cbox);
@@ -334,6 +348,126 @@ csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton
     return false;
 }
 
+static
+gnc_commodity *get_commodity_from_combo (GtkComboBox *combo)
+{
+    GtkTreeModel *model, *sort_model;
+    GtkTreeIter  iter, siter;
+    gchar *string;
+   gnc_commodity *comm;
+
+    if (!gtk_combo_box_get_active_iter (combo, &siter))
+        return nullptr;
+
+    sort_model = gtk_combo_box_get_model (combo);
+    model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model));
+
+    gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(sort_model),
+                                                    &iter, &siter);
+
+    gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &string, 2, &comm, -1);
+
+    PINFO("Commodity string is %s", string);
+
+    g_free (string);
+    return comm;
+}
+
+static void
+set_commodity_for_combo (GtkComboBox *combo, gnc_commodity *comm)
+{
+    GtkTreeModel *model, *sort_model;
+    GtkTreeIter  iter, siter;
+    gnc_commodity *model_comm;
+    gboolean valid;
+
+    sort_model = gtk_combo_box_get_model (combo);
+    model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model));
+    valid = gtk_tree_model_get_iter_first (model, &iter);
+
+    while (valid)
+    {
+        gtk_tree_model_get (model, &iter, 2, &model_comm, -1);
+        if (model_comm == comm)
+        {
+            if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(sort_model), &siter, &iter))
+            {
+                gtk_combo_box_set_active_iter (combo, &siter);
+                return;
+            }
+        }
+        /* Make iter point to the next row in the list store */
+        valid = gtk_tree_model_iter_next (model, &iter);
+    }
+    // Not found, set it to first iter
+    valid = gtk_tree_model_get_iter_first (model, &iter);
+    if (gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(sort_model), &siter, &iter))
+        gtk_combo_box_set_active_iter (combo, &siter);
+}
+
+static
+GtkTreeModel *get_model (bool all_commodity)
+{
+    GtkTreeModel *store, *model;
+    const gnc_commodity_table *commodity_table = gnc_get_current_commodities ();
+    gnc_commodity *tmp_commodity = nullptr;
+    char  *tmp_namespace = nullptr;
+    GList *commodity_list = nullptr;
+    GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
+    GtkTreeIter iter;
+
+    store = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER));
+    model = gtk_tree_model_sort_new_with_model (store);
+    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
+                                        0, GTK_SORT_ASCENDING);
+
+    gtk_list_store_append (GTK_LIST_STORE(store), &iter);
+    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, " ", 1, nullptr, -1);
+
+    namespace_list = g_list_first (namespace_list);
+    while (namespace_list != nullptr)
+    {
+        tmp_namespace = (char*)namespace_list->data;
+        DEBUG("Looking at namespace %s", tmp_namespace);
+
+        /* Hide the template entry */
+        if (g_utf8_collate (tmp_namespace, "template" ) != 0)
+        {
+            if ((g_utf8_collate (tmp_namespace, GNC_COMMODITY_NS_CURRENCY ) == 0) || (all_commodity == true)) 
+            {
+                commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
+                commodity_list  = g_list_first (commodity_list);
+                while (commodity_list != nullptr)
+                {
+                    gchar *name_str;
+                    gchar *save_str;
+                    gchar *settings_str;
+                    tmp_commodity = (gnc_commodity*)commodity_list->data;
+                    DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity));
+
+                    name_str = g_strconcat (tmp_namespace, " : (", gnc_commodity_get_mnemonic (tmp_commodity),
+                                            ") ", gnc_commodity_get_fullname (tmp_commodity), nullptr);
+
+                    settings_str = g_strconcat (tmp_namespace, "::", gnc_commodity_get_mnemonic (tmp_commodity), nullptr);
+                    DEBUG("Name string is %s, Save string is %s", name_str, settings_str);
+
+                    gtk_list_store_append (GTK_LIST_STORE(store), &iter);
+                    gtk_list_store_set (GTK_LIST_STORE(store), &iter, 0, name_str, 1, settings_str, 2, tmp_commodity, -1);
+
+                    g_free (name_str);
+                    g_free (settings_str);
+                    commodity_list = g_list_next (commodity_list);
+                }
+            }
+        }
+        namespace_list = g_list_next (namespace_list);
+    }
+    g_list_free (commodity_list);
+    g_list_free (namespace_list);
+
+    return model;
+}
+
 
 /*******************************************************
  * Assistant Constructor
@@ -343,6 +477,8 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
     auto builder = gtk_builder_new();
     gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "start_row_adj");
     gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "end_row_adj");
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "liststore1");
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "liststore2");
     gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "CSV Price Assistant");
     csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Price Assistant"));
 
@@ -453,6 +589,18 @@ CsvImpPriceAssist::CsvImpPriceAssist ()
         gtk_container_add (encoding_container, GTK_WIDGET(encselector));
         gtk_widget_show_all (GTK_WIDGET(encoding_container));
 
+        /* Add commodity selection widget */
+        commodity_selector = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_cbox"));
+        gtk_combo_box_set_model (GTK_COMBO_BOX(commodity_selector), get_model (true));
+        g_signal_connect(G_OBJECT(commodity_selector), "changed",
+                         G_CALLBACK(csv_price_imp_preview_commodity_sel_cb), this);
+
+        /* Add currency selection widget */
+        currency_selector = GTK_WIDGET(gtk_builder_get_object (builder, "currency_cbox"));
+        gtk_combo_box_set_model (GTK_COMBO_BOX(currency_selector), get_model (false));
+        g_signal_connect(G_OBJECT(currency_selector), "changed",
+                         G_CALLBACK(csv_price_imp_preview_currency_sel_cb), this);
+
         /* 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"));
@@ -570,6 +718,9 @@ CsvImpPriceAssist::file_confirm_cb ()
     preview_populate_settings_combo();
     gtk_combo_box_set_active (settings_combo, 0);
 
+    // set over_write to false as default
+    price_imp->over_write (false);
+
     auto num = gtk_assistant_get_current_page (csv_imp_asst);
     gtk_assistant_set_current_page (csv_imp_asst, num + 1);
 }
@@ -951,6 +1102,22 @@ CsvImpPriceAssist::preview_update_currency_format ()
     preview_refresh_table ();
 }
 
+void
+CsvImpPriceAssist::preview_update_currency ()
+{
+    gnc_commodity *comm = get_commodity_from_combo (GTK_COMBO_BOX(currency_selector));
+    price_imp->to_currency (comm);
+    preview_refresh_table ();
+}
+
+void
+CsvImpPriceAssist::preview_update_commodity ()
+{
+    gnc_commodity *comm = get_commodity_from_combo (GTK_COMBO_BOX(commodity_selector));
+    price_imp->from_commodity (comm);
+    preview_refresh_table ();
+}
+
 gboolean
 csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist)
 {
@@ -1413,6 +1580,28 @@ void CsvImpPriceAssist::preview_refresh_table ()
     for (uint32_t i = 0; i < ntcols; i++)
         preview_style_column (i, combostore);
 
+    auto column_types = price_imp->column_types_price();
+
+    // look for a commodity column, clear the commdoity combo
+    auto col_type_comm = std::find (column_types.begin(),
+                column_types.end(), GncPricePropType::FROM_COMMODITY);
+    if (col_type_comm != column_types.end())
+    {
+        g_signal_handlers_block_by_func (commodity_selector, (gpointer) csv_price_imp_preview_commodity_sel_cb, this);
+        set_commodity_for_combo (GTK_COMBO_BOX(commodity_selector), nullptr);
+        g_signal_handlers_unblock_by_func (commodity_selector, (gpointer) csv_price_imp_preview_commodity_sel_cb, this);
+    }
+
+    // look for a currency column, clear the currency combo
+    auto col_type_curr = std::find (column_types.begin(),
+                column_types.end(), GncPricePropType::TO_CURRENCY);
+    if (col_type_curr != column_types.end())
+    {
+        g_signal_handlers_block_by_func (currency_selector, (gpointer) csv_price_imp_preview_currency_sel_cb, this);
+        set_commodity_for_combo (GTK_COMBO_BOX(currency_selector), nullptr);
+        g_signal_handlers_unblock_by_func (currency_selector, (gpointer) csv_price_imp_preview_currency_sel_cb, this);
+    }
+
     /* Release our reference for the stores to allow proper memory management. */
     g_object_unref (store);
     g_object_unref (combostore);
@@ -1460,6 +1649,13 @@ CsvImpPriceAssist::preview_refresh ()
             price_imp->currency_format());
     go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str());
 
+    // Set the commodity and currency combos
+    set_commodity_for_combo(GTK_COMBO_BOX(commodity_selector),
+            price_imp->from_commodity());
+
+    set_commodity_for_combo(GTK_COMBO_BOX(currency_selector),
+            price_imp->to_currency());
+
     // Handle separator checkboxes and custom field, only relevant if the file format is csv
     if (price_imp->file_format() == GncImpFileFormat::CSV)
     {
@@ -1533,9 +1729,9 @@ CsvImpPriceAssist::assist_summary_page_prepare ()
     auto text = std::string("<span size=\"medium\"><b>");
     text += _("The prices were imported from the file '") + m_file_name + "'.";
     text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added);
-    text += _(" and ") + std::to_string(price_imp->m_prices_duplicated);
-    text += _(" were duplicated.");
-    text += "</b></span>";
+    text += _(", duplicated was ") + std::to_string(price_imp->m_prices_duplicated);
+    text += _(" and replaced was ") + std::to_string(price_imp->m_prices_replaced);
+    text += ".</b></span>";
 
     gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str());
 }
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
index 705e827..764b196 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
@@ -7,6 +7,26 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkListStore" id="liststore1">
+    <columns>
+      <!-- column-name string -->
+      <column type="gchararray"/>
+      <!-- column-name save -->
+      <column type="gchararray"/>
+      <!-- column-name commodity -->
+      <column type="gpointer"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="liststore2">
+    <columns>
+      <!-- column-name string -->
+      <column type="gchararray"/>
+      <!-- column-name save -->
+      <column type="gchararray"/>
+      <!-- column-name currency -->
+      <column type="gpointer"/>
+    </columns>
+  </object>
   <object class="GtkAdjustment" id="start_row_adj">
     <property name="upper">1000</property>
     <property name="step_increment">1</property>
@@ -515,7 +535,7 @@ Select location and file name for the Import, then click 'OK'...
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="tooltip_text" translatable="yes">Normally prices are not over written, select this to change that.</property>
+                                <property name="tooltip_text" translatable="yes">Normally prices are not over written, select this to change that. This setting is not saved.</property>
                                 <property name="draw_indicator">True</property>
                                 <signal name="toggled" handler="csv_price_imp_preview_overwrite_cb" swapped="no"/>
                               </object>
@@ -804,6 +824,9 @@ For example
                 <property name="y_options">GTK_FILL</property>
               </packing>
             </child>
+            <child>
+              <placeholder/>
+            </child>
           </object>
           <packing>
             <property name="expand">False</property>
@@ -812,6 +835,125 @@ For example
           </packing>
         </child>
         <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</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="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="commodity_hbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkComboBox" id="commodity_cbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="model">liststore1</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                              <attributes>
+                                <attribute name="text">0</attribute>
+                              </attributes>
+                            </child>
+                          </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="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes"><b>Commodity From</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>
+              </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="alignment5">
+                    <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="currency_hbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkComboBox" id="currency_cbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="model">liststore1</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="cellrenderertext3"/>
+                              <attributes>
+                                <attribute name="text">0</attribute>
+                              </attributes>
+                            </child>
+                          </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="label8">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes"><b>Currency To</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">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</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>
@@ -860,7 +1002,7 @@ For example
           <packing>
             <property name="expand">True</property>
             <property name="fill">True</property>
-            <property name="position">1</property>
+            <property name="position">2</property>
           </packing>
         </child>
         <child>
@@ -902,7 +1044,7 @@ For example
             <property name="expand">False</property>
             <property name="fill">False</property>
             <property name="padding">5</property>
-            <property name="position">2</property>
+            <property name="position">3</property>
           </packing>
         </child>
         <child>
@@ -930,7 +1072,7 @@ For example
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
-            <property name="position">3</property>
+            <property name="position">4</property>
           </packing>
         </child>
       </object>
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 5437237..e7bb72a 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -125,7 +125,6 @@ void GncPriceImport::file_format(GncImpFileFormat format)
         auto fwtok = dynamic_cast<GncFwTokenizer*>(m_tokenizer.get());
         fwtok->columns (m_settings.m_column_widths);
     }
-
 }
 
 GncImpFileFormat GncPriceImport::file_format()
@@ -140,6 +139,50 @@ void GncPriceImport::over_write (bool over)
 
 bool GncPriceImport::over_write () { return m_over_write; }
 
+/** Sets a from commodity. This is the commodity all import data relates to.
+ *  When a from commodity is set, there can't be any from columns selected
+ *  in the import data.
+ * @param from_commodity Pointer to a commodity or NULL.
+ */
+void GncPriceImport::from_commodity (gnc_commodity* from_commodity)
+{
+    m_settings.m_from_commodity = from_commodity;
+
+    if (m_settings.m_from_commodity)
+    {
+        auto col_type = std::find (m_settings.m_column_types_price.begin(),
+                m_settings.m_column_types_price.end(), GncPricePropType::FROM_COMMODITY);
+
+        if (col_type != m_settings.m_column_types_price.end())
+            set_column_type_price (col_type -m_settings.m_column_types_price.begin(),
+                            GncPricePropType::NONE);
+    }
+}
+
+gnc_commodity *GncPriceImport::from_commodity () { return m_settings.m_from_commodity; }
+
+/** Sets a to currency. This is the to currency all import data relates to.
+ *  When a to currency is set, there can't be any to currency columns selected
+ *  in the import data.
+ * @param to_currency Pointer to a commodity or NULL.
+ */
+void GncPriceImport::to_currency (gnc_commodity* to_currency)
+{
+    m_settings.m_to_currency = to_currency;
+
+    if (m_settings.m_to_currency)
+    {
+        auto col_type = std::find (m_settings.m_column_types_price.begin(),
+                m_settings.m_column_types_price.end(), GncPricePropType::TO_CURRENCY);
+
+        if (col_type != m_settings.m_column_types_price.end())
+            set_column_type_price (col_type -m_settings.m_column_types_price.begin(),
+                            GncPricePropType::NONE);
+    }
+}
+
+gnc_commodity *GncPriceImport::to_currency () { return m_settings.m_to_currency; }
+
 void GncPriceImport::reset_formatted_column (std::vector<GncPricePropType>& col_types)
 {
     for (auto col_type: col_types)
@@ -241,6 +284,8 @@ void GncPriceImport::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 */
+    from_commodity (m_settings.m_from_commodity);
+    to_currency (m_settings.m_to_currency);
     m_settings = settings;
     encoding (m_settings.m_encoding);
 
@@ -402,14 +447,19 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
 
     /* Verify a Currency to column is selected.
      */
-    if (!check_for_column_type(GncPricePropType::CURRENCY_TO))
-        error_msg.add_error( _("Please select a Currency to column."));
+    if (!check_for_column_type(GncPricePropType::TO_CURRENCY))
+    {
+        if (!m_settings.m_to_currency)
+            error_msg.add_error( _("Please select a Currency to column or set a Currency in the Currency To field."));
+    }
 
-    /* Verify at least one from column (symbol_from or currency_from) column is selected.
+    /* Verify a Commodity from column is selected.
      */
-    if (!check_for_column_type(GncPricePropType::SYMBOL_FROM) &&
-        !check_for_column_type(GncPricePropType::CURRENCY_FROM))
-        error_msg.add_error( _("Please select a symbol or currency from column."));
+    if (!check_for_column_type(GncPricePropType::FROM_COMMODITY))
+    {
+        if (!m_settings.m_from_commodity)
+            error_msg.add_error( _("Please select a Commodity from column or set a Commodity in the Commodity From field."));
+    }
 }
 
 
@@ -497,6 +547,40 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
 
     error_message.clear();
 
+    // Add a CURRENCY_TO property with the default currency to if no currency to column was set by the user
+    auto line_to_currency = price_props->get_to_currency();
+    if (!line_to_currency)
+    {
+        if (m_settings.m_to_currency)
+            price_props->set_to_currency(m_settings.m_to_currency);
+        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!
+            error_message = _("No Currency to column selected and no default Currency 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);
+        }
+    }
+
+    // Add a COMMODITY_FROM property with the default commodity from if no commodity from column was set by the user
+    auto line_from_commodity = price_props->get_from_commodity();
+    if (!line_from_commodity)
+    {
+        if (m_settings.m_from_commodity)
+            price_props->set_from_commodity(m_settings.m_from_commodity);
+        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!
+            error_message = _("No Commodity from column selected and no default Commodity 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 price properties into a price. */
     try
     {
@@ -507,11 +591,12 @@ void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_l
 
         /* If all went well, add this price to the list. */
         auto price_created = price_props->create_price (book, pdb, m_over_write);
-//FIXME Need to look at this
-        if (price_created)
+        if (price_created == ADDED)
             m_prices_added++;
-        else
+        else if (price_created == DUPLICATED)
             m_prices_duplicated++;
+        else if (price_created == REPLACED)
+            m_prices_replaced++;
     }
     catch (const std::invalid_argument& e)
     {
@@ -537,6 +622,7 @@ void GncPriceImport::create_prices ()
 
     m_prices_added = 0;
     m_prices_duplicated = 0;
+    m_prices_replaced = 0;
 
     /* Iterate over all parsed lines */
     for (auto parsed_lines_it = m_parsed_lines.begin();
@@ -550,8 +636,8 @@ void GncPriceImport::create_prices ()
         /* Should not throw anymore, otherwise verify needs revision */
         create_price (parsed_lines_it);
     }
-    PINFO("Number of lines is %d, added is %d, duplicates is %d",
-         (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated);
+    PINFO("Number of lines is %d, added %d, duplicated %d, replaced %d",
+         (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated, m_prices_replaced);
 }
 
 bool
@@ -611,6 +697,14 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type,
 
     m_settings.m_column_types_price.at (position) = type;
 
+    // If the user has set a Commodity from column, we can't have a commodity from default set
+    if (type == GncPricePropType::FROM_COMMODITY)
+        from_commodity (nullptr);
+
+    // If the user has set a Currency to column, we can't have a currency to default set
+    if (type == GncPricePropType::TO_CURRENCY)
+        to_currency (nullptr);
+
     /* Update the preparsed data */
     for (auto parsed_lines_it = m_parsed_lines.begin();
             parsed_lines_it != m_parsed_lines.end();
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 60cdc27..809464e 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -32,7 +32,7 @@
 
 extern "C" {
 #include "config.h"
-
+#include "gnc-commodity.h"
 }
 
 #include <vector>
@@ -85,6 +85,12 @@ public:
     void over_write (bool over);
     bool over_write ();
 
+    void from_commodity (gnc_commodity *from_commodity);
+    gnc_commodity *from_commodity ();
+
+    void to_currency (gnc_commodity *to_currency);
+    gnc_commodity *to_currency ();
+
     void currency_format (int currency_format);
     int currency_format ();
 
@@ -131,6 +137,7 @@ public:
                                                      price properties. */
     int  m_prices_added;
     int  m_prices_duplicated;
+    int  m_prices_replaced;
 
 private:
     /** A helper function used by create_prices. It will attempt
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index a39f178..e6f983e 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -32,8 +32,6 @@ extern "C" {
 
 #include "engine-helpers.h"
 #include "gnc-ui-util.h"
-#include "gnc-pricedb.h"
-
 }
 
 #include <string>
@@ -48,9 +46,8 @@ std::map<GncPricePropType, const char*> gnc_price_col_type_strs = {
         { GncPricePropType::NONE, N_("None") },
         { GncPricePropType::DATE, N_("Date") },
         { GncPricePropType::AMOUNT, N_("Amount") },
-        { GncPricePropType::CURRENCY_FROM, N_("Currency From") },
-        { GncPricePropType::CURRENCY_TO, N_("Currency To") },
-        { GncPricePropType::SYMBOL_FROM, N_("Symbol From") },
+        { GncPricePropType::FROM_COMMODITY, N_("Commodity From") },
+        { GncPricePropType::TO_CURRENCY, N_("Currency To") },
 };
 
 /* Regular expressions used to parse dates per date format */
@@ -243,6 +240,7 @@ gnc_commodity* parse_commodity_price_comm (const std::string& comm_str)
         return comm;
 }
 
+//FIXME can we change above to do below
 gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency)
 {
     if (sym_str.empty())
@@ -285,8 +283,8 @@ gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_c
         throw std::invalid_argument (_("Value can't be parsed into a valid commodity."));
     else
     {
-        if (gnc_commodity_is_currency (retval) != is_currency)
-            throw std::invalid_argument (_("Value parsed into an invalid commodity for column type."));
+        if ((is_currency == true) && (gnc_commodity_is_currency (retval) != true))
+                throw std::invalid_argument (_("Value parsed into an invalid currency for currency column type."));
         else
             return retval;
     }
@@ -312,25 +310,18 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
                 m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails
                 break;
 
-            case GncPricePropType::CURRENCY_FROM:
-                m_currency_from = boost::none;
-                comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
+            case GncPricePropType::FROM_COMMODITY:
+                m_from_commodity = boost::none;
+                comm = parse_commodity_price_sym (value, false); // Throws if parsing fails
                 if (comm)
-                    m_currency_from = comm;
+                    m_from_commodity = comm;
                 break;
 
-            case GncPricePropType::CURRENCY_TO:
-                m_currency_to = boost::none;
+            case GncPricePropType::TO_CURRENCY:
+                m_to_currency = boost::none;
                 comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
                 if (comm)
-                    m_currency_to = comm;
-                break;
-
-            case GncPricePropType::SYMBOL_FROM:
-                m_symbol_from = boost::none;
-                comm = parse_commodity_price_sym (value, false); // Throws if parsing fails
-                if (comm)
-                    m_symbol_from = comm;
+                    m_to_currency = comm;
                 break;
 
             default:
@@ -378,15 +369,15 @@ std::string GncImportPrice::verify_essentials (void)
         return _("No date column.");
     else if (m_amount == boost::none)
         return _("No amount column.");
-    else if (m_currency_to == boost::none)
+    else if (m_to_currency == boost::none)
         return _("No Currency to column.");
-    else if ((m_symbol_from == boost::none) && (m_currency_from == boost::none))
-        return _("No from column.");
+    else if (m_from_commodity == boost::none)
+        return _("No Commodity from column.");
     else
         return std::string();
 }
 
-bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
+Result GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
 {
     /* Gently refuse to create the price if the basics are not set correctly
      * This should have been tested before calling this function though!
@@ -395,47 +386,40 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
     if (!check.empty())
     {
         PWARN ("Refusing to create price because essentials not set properly: %s", check.c_str());
-        return false;
+        return FAILED;
     }
 
     Timespec date;
     timespecFromTime64 (&date, *m_date);
     date.tv_nsec = 0;
 
-#ifdef skip
-//FIXME Numeric needs changing, copied from old version...
     bool rev = false;
-    gnc_commodity *comm_from = nullptr;
+    auto amount = *m_amount;
 
-    if (m_currency_from != boost::none) // Currency Import
+    GNCPrice *old_price = gnc_pricedb_lookup_day (pdb, *m_from_commodity, *m_to_currency, date);
+
+    if (gnc_commodity_is_currency (*m_from_commodity)) // Currency Import
     {
         // Check for currency in reverse direction.
-        GNCPrice *rev_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, *m_currency_from, date);
-        if (rev_price != nullptr)
-            rev = true;
-        gnc_price_unref (rev_price);
+        if (old_price != nullptr)
+        {
+            // Check for price in reverse direction.
+            if (gnc_commodity_equiv (gnc_price_get_currency (old_price), *m_from_commodity))
+                rev = true;
+
+            DEBUG("Commodity from is a Currency");
+        }
 
         // Check for price less than 1, reverse if so.
-        if (gnc_numeric_compare (*m_amount, gnc_numeric_create (1, 1)) != 1)
+        if (*m_amount < GncNumeric(1,1))
             rev = true;
 
-        comm_from = *m_currency_from;
-        DEBUG("Commodity from is a Currency");
     }
-    else
-        comm_from = *m_symbol_from;
-
     DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date),
-          rev, gnc_commodity_get_fullname (comm_from), gnc_commodity_get_fullname (*m_currency_to),
-          gnc_num_dbg_to_string (*m_amount)           );
-
-    GNCPrice *old_price = nullptr;
+        rev, gnc_commodity_get_fullname (*m_from_commodity), gnc_commodity_get_fullname (*m_to_currency),
+        amount.to_string().c_str());
 
-    // Should the commodities be reversed
-    if (rev)
-        old_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, comm_from, date);
-    else
-        old_price = gnc_pricedb_lookup_day (pdb, comm_from, *m_currency_to, date);
+    Result ret_val = ADDED;
 
     // Should old price be over writen
     if ((old_price != nullptr) && (over == true))
@@ -444,31 +428,29 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
         gnc_pricedb_remove_price (pdb, old_price);
         gnc_price_unref (old_price);
         old_price = nullptr;
+        ret_val = REPLACED;
     }
-#endif
-    bool ret_val = true;
-#ifdef skip
+
     // Create the new price
     if (old_price == nullptr)
     {
         DEBUG("Create");
         GNCPrice *price = gnc_price_create (book);
         gnc_price_begin_edit (price);
-
         if (rev)
         {
-            gnc_price_set_commodity (price, *m_currency_to);
-            gnc_price_set_currency (price, comm_from);
-            *m_amount = gnc_numeric_convert (gnc_numeric_invert (*m_amount),
-                                          CURRENCY_DENOM, GNC_HOW_RND_ROUND_HALF_UP);
-            gnc_price_set_value (price, *m_amount);
+            amount = amount.inv(); //invert the amount
+            gnc_price_set_commodity (price, *m_to_currency);
+            gnc_price_set_currency (price, *m_from_commodity);
         }
         else
         {
-            gnc_price_set_commodity (price, comm_from);
-            gnc_price_set_currency (price, *m_currency_to);
-            gnc_price_set_value (price, *m_amount);
+            gnc_price_set_commodity (price, *m_from_commodity);
+            gnc_price_set_currency (price, *m_to_currency);
         }
+        auto amount_conv = amount.convert<RoundType::half_up>(CURRENCY_DENOM);
+        gnc_price_set_value (price, static_cast<gnc_numeric>(amount_conv));
+
         gnc_price_set_time (price, date);
         gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE);
 //FIXME Not sure which one        gnc_price_set_source (price, PRICE_SOURCE_FQ);
@@ -479,15 +461,15 @@ bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
 
         gnc_price_unref (price);
 
-         if (perr == false)
+        if (perr == false)
             throw std::invalid_argument (_("Failed to create price from selected columns."));
 //FIXME Not sure about this, should this be a PWARN
     }
     else
-
-#endif
-        ret_val = false;
-
+    {
+        gnc_price_unref (old_price);
+        ret_val = DUPLICATED;
+    }
     return ret_val;
 }
 
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 57083e9..a8550ea 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -49,12 +49,13 @@ enum class GncPricePropType {
     NONE,
     DATE,
     AMOUNT,
-    CURRENCY_FROM,
-    CURRENCY_TO,
-    SYMBOL_FROM,
-    PRICE_PROPS = SYMBOL_FROM
+    FROM_COMMODITY,
+    TO_CURRENCY,
+    PRICE_PROPS = TO_CURRENCY
 };
 
+enum Result { FAILED, ADDED, DUPLICATED, REPLACED };
+
 /** Maps all column types to a string representation.
  *  The actual definition is in gnc-price-props.cpp.
  *  Attention: that definition should be adjusted for any
@@ -91,7 +92,14 @@ public:
     void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
     void reset (GncPricePropType prop_type);
     std::string verify_essentials (void);
-    bool create_price (QofBook* book, GNCPriceDB *pdb, bool over);
+    Result create_price (QofBook* book, GNCPriceDB *pdb, bool over);
+
+    gnc_commodity* get_from_commodity () { if (m_from_commodity) return *m_from_commodity; else return nullptr; }
+    void set_from_commodity (gnc_commodity* comm) { if (comm) m_from_commodity = comm; else m_from_commodity = boost::none; }
+
+    gnc_commodity* get_to_currency () { if (m_to_currency) return *m_to_currency; else return nullptr; }
+    void set_to_currency (gnc_commodity* curr) { if (curr) m_to_currency = curr; else m_to_currency = boost::none; }
+
     std::string errors();
 
 private:
@@ -99,9 +107,8 @@ private:
     int m_currency_format;
     boost::optional<time64> m_date;
     boost::optional<GncNumeric> m_amount;
-    boost::optional<gnc_commodity*> m_currency_from;
-    boost::optional<gnc_commodity*> m_currency_to;
-    boost::optional<gnc_commodity*> m_symbol_from;
+    boost::optional<gnc_commodity*> m_from_commodity;
+    boost::optional<gnc_commodity*> m_to_currency;
     bool created = false;
 
     std::map<GncPricePropType, std::string> m_errors;

commit 1435813f0208e69aac24705f93f03d10b9cab90b
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:04:49 2017 +0000

    Some text changes

diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
index 09deb93..5437237 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -400,7 +400,7 @@ void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
     if (!check_for_column_type(GncPricePropType::AMOUNT))
         error_msg.add_error( _("Please select an amount column."));
 
-    /* Verify an Currency to column is selected.
+    /* Verify a Currency to column is selected.
      */
     if (!check_for_column_type(GncPricePropType::CURRENCY_TO))
         error_msg.add_error( _("Please select a Currency to column."));
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
index 0898215..60cdc27 100644
--- a/gnucash/import-export/csv-imp/gnc-price-import.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -68,11 +68,10 @@ struct ErrorListPrice;
  * - 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
+ * - parse the file into lines, which in turn are split up in to 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 */
+ * - last step is convert the mapped columns into a list of prices to add */
 class GncPriceImport
 {
 public:

commit cf90b8cb47a6dfe38e870310534fa1a1c0e294b7
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:03:07 2017 +0000

    Remove not required account update

diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
index a6ba349..6c4279a 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -87,7 +87,6 @@ public:
     void preview_over_write (bool over);
     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 ();
@@ -306,11 +305,6 @@ void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImp
     info->preview_update_file_format();
 }
 
-void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info)
-{
-    info->preview_update_account();
-}
-
 void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
                               CsvImpPriceAssist* info)
 {

commit 393b8a126d0dfd7236800fd2705eb8d62deeaf98
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 11:01:16 2017 +0000

    Add CSV Price importer assistant files
    
    These file are largely based on the csv transaction importer.
    They are just the start for subsequent changes.

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index 0bc7d29..44a14ca 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -10,6 +10,7 @@ SET(csv_import_remote_SOURCES
 SET(csv_import_SOURCES
   gncmod-csv-import.c
   assistant-csv-account-import.c
+  assistant-csv-price-import.cpp
   assistant-csv-trans-import.cpp
   gnc-plugin-csv-import.c
   csv-account-import.c
@@ -37,6 +38,7 @@ SET(csv_import_remote_HEADERS
 
 SET(csv_import_noinst_HEADERS
   assistant-csv-account-import.h
+  assistant-csv-price-import.h
   assistant-csv-trans-import.h
   gnc-plugin-csv-import.h
   csv-account-import.h
@@ -88,7 +90,7 @@ INSTALL(TARGETS gncmod-csv-import
 # No headers to install
 
 SET(csv_import_GLADE assistant-csv-account-import.glade
-      assistant-csv-trans-import.glade)
+      assistant-csv-price-import.glade assistant-csv-trans-import.glade)
 
 INSTALL(FILES ${csv_import_GLADE} DESTINATION ${CMAKE_INSTALL_DATADIR}/gnucash/gtkbuilder)
 
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index 4083963..4b473de 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -5,6 +5,7 @@ pkglib_LTLIBRARIES=libgncmod-csv-import.la
 libgncmod_csv_import_la_SOURCES = \
   gncmod-csv-import.c \
   assistant-csv-account-import.c \
+  assistant-csv-price-import.cpp \
   assistant-csv-trans-import.cpp \
   gnc-plugin-csv-import.c \
   csv-account-import.c \
@@ -22,6 +23,7 @@ libgncmod_csv_import_la_SOURCES = \
 
 noinst_HEADERS = \
   assistant-csv-account-import.h \
+  assistant-csv-price-import.h \
   assistant-csv-trans-import.h \
   gnc-plugin-csv-import.h \
   csv-account-import.h \
@@ -79,6 +81,7 @@ ui_DATA = \
 gtkbuilderdir = ${GNC_GTKBUILDER_DIR}
 gtkbuilder_DATA = \
 	assistant-csv-account-import.glade \
+	assistant-csv-price-import.glade \
 	assistant-csv-trans-import.glade
 
 EXTRA_DIST = $(ui_DATA) $(gtkbuilder_DATA) CMakeLists.txt
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
new file mode 100644
index 0000000..a6ba349
--- /dev/null
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
@@ -0,0 +1,1621 @@
+/*******************************************************************\
+ * assistant-csv-price-import.c -- An assistant for importing       *
+ *                                     Prices from a file.          *
+ *                                                                  *
+ * Copyright (C) 2017 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-price-import.cpp
+    @brief CSV Import Assistant
+    @author Copyright (c) 2016 Geert Janssens
+    @author Copyright (c) 2017 Robert Fewell
+*/
+
+#include <guid.hpp>
+
+extern "C"
+{
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <stdlib.h>
+
+#include "gnc-ui.h"
+#include "gnc-uri-utils.h"
+#include "gnc-ui-util.h"
+#include "dialog-utils.h"
+
+#include "gnc-component-manager.h"
+
+#include "gnc-state.h"
+
+#include "assistant-csv-price-import.h"
+
+#include "gnc-csv-gnumeric-popup.h"
+#include "go-charmap-sel.h"
+}
+
+#include "gnc-csv-trans-settings.hpp"
+#include "gnc-price-import.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_PRICE_CM_CLASS "assistant-csv-price-import"
+
+/* This static indicates the debugging module that this .o belongs to.  */
+static QofLogModule log_module = GNC_MOD_ASSISTANT;
+
+class  CsvImpPriceAssist
+{
+public:
+    CsvImpPriceAssist ();
+
+    void assist_prepare_cb (GtkWidget *page);
+    void assist_file_page_prepare ();
+    void assist_preview_page_prepare ();
+    void assist_confirm_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_over_write (bool over);
+    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 (GtkComboBox* cbox);
+    void preview_update_fw_columns (GtkTreeView* treeview, GdkEventButton* event);
+
+    void preview_populate_settings_combo();
+    void preview_handle_save_del_sensitivity (GtkComboBox* combo);
+    void preview_split_column (int col, int offset);
+    void preview_refresh_table ();
+    void preview_refresh ();
+    void preview_validate_settings ();
+
+    friend gboolean
+    fixed_context_menu_handler_price (GnumericPopupMenuElement const *element,
+            gpointer userdata);
+private:
+    /* helper functions to manage the context menu for fixed with columns */
+    uint32_t get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx);
+    void fixed_context_menu (GdkEventButton *event, int col, int dx);
+    /* 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, uint32_t colnum);
+    /* helper function to set rendering parameters for preview data columns */
+    void preview_style_column (uint32_t col_num, GtkTreeModel* model);
+
+    GtkAssistant    *csv_imp_asst;
+
+    GtkWidget       *file_page;                     /**< Assistant file page widget */
+    GtkWidget       *file_chooser;                  /**< The widget for the file chooser */
+    std::string      m_file_name;                   /**< The import file name */
+
+    GtkWidget       *preview_page;                  /**< Assistant preview page widget */
+    GtkComboBox     *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 */
+    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       *over_write_cbutton;            /**< The widget for Price Over Write */
+    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 */
+    GtkComboBoxText *currency_format_combo;         /**< The Combo Text widget for selecting the currency format */
+    GtkTreeView     *treeview;                      /**< The treeview containing the data */
+    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 the user has clicked */
+    int              fixed_context_offset;          /**< The offset (in characters) in the column
+                                                       * the user has clicked */
+
+    GtkWidget       *confirm_page;                  /**< Assistant confirm page widget */
+
+    GtkWidget       *summary_page;                  /**< Assistant summary page widget */
+    GtkWidget       *summary_label;                 /**< The summary text */
+
+    std::unique_ptr<GncPriceImport> price_imp;      /**< The actual data we are previewing */
+};
+
+
+/*******************************************************
+ * Assistant call back functions
+ *******************************************************/
+
+extern "C"
+{
+void csv_price_imp_assist_prepare_cb (GtkAssistant  *assistant, GtkWidget *page, CsvImpPriceAssist* info);
+void csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info);
+void csv_price_imp_assist_cancel_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info);
+void csv_price_imp_assist_close_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info);
+void csv_price_imp_assist_finish_cb (GtkAssistant *gtkassistant, CsvImpPriceAssist* info);
+void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info);
+void csv_price_imp_preview_del_settings_cb (GtkWidget *button, CsvImpPriceAssist *info);
+void csv_price_imp_preview_save_settings_cb (GtkWidget *button, CsvImpPriceAssist *info);
+void csv_price_imp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpPriceAssist *info);
+void csv_price_imp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text,
+        gint new_text_length, gint *position, CsvImpPriceAssist *info);
+void csv_price_imp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpPriceAssist *info);
+void csv_price_imp_preview_srow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info);
+void csv_price_imp_preview_erow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info);
+void csv_price_imp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info);
+void csv_price_imp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info);
+void csv_price_imp_preview_overwrite_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info);
+void csv_price_imp_preview_sep_button_cb (GtkWidget* widget, CsvImpPriceAssist* info);
+void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpPriceAssist* info);
+void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info);
+void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
+                              CsvImpPriceAssist* info);
+}
+
+void
+csv_price_imp_assist_prepare_cb (GtkAssistant *assistant, GtkWidget *page,
+        CsvImpPriceAssist* info)
+{
+    info->assist_prepare_cb(page);
+}
+
+void
+csv_price_imp_assist_destroy_cb (GtkWidget *object, CsvImpPriceAssist* info)
+{
+    gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info);
+    delete info;
+}
+
+void
+csv_price_imp_assist_cancel_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
+{
+    info->assist_finish (true);
+    gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info);
+}
+
+void
+csv_price_imp_assist_close_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
+{
+    gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS, info);
+}
+
+void
+csv_price_imp_assist_finish_cb (GtkAssistant *assistant, CsvImpPriceAssist* info)
+{
+    info->assist_finish (false);
+}
+
+void csv_price_imp_file_confirm_cb (GtkWidget *button, CsvImpPriceAssist *info)
+{
+    info->file_confirm_cb();
+}
+
+void csv_price_imp_preview_del_settings_cb (GtkWidget *button, CsvImpPriceAssist *info)
+{
+    info->preview_settings_delete();
+}
+
+void csv_price_imp_preview_save_settings_cb (GtkWidget *button, CsvImpPriceAssist *info)
+{
+    info->preview_settings_save();
+}
+
+void csv_price_imp_preview_settings_sel_changed_cb (GtkComboBox *combo, CsvImpPriceAssist *info)
+{
+    info->preview_settings_load();
+}
+
+void
+csv_price_imp_preview_settings_text_inserted_cb (GtkEditable *entry, gchar *new_text,
+        gint new_text_length, gint *position, CsvImpPriceAssist *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_price_imp_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_price_imp_preview_settings_text_inserted_cb, info);
+
+    g_signal_stop_emission_by_name (entry, "insert_text");
+}
+
+void
+csv_price_imp_preview_settings_text_changed_cb (GtkEntry *entry, CsvImpPriceAssist *info)
+{
+    info->preview_settings_name(entry);
+}
+
+void csv_price_imp_preview_srow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_price_imp_preview_erow_cb (GtkSpinButton *spin, CsvImpPriceAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_price_imp_preview_skiprows_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_price_imp_preview_skiperrors_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info)
+{
+    info->preview_update_skipped_rows();
+}
+
+void csv_price_imp_preview_overwrite_cb (GtkToggleButton *checkbox, CsvImpPriceAssist *info)
+{
+    info->preview_over_write (gtk_toggle_button_get_active (checkbox));
+}
+
+void csv_price_imp_preview_sep_button_cb (GtkWidget* widget, CsvImpPriceAssist* info)
+{
+    info->preview_update_separators(widget);
+}
+
+void csv_price_imp_preview_sep_fixed_sel_cb (GtkToggleButton* csv_button, CsvImpPriceAssist* info)
+{
+    info->preview_update_file_format();
+}
+
+void csv_price_imp_preview_acct_sel_cb (GtkWidget* widget, CsvImpPriceAssist* info)
+{
+    info->preview_update_account();
+}
+
+void csv_price_imp_preview_enc_sel_cb (GOCharmapSel* selector, const char* encoding,
+                              CsvImpPriceAssist* info)
+{
+    info->preview_update_encoding(encoding);
+}
+
+static void csv_price_imp_preview_date_fmt_sel_cb (GtkComboBox* format_selector, CsvImpPriceAssist* info)
+{
+    info->preview_update_date_format();
+}
+
+static void csv_price_imp_preview_currency_fmt_sel_cb (GtkComboBox* format_selector, CsvImpPriceAssist* info)
+{
+    info->preview_update_currency_format();
+}
+
+void csv_price_imp_preview_col_type_changed_cb (GtkComboBox* cbox, CsvImpPriceAssist* info)
+{
+    info->preview_update_col_type (cbox);
+}
+
+gboolean
+csv_price_imp_preview_treeview_clicked_cb (GtkTreeView* treeview, GdkEventButton* event,
+                                        CsvImpPriceAssist* info)
+{
+    info->preview_update_fw_columns(treeview, event);
+    return false;
+}
+
+
+/*******************************************************
+ * Assistant Constructor
+ *******************************************************/
+CsvImpPriceAssist::CsvImpPriceAssist ()
+{
+    auto builder = gtk_builder_new();
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "start_row_adj");
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "end_row_adj");
+    gnc_builder_add_from_file  (builder , "assistant-csv-price-import.glade", "CSV Price Assistant");
+    csv_imp_asst = GTK_ASSISTANT(gtk_builder_get_object (builder, "CSV Price 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, "confirm_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_price_imp_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_price_imp_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_price_imp_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_price_imp_preview_settings_text_changed_cb), this);
+        g_signal_connect (G_OBJECT(emb_entry), "insert-text",
+                         G_CALLBACK(csv_price_imp_preview_settings_text_inserted_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"));
+        over_write_cbutton = GTK_WIDGET(gtk_builder_get_object (builder, "over_write_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. */
+        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"));
+
+        /* 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_price_imp_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_price_imp_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_price_imp_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"));
+        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. */
+        encoding_selected_called = false;
+    }
+
+    /* Confirm Page */
+    confirm_page = GTK_WIDGET(gtk_builder_get_object (builder, "confirm_page"));
+
+    /* 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));
+}
+
+/**************************************************
+ * Code related to the file chooser page
+ **************************************************/
+
+/* csv_price_imp_file_confirm_cb
+ *
+ * call back for ok button in file chooser widget
+ */
+void
+CsvImpPriceAssist::file_confirm_cb ()
+{
+    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);
+
+    m_file_name = file_name;
+    gnc_set_default_directory (GNC_PREFS_GROUP, starting_dir);
+
+    DEBUG("file_name selected is %s", m_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. */
+    price_imp = std::unique_ptr<GncPriceImport>(new GncPriceImport);
+    /* Assume data is CSV. User can later override to Fixed Width if needed */
+    try
+    {
+        price_imp->file_format (GncImpFileFormat::CSV);
+        price_imp->load_file (m_file_name);
+        price_imp->tokenize (true);
+    }
+    catch (std::ifstream::failure& e)
+    {
+        /* File loading failed ... */
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what());
+        return;
+    }
+    catch (std::range_error &e)
+    {
+        /* Parsing failed ... */
+        gnc_error_dialog (GTK_WIDGET(csv_imp_asst), "%s", e.what());
+        return;
+    }
+    /* Get settings store and populate */
+    preview_populate_settings_combo();
+    gtk_combo_box_set_active (settings_combo, 0);
+
+    auto num = gtk_assistant_get_current_page (csv_imp_asst);
+    gtk_assistant_set_current_page (csv_imp_asst, num + 1);
+}
+
+
+/**************************************************
+ * Code related to the preview page
+ **************************************************/
+
+/* Set the available presets in the settings combo box
+ */
+void CsvImpPriceAssist::preview_populate_settings_combo()
+{
+    // Clear the list store
+    auto model = gtk_combo_box_get_model (settings_combo);
+    gtk_list_store_clear (GTK_LIST_STORE(model));
+
+    // Append the default entry
+//FIXME get_trans_presets ????
+    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->m_name.c_str(), -1);
+    }
+}
+
+/* Enable or disable the save and delete settings buttons
+ * depending on what is selected and entered as settings name
+ */
+void CsvImpPriceAssist::preview_handle_save_del_sensitivity (GtkComboBox* combo)
+{
+    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 and save button */
+    if (gtk_combo_box_get_active_iter (combo, &iter))
+    {
+        CsvTransSettings *preset;
+        GtkTreeModel *model = gtk_combo_box_get_model (combo);
+        gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+//FIXME
+        if (preset && !trans_preset_is_reserved_name (preset->m_name))
+        {
+            /* Current preset is not read_only, so buttons can be enabled */
+            can_delete = true;
+            can_save = true;
+        }
+    }
+//FIXME
+    else if (entry_text && (strlen (entry_text) > 0) &&
+            !trans_preset_is_reserved_name (std::string(entry_text)))
+        can_save = true;
+
+    gtk_widget_set_sensitive (save_button, can_save);
+    gtk_widget_set_sensitive (del_button, can_delete);
+}
+
+void
+CsvImpPriceAssist::preview_settings_name (GtkEntry* entry)
+{
+    auto text = gtk_entry_get_text (entry);
+    if (text)
+        price_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
+CsvImpPriceAssist::preview_settings_load ()
+{
+    // Get the Active Selection
+    GtkTreeIter iter;
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
+        return;
+
+    CsvTransSettings *preset = nullptr;
+    auto model = gtk_combo_box_get_model (settings_combo);
+    gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+
+    if (!preset)
+        return;
+
+    price_imp->settings (*preset);
+    if (preset->m_load_error)
+        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."));
+
+    preview_refresh ();
+    preview_handle_save_del_sensitivity (settings_combo);
+}
+
+/* Callback to delete a settings entry
+ */
+void
+CsvImpPriceAssist::preview_settings_delete ()
+{
+    // Get the Active Selection
+    GtkTreeIter iter;
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
+        return;
+
+    CsvTransSettings *preset = nullptr;
+    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(csv_imp_asst),
+                                GTK_RESPONSE_CANCEL,
+                                "%s", _("Delete the Import Settings."));
+    if (response == GTK_RESPONSE_OK)
+    {
+        preset->remove();
+        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
+CsvImpPriceAssist::preview_settings_save ()
+{
+    auto title = _("Save the Import Settings.");
+    auto new_name = price_imp->settings_name();
+
+    /* Check if the entry text matches an already existing preset */
+    GtkTreeIter iter;
+    if (!gtk_combo_box_get_active_iter (settings_combo, &iter))
+    {
+
+        auto model = gtk_combo_box_get_model (settings_combo);
+        bool valid = gtk_tree_model_get_iter_first (model, &iter);
+        while (valid)
+        {
+            // Walk through the list, reading each row
+            CsvTransSettings *preset;
+            gtk_tree_model_get (model, &iter, SET_GROUP, &preset, -1);
+
+            if (preset && (preset->m_name == std::string(new_name)))
+            {
+                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)
+                    return;
+
+                break;
+            }
+            valid = gtk_tree_model_iter_next (model, &iter);
+        }
+    }
+
+    /* All checks passed, let's save this preset */
+    if (!price_imp->save_settings())
+    {
+        gnc_info_dialog (GTK_WIDGET(csv_imp_asst),
+            "%s", _("The settings have been saved."));
+
+        // Update the settings store
+        preview_populate_settings_combo();
+        auto model = gtk_combo_box_get_model (settings_combo);
+
+        // Get the first entry in model
+        GtkTreeIter   iter;
+        bool valid = gtk_tree_model_get_iter_first (model, &iter);
+        while (valid)
+        {
+            // 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 (settings_combo, &iter);
+
+            g_free (name);
+
+            valid = gtk_tree_model_iter_next (model, &iter);
+        }
+    }
+    else
+        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 CsvImpPriceAssist::preview_update_skipped_rows ()
+{
+    /* Update skip rows in the parser */
+    price_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)));
+
+    /* 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, price_imp->m_parsed_lines.size()
+            - price_imp->skip_start_lines() -1);
+
+    adj = gtk_spin_button_get_adjustment (start_row_spin);
+    gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size()
+            - price_imp->skip_end_lines() - 1);
+
+    preview_refresh_table ();
+}
+
+void CsvImpPriceAssist::preview_over_write (bool over)
+{
+    price_imp->over_write (over);
+}
+
+/** 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 CsvImpPriceAssist::preview_update_separators (GtkWidget* widget)
+{
+    /* Only manipulate separator characters if the currently open file is
+     * csv separated. */
+    if (price_imp->file_format() != GncImpFileFormat::CSV)
+        return;
+
+    /* Add the corresponding characters to checked_separators for each
+     * button that is checked. */
+    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(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(custom_cbutton)))
+    {
+        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. */
+    price_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
+    {
+        price_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 (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(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;
+    }
+}
+
+/** 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 CsvImpPriceAssist::preview_update_file_format ()
+{
+    /* Set the parsing type correctly. */
+    try
+    {
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(csv_button)))
+        {
+            price_imp->file_format (GncImpFileFormat::CSV);
+            g_signal_handlers_disconnect_by_func(G_OBJECT(treeview),
+                    (gpointer)csv_price_imp_preview_treeview_clicked_cb, (gpointer)this);
+            gtk_widget_set_visible (separator_table, true);
+            gtk_widget_set_visible (fw_instructions_hbox, false);
+        }
+        else
+        {
+            price_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_price_imp_preview_treeview_clicked_cb), (gpointer)this);
+            gtk_widget_set_visible (separator_table, false);
+            gtk_widget_set_visible (fw_instructions_hbox, true);
+
+        }
+        price_imp->tokenize (false);
+        preview_refresh_table ();
+    }
+    catch (std::range_error &e)
+    {
+        /* Parsing failed ... */
+        gnc_error_dialog (nullptr, "%s", e.what());
+        return;
+    }
+    catch (...)
+    {
+        // FIXME Handle file loading errors (possibly thrown by file_format above)
+        PWARN("Got an error during file loading");
+    }
+}
+
+/** 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
+ */
+void
+CsvImpPriceAssist::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 (encoding_selected_called)
+    {
+        std::string previous_encoding = price_imp->m_tokenizer->encoding();
+        /* Try converting the new encoding and reparsing. */
+        try
+        {
+            price_imp->encoding (encoding);
+            preview_refresh_table ();
+        }
+        catch (...)
+        {
+            /* If it fails, change back to the old encoding. */
+            gnc_error_dialog (nullptr, "%s", _("Invalid encoding selected"));
+            go_charmap_sel_set_encoding (encselector, previous_encoding.c_str());
+        }
+    }
+    encoding_selected_called = !encoding_selected_called;
+}
+
+void
+CsvImpPriceAssist::preview_update_date_format ()
+{
+    price_imp->date_format (gtk_combo_box_get_active (GTK_COMBO_BOX(date_format_combo)));
+    preview_refresh_table ();
+}
+
+void
+CsvImpPriceAssist::preview_update_currency_format ()
+{
+    price_imp->currency_format (gtk_combo_box_get_active (GTK_COMBO_BOX(currency_format_combo)));
+    preview_refresh_table ();
+}
+
+gboolean
+csv_imp_preview_queue_rebuild_table (CsvImpPriceAssist *assist)
+{
+    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_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
+ * 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.
+ * @param new_text The text the user selected
+ * @param info The display of the data being imported
+ */
+void CsvImpPriceAssist::preview_update_col_type (GtkComboBox* cbox)
+{
+    /* Get the new text */
+    GtkTreeIter iter;
+    auto model = gtk_combo_box_get_model (cbox);
+    gtk_combo_box_get_active_iter (cbox, &iter);
+    auto new_col_type = GncPricePropType::NONE;
+    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"));
+    price_imp->set_column_type_price (col_num, new_col_type);
+
+    /* 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);
+}
+
+/*======================================================================*/
+/*================== Beginning of Gnumeric Code ========================*/
+
+/* The following is code copied from Gnumeric 1.7.8 licensed under the
+ * GNU General Public License version 2 and/or version 3. It is from the file
+ * gnumeric/gnucash/dialogs/dialog-stf-fixed-page.c, and it has been
+ * modified slightly to work within GnuCash. */
+
+/*
+ * Copyright 2001 Almer S. Tigelaar <almer at gnome.org>
+ * Copyright 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.
+ */
+
+enum
+{
+    CONTEXT_STF_IMPORT_MERGE_LEFT = 1,
+    CONTEXT_STF_IMPORT_MERGE_RIGHT = 2,
+    CONTEXT_STF_IMPORT_SPLIT = 3,
+    CONTEXT_STF_IMPORT_WIDEN = 4,
+    CONTEXT_STF_IMPORT_NARROW = 5
+};
+
+static GnumericPopupMenuElement const popup_elements[] =
+{
+    {
+        N_("Merge with column on _left"), GTK_STOCK_REMOVE,
+        0, 1 << CONTEXT_STF_IMPORT_MERGE_LEFT, CONTEXT_STF_IMPORT_MERGE_LEFT
+    },
+    {
+        N_("Merge with column on _right"), GTK_STOCK_REMOVE,
+        0, 1 << CONTEXT_STF_IMPORT_MERGE_RIGHT, CONTEXT_STF_IMPORT_MERGE_RIGHT
+    },
+    { "", nullptr, 0, 0, 0 },
+    {
+        N_("_Split this column"), nullptr,
+        0, 1 << CONTEXT_STF_IMPORT_SPLIT, CONTEXT_STF_IMPORT_SPLIT
+    },
+    { "", nullptr, 0, 0, 0 },
+    {
+        N_("_Widen this column"), GTK_STOCK_GO_FORWARD,
+        0, 1 << CONTEXT_STF_IMPORT_WIDEN, CONTEXT_STF_IMPORT_WIDEN
+    },
+    {
+        N_("_Narrow this column"), GTK_STOCK_GO_BACK,
+        0, 1 << CONTEXT_STF_IMPORT_NARROW, CONTEXT_STF_IMPORT_NARROW
+    },
+    { nullptr, nullptr, 0, 0, 0 },
+};
+
+uint32_t CsvImpPriceAssist::get_new_col_rel_pos (GtkTreeViewColumn *tcol, int dx)
+{
+    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;
+    g_object_get (G_OBJECT(cell), "font_desc", &font_desc, nullptr);
+
+    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);
+    if (width < 1) width = 1;
+    uint32_t charindex = (dx + width / 2) / width;
+    g_object_unref (layout);
+    pango_font_description_free (font_desc);
+
+    return charindex;
+}
+
+gboolean
+fixed_context_menu_handler_price (GnumericPopupMenuElement const *element,
+        gpointer userdata)
+{
+    auto info = (CsvImpPriceAssist*)userdata;
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(info->price_imp->m_tokenizer.get());
+
+    switch (element->index)
+    {
+    case CONTEXT_STF_IMPORT_MERGE_LEFT:
+        fwtok->col_delete (info->fixed_context_col - 1);
+        break;
+    case CONTEXT_STF_IMPORT_MERGE_RIGHT:
+        fwtok->col_delete (info->fixed_context_col);
+        break;
+    case CONTEXT_STF_IMPORT_SPLIT:
+        fwtok->col_split (info->fixed_context_col, info->fixed_context_offset);
+        break;
+    case CONTEXT_STF_IMPORT_WIDEN:
+        fwtok->col_widen (info->fixed_context_col);
+        break;
+    case CONTEXT_STF_IMPORT_NARROW:
+        fwtok->col_narrow (info->fixed_context_col);
+        break;
+    default:
+        ; /* Nothing */
+    }
+
+    try
+    {
+        info->price_imp->tokenize (false);
+    }
+    catch(std::range_error& e)
+    {
+        gnc_error_dialog (nullptr, "%s", e.what());
+        return false;
+    }
+    info->preview_refresh_table ();
+    return true;
+}
+
+void
+CsvImpPriceAssist::fixed_context_menu (GdkEventButton *event,
+                    int col, int offset)
+{
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(price_imp->m_tokenizer.get());
+    fixed_context_col = col;
+    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, 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);
+
+    gnumeric_create_popup_menu (popup_elements, &fixed_context_menu_handler_price,
+                                this, 0,
+                                sensitivity_filter, event);
+}
+
+/*===================== End of Gnumeric Code ===========================*/
+/*======================================================================*/
+void
+CsvImpPriceAssist::preview_split_column (int col, int offset)
+{
+    auto fwtok = dynamic_cast<GncFwTokenizer*>(price_imp->m_tokenizer.get());
+    fwtok->col_split (col, offset);
+    try
+    {
+        price_imp->tokenize (false);
+    }
+    catch (std::range_error& e)
+    {
+        gnc_error_dialog (nullptr, "%s", e.what());
+        return;
+    }
+    preview_refresh_table();
+}
+
+/** Event handler for clicking on column headers. This function is
+ * called whenever the user clicks on column headers in
+ * preview->treeview to modify columns when in fixed-width mode.
+ * @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
+CsvImpPriceAssist::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;
+
+    /* Find the column that was clicked. */
+    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;
+
+    /* 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;
+
+    /* 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 (dcol, offset);
+    else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
+        /* Right clicking brings up a context menu. */
+        fixed_context_menu (event, dcol, offset);
+}
+
+/* Convert state info (errors/skipped) in visual feedback to decorate the preview table */
+void
+CsvImpPriceAssist::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())
+    {
+        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
+ * with valid column types and selects the given column type
+ */
+GtkWidget*
+CsvImpPriceAssist::preview_cbox_factory (GtkTreeModel* model, uint32_t colnum)
+{
+    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>( price_imp->column_types_price()[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_price_imp_preview_col_type_changed_cb), (gpointer)this);
+
+    gtk_widget_show (cbox);
+    return cbox;
+}
+
+void
+CsvImpPriceAssist::preview_style_column (uint32_t 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_price (void)
+{
+    auto combostore = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+    for (auto col_type : gnc_price_col_type_strs)
+    {
+        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 CsvImpPriceAssist::preview_refresh_table ()
+{
+    preview_validate_settings ();
+
+    /* 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 + price_imp->column_types_price().size();
+    auto model_col_types = g_new (GType, ncols);
+    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 importer object. */
+    for (auto parse_line : price_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<3>(parse_line));
+
+        /* 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++)
+        {
+            uint32_t 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);
+
+    /* 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.
+     */
+
+    /* 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)
+    {
+        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 if the model has more data columns than the treeview. */
+    while (ntcols < ncols - PREV_N_FIXED_COLS + 1)
+    {
+        /* Default cell renderer is text, except for the first (error) column */
+        auto renderer = gtk_cell_renderer_text_new();
+        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);
+    }
+
+    /* Reset column attributes as they are undefined after recreating the model */
+    auto combostore = make_column_header_model_price ();
+    for (uint32_t 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);
+    g_object_unref (combostore);
+
+    /* Make the things actually appear. */
+    gtk_widget_show_all (GTK_WIDGET(treeview));
+}
+
+/* Update the preview page based on the current state of the importer.
+ * Should be called when settings are changed.
+ */
+void
+CsvImpPriceAssist::preview_refresh ()
+{
+    // Set start row
+    auto adj = gtk_spin_button_get_adjustment (start_row_spin);
+    gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (start_row_spin,
+            price_imp->skip_start_lines());
+
+    // Set end row
+    adj = gtk_spin_button_get_adjustment (end_row_spin);
+    gtk_adjustment_set_upper (adj, price_imp->m_parsed_lines.size());
+    gtk_spin_button_set_value (end_row_spin,
+            price_imp->skip_end_lines());
+
+    // Set Alternate rows
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(skip_alt_rows_button),
+            price_imp->skip_alt_lines());
+
+    // Set over-write indicator
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(over_write_cbutton),
+            price_imp->over_write());
+
+    // Set Import Format
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(csv_button),
+            (price_imp->file_format() == GncImpFileFormat::CSV));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(fixed_button),
+            (price_imp->file_format() != GncImpFileFormat::CSV));
+
+    // This section deals with the combo's and character encoding
+    gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo),
+            price_imp->date_format());
+    gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo),
+            price_imp->currency_format());
+    go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str());
+
+    // Handle separator checkboxes and custom field, only relevant if the file format is csv
+    if (price_imp->file_format() == GncImpFileFormat::CSV)
+    {
+        auto separators = price_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(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(custom_cbutton),
+                !separators.empty());
+        gtk_entry_set_text (GTK_ENTRY(custom_entry), separators.c_str());
+    }
+    // Repopulate the parsed data table
+    g_idle_add ((GSourceFunc)csv_imp_preview_queue_rebuild_table, this);
+}
+
+/* Check if all selected data can be parsed sufficiently to continue
+ */
+void CsvImpPriceAssist::preview_validate_settings ()
+{
+    /* Allow the user to proceed only if there are no inconsistencies in the settings */
+    auto error_msg = price_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());
+}
+
+/*******************************************************
+ * Assistant page prepare functions
+ *******************************************************/
+
+void
+CsvImpPriceAssist::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(file_chooser), starting_dir);
+        g_free (starting_dir);
+    }
+}
+
+void
+CsvImpPriceAssist::assist_preview_page_prepare ()
+{
+    /* Disable the Forward Assistant Button */
+    gtk_assistant_set_page_complete (csv_imp_asst, preview_page, false);
+
+    /* Load the data into the treeview. */
+    preview_refresh_table ();
+}
+
+void
+CsvImpPriceAssist::assist_confirm_page_prepare ()
+{
+}
+
+void
+CsvImpPriceAssist::assist_summary_page_prepare ()
+{
+    auto text = std::string("<span size=\"medium\"><b>");
+    text += _("The prices were imported from the file '") + m_file_name + "'.";
+    text += _("\n\nThe number of Prices added was ") + std::to_string(price_imp->m_prices_added);
+    text += _(" and ") + std::to_string(price_imp->m_prices_duplicated);
+    text += _(" were duplicated.");
+    text += "</b></span>";
+
+    gtk_label_set_markup (GTK_LABEL(summary_label), text.c_str());
+}
+
+void
+CsvImpPriceAssist::assist_prepare_cb (GtkWidget *page)
+{
+    if (page == file_page)
+        assist_file_page_prepare ();
+    else if (page == preview_page)
+        assist_preview_page_prepare ();
+    else if (page == confirm_page)
+        assist_confirm_page_prepare ();
+    else if (page == summary_page)
+        assist_summary_page_prepare ();
+}
+
+void
+CsvImpPriceAssist::assist_finish (bool canceled)
+{
+    /* Start the import */
+//FIXME Apply button
+g_print("Finish\n");
+//    if (canceled || price_imp->m_transactions.empty())
+//        gnc_gen_trans_list_delete (gnc_csv_importer_gui);
+//    else
+//        gnc_gen_trans_assist_start (gnc_csv_importer_gui);
+
+
+//FIXME Cancel comes here to, check when nothing set, goes to catch below also
+
+    /* Create prices from the parsed data */
+    try
+    {
+        price_imp->create_prices ();
+    }
+    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 creating prices. Please report this as a bug.\n\n"
+              "Error message:\n%s"), err.what());
+        gtk_assistant_set_current_page (csv_imp_asst, 2);
+    }
+}
+
+void
+CsvImpPriceAssist::assist_compmgr_close ()
+{
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(csv_imp_asst));
+    gtk_widget_destroy (GTK_WIDGET(csv_imp_asst));
+}
+
+static void
+csv_price_imp_close_handler (gpointer user_data)
+{
+    auto info = (CsvImpPriceAssist*)user_data;
+    info->assist_compmgr_close();
+}
+
+/********************************************************************\
+ * gnc_file_csv_price_import                                        *
+ * opens up a assistant to import prices.                           *
+ *                                                                  *
+ * Args:   none                                                     *
+ * Return: nothing                                                  *
+\********************************************************************/
+void
+gnc_file_csv_price_import(void)
+{
+    auto info = new CsvImpPriceAssist;
+    gnc_register_gui_component (ASSISTANT_CSV_IMPORT_PRICE_CM_CLASS,
+                                nullptr, csv_price_imp_close_handler,
+                                info);
+}
diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.glade b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
new file mode 100644
index 0000000..705e827
--- /dev/null
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.glade
@@ -0,0 +1,1001 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="2.24"/>
+  <!-- interface-naming-policy project-wide -->
+  <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 Price Assistant">
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <property name="title" translatable="yes">CSV Price Import</property>
+    <property name="default_width">400</property>
+    <property name="default_height">500</property>
+    <signal name="close" handler="csv_price_imp_assist_close_cb" swapped="no"/>
+    <signal name="destroy" handler="csv_price_imp_assist_destroy_cb" swapped="no"/>
+    <signal name="apply" handler="csv_price_imp_assist_finish_cb" swapped="no"/>
+    <signal name="prepare" handler="csv_price_imp_assist_prepare_cb" swapped="no"/>
+    <signal name="cancel" handler="csv_price_imp_assist_cancel_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 Prices from a CSV file.
+
+There is a minimum number of columns that have to be present for a successful import, for Stock prices these are Date, Amount, Symbol From and for Currency they are Date, Amount, Currency From and Currency To.
+
+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.
+
+Examples are "RR.L","21/11/2016",5.345,"GBP" and "USD","2016-11-21",1.56,"GBP"
+
+There is an option for specifying the start row, end row and an option to skip alternate rows beginning from the start row which can be used if you have some header text. Also there is an option to over write existing prices for that day if required.
+
+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.
+
+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="wrap">True</property>
+      </object>
+      <packing>
+        <property name="page_type">intro</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="label7">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">
+Select location and file name for the Import, then click 'OK'...
+</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">Select File for Import</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="preview_page">
+        <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="border_width">12</property>
+        <property name="spacing">2</property>
+        <child>
+          <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">2</property>
+            <property name="column_spacing">5</property>
+            <property name="row_spacing">5</property>
+            <child>
+              <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>
+                          <object class="GtkButton" id="delete_settings">
+                            <property name="visible">True</property>
+                            <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_price_imp_preview_del_settings_cb" swapped="no"/>
+                            <child>
+                              <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>
+                          <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_price_imp_preview_save_settings_cb" swapped="no"/>
+                            <child>
+                              <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>
+                  </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="GtkVBox" id="vbox1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkTable" id="table4">
+                            <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="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_price_imp_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="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkTable" id="separator_table">
+                            <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>
+                            <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_price_imp_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_price_imp_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_price_imp_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_price_imp_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_price_imp_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_price_imp_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_price_imp_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_price_imp_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="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <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="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkTable" id="table5">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <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="over_write_button">
+                                <property name="label" translatable="yes">Allow existing prices to be over written.</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 prices are not over written, select this to change that.</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="csv_price_imp_preview_overwrite_cb" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </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="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="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">
+                        <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">
+                            <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_price_imp_preview_srow_cb" swapped="no"/>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </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_price_imp_preview_erow_cb" swapped="no"/>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </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>
+                          <object class="GtkCheckButton" id="skip_rows">
+                            <property name="label" translatable="yes">Skip alternate lines</property>
+                            <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_price_imp_preview_skiprows_cb" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </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="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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <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="viewport2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkVBox" id="vbox8">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkTreeView" id="ctreeview">
+                        <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="headers_visible">False</property>
+                        <property name="enable_grid_lines">both</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkTreeView" id="treeview">
+                        <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="enable_grid_lines">both</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <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>
+        <child>
+          <object class="GtkHBox" id="hbox14">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <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>
+                <property name="xalign">1</property>
+                <property name="image_position">right</property>
+                <property name="draw_indicator">True</property>
+                <signal name="toggled" handler="csv_price_imp_preview_skiperrors_cb" swapped="no"/>
+              </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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="page_type">intro</property>
+        <property name="complete">True</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="confirm_page">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="border_width">12</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 add Prices.
+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>
+      </object>
+      <packing>
+        <property name="page_type">confirm</property>
+        <property name="title" translatable="yes">Import Prices Now</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="wrap">True</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</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/gnucash/import-export/csv-imp/assistant-csv-price-import.h b/gnucash/import-export/csv-imp/assistant-csv-price-import.h
new file mode 100644
index 0000000..213b622
--- /dev/null
+++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.h
@@ -0,0 +1,36 @@
+/*******************************************************************\
+ * assistant-csv-price-import.h -- An assistant for importing       *
+ *                                     Prices from a file.          *
+ *                                                                  *
+ * Copyright (C) 2017 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-price-import.h
+    @brief CSV Import Assistant
+    @author Copyright (c) 2017 Robert Fewell
+*/
+#ifndef GNC_ASSISTANT_CSV_IMPORT_PRICE_H
+#define GNC_ASSISTANT_CSV_IMPORT_PRICE_H
+
+
+/** The gnc_file_csv_price_import() will let the user import the
+ *  commodity prices from a file.
+ */
+void gnc_file_csv_price_import (void);
+#endif
diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml b/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
index c119a64..bdd3043 100644
--- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
+++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import-ui.xml
@@ -5,6 +5,7 @@
       	<placeholder name="FileImportPlaceholder">
       	   <menuitem name="FileCsvImportAccounts" action="CsvImportAccountAction"/>
       	   <menuitem name="FileCsvImportTrans" action="CsvImportTransAction"/>
+           <menuitem name="FileCsvImportPrice" action="CsvImportPriceAction"/>
       	</placeholder>
       </menu>
     </menu>
diff --git a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
index 25954be..c14092d 100644
--- a/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
+++ b/gnucash/import-export/csv-imp/gnc-plugin-csv-import.c
@@ -30,6 +30,7 @@
 
 #include "assistant-csv-account-import.h"
 #include "assistant-csv-trans-import.h"
+#include "assistant-csv-price-import.h"
 
 static void gnc_plugin_csv_import_class_init (GncPluginCsvImportClass *klass);
 static void gnc_plugin_csv_import_init (GncPluginCsvImport *plugin);
@@ -38,6 +39,7 @@ 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_price_cmd (GtkAction *action, GncMainWindowActionData *data);
 
 #define PLUGIN_ACTIONS_NAME "gnc-plugin-csv-import-actions"
 #define PLUGIN_UI_FILENAME  "gnc-plugin-csv-import-ui.xml"
@@ -54,6 +56,11 @@ static GtkActionEntry gnc_plugin_actions [] =
         N_("Import Transactions from a CSV file"),
         G_CALLBACK (gnc_plugin_csv_import_trans_cmd)
     },
+    {
+        "CsvImportPriceAction", GTK_STOCK_CONVERT, N_("Import _Prices from a CSV file..."), NULL,
+        N_("Import Prices from a CSV file"),
+        G_CALLBACK (gnc_plugin_csv_import_price_cmd)
+    },
 };
 static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions);
 
@@ -157,6 +164,13 @@ gnc_plugin_csv_import_trans_cmd (GtkAction *action,
     gnc_file_csv_trans_import ();
 }
 
+static void
+gnc_plugin_csv_import_price_cmd (GtkAction *action,
+                                 GncMainWindowActionData *data)
+{
+    gnc_file_csv_price_import ();
+}
+
 /************************************************************
  *                    Plugin Bootstrapping                   *
  ************************************************************/

commit 4fe77a577ae429c0407868a62805bd4095c87ca8
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 10:55:29 2017 +0000

    Rename function gnc_csv_price_col_type_strs to gnc_price_col_type_strs

diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
index 1fdcaa53..a39f178 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.cpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -44,7 +44,7 @@ extern "C" {
 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_IMPORT;
 
 /* This map contains a set of strings representing the different column types. */
-std::map<GncPricePropType, const char*> gnc_csv_price_col_type_strs = {
+std::map<GncPricePropType, const char*> gnc_price_col_type_strs = {
         { GncPricePropType::NONE, N_("None") },
         { GncPricePropType::DATE, N_("Date") },
         { GncPricePropType::AMOUNT, N_("Amount") },
@@ -341,7 +341,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
     }
     catch (const std::invalid_argument& e)
     {
-        auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) +
+        auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) +
                        std::string(_(" could not be understood.\n")) +
                        e.what();
         m_errors.emplace(prop_type, err_str);
@@ -349,7 +349,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value)
     }
     catch (const std::out_of_range& e)
     {
-        auto err_str = std::string(_(gnc_csv_price_col_type_strs[prop_type])) +
+        auto err_str = std::string(_(gnc_price_col_type_strs[prop_type])) +
                        std::string(_(" could not be understood.\n")) +
                        e.what();
         m_errors.emplace(prop_type, err_str);
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index d101280..57083e9 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -56,10 +56,10 @@ enum class GncPricePropType {
 };
 
 /** Maps all column types to a string representation.
- *  The actual definition is in gnc-csv-imp-prices.cpp.
+ *  The actual definition is in gnc-price-props.cpp.
  *  Attention: that definition should be adjusted for any
  *  changes to enum class GncPricePropType ! */
-extern std::map<GncPricePropType, const char*> gnc_csv_price_col_type_strs;
+extern std::map<GncPricePropType, const char*> gnc_price_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.

commit eb712dc7d88dc16a5c751a8fea988b35dd20da47
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 10:53:41 2017 +0000

    Add price import files for the csv price importer
    
    These files are largely based on the csv transaction importer and with
    minimum of changes to settings files.

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index e2fa97f..0bc7d29 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -19,6 +19,7 @@ SET(csv_import_SOURCES
   gnc-csv-trans-settings.cpp
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
+  gnc-price-import.cpp
   gnc-price-props.cpp
   gnc-tokenizer.cpp
   gnc-trans-props.cpp
@@ -45,6 +46,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-trans-settings.hpp
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
+  gnc-price-import.hpp
   gnc-price-props.hpp
   gnc-tokenizer.hpp
   gnc-trans-props.hpp
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index 8b13d01..4083963 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-csv-gnumeric-popup.c \
   gnc-dummy-tokenizer.cpp \
   gnc-fw-tokenizer.cpp \
+  gnc-price-import.cpp \
   gnc-price-props.cpp \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
@@ -29,6 +30,7 @@ noinst_HEADERS = \
   gnc-csv-gnumeric-popup.h \
   gnc-dummy-tokenizer.hpp \
   gnc-fw-tokenizer.hpp \
+  gnc-price-import.hpp \
   gnc-price-props.hpp \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
index 6611875..48cfdce 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.cpp
@@ -108,6 +108,14 @@ static std::shared_ptr<CsvTransSettings> create_int_gnc_exp_preset(void)
             GncTransPropType::PRICE
     };
 
+    preset->m_column_types_price = {
+            GncPricePropType::DATE,
+            GncPricePropType::AMOUNT,
+            GncPricePropType::CURRENCY_FROM,
+            GncPricePropType::CURRENCY_TO,
+            GncPricePropType::SYMBOL_FROM
+    };
+
     return preset;
 }
 
diff --git a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
index 379c660..93275f2 100644
--- a/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
+++ b/gnucash/import-export/csv-imp/gnc-csv-trans-settings.hpp
@@ -36,6 +36,7 @@ extern "C" {
 #include <string>
 #include <vector>
 #include "gnc-trans-props.hpp"
+#include "gnc-price-props.hpp"
 #include "gnc-tokenizer.hpp"
 
 /** Enumeration for separator checkbutton types. These are the
@@ -93,7 +94,8 @@ std::string   m_separators;                   // Separators for csv format
 
 Account      *m_base_account;                 // Base account
 std::vector<GncTransPropType> m_column_types; // The Column types in order
-std::vector<uint32_t> m_column_widths;            // The Column widths
+std::vector<GncPricePropType> m_column_types_price; // The Column Price types in order
+std::vector<uint32_t> m_column_widths;        // The Column widths
 
 bool          m_load_error;                   // Was there an error while parsing the state file ?
 };
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.cpp b/gnucash/import-export/csv-imp/gnc-price-import.cpp
new file mode 100644
index 0000000..09deb93
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-price-import.cpp
@@ -0,0 +1,654 @@
+/********************************************************************\
+ * gnc-price-import.cpp - import prices 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                   *
+ *                                                                  *
+\********************************************************************/
+
+#include <guid.hpp>
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "gnc-ui-util.h" //get book
+#include "gnc-commodity.h"
+#include "gnc-pricedb.h"
+}
+
+#include <boost/regex.hpp>
+#include <boost/regex/icu.hpp>
+
+#include "gnc-price-import.hpp"
+#include "gnc-price-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;
+
+const int num_date_formats_price = 5;
+const gchar* date_format_user_price[] = {N_("y-m-d"),
+                                   N_("d-m-y"),
+                                   N_("m-d-y"),
+                                   N_("d-m"),
+                                   N_("m-d")
+                                  };
+
+const int num_currency_formats_price = 3;
+const gchar* currency_format_user_price[] = {N_("Locale"),
+                                       N_("Period: 123,456.78"),
+                                       N_("Comma: 123.456,78")
+                                      };
+
+
+/** Constructor for GncPriceImport.
+ * @return Pointer to a new GncCSvParseData
+ */
+GncPriceImport::GncPriceImport(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_skip_errors = false;
+    file_format(m_settings.m_file_format = format);
+}
+
+/** Destructor for GncPriceImport.
+ */
+GncPriceImport::~GncPriceImport()
+{
+}
+
+/** 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 std::ifstream::failure if file reloading fails
+ */
+void GncPriceImport::file_format(GncImpFileFormat 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 (m_tokenizer)
+    {
+        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;
+    m_tokenizer = gnc_tokenizer_factory(m_settings.m_file_format);
+
+    // Set up new tokenizer with common settings
+    // 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 GncPriceImport::file_format()
+{
+    return m_settings.m_file_format;
+}
+
+void GncPriceImport::over_write (bool over)
+{
+    m_over_write = over;
+}
+
+bool GncPriceImport::over_write () { return m_over_write; }
+
+void GncPriceImport::reset_formatted_column (std::vector<GncPricePropType>& col_types)
+{
+    for (auto col_type: col_types)
+    {
+        auto col = std::find (m_settings.m_column_types_price.begin(),
+                m_settings.m_column_types_price.end(), col_type);
+        if (col != m_settings.m_column_types_price.end())
+            set_column_type_price (col - m_settings.m_column_types_price.begin(), col_type, true);
+    }
+}
+
+void GncPriceImport::currency_format (int currency_format)
+{
+    m_settings.m_currency_format = currency_format;
+
+    /* Reparse all currency related columns */
+    std::vector<GncPricePropType> commodities = { GncPricePropType::AMOUNT };
+    reset_formatted_column (commodities);
+}
+int GncPriceImport::currency_format () { return m_settings.m_currency_format; }
+
+void GncPriceImport::date_format (int date_format)
+{
+    m_settings.m_date_format = date_format;
+
+    /* Reparse all date related columns */
+    std::vector<GncPricePropType> dates = { GncPricePropType::DATE };
+    reset_formatted_column (dates);
+}
+int GncPriceImport::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
+ * the wrong encoding.
+ * @param encoding Encoding that data should be translated using
+ */
+void GncPriceImport::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;
+}
+
+std::string GncPriceImport::encoding () { return m_settings.m_encoding; }
+
+void GncPriceImport::update_skipped_lines(boost::optional<uint32_t> start, boost::optional<uint32_t> 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 (uint32_t i = 0; i < m_parsed_lines.size(); i++)
+    {
+        std::get<3>(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
+    }
+}
+
+uint32_t GncPriceImport::skip_start_lines () { return m_settings.m_skip_start_lines; }
+uint32_t GncPriceImport::skip_end_lines () { return m_settings.m_skip_end_lines; }
+bool GncPriceImport::skip_alt_lines () { return m_settings.m_skip_alt_lines; }
+bool GncPriceImport::skip_err_lines () { return m_skip_errors; }
+
+void GncPriceImport::separators (std::string separators)
+{
+    if (file_format() != GncImpFileFormat::CSV)
+        return;
+
+    m_settings.m_separators = separators;
+    auto csvtok = dynamic_cast<GncCsvTokenizer*>(m_tokenizer.get());
+    csvtok->set_separators (separators);
+
+}
+std::string GncPriceImport::separators () { return m_settings.m_separators; }
+
+void GncPriceImport::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;
+    encoding (m_settings.m_encoding);
+
+    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);
+    }
+    catch (...)
+    { };
+
+    /* Tokenizing will clear column types, reset them here
+     * based on the loaded settings.
+     */
+    std::copy_n (settings.m_column_types_price.begin(),
+            std::min (m_settings.m_column_types_price.size(), settings.m_column_types_price.size()),
+            m_settings.m_column_types_price.begin());
+
+}
+
+bool GncPriceImport::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();
+}
+
+void GncPriceImport::settings_name (std::string name) { m_settings.m_name = name; }
+std::string GncPriceImport::settings_name () { return m_settings.m_name; }
+
+/** Loads a file into a GncPriceImport. This is the first function
+ * that must be called after creating a new GncPriceImport. 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
+ */
+void GncPriceImport::load_file (const std::string& filename)
+{
+
+    /* Get the raw data first and handle an error if one occurs. */
+    try
+    {
+        m_tokenizer->load_file (filename);
+        return;
+    }
+    catch (std::ifstream::failure& ios_err)
+    {
+        // Just log the error and pass it on the call stack for proper handling
+        PWARN ("Error: %s", ios_err.what());
+        throw;
+    }
+}
+
+/** Splits a file into cells. This requires having an encoding that
+ * works (see GncPriceImport::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
+ *          GncPricePropType::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 GncPriceImport::tokenize (bool guessColTypes)
+{
+    if (!m_tokenizer)
+        return;
+
+    uint32_t max_cols = 0;
+    m_tokenizer->tokenize();
+    m_parsed_lines.clear();
+    for (auto tokenized_line : m_tokenizer->get_tokens())
+    {
+        m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(),
+                std::make_shared<GncImportPrice>(date_format(), currency_format()),
+                false));
+        auto length = tokenized_line.size();
+        if (length > max_cols)
+            max_cols = length;
+    }
+
+    /* If it failed, generate an error. */
+    if (m_parsed_lines.size() == 0)
+    {
+        throw (std::range_error ("Tokenizing failed."));
+        return;
+    }
+
+    m_settings.m_column_types_price.resize(max_cols, GncPricePropType::NONE);
+
+    /* Force reinterpretation of already set columns and/or base_account */
+    for (uint32_t i = 0; i < m_settings.m_column_types_price.size(); i++)
+        set_column_type_price (i, m_settings.m_column_types_price[i], true);
+
+    if (guessColTypes)
+    {
+        /* Guess column_types based
+         * on the contents of each column. */
+        /* TODO Make it actually guess. */
+    }
+}
+
+
+struct ErrorListPrice
+{
+public:
+    void add_error (std::string msg);
+    std::string str();
+    bool empty() { return m_error.empty(); }
+private:
+    std::string m_error;
+};
+
+void ErrorListPrice::add_error (std::string msg)
+{
+    m_error += "- " + msg + "\n";
+}
+
+std::string ErrorListPrice::str()
+{
+    return m_error.substr(0, m_error.size() - 1);
+}
+
+
+/* Test for the required minimum number of columns selected and
+ * the selection is consistent.
+ * @param An ErrorListPrice object to which all found issues are added.
+ */
+void GncPriceImport::verify_column_selections (ErrorListPrice& error_msg)
+{
+    /* Verify if a date column is selected and it's parsable.
+     */
+    if (!check_for_column_type(GncPricePropType::DATE))
+        error_msg.add_error( _("Please select a date column."));
+
+    /* Verify an amount column is selected.
+     */
+    if (!check_for_column_type(GncPricePropType::AMOUNT))
+        error_msg.add_error( _("Please select an amount column."));
+
+    /* Verify an Currency to column is selected.
+     */
+    if (!check_for_column_type(GncPricePropType::CURRENCY_TO))
+        error_msg.add_error( _("Please select a Currency to column."));
+
+    /* Verify at least one from column (symbol_from or currency_from) column is selected.
+     */
+    if (!check_for_column_type(GncPricePropType::SYMBOL_FROM) &&
+        !check_for_column_type(GncPricePropType::CURRENCY_FROM))
+        error_msg.add_error( _("Please select a symbol or currency from column."));
+}
+
+
+/* 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.
+ */
+std::string GncPriceImport::verify ()
+{
+    auto newline = std::string();
+    auto error_msg = ErrorListPrice();
+
+    /* 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_msg.add_error(_("No lines are selected for importing. Please reduce the number of lines to skip."));
+        return error_msg.str();
+    }
+
+    verify_column_selections (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<3>(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();
+}
+
+/** Checks whether the parsed line contains all essential properties.
+ * @param parsed_line The line we are checking
+ * @exception std::invalid_argument in an essential property is missing
+ */
+static void price_properties_verify_essentials (std::vector<parse_line_t>::iterator& parsed_line)
+{
+    std::string error_message;
+    std::shared_ptr<GncImportPrice> price_props;
+    std::tie(std::ignore, error_message, price_props, std::ignore) = *parsed_line;
+
+    auto price_error = price_props->verify_essentials();
+
+    error_message.clear();
+    if (!price_error.empty())
+    {
+        error_message += price_error;
+        error_message += "\n";
+    }
+
+    if (!error_message.empty())
+        throw std::invalid_argument(error_message);
+}
+
+void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_line)
+{
+    StrVec line;
+    std::string error_message;
+    std::shared_ptr<GncImportPrice> price_props = nullptr;
+    bool skip_line = false;
+    std::tie(line, error_message, price_props, skip_line) = *parsed_line;
+
+    if (skip_line)
+        return;
+
+    error_message.clear();
+
+    /* If column parsing was successful, convert price properties into a price. */
+    try
+    {
+        price_properties_verify_essentials (parsed_line);
+
+        QofBook* book = gnc_get_current_book();
+        GNCPriceDB *pdb = gnc_pricedb_get_db (book);
+
+        /* If all went well, add this price to the list. */
+        auto price_created = price_props->create_price (book, pdb, m_over_write);
+//FIXME Need to look at this
+        if (price_created)
+            m_prices_added++;
+        else
+            m_prices_duplicated++;
+    }
+    catch (const std::invalid_argument& e)
+    {
+        error_message = e.what();
+        PINFO("User warning: %s", error_message.c_str());
+    }
+}
+
+
+/** Creates a list of prices from parsed data. The parsed data
+ * 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 or processing fails.
+ */
+void GncPriceImport::create_prices ()
+{
+    /* Start with verifying the current data. */
+    auto verify_result = verify();
+    if (!verify_result.empty())
+        throw std::invalid_argument (verify_result);
+
+    m_prices_added = 0;
+    m_prices_duplicated = 0;
+
+    /* Iterate over all parsed lines */
+    for (auto parsed_lines_it = m_parsed_lines.begin();
+            parsed_lines_it != m_parsed_lines.end();
+            ++parsed_lines_it)
+    {
+        /* Skip current line if the user specified so */
+        if ((std::get<3>(*parsed_lines_it)))
+            continue;
+
+        /* Should not throw anymore, otherwise verify needs revision */
+        create_price (parsed_lines_it);
+    }
+    PINFO("Number of lines is %d, added is %d, duplicates is %d",
+         (int)m_parsed_lines.size(), m_prices_added, m_prices_duplicated);
+}
+
+bool
+GncPriceImport::check_for_column_type (GncPricePropType type)
+{
+    return (std::find (m_settings.m_column_types_price.begin(),
+                       m_settings.m_column_types_price.end(), type)
+                        != m_settings.m_column_types_price.end());
+}
+
+/* A helper function intended to be called only from set_column_type_price */
+void GncPriceImport::update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type)
+{
+    if (prop_type == GncPricePropType::NONE)
+        return; /* Only deal with price related properties. */
+
+    auto price_props = std::make_shared<GncImportPrice> (*(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())
+        price_props->reset (prop_type);
+    else
+    {
+        try
+        {
+            price_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<3>(m_parsed_lines[row]))
+                PINFO("User warning: %s", e.what());
+        }
+    }
+    /* Store the result */
+    std::get<2>(m_parsed_lines[row]) = price_props;
+}
+
+void
+GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, bool force)
+{
+    if (position >= m_settings.m_column_types_price.size())
+        return;
+
+    auto old_type = m_settings.m_column_types_price[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_price.begin(), m_settings.m_column_types_price.end(),
+            type, GncPricePropType::NONE);
+
+    m_settings.m_column_types_price.at (position) = type;
+
+    /* Update the preparsed data */
+    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 price 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<2>(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format);
+
+        uint32_t 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 > GncPricePropType::NONE)
+                    && (old_type <= GncPricePropType::PRICE_PROPS))
+                update_price_props (row, old_col, old_type);
+        }
+        /* Then set the property represented by the new column type */
+        if ((type > GncPricePropType::NONE)
+                && (type <= GncPricePropType::PRICE_PROPS))
+            update_price_props (row, position, type);
+
+        /* Report errors if there are any */
+        auto price_errors = std::get<2>(*parsed_lines_it)->errors();
+        std::get<1>(*parsed_lines_it) =
+                price_errors +
+                (price_errors.empty() ? std::string() : "\n");
+    }
+}
+
+std::vector<GncPricePropType> GncPriceImport::column_types_price ()
+{
+    return m_settings.m_column_types_price;
+}
+
diff --git a/gnucash/import-export/csv-imp/gnc-price-import.hpp b/gnucash/import-export/csv-imp/gnc-price-import.hpp
new file mode 100644
index 0000000..0898215
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-price-import.hpp
@@ -0,0 +1,160 @@
+/********************************************************************\
+ * gnc-price-import.hpp - import prices 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 prices from CSV or fixed width files
+     *
+     gnc-price-import.hpp
+     @author Copyright (c) 2015 Geert Janssens <geert at kobaltwit.be>
+     @author Copyright (c) 2017 Robert Fewell
+ */
+
+#ifndef GNC_PRICE_IMPORT_HPP
+#define GNC_PRICE_IMPORT_HPP
+
+extern "C" {
+#include "config.h"
+
+}
+
+#include <vector>
+#include <set>
+#include <map>
+#include <memory>
+
+#include "gnc-tokenizer.hpp"
+#include "gnc-price-props.hpp"
+#include "gnc-csv-trans-settings.hpp"
+#include <boost/optional.hpp>
+
+/* 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[];
+
+/** Tuple to hold
+ *  - a tokenized line of input
+ *  - an optional error string
+ *  - a struct to hold user selected properties for a price */
+using parse_line_t = std::tuple<StrVec,
+                                std::string,
+                                std::shared_ptr<GncImportPrice>,
+                                bool>;
+struct ErrorListPrice;
+
+/** The actual PriceImport 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 GncPriceImport
+{
+public:
+    // Constructor - Destructor
+    GncPriceImport(GncImpFileFormat format = GncImpFileFormat::UNKNOWN);
+    ~GncPriceImport();
+
+    void file_format(GncImpFileFormat format);
+    GncImpFileFormat file_format();
+
+    void over_write (bool over);
+    bool over_write ();
+
+    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 update_skipped_lines (boost::optional<uint32_t> start, boost::optional<uint32_t> end,
+                               boost::optional<bool> alt, boost::optional<bool> errors);
+    uint32_t skip_start_lines ();
+    uint32_t skip_end_lines ();
+    bool skip_alt_lines ();
+    bool skip_err_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();
+
+    /** This function will attempt to convert all tokenized lines into
+     *  prices using the column types the user has set.
+     */
+    void create_prices ();
+    bool check_for_column_type (GncPricePropType type);
+    void set_column_type_price (uint32_t position, GncPricePropType type, bool force = false);
+    std::vector<GncPricePropType> column_types_price ();
+
+    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
+                                                     price properties. */
+    int  m_prices_added;
+    int  m_prices_duplicated;
+
+private:
+    /** A helper function used by create_prices. It will attempt
+     *  to convert a single tokenized line into a price using
+     *  the column types the user has set.
+     */
+    void create_price (std::vector<parse_line_t>::iterator& parsed_line);
+
+    void verify_column_selections (ErrorListPrice& error_msg);
+
+    /* Internal helper function to force reparsing of columns subject to format changes */
+    void reset_formatted_column (std::vector<GncPricePropType>& col_types);
+
+    /* Two internal helper functions that should only be called from within
+     * set_column_type_price for consistency (otherwise error messages may not be (re)set)
+     */
+    void update_price_props (uint32_t row, uint32_t col, GncPricePropType prop_type);
+
+    struct CsvTranSettings;
+    CsvTransSettings m_settings;
+    bool m_skip_errors;
+    bool m_over_write;
+};
+
+
+#endif
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
index 6f1189b..d101280 100644
--- a/gnucash/import-export/csv-imp/gnc-price-props.hpp
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -31,7 +31,7 @@ extern "C" {
 #endif
 
 #include <glib/gi18n.h>
-
+#include "gnc-pricedb.h"
 #include "gnc-commodity.h"
 }
 

commit 3410a03b2291f6a095040dca442df1bf903a284c
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Dec 1 10:44:15 2017 +0000

    Add property files for the csv price importer
    
    These files are largely based on the csv transaction importer

diff --git a/gnucash/import-export/csv-imp/CMakeLists.txt b/gnucash/import-export/csv-imp/CMakeLists.txt
index c2a77b8..e2fa97f 100644
--- a/gnucash/import-export/csv-imp/CMakeLists.txt
+++ b/gnucash/import-export/csv-imp/CMakeLists.txt
@@ -19,6 +19,7 @@ SET(csv_import_SOURCES
   gnc-csv-trans-settings.cpp
   gnc-dummy-tokenizer.cpp
   gnc-fw-tokenizer.cpp
+  gnc-price-props.cpp
   gnc-tokenizer.cpp
   gnc-trans-props.cpp
   gnc-tx-import.cpp
@@ -44,6 +45,7 @@ SET(csv_import_noinst_HEADERS
   gnc-csv-trans-settings.hpp
   gnc-dummy-tokenizer.hpp
   gnc-fw-tokenizer.hpp
+  gnc-price-props.hpp
   gnc-tokenizer.hpp
   gnc-trans-props.hpp
   gnc-tx-import.hpp
diff --git a/gnucash/import-export/csv-imp/Makefile.am b/gnucash/import-export/csv-imp/Makefile.am
index 06ef43f..8b13d01 100644
--- a/gnucash/import-export/csv-imp/Makefile.am
+++ b/gnucash/import-export/csv-imp/Makefile.am
@@ -13,6 +13,7 @@ libgncmod_csv_import_la_SOURCES = \
   gnc-csv-gnumeric-popup.c \
   gnc-dummy-tokenizer.cpp \
   gnc-fw-tokenizer.cpp \
+  gnc-price-props.cpp \
   gnc-tokenizer.cpp \
   gnc-tx-import.cpp \
   gnc-trans-props.cpp \
@@ -28,6 +29,7 @@ noinst_HEADERS = \
   gnc-csv-gnumeric-popup.h \
   gnc-dummy-tokenizer.hpp \
   gnc-fw-tokenizer.hpp \
+  gnc-price-props.hpp \
   gnc-tokenizer.hpp \
   gnc-tx-import.hpp \
   gnc-trans-props.hpp \
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.cpp b/gnucash/import-export/csv-imp/gnc-price-props.cpp
new file mode 100644
index 0000000..1fdcaa53
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-price-props.cpp
@@ -0,0 +1,508 @@
+/********************************************************************\
+ * gnc-price-props.cpp - encapsulate price 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                   *
+ *                                                                  *
+\********************************************************************/
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "engine-helpers.h"
+#include "gnc-ui-util.h"
+#include "gnc-pricedb.h"
+
+}
+
+#include <string>
+#include <boost/regex.hpp>
+#include <boost/regex/icu.hpp>
+#include "gnc-price-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<GncPricePropType, const char*> gnc_csv_price_col_type_strs = {
+        { GncPricePropType::NONE, N_("None") },
+        { GncPricePropType::DATE, N_("Date") },
+        { GncPricePropType::AMOUNT, N_("Amount") },
+        { GncPricePropType::CURRENCY_FROM, N_("Currency From") },
+        { GncPricePropType::CURRENCY_TO, N_("Currency To") },
+        { GncPricePropType::SYMBOL_FROM, N_("Symbol From") },
+};
+
+/* Regular expressions used to parse dates per date format */
+const char* date_regex_price[] = {
+                             "(?:"                                   // 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_price (const std::string &date_str, int format)
+{
+    boost::regex r(date_regex_price[format]);
+    boost::smatch what;
+    if(!boost::regex_search(date_str, what, r))
+        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 (_("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"));
+
+    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);
+    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;
+}
+
+
+/** 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 GncNumeric
+ * @exception May throw std::invalid argument if string can't be parsed properly
+ */
+GncNumeric parse_amount_price (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 (_("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, "");
+
+    /* 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 (_("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 (_("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 (_("Value can't be parsed into a number using the selected currency format."));
+        break;
+    }
+
+    return GncNumeric(val);
+}
+
+gnc_commodity* parse_commodity_price_comm (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 (_("Value can't be parsed into a valid commodity."));
+    else
+        return comm;
+}
+
+gnc_commodity * parse_commodity_price_sym (const std::string& sym_str, bool is_currency)
+{
+    if (sym_str.empty())
+        return nullptr;
+
+    auto commodity_table = gnc_get_current_commodities ();
+    GList         *namespaces;
+    gnc_commodity *retval = nullptr;
+    gnc_commodity *tmp_commodity = nullptr;
+    char  *tmp_namespace = nullptr;
+    GList *commodity_list = NULL;
+    GList *namespace_list = gnc_commodity_table_get_namespaces (commodity_table);
+
+    namespace_list = g_list_first (namespace_list);
+    while (namespace_list != NULL && retval == NULL)
+    {
+        tmp_namespace = (char*)namespace_list->data;
+        DEBUG("Looking at namespace %s", tmp_namespace);
+        commodity_list = gnc_commodity_table_get_commodities (commodity_table, tmp_namespace);
+        commodity_list  = g_list_first (commodity_list);
+        while (commodity_list != NULL && retval == NULL)
+        {
+            const char* tmp_mnemonic = NULL;
+            tmp_commodity = (gnc_commodity*)commodity_list->data;
+            DEBUG("Looking at commodity %s", gnc_commodity_get_fullname (tmp_commodity));
+            tmp_mnemonic = gnc_commodity_get_mnemonic (tmp_commodity);
+            if (g_strcmp0 (tmp_mnemonic, sym_str.c_str()) == 0)
+            {
+                retval = tmp_commodity;
+                DEBUG("Commodity %s%s", gnc_commodity_get_fullname (retval), " matches.");
+            }
+            commodity_list = g_list_next (commodity_list);
+        }
+        namespace_list = g_list_next (namespace_list);
+    }
+    g_list_free (commodity_list);
+    g_list_free (namespace_list);
+
+    if (!retval)
+        throw std::invalid_argument (_("Value can't be parsed into a valid commodity."));
+    else
+    {
+        if (gnc_commodity_is_currency (retval) != is_currency)
+            throw std::invalid_argument (_("Value parsed into an invalid commodity for column type."));
+        else
+            return retval;
+    }
+}
+
+void GncImportPrice::set (GncPricePropType 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);
+
+        gnc_commodity *comm = nullptr;
+        switch (prop_type)
+        {
+            case GncPricePropType::DATE:
+                m_date = boost::none;
+                m_date = parse_date_price (value, m_date_format); // Throws if parsing fails
+                break;
+
+            case GncPricePropType::AMOUNT:
+                m_amount = boost::none;
+                m_amount = parse_amount_price (value, m_currency_format); // Will throw if parsing fails
+                break;
+
+            case GncPricePropType::CURRENCY_FROM:
+                m_currency_from = boost::none;
+                comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
+                if (comm)
+                    m_currency_from = comm;
+                break;
+
+            case GncPricePropType::CURRENCY_TO:
+                m_currency_to = boost::none;
+                comm = parse_commodity_price_sym (value, true); // Throws if parsing fails
+                if (comm)
+                    m_currency_to = comm;
+                break;
+
+            case GncPricePropType::SYMBOL_FROM:
+                m_symbol_from = boost::none;
+                comm = parse_commodity_price_sym (value, false); // Throws if parsing fails
+                if (comm)
+                    m_symbol_from = comm;
+                break;
+
+            default:
+                /* Issue a warning for all other prop_types. */
+                PWARN ("%d is an invalid property for a Price", static_cast<int>(prop_type));
+                break;
+        }
+    }
+    catch (const std::invalid_argument& e)
+    {
+        auto err_str = std::string(_(gnc_csv_price_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_price_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 GncImportPrice::reset (GncPricePropType prop_type)
+{
+    try
+    {
+        set (prop_type, std::string());
+    }
+    catch (...)
+    {
+        // Set with an empty string will effectively clear the property
+        // but can also set an error for the property. Clear that error here.
+        m_errors.erase(prop_type);
+    }
+}
+
+std::string GncImportPrice::verify_essentials (void)
+{
+    /* Make sure this price has the minimum required set of properties defined */
+    if (m_date == boost::none)
+        return _("No date column.");
+    else if (m_amount == boost::none)
+        return _("No amount column.");
+    else if (m_currency_to == boost::none)
+        return _("No Currency to column.");
+    else if ((m_symbol_from == boost::none) && (m_currency_from == boost::none))
+        return _("No from column.");
+    else
+        return std::string();
+}
+
+bool GncImportPrice::create_price (QofBook* book, GNCPriceDB *pdb, bool over)
+{
+    /* Gently refuse to create the price 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 price because essentials not set properly: %s", check.c_str());
+        return false;
+    }
+
+    Timespec date;
+    timespecFromTime64 (&date, *m_date);
+    date.tv_nsec = 0;
+
+#ifdef skip
+//FIXME Numeric needs changing, copied from old version...
+    bool rev = false;
+    gnc_commodity *comm_from = nullptr;
+
+    if (m_currency_from != boost::none) // Currency Import
+    {
+        // Check for currency in reverse direction.
+        GNCPrice *rev_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, *m_currency_from, date);
+        if (rev_price != nullptr)
+            rev = true;
+        gnc_price_unref (rev_price);
+
+        // Check for price less than 1, reverse if so.
+        if (gnc_numeric_compare (*m_amount, gnc_numeric_create (1, 1)) != 1)
+            rev = true;
+
+        comm_from = *m_currency_from;
+        DEBUG("Commodity from is a Currency");
+    }
+    else
+        comm_from = *m_symbol_from;
+
+    DEBUG("Date is %s, Rev is %d, Commodity from is '%s', Currency is '%s', Amount is %s", gnc_print_date (date),
+          rev, gnc_commodity_get_fullname (comm_from), gnc_commodity_get_fullname (*m_currency_to),
+          gnc_num_dbg_to_string (*m_amount)           );
+
+    GNCPrice *old_price = nullptr;
+
+    // Should the commodities be reversed
+    if (rev)
+        old_price = gnc_pricedb_lookup_day (pdb, *m_currency_to, comm_from, date);
+    else
+        old_price = gnc_pricedb_lookup_day (pdb, comm_from, *m_currency_to, date);
+
+    // Should old price be over writen
+    if ((old_price != nullptr) && (over == true))
+    {
+        DEBUG("Over write");
+        gnc_pricedb_remove_price (pdb, old_price);
+        gnc_price_unref (old_price);
+        old_price = nullptr;
+    }
+#endif
+    bool ret_val = true;
+#ifdef skip
+    // Create the new price
+    if (old_price == nullptr)
+    {
+        DEBUG("Create");
+        GNCPrice *price = gnc_price_create (book);
+        gnc_price_begin_edit (price);
+
+        if (rev)
+        {
+            gnc_price_set_commodity (price, *m_currency_to);
+            gnc_price_set_currency (price, comm_from);
+            *m_amount = gnc_numeric_convert (gnc_numeric_invert (*m_amount),
+                                          CURRENCY_DENOM, GNC_HOW_RND_ROUND_HALF_UP);
+            gnc_price_set_value (price, *m_amount);
+        }
+        else
+        {
+            gnc_price_set_commodity (price, comm_from);
+            gnc_price_set_currency (price, *m_currency_to);
+            gnc_price_set_value (price, *m_amount);
+        }
+        gnc_price_set_time (price, date);
+        gnc_price_set_source (price, PRICE_SOURCE_USER_PRICE);
+//FIXME Not sure which one        gnc_price_set_source (price, PRICE_SOURCE_FQ);
+        gnc_price_set_typestr (price, PRICE_TYPE_LAST);
+        gnc_price_commit_edit (price);
+
+        bool perr = gnc_pricedb_add_price (pdb, price);
+
+        gnc_price_unref (price);
+
+         if (perr == false)
+            throw std::invalid_argument (_("Failed to create price from selected columns."));
+//FIXME Not sure about this, should this be a PWARN
+    }
+    else
+
+#endif
+        ret_val = false;
+
+    return ret_val;
+}
+
+static std::string gen_err_str (std::map<GncPricePropType, std::string>& errors)
+{
+    auto full_error = std::string();
+    for (auto error : errors)
+    {
+        full_error += (full_error.empty() ? "" : "\n") + error.second;
+    }
+    return full_error;
+}
+
+std::string GncImportPrice::errors ()
+{
+    return gen_err_str (m_errors);
+}
+
diff --git a/gnucash/import-export/csv-imp/gnc-price-props.hpp b/gnucash/import-export/csv-imp/gnc-price-props.hpp
new file mode 100644
index 0000000..6f1189b
--- /dev/null
+++ b/gnucash/import-export/csv-imp/gnc-price-props.hpp
@@ -0,0 +1,110 @@
+/********************************************************************\
+ * gnc-price-props.hpp - encapsulate price 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_PRICE_PROPS_HPP
+#define GNC_PRICE_PROPS_HPP
+
+extern "C" {
+#include <platform.h>
+#if PLATFORM(WINDOWS)
+#include <windows.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "gnc-commodity.h"
+}
+
+#include <string>
+#include <map>
+#include <memory>
+#include <boost/optional.hpp>
+#include <gnc-numeric.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 GncPricePropType::NONE
+ * type. */
+enum class GncPricePropType {
+    NONE,
+    DATE,
+    AMOUNT,
+    CURRENCY_FROM,
+    CURRENCY_TO,
+    SYMBOL_FROM,
+    PRICE_PROPS = SYMBOL_FROM
+};
+
+/** Maps all column types to a string representation.
+ *  The actual definition is in gnc-csv-imp-prices.cpp.
+ *  Attention: that definition should be adjusted for any
+ *  changes to enum class GncPricePropType ! */
+extern std::map<GncPricePropType, const char*> gnc_csv_price_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_price_prop_type_str
+{
+    test_price_prop_type_str( const char* name ) : m_name(name) {}
+    bool operator()( const std::pair<GncPricePropType, const char*>& v ) const
+    {
+        return !g_strcmp0(v.second, m_name);
+    }
+private:
+    const char *m_name;
+};
+
+time64 parse_date_price (const std::string &date_str, int format);
+gnc_commodity* parse_commodity_price_comm (const std::string& comm_str);
+gnc_commodity* parse_commodity_price_sym (const std::string& comm_str, bool is_currency);
+GncNumeric parse_amount_price (const std::string &str, int currency_format);
+
+struct GncImportPrice
+{
+public:
+    GncImportPrice (int date_format, int currency_format) : m_date_format{date_format},
+        m_currency_format{currency_format}{};
+
+    void set (GncPricePropType prop_type, const std::string& value);
+    void set_date_format (int date_format) { m_date_format = date_format ;}
+    void set_currency_format (int currency_format) { m_currency_format = currency_format ;}
+    void reset (GncPricePropType prop_type);
+    std::string verify_essentials (void);
+    bool create_price (QofBook* book, GNCPriceDB *pdb, bool over);
+    std::string errors();
+
+private:
+    int m_date_format;
+    int m_currency_format;
+    boost::optional<time64> m_date;
+    boost::optional<GncNumeric> m_amount;
+    boost::optional<gnc_commodity*> m_currency_from;
+    boost::optional<gnc_commodity*> m_currency_to;
+    boost::optional<gnc_commodity*> m_symbol_from;
+    bool created = false;
+
+    std::map<GncPricePropType, std::string> m_errors;
+};
+
+#endif

commit f3bc8eea8b12b485be698e1501afb168fc20956d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Nov 19 05:20:35 2017 +0800

    Bugzilla 790526 Correct weeknum calculator
    
    This change will fix 'num-of-weeks-since-1/jan/1970' which formerly used quotient to remove
    the fractional part of the division. For negative values of num-of-weeks, the number is truncated
    in the wrong direction (i.e. towards 0). This change uses floor instead to ensure the num-of-weeks
    found is the nearest integer LESS than the fractional number.

diff --git a/src/app-utils/date-utilities.scm b/src/app-utils/date-utilities.scm
index 03faa8f..72eb0f3 100644
--- a/src/app-utils/date-utilities.scm
+++ b/src/app-utils/date-utilities.scm
@@ -198,7 +198,7 @@
   (/ (- (/ (/ caltime 3600.0) 24) 3) 7))
 
 (define (gnc:date-to-week caltime)
-  (quotient (- (quotient caltime 86400) 3) 7))
+  (floor (/ (- (/ caltime 86400) 3) 7)))
 
 ;; convert a date in seconds since 1970 into # of days since Feb 28, 1970
 ;; ignoring leap-seconds



Summary of changes:
 AUTHORS                                            |    1 +
 CMakeLists.txt                                     |   75 +-
 ChangeLog => ChangeLog.2017                        |  886 +++++-
 Makefile.am                                        |   20 +-
 NEWS                                               |  119 +
 README                                             |    3 +-
 README.dependencies                                |    2 +-
 bindings/python/tests/Makefile.am                  |    7 +-
 bindings/python/tests/runTests.py.in               |    2 +-
 borrowed/CMakeLists.txt                            |    2 +-
 cmake/CMakeLists.txt                               |   27 -
 common/CMakeLists.txt                              |    3 +-
 common/Makefile.am                                 |    2 -
 common/base-typemaps.i                             |   59 +-
 common/cmake_modules/CMakeLists.txt                |    4 +-
 common/cmake_modules/GncAddGSchemaTargets.cmake    |   40 +-
 common/cmake_modules/GncAddSchemeTargets.cmake     |   84 +-
 common/cmake_modules/GncAddTest.cmake              |    4 +-
 common/cmake_modules/GncConfigure.cmake            |   44 -
 common/guile-mappings.h                            |    9 -
 common/swig-utf8.patch                             |   60 -
 common/test-core/CMakeLists.txt                    |   10 +
 common/test-core/Makefile.am                       |    9 -
 common/test-core/test-stuff.h                      |    7 +-
 common/test-core/unittest-support.c                |   44 +
 common/test-core/unittest-support.h                |   20 +
 common/test-core/unittest-support.scm              |    8 +-
 configure.ac                                       |   69 +-
 doc/Makefile.am                                    |   28 +-
 doc/README.build-system                            |   10 +-
 doc/gnucash.1.in                                   |    2 +-
 gnucash/CMakeLists.txt                             |   32 +-
 gnucash/Makefile.am                                |    8 +-
 gnucash/environment.in                             |   10 +-
 gnucash/gnome-utils/Makefile.am                    |    9 -
 gnucash/gnome-utils/dialog-preferences.c           |   13 +-
 gnucash/gnome-utils/dialog-preferences.h           |    4 +-
 gnucash/gnome-utils/gnc-cell-renderer-date.c       |    3 +-
 gnucash/gnome-utils/gnc-file.c                     |    6 -
 gnucash/gnome-utils/gnc-icons.h                    |    2 +-
 gnucash/gnome-utils/gnc-main-window.c              |   16 +-
 gnucash/gnome-utils/gnc-tree-view-account.c        |   16 +-
 gnucash/gnome-utils/gnc-tree-view-account.h        |   15 +-
 gnucash/gnome-utils/gnc-tree-view.c                |   72 +
 gnucash/gnome-utils/gnc-tree-view.h                |   14 +
 gnucash/gnome-utils/gnome-utils.scm                |    7 +-
 gnucash/gnome-utils/gschemas/CMakeLists.txt        |    4 +-
 .../gtkbuilder/dialog-preferences.glade            |    1 +
 gnucash/gnome/Makefile.am                          |    7 -
 gnucash/gnome/assistant-loan.c                     |   13 +-
 gnucash/gnome/dialog-fincalc.c                     |    6 +-
 gnucash/gnome/dialog-fincalc.h                     |    2 +-
 gnucash/gnome/dialog-invoice.c                     |    8 +
 gnucash/gnome/dialog-invoice.h                     |    1 +
 gnucash/gnome/dialog-sx-editor.c                   |   19 +-
 gnucash/gnome/dialog-sx-editor.h                   |    5 +-
 gnucash/gnome/dialog-sx-editor2.c                  |   19 +-
 gnucash/gnome/dialog-sx-editor2.h                  |    4 +-
 gnucash/gnome/dialog-sx-from-trans.c               |    7 +-
 gnucash/gnome/dialog-sx-from-trans.h               |    2 +-
 gnucash/gnome/dialog-trans-assoc.c                 |   10 +-
 gnucash/gnome/dialog-trans-assoc.h                 |    2 +-
 gnucash/gnome/gnc-budget-view.c                    |   11 +
 gnucash/gnome/gnc-budget-view.h                    |    1 +
 gnucash/gnome/gnc-plugin-account-tree.c            |   40 +
 gnucash/gnome/gnc-plugin-basic-commands.c          |   12 +-
 gnucash/gnome/gnc-plugin-budget.c                  |   57 +-
 gnucash/gnome/gnc-plugin-budget.h                  |    2 +-
 gnucash/gnome/gnc-plugin-business.c                |   40 +-
 gnucash/gnome/gnc-plugin-page-account-tree.c       |   41 +
 gnucash/gnome/gnc-plugin-page-account-tree.h       |    9 +
 gnucash/gnome/gnc-plugin-page-budget.c             |   32 +-
 gnucash/gnome/gnc-plugin-page-budget.h             |   10 +
 gnucash/gnome/gnc-plugin-page-invoice.c            |   75 +-
 gnucash/gnome/gnc-plugin-page-owner-tree.c         |   50 +
 gnucash/gnome/gnc-plugin-page-register.c           |   44 +-
 gnucash/gnome/gnc-plugin-page-register.h           |   10 +
 gnucash/gnome/gnc-plugin-page-register2.c          |    6 +-
 gnucash/gnome/gnc-plugin-page-sx-list.c            |   74 +-
 gnucash/gnome/gnc-plugin-register.c                |   24 +
 gnucash/gnome/gnc-split-reg.c                      |   73 +-
 gnucash/gnome/gnc-split-reg.h                      |    5 +
 gnucash/gnome/gschemas/CMakeLists.txt              |    7 +-
 gnucash/gnome/gtkbuilder/dialog-price.glade        |    1 -
 gnucash/gnome/gtkbuilder/dialog-sx.glade           |    1 -
 gnucash/gnome/gtkbuilder/dialog-trans-assoc.glade  |    1 -
 .../gnome/gtkbuilder/gnc-plugin-page-budget.glade  |    3 -
 gnucash/gnucash-valgrind.in                        |    4 +-
 gnucash/html/Makefile.am                           |    7 -
 gnucash/html/gnc-html-webkit2.c                    |   86 +-
 gnucash/import-export/aqb/gnc-gwen-gui.c           |    4 +-
 gnucash/import-export/aqb/gschemas/CMakeLists.txt  |    7 +-
 .../import-export/csv-exp/gschemas/CMakeLists.txt  |    4 +-
 gnucash/import-export/csv-imp/CMakeLists.txt       |   16 +-
 gnucash/import-export/csv-imp/Makefile.am          |   15 +-
 ...s-import.cpp => assistant-csv-price-import.cpp} | 1143 ++++----
 ...port.glade => assistant-csv-price-import.glade} |  429 ++-
 ...trans-import.h => assistant-csv-price-import.h} |   20 +-
 .../csv-imp/assistant-csv-trans-import.cpp         |   27 +-
 ...ns-settings.cpp => gnc-csv-import-settings.cpp} |  247 +-
 ...ns-settings.hpp => gnc-csv-import-settings.hpp} |   67 +-
 .../csv-imp/gnc-csv-price-import-settings.cpp      |  258 ++
 .../csv-imp/gnc-csv-price-import-settings.hpp      |   79 +
 .../csv-imp/gnc-csv-trans-import-settings.cpp      |  262 ++
 .../csv-imp/gnc-csv-trans-import-settings.hpp      |   79 +
 .../csv-imp/gnc-plugin-csv-import-ui.xml           |    1 +
 .../import-export/csv-imp/gnc-plugin-csv-import.c  |   14 +
 gnucash/import-export/csv-imp/gnc-price-import.cpp |  757 +++++
 gnucash/import-export/csv-imp/gnc-price-import.hpp |  173 ++
 gnucash/import-export/csv-imp/gnc-price-props.cpp  |  349 +++
 gnucash/import-export/csv-imp/gnc-price-props.hpp  |  116 +
 gnucash/import-export/csv-imp/gnc-tx-import.cpp    |    6 +-
 gnucash/import-export/csv-imp/gnc-tx-import.hpp    |    8 +-
 .../import-export/csv-imp/gschemas/CMakeLists.txt  |    4 +-
 gnucash/import-export/gschemas/CMakeLists.txt      |    4 +-
 gnucash/import-export/ofx/gschemas/CMakeLists.txt  |    4 +-
 gnucash/import-export/qif-imp/Makefile.am          |    5 +-
 .../import-export/qif-imp/gschemas/CMakeLists.txt  |    4 +-
 gnucash/import-export/qif-imp/qif-import.scm       |   10 +-
 .../register/ledger-core/split-register-control.c  |   11 +
 .../register/ledger-core/split-register-model.c    |  106 +-
 gnucash/register/ledger-core/split-register.c      |   27 +
 gnucash/register/ledger-core/split-register.h      |    2 +
 gnucash/register/register-gnome/gnucash-header.c   |   38 +-
 gnucash/register/register-gnome/gnucash-register.c |    4 -
 gnucash/register/register-gnome/gnucash-sheet.c    |   16 +-
 gnucash/register/register-gnome/gnucash-sheet.h    |    2 +
 gnucash/register/register-gnome/gnucash-style.c    |    4 +-
 gnucash/report/business-reports/Makefile.am        |    2 -
 .../report/business-reports/balsheet-eg.eguile.scm |    2 +-
 gnucash/report/business-reports/balsheet-eg.scm    |    5 +-
 gnucash/report/business-reports/owner-report.scm   |    3 +-
 gnucash/report/business-reports/receipt.scm        |    7 +-
 gnucash/report/business-reports/taxinvoice.scm     |    7 +-
 gnucash/report/locale-specific/us/Makefile.am      |    2 -
 gnucash/report/locale-specific/us/taxtxf.scm       |   11 +-
 gnucash/report/report-gnome/Makefile.am            |    9 -
 .../report-gnome/dialog-report-column-view.c       |   10 +-
 .../report/report-gnome/gnc-plugin-page-report.c   |   60 +-
 gnucash/report/report-gnome/report-gnome.scm       |    7 +-
 .../test/test-load-report-gnome-module.scm         |    3 -
 gnucash/report/report-system/CMakeLists.txt        |   27 +-
 gnucash/report/report-system/Makefile.am           |    9 -
 .../report/report-system/commodity-utilities.scm   |   14 +-
 gnucash/report/report-system/eguile-gnc.scm        |    5 +-
 gnucash/report/report-system/eguile-utilities.scm  |    7 -
 gnucash/report/report-system/html-barchart.scm     |    2 -
 gnucash/report/report-system/html-linechart.scm    |    2 -
 gnucash/report/report-system/html-piechart.scm     |    4 +-
 gnucash/report/report-system/html-scatter.scm      |    4 +-
 gnucash/report/report-system/report-system.scm     |    2 +-
 gnucash/report/report-system/report-utilities.scm  |   40 +-
 gnucash/report/report-system/report.scm            |    8 +-
 .../test/test-load-report-system-module.scm        |    2 -
 gnucash/report/standard-reports/Makefile.am        |    2 -
 .../report/standard-reports/advanced-portfolio.scm |    8 +-
 gnucash/report/standard-reports/cash-flow.scm      |   16 +-
 .../report/standard-reports/category-barchart.scm  |    8 +-
 gnucash/report/standard-reports/net-barchart.scm   |    4 +-
 gnucash/report/standard-reports/sx-summary.scm     |    2 +-
 .../standard-reports/test/test-cash-flow.scm       |   44 +-
 .../test/test-cashflow-barchart.scm                |   38 +-
 .../test/test-generic-net-barchart.scm             |   79 +-
 .../test/test-generic-net-linechart.scm            |   14 +-
 .../test/test-standard-net-linechart.scm           |    8 +-
 gnucash/report/standard-reports/transaction.scm    | 3053 +++++++++++---------
 gnucash/report/stylesheets/Makefile.am             |    2 -
 gnucash/report/utility-reports/Makefile.am         |    2 -
 gnucash/report/utility-reports/hello-world.scm     |    7 +-
 gnucash/report/utility-reports/test-graphing.scm   |    1 -
 gnucash/report/utility-reports/view-column.scm     |    8 +-
 .../report/utility-reports/welcome-to-gnucash.scm  |    7 +-
 libgnucash/app-utils/Makefile.am                   |   10 +-
 libgnucash/app-utils/app-utils.scm                 |   19 +-
 libgnucash/app-utils/c-interface.scm               |   80 +-
 libgnucash/app-utils/gettext.scm                   |   15 +-
 libgnucash/app-utils/gfec.c                        |  189 +-
 libgnucash/app-utils/gnc-euro.c                    |    2 +-
 libgnucash/app-utils/gnc-exp-parser.c              |    2 +-
 libgnucash/app-utils/guile-util.c                  |    4 +-
 .../app-utils/make-prefs-migration-script.xsl      |   12 +-
 libgnucash/app-utils/options.scm                   |   71 +-
 libgnucash/app-utils/test/CMakeLists.txt           |   19 +-
 libgnucash/app-utils/test/Makefile.am              |    7 +-
 .../test/test-c-interface.scm}                     |   41 +-
 libgnucash/app-utils/test/test-date-utilities.scm  |   38 +
 libgnucash/app-utils/test/test-exp-parser.c        |    6 +-
 .../app-utils/test/test-load-app-utils-module.scm  |   14 +-
 libgnucash/app-utils/test/test-option-util.cpp     |   10 +-
 .../app-utils/test/test-scm-query-string.cpp       |    5 -
 libgnucash/backend/dbi/gnc-backend-dbi.cpp         |    7 +-
 .../backend/dbi/test/test-backend-dbi-basic.cpp    |   14 +-
 libgnucash/backend/sql/gnc-price-sql.cpp           |    2 +-
 libgnucash/backend/sql/gnc-slots-sql.cpp           |   71 +-
 libgnucash/backend/xml/io-gncxml-v1.cpp            |    2 +-
 libgnucash/backend/xml/sixtp-dom-generators.cpp    |    4 +-
 libgnucash/backend/xml/sixtp-dom-parsers.cpp       |    2 +-
 .../backend/xml/test/test-date-converting.cpp      |    2 +-
 .../backend/xml/test/test-dom-converters1.cpp      |    2 +-
 libgnucash/backend/xml/test/test-kvp-frames.cpp    |    6 +-
 .../backend/xml/test/test-load-example-account.cpp |    2 +-
 libgnucash/backend/xml/test/test-load-xml2.cpp     |    2 +-
 libgnucash/backend/xml/test/test-save-in-lang.cpp  |    2 +-
 .../backend/xml/test/test-string-converters.cpp    |    2 +-
 libgnucash/backend/xml/test/test-xml-account.cpp   |    2 +-
 libgnucash/backend/xml/test/test-xml-commodity.cpp |    2 +-
 libgnucash/backend/xml/test/test-xml-pricedb.cpp   |    2 +-
 .../backend/xml/test/test-xml-transaction.cpp      |    3 +-
 libgnucash/backend/xml/test/test-xml2-is-file.cpp  |    2 +-
 libgnucash/core-utils/CMakeLists.txt               |    2 +-
 libgnucash/core-utils/Makefile.am                  |   26 +-
 libgnucash/core-utils/core-utils.scm               |    8 +-
 libgnucash/core-utils/gnc-glib-utils.h             |    8 +
 libgnucash/core-utils/gnc-guile-utils.c            |   23 +-
 libgnucash/core-utils/gncla-dir.h.in               |   12 +-
 libgnucash/doc/Makefile.am                         |    6 +-
 libgnucash/doc/doxygen.cfg.in                      |    8 +-
 libgnucash/engine/{Account.c => Account.cpp}       | 1339 ++++-----
 libgnucash/engine/Account.h                        |   12 +-
 libgnucash/engine/AccountP.h                       |    7 +
 libgnucash/engine/CMakeLists.txt                   |    3 +-
 libgnucash/engine/Makefile.am                      |   11 +-
 libgnucash/engine/Scrub.c                          |   14 +-
 libgnucash/engine/Split.c                          |   75 +-
 libgnucash/engine/Split.h                          |    8 +
 libgnucash/engine/Transaction.c                    |  128 +-
 libgnucash/engine/Transaction.h                    |    8 +
 libgnucash/engine/engine-helpers-guile.h           |    3 -
 libgnucash/engine/engine-helpers.c                 |   97 +-
 libgnucash/engine/engine.i                         |    2 +
 libgnucash/engine/engine.scm                       |   13 +-
 libgnucash/engine/gnc-aqbanking-templates.cpp      |   22 +-
 libgnucash/engine/gnc-budget.c                     |   42 +-
 libgnucash/engine/gnc-commodity.c                  |   12 +-
 libgnucash/engine/gnc-commodity.h                  |    8 +
 libgnucash/engine/gnc-datetime.cpp                 |    9 +-
 libgnucash/engine/gnc-engine.h                     |    7 +
 libgnucash/engine/gnc-features.c                   |   28 +
 libgnucash/engine/gnc-features.h                   |   14 +
 libgnucash/engine/gnc-lot.c                        |   26 +-
 libgnucash/engine/gnc-lot.h                        |    9 +
 libgnucash/engine/gnc-numeric.scm                  |   22 +-
 libgnucash/engine/gnc-pricedb.h                    |    8 +
 libgnucash/engine/gnc-timezone.cpp                 |    5 +-
 libgnucash/engine/gncCustomer.c                    |   30 +-
 libgnucash/engine/gncEmployee.c                    |   37 +-
 libgnucash/engine/gncInvoice.c                     |    8 +-
 libgnucash/engine/gncJob.c                         |   12 +-
 libgnucash/engine/gncVendor.c                      |   30 +-
 libgnucash/engine/guid.cpp                         |   21 +-
 libgnucash/engine/guid.hpp                         |    1 +
 libgnucash/engine/kvp-frame.cpp                    |  241 +-
 libgnucash/engine/kvp-frame.hpp                    |  127 +-
 libgnucash/engine/kvp-scm.cpp                      |   23 +-
 libgnucash/engine/kvp-value.cpp                    |   45 +-
 libgnucash/engine/kvp-value.hpp                    |    1 +
 libgnucash/engine/policy.h                         |    8 +
 libgnucash/engine/qof-backend.hpp                  |    2 +-
 libgnucash/engine/qof-string-cache.cpp             |   17 +-
 libgnucash/engine/qof-string-cache.h               |   10 +-
 libgnucash/engine/qofbook.cpp                      |  171 +-
 libgnucash/engine/qofinstance-p.h                  |   54 +-
 libgnucash/engine/qofinstance.cpp                  |  140 +-
 libgnucash/engine/qofsession.cpp                   |    8 +-
 libgnucash/engine/test-core/test-engine-stuff.cpp  |    4 +-
 libgnucash/engine/test/gtest-gnc-datetime.cpp      |    7 +-
 libgnucash/engine/test/gtest-gnc-timezone.cpp      |   25 +
 libgnucash/engine/test/gtest-import-map.cpp        |  151 +-
 libgnucash/engine/test/test-account-object.cpp     |    4 +-
 libgnucash/engine/test/test-extras.scm             |    8 +-
 libgnucash/engine/test/test-kvp-frame.cpp          |   92 +-
 libgnucash/engine/test/test-qofsession.cpp         |    9 +-
 libgnucash/engine/test/test-split.scm              |    4 +-
 libgnucash/engine/test/utest-Account.cpp           |   36 +-
 libgnucash/engine/test/utest-Split.cpp             |   29 +-
 libgnucash/engine/test/utest-Transaction.cpp       |   36 +-
 libgnucash/gnc-module/Makefile.am                  |    9 -
 libgnucash/gnc-module/gnc-module.scm               |   16 +-
 libgnucash/gnc-module/test/mod-bar/Makefile.am     |    7 -
 libgnucash/gnc-module/test/mod-baz/Makefile.am     |    7 -
 libgnucash/gnc-module/test/mod-foo/Makefile.am     |    7 -
 libgnucash/quotes/CMakeLists.txt                   |   25 +-
 libgnucash/quotes/Makefile.am                      |   16 +-
 libgnucash/quotes/gnc-fq-check.in                  |    2 +-
 libgnucash/quotes/gnc-fq-helper.in                 |    2 +-
 libgnucash/quotes/gnc-fq-update.in                 |    2 +-
 libgnucash/scm/CMakeLists.txt                      |   15 +-
 libgnucash/scm/Makefile.am                         |   10 +-
 libgnucash/scm/build-config.scm.in                 |    4 +-
 libgnucash/scm/gnumeric/Makefile.am                |    2 -
 libgnucash/scm/main.scm                            |   41 -
 libgnucash/tax/us/Makefile.am                      |    2 -
 libgnucash/tax/us/de_DE.scm                        |    7 +-
 make-gnucash-potfiles.in                           |   10 +-
 po/POTFILES.in                                     |   10 +-
 295 files changed, 8966 insertions(+), 5921 deletions(-)
 rename ChangeLog => ChangeLog.2017 (89%)
 delete mode 100644 common/cmake_modules/GncConfigure.cmake
 delete mode 100644 common/swig-utf8.patch
 copy gnucash/import-export/csv-imp/{assistant-csv-trans-import.cpp => assistant-csv-price-import.cpp} (62%)
 copy gnucash/import-export/csv-imp/{assistant-csv-trans-import.glade => assistant-csv-price-import.glade} (83%)
 copy gnucash/import-export/csv-imp/{assistant-csv-trans-import.h => assistant-csv-price-import.h} (73%)
 rename gnucash/import-export/csv-imp/{gnc-csv-trans-settings.cpp => gnc-csv-import-settings.cpp} (53%)
 rename gnucash/import-export/csv-imp/{gnc-csv-trans-settings.hpp => gnc-csv-import-settings.hpp} (69%)
 create mode 100644 gnucash/import-export/csv-imp/gnc-csv-price-import-settings.cpp
 create mode 100644 gnucash/import-export/csv-imp/gnc-csv-price-import-settings.hpp
 create mode 100644 gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.cpp
 create mode 100644 gnucash/import-export/csv-imp/gnc-csv-trans-import-settings.hpp
 create mode 100644 gnucash/import-export/csv-imp/gnc-price-import.cpp
 create mode 100644 gnucash/import-export/csv-imp/gnc-price-import.hpp
 create mode 100644 gnucash/import-export/csv-imp/gnc-price-props.cpp
 create mode 100644 gnucash/import-export/csv-imp/gnc-price-props.hpp
 copy libgnucash/{engine/test/test-test-extras.scm => app-utils/test/test-c-interface.scm} (50%)
 create mode 100644 libgnucash/app-utils/test/test-date-utilities.scm
 rename libgnucash/engine/{Account.c => Account.cpp} (83%)



More information about the gnucash-changes mailing list