gnucash master: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Thu Mar 31 15:28:23 EDT 2022


Updated	 via  https://github.com/Gnucash/gnucash/commit/1fddf70e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/abd1a0b3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4fe83c3b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f4bfd9df (commit)
	 via  https://github.com/Gnucash/gnucash/commit/278aa484 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5457d4a9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/30e6c8ab (commit)
	 via  https://github.com/Gnucash/gnucash/commit/572cb6b1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d4c3c30b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a7a643f7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/65bd8602 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6841e5b5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/11225d97 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/16dc1596 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/759376eb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/18edc175 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b5d0c425 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a700701c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/eb0bd4f9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a3f50586 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6cd88c23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e9b850cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/66f9fc81 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0ce841d4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/216b483e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/947c0619 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f26014a0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/24d2999a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/51cc2668 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/26924937 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6f93a68b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c3b8b6cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5e84f118 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8eddb63e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/19a2dd49 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/98049280 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e17ee38c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a3a381cf (commit)
	 via  https://github.com/Gnucash/gnucash/commit/dd7feb99 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d1fe359e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/74fd716a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f6c9e63e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4c43dac1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cf5da9ff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/96b09ded (commit)
	 via  https://github.com/Gnucash/gnucash/commit/00a982d9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/00c2e99d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bed44f40 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a487ca3f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3935f1a9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/62ab148a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b3f96701 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7392ac6f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b361582c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/10381d42 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/97a317b5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3d1812aa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bbe8ebbc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/42b4755a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c0692462 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/53fac914 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e77b5ec0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/474bc360 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1cd2cf21 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/852b2ffc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e43ff932 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1af97ebb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/86da12d8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a7f0476a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e8f855e6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/86d76371 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8acf52a6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e4c9900c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3c95ad8a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8079470c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c83a3f44 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f0ecc0e2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b6622a38 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/26946d79 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/914f5f35 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/71955cc3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a97b3e0c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/58d090ff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a2e1a3e1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/99eaa81a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0bd9033b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0f02236e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/18997db7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/dd8e8b4e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/05ac0564 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9d150b1a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f5d8d508 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c034a26a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/67dab6b3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a1af86ed (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c34986da (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3f89d063 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6eb5dfed (commit)
	 via  https://github.com/Gnucash/gnucash/commit/29a2365f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7885353f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bbe74aa0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5a2ba091 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7c6ecafd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ce5fe577 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/52ef53a4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7239eb80 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/23461f1d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/12f8df1e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/be322a0d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/27670a6e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a94f69d6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0f8446a1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f66a918b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0a8f66ee (commit)
	 via  https://github.com/Gnucash/gnucash/commit/418eb066 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7d0cdf7c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/86a2f155 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/693e966b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/63502900 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/42185c0e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5c743378 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c04f4a00 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e7309f07 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3aa86ca9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4a305998 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d9984f75 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/43cd81ba (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9bd3baff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/534a7c28 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c62b526b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/efc73464 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f20c358c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b5c04771 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f7f2d229 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/81d26189 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/16a36d91 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7c1b4b79 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d41292fd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a21f329b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8c2a8edb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/86102e1b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/53ad0ba4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/08d1eebb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fba02485 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/55a2ed1d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/18b83874 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0b7ccfbd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a602f64b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/43f4bcb6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7fa6778b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cb7270ca (commit)
	 via  https://github.com/Gnucash/gnucash/commit/76b0001c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e2c87f23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ea835b31 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/472814d3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/023db233 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ae73b385 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e4841601 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7022f522 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/776d1aaa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8c77ce96 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/90b8fce5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/60c1d16e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4f3fd665 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b19f3d38 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/52600bbd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/31a0153f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/85db341a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/21398dfd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7dab089d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d1cfd62f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1f0bfe0c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c6fce317 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/507f35cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8440b9c9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2ccfe0c1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/276d3397 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5f9c66aa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6491c985 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/245a8fcc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c751e561 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/010ab1a9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/79fdb412 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e3b5a7d8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2dd8d782 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7cb27b26 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4a4e5d36 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d8f83d6e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3514725a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e78c0126 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ae79fd01 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/28438e31 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/41e59df7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1eef796f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/99103ffd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8fe338f3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9111f118 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/01c0fe23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/25b717d4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4451f58b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6feb92d4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f9e136db (commit)
	 via  https://github.com/Gnucash/gnucash/commit/eb6e31f8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/67508ea0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3b4785e7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c63db36a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/01061764 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5fd53c94 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/a995343a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/16da3208 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ac0e7063 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3200bd49 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/fce33799 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/00aa0f60 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6c8e0e23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3b78b6e8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d5f6a253 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/93a3716c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/dc876d40 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/102f36c3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6c7f976a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/99c2c5e4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1bea809c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cbf7d70e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/81c5ac66 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/aa246d30 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/20b3ef8a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cd6ccbe3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/691cb099 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cbd0607e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5a82aac6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ffc68664 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d2535fe2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/009219c6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/276641ef (commit)
	 via  https://github.com/Gnucash/gnucash/commit/51a1430c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/76172af2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6ab5618b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d7a2a0ff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/826d75af (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c5294ed6 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7ccba537 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1ea38226 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/98ca1907 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4b997cd0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b2fb57d3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4dcf4a0e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4cabd6c0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e2a36a8b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6df516df (commit)
	 via  https://github.com/Gnucash/gnucash/commit/52d0ec52 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/883127a5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e583c84f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/aaf6b14c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1b00399b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5a9c4cca (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b95ea2c4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7183e7c4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/399573a8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/12c5b944 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3f576671 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2f2ac999 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/252ba9b4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ff7b263a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/39b7c9c7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3dc4bc23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/435667e8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8eedcb6d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d544f852 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/694a15ed (commit)
	 via  https://github.com/Gnucash/gnucash/commit/16d1f065 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cee3cdaf (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9cdcaf0d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/935ce6db (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e51faff3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c5fac51a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3296212a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0a13b4c5 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/4146251c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6ccb9dbb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/94628097 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3769a356 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6deedd44 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/40361ec8 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/41ef2c5d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/083e5b93 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cf0b1da4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2ee0edaa (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d2655d3f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ade7fc8b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f3eee511 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/455d3c2d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/16fd632e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/13b94d23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d88ec0dc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/01dc70cc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/01fcae6a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b6fd8447 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c0ba3e27 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b495da4e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0b77641e (commit)
	from  https://github.com/Gnucash/gnucash/commit/73ddaa8c (commit)



commit 1fddf70e214a62c3eccece76a02fe5b3d3a0c1d9
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 31 12:27:58 2022 -0700

    [C++options] Fix previous month and previous quarter at the end of March
    .
    
    Because March has more days than February the previous month offset was
    getting normalized back to the current month--th 29 February this
    year is really 1 March, so normalizing before setting the day caused
    begin/end previous month to return the begin/end of the current month.
    That probably happened on the 31st of May, July, October, and December
    as well, I just hadn't managed to test on those days. Switching the
    normalization to after calculating the day of the month broke the
    previous quarter calculation because now the month was out of range, so
    normalize month & year first.

diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp
index 3d2a3e141..2d43f732c 100644
--- a/libgnucash/app-utils/gnc-option-date.cpp
+++ b/libgnucash/app-utils/gnc-option-date.cpp
@@ -481,7 +481,11 @@ reldate_set_day_and_time(struct tm& now, RelativeDateType type)
     }
     else if (type == RelativeDateType::END)
     {
-        now.tm_mday = gnc_date_get_last_mday(now.tm_mon, now.tm_year + 1900);
+        /* Ensure that the month is between 0 and 12*/
+        auto year_delta = now.tm_mon / 12 + now.tm_mon < 0 ? -1 : 0;
+        auto month = now.tm_mon - 12 * year_delta;
+        auto year = now.tm_year + year_delta + 1900;
+        now.tm_mday = gnc_date_get_last_mday(month, year);
         gnc_tm_set_day_end(&now);
     }
     // Do nothing for LAST and NEXT.
@@ -559,8 +563,8 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
             else if (reldate_is_next(period))
                 now.tm_mday += 7;
     }
-    normalize_reldate_tm(now);
     reldate_set_day_and_time(now, checked_reldate(period).m_type);
+    normalize_reldate_tm(now);
     return static_cast<time64>(GncDateTime(now));
 }
 

commit abd1a0b3f16e47e94236c4e9064e9e5f0fb76ba0
Merge: 73ddaa8c4 4fe83c3b3
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 29 15:58:22 2022 -0700

    Merge branch 'c++options'

diff --cc gnucash/gnome/CMakeLists.txt
index ade0f0ce4,d48762487..2aafc0530
--- a/gnucash/gnome/CMakeLists.txt
+++ b/gnucash/gnome/CMakeLists.txt
@@@ -27,9 -27,10 +27,9 @@@ set (gnc_gnome_noinst_HEADER
    dialog-payment.h
    dialog-print-check.h
    dialog-progress.h
-   dialog-report-column-view.h
+   dialog-report-column-view.hpp
    dialog-report-style-sheet.h
    dialog-sx-editor.h
 -  dialog-sx-editor2.h
    dialog-sx-from-trans.h
    dialog-sx-since-last-run.h
    dialog-vendor.h
@@@ -64,11 -69,10 +64,11 @@@ gnc_add_swig_guile_command (swig-gnome-
  
  set (gnc_gnome_SOURCES
    assistant-acct-period.c
-   assistant-hierarchy.c
+   assistant-hierarchy.cpp
    assistant-loan.cpp
    assistant-stock-split.c
 +  assistant-stock-transaction.cpp
-   business-options-gnome.c
+   business-options-gnome.cpp
    business-urls.c
    business-gnome-utils.c
    dialog-doclink.c
@@@ -93,9 -98,10 +93,9 @@@
    dialog-price-edit-db.c
    dialog-print-check.c
    dialog-progress.c
-   dialog-report-column-view.c
-   dialog-report-style-sheet.c
+   dialog-report-column-view.cpp
+   dialog-report-style-sheet.cpp
    dialog-sx-editor.c
 -  dialog-sx-editor2.c
    dialog-sx-from-trans.c
    dialog-sx-since-last-run.c
    dialog-tax-info.c
@@@ -112,14 -119,17 +112,14 @@@
    gnc-plugin-page-invoice.c
    gnc-plugin-page-owner-tree.c
    gnc-plugin-page-register.c
-   gnc-plugin-page-report.c
 -  gnc-plugin-page-register2.c
+   gnc-plugin-page-report.cpp
    gnc-plugin-page-sx-list.c
    gnc-split-reg.c
 -  gnc-split-reg2.c
    reconcile-view.c
    search-owner.c
    top-level.c
    window-reconcile.c
-   window-report.c
 -  window-reconcile2.c
+   window-report.cpp
    window-autoclear.c
  )
  
diff --cc libgnucash/engine/qofbook.cpp
index 1cbc673eb,22dd2fb06..e03fda12e
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@@ -1255,29 -1135,8 +1135,29 @@@ qof_book_set_feature (QofBook *book, co
      }
  }
  
 +
 +void
 +qof_book_unset_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});
 +    if (feature_slot)
 +    {
 +        auto feature_frame = feature_slot->get<KvpFrame*>();
 +        feature = feature_frame->get_slot({key});
 +    }
 +    if (feature == nullptr || g_strcmp0 (feature->get<const char*>(), descr))
 +    {
 +        qof_book_begin_edit (book);
 +        delete frame->set_path({GNC_FEATURES, key}, nullptr);
 +        qof_instance_set_dirty (QOF_INSTANCE (book));
 +        qof_book_commit_edit (book);
 +    }
 +}
 +
  void
- qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb)
+ qof_book_load_options (QofBook *book, GncOptionLoad load_cb, GncOptionDB *odb)
  {
      load_cb (odb, book);
  }
diff --cc po/POTFILES.in
index 8cd8b2450,9a0b099f1..70e024f22
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@@ -48,12 -48,11 +48,12 @@@ borrowed/libc/setenv.
  borrowed/libc/strptime.c
  doc/tip_of_the_day.list.c
  gnucash/gnome/assistant-acct-period.c
- gnucash/gnome/assistant-hierarchy.c
+ gnucash/gnome/assistant-hierarchy.cpp
  gnucash/gnome/assistant-loan.cpp
  gnucash/gnome/assistant-stock-split.c
 +gnucash/gnome/assistant-stock-transaction.cpp
  gnucash/gnome/business-gnome-utils.c
- gnucash/gnome/business-options-gnome.c
+ gnucash/gnome/business-options-gnome.cpp
  gnucash/gnome/business-urls.c
  gnucash/gnome/dialog-billterms.c
  gnucash/gnome/dialog-choose-owner.c
@@@ -77,8 -77,9 +77,8 @@@ gnucash/gnome/dialog-price-edit-db.
  gnucash/gnome/dialog-price-editor.c
  gnucash/gnome/dialog-print-check.c
  gnucash/gnome/dialog-progress.c
- gnucash/gnome/dialog-report-column-view.c
- gnucash/gnome/dialog-report-style-sheet.c
+ gnucash/gnome/dialog-report-column-view.cpp
+ gnucash/gnome/dialog-report-style-sheet.cpp
 -gnucash/gnome/dialog-sx-editor2.c
  gnucash/gnome/dialog-sx-editor.c
  gnucash/gnome/dialog-sx-from-trans.c
  gnucash/gnome/dialog-sx-since-last-run.c
@@@ -93,11 -94,14 +93,11 @@@ gnucash/gnome/gnc-plugin-page-account-t
  gnucash/gnome/gnc-plugin-page-budget.c
  gnucash/gnome/gnc-plugin-page-invoice.c
  gnucash/gnome/gnc-plugin-page-owner-tree.c
 -gnucash/gnome/gnc-plugin-page-register2.c
  gnucash/gnome/gnc-plugin-page-register.c
- gnucash/gnome/gnc-plugin-page-report.c
+ gnucash/gnome/gnc-plugin-page-report.cpp
  gnucash/gnome/gnc-plugin-page-sx-list.c
 -gnucash/gnome/gnc-plugin-register2.c
  gnucash/gnome/gnc-plugin-register.c
  gnucash/gnome/gnc-plugin-report-system.c
 -gnucash/gnome/gnc-split-reg2.c
  gnucash/gnome/gnc-split-reg.c
  gnucash/gnome/gnucash.appdata.xml.in.in
  gnucash/gnome/gnucash.desktop.in.in
@@@ -106,8 -110,9 +106,8 @@@ gnucash/gnome/report-menus.sc
  gnucash/gnome/search-owner.c
  gnucash/gnome/top-level.c
  gnucash/gnome/window-autoclear.c
 -gnucash/gnome/window-reconcile2.c
  gnucash/gnome/window-reconcile.c
- gnucash/gnome/window-report.c
+ gnucash/gnome/window-report.cpp
  gnucash/gnome-search/dialog-search.c
  gnucash/gnome-search/gnc-general-search.c
  gnucash/gnome-search/search-account.c

commit 4fe83c3b32884ab82dcccd24a221246316c817cb
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Mar 21 14:08:06 2022 -0700

    [C++options] Remove GncOptionValue<SCM>.
    
    No longer needed.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 7c4e795fc..5a6f0e9c7 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -725,35 +725,6 @@ GncOptionValue<ValueType>::deserialize(const std::string& str) noexcept
     return true;
 }
 
-template <> void
-GncOptionValue<SCM>::set_value(SCM new_value)
-{
-    if (m_value)
-        scm_gc_unprotect_object(m_value);
-    m_value = new_value;
-    scm_gc_protect_object(m_value);
-}
-
-template <> void
-GncOptionValue<SCM>::set_default_value(SCM new_value)
-{
-    if (m_value)
-        scm_gc_unprotect_object(m_value);
-    if (m_default_value)
-        scm_gc_unprotect_object(m_default_value);
-    m_value = m_default_value = new_value;
-    scm_gc_protect_object(m_value);
-    scm_gc_protect_object(m_default_value);
-}
-
-template <> void
-GncOptionValue<SCM>::reset_default_value()
-{
-    if (m_value)
-        scm_gc_unprotect_object(m_value);
-    m_value = m_default_value;
-    scm_gc_protect_object(m_value);
-}
 std::string
 GncOptionAccountListValue::serialize() const noexcept
 {
@@ -947,7 +918,6 @@ template GncOptionValue<size_t>::GncOptionValue(const GncOptionValue<size_t>&);
 template GncOptionValue<GncOptionAccountList>::GncOptionValue(const GncOptionValue<GncOptionAccountList>&);
 template GncOptionValue<GncMultichoiceOptionIndexVec>::GncOptionValue(const GncOptionValue<GncMultichoiceOptionIndexVec>&);
 template GncOptionValue<GncOptionReportPlacementVec>::GncOptionValue(const GncOptionValue<GncOptionReportPlacementVec>&);
-template GncOptionValue<SCM>::GncOptionValue(const GncOptionValue<SCM>&);
 template void GncOptionValue<bool>::set_value(bool);
 template void GncOptionValue<int>::set_value(int);
 template void GncOptionValue<int64_t>::set_value(int64_t);
@@ -999,7 +969,6 @@ template std::string GncOptionValue<const char*>::serialize() const noexcept;
 template std::string GncOptionValue<std::string>::serialize() const noexcept;
 template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
 template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
-template std::string GncOptionValue<SCM>::serialize() const noexcept;
 template std::string GncOptionValue<GncOptionReportPlacementVec>::serialize() const noexcept;
 template std::string GncOptionRangeValue<int>::serialize() const noexcept;
 template std::string GncOptionRangeValue<double>::serialize() const noexcept;
@@ -1012,7 +981,6 @@ template bool GncOptionValue<const char*>::deserialize(const std::string&) noexc
 template bool GncOptionValue<std::string>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
-template bool GncOptionValue<SCM>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<GncOptionReportPlacementVec>::deserialize(const std::string&) noexcept;
 template bool GncOptionRangeValue<int>::deserialize(const std::string&) noexcept;
 template bool GncOptionRangeValue<double>::deserialize(const std::string&) noexcept;
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 6bd82dc54..d126e3ea7 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -93,38 +93,16 @@ public:
                               ValueType value,
                               GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
         OptionClassifier{section, name, key, doc_string},
-        m_ui_type(ui_type), m_value{value}, m_default_value{value} {
-            if constexpr(std::is_same_v<std::decay_t<ValueType>, SCM>) {
-                if (value) {
-                    scm_gc_protect_object(m_value);
-                    scm_gc_protect_object(m_default_value);
-                }
-            }
-        }
+        m_ui_type(ui_type), m_value{value}, m_default_value{value} { }
     GncOptionValue<ValueType>(const GncOptionValue<ValueType>& from) :
         OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
                          from.m_doc_string},
         m_ui_type(from.get_ui_type()), m_value{from.get_value()},
-        m_default_value{from.get_default_value()}
-    {
-        if constexpr(std::is_same_v<std::decay_t<ValueType>, SCM>) {
-            if (m_value)
-                scm_gc_protect_object(m_value);
-            if (m_default_value)
-                scm_gc_protect_object(m_default_value);
-        }
-    }
+        m_default_value{from.get_default_value()}{}
     GncOptionValue<ValueType>(GncOptionValue<ValueType>&&) = default;
     GncOptionValue<ValueType>& operator=(const GncOptionValue<ValueType>&) = default;
     GncOptionValue<ValueType>& operator=(GncOptionValue<ValueType>&&) = default;
-    ~GncOptionValue<ValueType>() {
-            if constexpr(std::is_same_v<std::decay_t<ValueType>, SCM>) {
-                if (m_value)
-                    scm_gc_unprotect_object(m_value);
-                if (m_default_value)
-                    scm_gc_unprotect_object(m_default_value);
-            }
-        }
+    ~GncOptionValue<ValueType>() = default;
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
     void set_value(ValueType new_value);
@@ -297,8 +275,7 @@ template<class OptType,
 std::istream& operator>>(std::istream& iss, OptType& opt)
 {
     std::decay_t<decltype(opt.get_value())> value;
-    if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, SCM> ||
-                  std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _gncOwner*> ||
+    if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _gncOwner*> ||
                   std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _QofQuery*>)
         return iss;
     else
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index e69bc0c02..c113a6db6 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -433,16 +433,6 @@ GncOption::in_stream(std::istream& iss)
                       }, *m_option);
 }
 
-GncOption*
-gnc_make_SCM_option(const char* section, const char* name,
-                    const char* key, const char* doc_string,
-                    SCM value, GncOptionUIType ui_type)
-{
-    return new GncOption(section, name, key, doc_string,
-                         reinterpret_cast<SCM>(value), ui_type);
-}
-
-
 /* We must instantiate all of the templates we need here because we don't expose
  * the template implementation in the public header.
  */
@@ -460,8 +450,6 @@ template GncOption::GncOption(const char*, const char*, const char*,
 //                              const char*, double, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, std::string, GncOptionUIType);
-template GncOption::GncOption(const char*, const char*, const char*,
-                              const char*, SCM, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, const QofQuery*, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
@@ -480,7 +468,6 @@ template const Account* GncOption::get_value<const Account*>() const;
 template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_value<GncMultichoiceOptionIndexVec>() const;
-template SCM GncOption::get_value<SCM>() const;
 template GncOptionReportPlacementVec GncOption::get_value<GncOptionReportPlacementVec>() const;
 
 template bool GncOption::get_default_value<bool>() const;
@@ -495,7 +482,6 @@ template const Account* GncOption::get_default_value<const Account*>() const;
 template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_default_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_default_value<GncMultichoiceOptionIndexVec>() const;
-template SCM GncOption::get_default_value<SCM>() const;
 template GncOptionReportPlacementVec GncOption::get_default_value<GncOptionReportPlacementVec>() const;
 
 template void GncOption::set_value(bool);
@@ -512,7 +498,6 @@ template void GncOption::set_value(RelativeDatePeriod);
 template void GncOption::set_value(size_t);
 template void GncOption::set_value(GncOptionAccountList);
 template void GncOption::set_value(GncMultichoiceOptionIndexVec);
-template void GncOption::set_value(SCM);
 template void GncOption::set_value(GncOptionReportPlacementVec);
 
 template void GncOption::set_default_value(bool);
@@ -528,7 +513,6 @@ template void GncOption::set_default_value(RelativeDatePeriod);
 template void GncOption::set_default_value(size_t);
 template void GncOption::set_default_value(GncOptionAccountList);
 template void GncOption::set_default_value(GncMultichoiceOptionIndexVec);
-template void GncOption::set_default_value(SCM);
 template void GncOption::set_default_value(GncOptionReportPlacementVec);
 
 template void GncOption::get_limits(double&, double&, double&) const noexcept;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index e46fa9216..9cb97acff 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -104,7 +104,6 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionQofInstanceValue,
                                       GncOptionValue<const QofQuery*>,
                                       GncOptionValue<const GncOwner*>,
-                                      GncOptionValue<SCM>,
                                       GncOptionValue<GncOptionReportPlacementVec>,
                                       GncOptionAccountListValue,
                                       GncOptionAccountSelValue,
@@ -256,14 +255,6 @@ gnc_make_option(const char* section, const char* name,
     return new GncOption(section, name, key, doc_string, value, ui_type);
 }
 
-/**
- * Free function wrapping GncOption's constructor using an SCM value.
- * To work around SWIG_Guile's typedef of SCM to unsigned long
- */
-GncOption* gnc_make_SCM_option(const char* section, const char* name,
-                               const char* key, const char* doc_string,
-                               SCM value, GncOptionUIType ui_type);
-
 #endif //GNC_OPTION_HPP_
 
 /** @}
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 9d06fb951..b36a1ba60 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -831,16 +831,6 @@ gnc_register_owner_option(GncOptionDB* db, const char* section,
     db->register_option(section, std::move(option));
 }
 
-void
-gnc_register_internal_option(GncOptionDB* db, const char* section,
-                             const char* name, const char* key,
-                             const char* doc_string, SCM value)
-{
-    GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::INTERNAL};
-    db->register_option(section, std::move(option));
-}
-
 void
 gnc_register_invoice_option(GncOptionDB* db, const char* section,
                             const char* name, const char* key,
@@ -1249,22 +1239,6 @@ gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section,
     return option->get_value<const QofInstance*>();
 }
 
-SCM
-gnc_option_db_lookup_scm_value(GncOptionDB* odb, const char* section,
-                               const char* name)
-{
-    auto option{odb->find_option(section, name)};
-    return option->get_value<SCM>();
-}
-
-void
-gnc_option_db_set_scm_value(GncOptionDB* odb, const char* section,
-                            const char* name, SCM value)
-{
-    auto option{odb->find_option(section, name)};
-    option->set_value(value);
-}
-
 // Force creation of templates
 template void gnc_register_number_range_option(GncOptionDBPtr& db,
                                       const char* section, const char* name,
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index 8078459d5..88d12300d 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -166,31 +166,6 @@ const QofInstance* gnc_option_db_lookup_qofinstance_value(GncOptionDB*,
                                                           const char*,
                                                           const char*);
 
-/**
- * Retrieve the GList* value of an option in the GncOptionDB
- *
- * @param odb the GncOptionDB
- * @param section the section in which the option is stored
- * @param name the option name
- * @return the GList* of the value or nullptr if the option isn't found
- * or if its value isn't a string.
- */
-SCM gnc_option_db_lookup_scm_value(GncOptionDB*, const char*, const char*);
-
-/**
- * Set the GList* value of an option in the GncOptionDB.
- *
- * The value will not be saved if the option is not in the GncOptionDB or if the
- * type of the option isn't string or text.
- *
- * @param odb the GncOptionDB
- * @param section the section in which the option is stored
- * @param name the option name
- * @param value the value to be stored in the option.
- */
-void gnc_option_db_set_scm_value(GncOptionDB*, const char*,
-                                    const char*, SCM);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index d92f14505..25920f4c2 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -571,35 +571,6 @@ inline void gnc_register_color_option(GncOptionDBPtr& db, const char* section,
     gnc_register_color_option(db.get(), section, name, key, doc_string, value);
 }
 
-/**
- * Create a new internal string option and register it in the options database.
- *
- * Internal options are used for passing state data in some reports. As the name
- * suggests they do not create UI elements in options dialogs.
- * @param db A GncOptionDB* for calling from C. Caller retains ownership.
- * @param section The database section for the option.
- * @param name The option name.
- * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
- * @param value The initial and default value for the option.
- */
-void gnc_register_internal_option(GncOptionDB* db, const char* section,
-                                  const char* name, const char* key,
-                                  const char* doc_string, SCM value);
-
-
-/**
- * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
- */
-inline void gnc_register_internal_option(GncOptionDBPtr& db,
-                                         const char* section, const char* name,
-                                         const char* key,
-                                         const char* doc_string,
-                                         SCM value)
-{
-    gnc_register_internal_option(db.get(), section, name, key,
-                                 doc_string, value);
-}
-
 void gnc_register_internal_option(GncOptionDBPtr& db,
                                           const char* section, const char* name,
                                           const char* key,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index bff5ee3d4..8f696e0f0 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -1243,13 +1243,6 @@ inline SCM return_scm_value(ValueType value)
                     return scm_simple_format(SCM_BOOL_F, plain_format_str,
                                              scm_val);
                 }
-                if constexpr (is_same_decayed_v<decltype(option),
-                              GncOptionValue<SCM>>)
-                {
-                    auto scm_val{scm_list_1(return_scm_value(option.get_value()))};
-                    return scm_simple_format(SCM_BOOL_F, ticked_format_str,
-                                             scm_val);
-                }
                if constexpr (is_same_decayed_v<decltype(option),
                               GncOptionValue<GncOptionReportPlacementVec>>)
                 {
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 702fca981..51086d6b2 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -270,8 +270,13 @@
                                                (GncOptionUIType-INTERNAL)))
      ((string? default) (gnc-make-string-option section name key desc default
                                                 (GncOptionUIType-INTERNAL)))
+     ((procedure? default)
+        (format #t "gnc:make-internal-option passed procedure resolving to ~a~%" (default))
+        (gnc-make-bool-option section name key desc #f (GncOptionUIType-INTERNAL)))
      (else
-       (gnc-make-SCM-option section name key desc default type)))))
+       (format #t "gnc:make-internal-option passed something unknown that looks like ~a~%" default)
+       (gnc-make-bool-option section name key desc #f (GncOptionUIType-INTERNAL))))))
+
 (define-public (gnc:make-owner-option section name key docstring getter validator owner-type)
   (issue-deprecation-warning "gnc:make-owner-option is deprecated. Make and register the option in one command with gnc-register-owner-option.")
   (let* ((ui-type (cond

commit f4bfd9df3e43fb331287a76fd74049bf4989ee11
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Dec 14 12:54:13 2021 +0800

    [account-piecharts] don't use gnc:make-internal-option

diff --git a/gnucash/report/reports/standard/account-piecharts.scm b/gnucash/report/reports/standard/account-piecharts.scm
index 75e8f3263..aa21538b1 100644
--- a/gnucash/report/reports/standard/account-piecharts.scm
+++ b/gnucash/report/reports/standard/account-piecharts.scm
@@ -84,15 +84,12 @@ balance at a given time"))
 ;; The option-generator. The only dependence on the type of piechart
 ;; is the list of account types that the account selection option
 ;; accepts.
-(define (options-generator account-types reverse-balance? do-intervals? depth-based?)
+(define (options-generator account-types do-intervals? depth-based?)
   (let* ((options (gnc:new-options)) 
          (add-option 
           (lambda (new-option)
             (gnc:register-option options new-option))))
 
-    (add-option
-     (gnc:make-internal-option "__report" "reverse-balance?" reverse-balance?))
-
     (if do-intervals?
         (gnc:options-add-date-interval!
          options gnc:pagename-general
@@ -305,7 +302,7 @@ balance at a given time"))
 ;; account-types to work on and whether this report works on
 ;; intervals as arguments.
 (define (piechart-renderer report-obj reportname report-guid
-                           account-types do-intervals? depth-based?
+                           account-types do-intervals? depth-based? reverse-balance?
                            display-name sort-comparator get-data)
 
   ;; This is a helper function for looking up option values.
@@ -350,7 +347,6 @@ balance at a given time"))
         (height (get-option gnc:pagename-display optname-plot-height))
         (width (get-option gnc:pagename-display optname-plot-width))
 	(sort-method (get-option gnc:pagename-display optname-sort-method))
-	(reverse-balance? (get-option "__report" "reverse-balance?"))
 
         (document (gnc:make-html-document))
         (chart (gnc:make-html-chart))
@@ -451,7 +447,7 @@ balance at a given time"))
 
       (define (fix-signs combined)
         (map (lambda (pair)
-               (if (reverse-balance? (cadr pair))
+               (if reverse-balance?
                    (cons (- (car pair)) (cdr pair))
                    pair))
 	     combined))
@@ -595,8 +591,8 @@ balance at a given time"))
       document)))
 
 (define (build-report!
-          name acct-types income-expense? depth-based? menuname menutip
-          reverse-balance? uuid)
+         name acct-types income-expense? depth-based? reverse-balance?
+         menuname menutip uuid)
   (gnc:define-report
     'version 1
     'name name
@@ -607,12 +603,12 @@ balance at a given time"))
     'menu-name menuname
     'menu-tip menutip
     'options-generator (lambda () (options-generator acct-types
-                                                     reverse-balance?
                                                      income-expense?
                                                      depth-based?))
     'renderer (lambda (report-obj)
                 (piechart-renderer report-obj name uuid
                                    acct-types income-expense? depth-based?
+                                   reverse-balance?
                                    (if depth-based?
                                        display-name-accounts
                                        display-name-security)
@@ -626,17 +622,15 @@ balance at a given time"))
 (build-report!
   reportname-income
   (list ACCT-TYPE-INCOME)
-  #t #t
+  #t #t #t
   menuname-income menutip-income
-  (lambda (x) #t)
   "e1bd09b8a1dd49dd85760db9d82b045c")
 
 (build-report!
   reportname-expense
   (list ACCT-TYPE-EXPENSE)
-  #t #t
+  #t #t #f
   menuname-expense menutip-expense
-  (lambda (x) #f)
   "9bf1892805cb4336be6320fe48ce5446")
 
 (build-report!
@@ -645,9 +639,8 @@ balance at a given time"))
         ACCT-TYPE-SAVINGS ACCT-TYPE-MONEYMRKT
         ACCT-TYPE-RECEIVABLE ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL
         ACCT-TYPE-CURRENCY)
-  #f #t
+  #f #t #f
   menuname-assets menutip-assets
-  (lambda (x) #f)
   "5c7fd8a1fe9a4cd38884ff54214aa88a")
 
 (build-report!
@@ -656,16 +649,14 @@ balance at a given time"))
         ACCT-TYPE-SAVINGS ACCT-TYPE-MONEYMRKT
         ACCT-TYPE-RECEIVABLE ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL
         ACCT-TYPE-CURRENCY)
-  #f #f
+  #f #f #f
   menuname-securities menutip-securities
-  (lambda (x) #f)
   "e9418ff64f2c11e5b61d1c7508d793ed")
 
 (build-report!
   reportname-liabilities
   (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE ACCT-TYPE-CREDIT
         ACCT-TYPE-CREDITLINE)
-  #f #t
+  #f #t #t
   menuname-liabilities menutip-liabilities
-  (lambda (x) #t)
   "3fe6dce77da24c66bdc8f8efdea7f9ac")

commit 278aa484d72f2168d9217b43ef0c0e171300432b
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Mar 21 12:46:02 2022 -0700

    [C++options] Implement new GncOptionValue type GncOptionReportPlacement.
    
    For multicolumn reports so that we don't have to pass Scheme values.

diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp
index 504b6c28f..3a189219b 100644
--- a/gnucash/gnome/dialog-report-column-view.cpp
+++ b/gnucash/gnome/dialog-report-column-view.cpp
@@ -24,6 +24,7 @@
 #include <libguile.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
+#include <algorithm>
 
 extern "C"
 {
@@ -41,7 +42,7 @@ extern "C"
 #include "dialog-report-column-view.hpp"
 #include <dialog-options.hpp>
 #include <gnc-report.h>
-#include <gnc-optiondb.h>
+#include <gnc-optiondb-impl.hpp>
 
 enum available_cols
 {
@@ -59,6 +60,8 @@ enum contents_cols
     NUM_CONTENTS_COLS
 };
 
+using StrVec = std::vector<std::string>;
+
 struct gncp_column_view_edit
 {
     GncOptionsDialog * optwin;
@@ -68,8 +71,8 @@ struct gncp_column_view_edit
     SCM          view;
     GncOptionDB  * odb;
 
-    SCM       available_list;
-    SCM       contents_list;
+    StrVec  available_list;
+    GncOptionReportPlacementVec  contents_list;
     int       contents_selected;
 
     GtkWidget *add_button;
@@ -95,9 +98,9 @@ void gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data);
 
 static void
 gnc_column_view_set_option(GncOptionDB* odb, const char* section,
-                           const char* name, SCM new_value)
+                           const char* name, const GncOptionReportPlacementVec& new_value)
 {
-    gnc_option_db_set_scm_value(odb, section, name, new_value);
+    odb->find_option(section, name)->set_value(new_value);
 }
 
 static void
@@ -109,25 +112,28 @@ gnc_column_view_edit_destroy(gnc_column_view_edit * view)
     g_free(view);
 }
 
+static StrVec
+get_available_reports ()
+{
+    StrVec sv;
+    auto scm_list{scm_call_0(scm_c_eval_string("gnc:all-report-template-guids"))};
+    for (auto next{scm_list}; !scm_is_null(next); next = scm_cdr(next))
+        sv.emplace_back(scm_to_utf8_string(scm_car(next)));
+    return sv;
+}
+
 static void
 update_available_lists(gnc_column_view_edit * view)
 {
-    SCM   get_rpt_guids = scm_c_eval_string("gnc:all-report-template-guids");
     SCM   template_menu_name = scm_c_eval_string("gnc:report-template-menu-name/report-guid");
-    SCM   rpt_guids = scm_call_0(get_rpt_guids);
-    SCM   selection;
+    std::string selection;
 
-    gchar *name;
-    gchar *guid_str;
 
-    GtkTreeModel *model;
-    GtkListStore *store;
     GtkTreeIter iter;
-    GtkTreeSelection *tree_selection;
 
     /* Update the list of available reports (left selection box). */
-    tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view->available));
-    model = gtk_tree_view_get_model (GTK_TREE_VIEW(view->available));
+    auto tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view->available));
+    auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(view->available));
 
     if (gtk_tree_selection_get_selected(tree_selection, &model, &iter))
     {
@@ -135,41 +141,30 @@ update_available_lists(gnc_column_view_edit * view)
         gtk_tree_model_get(model, &iter,
                            AVAILABLE_COL_GUID, &guid_str,
                            -1);
-        selection = scm_from_utf8_string(guid_str);
+        selection = std::string(guid_str);
         g_free (guid_str);
     }
-    else
-        selection = SCM_UNDEFINED;
-
-    scm_gc_unprotect_object(view->available_list);
-    view->available_list = rpt_guids;
-    scm_gc_protect_object(view->available_list);
+    view->available_list = get_available_reports();
 
-    store = GTK_LIST_STORE(model);
+    auto store = GTK_LIST_STORE(model);
     gtk_list_store_clear(store);
 
-    if (scm_is_list(rpt_guids))
+    for (auto guid : view->available_list)
     {
-        for (int i = 0; !scm_is_null(rpt_guids); rpt_guids = SCM_CDR(rpt_guids), i++)
-        {
-            SCM rpt_guids_temp = SCM_CAR(rpt_guids);
-
-            guid_str = scm_to_utf8_string (rpt_guids_temp);
-            name = gnc_scm_to_utf8_string (scm_call_2(template_menu_name, rpt_guids_temp,
-                                             SCM_BOOL_F));
+       auto rpt_guid{scm_from_utf8_string(guid.c_str())};
+       auto name =
+           gnc_scm_to_utf8_string (scm_call_2(template_menu_name, rpt_guid, SCM_BOOL_F));
 
-            gtk_list_store_append(store, &iter);
-            gtk_list_store_set(store, &iter,
-                               AVAILABLE_COL_NAME, _(name),
-                               AVAILABLE_COL_GUID, guid_str,
-                               -1);
+       gtk_list_store_append(store, &iter);
+       gtk_list_store_set(store, &iter,
+                          AVAILABLE_COL_NAME, _(name),
+                          AVAILABLE_COL_GUID, guid.c_str(),
+                          -1);
 
-            if (scm_is_equal (rpt_guids_temp, selection))
-                gtk_tree_selection_select_iter (tree_selection, &iter);
+       if (guid == selection)
+           gtk_tree_selection_select_iter (tree_selection, &iter);
 
-            g_free (name);
-            g_free (guid_str);
-        }
+       g_free (name);
     }
 }
 
@@ -177,43 +172,25 @@ static void
 update_contents_lists(gnc_column_view_edit * view)
 {
     SCM   report_menu_name = scm_c_eval_string("gnc:report-menu-name");
-    SCM contents = gnc_option_db_lookup_scm_value(view->odb, "__general",
-                                                  "report-list");
-    SCM this_report;
-    SCM selection = SCM_UNDEFINED;
-
-    GtkListStore *store;
+    auto contents{view->odb->find_option("__general", "report-list")->get_value<GncOptionReportPlacementVec>()};
     GtkTreeIter iter;
-    GtkTreeSelection *tree_selection;
+    GncOptionReportPlacement selection{0, 0, 0};
 
     /* Update the list of selected reports (right selection box). */
-    tree_selection = gtk_tree_view_get_selection(view->contents);
-
-    if (scm_is_list(view->contents_list) && !scm_is_null (view->contents_list))
-    {
-        int row = view->contents_selected;
-        row = MIN (row, scm_ilength (view->contents_list) - 1);
-        selection = scm_list_ref (view->contents_list, scm_from_int (row));
-    }
+    auto tree_selection = gtk_tree_view_get_selection(view->contents);
 
-    scm_gc_unprotect_object(view->contents_list);
     view->contents_list = contents;
-    scm_gc_protect_object(view->contents_list);
 
-    store = GTK_LIST_STORE(gtk_tree_view_get_model(view->contents));
-    gtk_list_store_clear(store);
+    if (!contents.empty() && static_cast<size_t>(view->contents_selected) < contents.size())
+        selection = contents[view->contents_selected];
 
-    if (!scm_is_list(contents))
-        return;
+    auto store = GTK_LIST_STORE(gtk_tree_view_get_model(view->contents));
+    gtk_list_store_clear(store);
 
-    for (int i = 0; !scm_is_null(contents);
-         contents = SCM_CDR(contents), ++i)
+    for (size_t i = 0; i < contents.size(); ++i)
     {
-        SCM contents_temp = SCM_CAR(contents);
-
-        int id = scm_to_int(SCM_CAAR(contents));
-
-        this_report = gnc_report_find(id);
+        auto [id, wide, high] = contents[i];
+        auto this_report = gnc_report_find(id);
         auto name = gnc_scm_to_utf8_string (scm_call_1(report_menu_name, this_report));
 
         gtk_list_store_append(store, &iter);
@@ -221,11 +198,11 @@ update_contents_lists(gnc_column_view_edit * view)
             (store, &iter,
              CONTENTS_COL_NAME, _(name),
              CONTENTS_COL_ROW, i,
-             CONTENTS_COL_REPORT_COLS, scm_to_int(SCM_CADR(contents_temp)),
-             CONTENTS_COL_REPORT_ROWS, scm_to_int(SCM_CADDR(contents_temp)),
+             CONTENTS_COL_REPORT_COLS, wide,
+             CONTENTS_COL_REPORT_ROWS, high,
              -1);
 
-        if (scm_is_equal (contents_temp, selection))
+        if (id == std::get<0>(selection))
             gtk_tree_selection_select_iter (tree_selection, &iter);
 
         g_free (name);
@@ -256,7 +233,7 @@ gnc_column_view_update_buttons_cb (GtkTreeSelection *selection,
 
     if (is_selected)
     {
-        int len = scm_ilength (reinterpret_cast<SCM>(r->contents_list));
+        int len = r->contents_list.size();
 
         gtk_tree_model_get (model, &iter,
                            CONTENTS_COL_ROW, &r->contents_selected, -1);
@@ -366,9 +343,9 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
         r->size_button = GTK_WIDGET(gtk_builder_get_object (builder, "size_button1"));
 
         r->view      = view;
-        r->available_list = SCM_EOL;
+        r->available_list.clear();
         r->contents_selected = 0;
-        r->contents_list = SCM_EOL;
+        r->contents_list.clear();
         r->odb       = odb;
 
         r->optwin->build_contents(r->odb);
@@ -378,8 +355,6 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
                                  gtk_label_new(_("Contents")));
 
         scm_gc_protect_object(r->view);
-        scm_gc_protect_object(r->available_list);
-        scm_gc_protect_object(r->contents_list);
 
         /* Build the 'available' view */
         store = gtk_list_store_new (NUM_AVAILABLE_COLS, G_TYPE_STRING, G_TYPE_STRING);
@@ -449,12 +424,6 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data)
     auto r = static_cast<gnc_column_view_edit *>(user_data);
     SCM make_report = scm_c_eval_string("gnc:make-report");
     SCM mark_report = scm_c_eval_string("gnc:report-set-needs-save?!");
-    SCM template_name;
-    SCM new_report;
-    SCM newlist = SCM_EOL;
-    SCM oldlist = r->contents_list;
-    int count;
-    int oldlength, id;
     gchar *guid_str;
     GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(r->available));
     GtkTreeModel *model;
@@ -467,55 +436,26 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data)
     else
         return;
 
-    if (scm_is_list(r->available_list))
-    {
-        template_name = scm_from_utf8_string(guid_str);
-
-        new_report = scm_call_1(make_report, template_name);
-        id = scm_to_int(new_report);
-        scm_call_2(mark_report, gnc_report_find(id), SCM_BOOL_T);
-        oldlength = scm_ilength(r->contents_list);
-
-        if (oldlength > r->contents_selected)
-        {
-            for (count = 0; count < r->contents_selected; count++)
-            {
-                newlist = scm_cons(SCM_CAR(oldlist), newlist);
-                oldlist = SCM_CDR(oldlist);
-            }
-            newlist = scm_append
-                      (scm_list_n (scm_reverse
-                                   (scm_cons
-                                    (scm_list_4 (new_report,
-                                                 scm_from_int (1),
-                                                 scm_from_int (1),
-                                                 SCM_BOOL_F),
-                                     newlist)),
-                                   oldlist,
-                                   SCM_UNDEFINED));
-        }
-        else
-        {
-            newlist = scm_append
-                      (scm_list_n (oldlist,
-                                   scm_list_1
-                                   (scm_list_4 (new_report,
-                                                scm_from_int (1),
-                                                scm_from_int (1),
-                                                SCM_BOOL_F)),
-                                   SCM_UNDEFINED));
-            r->contents_selected = oldlength;
-        }
+    auto template_name = scm_from_utf8_string(guid_str);
 
-        scm_gc_unprotect_object(r->contents_list);
-        r->contents_list = newlist;
-        scm_gc_protect_object(r->contents_list);
+    auto new_report = scm_call_1(make_report, template_name);
+    auto id = scm_to_int(new_report);
+    scm_call_2(mark_report, gnc_report_find(id), SCM_BOOL_T);
+    auto oldlength = r->contents_list.size();
+    GncOptionReportPlacement new_rpt_placement{id, 1, 1};
 
-        gnc_column_view_set_option(r->odb, "__general", "report-list",
-                                   r->contents_list);
-        r->optwin->changed ();
+    if (oldlength > static_cast<size_t>(r->contents_selected))
+        r->contents_list.emplace(r->contents_list.begin() + r->contents_selected + 1, id, 1, 1);
+    else
+    {
+        r->contents_list.emplace_back(id, 1, 1);
+        r->contents_selected = oldlength;
     }
+
+    gnc_column_view_set_option(r->odb, "__general", "report-list",
+                               r->contents_list);
     g_free (guid_str);
+    r->optwin->changed();
     update_contents_lists(r);
 }
 
@@ -523,41 +463,34 @@ void
 gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data)
 {
     auto r = static_cast<gnc_column_view_edit *>(user_data);
-    SCM newlist = SCM_EOL;
-    SCM oldlist = r->contents_list;
-    int count;
-    int oldlength;
 
-    if (scm_is_list(r->contents_list))
-    {
-        oldlength = scm_ilength(r->contents_list);
-        if (oldlength > r->contents_selected)
-        {
-            for (count = 0; count < r->contents_selected; count++)
-            {
-                newlist = scm_cons(SCM_CAR(oldlist), newlist);
-                oldlist = SCM_CDR(oldlist);
-            }
-            if (count <= oldlength)
-            {
-                newlist = scm_append(scm_list_n (scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED));
-            }
-        }
+    r->contents_list.erase(r->contents_list.begin() + r->contents_selected);
+    if (r->contents_selected)
+        --r->contents_selected;
+    gnc_column_view_set_option(r->odb, "__general", "report-list",
+                               r->contents_list);
 
-        if (r->contents_selected > 0 && oldlength == r->contents_selected + 1)
-        {
-            r->contents_selected --;
-        }
+    r->optwin->changed();
+    update_contents_lists(r);
+}
 
-        scm_gc_unprotect_object(r->contents_list);
-        r->contents_list = newlist;
-        scm_gc_protect_object(r->contents_list);
+static void
+move_selected_item(gnc_column_view_edit* r, int increment)
+{
+    if (!r || !increment)
+        return;
 
-        gnc_column_view_set_option(r->odb, "__general", "report-list",
-                                   r->contents_list);
+    auto cur_sel{r->contents_list.begin() + r->contents_selected};
+    auto move_to{cur_sel + increment};
+    if (increment > 0)
+        std::reverse(cur_sel, move_to + 1);
+    else
+        std::reverse(move_to, cur_sel + 1);
+    r->contents_selected += increment;
 
-        r->optwin->changed();
-    }
+    gnc_column_view_set_option(r->odb, "__general", "report-list",
+                               r->contents_list);
+    r->optwin->changed();
     update_contents_lists(r);
 }
 
@@ -565,76 +498,14 @@ void
 gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data)
 {
     auto r = static_cast<gnc_column_view_edit *>(user_data);
-    SCM oldlist = r->contents_list;
-    SCM newlist = SCM_EOL;
-    SCM temp;
-    int oldlength;
-    int count;
-
-    oldlength = scm_ilength(r->contents_list);
-    if ((r->contents_selected > 0) && (oldlength > r->contents_selected))
-    {
-        for (count = 1; count < r->contents_selected; count++)
-        {
-            newlist = scm_cons(SCM_CAR(oldlist), newlist);
-            oldlist = SCM_CDR(oldlist);
-        }
-        temp = SCM_CAR(oldlist);
-        oldlist = SCM_CDR(oldlist);
-        newlist = scm_cons(temp, scm_cons(SCM_CAR(oldlist), newlist));
-        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;
-        scm_gc_protect_object(r->contents_list);
-
-        r->contents_selected = r->contents_selected - 1;
-
-        gnc_column_view_set_option(r->odb, "__general", "report-list",
-                                   r->contents_list);
-
-        r->optwin->changed();
-
-        update_contents_lists(r);
-    }
+    move_selected_item(r, -1);
 }
 
 void
 gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data)
 {
     auto r = static_cast<gnc_column_view_edit *>(user_data);
-    SCM oldlist = r->contents_list;
-    SCM newlist = SCM_EOL;
-    SCM temp;
-    int oldlength;
-    int count;
-
-    oldlength = scm_ilength(r->contents_list);
-    if (oldlength > (r->contents_selected + 1))
-    {
-        for (count = 0; count < r->contents_selected; count++)
-        {
-            newlist = scm_cons(SCM_CAR(oldlist), newlist);
-            oldlist = SCM_CDR(oldlist);
-        }
-        temp = SCM_CAR(oldlist);
-        oldlist = SCM_CDR(oldlist);
-        newlist = scm_cons(temp, scm_cons(SCM_CAR(oldlist), newlist));
-        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;
-        scm_gc_protect_object(r->contents_list);
-
-        r->contents_selected = r->contents_selected + 1;
-
-        gnc_column_view_set_option(r->odb, "__general", "report-list",
-                                   r->contents_list);
-
-        r->optwin->changed();
-
-        update_contents_lists(r);
-    }
+    move_selected_item(r, 1);
 }
 
 void
@@ -662,32 +533,27 @@ gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data)
     rowspin = GTK_WIDGET(gtk_builder_get_object (builder, "row_spin"));
     colspin = GTK_WIDGET(gtk_builder_get_object (builder, "col_spin"));
 
-    length = scm_ilength(r->contents_list);
-    if (length > r->contents_selected)
+    if (r->contents_list.size() > static_cast<size_t>(r->contents_selected))
     {
-        current = scm_list_ref(r->contents_list,
-                               scm_from_int (r->contents_selected));
+        auto [id, wide, high] = r->contents_list[r->contents_selected];
+
         gtk_spin_button_set_value(GTK_SPIN_BUTTON(colspin),
-                                  (float)scm_to_int(SCM_CADR(current)));
+                                  static_cast<float>(wide));
         gtk_spin_button_set_value(GTK_SPIN_BUTTON(rowspin),
-                                  (float)scm_to_int(SCM_CADDR(current)));
+                                  static_cast<float>(high));
 
         dlg_ret = gtk_dialog_run(GTK_DIALOG(dlg));
         gtk_widget_hide(dlg);
 
         if (dlg_ret == GTK_RESPONSE_OK)
         {
-            current = scm_list_4 (SCM_CAR (current),
-                                  scm_from_int (gtk_spin_button_get_value_as_int
-                                                (GTK_SPIN_BUTTON(colspin))),
-                                  scm_from_int (gtk_spin_button_get_value_as_int
-                                                (GTK_SPIN_BUTTON(rowspin))),
-                                  SCM_BOOL_F);
-            scm_gc_unprotect_object(r->contents_list);
-            r->contents_list = scm_list_set_x(r->contents_list,
-                                              scm_from_int (r->contents_selected),
-                                              current);
-            scm_gc_protect_object(r->contents_list);
+            std::get<1>(r->contents_list[r->contents_selected]) =
+                gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(colspin));
+            std::get<2>(r->contents_list[r->contents_selected]) =
+                gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rowspin));
+
+            gnc_column_view_set_option(r->odb, "__general", "report-list",
+                                       r->contents_list);
             r->optwin->changed();
             update_contents_lists(r);
         }
diff --git a/gnucash/report/reports/standard/view-column.scm b/gnucash/report/reports/standard/view-column.scm
index 7ef37994e..352d59317 100644
--- a/gnucash/report/reports/standard/view-column.scm
+++ b/gnucash/report/reports/standard/view-column.scm
@@ -42,8 +42,7 @@
 	    (gnc:register-option options opt))))
     ;; the report-list is edited by a special add-on page for the
     ;; options editor.
-    (opt-register 
-     (gnc:make-internal-option "__general" "report-list" '()))
+    (gnc-register-report-placement-option (gnc:options-get options) "__general" "report-list")
     
     (opt-register
      (gnc:make-number-range-option 
@@ -83,7 +82,6 @@
        (let* ((subreport (gnc-report-find (car report-info)))
 	      (colspan (cadr report-info))
 	      (rowspan (caddr report-info))
-	      (opt-callback (cadddr report-info))
 	      (toplevel-cell (gnc:make-html-table-cell/size rowspan colspan))
 	      (report-table (gnc:make-html-table))
 	      (contents-cell (gnc:make-html-table-cell)))
diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 470be01f9..7c4e795fc 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -27,6 +27,8 @@
 #include <guid.hpp>
 #include <cassert>
 #include <sstream>
+#include <numeric>
+
 extern "C"
 {
 #include "gnc-accounting-period.h"
@@ -661,6 +663,18 @@ GncOptionValue<ValueType>::serialize() const noexcept
         ostr << type << " " << guid;
         return ostr.str();
     }
+    if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
+    {
+        std::ostringstream ostr{};
+        ostr << "'(";
+        std::for_each(m_value.begin(), m_value.end(),
+                 [&ostr](auto rp){
+                    auto [id, wide, high] = rp;
+                    ostr << "(" << id << " " << wide << " " << high << " #f) ";
+                 });
+        ostr << ")";
+        return ostr.str();
+    }
     else if constexpr(is_same_decayed_v<ValueType, std::string>)
         return m_value;
     else if constexpr(is_same_decayed_v<ValueType, bool>)
@@ -684,6 +698,18 @@ GncOptionValue<ValueType>::deserialize(const std::string& str) noexcept
         auto inst{qof_instance_from_string(guid, get_ui_type())};
         qofOwnerSetEntity(const_cast<GncOwner*>(m_value), inst);
     }
+    if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
+    {
+        std::istringstream istr{str};
+        GncOptionReportPlacementVec rpv;
+        while (istr)
+        {
+            uint32_t id, wide, high;
+            istr >> id >> wide >> high;
+            rpv.emplace_back(id, wide, high);
+        }
+        set_value(rpv);
+    }
     else if constexpr(is_same_decayed_v<ValueType, std::string>)
         set_value(str);
     else if constexpr(is_same_decayed_v<ValueType, bool>)
@@ -920,6 +946,7 @@ template GncOptionValue<RelativeDatePeriod>::GncOptionValue(const GncOptionValue
 template GncOptionValue<size_t>::GncOptionValue(const GncOptionValue<size_t>&);
 template GncOptionValue<GncOptionAccountList>::GncOptionValue(const GncOptionValue<GncOptionAccountList>&);
 template GncOptionValue<GncMultichoiceOptionIndexVec>::GncOptionValue(const GncOptionValue<GncMultichoiceOptionIndexVec>&);
+template GncOptionValue<GncOptionReportPlacementVec>::GncOptionValue(const GncOptionValue<GncOptionReportPlacementVec>&);
 template GncOptionValue<SCM>::GncOptionValue(const GncOptionValue<SCM>&);
 template void GncOptionValue<bool>::set_value(bool);
 template void GncOptionValue<int>::set_value(int);
@@ -934,6 +961,7 @@ template void GncOptionValue<RelativeDatePeriod>::set_value(RelativeDatePeriod);
 template void GncOptionValue<size_t>::set_value(size_t);
 template void GncOptionValue<GncOptionAccountList>::set_value(GncOptionAccountList);
 template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_value(GncMultichoiceOptionIndexVec);
+template void GncOptionValue<GncOptionReportPlacementVec>::set_value(GncOptionReportPlacementVec);
 template void GncOptionValue<bool>::set_default_value(bool);
 template void GncOptionValue<int>::set_default_value(int);
 template void GncOptionValue<int64_t>::set_default_value(int64_t);
@@ -947,6 +975,7 @@ template void GncOptionValue<RelativeDatePeriod>::set_default_value(RelativeDate
 template void GncOptionValue<size_t>::set_default_value(size_t);
 template void GncOptionValue<GncOptionAccountList>::set_default_value(GncOptionAccountList);
 template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_default_value(GncMultichoiceOptionIndexVec);
+template void GncOptionValue<GncOptionReportPlacementVec>::set_default_value(GncOptionReportPlacementVec);
 template void GncOptionValue<bool>::reset_default_value();
 template void GncOptionValue<int>::reset_default_value();
 template void GncOptionValue<int64_t>::reset_default_value();
@@ -960,6 +989,7 @@ template void GncOptionValue<RelativeDatePeriod>::reset_default_value();
 template void GncOptionValue<size_t>::reset_default_value();
 template void GncOptionValue<GncOptionAccountList>::reset_default_value();
 template void GncOptionValue<GncMultichoiceOptionIndexVec>::reset_default_value();
+template void GncOptionValue<GncOptionReportPlacementVec>::reset_default_value();
 template std::string GncOptionValue<bool>::serialize() const noexcept;
 template std::string GncOptionValue<int>::serialize() const noexcept;
 template std::string GncOptionValue<int64_t>::serialize() const noexcept;
@@ -970,6 +1000,7 @@ template std::string GncOptionValue<std::string>::serialize() const noexcept;
 template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
 template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
 template std::string GncOptionValue<SCM>::serialize() const noexcept;
+template std::string GncOptionValue<GncOptionReportPlacementVec>::serialize() const noexcept;
 template std::string GncOptionRangeValue<int>::serialize() const noexcept;
 template std::string GncOptionRangeValue<double>::serialize() const noexcept;
 template bool GncOptionValue<bool>::deserialize(const std::string&) noexcept;
@@ -982,5 +1013,6 @@ template bool GncOptionValue<std::string>::deserialize(const std::string&) noexc
 template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<SCM>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<GncOptionReportPlacementVec>::deserialize(const std::string&) noexcept;
 template bool GncOptionRangeValue<int>::deserialize(const std::string&) noexcept;
 template bool GncOptionRangeValue<double>::deserialize(const std::string&) noexcept;
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index a784aa7cf..6bd82dc54 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -331,6 +331,16 @@ operator>> <GncOptionValue<bool>>(std::istream& iss,
     opt.set_value(instr == "#t" ? true : false);
     return iss;
 }
+
+template<> inline std::istream&
+operator>> <GncOptionValue<GncOptionReportPlacementVec>>(std::istream& iss,
+    GncOptionValue<GncOptionReportPlacementVec>& opt)
+{
+    uint32_t id, wide, high;
+    iss >> id >> wide >> high;
+        opt.set_value(GncOptionReportPlacementVec{{id, wide, high}});
+    return iss;
+}
 #endif // SWIG
 
 /** @class GncOptionRangeValue
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index 76c6e48e0..83b0f9347 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -67,7 +67,7 @@ enum class GncOptionUIType : unsigned int
     JOB,
     TAX_TABLE,
     QUERY,
-    REPORT_LIST,
+    REPORT_PLACEMENT,
     MAX_VALUE,  //Nake sure this one is always last
 };
 
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 5b2b940d1..e69bc0c02 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -481,6 +481,7 @@ template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_value<GncMultichoiceOptionIndexVec>() const;
 template SCM GncOption::get_value<SCM>() const;
+template GncOptionReportPlacementVec GncOption::get_value<GncOptionReportPlacementVec>() const;
 
 template bool GncOption::get_default_value<bool>() const;
 template int GncOption::get_default_value<int>() const;
@@ -495,6 +496,7 @@ template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() c
 template GncOptionAccountList GncOption::get_default_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_default_value<GncMultichoiceOptionIndexVec>() const;
 template SCM GncOption::get_default_value<SCM>() const;
+template GncOptionReportPlacementVec GncOption::get_default_value<GncOptionReportPlacementVec>() const;
 
 template void GncOption::set_value(bool);
 template void GncOption::set_value(int);
@@ -511,6 +513,7 @@ template void GncOption::set_value(size_t);
 template void GncOption::set_value(GncOptionAccountList);
 template void GncOption::set_value(GncMultichoiceOptionIndexVec);
 template void GncOption::set_value(SCM);
+template void GncOption::set_value(GncOptionReportPlacementVec);
 
 template void GncOption::set_default_value(bool);
 template void GncOption::set_default_value(int);
@@ -526,6 +529,7 @@ template void GncOption::set_default_value(size_t);
 template void GncOption::set_default_value(GncOptionAccountList);
 template void GncOption::set_default_value(GncMultichoiceOptionIndexVec);
 template void GncOption::set_default_value(SCM);
+template void GncOption::set_default_value(GncOptionReportPlacementVec);
 
 template void GncOption::get_limits(double&, double&, double&) const noexcept;
 template void GncOption::get_limits(int&, int&, int&) const noexcept;
@@ -541,6 +545,7 @@ template bool GncOption::validate(const Account*) const;
 template bool GncOption::validate(const QofQuery*) const;
 template bool GncOption::validate(RelativeDatePeriod) const;
 template bool GncOption::validate(GncMultichoiceOptionIndexVec) const;
+template bool GncOption::validate(GncOptionReportPlacementVec) const;
 
 template GncOption* gnc_make_option<const std::string&>(const char*,
                                                         const char*,
@@ -554,3 +559,4 @@ template GncOption* gnc_make_option<int64_t>(const char*, const char*,
                                              const char*, const char*, int64_t,
                                              GncOptionUIType);
 
+
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 72022d9f4..e46fa9216 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -40,8 +40,10 @@
 #include <iomanip>
 #include <variant>
 #include <memory>
+#include <tuple>
 #include "gnc-option-ui.hpp"
 #include "gnc-option-date.hpp"
+#include <guid.hpp>
 
 struct OptionClassifier;
 class GncOptionUIItem;
@@ -62,7 +64,8 @@ class GncOptionMultichoiceValue;
 template <typename ValueType> class GncOptionRangeValue;
 class GncOptionCommodityValue;
 class GncOptionDateValue;
-
+using GncOptionReportPlacement = std::tuple<uint32_t, uint32_t, uint32_t>;
+using GncOptionReportPlacementVec = std::vector<GncOptionReportPlacement>;
 template <typename T>
 struct is_OptionClassifier
 {
@@ -102,6 +105,7 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<const QofQuery*>,
                                       GncOptionValue<const GncOwner*>,
                                       GncOptionValue<SCM>,
+                                      GncOptionValue<GncOptionReportPlacementVec>,
                                       GncOptionAccountListValue,
                                       GncOptionAccountSelValue,
                                       GncOptionMultichoiceValue,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 8b060e01b..9d06fb951 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -840,6 +840,7 @@ gnc_register_internal_option(GncOptionDB* db, const char* section,
             GncOptionUIType::INTERNAL};
     db->register_option(section, std::move(option));
 }
+
 void
 gnc_register_invoice_option(GncOptionDB* db, const char* section,
                             const char* name, const char* key,
@@ -995,6 +996,20 @@ gnc_register_end_date_option(GncOptionDB* db, const char* section,
     db->register_option(section, std::move(option));
 }
 
+void
+gnc_register_report_placement_option(GncOptionDBPtr& db,
+                                     const char* section, const char* name)
+{
+    /* This is a special option with it's own UI file so we have fake values to pass
+     * to the template creation function.
+     */
+    GncOptionReportPlacementVec value;
+    GncOption option{GncOptionValue<GncOptionReportPlacementVec>{section, name,
+                                                              "no_key", "nodoc_string",
+                                                              value,GncOptionUIType::REPORT_PLACEMENT}};
+    db->register_option(section, std::move(option));
+}
+
 GncOptionDB*
 gnc_option_db_new(void)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 86a6aedbe..d92f14505 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -600,6 +600,21 @@ inline void gnc_register_internal_option(GncOptionDBPtr& db,
                                  doc_string, value);
 }
 
+void gnc_register_internal_option(GncOptionDBPtr& db,
+                                          const char* section, const char* name,
+                                          const char* key,
+                                          const char* doc_string,
+                                          const std::string& value);
+
+void gnc_register_internal_option(GncOptionDBPtr& db,
+                                          const char* section, const char* name,
+                                          const char* key,
+                                          const char* doc_string,
+                                          bool value);
+
+void gnc_register_report_placement_option(GncOptionDBPtr& db,
+                                          const char* section, const char* name);
+
 /**
  * Create a new currency option and register it in the options database.
  *
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 5f78cabdf..bff5ee3d4 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -274,6 +274,39 @@ scm_from_value<GncOwner*>(GncOwner* value)
     return scm_from_value<const GncOwner*>(value);
 }
 
+template <>inline SCM
+scm_from_value<GncOptionAccountList>(GncOptionAccountList value)
+{
+    SCM s_list = SCM_EOL;
+    auto book{gnc_get_current_book()};
+    for (auto guid : value)
+    {
+        auto acct{xaccAccountLookup(&guid, book)};
+        s_list = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0),
+                          s_list);
+    }
+    return scm_reverse(s_list);
+}
+
+template <>inline SCM
+scm_from_value<GncOptionReportPlacementVec>(GncOptionReportPlacementVec value)
+{
+    SCM s_list = SCM_EOL;
+    for (auto placement : value)
+   {
+        auto [id, wide, high] = placement;
+        auto scm_id{scm_from_uint32(id)};
+        auto scm_wide{scm_from_uint32(wide)};
+        auto scm_high{scm_from_uint32(high)};
+        /* The trailing SCM_BOOL_F is a placeholder for a never-used callback function,
+         * present for backward compatibility so that older GnuCash versions can load a
+         * saved multicolumn report.
+         */
+        s_list = scm_cons(scm_list_4(scm_id, scm_wide, scm_high, SCM_BOOL_F), s_list);
+    }
+    return scm_reverse(s_list);
+}
+
 static std::string
 scm_color_list_to_string(SCM list)
 {
@@ -440,18 +473,25 @@ scm_to_value<GncOptionAccountList>(SCM new_value)
     return retval;
 }
 
-template <>inline SCM
-scm_from_value<GncOptionAccountList>(GncOptionAccountList value)
+template <>inline GncOptionReportPlacementVec
+scm_to_value<GncOptionReportPlacementVec>(SCM new_value)
 {
-    SCM s_list = SCM_EOL;
-    auto book{gnc_get_current_book()};
-    for (auto guid : value)
+    GncOptionReportPlacementVec rp;
+    GncOptionAccountList retval{};
+    if (scm_is_false(scm_list_p(new_value)) || scm_is_null(new_value))
+        return rp;
+    auto next{new_value};
+    while (auto node{scm_car(next)})
     {
-        auto acct{xaccAccountLookup(&guid, book)};
-        s_list = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0),
-                          s_list);
+        auto id{scm_to_uint32(scm_car(node))};
+        auto wide{scm_to_uint32(scm_cadr(node))};
+        auto high{scm_to_uint32(scm_caddr(node))};
+        rp.emplace_back(id, wide, high);
+        next = scm_cdr(next);
+        if (scm_is_null(next))
+            break;
     }
-    return scm_reverse(s_list);
+    return rp;
 }
 
 QofBook* gnc_option_test_book_new();
@@ -640,6 +680,12 @@ gnc_option_test_book_destroy(QofBook* book)
     $1 = &acclist;
 }
 
+%typemap (in) GncOptionReportPlacementVec& (GncOptionReportPlacementVec rp)
+{
+    rp = scm_to_value<GncOptionReportPlacementVec>($input);
+    $1 = &rp;
+}
+
 %typemap(out) GncOptionAccountList
 {
     $result = SCM_EOL;
@@ -666,6 +712,11 @@ gnc_option_test_book_destroy(QofBook* book)
     $result = scm_reverse ($result)
 }
 
+%typemap(out) const GncOptionReportPlacementVec&
+{
+    $result = scm_from_value<GncOptionReportPlacementVec>($1);
+}
+
 wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
 %ignore swig_get_option(GncOption&);
@@ -709,7 +760,6 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %ignore gnc_register_number_Plot_size_option(GncOptionDB*, const char*, const char*, const char*, const char*, int);
 %ignore gnc_register_query_option(GncOptionDB*, const char*, const char*, const char*, const char*, QofQuery*);
 %ignore gnc_register_color_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
-%ignore gnc_register_internal_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
 %ignore gnc_register_currency_option(GncOptionDB*, const char*, const char*, const char*, const char*, gnc_commodity*);
 %ignore gnc_register_invoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncInvoice*);
 %ignore gnc_register_taxtable_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncTaxTable*);
@@ -721,7 +771,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %ignore gnc_register_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, RelativeDatePeriodVec, bool);
 %ignore gnc_register_start_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
 %ignore gnc_register_end_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
-
+%ignore gnc_register_internal_option(GncOptionDBPtr&, const char*, const char*, const char*, const char*, const std::string&);
+%ignore gnc_register_internal_option(GncOptionDBPtr&, const char*, const char*, const char*, const char*, bool);
 %typemap(in) GncOption* "$1 = scm_is_true($input) ? static_cast<GncOption*>(scm_to_pointer($input)) : nullptr;"
 %typemap(out) GncOption* "$result = ($1) ? scm_from_pointer($1, nullptr) : SCM_BOOL_F;"
 
@@ -1057,7 +1108,6 @@ inline SCM return_scm_value(ValueType value)
 %template(gnc_make_int64_option) gnc_make_option<int64_t>;
 %template(gnc_make_query_option) gnc_make_option<const QofQuery*>;
 %template(gnc_make_owner_option) gnc_make_option<const GncOwner*>;
-
 %extend GncOption {
     bool is_budget_option()
     {
@@ -1200,6 +1250,13 @@ inline SCM return_scm_value(ValueType value)
                     return scm_simple_format(SCM_BOOL_F, ticked_format_str,
                                              scm_val);
                 }
+               if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionValue<GncOptionReportPlacementVec>>)
+                {
+                    auto scm_val{scm_list_1(return_scm_value(option.get_value()))};
+                    return scm_simple_format(SCM_BOOL_F, ticked_format_str,
+                                             scm_val);
+                }
                 auto serial{option.serialize()};
                 if (serial.empty())
                 {
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 49959de90..e235b2c46 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -1311,3 +1311,19 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end)
     EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
+TEST(GncOption, test_create)
+{
+    uint32_t report_id = 123;
+    uint32_t wide = 2, high = 2;
+    GncOptionReportPlacementVec rp{{report_id, wide, high}};
+
+    GncOptionValue<GncOptionReportPlacementVec> rpv("foo", "bar", "baz", "Phony Option", rp);
+    GncOption option{rpv};
+    auto value{option.get_value<GncOptionReportPlacementVec>()};
+    EXPECT_EQ(value.size(), 1);
+    auto [sid, swide, shigh] = value.at(0);
+    EXPECT_EQ(report_id, sid);
+    EXPECT_EQ(wide, swide);
+    EXPECT_EQ(high, shigh);
+
+}
\ No newline at end of file
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 74d56b33b..9804aa77f 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -81,6 +81,21 @@ TEST_F(GncOptionDBTest, test_register_string_option)
     EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
 }
 
+TEST_F(GncOptionDBTest, test_register_report_placement_option)
+{
+    uint32_t report_id = 456;
+    uint32_t wide = 2, high = 2;
+    GncOptionReportPlacementVec rp{{report_id, wide, high}};
+
+    gnc_register_report_placement_option(m_db, "foo", "bar");
+    auto option{m_db->find_option("foo", "bar")};
+    option->set_value(rp);
+    auto value{option->get_value<GncOptionReportPlacementVec>()};
+    EXPECT_EQ(value.size(), 1);
+    auto [v_id, v_wide, v_height] = value.at(0);
+    EXPECT_EQ(report_id, v_id);
+}
+
 /* Note: The following test-fixture code is also present in slightly different
  * form in gtest-gnc-option.cpp.
  */
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index a1296b885..7174a4f53 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -54,6 +54,7 @@
   (test-gnc-make-date-option)
   (test-gnc-make-date-set-option)
   (test-gnc-make-number-range-option)
+  (test-gnc-make-report-placement-option)
   (test-end "test-gnc-optiondb-scheme"))
 
 (define (test-gnc-make-text-option)
@@ -235,3 +236,14 @@
     (gnc-set-option option-db "foo" "bar" 20)
     (test-equal 20.0 (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-number-range-option"))
+
+(define (test-gnc-make-report-placement-option)
+  (test-begin "test-gnc-report-placement-option")
+    (let* ((report1 123)
+           (report2 456)
+           (rp (list (list report1 2 3) (list report2 3 2)))
+           (option-db (new-gnc-optiondb)))
+           (gnc-register-report-placement-option option-db "foo" "bar")
+           (gnc-set-option option-db "foo" "bar" rp)
+           (test-equal report2 (car (cadr (gnc-option-value option-db "foo" "bar")))))
+  (test-end "test-gnc-report-placement-option"))
\ No newline at end of file

commit 5457d4a9e38bebd38f5d6e5e5a6e829477b9de46
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Mar 21 12:44:52 2022 -0700

    [C++options] Convert account-summary.scm to gnc-register-option.
    
    Just to make sure that it works.

diff --git a/gnucash/report/reports/standard/account-summary.scm b/gnucash/report/reports/standard/account-summary.scm
index 96b8c273f..be0d77088 100644
--- a/gnucash/report/reports/standard/account-summary.scm
+++ b/gnucash/report/reports/standard/account-summary.scm
@@ -143,23 +143,19 @@
 
 (define (accsum-options-generator sx? reportname)
   (let* ((options (gnc:new-options))
-         (add-option
-          (lambda (new-option)
-            (gnc:register-option options new-option))))
+         (odb (gnc:options-get options)))
 
-    (add-option
-     (gnc:make-string-option
+   (gnc-register-string-option odb
       gnc:pagename-general optname-report-title
-      "a" opthelp-report-title (G_ reportname)))
-    (add-option
-     (gnc:make-string-option
+      "a" opthelp-report-title (G_ reportname))
+   (gnc-register-string-option odb
       gnc:pagename-general optname-party-name
-      "b" opthelp-party-name ""))
+      "b" opthelp-party-name "")
     ;; this should default to company name in (gnc-get-current-book)
     ;; does anyone know the function to get the company name??
 
     ;; date at which to report balance
-    (if sx?
+   (if sx?
         (gnc:options-add-date-interval!
          options gnc:pagename-general
          optname-from-date optname-to-date "c")
@@ -167,98 +163,83 @@
          options gnc:pagename-general optname-date "c"))
 
     ;; accounts to work on
-    (add-option
-     (gnc:make-account-list-option
+   (gnc-register-account-list-option odb
       gnc:pagename-accounts optname-accounts
       "a"
       opthelp-accounts
-      (lambda ()
-        (gnc:filter-accountlist-type
-         (list ACCT-TYPE-BANK ACCT-TYPE-CASH ACCT-TYPE-CREDIT
-               ACCT-TYPE-ASSET ACCT-TYPE-LIABILITY
-               ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY
-               ACCT-TYPE-PAYABLE ACCT-TYPE-RECEIVABLE
-               ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE)
-         (gnc-account-get-descendants-sorted (gnc-get-current-root-account))))
-      #f #t))
-
-    (gnc:options-add-account-levels!
+      (gnc:filter-accountlist-type
+        (list ACCT-TYPE-BANK ACCT-TYPE-CASH ACCT-TYPE-CREDIT
+            ACCT-TYPE-ASSET ACCT-TYPE-LIABILITY
+            ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY
+            ACCT-TYPE-PAYABLE ACCT-TYPE-RECEIVABLE
+            ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE)
+            (gnc-account-get-descendants-sorted (gnc-get-current-root-account))))
+
+   (gnc:options-add-account-levels!
      options gnc:pagename-accounts optname-depth-limit
      "b" opthelp-depth-limit 3)
 
-    (add-option
-     (gnc:make-multichoice-option
+   (gnc-register-multichoice-option odb
       gnc:pagename-accounts optname-bottom-behavior
-      "c" opthelp-bottom-behavior 'summarize
+      "c" opthelp-bottom-behavior "summarize"
       (list
-       (vector 'summarize (N_ "Recursive Balance"))
-       (vector 'flatten (N_ "Raise Accounts"))
-       (vector 'truncate (N_ "Omit Accounts")))))
+       (vector "summarize" (N_ "Recursive Balance"))
+       (vector "flatten" (N_ "Raise Accounts"))
+       (vector "truncate" (N_ "Omit Accounts"))))
 
     ;; all about currencies
-    (gnc:options-add-currency!
+   (gnc:options-add-currency!
      options pagename-commodities
      optname-report-commodity "a")
 
-    (gnc:options-add-price-source!
+   (gnc:options-add-price-source!
      options pagename-commodities
      optname-price-source "b" 'pricedb-nearest)
 
-    (add-option
-     (gnc:make-simple-boolean-option
+   (gnc-register-simple-boolean-option odb
       pagename-commodities optname-show-foreign
-      "c" opthelp-show-foreign #t))
+      "c" opthelp-show-foreign #t)
 
-    (add-option
-     (gnc:make-simple-boolean-option
+   (gnc-register-simple-boolean-option odb
       pagename-commodities optname-show-rates
-      "d" opthelp-show-rates #f))
+      "d" opthelp-show-rates #f)
 
     ;; what to show for zero-balance accounts
-    (add-option
-     (gnc:make-simple-boolean-option
+   (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-show-zb-accts
-      "a" opthelp-show-zb-accts #t))
-    (add-option
-     (gnc:make-simple-boolean-option
+      "a" opthelp-show-zb-accts #t)
+   (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-omit-zb-bals
-      "b" opthelp-omit-zb-bals #f))
+      "b" opthelp-omit-zb-bals #f)
     ;; what to show for non-leaf accounts
-    (gnc:options-add-subtotal-view!
+   (gnc:options-add-subtotal-view!
      options gnc:pagename-display
      optname-parent-balance-mode optname-parent-total-mode
      "c")
 
     ;; some detailed formatting options
-    (add-option
-     (gnc:make-simple-boolean-option
+    (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-account-links
-      "e" opthelp-account-links #t))
-    (add-option
-     (gnc:make-simple-boolean-option
+      "e" opthelp-account-links #t)
+    (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-use-rules
-      "f" opthelp-use-rules #f))
+      "f" opthelp-use-rules #f)
 
-    (add-option
-     (gnc:make-simple-boolean-option
+    (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-show-bals
-      "g" opthelp-show-bals #t))
-    (add-option
-     (gnc:make-simple-boolean-option
+      "g" opthelp-show-bals #t)
+    (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-show-code
-      "h" opthelp-show-code #t))
-    (add-option
-     (gnc:make-simple-boolean-option
+      "h" opthelp-show-code #t)
+    (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-show-desc
-      "i" opthelp-show-desc #f))
-    (add-option
-     (gnc:make-simple-boolean-option
+      "i" opthelp-show-desc #f)
+    (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-show-type
-      "j" opthelp-show-type #f))
-    (add-option
-     (gnc:make-simple-boolean-option
+      "j" opthelp-show-type #f)
+    (gnc-register-simple-boolean-option odb
       gnc:pagename-display optname-show-notes
-      "k" opthelp-show-notes #f))
+      "k" opthelp-show-notes #f)
 
     ;; Set the general page as default option tab
     (gnc:options-set-default-section options gnc:pagename-display)

commit 30e6c8ab11be8f7014ddcd5bda82cf9d0ffe409c
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 15 16:16:50 2022 -0700

    [gnc-optiondb.i]Explicitly include array.
    
    Xcode-13.3 errors without it.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 7415e3176..5f78cabdf 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -67,6 +67,7 @@ namespace std {
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
 #include "gnc-option-date.hpp"
+#include <array>
 #include <sstream>
 #include <iomanip>
 

commit 572cb6b1d1da4aa1a971b16201c4048c5cfdb3a5
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Mar 13 15:51:16 2022 -0700

    Banish gnc_get_optiondb_from_dispatcher to gnc-report.cpp.
    
    Puts it closer to its points of use and removes it with its Scheme
    dependency from libgnucash.

diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp
index 3c7c7c9fc..504b6c28f 100644
--- a/gnucash/gnome/dialog-report-column-view.cpp
+++ b/gnucash/gnome/dialog-report-column-view.cpp
@@ -35,12 +35,12 @@ extern "C"
 #include "window-report.h"
 #include "guile-mappings.h"
 #include "gnc-guile-utils.h"
-#include "gnc-report.h"
 #include "gnc-ui.h"
 }
 
 #include "dialog-report-column-view.hpp"
 #include <dialog-options.hpp>
+#include <gnc-report.h>
 #include <gnc-optiondb.h>
 
 enum available_cols
diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp
index a1a8d651b..a87354154 100644
--- a/gnucash/gnome/dialog-report-style-sheet.cpp
+++ b/gnucash/gnome/dialog-report-style-sheet.cpp
@@ -37,10 +37,10 @@ extern "C"
 #include "gnc-gtk-utils.h"
 #include "gnc-gnome-utils.h"
 #include "gnc-guile-utils.h"
-#include "gnc-report.h"
 #include "gnc-ui.h"
 #include <guile-mappings.h>
 }
+#include "gnc-report.h"
 #include <dialog-options.hpp>
 #include <gnc-optiondb.h>
 
diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp
index ab5888207..23e98f52d 100644
--- a/gnucash/gnome/gnc-plugin-page-report.cpp
+++ b/gnucash/gnome/gnc-plugin-page-report.cpp
@@ -65,7 +65,6 @@ extern "C"
 #include "gnc-plugin-page-report.h"
 #include "gnc-plugin-file-history.h"
 #include "gnc-prefs.h"
-#include "gnc-report.h"
 #include "gnc-session.h"
 #include "gnc-ui-util.h"
 #include "gnc-ui.h"
@@ -79,6 +78,7 @@ extern "C"
 
 
 #include <memory>
+#include <gnc-report.h>
 #include <gnc-optiondb-impl.hpp>
 
 /* NW: you can add GNC_MOD_REPORT to gnc-engine.h
diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp
index 5a50e2201..5580bad58 100644
--- a/gnucash/gnome/window-report.cpp
+++ b/gnucash/gnome/window-report.cpp
@@ -37,13 +37,13 @@ extern "C"
 
 #include "swig-runtime.h"
 #include "gnc-guile-utils.h"
-#include "gnc-report.h"
 #include "gnc-ui.h"
 #include "window-report.h"
 #include "guile-mappings.h"
 
 #include "gnc-plugin-page-report.h"
 }
+#include "gnc-report.h"
 #include "dialog-options.hpp"
 #include "dialog-report-column-view.hpp"
 
diff --git a/gnucash/gnucash-commands.cpp b/gnucash/gnucash-commands.cpp
index 3a5edb8f3..646098784 100644
--- a/gnucash/gnucash-commands.cpp
+++ b/gnucash/gnucash-commands.cpp
@@ -37,7 +37,6 @@ extern "C" {
 #include <gnc-prefs.h>
 #include <gnc-prefs-utils.h>
 #include <gnc-gnome-utils.h>
-#include <gnc-report.h>
 #include <gnc-session.h>
 #include <qoflog.h>
 }
@@ -45,6 +44,7 @@ extern "C" {
 #include <boost/locale.hpp>
 #include <fstream>
 #include <iostream>
+#include <gnc-report.h>
 
 namespace bl = boost::locale;
 
diff --git a/gnucash/gnucash-core-app.cpp b/gnucash/gnucash-core-app.cpp
index 3bbcc5f75..5bde70c1a 100644
--- a/gnucash/gnucash-core-app.cpp
+++ b/gnucash/gnucash-core-app.cpp
@@ -42,7 +42,6 @@ extern "C" {
 #include <gnc-path.h>
 #include <gnc-prefs.h>
 #include <gnc-gsettings.h>
-#include <gnc-report.h>
 #include <gnc-splash.h>
 #include <gnc-version.h>
 #include "gnucash-locale-platform.h"
@@ -53,6 +52,7 @@ extern "C" {
 #include <iostream>
 #include <string>
 #include <vector>
+#include <gnc-report.h>
 
 namespace bl = boost::locale;
 
diff --git a/gnucash/gnucash.cpp b/gnucash/gnucash.cpp
index fbb223dbe..70ed0c269 100644
--- a/gnucash/gnucash.cpp
+++ b/gnucash/gnucash.cpp
@@ -54,7 +54,6 @@ extern "C" {
 #include <gnc-plugin-report-system.h>
 #include <gnc-prefs.h>
 #include <gnc-prefs-utils.h>
-#include <gnc-report.h>
 #include <gnc-session.h>
 #include <gnc-splash.h>
 #include <gnucash-register.h>
@@ -68,6 +67,7 @@ extern "C" {
 #include <boost/nowide/args.hpp>
 #endif
 #include <iostream>
+#include <gnc-report.h>
 #include <gnc-locale-utils.hpp>
 
 namespace bl = boost::locale;
diff --git a/gnucash/report/CMakeLists.txt b/gnucash/report/CMakeLists.txt
index 454d88a2d..afb1a40cc 100644
--- a/gnucash/report/CMakeLists.txt
+++ b/gnucash/report/CMakeLists.txt
@@ -14,7 +14,7 @@ gnc_add_swig_guile_command (swig-report-c
 )
 
 set (report_SOURCES
-  gnc-report.c
+        gnc-report.cpp
 )
 
 add_library (gnc-report
diff --git a/gnucash/report/gnc-report.c b/gnucash/report/gnc-report.cpp
similarity index 91%
rename from gnucash/report/gnc-report.c
rename to gnucash/report/gnc-report.cpp
index 1fb932e09..544f01139 100644
--- a/gnucash/report/gnc-report.c
+++ b/gnucash/report/gnc-report.cpp
@@ -30,19 +30,22 @@
 #include <glib/gstdio.h>
 #include <gtk/gtk.h>
 #include <libguile.h>
+extern "C"
+{
 #include <stdio.h>
 #include <string.h>
-#include "gfec.h"
 #include <string.h>
 #include <errno.h>
 #include <fcntl.h>
 
-#include "gnc-filepath-utils.h"
-#include "gnc-guile-utils.h"
+#include <gfec.h>
+#include <gnc-filepath-utils.h>
+#include <gnc-guile-utils.h>
+#include <gnc-engine.h>
+}
 #include "gnc-report.h"
-#include "gnc-engine.h"
 
-extern SCM scm_init_sw_report_module(void);
+extern "C" SCM scm_init_sw_report_module(void);
 
 static QofLogModule log_module = GNC_MOD_GUI;
 
@@ -130,11 +133,11 @@ gnc_report_remove_by_id(gint id)
 
 SCM gnc_report_find(gint id)
 {
-    gpointer report = NULL;
+    SCM report = nullptr;
 
     if (reports)
     {
-        report = g_hash_table_lookup(reports, &id);
+        report = static_cast<SCM>(g_hash_table_lookup(reports, &id));
     }
 
     if (!report)
@@ -303,9 +306,6 @@ gnc_saved_reports_write_internal (const gchar *file, const gchar *contents, gboo
 {
     gboolean success = TRUE;
     gint fd;
-#ifndef __MINGW32__
-    extern int errno;
-#endif
     ssize_t written;
     gint length;
     gint flags = O_WRONLY | O_CREAT | (overwrite ? O_TRUNC : O_APPEND);
@@ -393,3 +393,27 @@ gnc_saved_reports_write_to_file (const gchar* report_def, gboolean overwrite)
 
     return success;
 }
+
+GncOptionDB*
+gnc_get_optiondb_from_dispatcher(SCM dispatcher)
+{
+    SCM  get_options = scm_c_eval_string("gnc:options-get");
+    if (dispatcher == SCM_BOOL_F)
+        return nullptr;
+    auto scm_ptr{scm_call_1(get_options, dispatcher)};
+    auto smob{!scm_is_null(scm_ptr) && SCM_INSTANCEP(scm_ptr) &&
+              scm_is_true(scm_slot_exists_p(scm_ptr, SCM_EOL)) ?
+              scm_slot_ref(scm_ptr, SCM_EOL) : (scm_ptr)};
+
+    void *c_ptr{nullptr};
+    if (!SCM_NULLP(smob))
+    {
+        if (SCM_POINTER_P(smob))
+            c_ptr = SCM_POINTER_VALUE(smob);
+        else
+            c_ptr = reinterpret_cast<void*>(SCM_CELL_WORD_1(smob));
+    }
+    auto u_ptr{static_cast<std::unique_ptr<GncOptionDB>*>(c_ptr)};
+    return u_ptr->get();
+}
+
diff --git a/gnucash/report/gnc-report.h b/gnucash/report/gnc-report.h
index b0e83dca2..5e2a40ff7 100644
--- a/gnucash/report/gnc-report.h
+++ b/gnucash/report/gnc-report.h
@@ -27,6 +27,11 @@
 
 #include <glib.h>
 #include <libguile.h>
+#ifdef __cplusplus
+#include <gnc-optiondb.hpp>
+extern "C"
+{
+#endif
 
 #define SAVED_REPORTS_FILE "saved-reports-2.8"
 #define SAVED_REPORTS_FILE_OLD_REV "saved-reports-2.4"
@@ -35,34 +40,54 @@
  *
  *  Should be called once before using any of its features.
  */
-void gnc_report_init (void);
+void gnc_report_init(void);
+
 
+gboolean gnc_run_report_with_error_handling(gint report_id,
+                                            gchar** data,
+                                            gchar** errmsg);
 
-gboolean gnc_run_report_with_error_handling (gint report_id,
-                                             gchar **data,
-                                             gchar **errmsg);
-gboolean gnc_run_report_id_string_with_error_handling (const char * id_string,
-                                                       char **data,
-                                                       gchar **errmsg);
+gboolean gnc_run_report_id_string_with_error_handling(const char* id_string,
+                                                      char** data,
+                                                      gchar** errmsg);
 
 /**
  * @param report The SCM version of the report.
  * @return a caller-owned copy of the name of the report, or NULL if report
  * is invalid.
  **/
-gchar* gnc_report_name( SCM report );
+gchar* gnc_report_name(SCM report);
 
 /* returns #f if the report id cannot be found */
 SCM gnc_report_find(gint id);
+
 void gnc_report_remove_by_id(gint id);
+
 gint gnc_report_add(SCM report);
 
 void gnc_reports_flush_global(void);
-GHashTable *gnc_reports_get_global(void);
+
+GHashTable* gnc_reports_get_global(void);
 
 gchar* gnc_get_default_report_font_family(void);
 
-gboolean gnc_saved_reports_backup (void);
-gboolean gnc_saved_reports_write_to_file (const gchar* report_def, gboolean overwrite);
+gboolean gnc_saved_reports_backup(void);
+
+gboolean gnc_saved_reports_write_to_file(const gchar* report_def, gboolean overwrite);
 
+#ifdef __cplusplus
+} //extern "C"
+/**
+ * Obtain a GncOptionDB* from Scheme
+ *
+ * When report or stylesheet options are generated in Scheme the GncObjectDB is
+ * wrapped in a std::unique_ptr and then in a Guile SMOB by SWIG. The GUI code
+ * needs a reference to the GncObjectDB and we don't want to introduce swig
+ * library dependencies.
+ *
+ * @param dispatch The scheme dispatch function returned by gnc:new-options
+ * @return GncOptiondDB* Do not free this pointer!
+ */
+GncOptionDB* gnc_get_optiondb_from_dispatcher(SCM dispatcher);
+#endif
 #endif
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 0de184906..8b060e01b 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -570,29 +570,6 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
         });
 }
 
-GncOptionDB*
-gnc_get_optiondb_from_dispatcher(SCM dispatcher)
-{
-    SCM  get_options = scm_c_eval_string("gnc:options-get");
-    if (dispatcher == SCM_BOOL_F)
-        return nullptr;
-    auto scm_ptr{scm_call_1(get_options, dispatcher)};
-    auto smob{!scm_is_null(scm_ptr) && SCM_INSTANCEP(scm_ptr) &&
-              scm_is_true(scm_slot_exists_p(scm_ptr, SCM_EOL)) ?
-              scm_slot_ref(scm_ptr, SCM_EOL) : (scm_ptr)};
-
-    void *c_ptr{nullptr};
-    if (!SCM_NULLP(smob))
-    {
-        if (SCM_POINTER_P(smob))
-            c_ptr = SCM_POINTER_VALUE(smob);
-        else
-            c_ptr = reinterpret_cast<void*>(SCM_CELL_WORD_1(smob));
-    }
-    auto u_ptr{static_cast<std::unique_ptr<GncOptionDB>*>(c_ptr)};
-    return u_ptr->get();
-}
-
 void
 gnc_register_string_option(GncOptionDB* db, const char* section,
                            const char* name, const char* key,
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index f828536e9..8078459d5 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -89,20 +89,6 @@ GncOptionDB* gnc_option_db_new(void);
  */
 void gnc_option_db_destroy(GncOptionDB* odb);
 
-/**
- * Obtain a GncOptionDB* from Scheme
- *
- * When report or stylesheet options are generated in Scheme the GncObjectDB is
- * wrapped in a std::unique_ptr and then in a Guile SMOB by SWIG. The GUI code
- * needs a reference to the GncObjectDB and we don't want to introduce swig
- * library dependencies.
- *
- * @param dispatch The scheme dispatch function returned by gnc:new-options
- * @return GncOptiondDB* Do not free this pointer!
- */
-GncOptionDB*
-gnc_get_optiondb_from_dispatcher(SCM dispatcher);
-
 /**
  * Write all changed ui_item values to their options.
  * @param odb The GncOptionDB.
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f1b1f1e59..9a0b099f1 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -439,7 +439,7 @@ gnucash/report/commodity-utilities.scm
 gnucash/report/eguile-html-utilities.scm
 gnucash/report/eguile.scm
 gnucash/report/eguile-utilities.scm
-gnucash/report/gnc-report.c
+gnucash/report/gnc-report.cpp
 gnucash/report/html-acct-table.scm
 gnucash/report/html-anytag.scm
 gnucash/report/html-chart.scm

commit d4c3c30b1a0e27a7d9f97b62b1ef447b104c3cf6
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Mar 12 17:58:23 2022 -0800

    Use GUIDs to represent QofInstances instead of pointers.
    
    Converting them to pointers for Scheme to use. Prevents
    dangling pointers when the user deletes a QofInstance as long as the
    Scheme report code re-fetches its pointers from the options when it's
    regenerated.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index f1da64f69..71ee5f3df 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1524,7 +1524,7 @@ create_option_widget<GncOptionUIType::DATE_BOTH>(GncOption& option,
                                      documentation, enclosing, packed);
 }
 
-using GncOptionAccountList = std::vector<const Account*>;
+using GncOptionAccountList = std::vector<GncGUID>;
 
 static void
 account_select_all_cb(GtkWidget *widget, gpointer data)
@@ -1604,8 +1604,12 @@ public:
         GList *acc_list = nullptr;
         const GncOptionAccountList& accounts =
             option.get_value<GncOptionAccountList>();
-        for (auto account : accounts)
-            acc_list = g_list_prepend(acc_list, static_cast<void*>(const_cast<Account*>(account)));
+        auto book{gnc_get_current_book()};
+        for (auto guid : accounts)
+        {
+            auto account{xaccAccountLookup(&guid, book)};
+            acc_list = g_list_prepend(acc_list, account);
+        }
         acc_list = g_list_reverse(acc_list);
         gnc_tree_view_account_set_selected_accounts(widget, acc_list, TRUE);
         g_list_free(acc_list);
@@ -1617,7 +1621,10 @@ public:
         GncOptionAccountList acc_vec;
         acc_vec.reserve(g_list_length(acc_list));
         for (auto node = acc_list; node; node = g_list_next(node))
-            acc_vec.push_back(static_cast<const Account*>(node->data));
+        {
+            auto guid{qof_entity_get_guid(node->data)};
+            acc_vec.push_back(*guid);
+        }
         g_list_free(acc_list);
         option.set_value(acc_vec);
     }
diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 9c1cb89cd..470be01f9 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -275,13 +275,20 @@ GncOptionAccountListValue::validate(const GncOptionAccountList& values) const
         return true;
     if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) &&
         values.size() != 1)
+    {
+        std::cerr << "GncOptionAccountListValue::validate: Multiple values for a non-multiselect option." << std::endl;
         return false;
+    }
     if (m_allowed.empty())
         return true;
-    for(auto account : values) {
+    auto book{gnc_get_current_book()};
+    for(auto& guid : values)
+    {
         if (std::find(m_allowed.begin(), m_allowed.end(),
-                      xaccAccountGetType(account)) == m_allowed.end())
-            return false;
+                      xaccAccountGetType(xaccAccountLookup(&guid, book))) == m_allowed.end())
+        {
+            std::cerr << "GncOptionAccountListValue::validate: Account " << gnc::GUID(guid).to_string() << " is not of an allowed type" << std::endl;
+            return false; }
     }
     return true;
 }
@@ -310,17 +317,33 @@ GncOptionAccountListValue::get_default_value() const
     if (!account_list)
         return retval;
 
+    auto book{gnc_get_current_book()};
     for (auto node = account_list; node; node = g_list_next (node))
+    {
         if (std::find(m_allowed.begin(), m_allowed.end(),
                       xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
         {
-            retval.push_back(GNC_ACCOUNT(node->data));
+            retval.push_back(*qof_entity_get_guid(GNC_ACCOUNT(node->data)));
             break;
         }
+    }
     g_list_free(account_list);
     return retval;
 }
 
+static bool
+operator==(const GncGUID& l, const GncGUID& r)
+{
+    return guid_equal(&l, &r);
+}
+
+bool
+GncOptionAccountListValue::is_changed() const noexcept
+{
+    return m_value != m_default_value;
+}
+
+
 
 /**
  * Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
@@ -354,15 +377,20 @@ GncOptionAccountSelValue::validate(const Account* value) const
 const Account*
 GncOptionAccountSelValue::get_value() const
 {
-    return m_value ? m_value : get_default_value();
+    auto book{gnc_get_current_book()};
+    return guid_equal(guid_null(), &m_value) ? get_default_value() :
+           xaccAccountLookup(&m_value, book);
 }
 
 const Account*
 GncOptionAccountSelValue::get_default_value() const
 {
 
-    if (m_default_value)
-        return m_default_value;
+    if (!guid_equal(guid_null(), &m_default_value))
+    {
+        auto book{gnc_get_current_book()};
+        return xaccAccountLookup(&m_default_value, book);
+    }
 
     /* If no default has been set and there's an allowed set then find the first
      * account that matches one of the allowed account types.
@@ -713,7 +741,7 @@ GncOptionAccountListValue::serialize() const noexcept
         if (!first)
             retval += " ";
         first = false;
-        retval += qof_instance_to_string(QOF_INSTANCE(val));
+        retval += guid_to_string(&val);
     }
     return retval;
 }
@@ -732,8 +760,9 @@ GncOptionAccountListValue::deserialize(const std::string& str) noexcept
         if (!first)
             ++pos;
         first = false;
-        auto ptr = qof_instance_from_string(str.substr(pos, pos + GUID_ENCODING_LENGTH), get_ui_type());
-        m_value.push_back(reinterpret_cast<Account*>(ptr));
+        GncGUID guid{};
+        string_to_guid(str.substr(pos, pos + GUID_ENCODING_LENGTH).c_str(), &guid);
+        m_value.push_back(guid);
         pos += GUID_ENCODING_LENGTH;
     }
     return true;
@@ -743,7 +772,7 @@ std::string
 GncOptionAccountSelValue::serialize() const noexcept
 {
     static const std::string no_value{"No Value"};
-    return m_value ?qof_instance_to_string(QOF_INSTANCE(m_value)) : no_value;
+    return guid_equal(guid_null(), &m_value) ? no_value : guid_to_string(&m_value);
 }
 
 bool
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 9e74b1f81..a784aa7cf 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -717,7 +717,7 @@ operator>> <GncOptionMultichoiceValue>(std::istream& iss,
 }
 
 
-using GncOptionAccountList = std::vector<const Account*>;
+using GncOptionAccountList = std::vector<GncGUID>;
 
 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
 
@@ -795,7 +795,7 @@ public:
     }
     GList* account_type_list() const noexcept;
     void reset_default_value() { m_value = m_default_value; }
-    bool is_changed() const noexcept { return m_value != m_default_value; }
+    bool is_changed() const noexcept;
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
     bool is_multiselect() const noexcept { return m_multiselect; }
@@ -821,7 +821,7 @@ operator<< <GncOptionAccountListValue>(std::ostream& oss,
             first = false;
         else
             oss << " ";
-        oss << qof_instance_to_string(QOF_INSTANCE(value));
+        oss << guid_to_string(&value);
     }
     return oss;
 }
@@ -836,7 +836,10 @@ operator>> <GncOptionAccountListValue>(std::istream& iss,
         std::string str;
         std::getline(iss, str, ' ');
         if (!str.empty())
-            values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type()));
+        {
+            auto guid{qof_entity_get_guid(qof_instance_from_string(str, opt.get_ui_type()))};
+            values.push_back(*guid);
+        }
         else
             break;
     }
@@ -856,32 +859,32 @@ public:
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type) :
         OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
-        m_value{}, m_default_value{}, m_allowed{} {}
+        m_value{*guid_null()}, m_default_value{*guid_null()}, m_allowed{} {}
 
     GncOptionAccountSelValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           const Account* value) :
         OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
-        m_value{const_cast<Account*>(value)},
-        m_default_value{const_cast<Account*>(value)}, m_allowed{} {}
+        m_value{*qof_entity_get_guid(value)},
+        m_default_value{*qof_entity_get_guid(value)}, m_allowed{} {}
     GncOptionAccountSelValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           GncOptionAccountTypeList&& allowed) :
         OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
-        m_value{}, m_default_value{}, m_allowed{std::move(allowed)} {}
+        m_value{*guid_null()}, m_default_value{*guid_null()},
+        m_allowed{std::move(allowed)} {}
     GncOptionAccountSelValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           const Account* value,
                           GncOptionAccountTypeList&& allowed) :
         OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
-        m_value{}, m_default_value{}, m_allowed{std::move(allowed)} {
+        m_value{*guid_null()}, m_default_value{*guid_null()}, m_allowed{std::move(allowed)} {
             if (!validate(value))
                 throw std::invalid_argument("Supplied Value not in allowed set.");
-            m_value = const_cast<Account*>(value);
-            m_default_value = const_cast<Account*>(value);
+            m_value = m_default_value = *qof_entity_get_guid(value);
         }
 
     const Account* get_value() const;
@@ -889,25 +892,31 @@ public:
     bool validate (const Account* value) const;
     void set_value (const Account* value) {
         if (validate(value))
-            //throw!
-            m_value = const_cast<Account*>(value);
+        {
+            auto guid{qof_entity_get_guid(value)};
+            m_value = *guid;
+        }
+        //else throw
     }
     void set_default_value (const Account* value) {
         if (validate(value))
-            //throw!
-            m_value = m_default_value = const_cast<Account*>(value);
+        {
+            auto guid{qof_entity_get_guid(value)};
+            m_value = m_default_value = *guid;
+        }
+        //else throw
     }
     GList* account_type_list() const noexcept;
     void reset_default_value() { m_value = m_default_value; }
-    bool is_changed() const noexcept { return m_value != m_default_value; }
+    bool is_changed() const noexcept { return !guid_equal(&m_value, &m_default_value); }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
     std::string serialize() const noexcept;
     bool deserialize(const std::string& str) noexcept;
 private:
     GncOptionUIType m_ui_type;
-    Account* m_value;
-    Account* m_default_value;
+    GncGUID m_value;
+    GncGUID m_default_value;
     GncOptionAccountTypeList m_allowed;
 };
 
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 510b58a86..0de184906 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -28,6 +28,7 @@
 #include <sstream>
 #include <kvp-value.hpp>
 #include <qofbookslots.h>
+#include <guid.hpp>
 #include "gnc-optiondb.h"
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
@@ -708,6 +709,11 @@ gnc_register_account_list_limited_option(GncOptionDB* db,
 {
     try
     {
+        std::cout << "gnc_register_account_list_limited_option for accounts ";
+        std::for_each(value.begin(), value.end(), [](auto& guid){
+            std::cout << gnc::GUID(guid).to_string() << " ";
+        });
+        std::cout << std::endl;
         GncOption option{GncOptionAccountListValue{section, name, key, doc_string,
                     GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}};
         db->register_option(section, std::move(option));
@@ -729,7 +735,7 @@ find_children(Account* account, void* data)
     const GncOptionAccountTypeList& types = datapair->second;
     if (std::find(types.begin(), types.end(),
                   xaccAccountGetType(account)) != types.end())
-        list.push_back(account);
+        list.push_back(*qof_entity_get_guid(account));
 }
 
 GncOptionAccountList
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index b274155ad..86a6aedbe 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -54,7 +54,7 @@ extern "C"
 
 class GncOptionDB;
 using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
-using GncOptionAccountList = std::vector<const Account*>;
+using GncOptionAccountList = std::vector<GncGUID>;
 
 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
 using GncMultichoiceOptionEntry = std::tuple<const std::string,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index cebe26923..7415e3176 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -428,7 +428,10 @@ scm_to_value<GncOptionAccountList>(SCM new_value)
         void* account{};
         SWIG_ConvertPtr(node, &account, SWIGTYPE_p_Account, 0);
         if (account)
-            retval.push_back(static_cast<Account*>(account));
+        {
+            auto guid{qof_entity_get_guid(static_cast<Account*>(account))};
+            retval.push_back(*guid);
+        }
         next = scm_cdr(next);
         if (scm_is_null(next))
             break;
@@ -440,9 +443,13 @@ template <>inline SCM
 scm_from_value<GncOptionAccountList>(GncOptionAccountList value)
 {
     SCM s_list = SCM_EOL;
-    for (auto acct : value)
+    auto book{gnc_get_current_book()};
+    for (auto guid : value)
+    {
+        auto acct{xaccAccountLookup(&guid, book)};
         s_list = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0),
                           s_list);
+    }
     return scm_reverse(s_list);
 }
 
@@ -452,13 +459,14 @@ void gnc_option_test_book_destroy(QofBook*);
 QofBook*
 gnc_option_test_book_new()
 {
-    return static_cast<QofBook*>(g_object_new(QOF_TYPE_BOOK, nullptr));
+    auto session = gnc_get_current_session();
+    return gnc_get_current_book();
 }
 
 void
 gnc_option_test_book_destroy(QofBook* book)
 {
-    g_object_unref(book);
+    gnc_clear_current_session();
 }
 
 %}
@@ -574,7 +582,8 @@ gnc_option_test_book_destroy(QofBook* book)
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));
         Account* acct = (Account*)SWIG_MustGetPtr(s_account,
                                                   SWIGTYPE_p_Account, 1, 0);
-        $1.push_back(acct);
+        if (acct)
+            $1.push_back(*qof_entity_get_guid(acct));
     }
 }
 
@@ -602,7 +611,7 @@ gnc_option_test_book_destroy(QofBook* book)
     $1 = &types;
 }
 
-%typemap(in) GncOptionAccountList
+%typemap(in) GncOptionAccountList const & (GncOptionAccountList alist)
 {
     auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
     for (std::size_t i = 0; i < len; ++i)
@@ -610,8 +619,10 @@ gnc_option_test_book_destroy(QofBook* book)
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));
         Account* acct = (Account*)SWIG_MustGetPtr(s_account,
                                                   SWIGTYPE_p_Account, 1, 0);
-        $1.push_back(acct);
+        if (acct)
+            alist.push_back(*qof_entity_get_guid(acct));
     }
+    $1 = &alist;
 }
 
 %typemap(in) GncOptionAccountList& (GncOptionAccountList acclist)
@@ -623,7 +634,7 @@ gnc_option_test_book_destroy(QofBook* book)
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));
         Account* acct = (Account*)SWIG_MustGetPtr(s_account,
                                                   SWIGTYPE_p_Account, 1, 0);
-        acclist.push_back(acct);
+        acclist.push_back(*qof_entity_get_guid(acct));
     }
     $1 = &acclist;
 }
@@ -631,18 +642,26 @@ gnc_option_test_book_destroy(QofBook* book)
 %typemap(out) GncOptionAccountList
 {
     $result = SCM_EOL;
-    for (auto acct : $1)
+    auto book{gnc_get_current_book()};
+    for (auto guid : $1)
+    {
+        auto acct{xaccAccountLookup(&guid, book)};
         $result = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0),
                            $result);
+    }
     $result = scm_reverse($result);
 }
 
-%typemap(out) GncOptionAccountList&
+%typemap(out) const GncOptionAccountList&
 {
     $result = SCM_EOL;
-    for (auto acct : *$1)
+    auto book{gnc_get_current_book()};
+    for (auto guid : *$1)
+    {
+        auto acct{xaccAccountLookup(&guid, book)};
         $result = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0),
                            $result);
+    }
     $result = scm_reverse ($result)
 }
 
@@ -1080,17 +1099,17 @@ inline SCM return_scm_value(ValueType value)
                               GncOptionAccountListValue>)
                 {
                     static const SCM list_format_str{scm_from_utf8_string("'~s")};
-                    auto acct_list{option.get_value()};
-                    if (acct_list.empty())
+                    auto guid_list{option.get_value()};
+                    if (guid_list.empty())
                         return no_value;
-                    SCM guid_list{SCM_EOL};
-                    for(auto acct : acct_list)
+                    SCM string_list{SCM_EOL};
+                    for(auto guid : guid_list)
                     {
-                        auto acct_str{qof_instance_to_string(QOF_INSTANCE(acct))};
-                        auto acct_scm{scm_from_utf8_string(acct_str.c_str())};
-                        guid_list = scm_cons(acct_scm, guid_list);
+                        auto guid_str{guid_to_string(&guid)};
+                        auto guid_scm{scm_from_utf8_string(guid_str)};
+                        string_list = scm_cons(guid_scm, string_list);
                     }
-                    return scm_simple_format(SCM_BOOL_F, list_format_str, scm_list_1(guid_list));
+                    return scm_simple_format(SCM_BOOL_F, list_format_str, scm_list_1(string_list));
 
                 }
                 if constexpr (is_QofInstanceValue_v<decltype(option)>)
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 94df08f7a..49959de90 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -515,7 +515,7 @@ find_children(Account* account, void* data)
     const GncOptionAccountTypeList& types = datapair->second;
     if (std::find(types.begin(), types.end(),
                   xaccAccountGetType(account)) != types.end())
-        list.push_back(account);
+        list.push_back(*qof_entity_get_guid(account));
 }
 
 class GncOptionAccountTest : public ::testing::Test
@@ -571,6 +571,12 @@ protected:
     Account* m_root;
 };
 
+static bool
+operator==(const GncGUID& l, const GncGUID& r)
+{
+    return guid_equal(&l, &r);
+}
+
 TEST_F(GncOptionAccountTest, test_test_constructor_and_destructor)
 {
     EXPECT_TRUE(m_book != NULL);
@@ -662,9 +668,9 @@ TEST_F(GncOptionAccountTest, test_account_list_out)
                                            GncOptionUIType::ACCOUNT_LIST,
                                            acclist}};
     std::ostringstream oss;
-    std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
+    std::string acc_guids{gnc::GUID{acclist[0]}.to_string()};
     acc_guids += " ";
-    acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
+    acc_guids += gnc::GUID{acclist[1]}.to_string();
 
     oss << option;
     EXPECT_EQ(acc_guids, oss.str());
@@ -675,7 +681,7 @@ TEST_F(GncOptionAccountTest, test_account_list_out)
                                                GncOptionUIType::ACCOUNT_LIST,
                                                accsel,
                                                GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
-    acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string();
+    acc_guids = gnc::GUID{accsel[0]}.to_string();
 
     oss.str("");
     oss << sel_option;
@@ -688,9 +694,9 @@ TEST_F(GncOptionAccountTest, test_account_list_in)
     GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option",
                                            GncOptionUIType::ACCOUNT_LIST,
                                            acclist}};
-    std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
+    std::string acc_guids{gnc::GUID{acclist[0]}.to_string()};
     acc_guids += " ";
-    acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
+    acc_guids += gnc::GUID{acclist[1]}.to_string();
 
     std::istringstream iss{acc_guids};
     iss >> option;
@@ -703,7 +709,7 @@ TEST_F(GncOptionAccountTest, test_account_list_in)
                                                accsel,
                                                GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
-    acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string();
+    acc_guids = gnc::GUID{acclistbad[1]}.to_string();
     acc_guids += " ";
 
     iss.str(acc_guids);
@@ -711,7 +717,7 @@ TEST_F(GncOptionAccountTest, test_account_list_in)
     EXPECT_EQ(accsel, sel_option.get_value<GncOptionAccountList>());
 
     iss.clear();  //Reset the failedbit from the invalid selection type.
-    acc_guids = gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
+    acc_guids = gnc::GUID{acclist[1]}.to_string();
     EXPECT_NO_THROW({
             iss.str(acc_guids);
             iss >> sel_option;
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 15a3cca4e..74d56b33b 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -86,10 +86,12 @@ TEST_F(GncOptionDBTest, test_register_string_option)
  */
 
 
-struct AccountTestBook
+struct GncOptionDBAccountTest : public ::testing::Test
 {
-    AccountTestBook() :
-        m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)}
+    GncOptionDBAccountTest() :
+        m_sess{gnc_get_current_session()}, m_book{gnc_get_current_book()},
+        m_root{gnc_account_create_root(m_book)},
+        m_db{std::make_unique<GncOptionDB>()}
     {
         auto create_account = [this](Account* parent, GNCAccountType type,
                                        const char* name)->Account* {
@@ -117,31 +119,31 @@ struct AccountTestBook
         create_account(expenses, ACCT_TYPE_EXPENSE, "Gas");
         create_account(expenses, ACCT_TYPE_EXPENSE, "Rent");
    }
-    ~AccountTestBook()
+    ~GncOptionDBAccountTest()
     {
         xaccAccountBeginEdit(m_root);
         xaccAccountDestroy(m_root); //It does the commit
-        qof_book_destroy(m_book);
+        gnc_clear_current_session();
     }
 
+    QofSession* m_sess;
     QofBook* m_book;
     Account* m_root;
+    GncOptionDBPtr m_db;
 };
 
-TEST_F(GncOptionDBTest, test_register_account_list_option)
+TEST_F(GncOptionDBAccountTest, test_register_account_list_option)
 {
-    AccountTestBook book;
-    auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
+    auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
     gnc_register_account_list_option(m_db, "foo", "bar", "baz",
                                      "Phony Option", acclist);
     EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
     EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(3));
 }
 
-TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
+TEST_F(GncOptionDBAccountTest, test_register_account_list_limited_option)
 {
-    AccountTestBook book;
-    auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
+    auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_STOCK});
@@ -149,10 +151,9 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
     EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(3));
 }
 
-TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
+TEST_F(GncOptionDBAccountTest, test_register_account_sel_limited_option)
 {
-    AccountTestBook book;
-    auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
+    auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
     GncOptionAccountList accsel{acclist[2]};
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", accsel,
@@ -161,10 +162,9 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
     EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(0));
 }
 
-TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct)
+TEST_F(GncOptionDBAccountTest, test_register_account_sel_limited_option_fail_construct)
 {
-    AccountTestBook book;
-    auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
+    auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
     GncOptionAccountList accsel{acclist[2]};
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option",
                                      accsel, {ACCT_TYPE_BANK});
@@ -276,6 +276,12 @@ TEST_F(GncOptionDBTest, test_register_start_date_option)
 
 }
 
+static bool
+operator==(const GncGUID& l, const GncGUID& r)
+{
+    return guid_equal(&l, &r);
+}
+
 class GncOptionDBIOTest : public ::testing::Test
 {
 protected:
@@ -322,7 +328,8 @@ protected:
                                  RelativeDatePeriod::START_CURRENT_QUARTER);
         gnc_register_account_list_option(m_db, "quux", "xyzzy", "second",
                                          "Phony AccountList Option",
-                                         {aapl, hpe});
+                                         {*qof_entity_get_guid(aapl),
+                                          *qof_entity_get_guid(hpe)});
     }
 
     ~GncOptionDBIOTest()
diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
index c582be5a4..8b070433f 100644
--- a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
+++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
@@ -315,7 +315,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
             (test-template test-account-list-output-template)
             (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK))))
         (gnc:option-set-value option new-acclist)
-        (test-equal "account list form"
+        (test-equal "account list form" ;;fails
                     (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
@@ -338,7 +338,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
       (let ((option (gnc:lookup-option odb "foo" "bar"))
             (test-template test-string-output-template))
         (gnc:option-set-value option bank)
-        (test-equal "account sel form"
+        (test-equal "account sel form" ;; fails
                     (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 30b5da1d0..a1296b885 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -112,16 +112,16 @@
         (test-equal (car acctlist) (car acct-list))) )))
 
   (define (test-make-account-list-limited-option book)
-    (test-group "test-make-account-list-option"
+    (test-group "test-make-account-list-limited-option"
     (let ((option-db (new-gnc-optiondb))
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
-      (gnc-register-account-list-limited-option
+      (gnc-register-account-list-limited-option ;; Error not account type twice
        option-db "foo" "bar" "baz"
        "Phony Option" acctlist (list ACCT-TYPE-STOCK))
       (let ((acct-list (gnc-option-value option-db "foo" "bar")))
-        (test-equal (length acctlist) (length acct-list))
-        (test-equal (cadr acctlist) (cadr acct-list)))
+        (test-equal (length acctlist) (length acct-list)) ;; fails acct-list 4 vs #f
+        (test-equal (cadr acctlist) (cadr acct-list)))    ;; fails () vs. #f both wrong
       (gnc-register-account-list-limited-option
        option-db "waldo" "pepper" "baz"
        "Phony Option" acctlist (list ACCT-TYPE-BANK))

commit a7a643f7f2f364228d57eda935e9b9d2c8fa2f84
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 3 14:47:18 2022 -0800

    Store option commodities and namespace and mnemonic instead of pointer.
    
    Protects against crashes caused by the user deleting the commodity and
    allows the option to work if a deleted commodity is recreated.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index f8905edaa..9c1cb89cd 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -127,47 +127,18 @@ GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept
 {
     QofInstance* inst{};
     // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic
-    if (m_ui_type == GncOptionUIType::CURRENCY ||
-        m_ui_type == GncOptionUIType::COMMODITY)
-    {
-        auto book{gnc_get_current_book()};
-        auto table = gnc_commodity_table_get_table(book);
-        auto sep{str.find(":")};
-        if (sep != std::string::npos)
-        {
-            auto name_space{str.substr(0, sep)};
-            auto mnemonic{str.substr(sep + 1, -1)};
-            inst = QOF_INSTANCE(gnc_commodity_table_lookup(table,
-                                                              name_space.c_str(),
-                                                              mnemonic.c_str()));
-        }
-        if (!inst && m_ui_type == GncOptionUIType::CURRENCY)
-            inst = QOF_INSTANCE(gnc_commodity_table_lookup(table,
-                                                             "CURRENCY",
-                                                             str.c_str()));
+    try {
+        auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
+        inst = qof_instance_from_guid(&guid, m_ui_type);
         if (inst)
         {
             m_value = make_gnc_item(inst);
             return true;
         }
     }
-
-    if (!inst)
+    catch (const gnc::guid_syntax_exception& err)
     {
-        try {
-            auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
-            inst = qof_instance_from_guid(&guid, m_ui_type);
-            if (inst)
-            {
-                auto coll{qof_instance_get_collection(inst)};
-                m_value = std::make_pair(qof_collection_get_type(coll), guid);
-                return true;
-            }
-        }
-        catch (const gnc::guid_syntax_exception& err)
-        {
-            PWARN("Failed to convert %s to a GUID", str.c_str());
-        }
+        PWARN("Failed to convert %s to a GUID", str.c_str());
     }
     return false;
 }
@@ -200,6 +171,103 @@ GncOptionQofInstanceValue::serialize() const noexcept
     return retval;
 }
 
+static gnc_commodity*
+gnc_commodity_from_namespace_and_mnemonic(std::string_view name_space,
+                                          std::string_view mnemonic)
+{
+    auto book{gnc_get_current_book()};
+    auto table = gnc_commodity_table_get_table(book);
+    return gnc_commodity_table_lookup(table, name_space.data(),
+                                      mnemonic.data());
+}
+
+gnc_commodity*
+GncOptionCommodityValue::get_value() const
+{
+    return gnc_commodity_from_namespace_and_mnemonic(m_namespace, m_mnemonic);
+}
+
+gnc_commodity*
+GncOptionCommodityValue::get_default_value() const
+{
+    return gnc_commodity_from_namespace_and_mnemonic(m_default_namespace,
+                                                     m_default_mnemonic);
+}
+
+void
+GncOptionCommodityValue::set_value(gnc_commodity* value)
+{
+    if (!validate(value))
+        throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
+    m_mnemonic = gnc_commodity_get_mnemonic(value);
+    m_namespace = gnc_commodity_get_namespace(value);
+}
+
+void
+GncOptionCommodityValue::set_default_value(gnc_commodity* value)
+{
+    if (!validate(value))
+        throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
+    m_mnemonic = m_default_mnemonic = gnc_commodity_get_mnemonic(value);
+    m_namespace = m_default_namespace = gnc_commodity_get_namespace(value);
+}
+
+void
+GncOptionCommodityValue::reset_default_value()
+{
+    m_mnemonic = m_default_mnemonic;
+    m_namespace = m_default_namespace;
+}
+
+bool
+GncOptionCommodityValue::is_changed() const noexcept
+{
+    return m_namespace == m_default_namespace && m_mnemonic == m_default_mnemonic;
+}
+
+bool
+GncOptionCommodityValue::validate(gnc_commodity* comm) const noexcept
+{
+    if (!GNC_IS_COMMODITY(comm))
+        return false;
+    if (m_is_currency && !gnc_commodity_is_currency(comm))
+        return false;
+    return true;
+}
+
+std::string
+GncOptionCommodityValue::serialize() const noexcept
+{
+    if (m_is_currency)
+        return m_mnemonic;
+    else
+        return m_namespace + ":" + m_mnemonic;
+}
+
+bool
+GncOptionCommodityValue::deserialize(const std::string& str) noexcept
+{
+   auto sep{str.find(":")};
+    gnc_commodity* comm{};
+    std::string mnemonic, name_space;
+    if (sep != std::string::npos)
+    {
+        name_space = str.substr(0, sep);
+        mnemonic = str.substr(sep + 1, -1);
+    }
+    else
+    {
+        name_space = "CURRENCY";
+        mnemonic = str;
+    }
+    comm = gnc_commodity_from_namespace_and_mnemonic(name_space, mnemonic);
+    if (!validate(comm))
+        return false;
+    m_namespace = std::move(name_space);
+    m_mnemonic = std::move(mnemonic);
+    return true;
+}
+
 bool
 GncOptionAccountListValue::validate(const GncOptionAccountList& values) const
 {
@@ -470,10 +538,6 @@ qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
     QofIdType qof_type;
     switch(type)
     {
-        case GncOptionUIType::CURRENCY:
-        case GncOptionUIType::COMMODITY:
-            qof_type = "Commodity";
-            break;
         case GncOptionUIType::BUDGET:
             qof_type = "Budget";
             break;
@@ -510,37 +574,13 @@ QofInstance*
 qof_instance_from_string(const std::string& str, GncOptionUIType type)
 {
     QofInstance* retval{nullptr};
-    // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic
-    if (type == GncOptionUIType::CURRENCY ||
-        type == GncOptionUIType::COMMODITY)
-    {
-        auto book{gnc_get_current_book()};
-        auto table = gnc_commodity_table_get_table(book);
-        auto sep{str.find(":")};
-        if (sep != std::string::npos)
-        {
-            auto name_space{str.substr(0, sep)};
-            auto mnemonic{str.substr(sep + 1, -1)};
-            retval = QOF_INSTANCE(gnc_commodity_table_lookup(table,
-                                                             name_space.c_str(),
-                                                             mnemonic.c_str()));
-        }
-        if (!retval && type == GncOptionUIType::CURRENCY)
-            retval = QOF_INSTANCE(gnc_commodity_table_lookup(table,
-                                                             "CURRENCY",
-                                                             str.c_str()));
+    try {
+        auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
+        retval = qof_instance_from_guid(&guid, type);
     }
-
-    if (!retval)
+    catch (const gnc::guid_syntax_exception& err)
     {
-        try {
-            auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
-            retval = qof_instance_from_guid(&guid, type);
-        }
-        catch (const gnc::guid_syntax_exception& err)
-        {
-            PWARN("Failed to convert %s to a GUID", str.c_str());
-        }
+        PWARN("Failed to convert %s to a GUID", str.c_str());
     }
     return retval;
 }
@@ -549,26 +589,8 @@ std::string
 qof_instance_to_string(const QofInstance* inst)
 {
     std::string retval;
-    if (GNC_IS_COMMODITY(inst))
-    {
-        auto commodity{GNC_COMMODITY(inst)};
-        if (!gnc_commodity_is_currency(commodity))
-        {
-            auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))};
-            if (name_space && *name_space != '\0')
-            {
-                retval = name_space;
-                retval += ":";
-            }
-        }
-        retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst));
-        return retval;
-    }
-    else
-    {
-        gnc::GUID guid{*qof_instance_get_guid(inst)};
-        retval = guid.to_string();
-    }
+    gnc::GUID guid{*qof_instance_get_guid(inst)};
+    retval = guid.to_string();
     return retval;
 }
 
@@ -678,41 +700,6 @@ GncOptionValue<SCM>::reset_default_value()
     m_value = m_default_value;
     scm_gc_protect_object(m_value);
 }
-
-template <typename ValueType> std::string
-GncOptionValidatedValue<ValueType>::serialize() const noexcept
-{
-    static const std::string no_value{"No Value"};
-    if constexpr(std::is_same_v<ValueType, const QofInstance*>)
-        return m_value ? qof_instance_to_string(m_value) : no_value;
-    else if constexpr(is_same_decayed_v<ValueType, std::string>)
-        return m_value;
-    else if constexpr(is_same_decayed_v<ValueType, bool>)
-        return m_value ? "True" : "False";
-    else if constexpr(std::is_arithmetic_v<ValueType>)
-        return std::to_string(m_value);
-    else
-        return "Invalid Value Type";
-}
-
-template <typename ValueType> bool
-GncOptionValidatedValue<ValueType>::deserialize(const std::string& str) noexcept
-{
-    if constexpr(is_same_decayed_v<ValueType, std::string>)
-        set_value(str);
-    else if constexpr(is_same_decayed_v<ValueType, bool>)
-        set_value(str == "True");
-    else if constexpr(is_same_decayed_v<ValueType, int>)
-        set_value(stoi(str));
-    else if constexpr(is_same_decayed_v<ValueType, int64_t>)
-        set_value(stoll(str));
-    else if constexpr(is_same_decayed_v<ValueType, double>)
-        set_value(stod(str));
-    else
-        return false;
-    return true;
-}
-
 std::string
 GncOptionAccountListValue::serialize() const noexcept
 {
@@ -881,6 +868,16 @@ GncOptionDateValue::deserialize(const std::string& str) noexcept
     }
 }
 
+std::istream&
+operator>> (std::istream& iss, GncOptionCommodityValue& opt)
+{
+    std::string instr;
+    iss >> instr;
+    if (!opt.deserialize(instr))
+        throw std::invalid_argument("Invalid commodity string in stream.");
+    return iss;
+}
+
 template GncOptionValue<bool>::GncOptionValue(const GncOptionValue<bool>&);
 template GncOptionValue<int>::GncOptionValue(const GncOptionValue<int>&);
 template GncOptionValue<int64_t>::GncOptionValue(const GncOptionValue<int64_t>&);
@@ -944,16 +941,6 @@ template std::string GncOptionValue<std::string>::serialize() const noexcept;
 template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
 template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
 template std::string GncOptionValue<SCM>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<bool>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<int>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<int64_t>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<double>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<char*>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<const char*>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<std::string>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<const QofInstance*>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<const QofQuery*>::serialize() const noexcept;
-template std::string GncOptionValidatedValue<const GncOwner*>::serialize() const noexcept;
 template std::string GncOptionRangeValue<int>::serialize() const noexcept;
 template std::string GncOptionRangeValue<double>::serialize() const noexcept;
 template bool GncOptionValue<bool>::deserialize(const std::string&) noexcept;
@@ -966,15 +953,5 @@ template bool GncOptionValue<std::string>::deserialize(const std::string&) noexc
 template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<SCM>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<bool>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<int>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<int64_t>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<double>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<char*>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<const char*>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<std::string>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<const QofInstance*>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<const QofQuery*>::deserialize(const std::string&) noexcept;
-template bool GncOptionValidatedValue<const GncOwner*>::deserialize(const std::string&) noexcept;
 template bool GncOptionRangeValue<int>::deserialize(const std::string&) noexcept;
 template bool GncOptionRangeValue<double>::deserialize(const std::string&) noexcept;
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 253bc4d75..9e74b1f81 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -172,75 +172,54 @@ private:
     GncItem m_default_value;
 };
 
-/** class GncOptionValidatedValue
- *  Validated values have an additional member function, provided as a
- *  constructor argument, that checks value parameters for some property before
- *  setting the object's value member. If the function returns false a
- *  std::invalid_argument exception is thrown.
+/** class GncOptionCommodityValue
+ * Commodities are stored with their namespace and mnemonic instead of their gncGIUD
+ * so that they can be correctly retrieved even if they're deleted and recreated.
+ * Additionally if GncOptionCommodityValue is created with GncOptionUIType::CURRENCY
+ * it will throw std::invalid_argument if one attempts to set a value that isn't a
+ * currency.
  */
-template <typename ValueType>
-class GncOptionValidatedValue : public OptionClassifier
+
+class GncOptionCommodityValue : public OptionClassifier
 {
 public:
-    GncOptionValidatedValue<ValueType>() = delete;
-    GncOptionValidatedValue<ValueType>(const char* section, const char* name,
+    GncOptionCommodityValue() = delete;
+    GncOptionCommodityValue(const char* section, const char* name,
                                        const char* key, const char* doc_string,
-                                       ValueType value,
-                                       std::function<bool(ValueType)>validator,
-                                       GncOptionUIType ui_type = GncOptionUIType::INTERNAL
-        ) :
+                                       gnc_commodity* value,
+                                       GncOptionUIType ui_type = GncOptionUIType::COMMODITY) :
         OptionClassifier{section, name, key, doc_string},
-        m_ui_type{ui_type}, m_value{value}, m_default_value{value},
-        m_validator{validator}
-        {
-            if (!this->validate(value))
-            throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
-        }
-    GncOptionValidatedValue<ValueType>(const char* section, const char* name,
-                                       const char* key, const char* doc_string,
-                                       ValueType value,
-                                       std::function<bool(ValueType)>validator,
-                                       ValueType val_data) :
-        OptionClassifier{section, name, key, doc_string},
-        m_ui_type{GncOptionUIType::INTERNAL}, m_value{value},
-        m_default_value{value}, m_validator{validator}, m_validation_data{val_data}
-    {
-            if (!this->validate(value))
-            throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
-    }
-    GncOptionValidatedValue<ValueType>(const GncOptionValidatedValue<ValueType>&) = default;
-    GncOptionValidatedValue<ValueType>(GncOptionValidatedValue<ValueType>&&) = default;
-    GncOptionValidatedValue<ValueType>& operator=(const GncOptionValidatedValue<ValueType>&) = default;
-    GncOptionValidatedValue<ValueType>& operator=(GncOptionValidatedValue<ValueType>&&) = default;
-    ValueType get_value() const { return m_value; }
-    ValueType get_default_value() const { return m_default_value; }
-    bool validate(ValueType value) const { return m_validator(value); }
-    void set_value(ValueType value)
-    {
-        if (this->validate(value))
-            m_value = value;
-        else
-            throw std::invalid_argument("Validation failed, value not set.");
-    }
-    void set_default_value(ValueType value)
+        m_ui_type{ui_type}, m_is_currency{ui_type == GncOptionUIType::CURRENCY},
+        m_namespace{gnc_commodity_get_namespace(value)},
+        m_mnemonic{gnc_commodity_get_mnemonic(value)},
+        m_default_namespace{gnc_commodity_get_namespace(value)},
+        m_default_mnemonic{gnc_commodity_get_mnemonic(value)}
     {
-        if (this->validate(value))
-            m_value = m_default_value = value;
-        else
-            throw std::invalid_argument("Validation failed, value not set.");
+       if (!validate(value))
+            throw std::invalid_argument("Attempt to create GncOptionCommodityValue with currency UIType and non-currency value.");
     }
-    void reset_default_value() { m_value = m_default_value; }
-    bool is_changed() const noexcept { return m_value != m_default_value; }
+    GncOptionCommodityValue(const GncOptionCommodityValue&) = default;
+    GncOptionCommodityValue(GncOptionCommodityValue&&) = default;
+    GncOptionCommodityValue& operator=(const GncOptionCommodityValue&) = default;
+    GncOptionCommodityValue& operator=(GncOptionCommodityValue&&) = default;
+    gnc_commodity* get_value() const;
+    gnc_commodity* get_default_value() const;
+    bool validate(gnc_commodity*) const noexcept;
+    void set_value(gnc_commodity* value);
+    void set_default_value(gnc_commodity* value);
+    void reset_default_value();
+    bool is_changed() const noexcept;
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
     std::string serialize() const noexcept;
     bool deserialize(const std::string& str) noexcept;
 private:
     GncOptionUIType m_ui_type;
-    ValueType m_value;
-    ValueType m_default_value;
-    std::function<bool(ValueType)> m_validator;                         //11
-    ValueType m_validation_data;
+    bool m_is_currency;
+    std::string m_namespace;
+    std::string m_mnemonic;
+    std::string m_default_namespace;
+    std::string m_default_mnemonic;
 };
 
 QofInstance* qof_instance_from_string(const std::string& str,
@@ -252,9 +231,7 @@ template <typename T>
 struct is_QofInstanceValue
 {
     static constexpr bool value =
-         (std::is_same_v<std::decay_t<T>, GncOptionQofInstanceValue> ||
-          std::is_same_v<std::decay_t<T>,
-          GncOptionValidatedValue<const QofInstance*>>);
+        std::is_same_v<std::decay_t<T>, GncOptionQofInstanceValue>;
 };
 
 template <typename T> inline constexpr bool
@@ -264,9 +241,7 @@ template <typename T>
 struct is_QofQueryValue
 {
     static constexpr bool value =
-         (std::is_same_v<std::decay_t<T>, GncOptionValue<const QofQuery*>> ||
-          std::is_same_v<std::decay_t<T>,
-          GncOptionValidatedValue<const QofQuery*>>);
+         std::is_same_v<std::decay_t<T>, GncOptionValue<const QofQuery*>>;
 };
 
 template <typename T> inline constexpr bool
@@ -298,25 +273,20 @@ operator<< <GncOptionValue<bool>>(std::ostream& oss,
     return oss;
 }
 
+inline std::ostream&
+operator<< (std::ostream& oss, const GncOptionCommodityValue& opt)
+{
+    oss << opt.serialize();
+    return oss;
+}
+
 template<class OptType,
          typename std::enable_if_t<is_QofInstanceValue_v<OptType>, int> = 0>
 inline std::ostream&
 operator<< (std::ostream& oss, const OptType& opt)
 {
     auto value = opt.get_value();
-    if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
-        type == GncOptionUIType::CURRENCY)
-    {
-        if (type == GncOptionUIType::COMMODITY)
-        {
-            oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " ";
-        }
-        oss << gnc_commodity_get_mnemonic(GNC_COMMODITY(value));
-    }
-    else
-    {
-        oss << qof_instance_to_string(value);
-    }
+    oss << qof_instance_to_string(value);
     return oss;
 }
 
@@ -339,35 +309,15 @@ std::istream& operator>>(std::istream& iss, OptType& opt)
     }
 }
 
+std::istream& operator>> (std::istream& iss, GncOptionCommodityValue& opt);
+
 template<class OptType,
          typename std::enable_if_t<is_QofInstanceValue_v<OptType>, int> = 0>
 std::istream&
 operator>> (std::istream& iss, OptType& opt)
 {
     std::string instr;
-    auto type = opt.get_ui_type();
-    if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
-    {
-        std::string name_space, mnemonic;
-        if (type == GncOptionUIType::COMMODITY)
-            iss >> name_space;
-        else
-            name_space = GNC_COMMODITY_NS_CURRENCY;
-        if (name_space.find(":") == std::string::npos)
-        {
-            iss >> mnemonic;
-            instr = name_space + ":";
-            instr += mnemonic;
-        }
-        else
-        {
-            instr = name_space;
-        }
-     }
-    else
-    {
-        iss >> instr;
-    }
+    iss >> instr;
     opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
     return iss;
 }
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index aa7ae040b..5b2b940d1 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -302,15 +302,15 @@ GncOption::validate(ValueType value) const
     return std::visit(
         [value] (const auto& option) -> bool {
             if constexpr ((is_same_decayed_v<decltype(option),
-                           GncOptionMultichoiceValue> &&
-                           is_same_decayed_v<ValueType,
-                           std::string>) ||
+                                             GncOptionMultichoiceValue> &&
+                           is_same_decayed_v<ValueType, std::string>) ||
                           (is_same_decayed_v<decltype(option),
-                           GncOptionMultichoiceValue> &&
+                                             GncOptionMultichoiceValue> &&
                            is_same_decayed_v<ValueType,
-                           GncMultichoiceOptionIndexVec>) ||
-                          is_same_decayed_v<decltype(option),
-                          GncOptionValidatedValue<ValueType>>)
+                                             GncMultichoiceOptionIndexVec>) ||
+                          (is_same_decayed_v<decltype(option),
+                                             GncOptionCommodityValue> &&
+                           is_same_decayed_v<ValueType, gnc_commodity*>))
                 return option.validate(value);
             else
                 return false;
@@ -448,8 +448,6 @@ gnc_make_SCM_option(const char* section, const char* name,
  */
 
 
-template class GncOptionValidatedValue<const QofInstance*>;
-
 template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, bool, GncOptionUIType);
 //template GncOption::GncOption(const char*, const char*, const char*,
@@ -477,6 +475,7 @@ template size_t GncOption::get_value<size_t>() const;
 template const char* GncOption::get_value<const char*>() const;
 template std::string GncOption::get_value<std::string>() const;
 template const QofInstance* GncOption::get_value<const QofInstance*>() const;
+template gnc_commodity* GncOption::get_value<gnc_commodity*>() const;
 template const Account* GncOption::get_value<const Account*>() const;
 template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_value<GncOptionAccountList>() const;
@@ -490,6 +489,7 @@ template double GncOption::get_default_value<double>() const;
 template const char* GncOption::get_default_value<const char*>() const;
 template std::string GncOption::get_default_value<std::string>() const;
 template const QofInstance* GncOption::get_default_value<const QofInstance*>() const;
+template gnc_commodity* GncOption::get_default_value<gnc_commodity*>() const;
 template const Account* GncOption::get_default_value<const Account*>() const;
 template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_default_value<GncOptionAccountList>() const;
@@ -504,6 +504,7 @@ template void GncOption::set_value(char*);
 template void GncOption::set_value(const char*);
 template void GncOption::set_value(std::string);
 template void GncOption::set_value(const QofInstance*);
+template void GncOption::set_value(gnc_commodity*);
 template void GncOption::set_value(const Account*);
 template void GncOption::set_value(RelativeDatePeriod);
 template void GncOption::set_value(size_t);
@@ -535,6 +536,7 @@ template bool GncOption::validate(double) const;
 template bool GncOption::validate(const char*) const;
 template bool GncOption::validate(std::string) const;
 template bool GncOption::validate(const QofInstance*) const;
+template bool GncOption::validate(gnc_commodity*) const;
 template bool GncOption::validate(const Account*) const;
 template bool GncOption::validate(const QofQuery*) const;
 template bool GncOption::validate(RelativeDatePeriod) const;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index e0bdf61fa..72022d9f4 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -60,7 +60,7 @@ class GncOptionAccountListValue;
 class GncOptionAccountSelValue;
 class GncOptionMultichoiceValue;
 template <typename ValueType> class GncOptionRangeValue;
-template <typename ValueType> class GncOptionValidatedValue;
+class GncOptionCommodityValue;
 class GncOptionDateValue;
 
 template <typename T>
@@ -107,8 +107,7 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionMultichoiceValue,
                                       GncOptionRangeValue<int>,
                                       GncOptionRangeValue<double>,
-                                      GncOptionValidatedValue<const QofInstance*>,
-                                      GncOptionValidatedValue<const QofQuery*>,
+                                      GncOptionCommodityValue,
                                       GncOptionDateValue>;
 
 using GncOptionVariantPtr = std::unique_ptr<GncOptionVariant>;
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 5da83348b..510b58a86 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -444,8 +444,6 @@ is_qofinstance_ui_type(GncOptionUIType type)
 {
     switch (type)
     {
-        case GncOptionUIType::CURRENCY:
-        case GncOptionUIType::COMMODITY:
         case GncOptionUIType::ACCOUNT_SEL:
         case GncOptionUIType::BUDGET:
         case GncOptionUIType::OWNER:
@@ -651,8 +649,8 @@ gnc_register_commodity_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, gnc_commodity *value)
 {
-    GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
-                                               (const QofInstance*)value,
+    GncOption option{GncOptionCommodityValue{section, name, key, doc_string,
+                                               value,
                                                GncOptionUIType::COMMODITY}};
     db->register_option(section, std::move(option));
 }
@@ -917,14 +915,8 @@ gnc_register_currency_option(GncOptionDB* db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, gnc_commodity *value)
 {
-    GncOption option{GncOptionValidatedValue<const QofInstance*>{
-        section, name, key, doc_string, (const QofInstance*)value,
-        [](const QofInstance* new_value) -> bool
-            {
-                return GNC_IS_COMMODITY (new_value) &&
-                    gnc_commodity_is_currency(GNC_COMMODITY(new_value));
-            },
-            GncOptionUIType::CURRENCY
+    GncOption option{GncOptionCommodityValue{
+        section, name, key, doc_string, value,GncOptionUIType::CURRENCY
         }};
     db->register_option(section, std::move(option));
 }
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index f9c77abf7..cebe26923 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -240,6 +240,14 @@ scm_from_value<const Account*>(const Account* value)
     return scm_from_value<const QofInstance*>(QOF_INSTANCE(value));
 }
 
+template <> inline SCM
+scm_from_value<gnc_commodity*>(gnc_commodity* value)
+{
+    if (!value)
+        return SCM_BOOL_F;
+    return scm_from_value<const QofInstance*>((const QofInstance*)value);
+}
+
 template <> inline SCM
 scm_from_value<QofQuery*>(QofQuery* value)
 {
@@ -334,7 +342,7 @@ scm_to_value<const QofInstance*>(SCM new_value)
 
     auto info = SWIG_PointerType(new_value);
 
-    static const std::array<swig_type_info*, 11> types{
+    static const std::array<swig_type_info*, 10> types{
         SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
         SWIGTYPE_p_budget_s, SWIGTYPE_p__gncInvoice,
         SWIGTYPE_p__gncTaxTable, SWIGTYPE_p_Account,
@@ -352,6 +360,36 @@ scm_to_value<const QofInstance*>(SCM new_value)
     return static_cast<const QofInstance*>(ptr);
 }
 
+template <> inline gnc_commodity*
+scm_to_value<gnc_commodity*>(SCM new_value)
+{
+    auto comm{scm_to_value<const QofInstance*>(new_value)};
+    if (comm)
+        return GNC_COMMODITY(comm);
+    if (scm_is_list(new_value))
+    {
+        auto len{scm_to_uint(scm_length(new_value))};
+        std::string mnemonic{scm_to_utf8_string(scm_list_ref(new_value,
+                                                         scm_from_uint(0)))};
+        std::string name_space{"CURRENCY"};
+        if (len > 1)
+           name_space = scm_to_utf8_string(scm_list_ref(new_value,
+                                                        scm_from_uint(1)));
+        auto book{gnc_get_current_book()};
+        auto table = gnc_commodity_table_get_table(book);
+        return gnc_commodity_table_lookup(table, name_space.c_str(),
+                                          mnemonic.c_str());
+    }
+    if (scm_is_string(new_value))
+    {
+        auto book{gnc_get_current_book()};
+        auto table = gnc_commodity_table_get_table(book);
+        std::string mnemonic{scm_to_utf8_string(new_value)};
+        return gnc_commodity_table_lookup(table, "CURRENCY", mnemonic.c_str());
+    }
+    return nullptr;
+}
+
 template <> inline const Account*
 scm_to_value<const Account*>(SCM new_value)
 {
@@ -431,6 +469,12 @@ gnc_option_test_book_destroy(QofBook* book)
 %ignore GncOptionMultichoiceValue(GncOptionMultichoiceValue&&);
 %ignore GncOptionMultichoiceValue::operator=(const GncOptionMultichoiceValue&);
 %ignore GncOptionMultichoiceValue::operator=(GncOptionMultichoiceValue&&);
+%ignore GncOptionQofInstanceValue(GncOptionQofInstanceValue&&);
+%ignore GncOptionQofInstanceValue::operator=(const GncOptionQofInstanceValue&);
+%ignore GncOptionQofInstanceValue::operator=(GncOptionQofInstanceValue&&);
+%ignore GncOptionCommodityValue(GncOptionCommodityValue&&);
+%ignore GncOptionCommodityValue::operator=(const GncOptionCommodityValue&);
+%ignore GncOptionCommodityValue::operator=(GncOptionCommodityValue&&);
 %ignore GncOptionDateValue(GncOptionDateValue&&);
 %ignore GncOptionDateValue::operator=(const GncOptionDateValue&);
 %ignore GncOptionDateValue::operator=(GncOptionDateValue&&);
@@ -1056,25 +1100,27 @@ inline SCM return_scm_value(ValueType value)
                     if (serial.empty())
                         return no_value;
                     auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))};
-                    if (uitype == GncOptionUIType::CURRENCY)
-                    {
-                        const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")};
-                        return scm_simple_format(SCM_BOOL_F, quoted_format_str, value);
-                    }
-                    else if (uitype == GncOptionUIType::COMMODITY)
-                    {
-                        const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")};
-                        auto comm{GNC_COMMODITY(option.get_value())};
-                        auto name_space{gnc_commodity_get_namespace(comm)};
-                        auto mnemonic{gnc_commodity_get_mnemonic(comm)};
-                        auto commodity_val{scm_list_2(scm_from_utf8_string(name_space),
-                                                      scm_from_utf8_string(mnemonic))};
-                        return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val);
-                    }
-                    else
-                    {
                         return scm_simple_format(SCM_BOOL_F, plain_format_str, value);
-                    }
+                }
+                if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionCommodityValue>)
+                {
+                     auto comm{option.get_value()};
+                     auto mnemonic{gnc_commodity_get_mnemonic(comm)};
+                     if (gnc_commodity_is_currency(comm))
+                     {
+                         auto value{scm_list_1(scm_from_utf8_string(mnemonic))};
+                         const SCM quoted_format_str{scm_from_utf8_string("~s")};
+                         return scm_simple_format(SCM_BOOL_F, quoted_format_str, value);
+                     }
+                     else
+                     {
+                          const SCM commodity_fmt{scm_from_utf8_string("~s ~s")};
+                          auto name_space{gnc_commodity_get_namespace(comm)};
+                          auto commodity_val{scm_list_2(scm_from_utf8_string(name_space),
+                                                        scm_from_utf8_string(mnemonic))};
+                          return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val);
+                     }
                 }
                 if constexpr (is_same_decayed_v<decltype(option),
                               GncOptionDateValue>)
@@ -1198,6 +1244,34 @@ inline SCM return_scm_value(ValueType value)
                             option.set_value(scm_to_int(new_value));
                         return;
                     }
+                    if constexpr (is_same_decayed_v<decltype(option),
+                                  GncOptionCommodityValue>)
+                    {
+                        if (scm_list_p(new_value) == SCM_BOOL_F)
+                        {
+                            if (scm_is_string(new_value))
+                            {
+                                 auto strval{scm_to_utf8_string(new_value)};
+                                 auto val{qof_instance_from_string(strval, option.get_ui_type())};
+                                 option.set_value(GNC_COMMODITY(val));
+                                 return;
+                            }
+                            option.set_value(scm_to_value<gnc_commodity*>(new_value));
+                            return;
+                        }
+                        auto len{scm_to_uint(scm_length(new_value))};
+                        std::string mnemonic{scm_to_utf8_string(scm_list_ref(new_value, scm_from_uint(0)))};
+                        if (len > 1)
+                        {
+                             std::string name_space{scm_to_utf8_string(scm_list_ref(new_value, scm_from_uint(1)))};
+                             option.deserialize(name_space + ":" + mnemonic);
+                        }
+                        else
+                        {
+                            option.deserialize(mnemonic);
+                        }
+                        return;
+                    }
                     if constexpr (is_QofInstanceValue_v<decltype(option)>)
                     {
                         if (scm_is_string(new_value))
@@ -1288,6 +1362,12 @@ inline SCM return_scm_value(ValueType value)
                             option.set_default_value(scm_to_int(new_value));
                         return;
                     }
+                    if constexpr (is_same_decayed_v<decltype(option),
+                                  GncOptionCommodityValue>)
+                    {
+                       auto comm{scm_to_value<gnc_commodity*>(new_value)};
+                       option.set_default_value(comm);
+                    }
                     if constexpr (is_QofInstanceValue_v<decltype(option)>)
                     {
                         if (scm_is_string(new_value))
@@ -1589,8 +1669,8 @@ inline SCM return_scm_value(ValueType value)
                               const char* key, const char* doc_string,
                               gnc_commodity *value)
     {
-        return new GncOption{GncOptionQofInstanceValue{
-                section, name, key, doc_string, (const QofInstance*)value,
+        return new GncOption{GncOptionCommodityValue{
+                section, name, key, doc_string, value,
                     GncOptionUIType::COMMODITY}};
     }
 
@@ -1603,15 +1683,17 @@ inline SCM return_scm_value(ValueType value)
         const auto book{qof_session_get_book(gnc_get_current_session())};
         const auto commodity_table{gnc_commodity_table_get_table(book)};
         const auto namespaces{gnc_commodity_table_get_namespaces(commodity_table)};
-        for (auto node = namespaces; node && commodity == nullptr; node = g_list_next(node))
+        for (auto node = namespaces; node && commodity == nullptr;
+             node = g_list_next(node))
+        {
             commodity = gnc_commodity_table_lookup(commodity_table,
                                                    (const char*)(node->data),
                                                    value);
 
-        if (commodity)
-            return gnc_make_commodity_option(section, name, key, doc_string,
-                                            commodity);
-
+            if (commodity)
+                return gnc_make_commodity_option(section, name, key, doc_string,
+                                                 commodity);
+        }
         return nullptr;
     }
 
@@ -1622,16 +1704,9 @@ inline SCM return_scm_value(ValueType value)
     {
         try
         {
-            return new GncOption{GncOptionValidatedValue<const QofInstance*>{
-                    section, name, key, doc_string, (const QofInstance*)value,
-                        [](const QofInstance* new_value) -> bool
-                    {
-                        return GNC_IS_COMMODITY (new_value) &&
-                            gnc_commodity_is_currency(GNC_COMMODITY(new_value));
-                    },
-                        GncOptionUIType::CURRENCY
-                            }
-            };
+            return new GncOption{GncOptionCommodityValue{
+                    section, name, key, doc_string, value,
+                    GncOptionUIType::CURRENCY}};
         }
         catch (const std::exception& err)
         {
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 8f2e3f863..94df08f7a 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -243,13 +243,9 @@ make_currency_option (const char* section, const char* name,
                       const char* key, const char* doc_string,
                       gnc_commodity *value, bool is_currency=false)
 {
-    GncOption option{GncOptionValidatedValue<const QofInstance*>{
-        section, name, key, doc_string, (const QofInstance*)value,
-        [](const QofInstance* new_value) -> bool
-            {
-                return GNC_IS_COMMODITY (new_value) &&
-                    gnc_commodity_is_currency(GNC_COMMODITY(new_value));
-            }, is_currency ? GncOptionUIType::CURRENCY : GncOptionUIType::COMMODITY}
+    GncOption option{GncOptionCommodityValue{
+        section, name, key, doc_string, value,
+        is_currency ? GncOptionUIType::CURRENCY : GncOptionUIType::COMMODITY}
     };
     return option;
 }
@@ -258,7 +254,7 @@ TEST_F(GncOptionCommodityTest, test_currency_ctor)
 {
     EXPECT_THROW({
             auto option = make_currency_option("foo", "bar", "baz",
-                                               "Phony Option", m_hpe, false);
+                                               "Phony Option", m_hpe, true);
         }, std::invalid_argument);
     EXPECT_NO_THROW({
             auto option = make_currency_option("foo", "bar", "baz",
@@ -275,23 +271,23 @@ TEST_F(GncOptionCommodityTest, test_currency_setter)
     auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
                                       m_eur, true);
     EXPECT_NO_THROW({
-            option.set_value((const QofInstance*)m_usd);
+            option.set_value(m_usd);
         });
     EXPECT_PRED2(gnc_commodity_equal, m_usd,
-                 GNC_COMMODITY(option.get_value<const QofInstance*>()));
+                 GNC_COMMODITY(option.get_value<gnc_commodity*>()));
     EXPECT_THROW({
-            option.set_value((const QofInstance*)m_hpe);
+            option.set_value(m_hpe);
         }, std::invalid_argument);
     EXPECT_PRED2(gnc_commodity_equal, m_usd,
-                 GNC_COMMODITY(option.get_value<const QofInstance*>()));
+                 GNC_COMMODITY(option.get_value<gnc_commodity *>()));
 }
 
 TEST_F(GncOptionCommodityTest, test_currency_validator)
 {
     auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
                                       m_eur, true);
-    EXPECT_TRUE(option.validate((const QofInstance*)m_usd));
-    EXPECT_FALSE(option.validate((const QofInstance*)m_aapl));
+    EXPECT_TRUE(option.validate(m_usd));
+    EXPECT_FALSE(option.validate(m_aapl));
 }
 
 static inline std::string make_currency_str(gnc_commodity* cur)
@@ -344,19 +340,19 @@ TEST_F(GncOptionCommodityTest, test_currency_in)
             std::string usd_str{make_currency_str(m_usd)};
             std::istringstream iss{usd_str};
             iss >> option;
-            EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<const QofInstance*>());
+            EXPECT_EQ(m_usd, option.get_value<gnc_commodity*>());
         });
 }
 
 TEST_F(GncOptionCommodityTest, test_commodity_in)
 {
-    GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl,
+    GncOption option{GncOptionCommodityValue{"foo", "bar", "baz", "Phony Option", m_aapl,
                      GncOptionUIType::COMMODITY}};
 
     std::string hpe_str{make_commodity_str(m_hpe)};
     std::istringstream iss{hpe_str};
     iss >> option;
-    EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
+    EXPECT_EQ(m_hpe, option.get_value<gnc_commodity*>());
 }
 
 class GncUIType
diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
index cd8facb9e..c582be5a4 100644
--- a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
+++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
@@ -87,19 +87,19 @@
 (let ((option (gnc:lookup-option options
                                  \"foo\"
                                  \"bar\")))
-  ((lambda (o) (if o (gnc:option-set-value o \"~a\"))) option))
+  ((lambda (o) (if o (gnc:option-set-value o ~s))) option))
 
 " value))
 
 (define (test-commodity-output-template value)
   (let ((value-parts (string-split value #\:)))
-    (format #f "
+       (format #f "
 ; Section: foo
 
 (let ((option (gnc:lookup-option options
                                  \"foo\"
                                  \"bar\")))
-  ((lambda (o) (if o (gnc:option-set-value o \"~a\" \"~a\"))) option))
+  ((lambda (o) (if o (gnc:option-set-value o ~s ~s))) option))
 
 " (car value-parts) (cadr value-parts))))
 
@@ -208,6 +208,8 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
          (comm-tbl (gnc-commodity-table-get-table book))
          (AAPL (gnc-commodity-new book "Apple" "NASDAQ" "AAPL" "" 1))
          (FMAGX (gnc-commodity-new book "Fidelity Magellan Fund" "FUND" "FMAGX" "" 1000)))
+         (gnc-commodity-table-insert comm-tbl AAPL)
+         (gnc-commodity-table-insert comm-tbl FMAGX)
     (test-option-scheme-output "commodity"
                                gnc:make-commodity-option GncOption-serialize
                                test-commodity-output-template

commit 65bd860249b3fc284558144eb81df413d92b32ca
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jan 1 16:05:06 2022 -0800

    Fix relative_date_to_time64 calculations.
    
    Where the date requested extends beyond the start or end of the year.

diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp
index 30847b2a5..3d2a3e141 100644
--- a/libgnucash/app-utils/gnc-option-date.cpp
+++ b/libgnucash/app-utils/gnc-option-date.cpp
@@ -434,13 +434,23 @@ normalize_reldate_tm(struct tm& now)
 {
     auto factor{abs(now.tm_mon) / 12};
     now.tm_mon /= factor > 0 ? factor : 1;
-    now.tm_year += now.tm_mon < 0 ? -factor : factor;
+    now.tm_year += now.tm_mon < 0 ? -factor: factor;
+
+    auto days = [](auto month, int year)
+    {
+        auto mon{month % 12 + (month < 0 ? 12 : 0)};
+        auto num_days{days_in_month[mon]};
+        //Leap year check.
+        if (mon == 1 && year % 4 == 0 && !(year % 100 == 0 && (year + 1900) % 400 != 0))
+            ++num_days;
+        return num_days;
+    };
 
     while (now.tm_mday < 1)
-        now.tm_mday += days_in_month[--now.tm_mon];
+        now.tm_mday += days(--now.tm_mon, now.tm_year);
 
-    while (now.tm_mday > days_in_month[now.tm_mon])
-        now.tm_mday -= days_in_month[now.tm_mon++];
+    while (now.tm_mday > days(now.tm_mon, now.tm_year))
+        now.tm_mday -= days(now.tm_mon++, now.tm_year);
 
     while (now.tm_mon < 0)
     {

commit 6841e5b5c8ac57d204c1bbf73ad232a4e5ed4ad8
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 30 12:21:56 2021 -0800

    Replace GncOptionValue<const QofInstance*> with GncOptionQofInstanceValue.
    
    That stores its values as a pair of <QofIdType, gncGUID> so that it
    won't have dangling ptrs if the instance gets deleted.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 6bd896595..f8905edaa 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -38,6 +38,168 @@ static const QofLogModule log_module{"gnc.options"};
 const std::string GncOptionMultichoiceValue::c_empty_string{""};
 const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
 
+using GncItem = std::pair<QofIdTypeConst, GncGUID>;
+
+static GncItem
+make_gnc_item(const QofInstance* inst)
+{
+    if (!inst)
+        return std::make_pair<QofIdTypeConst, GncGUID>("", guid_new_return());
+    auto type{qof_collection_get_type(qof_instance_get_collection(inst))};
+    auto guid{qof_instance_get_guid(inst)};
+    return std::make_pair(std::move(type), std::move(*const_cast<GncGUID*>(guid)));
+}
+
+static const QofInstance*
+qof_instance_from_gnc_item(const GncItem& item)
+{
+    auto [type, guid] = item;
+    auto book{gnc_get_current_book()};
+    auto coll{qof_book_get_collection(book, type)};
+    return static_cast<QofInstance*>(qof_collection_lookup_entity(coll, &guid));
+}
+
+static bool
+operator!=(const GncItem& left, const GncItem& right)
+{
+    auto [ltype, lguid]{left};
+    auto [rtype, rguid]{right};
+    return strcmp(rtype, ltype) && !guid_equal(&rguid, &lguid);
+}
+
+GncOptionQofInstanceValue::GncOptionQofInstanceValue(
+    const char* section, const char* name,
+    const char* key, const char* doc_string,
+    const QofInstance* value, GncOptionUIType ui_type) :
+    OptionClassifier{section, name, key, doc_string},
+    m_ui_type(ui_type), m_value{},
+    m_default_value{} {
+    m_value = make_gnc_item(value);
+    m_default_value = make_gnc_item(value);
+}
+
+GncOptionQofInstanceValue::GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from) :
+    OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
+                     from.m_doc_string},
+    m_ui_type(from.get_ui_type()), m_value{from.get_item()},
+    m_default_value{from.get_default_item()}
+{
+}
+void
+GncOptionQofInstanceValue::set_value(const QofInstance* new_value)
+{
+    m_value = make_gnc_item(new_value);
+}
+
+void
+GncOptionQofInstanceValue::set_default_value(const QofInstance *new_value)
+{
+    m_value = m_default_value = make_gnc_item(new_value);
+
+}
+
+const QofInstance*
+GncOptionQofInstanceValue::get_value() const
+{
+    return qof_instance_from_gnc_item(m_value);
+}
+
+const QofInstance*
+GncOptionQofInstanceValue::get_default_value() const
+{
+    return qof_instance_from_gnc_item(m_default_value);
+}
+
+void
+GncOptionQofInstanceValue::reset_default_value()
+{
+    m_value = m_default_value;
+}
+
+bool
+GncOptionQofInstanceValue::is_changed() const noexcept
+{
+    return m_value != m_default_value;
+}
+
+bool
+GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept
+{
+    QofInstance* inst{};
+    // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic
+    if (m_ui_type == GncOptionUIType::CURRENCY ||
+        m_ui_type == GncOptionUIType::COMMODITY)
+    {
+        auto book{gnc_get_current_book()};
+        auto table = gnc_commodity_table_get_table(book);
+        auto sep{str.find(":")};
+        if (sep != std::string::npos)
+        {
+            auto name_space{str.substr(0, sep)};
+            auto mnemonic{str.substr(sep + 1, -1)};
+            inst = QOF_INSTANCE(gnc_commodity_table_lookup(table,
+                                                              name_space.c_str(),
+                                                              mnemonic.c_str()));
+        }
+        if (!inst && m_ui_type == GncOptionUIType::CURRENCY)
+            inst = QOF_INSTANCE(gnc_commodity_table_lookup(table,
+                                                             "CURRENCY",
+                                                             str.c_str()));
+        if (inst)
+        {
+            m_value = make_gnc_item(inst);
+            return true;
+        }
+    }
+
+    if (!inst)
+    {
+        try {
+            auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
+            inst = qof_instance_from_guid(&guid, m_ui_type);
+            if (inst)
+            {
+                auto coll{qof_instance_get_collection(inst)};
+                m_value = std::make_pair(qof_collection_get_type(coll), guid);
+                return true;
+            }
+        }
+        catch (const gnc::guid_syntax_exception& err)
+        {
+            PWARN("Failed to convert %s to a GUID", str.c_str());
+        }
+    }
+    return false;
+}
+
+std::string
+GncOptionQofInstanceValue::serialize() const noexcept
+{
+    auto inst{get_value()};
+    std::string retval;
+    if (GNC_IS_COMMODITY(inst))
+    {
+        auto commodity{GNC_COMMODITY(inst)};
+        if (!gnc_commodity_is_currency(commodity))
+        {
+            auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))};
+            if (name_space && *name_space != '\0')
+            {
+                retval = name_space;
+                retval += ":";
+            }
+        }
+        retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst));
+        return retval;
+    }
+    else
+    {
+        gnc::GUID guid{m_value.second};
+        retval = guid.to_string();
+    }
+    return retval;
+}
+
 bool
 GncOptionAccountListValue::validate(const GncOptionAccountList& values) const
 {
@@ -536,9 +698,7 @@ GncOptionValidatedValue<ValueType>::serialize() const noexcept
 template <typename ValueType> bool
 GncOptionValidatedValue<ValueType>::deserialize(const std::string& str) noexcept
 {
-    if constexpr(std::is_same_v<ValueType, const QofInstance*>)
-        set_value(qof_instance_from_string(str, get_ui_type()));
-    else if constexpr(is_same_decayed_v<ValueType, std::string>)
+    if constexpr(is_same_decayed_v<ValueType, std::string>)
         set_value(str);
     else if constexpr(is_same_decayed_v<ValueType, bool>)
         set_value(str == "True");
@@ -728,7 +888,6 @@ template GncOptionValue<double>::GncOptionValue(const GncOptionValue<double>&);
 template GncOptionValue<char*>::GncOptionValue(const GncOptionValue<char*>&);
 template GncOptionValue<const char*>::GncOptionValue(const GncOptionValue<const char*>&);
 template GncOptionValue<std::string>::GncOptionValue(const GncOptionValue<std::string>&);
-template GncOptionValue<const QofInstance*>::GncOptionValue(const GncOptionValue<const QofInstance*>&);
 template GncOptionValue<const QofQuery*>::GncOptionValue(const GncOptionValue<const QofQuery*>&);
 template GncOptionValue<const GncOwner*>::GncOptionValue(const GncOptionValue<const GncOwner*>&);
 template GncOptionValue<RelativeDatePeriod>::GncOptionValue(const GncOptionValue<RelativeDatePeriod>&);
@@ -743,7 +902,6 @@ template void GncOptionValue<double>::set_value(double);
 template void GncOptionValue<char*>::set_value(char*);
 template void GncOptionValue<const char*>::set_value(const char*);
 template void GncOptionValue<std::string>::set_value(std::string);
-template void GncOptionValue<const QofInstance*>::set_value(const QofInstance*);
 template void GncOptionValue<const QofQuery*>::set_value(const QofQuery*);
 template void GncOptionValue<const GncOwner*>::set_value(const GncOwner*);
 template void GncOptionValue<RelativeDatePeriod>::set_value(RelativeDatePeriod);
@@ -757,7 +915,6 @@ template void GncOptionValue<double>::set_default_value(double);
 template void GncOptionValue<char*>::set_default_value(char*);
 template void GncOptionValue<const char*>::set_default_value(const char*);
 template void GncOptionValue<std::string>::set_default_value(std::string);
-template void GncOptionValue<const QofInstance*>::set_default_value(const QofInstance*);
 template void GncOptionValue<const QofQuery*>::set_default_value(const QofQuery*);
 template void GncOptionValue<const GncOwner*>::set_default_value(const GncOwner*);
 template void GncOptionValue<RelativeDatePeriod>::set_default_value(RelativeDatePeriod);
@@ -771,7 +928,6 @@ template void GncOptionValue<double>::reset_default_value();
 template void GncOptionValue<char*>::reset_default_value();
 template void GncOptionValue<const char*>::reset_default_value();
 template void GncOptionValue<std::string>::reset_default_value();
-template void GncOptionValue<const QofInstance*>::reset_default_value();
 template void GncOptionValue<const QofQuery*>::reset_default_value();
 template void GncOptionValue<const GncOwner*>::reset_default_value();
 template void GncOptionValue<RelativeDatePeriod>::reset_default_value();
@@ -785,7 +941,6 @@ template std::string GncOptionValue<double>::serialize() const noexcept;
 template std::string GncOptionValue<char*>::serialize() const noexcept;
 template std::string GncOptionValue<const char*>::serialize() const noexcept;
 template std::string GncOptionValue<std::string>::serialize() const noexcept;
-template std::string GncOptionValue<const QofInstance*>::serialize() const noexcept;
 template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
 template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
 template std::string GncOptionValue<SCM>::serialize() const noexcept;
@@ -808,7 +963,6 @@ template bool GncOptionValue<double>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<char*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<const char*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<std::string>::deserialize(const std::string&) noexcept;
-template bool GncOptionValue<const QofInstance*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
 template bool GncOptionValue<SCM>::deserialize(const std::string&) noexcept;
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 3adb3f634..253bc4d75 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -141,6 +141,37 @@ private:
     ValueType m_default_value;
 };
 
+using GncItem = std::pair<QofIdTypeConst, GncGUID>;
+
+class GncOptionQofInstanceValue: public OptionClassifier {
+public:
+    GncOptionQofInstanceValue(
+        const char* section, const char* name,
+        const char* key, const char* doc_string,
+        const QofInstance* value,
+        GncOptionUIType ui_type = GncOptionUIType::INTERNAL);
+    GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from);
+    GncOptionQofInstanceValue(GncOptionQofInstanceValue&&) = default;
+    GncOptionQofInstanceValue& operator=(GncOptionQofInstanceValue&&) = default;
+    ~GncOptionQofInstanceValue() = default;
+    const QofInstance* get_value() const;
+    const QofInstance* get_default_value() const;
+    GncItem get_item() const { return m_value; }
+    GncItem get_default_item() const { return m_default_value; }
+    void set_value(const QofInstance* new_value);
+    void set_default_value(const QofInstance* new_value);
+    void reset_default_value();
+    bool is_changed() const noexcept;
+    GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
+    void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+    std::string serialize() const noexcept;
+    bool deserialize(const std::string& str) noexcept;
+private:
+    GncOptionUIType m_ui_type;
+    GncItem m_value;
+    GncItem m_default_value;
+};
+
 /** class GncOptionValidatedValue
  *  Validated values have an additional member function, provided as a
  *  constructor argument, that checks value parameters for some property before
@@ -221,7 +252,7 @@ template <typename T>
 struct is_QofInstanceValue
 {
     static constexpr bool value =
-         (std::is_same_v<std::decay_t<T>, GncOptionValue<const QofInstance*>> ||
+         (std::is_same_v<std::decay_t<T>, GncOptionQofInstanceValue> ||
           std::is_same_v<std::decay_t<T>,
           GncOptionValidatedValue<const QofInstance*>>);
 };
@@ -276,7 +307,7 @@ operator<< (std::ostream& oss, const OptType& opt)
     if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
         type == GncOptionUIType::CURRENCY)
     {
-        if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY)
+        if (type == GncOptionUIType::COMMODITY)
         {
             oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " ";
         }
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 8c156c25e..aa7ae040b 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -35,7 +35,7 @@ extern "C"
 
 template <typename ValueType,
           typename std::enable_if_t<!is_OptionClassifier_v<ValueType>,
-                               int>>
+                                    int>>
 GncOption::GncOption(const char* section, const char* name,
                      const char* key, const char* doc_string,
                      ValueType value, GncOptionUIType ui_type) :
@@ -343,7 +343,7 @@ GncOption::permissible_value_index(const char* value) const
                                             GncOptionDateValue>)
                 return option.permissible_value_index(value);
             else
-                return size_t_max;;
+                return size_t_max;
         }, *m_option);
 }
 
@@ -462,8 +462,6 @@ template GncOption::GncOption(const char*, const char*, const char*,
 //                              const char*, double, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, std::string, GncOptionUIType);
-template GncOption::GncOption(const char*, const char*, const char*,
-                              const char*, const QofInstance*, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, SCM, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
@@ -553,9 +551,4 @@ template GncOption* gnc_make_option<bool>(const char*, const char*, const char*,
 template GncOption* gnc_make_option<int64_t>(const char*, const char*,
                                              const char*, const char*, int64_t,
                                              GncOptionUIType);
-template GncOption* gnc_make_option<const QofInstance*>(const char*,
-                                                        const char*,
-                                                        const char*,
-                                                        const char*,
-                                                        const QofInstance*,
-                                                        GncOptionUIType);
+
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 2c93c9283..e0bdf61fa 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -55,6 +55,7 @@ using QofQuery = _QofQuery;
 struct QofInstance_s;
 using QofInstance = QofInstance_s;
 template <typename ValueType> class GncOptionValue;
+class GncOptionQofInstanceValue;
 class GncOptionAccountListValue;
 class GncOptionAccountSelValue;
 class GncOptionMultichoiceValue;
@@ -97,7 +98,7 @@ is_RangeValue_v = is_RangeValue<T>::value;
 using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<bool>,
                                       GncOptionValue<int64_t>,
-                                      GncOptionValue<const QofInstance*>,
+                                      GncOptionQofInstanceValue,
                                       GncOptionValue<const QofQuery*>,
                                       GncOptionValue<const GncOwner*>,
                                       GncOptionValue<SCM>,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 1e561dc94..5da83348b 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -630,8 +630,9 @@ gnc_register_budget_option(GncOptionDB* db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, GncBudget *value)
 {
-    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
-            GncOptionUIType::BUDGET};
+    GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
+                                               (const QofInstance*)value,
+                                               GncOptionUIType::BUDGET}};
     db->register_option(section, std::move(option));
 }
 
@@ -650,8 +651,9 @@ gnc_register_commodity_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, gnc_commodity *value)
 {
-    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
-            GncOptionUIType::COMMODITY};
+    GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
+                                               (const QofInstance*)value,
+                                               GncOptionUIType::COMMODITY}};
     db->register_option(section, std::move(option));
 }
 
@@ -862,8 +864,9 @@ gnc_register_invoice_option(GncOptionDB* db, const char* section,
                             const char* name, const char* key,
                             const char* doc_string, GncInvoice* value)
 {
-    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
-            GncOptionUIType::INVOICE};
+    GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
+                                               (const QofInstance*)value,
+                                               GncOptionUIType::INVOICE}};
     db->register_option(section, std::move(option));
 }
 
@@ -872,8 +875,9 @@ gnc_register_taxtable_option(GncOptionDB* db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, GncTaxTable* value)
 {
-    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
-            GncOptionUIType::TAX_TABLE};
+    GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
+                                               (const QofInstance*)value,
+                                               GncOptionUIType::TAX_TABLE}};
     db->register_option(section, std::move(option));
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 8a23eb1c3..f9c77abf7 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -140,9 +140,10 @@ SCM scm_init_sw_gnc_optiondb_module(void);
                 $descriptor(_gncJob*), $descriptor(_gncVendor*)
                 };
         void* ptr{};
+        SCM instance{$input};
         auto pos = std::find_if(types.begin(), types.end(),
-                                [&$input, &ptr](auto type){
-                                    SWIG_ConvertPtr($input, &ptr, type, 0);
+                                [&instance, &ptr](auto type){
+                                    SWIG_ConvertPtr(instance, &ptr, type, 0);
                                     return ptr != nullptr; });
         if (pos == types.end())
             $1 = nullptr;
@@ -990,7 +991,6 @@ inline SCM return_scm_value(ValueType value)
 %template(gnc_make_string_option) gnc_make_option<std::string>;
 %template(gnc_make_bool_option) gnc_make_option<bool>;
 %template(gnc_make_int64_option) gnc_make_option<int64_t>;
-%template(gnc_make_qofinstance_option) gnc_make_option<const QofInstance*>;
 %template(gnc_make_query_option) gnc_make_option<const QofQuery*>;
 %template(gnc_make_owner_option) gnc_make_option<const GncOwner*>;
 
@@ -1383,6 +1383,26 @@ inline SCM return_scm_value(ValueType value)
         qof_book_commit_edit(book);
     }
 
+    static GncOption*
+    gnc_make_qofinstance_option(const char* section,
+                                const char* name, const char* key,
+                                const char* doc_string,
+                                const QofInstance* value,
+                                GncOptionUIType ui_type)
+    {
+        try {
+            return new GncOption(GncOptionQofInstanceValue{section, name, key,
+                                                           doc_string,
+                                                           value, ui_type});
+        }
+        catch (const std::exception& err)
+        {
+            std::cerr << "Make QofInstance option threw unexpected exception"
+            << err.what() << ", option not created." << std::endl;
+            return nullptr;
+        }
+    }
+
     static GncOption*
     gnc_make_account_list_option(const char* section,
                                  const char* name, const char* key,
@@ -1569,7 +1589,7 @@ inline SCM return_scm_value(ValueType value)
                               const char* key, const char* doc_string,
                               gnc_commodity *value)
     {
-        return new GncOption{GncOptionValue<const QofInstance*>{
+        return new GncOption{GncOptionQofInstanceValue{
                 section, name, key, doc_string, (const QofInstance*)value,
                     GncOptionUIType::COMMODITY}};
     }
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 0582979c6..8f2e3f863 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -154,8 +154,9 @@ TEST_F(GncOptionTest, test_budget_ctor)
 {
     auto budget = gnc_budget_new(m_book);
     EXPECT_NO_THROW({
-            GncOption option("foo", "bar", "baz", "Phony Option",
-                             (const QofInstance*)budget);
+            GncOption option(GncOptionQofInstanceValue{"foo", "bar", "baz",
+                                                       "Phony Option",
+                                                       (const QofInstance*)budget});
         });
     gnc_budget_destroy(budget);
 }
@@ -163,7 +164,7 @@ TEST_F(GncOptionTest, test_budget_ctor)
 TEST_F(GncOptionTest, test_budget_out)
 {
     auto budget = gnc_budget_new(m_book);
-    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)budget};
+    GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", (const QofInstance*)budget}};
 
     auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
     std::ostringstream oss;
@@ -177,7 +178,7 @@ TEST_F(GncOptionTest, test_budget_in)
     auto budget = gnc_budget_new(m_book);
     auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
     std::istringstream iss{budget_guid};
-    GncOption option{GncOptionValue<const QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
+    GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
     iss >> option;
     EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<const QofInstance*>());
     gnc_budget_destroy(budget);
@@ -188,9 +189,10 @@ TEST_F(GncOptionTest, test_commodity_ctor)
     auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
                                     "NYSE", "HPE", NULL, 1);
     EXPECT_NO_THROW({
-            GncOption option("foo", "bar", "baz", "Phony Option",
-                             (const QofInstance*)hpe);
-        });
+            GncOption option(GncOptionQofInstanceValue{"foo", "bar", "baz",
+                                                       "Phony Option",
+                                                       (const QofInstance*)hpe});
+    });
     gnc_commodity_destroy(hpe);
 }
 
@@ -319,8 +321,8 @@ TEST_F(GncOptionCommodityTest, test_currency_out)
 
 TEST_F(GncOptionCommodityTest, test_commodity_out)
 {
-    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe,
-                     GncOptionUIType::COMMODITY};
+    GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe,
+                     GncOptionUIType::COMMODITY}};
     std::string hpe_str{make_commodity_str(m_hpe)};
     std::ostringstream oss;
     oss << option;
@@ -348,8 +350,8 @@ TEST_F(GncOptionCommodityTest, test_currency_in)
 
 TEST_F(GncOptionCommodityTest, test_commodity_in)
 {
-    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl,
-                     GncOptionUIType::COMMODITY};
+    GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl,
+                     GncOptionUIType::COMMODITY}};
 
     std::string hpe_str{make_commodity_str(m_hpe)};
     std::istringstream iss{hpe_str};

commit 11225d974172d93c134d3eb2beb002955a0fa08f
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Dec 15 14:54:41 2021 -0800

    c++options: Remove gnc:options-data

diff --git a/gnucash/report/reports/standard/test/test-stress-options.scm b/gnucash/report/reports/standard/test/test-stress-options.scm
index 9a1951785..e0f7d8c74 100644
--- a/gnucash/report/reports/standard/test/test-stress-options.scm
+++ b/gnucash/report/reports/standard/test/test-stress-options.scm
@@ -11,6 +11,7 @@
 (use-modules (gnucash reports))
 (use-modules (tests test-report-extras))
 (use-modules (srfi srfi-9))
+(use-modules (srfi srfi-26))
 (use-modules (srfi srfi-64))
 (use-modules (srfi srfi-98))
 (use-modules (tests srfi64-extras))
@@ -68,7 +69,8 @@
        (gnc:options-for-each
         (lambda (option)
           (when (case (gnc:option-type option)
-                  ((multichoice) (pair? (cdr (gnc:option-data option))))
+                  ((multichoice)
+                   (> (GncOption-num-permissible-values option) 1))
                   ((boolean) #t)
                   (else #f))
             (set! report-options-tested
@@ -76,8 +78,9 @@
                      (gnc:option-section option)
                      (gnc:option-name option)
                      (case (gnc:option-type option)
-                       ((multichoice) (map (lambda (d) (vector-ref d 0))
-                                           (gnc:option-data option)))
+                       ((multichoice)
+                        (map (cut GncOption-permissible-value option <>)
+                             (iota (GncOption-num-permissible-values option))))
                        ((boolean) (list #t #f))))
                     report-options-tested))))
         options)
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index c26663dd6..702fca981 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -85,15 +85,6 @@
 (define-public (gnc:option-type option)
   (GncOption-get-type option))
 
-;; Used only by test-stress-options.scm
-(define-public (gnc:option-data option)
-;  (define num-values (GncOption-num-permissible-values option))
-;  (let loop ((i 0) (retval '()))
-;      (if (>= i num-values) (reverse retval)
-;          (let ((value (GncOption-permissible-value option i))
-;                (name (GncOption-permissible-value-name option i)))
-;            (loop (1+ i) (cons (vector value name) retval))))))
-  (list (vector 1 2)))
 ;; Create the database and return a dispatch function.
 (define-public (gnc:new-options)
   (let ((optiondb (new-gnc-optiondb)))

commit 16dc15964c0eaa8727b8bd2d0d0940bd8451e97e
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Dec 15 14:32:39 2021 -0800

    c++options: Remove the callback registration functions.

diff --git a/gnucash/report/report-core.scm b/gnucash/report/report-core.scm
index 52505a0a5..036ffe0d8 100644
--- a/gnucash/report/report-core.scm
+++ b/gnucash/report/report-core.scm
@@ -375,14 +375,7 @@ not found.")))
     (let ((options (if (null? rest)
                        (gnc:report-template-new-options template)
                        (car rest))))
-      (gnc:report-set-options! r options)
-      (gnc:options-register-callback
-       #f #f
-       (lambda ()
-         (gnc:report-set-dirty?! r #t)
-         (let ((cb (gnc:report-template-options-changed-cb template)))
-           (if cb (cb r))))
-       options))
+      (gnc:report-set-options! r options))
     (gnc:report-set-id! r (gnc-report-add r))
     (gnc:report-id r)))
 
diff --git a/gnucash/report/reports/standard/view-column.scm b/gnucash/report/reports/standard/view-column.scm
index b84d536b9..7ef37994e 100644
--- a/gnucash/report/reports/standard/view-column.scm
+++ b/gnucash/report/reports/standard/view-column.scm
@@ -53,18 +53,6 @@
     
     options))
 
-(define (make-child-options-callback view child)
-  (let* ((view-opts (gnc:report-options view))
-	 (child-opts (gnc:report-options child))
-	 (id 
-	  (gnc:options-register-callback
-	   #f #f 
-	   (lambda ()
-	     (gnc:report-set-dirty?! child #t)
-	     (gnc:options-touch view-opts))
-	   child-opts)))
-    id))
-
 (define (render-view report)
   (let* ((view-doc (gnc:make-html-document))
 	 (options (gnc:report-options report))
@@ -80,17 +68,6 @@
 	 (current-width 0)
 	 (current-row-num 0))
 
-    ;; make sure each subreport has an option change callback that 
-    ;; pings the parent
-    (let loop ((reports reports) (new-reports '()))
-      (match reports
-        (() (gnc:option-set-value report-opt (reverse new-reports)))
-        (((child rowspan colspan callback) . rest)
-         (let ((callback (or callback
-                             (make-child-options-callback
-                              report (gnc-report-find child)))))
-           (loop rest (cons (list child rowspan colspan callback) new-reports))))))
-    
     ;; we really would rather do something smart here with the
     ;; report's cached text if possible.  For the moment, we'll have
     ;; to rerun every report, every time... FIXME
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 7cfa911ad..c26663dd6 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -178,12 +178,6 @@
   (with-output-to-string generate-forms))
 
 
-;; FIXME: Fake callback functions for boolean-complex and multichoice-callback
-
-(define-public (gnc:options-register-callback section name callback options) (options 'register-callback) 1)
-(define-public (gnc:options-register-c-callback section name callback data options) (options 'register-c-callback) 1)
-(define-public (gnc:options-unregister-callback-id id) 0 (options 'unregister-callback-id))
-
 ;; The following implement the old API that separated creation from registration.
 (define-public (gnc:register-option optdb opt)
   (issue-deprecation-warning "gnc:register-option is deprecated. Use gnc-register-foo-option instead.")

commit 759376eb1323f21d908a820d2b81bbc82316a584
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Dec 15 14:13:47 2021 -0800

    c++options: Fix multicolumn report

diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp
index 5c45dcea6..3c7c7c9fc 100644
--- a/gnucash/gnome/dialog-report-column-view.cpp
+++ b/gnucash/gnome/dialog-report-column-view.cpp
@@ -79,11 +79,19 @@ struct gncp_column_view_edit
     GtkWidget *size_button;
 };
 
+/* Even though these aren't external nor used outside this file they must be
+ * declared this way to ensure that they're in the library's symbol table and
+ * aren't mangled. That's so that dlsym is able to find them when GtkBuilder
+ * needs to connect the signals to them.
+ */
+extern "C"
+{
 void gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data);
 void gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data);
 void gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data);
 void gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data);
 void gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data);
+}
 
 static void
 gnc_column_view_set_option(GncOptionDB* odb, const char* section,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 7120fafe4..1e561dc94 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1259,13 +1259,16 @@ SCM
 gnc_option_db_lookup_scm_value(GncOptionDB* odb, const char* section,
                                const char* name)
 {
-    return SCM_BOOL_F;
+    auto option{odb->find_option(section, name)};
+    return option->get_value<SCM>();
 }
 
 void
-gnc_option_db_set_scm_value(GncOptionDB*, const char*, const char*, SCM)
+gnc_option_db_set_scm_value(GncOptionDB* odb, const char* section,
+                            const char* name, SCM value)
 {
-    std::cerr << "Use gnc_set_option." << std::endl;
+    auto option{odb->find_option(section, name)};
+    option->set_value(value);
 }
 
 // Force creation of templates
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index a74fdc91b..8a23eb1c3 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -1039,7 +1039,7 @@ inline SCM return_scm_value(ValueType value)
                     auto acct_list{option.get_value()};
                     if (acct_list.empty())
                         return no_value;
-                    SCM guid_list{scm_c_eval_string("'()")};//Empty list
+                    SCM guid_list{SCM_EOL};
                     for(auto acct : acct_list)
                     {
                         auto acct_str{qof_instance_to_string(QOF_INSTANCE(acct))};
@@ -1127,6 +1127,13 @@ inline SCM return_scm_value(ValueType value)
                     return scm_simple_format(SCM_BOOL_F, plain_format_str,
                                              scm_val);
                 }
+                if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionValue<SCM>>)
+                {
+                    auto scm_val{scm_list_1(return_scm_value(option.get_value()))};
+                    return scm_simple_format(SCM_BOOL_F, ticked_format_str,
+                                             scm_val);
+                }
                 auto serial{option.serialize()};
                 if (serial.empty())
                 {

commit 18edc175419534f578d0d365308eabc54a866325
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 14 14:20:20 2021 -0800

    c++options remove stray debugging comment.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 133949c6a..7120fafe4 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1259,7 +1259,6 @@ SCM
 gnc_option_db_lookup_scm_value(GncOptionDB* odb, const char* section,
                                const char* name)
 {
-    std::cerr << "Use gnc_option_db_lookup_value." << std::endl;
     return SCM_BOOL_F;
 }
 

commit b5d0c42505a12e341d4c4fa6a7ebf48ddc38bd88
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 14 12:06:39 2021 -0800

    c++options: Put copyright and FSF header comment on gnc-optiondb.i.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 9ab5d4bbe..a74fdc91b 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -1,7 +1,28 @@
 /*
- * Temporary swig interface file while developing C++ options.
+ * gnc-optiondb.i -- Swig Guile interface for the options system.
  *
- * unique_ptr SWIG wrapper from https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig
+ * Copyright 2021 John Ralls <jralls at ceridwen.us>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+/* unique_ptr SWIG wrapper from
+ * https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig
  */
 #if defined(SWIGGUILE)
 

commit a700701cd3dcb9f37aa17b85d8cf7f2d0147f87d
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 14 11:11:51 2021 -0800

    c++options: Remove three unused test functions.
    
    Scheme serialization is now tested in test-option-gnc-scheme-output.scm.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 702d62fbe..3adb3f634 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -56,7 +56,6 @@ extern "C"
 #include "gnc-option-uitype.hpp"
 
 
-static const char* commodity_scm_intro{"'(commodity-scm "};
 #ifndef SWIG
 size_t constexpr classifier_size_max{50};
 size_t constexpr sort_tag_size_max{10};
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index e2e01e132..0582979c6 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -306,25 +306,6 @@ static inline std::string make_commodity_str(gnc_commodity* com)
     return com_str;
 }
 
-static inline std::string make_currency_SCM_str(gnc_commodity* cur)
-{
-    std::string cur_str{gnc_commodity_get_mnemonic(cur)};
-    cur_str.insert(0, "\"");
-    cur_str += "\"";
-    return cur_str;
-}
-
-static inline std::string make_commodity_SCM_str(gnc_commodity* com)
-{
-    std::string com_str{commodity_scm_intro};
-    com_str += "\"";
-    com_str += gnc_commodity_get_namespace(com);
-    com_str += "\" \"";
-    com_str += gnc_commodity_get_mnemonic(com);
-    com_str += "\")";
-    return com_str;
-}
-
 TEST_F(GncOptionCommodityTest, test_currency_out)
 {
     auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
@@ -740,28 +721,6 @@ TEST_F(GncOptionAccountTest, test_account_list_in)
     EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[0]);
 }
 
-static inline std::string
-make_account_list_SCM_str(const GncOptionAccountList& acclist)
-{
-    std::string retval{"'("};
-    bool first = true;
-    for (auto acc : acclist)
-    {
-        if (first)
-        {
-            first = false;
-            retval += '"';
-        }
-        else
-            retval += " \"";
-        retval += gnc::GUID{*qof_instance_get_guid(acc)}.to_string();
-        retval += '"';
-    }
-    retval += ')';
-    return retval;
-}
-
-
 using KT = GncOptionMultichoiceKeyType;
 class GncOptionMultichoiceTest : public ::testing::Test
 {

commit eb0bd4f998ad4a39fd0c890ee258f98fbf28972e
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 14 11:18:15 2021 -0800

    c++options: Remove unneeded C++20 header and resolve ambiguous call.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index e8aaa6ea4..6bd896595 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -26,7 +26,6 @@
 #include <gnc-datetime.hpp>
 #include <guid.hpp>
 #include <cassert>
-#include <charconv>
 #include <sstream>
 extern "C"
 {
@@ -697,7 +696,8 @@ GncOptionDateValue::deserialize(const std::string& str) noexcept
     auto period_str{str.substr(date_value_pos)};
     if (type_str == "absolute")
     {
-        set_value(std::stoll(period_str));
+        // Need a cast to disambiguate from time64.
+        set_value(static_cast<size_t>(std::stoll(period_str)));
         return true;
     }
     else if (type_str == "relative ")

commit a3f50586dfaf76c0801bf9ff62561d1492f5d8db
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 14 11:03:37 2021 -0800

    c++options: More thorough testing of scheme serialization.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index f339eabf3..e8aaa6ea4 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -457,7 +457,7 @@ GncOptionValue<ValueType>::serialize() const noexcept
     else if constexpr(std::is_arithmetic_v<ValueType>)
         return std::to_string(m_value);
     else
-        return "";
+        return "Serialization not implemented";
 }
 
 template <typename ValueType> bool
diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
index 34d1389a6..cd8facb9e 100644
--- a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
+++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
@@ -32,13 +32,23 @@
   (test-begin "test-gnc-option-scheme-io")
   (test-gnc-string-option-to-scheme)
   (test-gnc-text-option-to-scheme)
-  (test-gnc-pixmap-option-to-scheme)
+  (test-gnc-font-option-to-scheme)
   (test-gnc-currency-option-to-scheme)
   (test-gnc-budget-option-to-scheme)
-  (test-gnc-font-option-to-scheme)
   (test-gnc-commodity-option-to-scheme)
+  (test-gnc-bool-option-to-scheme)
+  (test-gnc-pixmap-option-to-scheme)
   (test-gnc-date-option-to-scheme)
+  (test-gnc-account-options-to-scheme)
   (test-gnc-multichoice-option-to-scheme)
+  (test-gnc-list-option-to-scheme)
+  (test-gnc-number-range-option-to-scheme)
+  (test-gnc-number-plot-size-option-to-scheme)
+  (test-gnc-query-option-to-scheme)
+  (test-gnc-color-option-to-scheme)
+  (test-gnc-invoice-option-to-scheme)
+  (test-gnc-owner-option-to-scheme)
+  (test-gnc-internal-option-to-scheme)
   (test-end "test-gnc-option-scheme-io"))
 
 (define test-unchanged-section-output-template
@@ -82,7 +92,8 @@
 " value))
 
 (define (test-commodity-output-template value)
-  (format #f "
+  (let ((value-parts (string-split value #\:)))
+    (format #f "
 ; Section: foo
 
 (let ((option (gnc:lookup-option options
@@ -90,7 +101,7 @@
                                  \"bar\")))
   ((lambda (o) (if o (gnc:option-set-value o \"~a\" \"~a\"))) option))
 
-" (string-split value #\:)))
+" (car value-parts) (cadr value-parts))))
 
 (define (test-budget-output-template value)
   (format #f "
@@ -105,25 +116,31 @@
           (gncBudgetGetGUID value)))
 
 
-(define (test-option-scheme-output make-option-func test-template default value)
+(define (test-option-scheme-output name make-option-func get-value-func test-template default value)
   (let ((odb (gnc:new-options))
         (option (make-option-func "foo" "bar" "baz" "Test Option" default)))
     (gnc:register-option odb option)
-    (test-equal test-unchanged-section-output-template
+    (test-equal (string-append name " unchanged")
+                test-unchanged-section-output-template
                 (gnc:generate-restore-forms odb "options"))
     (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") value)
-    (test-equal (test-template (GncOption-serialize (gnc:lookup-option odb "foo" "bar")))
+    (test-equal (string-append name " value")
+                (test-template (get-value-func (gnc:lookup-option odb "foo" "bar")))
                 (gnc:generate-restore-forms odb "options"))))
 
 (define (test-gnc-string-option-to-scheme)
   (test-begin "test-gnc-string-option-to-scheme")
-  (test-option-scheme-output gnc:make-string-option test-string-output-template
+  (test-option-scheme-output "string"
+                             gnc:make-string-option GncOption-get-scm-value
+                             test-string-output-template
                              "waldo" "pepper")
   (test-end "test-gnc-string-option-to-scheme"))
 
 (define (test-gnc-text-option-to-scheme)
   (test-begin "test-gnc-text-option-to-scheme")
-  (test-option-scheme-output gnc:make-string-option test-string-output-template
+  (test-option-scheme-output "text"
+                             gnc:make-string-option GncOption-get-scm-value
+                             test-string-output-template
                              ""
 "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium
 doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore
@@ -132,7 +149,9 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
 
 (define (test-gnc-font-option-to-scheme)
   (test-begin "test-gnc-font-option-to-scheme")
-  (test-option-scheme-output gnc:make-font-option test-string-output-template
+  (test-option-scheme-output "font"
+                             gnc:make-font-option GncOption-get-scm-value
+                             test-string-output-template
                              "URW Bookman L Bold Italic 12"
                              "Helvetica 12")
   (test-end "test-gnc-font-option-to-scheme"))
@@ -147,8 +166,10 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
           (EUR (gnc-commodity-new book "European Union Euro" "CURRENCY" "EUR" "" 100)))
       (gnc-commodity-table-insert table USD)
       (gnc-commodity-table-insert table EUR)
-      (test-option-scheme-output gnc:make-currency-option test-currency-output-template
-                               USD EUR)
+      (test-option-scheme-output "currency"
+                                 gnc:make-currency-option GncOption-serialize
+                                 test-currency-output-template
+                                 USD EUR)
 ;; Garbage collection has already eaten USD and EUR.
       (test-book-clear-data book "gnc-commodity-table")
       (gnc-commodity-table-destroy table)
@@ -157,8 +178,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
 
 (define (test-gnc-budget-option-to-scheme)
  (test-begin "test-gnc-budget-option-to-scheme")
-  (let* ((session (gnc-get-current-session))
-        (book (gnc-get-current-book))
+  (let* ((book (gnc-get-current-book))
         (budget2 (gnc-budget-new book))
         (budget1 (gnc-budget-new book))
         (guid1 (gncBudgetGetGUID budget1))
@@ -171,32 +191,43 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
     (let ((odb (gnc:new-options))
           (option (gnc:make-budget-option "foo" "bar" "baz" "Test Option")))
     (gnc:register-option odb option)
-    (test-equal test-unchanged-section-output-template
+    (test-equal "budget unchanged"
+                test-unchanged-section-output-template
                 (gnc:generate-restore-forms odb "options"))
     (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") budget2)
-    (test-equal (gnc-budget-get-default book) budget1)
-    (test-equal (test-budget-output-template budget2)
+    (test-equal "default budget value" (gnc-budget-get-default book) budget1)
+    (test-equal "budget restore form" (test-budget-output-template budget2)
                 (gnc:generate-restore-forms odb "options")))
     (gnc-clear-current-session))
   (test-end "test-gnc-budget-option-to-scheme"))
 
 (define (test-gnc-commodity-option-to-scheme)
   (test-begin "test-gnc-commodity-option-to-scheme")
-  (let* ((book (gnc-option-test-book-new))
+  (let* ((session (gnc-get-current-session))
+         (book (gnc-get-current-book))
+         (comm-tbl (gnc-commodity-table-get-table book))
          (AAPL (gnc-commodity-new book "Apple" "NASDAQ" "AAPL" "" 1))
          (FMAGX (gnc-commodity-new book "Fidelity Magellan Fund" "FUND" "FMAGX" "" 1000)))
-  (test-option-scheme-output gnc:make-commodity-option test-currency-output-template
-                             AAPL FMAGX))
+    (test-option-scheme-output "commodity"
+                               gnc:make-commodity-option GncOption-serialize
+                               test-commodity-output-template
+                               AAPL FMAGX))
   (test-end "test-gnc-commodity-option-to-scheme"))
 
 (define (test-gnc-bool-option-to-scheme)
   (test-begin "test-gnc-bool-option-to-scheme")
-  (test-option-scheme-output gnc:make-simple-boolean-option test-string-output-template #f #t)
+  (test-option-scheme-output "bool"
+                             gnc:make-simple-boolean-option
+                             GncOption-get-scm-value
+                             test-string-output-template #f #t)
   (test-end "test-gnc-bool-option-to-scheme"))
 
 (define (test-gnc-pixmap-option-to-scheme)
   (test-begin "test-gnc-pixmap-option-to-scheme")
-  (test-option-scheme-output gnc:make-pixmap-option test-string-output-template "" "~/mybusiness/mylogo.png")
+  (test-option-scheme-output "pixmap"
+                             gnc:make-pixmap-option GncOption-get-scm-value
+                             test-string-output-template
+                             "" "~/mybusiness/mylogo.png")
   (test-end "test-gnc-pixmap-option-to-scheme"))
 
 (define (test-gnc-date-option-to-scheme)
@@ -252,48 +283,61 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
 ;; Destroying the book destroys the account tree too
     (gnc-option-test-book-destroy book))
 
-  (define (test-gnc-account-list-option-to-scheme)
+  (define (test-gnc-account-list-option-to-scheme book)
+    (define (test-account-list-output-template value)
+        (format #f "
+; Section: foo
+
+(let ((option (gnc:lookup-option options
+                                 \"foo\"
+                                 \"bar\")))
+  ((lambda (o) (if o (gnc:option-set-value o '~s))) option))
+
+" (reverse (string-split value #\ ))))
+
     (test-begin "test-gnc-account-list-option-to-scheme")
     (let ((odb (gnc:new-options))
           (acctlist (gnc-account-list-from-types book
                                                  (list ACCT-TYPE-STOCK))))
-      (gnc-register-option odb
+      (gnc:register-option odb
                            (gnc:make-account-list-option
-                            "foo" "bar" "a" "baz" acctlist
+                            "foo" "bar" "a" "baz" (lambda () acctlist)
                             (lambda (ac)
                               (let ((type (xaccAccountGetAccountType ac)))
                                 (or (eq type ACCT-TYPE-STOCK)
                                     (eq type ACCT-TYPE-BANK)))) #t))
-      (test-equal test-unchanged-section-output-template
+      (test-equal "account list unchanged"
+                  test-unchanged-section-output-template
                   (gnc:generate-restore-forms odb "options"))
       (let ((option (gnc:lookup-option odb "foo" "bar"))
-            (test-template test-literal-output-template)
+            (test-template test-account-list-output-template)
             (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK))))
-        (gnc-option-set-value option new-acclist)
-        (test-equal (test-template (GncOption-serialize option))
+        (gnc:option-set-value option new-acclist)
+        (test-equal "account list form"
+                    (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-account-list-option-to-scheme"))
 
-  (define (test-gnc-account-sel-option-to-scheme)
+  (define (test-gnc-account-sel-option-to-scheme book)
     (test-begin "test-gnc-account-sel-option-to-scheme")
     (let ((odb (gnc:new-options))
-          (acctlist (gnc-account-list-from-types book
-                                                 (list ACCT-TYPE-STOCK))))
-      (gnc-register-option odb
-                           (gnc:make-account-list-option
-                            "foo" "bar" "a" "baz" acctlist
+          (bank (gnc-account-lookup-by-name(gnc-book-get-root-account book)
+                                                "Bank")))
+      (gnc:register-option odb
+                           (gnc:make-account-sel-option
+                            "foo" "bar" "a" "baz" (lambda () '())
                             (lambda (ac)
                               (let ((type (xaccAccountGetAccountType ac)))
                                 (or (eq type ACCT-TYPE-STOCK)
-                                    (eq type ACCT-TYPE-BANK)))) #t))
-      (test-equal test-unchanged-section-output-template
+                                    (eq type ACCT-TYPE-BANK))))))
+      (test-equal "account sel unchanged" test-unchanged-section-output-template
                   (gnc:generate-restore-forms odb "options"))
       (let ((option (gnc:lookup-option odb "foo" "bar"))
-            (test-template test-literal-output-template)
-            (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK))))
-        (gnc-option-set-value option new-acclist)
-        (test-equal (test-template (GncOption-serialize option))
+            (test-template test-string-output-template))
+        (gnc:option-set-value option bank)
+        (test-equal "account sel form"
+                    (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-account-sel-option-to-scheme"))
@@ -302,8 +346,8 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
          (root-account (gnc-account-create-root book)))
     (test-group-with-cleanup "test-gnc-account-options-to-schemes"
                              (make-account-tree book root-account)
-                             (test-gnc-account-list-option-to-scheme)
-                             (test-gnc-account-sel-option-to-scheme)
+                             (test-gnc-account-list-option-to-scheme book)
+                             (test-gnc-account-sel-option-to-scheme book)
                              (cleanup book root-account))))
 
 
@@ -319,11 +363,11 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
           (list (vector 'all "All")
           (vector 1 "1") (vector 2 "2") (vector 3 "3")
           (vector 4 "4") (vector 5 "5") (vector 6 "6"))))
-    (test-equal test-unchanged-section-output-template
+    (test-equal "multichoice unchanged" test-unchanged-section-output-template
                 (gnc:generate-restore-forms odb "options"))
     (let ((option (gnc:lookup-option odb "foo" "bar")))
       (gnc:option-set-value option value)
-      (test-equal (test-template (GncOption-serialize option))
+      (test-equal "multichoice form" (test-template (GncOption-serialize option))
                   (gnc:generate-restore-forms odb "options"))))
   (test-end "test-gnc-multichoice-option-to-scheme"))
 
@@ -333,15 +377,15 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
           (choices (list (vector 'good "The Good")
                          (vector 'bad  "The Bad")
                          (vector 'ugly "The Ugly"))))
-      (gnc-register-option odb
+      (gnc:register-option odb
                            (gnc:make-list-option
                             "foo" "bar" "a" "baz" '(bad) choices))
-      (test-equal test-unchanged-section-output-template
+      (test-equal "list unchanged" test-unchanged-section-output-template
                   (gnc:generate-restore-forms odb "options"))
       (let ((option (gnc:lookup-option odb "foo" "bar"))
             (test-template test-literal-output-template))
-        (gnc-option-set-value option '(ugly))
-        (test-equal (test-template (GncOption-serialize option))
+        (gnc:option-set-value option '(ugly))
+        (test-equal "list form" (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-list-option-to-scheme"))
@@ -353,16 +397,17 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
           (max-value 100.0)
           (dec-places 2.0)
           (step 0.10))
-      (gnc-register-option odb
+      (gnc:register-option odb
                            (gnc:make-number-range-option
                             "foo" "bar" "a" "baz" 49.0 min-value
                             max-value dec-places step))
-      (test-equal test-unchanged-section-output-template
+      (test-equal "number-range unchanged" test-unchanged-section-output-template
                   (gnc:generate-restore-forms odb "options"))
       (let ((option (gnc:lookup-option odb "foo" "bar"))
             (test-template test-literal-output-template))
-        (gnc-option-set-value option 42.0)
-        (test-equal (test-template (GncOption-serialize option))
+        (gnc:option-set-value option 42.0)
+        (test-equal "number-range form"
+                    (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-number-range-option-to-scheme"))
@@ -370,26 +415,45 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
 (define (test-gnc-number-plot-size-option-to-scheme)
     (test-begin "test-gnc-number-plot-size-option-to-scheme")
     (let ((odb (gnc:new-options))
-          (min-value 100)
-          (max-value 10000)
+          (min-value 10)
+          (max-value 100)
           (dec-places 0)
           (step 5))
-      (gnc-register-option odb
+      (gnc:register-option odb
                            (gnc:make-number-plot-size-option
-                            "foo" "bar" "a" "baz" 490 min-value
+                            "foo" "bar" "a" "baz" 49 min-value
                             max-value dec-places step))
-      (test-equal test-unchanged-section-output-template
+      (test-equal "number-plot unchanged" test-unchanged-section-output-template
                   (gnc:generate-restore-forms odb "options"))
       (let ((option (gnc:lookup-option odb "foo" "bar"))
             (test-template test-literal-output-template))
-        (gnc-option-set-value option 420)
-        (test-equal (test-template (GncOption-serialize option))
+        (gnc:option-set-value option 42)
+        (test-equal "number-plot form"
+                    (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-number-plot-size-option-to-scheme"))
 
 (define (test-gnc-query-option-to-scheme)
-  (test-begin "test-gnc-number-plot-size-option-to-scheme")
+  (define query-unchanged-section-output-template
+    "
+; Section: __reg
+
+"
+    )
+
+  (define (query-literal-output-template value)
+    (format #f "
+; Section: __reg
+
+(let ((option (gnc:lookup-option options
+                                 \"__reg\"
+                                 \"query\")))
+  ((lambda (o) (if o (gnc:option-set-value o '~a))) option))
+
+" value))
+ 
+  (test-begin "test-gnc-query-option-to-scheme")
   (let ((odb (gnc:new-options))
         (query-scm '(query-v2
                      (terms (((("book" "guid") #f guid 3 1 ("3a5a4bc736d84b879b776ea8caadd3b2"))
@@ -399,56 +463,127 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
                      (secondary-sort #f)
                      (tertiary-sort #f)
                      (max-results -1))))
-    (gnc-register-option odb
+    (gnc:register-option odb
                          (gnc:make-query-option "__reg" "query" '()))
-      (test-equal test-unchanged-section-output-template
+      (test-equal "query unchanged" query-unchanged-section-output-template
                   (gnc:generate-restore-forms odb "options"))
       (let ((option (gnc:lookup-option odb "__reg" "query"))
-            (test-template test-literal-output-template))
-        (gnc-option-set-value option (gnc-scm2query query-scm))
-        (test-equal (test-template (GncOption-serialize option))
+            (test-template query-literal-output-template))
+        (gnc:option-set-value option (gnc-scm2query query-scm))
+        (test-equal  "query form" (test-template (GncOption-get-scm-value option))
                     (gnc:generate-restore-forms odb "options"))
         ))
-  (test-end "test-gnc-number-plot-size-option-to-scheme"))
+  (test-end "test-gnc-query-option-to-scheme"))
 
 (define (test-gnc-color-option-to-scheme)
+  (define (test-color-output-template value)
+    (let* ((len (string-length value))
+           (red (string->number (substring/shared value 0 2) 16))
+           (blue (string->number (substring/shared value 2 4) 16))
+           (green (string->number (substring/shared value 4 6) 16))
+           (alpha (if (> len 7)
+                      (string->number (substring/shared value 6 8) 16)
+                      #xff)))
+    (format #f "
+; Section: foo
+
+(let ((option (gnc:lookup-option options
+                                 \"foo\"
+                                 \"bar\")))
+  ((lambda (o) (if o (gnc:option-set-value o '(~f ~f ~f ~f)))) option))
+
+" red blue green alpha)))
     (test-begin "test-gnc-coloroption-to-scheme")
     (let ((odb (gnc:new-options))
-          (default-color (list #xb2 #x22 $x22 #xff))
+          (default-color (list #xb2 #x22 #x22 #xff))
           (new-color (list #x00 #xca #x3b #xff)))
-      (test-option-scheme-output gnc:make-color-option
-                                 test-literal-output-template
-                                 default-color new-color))
+      (gnc:register-option odb
+                           (gnc:make-color-option
+                            "foo" "bar" "a" "baz" default-color #f #t))
+      (test-equal "color unchanged" test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let ((option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-color-output-template))
+        (gnc:option-set-value option new-color)
+        (test-equal "color form"
+                    (test-template (GncOption-serialize option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
     (test-end  "test-gnc-color-option-to-scheme"))
 
 (define (test-gnc-invoice-option-to-scheme)
-    (test-begin "test-gnc-invoice-option-to-scheme")
-    (let ((odb (gnc:new-options))
-          (invoice '"13b305236443451a86c5366b7f890ecb"))
-      (test-option-scheme-output gnc:make-color-option
-                                 test-literal-output-template
-                                 (lambda () '()) invoice))
+  (test-begin "test-gnc-invoice-option-to-scheme")
+  (let ((odb (gnc:new-options)))
+       (gnc:register-option odb
+                           (gnc:make-invoice-option "foo" "bar" "a" "baz"
+                                                  (lambda () '()) (lambda () #t)))
+      (test-equal "invoice unchanged" test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let* ((book (gnc-get-current-book))
+            (inv (gncInvoiceCreate book))
+            (option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-string-output-template))
+        (gnc:option-set-value option inv)
+        (test-equal "invoice form" (test-template (GncOption-serialize option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
     (test-end  "test-gnc-invoice-option-to-scheme"))
 
 (define (test-gnc-owner-option-to-scheme)
-    (test-begin "test-owner-option-to-scheme")
+  (test-begin "test-owner-option-to-scheme")
     (let ((odb (gnc:new-options)))
-      (gnc-register-option odb
+      (gnc:register-option odb
                            (gnc:make-owner-option "foo" "bar" "a" "baz"
-                                                  (lambda () '()) #f
-                                                  'GNC-OWNER-CUSTOMER))
-      (test-equal test-unchanged-section-output-template
+                                                  (lambda () '()) (lambda () #t)
+                                                  GNC-OWNER-CUSTOMER))
+      (test-equal "owner unchanged" test-unchanged-section-output-template
                   (gnc:generate-restore-forms odb "options"))
-      (let ((option (gnc:lookup-option odb "foo" "bar"))
-            (test-template test-literal-output-template))
-        (gnc-option-set-value option '"13b305236443451a86c5366b7f890ecb")
-        (test-equal (test-template (GncOption-serialize option))
+      (let* ((option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-literal-output-template)
+            (book (gnc-get-current-book))
+            (owner (gncOwnerNew)))
+        (gncOwnerInitCustomer owner (gncCustomerCreate book))
+        (gnc:option-set-value option owner)
+        (test-equal "owner form"
+                    (test-template (cons (gncOwnerGetType owner)
+                                         (gncOwnerReturnGUID owner)))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-owner-option-to-scheme"))
 
+(define (test-gnc-internal-option-to-scheme)
+  (define (test-output-template name value)
+    (format #f "
+(let ((option (gnc:lookup-option options
+                                 \"__reg\"
+                                 ~s)))
+  ((lambda (o) (if o (gnc:option-set-value o ~s))) option))
+" name value))
+  (test-begin "test-gnc-internal-option-to-scheme")
+  (let ((odb (gnc:new-options))
+        (option-b (gnc:make-internal-option "__reg" "bar" #f))
+        (option-s (gnc:make-internal-option "__reg" "baz" "waldo")))
+    (gnc:register-option odb option-b)
+    (gnc:register-option odb option-s)
+    (test-equal "Internal unchanged" "
+; Section: __reg
+
+"
+                (gnc:generate-restore-forms odb "options"))
+    (gnc:option-set-value (gnc:lookup-option odb "__reg" "bar") #t)
+    (gnc:option-set-value (gnc:lookup-option odb "__reg" "baz") "pepper")
+    (test-equal "internal form" (format #f "
+; Section: __reg
+~a~a
+"
+                                        (test-output-template "bar" #t)
+                                        (test-output-template "baz" "pepper"))
+                (gnc:generate-restore-forms odb "options"))
+                )
+  (test-end "test-gnc-internal-option-to-scheme"))
+
 ;; The following are saved only to KVP, no Scheme generator needed:
 ;;(define (test-gnc-dateformat-option-to-scheme)
-;;(define (test-gnc-taxtable-option-to-scheme) 
+;;(define (test-gnc-taxtable-option-to-scheme)
 ;;(define (test-gnc-counter-option-to-scheme)
 ;;(define (test-gnc-counter-format-option-to-scheme)

commit 6cd88c230c1032b6e7427a04ae2220e0e72e7100
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 14 11:03:01 2021 -0800

    c++options: Create bool or string internal options when possible.
    
    There's no need to save internal options as SCM when we support the
    underlying type in C++.

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index f0aaf2077..7cfa911ad 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -280,7 +280,13 @@
   (let ((type (GncOptionUIType-INTERNAL))
         (key "_")
         (desc "internal"))
-    (gnc-make-SCM-option section name key desc default type)))
+    (cond
+     ((boolean? default) (gnc-make-bool-option section name key desc default
+                                               (GncOptionUIType-INTERNAL)))
+     ((string? default) (gnc-make-string-option section name key desc default
+                                                (GncOptionUIType-INTERNAL)))
+     (else
+       (gnc-make-SCM-option section name key desc default type)))))
 (define-public (gnc:make-owner-option section name key docstring getter validator owner-type)
   (issue-deprecation-warning "gnc:make-owner-option is deprecated. Make and register the option in one command with gnc-register-owner-option.")
   (let* ((ui-type (cond

commit e9b850cca5a6be742919668886572e9e07744aa7
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 14 10:58:48 2021 -0800

    c++options: Correct Scheme serialization of type bool.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 769e1d6ed..9ab5d4bbe 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -1099,6 +1099,13 @@ inline SCM return_scm_value(ValueType value)
                         return scm_simple_format(SCM_BOOL_F, ticked_format_str, scm_str);
                     }
                 }
+                if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionValue<bool>>)
+                {
+                    auto scm_val{scm_list_1(return_scm_value(option.get_value()))};
+                    return scm_simple_format(SCM_BOOL_F, plain_format_str,
+                                             scm_val);
+                }
                 auto serial{option.serialize()};
                 if (serial.empty())
                 {

commit 66f9fc81c70d2c767bb9befb548b0a8b5656d34f
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 14 10:57:41 2021 -0800

    c++options: Implement serialization for GncOwner.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 92f60c29c..f339eabf3 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -27,7 +27,7 @@
 #include <guid.hpp>
 #include <cassert>
 #include <charconv>
-
+#include <sstream>
 extern "C"
 {
 #include "gnc-accounting-period.h"
@@ -440,6 +440,16 @@ GncOptionValue<ValueType>::serialize() const noexcept
     static const std::string no_value{"No Value"};
     if constexpr(std::is_same_v<ValueType, const QofInstance*>)
         return m_value ? qof_instance_to_string(m_value) : no_value;
+    if constexpr(std::is_same_v<ValueType, const GncOwner*>)
+    {
+        if (!m_value)
+            return no_value;
+        auto guid{qof_instance_to_string(qofOwnerGetOwner(m_value))};
+        auto type{qofOwnerGetType(m_value)};
+        std::ostringstream ostr{};
+        ostr << type << " " << guid;
+        return ostr.str();
+    }
     else if constexpr(is_same_decayed_v<ValueType, std::string>)
         return m_value;
     else if constexpr(is_same_decayed_v<ValueType, bool>)
@@ -455,6 +465,14 @@ GncOptionValue<ValueType>::deserialize(const std::string& str) noexcept
 {
     if constexpr(std::is_same_v<ValueType, const QofInstance*>)
         set_value(qof_instance_from_string(str, get_ui_type()));
+    if constexpr(std::is_same_v<ValueType, const GncOwner*>)
+    {
+        std::istringstream istr{str};
+        std::string type, guid;
+        istr >> type >> guid;
+        auto inst{qof_instance_from_string(guid, get_ui_type())};
+        qofOwnerSetEntity(const_cast<GncOwner*>(m_value), inst);
+    }
     else if constexpr(is_same_decayed_v<ValueType, std::string>)
         set_value(str);
     else if constexpr(is_same_decayed_v<ValueType, bool>)
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 334bb5d11..769e1d6ed 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -1069,7 +1069,11 @@ inline SCM return_scm_value(ValueType value)
                 if constexpr (is_same_decayed_v<decltype(option),
                               GncOptionValue<const GncOwner*>>)
                 {
-                    return scm_from_utf8_string("Not Yet Implemented");
+                    auto value{option.get_value()};
+                    auto guid{scm_from_utf8_string(qof_instance_to_string(qofOwnerGetOwner(value)).c_str())};
+                    auto type{scm_from_long(gncOwnerGetType(value))};
+                    return scm_simple_format(SCM_BOOL_F, ticked_format_str,
+                                             scm_list_1(scm_cons(type, guid)));
                 }
                 if constexpr (is_QofQueryValue_v<decltype(option)>)
                 {

commit 0ce841d4ce650135b8d9c293e06b1cb667e175e9
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Dec 13 11:06:33 2021 -0800

    c++options Fix Scheme bindings for QofQuery options.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 5131aef8c..92f60c29c 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -334,9 +334,6 @@ qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
         case GncOptionUIType::TAX_TABLE:
             qof_type = "gncTaxtable";
             break;
-        case GncOptionUIType::QUERY:
-            qof_type = "gncQuery";
-            break;
         case GncOptionUIType::ACCOUNT_LIST:
         case GncOptionUIType::ACCOUNT_SEL:
         default:
@@ -432,6 +429,11 @@ GncOptionValue<ValueType>::reset_default_value()
     m_value = m_default_value;
 }
 
+/* Missing on purpose: QofQuery because for current usage it's serialized with
+ * gnc_query2scm. The future is to replace QofQuery with SQL queries so there's
+ * not much point to spending the time to create a std::string serialization for
+ * them.
+ */
 template <typename ValueType> std::string
 GncOptionValue<ValueType>::serialize() const noexcept
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 3cb568f40..334bb5d11 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -219,16 +219,15 @@ scm_from_value<const Account*>(const Account* value)
 }
 
 template <> inline SCM
-scm_from_value<const QofQuery*>(const QofQuery* value)
+scm_from_value<QofQuery*>(QofQuery* value)
 {
-    auto ptr{static_cast<void*>(const_cast<QofQuery*>(value))};
-    return SWIG_NewPointerObj(ptr, SWIGTYPE_p__QofQuery, FALSE);
+    return gnc_query2scm(value);
 }
 
 template <> inline SCM
-scm_from_value<QofQuery*>(QofQuery* value)
+scm_from_value<const QofQuery*>(const QofQuery* value)
 {
-    return scm_from_value<const QofQuery*>(value);
+    return scm_from_value<QofQuery*>(const_cast<QofQuery*>(value));
 }
 
 template <> inline SCM
@@ -1014,18 +1013,18 @@ inline SCM return_scm_value(ValueType value)
                 static const auto no_value{scm_from_utf8_string("No Value")};
                 if constexpr (is_same_decayed_v<decltype(option),
                               GncOptionAccountListValue>)
-        {
+                {
                     static const SCM list_format_str{scm_from_utf8_string("'~s")};
                     auto acct_list{option.get_value()};
                     if (acct_list.empty())
                         return no_value;
                     SCM guid_list{scm_c_eval_string("'()")};//Empty list
                     for(auto acct : acct_list)
-        {
+                    {
                         auto acct_str{qof_instance_to_string(QOF_INSTANCE(acct))};
                         auto acct_scm{scm_from_utf8_string(acct_str.c_str())};
                         guid_list = scm_cons(acct_scm, guid_list);
-        }
+                    }
                     return scm_simple_format(SCM_BOOL_F, list_format_str, scm_list_1(guid_list));
 
                 }
@@ -1037,13 +1036,13 @@ inline SCM return_scm_value(ValueType value)
                         return no_value;
                     auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))};
                     if (uitype == GncOptionUIType::CURRENCY)
-        {
-            const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")};
-            return scm_simple_format(SCM_BOOL_F, quoted_format_str, value);
-        }
-        else if (uitype == GncOptionUIType::COMMODITY)
-        {
-            const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")};
+                    {
+                        const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")};
+                        return scm_simple_format(SCM_BOOL_F, quoted_format_str, value);
+                    }
+                    else if (uitype == GncOptionUIType::COMMODITY)
+                    {
+                        const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")};
                         auto comm{GNC_COMMODITY(option.get_value())};
                         auto name_space{gnc_commodity_get_namespace(comm)};
                         auto mnemonic{gnc_commodity_get_mnemonic(comm)};
@@ -1052,20 +1051,20 @@ inline SCM return_scm_value(ValueType value)
                         return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val);
                     }
                     else
-            {
+                    {
                         return scm_simple_format(SCM_BOOL_F, plain_format_str, value);
-            }
-        }
+                    }
+                }
                 if constexpr (is_same_decayed_v<decltype(option),
                               GncOptionDateValue>)
-        {
+                {
                     auto serial{option.serialize()};
                     if (serial.empty())
                         return no_value;
                     auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))};
-            const SCM date_fmt{scm_from_utf8_string("'~a")};
-            return scm_simple_format(SCM_BOOL_F, date_fmt, value);
-        }
+                    const SCM date_fmt{scm_from_utf8_string("'~a")};
+                    return scm_simple_format(SCM_BOOL_F, date_fmt, value);
+                }
 
                 if constexpr (is_same_decayed_v<decltype(option),
                               GncOptionValue<const GncOwner*>>)
@@ -1084,14 +1083,14 @@ inline SCM return_scm_value(ValueType value)
                               GncOptionRangeValue<int>>  ||
                               is_same_decayed_v<decltype(option),
                               GncOptionRangeValue<double>>)
-        {
+                {
                     auto serial{option.serialize()};
                     if (serial.empty())
-            {
+                    {
                         return no_value;
-            }
-            else
-            {
+                    }
+                    else
+                    {
                         auto scm_str{scm_list_1(scm_from_utf8_string(serial.c_str()))};
                         return scm_simple_format(SCM_BOOL_F, ticked_format_str, scm_str);
                     }
@@ -1100,7 +1099,7 @@ inline SCM return_scm_value(ValueType value)
                 if (serial.empty())
                 {
                     return no_value;
-            }
+                }
                 else if ($self->get_ui_type() == GncOptionUIType::COLOR)
                 {
                     auto red{static_cast<double>(std::stoi(serial.substr(0, 2),
@@ -1124,7 +1123,7 @@ inline SCM return_scm_value(ValueType value)
                 {
                     auto scm_str{scm_list_1(scm_from_utf8_string(serial.c_str()))};
                     return scm_simple_format(SCM_BOOL_F, plain_format_str, scm_str);
-        }
+                }
             }, swig_get_option($self));
     }
 
@@ -1175,6 +1174,20 @@ inline SCM return_scm_value(ValueType value)
                         }
                         return;
                     }
+                    if constexpr (is_QofQueryValue_v<decltype(option)>)
+                    {
+                        if (scm_is_pair(new_value))
+                        {
+                            auto val{gnc_scm2query(new_value)};
+                            option.set_value(val);
+                        }
+                        else
+                        {
+                            auto val{scm_to_value<const QofQuery*>(new_value)};
+                            option.set_value(val);
+                        }
+                        return;
+                    }
                     if constexpr (is_same_decayed_v<decltype(option),
                                   GncOptionAccountSelValue>)
                     {
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 52e55c2e0..f0aaf2077 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -274,7 +274,7 @@
 (define-public (gnc:make-query-option section name default)
   (issue-deprecation-warning "gnc:make-query-option is deprecated. Make and register the option in one command with gnc-register-query-option.")
   (let ((defv (if (list? default) default (gnc-query2scm default))))
-    (gnc-make-SCM-option section name "" "query" defv (GncOptionUIType-INTERNAL))))
+    (gnc-make-query-option section name "" "query" defv (GncOptionUIType-INTERNAL))))
 (define-public (gnc:make-internal-option section name default)
   (issue-deprecation-warning "gnc:make-internal-option is deprecated. Make and register the option in one command with gnc-register-internal-option.")
   (let ((type (GncOptionUIType-INTERNAL))

commit 216b483e265f05e118fba053451f1380e93fdb9a
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Dec 13 11:05:15 2021 -0800

    c++options SCM bindings Rewrite save_scm_value to branch on type.
    
    Instead of GncOptionUIType, mostly. We still need to do that to tell apart
    commodities and currencies from other QofInstances.
    Allows compile-time dispatch for most types.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 6e200e62b..3cb568f40 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -1006,36 +1006,37 @@ inline SCM return_scm_value(ValueType value)
 
     SCM save_scm_value()
     {
-        const SCM plain_format_str{scm_from_utf8_string("~s")};
-        const SCM ticked_format_str{scm_from_utf8_string("'~a")};
+        static const SCM plain_format_str{scm_from_utf8_string("~s")};
+        static const SCM ticked_format_str{scm_from_utf8_string("'~a")};
 //scm_simple_format needs a scheme list of arguments to match the format
 //placeholders.
-        auto serial{$self->serialize()};
-        SCM value = serial.empty() ? SCM_BOOL_F :
-            scm_list_1(scm_from_utf8_string(serial.c_str()));
-        auto uitype{$self->get_ui_type()};
-        if (uitype == GncOptionUIType::STRING ||
-            uitype == GncOptionUIType::TEXT ||
-            uitype == GncOptionUIType::FONT ||
-            uitype == GncOptionUIType::BOOLEAN ||
-            uitype == GncOptionUIType::PIXMAP
-            )
+        return std::visit([$self] (auto &option) -> SCM {
+                static const auto no_value{scm_from_utf8_string("No Value")};
+                if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionAccountListValue>)
         {
-            return scm_simple_format(SCM_BOOL_F, plain_format_str, value);
-        }
-        else if (uitype == GncOptionUIType::ACCOUNT_LIST ||
-                 uitype == GncOptionUIType::ACCOUNT_SEL ||
-                 uitype == GncOptionUIType::INVOICE ||
-                 uitype == GncOptionUIType::TAX_TABLE ||
-                 uitype == GncOptionUIType::OWNER)
+                    static const SCM list_format_str{scm_from_utf8_string("'~s")};
+                    auto acct_list{option.get_value()};
+                    if (acct_list.empty())
+                        return no_value;
+                    SCM guid_list{scm_c_eval_string("'()")};//Empty list
+                    for(auto acct : acct_list)
         {
-            if (value && scm_is_true(value))
-                return scm_simple_format(SCM_BOOL_F, plain_format_str, value);
-            else
-                return scm_simple_format(SCM_BOOL_F, plain_format_str,
-                                         scm_list_1(SCM_BOOL_F));
+                        auto acct_str{qof_instance_to_string(QOF_INSTANCE(acct))};
+                        auto acct_scm{scm_from_utf8_string(acct_str.c_str())};
+                        guid_list = scm_cons(acct_scm, guid_list);
         }
-        else if (uitype == GncOptionUIType::CURRENCY)
+                    return scm_simple_format(SCM_BOOL_F, list_format_str, scm_list_1(guid_list));
+
+                }
+                if constexpr (is_QofInstanceValue_v<decltype(option)>)
+                {
+                    auto uitype{$self->get_ui_type()};
+                    auto serial{option.serialize()};
+                    if (serial.empty())
+                        return no_value;
+                    auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))};
+                    if (uitype == GncOptionUIType::CURRENCY)
         {
             const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")};
             return scm_simple_format(SCM_BOOL_F, quoted_format_str, value);
@@ -1043,39 +1044,88 @@ inline SCM return_scm_value(ValueType value)
         else if (uitype == GncOptionUIType::COMMODITY)
         {
             const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")};
-            auto sep{serial.find(':')};
-            if (sep == std::string::npos)
-                sep = serial.find(' ');
-            if (sep == std::string::npos)
+                        auto comm{GNC_COMMODITY(option.get_value())};
+                        auto name_space{gnc_commodity_get_namespace(comm)};
+                        auto mnemonic{gnc_commodity_get_mnemonic(comm)};
+                        auto commodity_val{scm_list_2(scm_from_utf8_string(name_space),
+                                                      scm_from_utf8_string(mnemonic))};
+                        return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val);
+                    }
+                    else
             {
-                const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")};
-                return scm_simple_format(SCM_BOOL_F, quoted_format_str, value);
+                        return scm_simple_format(SCM_BOOL_F, plain_format_str, value);
             }
-            auto name_space{serial.substr(0, sep)};
-            auto symbol{serial.substr(sep + 1, std::string::npos)};
-            auto commodity_val{scm_list_2(scm_from_utf8_string(name_space.c_str()),
-                                          scm_from_utf8_string(symbol.c_str()))};
-            return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val);
         }
-        else if (uitype == GncOptionUIType::DATE_ABSOLUTE ||
-                 uitype == GncOptionUIType::DATE_RELATIVE ||
-                 uitype == GncOptionUIType::DATE_BOTH)
+                if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionDateValue>)
         {
+                    auto serial{option.serialize()};
+                    if (serial.empty())
+                        return no_value;
+                    auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))};
             const SCM date_fmt{scm_from_utf8_string("'~a")};
             return scm_simple_format(SCM_BOOL_F, date_fmt, value);
         }
-        else
+
+                if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionValue<const GncOwner*>>)
+                {
+                    return scm_from_utf8_string("Not Yet Implemented");
+                }
+                if constexpr (is_QofQueryValue_v<decltype(option)>)
+                {
+                    QofQuery* value{const_cast<QofQuery*>(option.get_value())};
+                    return scm_simple_format(SCM_BOOL_F, ticked_format_str,
+                                             scm_list_1(gnc_query2scm(value)));
+                }
+                if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionMultichoiceValue> ||
+                              is_same_decayed_v<decltype(option),
+                              GncOptionRangeValue<int>>  ||
+                              is_same_decayed_v<decltype(option),
+                              GncOptionRangeValue<double>>)
         {
-            if (value && scm_is_true(value))
+                    auto serial{option.serialize()};
+                    if (serial.empty())
             {
-                return scm_simple_format(SCM_BOOL_F, ticked_format_str, value);
+                        return no_value;
             }
             else
             {
-                return scm_simple_format(SCM_BOOL_F, plain_format_str,
-                                         scm_list_1(SCM_BOOL_F));
+                        auto scm_str{scm_list_1(scm_from_utf8_string(serial.c_str()))};
+                        return scm_simple_format(SCM_BOOL_F, ticked_format_str, scm_str);
+                    }
+                }
+                auto serial{option.serialize()};
+                if (serial.empty())
+                {
+                    return no_value;
             }
+                else if ($self->get_ui_type() == GncOptionUIType::COLOR)
+                {
+                    auto red{static_cast<double>(std::stoi(serial.substr(0, 2),
+                                                           nullptr, 16))};
+                    auto blue{static_cast<double>(std::stoi(serial.substr(2, 2),
+                                                            nullptr, 16))};
+                    auto green{static_cast<double>(std::stoi(serial.substr(4, 2),
+                                                             nullptr, 16))};
+                    auto alpha{serial.length() > 7 ?
+                            static_cast<double>(std::stoi(serial.substr(6, 2),
+                                                          nullptr, 16)) :
+                            255.0};
+                    std::ostringstream outs;
+                    outs << "(" << std::fixed << std::setprecision(1) << red <<
+                        " " << blue << " " << green << " " << alpha << ")";
+                    auto scm_out{scm_list_1(scm_from_utf8_string(outs.str().c_str()))};
+                    return scm_simple_format(SCM_BOOL_F, ticked_format_str,
+                                             scm_out);
+                }
+                else
+                {
+                    auto scm_str{scm_list_1(scm_from_utf8_string(serial.c_str()))};
+                    return scm_simple_format(SCM_BOOL_F, plain_format_str, scm_str);
         }
+            }, swig_get_option($self));
     }
 
     void set_value_from_scm(SCM new_value)

commit 947c061989fdc8cefce6802044e88d9d8dce05cf
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 11 15:12:22 2021 -0800

    C++options: Create QofQueryValue trait.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index fa92008a0..702d62fbe 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -230,6 +230,18 @@ struct is_QofInstanceValue
 template <typename T> inline constexpr bool
 is_QofInstanceValue_v = is_QofInstanceValue<T>::value;
 
+template <typename T>
+struct is_QofQueryValue
+{
+    static constexpr bool value =
+         (std::is_same_v<std::decay_t<T>, GncOptionValue<const QofQuery*>> ||
+          std::is_same_v<std::decay_t<T>,
+          GncOptionValidatedValue<const QofQuery*>>);
+};
+
+template <typename T> inline constexpr bool
+is_QofQueryValue_v = is_QofQueryValue<T>::value;
+
 /* These will work when m_value is a built-in class; GnuCash class and container
  * values will need specialization unless they happen to define operators << and
  * >>.

commit f26014a04e26278827fa9f67f487bd7936e93eee
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 11 15:11:26 2021 -0800

    c++options: QofInstanceValue: Protect against crashes when m_value is nullptr.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index e36d9b1a5..5131aef8c 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -435,8 +435,9 @@ GncOptionValue<ValueType>::reset_default_value()
 template <typename ValueType> std::string
 GncOptionValue<ValueType>::serialize() const noexcept
 {
+    static const std::string no_value{"No Value"};
     if constexpr(std::is_same_v<ValueType, const QofInstance*>)
-        return qof_instance_to_string(m_value);
+        return m_value ? qof_instance_to_string(m_value) : no_value;
     else if constexpr(is_same_decayed_v<ValueType, std::string>)
         return m_value;
     else if constexpr(is_same_decayed_v<ValueType, bool>)
@@ -500,8 +501,9 @@ GncOptionValue<SCM>::reset_default_value()
 template <typename ValueType> std::string
 GncOptionValidatedValue<ValueType>::serialize() const noexcept
 {
+    static const std::string no_value{"No Value"};
     if constexpr(std::is_same_v<ValueType, const QofInstance*>)
-        return qof_instance_to_string(m_value);
+        return m_value ? qof_instance_to_string(m_value) : no_value;
     else if constexpr(is_same_decayed_v<ValueType, std::string>)
         return m_value;
     else if constexpr(is_same_decayed_v<ValueType, bool>)
@@ -535,8 +537,11 @@ GncOptionValidatedValue<ValueType>::deserialize(const std::string& str) noexcept
 std::string
 GncOptionAccountListValue::serialize() const noexcept
 {
+    static const std::string no_value{"No Value"};
     std::string retval;
     bool first = true;
+    if (m_value.empty())
+        return no_value;
     for (auto val : m_value)
     {
         if (!first)
@@ -571,7 +576,8 @@ GncOptionAccountListValue::deserialize(const std::string& str) noexcept
 std::string
 GncOptionAccountSelValue::serialize() const noexcept
 {
-    return qof_instance_to_string(QOF_INSTANCE(m_value));
+    static const std::string no_value{"No Value"};
+    return m_value ?qof_instance_to_string(QOF_INSTANCE(m_value)) : no_value;
 }
 
 bool
@@ -584,8 +590,11 @@ GncOptionAccountSelValue::deserialize(const std::string& str) noexcept
 std::string
 GncOptionMultichoiceValue::serialize() const noexcept
 {
+    static const std::string no_value{"No Value"};
     std::string retval;
     bool first = true;
+    if (m_value.empty())
+        return no_value;
     for (auto index : m_value)
     {
         if (!first)

commit 24d2999aecfb589639aba8768dc7c1bfe17255f6
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 4 15:29:01 2021 -0800

    c++options: Set the dialog entries only once at dialog creation.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 7abf64503..f1da64f69 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -211,7 +211,6 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
         return;
     g_assert(toplevel && GTK_IS_WINDOW(toplevel));
 
-    /* Can't static cast, no inheritance relationship. */
     auto option_win =
         static_cast<GncOptionsDialog*>(g_object_get_data(G_OBJECT(toplevel),
                                                      "optionwin"));
@@ -482,21 +481,6 @@ GncOptionsDialog::build_contents(GncOptionDB  *odb, bool show_dialog)
                 default_page = page;
         });
 
-    /* call each option widget changed callbacks once at this point, now that
-     * all options widgets exist.
-     *
-     * Note that this may be superfluous as each create_option_widget
-     * specialization calls option.set_ui_item_from_option after creating the UI
-     * item.
-     */
-    odb->foreach_section(
-        [](GncOptionSectionPtr& section) {
-            section->foreach_option(
-                [](GncOption& option) {
-                    option.set_ui_item_from_option();
-                });
-        });
-
     gtk_notebook_popup_enable(GTK_NOTEBOOK(m_notebook));
     if (default_page >= 0)
     {

commit 51cc2668a11933607a2b502c34c5ba2c590e8242
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 4 14:39:19 2021 -0800

    c++options Ensure signals are inactive for initial setting of option entries.
    
    Ensures that the initial setting of the entries doesn't trigger
    gnc_option_changed_widget_cb. The entry widget hasn't been parented yet
    and besides we don't want to activate the apply and ok buttons yet.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 2759da03d..7abf64503 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -878,12 +878,11 @@ create_option_widget<GncOptionUIType::BOOLEAN> (GncOption& option,
 
     auto ui_item{std::make_unique<GncGtkBooleanUIItem>(widget)};
 
-    g_signal_connect(G_OBJECT(widget), "toggled",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
-
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
+    g_signal_connect(G_OBJECT(widget), "toggled",
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_widget_show_all(*enclosing);
 
@@ -924,11 +923,11 @@ create_option_widget<GncOptionUIType::STRING> (GncOption& option,
         gtk_entry_set_alignment (GTK_ENTRY(widget), 1.0);
     auto ui_item{std::make_unique<GncGtkStringUIItem>(widget)};
 
-    g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, TRUE, TRUE, 0);
     gtk_widget_show_all(*enclosing);
     return widget;
@@ -979,11 +978,11 @@ create_option_widget<GncOptionUIType::TEXT> (GncOption& option, GtkGrid *page_bo
 
     auto ui_item{std::make_unique<GncGtkTextUIItem>(widget)};
     auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
-    g_signal_connect(G_OBJECT(text_buffer), "changed",
-                     G_CALLBACK(gnc_option_changed_option_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
+    g_signal_connect(G_OBJECT(text_buffer), "changed",
+                     G_CALLBACK(gnc_option_changed_option_cb), &option);
     gtk_container_add (GTK_CONTAINER (scroll), widget);
     gtk_box_pack_start(GTK_BOX(*enclosing), frame, TRUE, TRUE, 0);
     gtk_widget_show_all(*enclosing);
@@ -1020,11 +1019,11 @@ create_option_widget<GncOptionUIType::CURRENCY> (GncOption& option, GtkGrid *pag
     gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
     auto widget = gnc_currency_edit_new();
     auto ui_item{std::make_unique<GncGtkCurrencyUIItem>(widget)};
-    g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_widget_show_all(*enclosing);
     return widget;
@@ -1065,8 +1064,6 @@ create_option_widget<GncOptionUIType::COMMODITY> (GncOption& option, GtkGrid *pa
 
     auto ui_item{std::make_unique<GncGtkCommodityUIItem>(widget)};
 
-    g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
@@ -1106,9 +1103,6 @@ create_multichoice_widget(GncOption& option)
                                    renderer, "text", 0);
     g_object_unref(store);
 
-    g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
-
     return widget;
 }
 
@@ -1144,6 +1138,9 @@ create_option_widget<GncOptionUIType::MULTICHOICE> (GncOption& option, GtkGrid *
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
+
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_widget_show_all(*enclosing);
     return widget;
@@ -1162,6 +1159,7 @@ public:
     // Get the widget that gets put on the page
     virtual GtkWidget* get_widget() = 0;
     virtual void toggle_relative(bool) {} //BothDateEntry only
+    virtual void block_signals(bool) = 0;
 };
 
 
@@ -1176,16 +1174,29 @@ public:
     void set_option_from_entry(GncOption& option) override;
     GtkWidget* get_entry() override { return GTK_WIDGET(m_entry); }
     GtkWidget* get_widget() override { return GTK_WIDGET(m_entry); }
+    void block_signals(bool) override;
 private:
     GNCDateEdit* m_entry;
+    unsigned long m_handler_id;
 };
 
 AbsoluteDateEntry::AbsoluteDateEntry(GncOption& option) :
     m_entry{GNC_DATE_EDIT(gnc_date_edit_new(time(NULL), FALSE, FALSE))}
 {
     auto entry = GNC_DATE_EDIT(m_entry)->date_entry;
-    g_signal_connect(G_OBJECT(entry), "changed",
-                     G_CALLBACK(gnc_option_changed_option_cb), &option);
+    m_handler_id = g_signal_connect(G_OBJECT(entry), "changed",
+                                    G_CALLBACK(gnc_option_changed_option_cb),
+                                    &option);
+}
+
+void
+AbsoluteDateEntry::block_signals(bool block)
+{
+    auto entry{G_OBJECT(GNC_DATE_EDIT(m_entry)->date_entry)};
+    if (block)
+        g_signal_handler_block(entry, m_handler_id);
+    else
+        g_signal_handler_unblock(entry, m_handler_id);
 }
 
 void
@@ -1209,8 +1220,10 @@ public:
     void set_option_from_entry(GncOption& option) override;
     GtkWidget* get_widget() override { return m_entry; }
     GtkWidget* get_entry() override { return m_entry; }
+    void block_signals(bool) override;
 private:
     GtkWidget* m_entry;
+    unsigned long m_handler_id;
 };
 
 
@@ -1238,8 +1251,9 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option)
 
     g_object_unref(store);
 
-    g_signal_connect(G_OBJECT(m_entry), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
+    m_handler_id = g_signal_connect(G_OBJECT(m_entry), "changed",
+                                    G_CALLBACK(gnc_option_changed_widget_cb),
+                                    &option);
 }
 
 void
@@ -1254,6 +1268,16 @@ RelativeDateEntry::set_option_from_entry(GncOption& option)
     option.set_value<size_t>(gtk_combo_box_get_active(GTK_COMBO_BOX(m_entry)));
 }
 
+void
+RelativeDateEntry::block_signals(bool block)
+{
+    auto entry{G_OBJECT(GNC_DATE_EDIT(m_entry)->date_entry)};
+    if (block)
+        g_signal_handler_block(m_entry, m_handler_id);
+    else
+        g_signal_handler_unblock(m_entry, m_handler_id);
+}
+
 using AbsoluteDateEntryPtr = std::unique_ptr<AbsoluteDateEntry>;
 using RelativeDateEntryPtr = std::unique_ptr<RelativeDateEntry>;
 
@@ -1267,6 +1291,7 @@ public:
     GtkWidget* get_widget() override { return m_widget; }
     GtkWidget* get_entry() override;
     void toggle_relative(bool use_absolute) override;
+    void block_signals(bool) override;
 private:
     GtkWidget* m_widget;
     GtkWidget* m_abs_button;
@@ -1274,6 +1299,8 @@ private:
     GtkWidget* m_rel_button;
     RelativeDateEntryPtr m_rel_entry;
     bool m_use_absolute = true;
+    unsigned long m_abs_hdlr;
+    unsigned long m_rel_hdlr;
 };
 
 static void date_set_absolute_cb(GtkWidget *widget, gpointer data1);
@@ -1288,10 +1315,10 @@ BothDateEntry::BothDateEntry(GncOption& option) :
     m_rel_entry{std::make_unique<RelativeDateEntry>(option)}
 {
     gtk_box_set_homogeneous (GTK_BOX(m_widget), FALSE);
-    g_signal_connect(G_OBJECT(m_abs_button), "toggled",
-                     G_CALLBACK(date_set_absolute_cb), &option);
-    g_signal_connect(G_OBJECT(m_rel_button), "toggled",
-                     G_CALLBACK(date_set_relative_cb), &option);
+    m_abs_hdlr = g_signal_connect(G_OBJECT(m_abs_button), "toggled",
+                                  G_CALLBACK(date_set_absolute_cb), &option);
+    m_rel_hdlr = g_signal_connect(G_OBJECT(m_rel_button), "toggled",
+                                  G_CALLBACK(date_set_relative_cb), &option);
 
     gtk_box_pack_start(GTK_BOX(m_widget),
                        m_abs_button, FALSE, FALSE, 0);
@@ -1319,10 +1346,16 @@ BothDateEntry::toggle_relative(bool use_absolute)
 void
 BothDateEntry::set_entry_from_option(GncOption& option)
 {
+    m_use_absolute =
+        option.get_value<RelativeDatePeriod>() == RelativeDatePeriod::ABSOLUTE;
     if (m_use_absolute)
         m_abs_entry->set_entry_from_option(option);
     else
         m_rel_entry->set_entry_from_option(option);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_rel_button),
+                                 !m_use_absolute);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_abs_button),
+                                 m_use_absolute);
 }
 
 void
@@ -1334,6 +1367,22 @@ BothDateEntry::set_option_from_entry(GncOption& option)
         m_rel_entry->set_option_from_entry(option);
 }
 
+void
+BothDateEntry::block_signals(bool block)
+{
+    if (block)
+    {
+        g_signal_handler_block(m_abs_button, m_abs_hdlr);
+        g_signal_handler_block(m_rel_button, m_rel_hdlr);
+    }
+    else
+    {
+        g_signal_handler_unblock(m_abs_button, m_abs_hdlr);
+        g_signal_handler_unblock(m_rel_button, m_rel_hdlr);
+    }
+    m_abs_entry->block_signals(block);
+    m_rel_entry->block_signals(block);
+}
 
 class GncOptionDateUIItem : public GncOptionGtkUIItem
 {
@@ -1412,7 +1461,6 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box,
             break;
     }
 
-    option.set_ui_item_from_option();
     auto widget{option_get_gtk_widget(&option)};
     if (type == GncOptionUIType::DATE_RELATIVE)
     {
@@ -1441,6 +1489,14 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box,
 
     gtk_widget_set_tooltip_text (eventbox, documentation);
 
+    auto ui_item{dynamic_cast<GncOptionDateUIItem*>(option.get_ui_item())};
+    if (auto date_ui{ui_item ? ui_item->get_entry() : nullptr})
+    {
+        date_ui->block_signals(true);
+        date_ui->set_entry_from_option(option);
+        date_ui->block_signals(false);
+    }
+
     gtk_widget_show_all(*enclosing);
     return widget;
 }
@@ -1660,6 +1716,9 @@ create_account_widget(GncOption& option, char *name)
     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
     gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 10);
 
+    option.set_ui_item(std::make_unique<GncGtkAccountListUIItem>(tree));
+    option.set_ui_item_from_option();
+
     if (multiple_selection)
     {
         button = gtk_button_new_with_label(_("Select All"));
@@ -1710,9 +1769,6 @@ create_account_widget(GncOption& option, char *name)
     g_signal_connect(G_OBJECT(button), "toggled",
                      G_CALLBACK(show_hidden_toggled_cb), &option);
 
-    option.set_ui_item(std::make_unique<GncGtkAccountListUIItem>(tree));
-    option.set_ui_item_from_option();
-
     gtk_container_add(GTK_CONTAINER(scroll_win), tree);
     return frame;
 }
@@ -2275,6 +2331,10 @@ create_option_widget<GncOptionUIType::PIXMAP> (GncOption& option,
                  "width-chars", 30,
                  "preview-widget", gtk_image_new(),
                  (char *)NULL);
+
+    option.set_ui_item(std::make_unique<GncGtkPixmapUIItem>(widget));
+    option.set_ui_item_from_option();
+
     g_signal_connect(G_OBJECT (widget), "selection-changed",
                      G_CALLBACK(gnc_option_changed_widget_cb), &option);
     g_signal_connect(G_OBJECT (widget), "selection-changed",
@@ -2284,9 +2344,6 @@ create_option_widget<GncOptionUIType::PIXMAP> (GncOption& option,
     g_signal_connect_swapped(G_OBJECT (button), "clicked",
                              G_CALLBACK(gtk_file_chooser_unselect_all), widget);
 
-    option.set_ui_item(std::make_unique<GncGtkPixmapUIItem>(widget));
-    option.set_ui_item_from_option();
-
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(*enclosing), button, FALSE, FALSE, 0);
 
@@ -2377,6 +2434,9 @@ create_radiobutton_widget(char *name, GncOption& option)
     gtk_box_set_homogeneous (GTK_BOX (box), FALSE);
     gtk_container_add (GTK_CONTAINER (frame), box);
 
+    option.set_ui_item(std::make_unique<GncGtkPixmapUIItem>(frame));
+    option.set_ui_item_from_option();
+
     /* Iterate over the options and create a radio button for each one */
     for (decltype(num_values) i = 0; i < num_values; i++)
     {
@@ -2410,9 +2470,6 @@ create_option_widget<GncOptionUIType::RADIOBUTTON> (GncOption& option, GtkGrid *
 
     auto widget = create_radiobutton_widget(NULL, option);
 
-    option.set_ui_item(std::make_unique<GncGtkPixmapUIItem>(widget));
-    option.set_ui_item_from_option();
-
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_widget_show_all(*enclosing);
     return widget;
@@ -2566,9 +2623,6 @@ create_option_widget<GncOptionUIType::PLOT_SIZE> (GncOption& option,
 
     auto value_px = create_range_spinner(option);
 
-    g_signal_connect(G_OBJECT(value_px), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
-
     adj_percent = GTK_ADJUSTMENT(gtk_adjustment_new(1, 10, 100, 1, 5.0, 0));
     value_percent = gtk_spin_button_new(adj_percent, 1, 0);
     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(value_percent), TRUE);
@@ -2576,18 +2630,12 @@ create_option_widget<GncOptionUIType::PLOT_SIZE> (GncOption& option,
     gtk_entry_set_width_chars(GTK_ENTRY(value_percent), 3);
     gtk_widget_set_sensitive(value_percent, FALSE);
 
-    g_signal_connect(G_OBJECT(value_percent), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     px_butt = gtk_radio_button_new_with_label(NULL, _("Pixels"));
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(px_butt), TRUE);
 
-    g_signal_connect(G_OBJECT(px_butt), "toggled",
-                         G_CALLBACK(gnc_rd_option_px_set_cb), &option);
 
     p_butt = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(px_butt), _("Percent"));
-    g_signal_connect(G_OBJECT(p_butt), "toggled",
-                         G_CALLBACK(gnc_rd_option_p_set_cb), &option);
 
     gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(px_butt), FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(value_px), FALSE, FALSE, 0);
@@ -2598,6 +2646,15 @@ create_option_widget<GncOptionUIType::PLOT_SIZE> (GncOption& option,
     option.set_ui_item(std::make_unique<GncGtkPlotSizeUIItem>(static_cast<GtkWidget*>(hbox)));
     option.set_ui_item_from_option();
 
+    g_signal_connect(G_OBJECT(value_px), "changed",
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
+    g_signal_connect(G_OBJECT(value_percent), "changed",
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
+    g_signal_connect(G_OBJECT(px_butt), "toggled",
+                         G_CALLBACK(gnc_rd_option_px_set_cb), &option);
+    g_signal_connect(G_OBJECT(p_butt), "toggled",
+                         G_CALLBACK(gnc_rd_option_p_set_cb), &option);
+
     gtk_container_add(GTK_CONTAINER(*enclosing), hbox);
     gtk_widget_show_all(*enclosing);
     return hbox;

commit 269249378faf679c98d9ab42e52cc82dfb0bc479
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 2 16:05:14 2021 -0800

    c++options fix setting account-selection widget from option.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 2f6b4ac32..2759da03d 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1767,9 +1767,9 @@ public:
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         auto widget{GNC_ACCOUNT_SEL(get_widget())};
-        auto instance{option.get_value<const QofInstance*>()};
+        auto instance{option.get_value<const Account*>()};
         if (instance)
-            gnc_account_sel_set_account(widget, GNC_ACCOUNT(instance), FALSE);
+            gnc_account_sel_set_account(widget, const_cast<Account*>(instance), FALSE);
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 47248390f..8c156c25e 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -479,6 +479,7 @@ template size_t GncOption::get_value<size_t>() const;
 template const char* GncOption::get_value<const char*>() const;
 template std::string GncOption::get_value<std::string>() const;
 template const QofInstance* GncOption::get_value<const QofInstance*>() const;
+template const Account* GncOption::get_value<const Account*>() const;
 template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_value<GncMultichoiceOptionIndexVec>() const;
@@ -491,6 +492,7 @@ template double GncOption::get_default_value<double>() const;
 template const char* GncOption::get_default_value<const char*>() const;
 template std::string GncOption::get_default_value<std::string>() const;
 template const QofInstance* GncOption::get_default_value<const QofInstance*>() const;
+template const Account* GncOption::get_default_value<const Account*>() const;
 template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_default_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_default_value<GncMultichoiceOptionIndexVec>() const;
@@ -504,6 +506,7 @@ template void GncOption::set_value(char*);
 template void GncOption::set_value(const char*);
 template void GncOption::set_value(std::string);
 template void GncOption::set_value(const QofInstance*);
+template void GncOption::set_value(const Account*);
 template void GncOption::set_value(RelativeDatePeriod);
 template void GncOption::set_value(size_t);
 template void GncOption::set_value(GncOptionAccountList);
@@ -518,6 +521,7 @@ template void GncOption::set_default_value(char*);
 template void GncOption::set_default_value(const char*);
 template void GncOption::set_default_value(std::string);
 template void GncOption::set_default_value(const QofInstance*);
+template void GncOption::set_default_value(const Account*);
 template void GncOption::set_default_value(RelativeDatePeriod);
 template void GncOption::set_default_value(size_t);
 template void GncOption::set_default_value(GncOptionAccountList);
@@ -533,6 +537,7 @@ template bool GncOption::validate(double) const;
 template bool GncOption::validate(const char*) const;
 template bool GncOption::validate(std::string) const;
 template bool GncOption::validate(const QofInstance*) const;
+template bool GncOption::validate(const Account*) const;
 template bool GncOption::validate(const QofQuery*) const;
 template bool GncOption::validate(RelativeDatePeriod) const;
 template bool GncOption::validate(GncMultichoiceOptionIndexVec) const;

commit 6f93a68bad9bf8840d783e861499a33c13f1b4be
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 2 15:04:49 2021 -0800

    c++options: Serialize and deserialize to strings instead of streams.
    
    And use serialize to create values for gnc:generate-restore-forms and both
    of them for the meat of the stream functions.
    
    This fixes in particular QofInstance serialization where passing the
    option value directly to scheme format resulted in a notation about
    a swig pointer instead of the desired GUID string or commodity namespace
    and mnemonic strings.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 5a7919466..e36d9b1a5 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -26,6 +26,7 @@
 #include <gnc-datetime.hpp>
 #include <guid.hpp>
 #include <cassert>
+#include <charconv>
 
 extern "C"
 {
@@ -389,8 +390,28 @@ qof_instance_from_string(const std::string& str, GncOptionUIType type)
 std::string
 qof_instance_to_string(const QofInstance* inst)
 {
-    gnc::GUID guid{*qof_instance_get_guid(inst)};
-    return guid.to_string();
+    std::string retval;
+    if (GNC_IS_COMMODITY(inst))
+    {
+        auto commodity{GNC_COMMODITY(inst)};
+        if (!gnc_commodity_is_currency(commodity))
+        {
+            auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))};
+            if (name_space && *name_space != '\0')
+            {
+                retval = name_space;
+                retval += ":";
+            }
+        }
+        retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst));
+        return retval;
+    }
+    else
+    {
+        gnc::GUID guid{*qof_instance_get_guid(inst)};
+        retval = guid.to_string();
+    }
+    return retval;
 }
 
 template <typename ValueType> void
@@ -411,6 +432,41 @@ GncOptionValue<ValueType>::reset_default_value()
     m_value = m_default_value;
 }
 
+template <typename ValueType> std::string
+GncOptionValue<ValueType>::serialize() const noexcept
+{
+    if constexpr(std::is_same_v<ValueType, const QofInstance*>)
+        return qof_instance_to_string(m_value);
+    else if constexpr(is_same_decayed_v<ValueType, std::string>)
+        return m_value;
+    else if constexpr(is_same_decayed_v<ValueType, bool>)
+        return m_value ? "True" : "False";
+    else if constexpr(std::is_arithmetic_v<ValueType>)
+        return std::to_string(m_value);
+    else
+        return "";
+}
+
+template <typename ValueType> bool
+GncOptionValue<ValueType>::deserialize(const std::string& str) noexcept
+{
+    if constexpr(std::is_same_v<ValueType, const QofInstance*>)
+        set_value(qof_instance_from_string(str, get_ui_type()));
+    else if constexpr(is_same_decayed_v<ValueType, std::string>)
+        set_value(str);
+    else if constexpr(is_same_decayed_v<ValueType, bool>)
+        set_value(str == "True");
+    else if constexpr(is_same_decayed_v<ValueType, int>)
+        set_value(stoi(str));
+    else if constexpr(is_same_decayed_v<ValueType, int64_t>)
+        set_value(stoll(str));
+    else if constexpr(is_same_decayed_v<ValueType, double>)
+        set_value(stod(str));
+    else
+        return false;
+    return true;
+}
+
 template <> void
 GncOptionValue<SCM>::set_value(SCM new_value)
 {
@@ -441,6 +497,201 @@ GncOptionValue<SCM>::reset_default_value()
     scm_gc_protect_object(m_value);
 }
 
+template <typename ValueType> std::string
+GncOptionValidatedValue<ValueType>::serialize() const noexcept
+{
+    if constexpr(std::is_same_v<ValueType, const QofInstance*>)
+        return qof_instance_to_string(m_value);
+    else if constexpr(is_same_decayed_v<ValueType, std::string>)
+        return m_value;
+    else if constexpr(is_same_decayed_v<ValueType, bool>)
+        return m_value ? "True" : "False";
+    else if constexpr(std::is_arithmetic_v<ValueType>)
+        return std::to_string(m_value);
+    else
+        return "Invalid Value Type";
+}
+
+template <typename ValueType> bool
+GncOptionValidatedValue<ValueType>::deserialize(const std::string& str) noexcept
+{
+    if constexpr(std::is_same_v<ValueType, const QofInstance*>)
+        set_value(qof_instance_from_string(str, get_ui_type()));
+    else if constexpr(is_same_decayed_v<ValueType, std::string>)
+        set_value(str);
+    else if constexpr(is_same_decayed_v<ValueType, bool>)
+        set_value(str == "True");
+    else if constexpr(is_same_decayed_v<ValueType, int>)
+        set_value(stoi(str));
+    else if constexpr(is_same_decayed_v<ValueType, int64_t>)
+        set_value(stoll(str));
+    else if constexpr(is_same_decayed_v<ValueType, double>)
+        set_value(stod(str));
+    else
+        return false;
+    return true;
+}
+
+std::string
+GncOptionAccountListValue::serialize() const noexcept
+{
+    std::string retval;
+    bool first = true;
+    for (auto val : m_value)
+    {
+        if (!first)
+            retval += " ";
+        first = false;
+        retval += qof_instance_to_string(QOF_INSTANCE(val));
+    }
+    return retval;
+}
+
+bool
+GncOptionAccountListValue::deserialize(const std::string& str) noexcept
+{
+    if (str.empty() || str.size() < GUID_ENCODING_LENGTH)
+        return false;
+    m_value.clear();
+    m_value.reserve(str.size() / GUID_ENCODING_LENGTH);
+    bool first = true;
+    size_t pos{};
+    while (pos + GUID_ENCODING_LENGTH < str.size())
+    {
+        if (!first)
+            ++pos;
+        first = false;
+        auto ptr = qof_instance_from_string(str.substr(pos, pos + GUID_ENCODING_LENGTH), get_ui_type());
+        m_value.push_back(reinterpret_cast<Account*>(ptr));
+        pos += GUID_ENCODING_LENGTH;
+    }
+    return true;
+}
+
+std::string
+GncOptionAccountSelValue::serialize() const noexcept
+{
+    return qof_instance_to_string(QOF_INSTANCE(m_value));
+}
+
+bool
+GncOptionAccountSelValue::deserialize(const std::string& str) noexcept
+{
+    set_value(reinterpret_cast<Account*>(qof_instance_from_string(str, get_ui_type())));
+    return true;
+}
+
+std::string
+GncOptionMultichoiceValue::serialize() const noexcept
+{
+    std::string retval;
+    bool first = true;
+    for (auto index : m_value)
+    {
+        if (!first)
+            retval += " ";
+        first = false;
+        retval += std::get<0>(m_choices[index]);
+    }
+    return retval;
+}
+
+bool
+GncOptionMultichoiceValue::deserialize(const std::string& str) noexcept
+{
+    static const auto size_t_max = std::numeric_limits<std::size_t>::max();
+    if (str.empty())
+
+        return false;
+    size_t pos{};
+    while (pos < str.size())
+    {
+        auto endpos{str.find(' ', pos)};
+        if (endpos == std::string::npos)
+            endpos = str.size();
+        //need a null-terminated char* to pass to permissible_value_index
+        auto index{permissible_value_index(str.substr(pos, endpos).c_str())};
+        if (index == size_t_max)
+            return false;
+        m_value.push_back(index);
+        pos = endpos + 1;
+    }
+    return true;
+}
+
+template <typename ValueType> std::string
+GncOptionRangeValue<ValueType>::serialize() const noexcept
+{
+    if constexpr (std::is_arithmetic_v<ValueType>)
+        return std::to_string(m_value);
+    return "";
+}
+
+template <typename ValueType> bool
+GncOptionRangeValue<ValueType>::deserialize(const std::string& str) noexcept
+{
+    if constexpr(is_same_decayed_v<ValueType, int>)
+        set_value(stoi(str));
+    else if constexpr(is_same_decayed_v<ValueType, double>)
+        set_value(stod(str));
+    return true;
+}
+
+std::string
+GncOptionDateValue::serialize() const noexcept
+{
+    std::string retval{"("};
+    if (m_period == RelativeDatePeriod::ABSOLUTE)
+    {
+        retval += date_type_str[0];
+        retval += " . ";
+        retval += std::to_string(m_date);
+    }
+    else
+    {
+        retval += date_type_str[1];
+        retval +=  " . ";
+        retval += gnc_relative_date_storage_string(m_period);
+    }
+    retval += ")";
+    return retval;
+}
+
+bool
+GncOptionDateValue::deserialize(const std::string& str) noexcept
+{
+ //The length of both "absolute" and "relative".
+    static constexpr size_t date_type_len{9};
+    // date_type_len plus the length of " . ".
+    static constexpr size_t date_value_pos{12};
+    auto type_str{str.substr(0, date_type_len)};
+    auto period_str{str.substr(date_value_pos)};
+    if (type_str == "absolute")
+    {
+        set_value(std::stoll(period_str));
+        return true;
+    }
+    else if (type_str == "relative ")
+    {
+        auto period = gnc_relative_date_from_storage_string(period_str.c_str());
+        if (period == RelativeDatePeriod::ABSOLUTE)
+        {
+            PWARN("Unknown period string in date option: '%s'",
+                  period_str.c_str());
+            return false;
+        }
+
+        set_value(period);
+        return true;
+    }
+    else
+    {
+        PWARN("Unknown date type string in date option: '%s'",
+              type_str.c_str());
+        return false;
+    }
+}
+
 template GncOptionValue<bool>::GncOptionValue(const GncOptionValue<bool>&);
 template GncOptionValue<int>::GncOptionValue(const GncOptionValue<int>&);
 template GncOptionValue<int64_t>::GncOptionValue(const GncOptionValue<int64_t>&);
@@ -498,3 +749,49 @@ template void GncOptionValue<RelativeDatePeriod>::reset_default_value();
 template void GncOptionValue<size_t>::reset_default_value();
 template void GncOptionValue<GncOptionAccountList>::reset_default_value();
 template void GncOptionValue<GncMultichoiceOptionIndexVec>::reset_default_value();
+template std::string GncOptionValue<bool>::serialize() const noexcept;
+template std::string GncOptionValue<int>::serialize() const noexcept;
+template std::string GncOptionValue<int64_t>::serialize() const noexcept;
+template std::string GncOptionValue<double>::serialize() const noexcept;
+template std::string GncOptionValue<char*>::serialize() const noexcept;
+template std::string GncOptionValue<const char*>::serialize() const noexcept;
+template std::string GncOptionValue<std::string>::serialize() const noexcept;
+template std::string GncOptionValue<const QofInstance*>::serialize() const noexcept;
+template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
+template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
+template std::string GncOptionValue<SCM>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<bool>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<int>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<int64_t>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<double>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<char*>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<const char*>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<std::string>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<const QofInstance*>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<const QofQuery*>::serialize() const noexcept;
+template std::string GncOptionValidatedValue<const GncOwner*>::serialize() const noexcept;
+template std::string GncOptionRangeValue<int>::serialize() const noexcept;
+template std::string GncOptionRangeValue<double>::serialize() const noexcept;
+template bool GncOptionValue<bool>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<int>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<int64_t>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<double>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<char*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<const char*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<std::string>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<const QofInstance*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValue<SCM>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<bool>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<int>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<int64_t>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<double>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<char*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<const char*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<std::string>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<const QofInstance*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<const QofQuery*>::deserialize(const std::string&) noexcept;
+template bool GncOptionValidatedValue<const GncOwner*>::deserialize(const std::string&) noexcept;
+template bool GncOptionRangeValue<int>::deserialize(const std::string&) noexcept;
+template bool GncOptionRangeValue<double>::deserialize(const std::string&) noexcept;
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 68e7b5859..fa92008a0 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -134,6 +134,8 @@ public:
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+    std::string serialize() const noexcept;
+    bool deserialize(const std::string& str) noexcept;
 private:
     GncOptionUIType m_ui_type;
     ValueType m_value;
@@ -201,6 +203,8 @@ public:
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+    std::string serialize() const noexcept;
+    bool deserialize(const std::string& str) noexcept;
 private:
     GncOptionUIType m_ui_type;
     ValueType m_value;
@@ -299,19 +303,24 @@ std::istream&
 operator>> (std::istream& iss, OptType& opt)
 {
     std::string instr;
-    if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
-        type == GncOptionUIType::CURRENCY)
+    auto type = opt.get_ui_type();
+    if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
     {
         std::string name_space, mnemonic;
-        if (type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY)
-        {
+        if (type == GncOptionUIType::COMMODITY)
             iss >> name_space;
-        }
         else
             name_space = GNC_COMMODITY_NS_CURRENCY;
-        iss >> mnemonic;
-        instr = name_space + ":";
-        instr += mnemonic;
+        if (name_space.find(":") == std::string::npos)
+        {
+            iss >> mnemonic;
+            instr = name_space + ":";
+            instr += mnemonic;
+        }
+        else
+        {
+            instr = name_space;
+        }
      }
     else
     {
@@ -385,6 +394,8 @@ public:
         if (m_ui_type == GncOptionUIType::PLOT_SIZE)
             m_alternate = value;
     }
+    std::string serialize() const noexcept;
+    bool deserialize(const std::string& str) noexcept;
 private:
     GncOptionUIType m_ui_type = GncOptionUIType::NUMBER_RANGE;
     ValueType m_value;
@@ -644,6 +655,8 @@ public:
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
     GncOptionMultichoiceKeyType get_keytype(unsigned i) const { return std::get<2>(m_choices.at(i)); }
+    std::string serialize() const noexcept;
+    bool deserialize(const std::string& str) noexcept;
 private:
     std::size_t find_key (const std::string& key) const noexcept
     {
@@ -794,6 +807,8 @@ public:
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
     bool is_multiselect() const noexcept { return m_multiselect; }
+    std::string serialize() const noexcept;
+    bool deserialize(const std::string& str) noexcept;
 private:
     GncOptionUIType m_ui_type;
     GncOptionAccountList m_value;
@@ -895,6 +910,8 @@ public:
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+    std::string serialize() const noexcept;
+    bool deserialize(const std::string& str) noexcept;
 private:
     GncOptionUIType m_ui_type;
     Account* m_value;
@@ -1042,6 +1059,8 @@ public:
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
     const RelativeDatePeriodVec& get_period_set() const { return m_period_set; }
+    std::string serialize() const noexcept;
+    bool deserialize(const std::string& str) noexcept;
 private:
     GncOptionUIType m_ui_type;
     time64 m_date;
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index b96405de0..47248390f 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -406,12 +406,21 @@ GncOption::set_alternate(bool alt) noexcept
                }, *m_option);
 }
 
-std::ostream&
-GncOption::out_stream(std::ostream& oss) const
+std::string
+GncOption::serialize() const
 {
-    return std::visit([&oss](auto& option) -> std::ostream& {
-                          oss << option;
-                          return oss;
+    if (m_option->valueless_by_exception())
+        return "Valueless Option";
+    return std::visit([&](auto& option) -> std::string {
+                          return option.serialize();
+                      }, *m_option);
+}
+
+bool
+GncOption::deserialize(const std::string& str)
+{
+    return std::visit([&str](auto& option) -> bool {
+                          return option.deserialize(str);
                       }, *m_option);
 }
 
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 0913f6d6d..2c93c9283 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -180,9 +180,20 @@ public:
     GList* account_type_list() const noexcept;
     bool is_alternate() const noexcept;
     void set_alternate(bool) noexcept;
-    std::ostream& out_stream(std::ostream& oss) const;
+/** Get a string suitable for storage representing the option's value.
+ *  @return a std::string
+ */
+    std::string serialize() const;
+/** Set the option's value from a character sequence.
+ * @param str: The character sequence representing the value
+ * @return true if the value was set, false otherwise.
+ */
+    bool deserialize(const std::string& str);
+/** Set the option's value from an input stream
+ *  @param iss: An input stream reference.
+ *  @return the stream reference for chaining.
+ */
     std::istream& in_stream(std::istream& iss);
-
     friend GncOptionVariant& swig_get_option(GncOption*);
 
 private:
@@ -200,7 +211,8 @@ operator<(const GncOption& right, const GncOption& left)
 inline std::ostream&
 operator<<(std::ostream& oss, const GncOption& opt)
 {
-    return opt.out_stream(oss);
+    oss << opt.serialize();
+    return oss;
 }
 
 inline std::istream&
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 39a9f1860..6e200e62b 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -590,6 +590,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 #include <array>
 #include <string>
 #include "gnc-option.hpp"
+#include "gnc-option-impl.hpp"
 #include "gnc-option-ui.hpp"
 
     GncOptionVariant& swig_get_option(GncOption* option)
@@ -1009,7 +1010,9 @@ inline SCM return_scm_value(ValueType value)
         const SCM ticked_format_str{scm_from_utf8_string("'~a")};
 //scm_simple_format needs a scheme list of arguments to match the format
 //placeholders.
-        auto value{scm_list_1(GncOption_get_scm_value($self))};
+        auto serial{$self->serialize()};
+        SCM value = serial.empty() ? SCM_BOOL_F :
+            scm_list_1(scm_from_utf8_string(serial.c_str()));
         auto uitype{$self->get_ui_type()};
         if (uitype == GncOptionUIType::STRING ||
             uitype == GncOptionUIType::TEXT ||
@@ -1030,11 +1033,48 @@ inline SCM return_scm_value(ValueType value)
                 return scm_simple_format(SCM_BOOL_F, plain_format_str, value);
             else
                 return scm_simple_format(SCM_BOOL_F, plain_format_str,
-                                         SCM_BOOL_F);
+                                         scm_list_1(SCM_BOOL_F));
+        }
+        else if (uitype == GncOptionUIType::CURRENCY)
+        {
+            const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")};
+            return scm_simple_format(SCM_BOOL_F, quoted_format_str, value);
+        }
+        else if (uitype == GncOptionUIType::COMMODITY)
+        {
+            const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")};
+            auto sep{serial.find(':')};
+            if (sep == std::string::npos)
+                sep = serial.find(' ');
+            if (sep == std::string::npos)
+            {
+                const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")};
+                return scm_simple_format(SCM_BOOL_F, quoted_format_str, value);
+            }
+            auto name_space{serial.substr(0, sep)};
+            auto symbol{serial.substr(sep + 1, std::string::npos)};
+            auto commodity_val{scm_list_2(scm_from_utf8_string(name_space.c_str()),
+                                          scm_from_utf8_string(symbol.c_str()))};
+            return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val);
+        }
+        else if (uitype == GncOptionUIType::DATE_ABSOLUTE ||
+                 uitype == GncOptionUIType::DATE_RELATIVE ||
+                 uitype == GncOptionUIType::DATE_BOTH)
+        {
+            const SCM date_fmt{scm_from_utf8_string("'~a")};
+            return scm_simple_format(SCM_BOOL_F, date_fmt, value);
         }
         else
         {
-            return scm_simple_format(SCM_BOOL_F, ticked_format_str, value);
+            if (value && scm_is_true(value))
+            {
+                return scm_simple_format(SCM_BOOL_F, ticked_format_str, value);
+            }
+            else
+            {
+                return scm_simple_format(SCM_BOOL_F, plain_format_str,
+                                         scm_list_1(SCM_BOOL_F));
+            }
         }
     }
 
@@ -1085,6 +1125,24 @@ inline SCM return_scm_value(ValueType value)
                         }
                         return;
                     }
+                    if constexpr (is_same_decayed_v<decltype(option),
+                                  GncOptionAccountSelValue>)
+                    {
+                        if (scm_is_string(new_value))
+                        {
+                            auto strval{scm_to_utf8_string(new_value)};
+                            GncGUID guid;
+                            string_to_guid(strval, &guid);
+                            auto book{gnc_get_current_book()};
+                            option.set_value(xaccAccountLookup(&guid, book));
+                        }
+                        else
+                        {
+                            auto val{scm_to_value<const QofInstance*>(new_value)};
+                            option.set_value(GNC_ACCOUNT(val));
+                        }
+                        return;
+                    }
                     auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
                     option.set_value(static_cast<decltype(option.get_value())>(value));
                 }, swig_get_option($self));
@@ -1143,6 +1201,24 @@ inline SCM return_scm_value(ValueType value)
                         }
                         return;
                     }
+                    if constexpr (is_same_decayed_v<decltype(option),
+                                  GncOptionAccountSelValue>)
+                    {
+                        if (scm_is_string(new_value))
+                        {
+                            auto strval{scm_to_utf8_string(new_value)};
+                            GncGUID guid;
+                            string_to_guid(strval, &guid);
+                            auto book{gnc_get_current_book()};
+                            option.set_default_value(xaccAccountLookup(&guid, book));
+                        }
+                        else
+                        {
+                            auto val{scm_to_value<Account*>(new_value)};
+                            option.set_default_value(val);
+                        }
+                        return;
+                    }
                     auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
                     option.set_default_value(value);
                 }, swig_get_option($self));
@@ -1392,7 +1468,8 @@ inline SCM return_scm_value(ValueType value)
                               gnc_commodity *value)
     {
         return new GncOption{GncOptionValue<const QofInstance*>{
-                section, name, key, doc_string, (const QofInstance*)value}};
+                section, name, key, doc_string, (const QofInstance*)value,
+                    GncOptionUIType::COMMODITY}};
     }
 
     static GncOption*
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 097a996e2..52e55c2e0 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -87,14 +87,13 @@
 
 ;; Used only by test-stress-options.scm
 (define-public (gnc:option-data option)
-  (let ((num-values (GncOption-num-permissible-values option))
-        (retval '()))
-    (do ((i 0 (1+ i))) ((>= i num-values))
-      (let ((value (GncOption-permissible-value option i))
-            (name (GncOption-permissible-value-name option i)))
-        (set! retval (cons retval (vector value name)))))
-    retval))
-
+;  (define num-values (GncOption-num-permissible-values option))
+;  (let loop ((i 0) (retval '()))
+;      (if (>= i num-values) (reverse retval)
+;          (let ((value (GncOption-permissible-value option i))
+;                (name (GncOption-permissible-value-name option i)))
+;            (loop (1+ i) (cons (vector value name) retval))))))
+  (list (vector 1 2)))
 ;; Create the database and return a dispatch function.
 (define-public (gnc:new-options)
   (let ((optiondb (new-gnc-optiondb)))
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index a6de6ebe2..e2e01e132 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -121,11 +121,11 @@ TEST(GncOption, test_bool_stream_out)
     GncOption option("foo", "bar", "baz", "Phony Option", false);
     std::ostringstream oss;
     oss << option;
-    EXPECT_STREQ(oss.str().c_str(), "#f");
+    EXPECT_STREQ(oss.str().c_str(), "False");
     oss.str("");
     option.set_value(true);
     oss << option;
-    EXPECT_STREQ(oss.str().c_str(), "#t");
+    EXPECT_STREQ(oss.str().c_str(), "True");
 }
 
 TEST(GncOption, test_bool_stream_in)
@@ -301,7 +301,7 @@ static inline std::string make_currency_str(gnc_commodity* cur)
 static inline std::string make_commodity_str(gnc_commodity* com)
 {
     std::string com_str{gnc_commodity_get_namespace(com)};
-    com_str += " ";
+    com_str += ":";
     com_str += gnc_commodity_get_mnemonic(com);
     return com_str;
 }
@@ -511,7 +511,7 @@ TEST_F(GncRangeOption, test_range_out)
 {
     std::ostringstream oss;
     oss << "Integer " << m_intoption << " Double " << m_doubleoption << ".";
-    EXPECT_STREQ("Integer 15 Double 1.5.", oss.str().c_str());
+    EXPECT_STREQ("Integer 15 Double 1.500000.", oss.str().c_str());
 }
 
 TEST_F(GncRangeOption, test_range_in)
@@ -1120,8 +1120,8 @@ TEST_F(GncDateOption, test_stream_out)
     m_option.set_value(time1);
     std::ostringstream oss;
     oss << time1;
-    std::string timestr{"absolute . "};
-    timestr += oss.str();
+    std::string timestr{"(absolute . "};
+    timestr += oss.str() + ")";
     oss.str("");
     oss << m_option;
     EXPECT_EQ(oss.str(), timestr);
@@ -1129,77 +1129,77 @@ TEST_F(GncDateOption, test_stream_out)
     m_option.set_value(RelativeDatePeriod::TODAY);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . today");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . today)");
 
     m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . start-this-month");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . start-this-month)");
 
     m_option.set_value(RelativeDatePeriod::END_THIS_MONTH);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . end-this-month");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . end-this-month)");
 
     m_option.set_value(RelativeDatePeriod::START_PREV_MONTH);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-month");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . start-prev-month)");
 
     m_option.set_value(RelativeDatePeriod::END_PREV_MONTH);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-month");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-month)");
 
     m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . start-current-quarter");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . start-current-quarter)");
 
     m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . end-current-quarter");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . end-current-quarter)");
 
     m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-quarter");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . start-prev-quarter)");
 
     m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-quarter");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-quarter)");
 
     m_option.set_value(RelativeDatePeriod::START_CAL_YEAR);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . start-cal-year");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . start-cal-year)");
 
     m_option.set_value(RelativeDatePeriod::END_CAL_YEAR);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . end-cal-year");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . end-cal-year)");
 
     m_option.set_value(RelativeDatePeriod::START_PREV_YEAR);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-year");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . start-prev-year)");
 
     m_option.set_value(RelativeDatePeriod::END_PREV_YEAR);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-year");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-year)");
 
     m_option.set_value(RelativeDatePeriod::START_ACCOUNTING_PERIOD);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-fin-year");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . start-prev-fin-year)");
 
     m_option.set_value(RelativeDatePeriod::END_ACCOUNTING_PERIOD);
     oss.str("");
     oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-fin-year");
+    EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-fin-year)");
 }
 
 TEST_F(GncDateOption, test_stream_in_absolute)
diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
index d0b3147e1..34d1389a6 100644
--- a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
+++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
@@ -66,10 +66,32 @@
 (let ((option (gnc:lookup-option options
                                  \"foo\"
                                  \"bar\")))
-  ((lambda (o) (if o (gnc:option-set-value o '~s))) option))
+  ((lambda (o) (if o (gnc:option-set-value o '~a))) option))
 
 " value))
 
+(define (test-currency-output-template value)
+  (format #f "
+; Section: foo
+
+(let ((option (gnc:lookup-option options
+                                 \"foo\"
+                                 \"bar\")))
+  ((lambda (o) (if o (gnc:option-set-value o \"~a\"))) option))
+
+" value))
+
+(define (test-commodity-output-template value)
+  (format #f "
+; Section: foo
+
+(let ((option (gnc:lookup-option options
+                                 \"foo\"
+                                 \"bar\")))
+  ((lambda (o) (if o (gnc:option-set-value o \"~a\" \"~a\"))) option))
+
+" (string-split value #\:)))
+
 (define (test-budget-output-template value)
   (format #f "
 ; Section: foo
@@ -90,7 +112,7 @@
     (test-equal test-unchanged-section-output-template
                 (gnc:generate-restore-forms odb "options"))
     (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") value)
-    (test-equal (test-template value)
+    (test-equal (test-template (GncOption-serialize (gnc:lookup-option odb "foo" "bar")))
                 (gnc:generate-restore-forms odb "options"))))
 
 (define (test-gnc-string-option-to-scheme)
@@ -125,7 +147,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
           (EUR (gnc-commodity-new book "European Union Euro" "CURRENCY" "EUR" "" 100)))
       (gnc-commodity-table-insert table USD)
       (gnc-commodity-table-insert table EUR)
-      (test-option-scheme-output gnc:make-currency-option test-literal-output-template
+      (test-option-scheme-output gnc:make-currency-option test-currency-output-template
                                USD EUR)
 ;; Garbage collection has already eaten USD and EUR.
       (test-book-clear-data book "gnc-commodity-table")
@@ -163,7 +185,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
   (let* ((book (gnc-option-test-book-new))
          (AAPL (gnc-commodity-new book "Apple" "NASDAQ" "AAPL" "" 1))
          (FMAGX (gnc-commodity-new book "Fidelity Magellan Fund" "FUND" "FMAGX" "" 1000)))
-  (test-option-scheme-output gnc:make-commodity-option test-literal-output-template
+  (test-option-scheme-output gnc:make-commodity-option test-currency-output-template
                              AAPL FMAGX))
   (test-end "test-gnc-commodity-option-to-scheme"))
 
@@ -181,19 +203,19 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
   (test-begin "test-gnc-date-option-to-scheme")
   (let ((odb (gnc:new-options)))
     (gnc:options-make-end-date! odb "foo" "bar" "baz" "Phoney Option")
-    (test-equal test-unchanged-section-output-template
+    (test-equal "Date Unchanged" test-unchanged-section-output-template
                 (gnc:generate-restore-forms odb "options"))
     (let* ((option (gnc:lookup-option odb "foo" "bar"))
           (test-template test-literal-output-template)
           (time (gnc-dmy2time64 25 12 2020))
           (value `(absolute . ,time)))
       (gnc:option-set-value option value)
-      (test-equal (test-template (GncOption-get-scm-value option))
+      (test-equal "Absolute Date" (test-template (GncOption-serialize option))
                   (gnc:generate-restore-forms odb "options"))
       (set! value '(relative . end-prev-year))
       (gnc:option-set-value option value)
-      (test-equal value (GncOption-get-scm-value option))
-      (test-equal (test-template (GncOption-get-scm-value option))
+      (test-equal "Relative Date Value" value (GncOption-get-scm-value option))
+      (test-equal "Relative Date" (test-template (GncOption-serialize option))
                   (gnc:generate-restore-forms odb "options"))))
   (test-end "test-gnc-date-option-to-scheme"))
 
@@ -248,7 +270,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
             (test-template test-literal-output-template)
             (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK))))
         (gnc-option-set-value option new-acclist)
-        (test-equal (test-template (GncOption-get-scm-value option))
+        (test-equal (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-account-list-option-to-scheme"))
@@ -271,7 +293,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
             (test-template test-literal-output-template)
             (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK))))
         (gnc-option-set-value option new-acclist)
-        (test-equal (test-template (GncOption-get-scm-value option))
+        (test-equal (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-account-sel-option-to-scheme"))
@@ -301,7 +323,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
                 (gnc:generate-restore-forms odb "options"))
     (let ((option (gnc:lookup-option odb "foo" "bar")))
       (gnc:option-set-value option value)
-      (test-equal (test-template (GncOption-get-scm-value option))
+      (test-equal (test-template (GncOption-serialize option))
                   (gnc:generate-restore-forms odb "options"))))
   (test-end "test-gnc-multichoice-option-to-scheme"))
 
@@ -319,7 +341,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
       (let ((option (gnc:lookup-option odb "foo" "bar"))
             (test-template test-literal-output-template))
         (gnc-option-set-value option '(ugly))
-        (test-equal (test-template (GncOption-get-scm-value option))
+        (test-equal (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-list-option-to-scheme"))
@@ -340,7 +362,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
       (let ((option (gnc:lookup-option odb "foo" "bar"))
             (test-template test-literal-output-template))
         (gnc-option-set-value option 42.0)
-        (test-equal (test-template (GncOption-get-scm-value option))
+        (test-equal (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-number-range-option-to-scheme"))
@@ -361,7 +383,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
       (let ((option (gnc:lookup-option odb "foo" "bar"))
             (test-template test-literal-output-template))
         (gnc-option-set-value option 420)
-        (test-equal (test-template (GncOption-get-scm-value option))
+        (test-equal (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-number-plot-size-option-to-scheme"))
@@ -384,7 +406,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
       (let ((option (gnc:lookup-option odb "__reg" "query"))
             (test-template test-literal-output-template))
         (gnc-option-set-value option (gnc-scm2query query-scm))
-        (test-equal (test-template (GncOption-get-scm-value option))
+        (test-equal (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
   (test-end "test-gnc-number-plot-size-option-to-scheme"))
@@ -420,7 +442,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
       (let ((option (gnc:lookup-option odb "foo" "bar"))
             (test-template test-literal-output-template))
         (gnc-option-set-value option '"13b305236443451a86c5366b7f890ecb")
-        (test-equal (test-template (GncOption-get-scm-value option))
+        (test-equal (test-template (GncOption-serialize option))
                     (gnc:generate-restore-forms odb "options"))
         ))
     (test-end  "test-gnc-owner-option-to-scheme"))

commit c3b8b6cc493ded3360b760b4fa960611da045000
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Nov 21 16:10:28 2021 -0800

    Make a proper class of GncOptionsDialog.
    
    Removes the C functions, allocates with new and delete and cleans itself
    up.

diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt
index c40af8c7f..9cf89ce74 100644
--- a/gnucash/gnome-utils/CMakeLists.txt
+++ b/gnucash/gnome-utils/CMakeLists.txt
@@ -132,7 +132,6 @@ set (gnome_utils_HEADERS
   dialog-file-access.h
   dialog-preferences.h
   dialog-object-references.h
-  dialog-options.h
   dialog-query-view.h
   dialog-reset-warnings.h
   dialog-totd.h
diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index cc370730b..2f6b4ac32 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -99,37 +99,6 @@ static constexpr const char* GNC_PREFS_GROUP{"dialogs.options"};
 /* A pointer to the last selected filename */
 #define LAST_SELECTION "last-selection"
 
-struct gnc_option_win
-{
-    GtkWidget  * window;
-    GtkWidget  * notebook;
-    GtkWidget  * page_list_view;
-    GtkWidget  * page_list;
-    GtkButton  * help_button;
-    GtkButton  * cancel_button;
-    GtkButton  * apply_button;
-    GtkButton  * ok_button;
-
-    bool toplevel;
-
-    GNCOptionWinCallback apply_cb;
-    gpointer             apply_cb_data;
-
-    GNCOptionWinCallback help_cb;
-    gpointer             help_cb_data;
-
-    GNCOptionWinCallback close_cb;
-    gpointer             close_cb_data;
-
-    /* Hold onto this for a complete reset */
-    GncOptionDB *option_db;
-
-    /* Hold on to this to unregister the right class */
-    const char *component_class;
-
-    /* widget being destroyed */
-    bool destroyed;
-};
 
 enum page_tree
 {
@@ -140,6 +109,8 @@ enum page_tree
 
 //Init the class static.
 std::vector<WidgetCreateFunc> GncOptionUIFactory::s_registry{static_cast<size_t>(GncOptionUIType::MAX_VALUE)};
+bool GncOptionUIFactory::s_initialized{false};
+static void gnc_options_ui_factory_initialize (void);
 
 void
 GncOptionUIFactory::set_func(GncOptionUIType type, WidgetCreateFunc func)
@@ -151,6 +122,11 @@ GtkWidget*
 GncOptionUIFactory::create(GncOption& option, GtkGrid* page, GtkLabel* name,
                      char* description, GtkWidget** enclosing, bool* packed)
 {
+    if (!s_initialized)
+    {
+        gnc_options_ui_factory_initialize();
+        s_initialized = true;
+    }
     auto type{option.get_ui_type()};
     auto func{s_registry[static_cast<size_t>(type)]};
     if (func)
@@ -206,8 +182,7 @@ GncOptionGtkUIItem::set_widget(GtkWidget* widget)
 
 
 static void dialog_reset_cb(GtkWidget * w, gpointer data);
-void dialog_list_select_cb (GtkTreeSelection *selection,
-                                        gpointer data);
+static void dialog_list_select_cb (GtkTreeSelection *selection, gpointer data);
 static void component_close_handler (gpointer data);
 
 static void
@@ -215,8 +190,8 @@ section_reset_widgets(GncOptionSection* section)
 {
 }
 
-GtkWidget* const
-gnc_option_get_gtk_widget (const GncOption* option)
+static inline GtkWidget* const
+option_get_gtk_widget (const GncOption* option)
 {
     if (!option) return nullptr;
     auto ui_item{dynamic_cast<const GncOptionGtkUIItem*>(option->get_ui_item())};
@@ -238,20 +213,24 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
 
     /* Can't static cast, no inheritance relationship. */
     auto option_win =
-        static_cast<GNCOptionWin*>(g_object_get_data(G_OBJECT(toplevel),
+        static_cast<GncOptionsDialog*>(g_object_get_data(G_OBJECT(toplevel),
                                                      "optionwin"));
-    gtk_widget_set_sensitive (GTK_WIDGET(option_win->apply_button), sensitive);
-    gtk_widget_set_sensitive (GTK_WIDGET(option_win->ok_button), sensitive);
-    gtk_button_set_label (option_win->cancel_button,
-                          sensitive ? _("_Cancel") : _("_Close"));
+    option_win->set_sensitive(sensitive);
 }
 
 void
-gnc_options_dialog_changed (GNCOptionWin *win)
+GncOptionsDialog::set_sensitive(bool sensitive) noexcept
 {
-    if (!win) return;
+    gtk_widget_set_sensitive (GTK_WIDGET(m_apply_button), sensitive);
+    gtk_widget_set_sensitive (GTK_WIDGET(m_ok_button), sensitive);
+    gtk_button_set_label (m_cancel_button,
+                          sensitive ? _("_Cancel") : _("_Close"));
+}
 
-    dialog_changed_internal (win->window, TRUE);
+void
+GncOptionsDialog::changed() noexcept
+{
+    set_sensitive(true);
 }
 
 void
@@ -266,7 +245,7 @@ void
 gnc_option_changed_option_cb(GtkWidget *dummy, GncOption* option)
 {
     if (!option) return;
-    auto widget{gnc_option_get_gtk_widget(option)};
+    auto widget{option_get_gtk_widget(option)};
     gnc_option_changed_widget_cb(widget, option);
 }
 
@@ -392,16 +371,17 @@ create_reset_button_box(GtkBox* page_content_box)
 }
 
 static int
-setup_notebook_pages(GNCOptionWin* propertybox, GtkBox* page_content_box,
+setup_notebook_pages(GncOptionsDialog* dlg, GtkBox* page_content_box,
                      const char* name)
 {
-    auto page_count = gtk_notebook_page_num(GTK_NOTEBOOK(propertybox->notebook),
+    auto notebook{dlg->get_notebook()};
+    auto page_count = gtk_notebook_page_num(GTK_NOTEBOOK(notebook),
                                             GTK_WIDGET(page_content_box));
 
-    if (propertybox->page_list_view)
+    if (dlg->get_page_list_view())
     {
         /* Build the matching list item for selecting from large page sets */
-        auto view = GTK_TREE_VIEW(propertybox->page_list_view);
+        auto view = GTK_TREE_VIEW(dlg->get_page_list_view());
         auto list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
 
         PINFO("Page name is %s and page_count is %d", name, page_count);
@@ -414,20 +394,19 @@ setup_notebook_pages(GNCOptionWin* propertybox, GtkBox* page_content_box,
 
         if (page_count > MAX_TAB_COUNT - 1)   /* Convert 1-based -> 0-based */
         {
-            gtk_widget_show(propertybox->page_list);
-            gtk_notebook_set_show_tabs(GTK_NOTEBOOK(propertybox->notebook), FALSE);
-            gtk_notebook_set_show_border(GTK_NOTEBOOK(propertybox->notebook), FALSE);
+            gtk_widget_show(dlg->get_page_list());
+            gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
+            gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
         }
         else
-            gtk_widget_hide(propertybox->page_list);
+            gtk_widget_hide(dlg->get_page_list());
 
     }
     return page_count;
 }
 
 static int
-gnc_options_dialog_append_page(GNCOptionWin * propertybox,
-                               GncOptionSectionPtr& section)
+dialog_append_page(GncOptionsDialog* dlg, GncOptionSectionPtr& section)
 {
     auto name = section->get_name().c_str();
     if (!name || *name == '\0')
@@ -462,55 +441,32 @@ gnc_options_dialog_append_page(GNCOptionWin * propertybox,
                                 _("Reset all values to their defaults."));
 
     g_signal_connect(G_OBJECT(reset_button), "clicked",
-                     G_CALLBACK(dialog_reset_cb), propertybox);
+                     G_CALLBACK(dialog_reset_cb), dlg);
     g_object_set_data(G_OBJECT(reset_button), "section",
                       static_cast<void*>(section.get()));
     gtk_box_pack_end(GTK_BOX(buttonbox), reset_button, FALSE, FALSE, 0);
     gtk_widget_show_all(GTK_WIDGET(page_content_box));
-    gtk_notebook_append_page(GTK_NOTEBOOK(propertybox->notebook),
+    gtk_notebook_append_page(GTK_NOTEBOOK(dlg->get_notebook()),
                              GTK_WIDGET(page_content_box), page_label);
 
     /* Switch to selection from a list if the page count threshold is reached */
-    return setup_notebook_pages(propertybox, page_content_box, name);
-}
-
-/********************************************************************\
- * gnc_options_dialog_build_contents                                *
- *   builds an options dialog given a property box and an options   *
- *   database and make the dialog visible                           *
- *                                                                  *
- * @param propertybox - gnome property box to use                    *
- * @param odb         - option database to use                       *
-\********************************************************************/
-void
-gnc_options_dialog_build_contents (GNCOptionWin *propertybox,
-                                   GncOptionDB  *odb)
-{
-    gnc_options_dialog_build_contents_full (propertybox, odb, true);
+    return setup_notebook_pages(dlg, page_content_box, name);
 }
 
-/********************************************************************\
- * gnc_options_dialog_build_contents_full                           *
- *   builds an options dialog given a property box and an options   *
- *   database and make the dialog visible depending on the          *
- *   show_dialog flag                                               *
- *                                                                  *
- * @param propertybox - gnome property box to use                    *
+/**
+ * Populate the dialog's notebook with the contents of odb.
+ *
  * @param odb         - option database to use                       *
  * @param show_dialog - should dialog be made visible or not         *
-\********************************************************************/
+ */
 void
-gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox,
-                                        GNCOptionDB  *odb, gboolean show_dialog)
+GncOptionsDialog::build_contents(GncOptionDB  *odb, bool show_dialog)
 {
     gint default_page = -1;
 
-    gint page;
-
-    g_return_if_fail (propertybox != NULL);
     g_return_if_fail (odb != NULL);
 
-    propertybox->option_db = odb;
+    m_option_db = odb;
 
     auto num_sections = odb->num_sections();
     auto default_section = odb->get_default_section();
@@ -519,9 +475,9 @@ gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox,
           default_section ? default_section->get_name().c_str() : "NULL");
 
     odb->foreach_section(
-        [propertybox, default_section, &default_page]
+        [this, default_section, &default_page]
         (GncOptionSectionPtr& section) {
-            auto page = gnc_options_dialog_append_page(propertybox, section);
+            auto page = dialog_append_page(this, section);
             if (default_section && section.get() == default_section)
                 default_page = page;
         });
@@ -541,107 +497,112 @@ gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox,
                 });
         });
 
-    gtk_notebook_popup_enable(GTK_NOTEBOOK(propertybox->notebook));
+    gtk_notebook_popup_enable(GTK_NOTEBOOK(m_notebook));
     if (default_page >= 0)
     {
         /* Find the page list and set the selection to the default page */
-        GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(propertybox->page_list_view));
+        auto selection{gtk_tree_view_get_selection(GTK_TREE_VIEW(m_page_list_view))};
         GtkTreeIter iter;
-        GtkTreeModel *model;
 
-        model = gtk_tree_view_get_model(GTK_TREE_VIEW(propertybox->page_list_view));
+        auto model{gtk_tree_view_get_model(GTK_TREE_VIEW(m_page_list_view))};
         gtk_tree_model_iter_nth_child(model, &iter, NULL, default_page);
         gtk_tree_selection_select_iter (selection, &iter);
-        gtk_notebook_set_current_page(GTK_NOTEBOOK(propertybox->notebook), default_page);
+        gtk_notebook_set_current_page(GTK_NOTEBOOK(m_notebook), default_page);
     }
-    dialog_changed_internal(propertybox->window, FALSE);
+    dialog_changed_internal(m_window, FALSE);
     if (show_dialog)
-        gtk_widget_show(propertybox->window);
+        gtk_widget_show(m_window);
 }
 
-GtkWidget *
-gnc_options_dialog_widget(GNCOptionWin * win)
+void GncOptionsDialog::call_apply_cb() noexcept
 {
-    return win->window;
+    auto close_cb = m_close_cb;
+
+    m_close_cb = nullptr;
+    if (m_apply_cb)
+        (m_apply_cb)(this, m_apply_cb_data);
+    m_close_cb = close_cb;
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(m_window));
+    set_sensitive(false);
 }
 
-GtkWidget *
-gnc_options_page_list(GNCOptionWin * win)
+void GncOptionsDialog::call_help_cb() noexcept
 {
-    return win->page_list;
+    if (m_help_cb)
+        (m_help_cb)(this, m_help_cb_data);
 }
 
-GtkWidget *
-gnc_options_dialog_notebook(GNCOptionWin * win)
+void GncOptionsDialog::call_close_cb() noexcept
 {
-    return win->notebook;
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(m_window));
+    if (m_close_cb)
+    {
+        gtk_window_close(GTK_WINDOW(m_window));
+        (m_close_cb)(this, m_close_cb_data);
+    }
+    else
+    {
+        gtk_widget_hide(m_window);
+    }
 }
 
-static void
-gnc_options_dialog_help_button_cb(GtkWidget * widget, GNCOptionWin *win)
+void GncOptionsDialog::call_book_help_cb() noexcept
 {
-    if (win->help_cb)
-        (win->help_cb)(win, win->help_cb_data);
+/*    if (m_book_options_help_cb)
+        (m_book_options_help_cb)(this, m_book_options_help_cb_data);
+*/
 }
 
-static void
-gnc_options_dialog_cancel_button_cb(GtkWidget * widget, GNCOptionWin *win)
+void GncOptionsDialog::call_style_sheet_help_cb() noexcept
 {
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
-
-    if (win->close_cb)
-        (win->close_cb)(win, win->close_cb_data);
-    else
-        gtk_widget_hide(win->window);
+/*
+    if (m_style_sheet_help_cb)
+        (m_style_shet_help_cb)(this, m_style_sheet_help_cb_data);
+*/
 }
 
+// Help button signal handler
 static void
-gnc_options_dialog_apply_button_cb(GtkWidget * widget, GNCOptionWin *win)
+dialog_help_button_cb(GtkWidget * widget, GncOptionsDialog *win)
 {
-    GNCOptionWinCallback close_cb = win->close_cb;
-
-    win->close_cb = NULL;
-    if (win->apply_cb)
-        win->apply_cb (win, win->apply_cb_data);
-    win->close_cb = close_cb;
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
-    dialog_changed_internal (win->window, FALSE);
+    win->call_help_cb();
 }
 
+// Cancel/close button clicked signal handler
 static void
-gnc_options_dialog_ok_button_cb(GtkWidget * widget, GNCOptionWin *win)
+dialog_cancel_button_cb(GtkWidget * widget, GncOptionsDialog *win)
 {
-    GNCOptionWinCallback close_cb = win->close_cb;
-
-    win->close_cb = NULL;
-    if (win->apply_cb)
-        win->apply_cb (win, win->apply_cb_data);
-    win->close_cb = close_cb;
-
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
+    win->call_close_cb();
+}
 
-    if (win->close_cb)
-        (win->close_cb)(win, win->close_cb_data);
-    else
-        gtk_widget_hide(win->window);
+// Apply button clicked signal handler
+static void
+dialog_apply_button_cb(GtkWidget * widget, GncOptionsDialog *win)
+{
+    win->call_apply_cb();
 }
 
+// OK Button clicked signal handler
 static void
-gnc_options_dialog_destroy_cb (GtkWidget *object, GNCOptionWin *win)
+dialog_ok_button_cb(GtkWidget * widget, GncOptionsDialog *win)
 {
-    if (!win) return;
+    win->call_apply_cb();
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->get_widget()));
+    win->call_close_cb();
+}
 
-    if (win->destroyed == FALSE)
-    {
-        if (win->close_cb)
-            (win->close_cb)(win, win->close_cb_data);
-    }
+// "destroy" signal handler
+static void
+dialog_destroy_cb (GtkWidget *object, GncOptionsDialog *win)
+{
+    win->call_close_cb();
 }
 
+// "key_press_event" signal handler
 static bool
-gnc_options_dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
+dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
 {
-    GNCOptionWin *win = static_cast<decltype(win)>(data);
+    GncOptionsDialog *win = static_cast<decltype(win)>(data);
 
     if (event->keyval == GDK_KEY_Escape)
     {
@@ -655,7 +616,7 @@ gnc_options_dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gp
 static void
 dialog_reset_cb(GtkWidget * w, gpointer data)
 {
-    GNCOptionWin *win = static_cast<decltype(win)>(data);
+    GncOptionsDialog *win = static_cast<decltype(win)>(data);
     gpointer val;
     bool dialog_changed = false;
 
@@ -675,14 +636,14 @@ dialog_reset_cb(GtkWidget * w, gpointer data)
             option.set_ui_item_from_option();
         });
 
-    dialog_changed_internal (win->window, dialog_changed);
+    dialog_changed_internal (win->get_widget(), dialog_changed);
 }
 
-void
-dialog_list_select_cb (GtkTreeSelection *selection,
-                                   gpointer data)
+// changed signal handler
+static void
+dialog_list_select_cb (GtkTreeSelection *selection, gpointer data)
 {
-    GNCOptionWin * win = static_cast<decltype(win)>(data);
+    GncOptionsDialog * win = static_cast<decltype(win)>(data);
     GtkTreeModel *list;
     GtkTreeIter iter;
     gint index = 0;
@@ -693,192 +654,183 @@ dialog_list_select_cb (GtkTreeSelection *selection,
                        PAGE_INDEX, &index,
                        -1);
     PINFO("Index is %d", index);
-    gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), index);
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(win->get_notebook()), index);
 }
 
 static void
 component_close_handler (gpointer data)
 {
-    GNCOptionWin *win = static_cast<decltype(win)>(data);
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
-    gnc_options_dialog_cancel_button_cb (NULL, win);
+    GncOptionsDialog *win = static_cast<decltype(win)>(data);
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->get_widget()));
+    dialog_cancel_button_cb (NULL, win);
 }
 
-/* gnc_options_dialog_new:
+/** Constructs a GncOptionsDialog
  *
- *   - Opens the dialog-options glade file
- *   - Connects signals specified in the builder file
- *   - Sets the window's title
- *   - Initializes a new GtkNotebook, and adds it to the window
+ * Based on the description in the GtkBuilder file. Initializes signals.
+ * Two component classes might be used, DIALOG_BOOK_OPTIONS_CM_CLASS or DIALOG_OPTIONS_CM_CLASS of which the latter is the default. 
  *
+ * @param modal: If true the "Apply" button is hidden. It doesn't make the dialog run in its own event loop so it's not truly modal.
+ * @param title: The title that will appear in the dialog's title bar.
+ * @param component_class: For registering the dialog in the component manager.
+ * @param parent: The widget for which the dialog will be transient-for.
  */
-GNCOptionWin *
-gnc_options_dialog_new(gchar *title, GtkWindow *parent)
+GncOptionsDialog::GncOptionsDialog(bool modal, const char* title,
+                             const char* component_class,
+                           GtkWindow *parent) :
+    m_component_class{component_class ? component_class : DIALOG_OPTIONS_CM_CLASS}
 {
-    return gnc_options_dialog_new_modal(FALSE, title, NULL, parent);
-}
-
-/**
- *   - Opens the dialog-options glade file
- *   - Connects signals specified in the builder file
- *   - Sets the window's title
- *   - Initializes a new GtkNotebook, and adds it to the window
- *   - If modal TRUE, hides 'apply' button
- *   - If component_class is provided, it is used, otherwise,
- *     DIALOG_OPTIONS_CM_CLASS is used; this is used to distinguish the
- *     book-option dialog from report dialogs. The book-option dialog is a
- *     singleton, so if a dialog already exists it will be raised to the top of
- *     the window stack instead of creating a new dialog.
- */
-GNCOptionWin *
-gnc_options_dialog_new_modal(gboolean modal, gchar *title,
-                             const char *component_class,
-                             GtkWindow *parent)
-{
-    GNCOptionWin *retval;
-    GtkBuilder   *builder;
-    GtkWidget    *hbox;
-    gint component_id;
-    GtkWidget    *button;
-
-    retval = g_new0(GNCOptionWin, 1);
-    builder = gtk_builder_new();
+    auto builder = gtk_builder_new();
     gnc_builder_add_from_file (builder, "dialog-options.glade", "gnucash_options_window");
-    retval->window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window"));
-    retval->page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll"));
-    retval->component_class = component_class ? component_class : DIALOG_OPTIONS_CM_CLASS;
-    g_object_set_data(G_OBJECT(retval->window), "optionwin", retval);
+    m_window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window"));
+    g_object_ref(m_window);
+    m_page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll"));
+    g_object_set_data(G_OBJECT(m_window), "optionwin", this);
 
     // Set the name for this dialog so it can be easily manipulated with css
-    gtk_widget_set_name (GTK_WIDGET(retval->window), "gnc-id-options");
+    gtk_widget_set_name (GTK_WIDGET(m_window), "gnc-id-options");
 
     /* Page List */
-    {
-        GtkTreeView *view;
-        GtkListStore *store;
-        GtkTreeSelection *selection;
-        GtkCellRenderer *renderer;
-        GtkTreeViewColumn *column;
-
-        retval->page_list_view = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_treeview"));
-
-        view = GTK_TREE_VIEW(retval->page_list_view);
 
-        store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING);
-        gtk_tree_view_set_model(view, GTK_TREE_MODEL(store));
-        g_object_unref(store);
+    m_page_list_view = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_treeview"));
 
-        renderer = gtk_cell_renderer_text_new();
-        column = gtk_tree_view_column_new_with_attributes(_("Page"), renderer,
-                 "text", PAGE_NAME, NULL);
-        gtk_tree_view_append_column(view, column);
+    auto view = GTK_TREE_VIEW(m_page_list_view);
 
-        gtk_tree_view_column_set_alignment(column, 0.5);
+    auto store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING);
+    gtk_tree_view_set_model(view, GTK_TREE_MODEL(store));
+    g_object_unref(store);
 
-        selection = gtk_tree_view_get_selection(view);
-        gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
-        g_signal_connect (selection, "changed",
-                          G_CALLBACK (dialog_list_select_cb), retval);
-    }
+    auto renderer = gtk_cell_renderer_text_new();
+    auto column =
+        gtk_tree_view_column_new_with_attributes(_("Page"), renderer,
+                                                 "text", PAGE_NAME,
+                                                 nullptr);
+    gtk_tree_view_append_column(view, column);
 
-    retval->help_button = GTK_BUTTON(gtk_builder_get_object (builder, "helpbutton"));
-    g_signal_connect(retval->help_button, "clicked", G_CALLBACK(gnc_options_dialog_help_button_cb), retval);
-    retval->cancel_button = GTK_BUTTON(gtk_builder_get_object (builder, "cancelbutton"));
-    g_signal_connect(retval->cancel_button, "clicked", G_CALLBACK(gnc_options_dialog_cancel_button_cb), retval);
-    retval->apply_button = GTK_BUTTON(gtk_builder_get_object (builder, "applybutton"));
-    g_signal_connect(retval->apply_button, "clicked", G_CALLBACK(gnc_options_dialog_apply_button_cb), retval);
-    retval->ok_button = GTK_BUTTON(gtk_builder_get_object (builder, "okbutton"));
-    g_signal_connect(retval->ok_button, "clicked", G_CALLBACK(gnc_options_dialog_ok_button_cb), retval);
+    gtk_tree_view_column_set_alignment(column, 0.5);
 
-    gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, retval);
+    auto selection = gtk_tree_view_get_selection(view);
+    gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+    g_signal_connect (selection, "changed",
+                      G_CALLBACK (dialog_list_select_cb), this);
+
+    m_help_button = GTK_BUTTON(gtk_builder_get_object (builder, "helpbutton"));
+    g_signal_connect(m_help_button, "clicked",
+                     G_CALLBACK(dialog_help_button_cb), this);
+    m_cancel_button = GTK_BUTTON(gtk_builder_get_object (builder, "cancelbutton"));
+    g_signal_connect(m_cancel_button, "clicked",
+                     G_CALLBACK(dialog_cancel_button_cb), this);
+    m_apply_button = GTK_BUTTON(gtk_builder_get_object (builder, "applybutton"));
+    g_signal_connect(m_apply_button, "clicked",
+                     G_CALLBACK(dialog_apply_button_cb), this);
+    m_ok_button = GTK_BUTTON(gtk_builder_get_object (builder, "okbutton"));
+    g_signal_connect(m_ok_button, "clicked",
+                     G_CALLBACK(dialog_ok_button_cb), this);
+
+    gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func,
+                                      this);
 
     // when added to a page of the hierarchy assistant there will be no parent
     if (parent)
-        gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(retval->window),
+        gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(m_window),
                                  parent);
 
     if (title)
-        gtk_window_set_title(GTK_WINDOW(retval->window), title);
+        gtk_window_set_title(GTK_WINDOW(m_window), title);
 
     /* modal */
-    if (modal == TRUE)
-    {
-        GtkWidget *apply_button;
-
-        apply_button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton"));
-        gtk_widget_hide (apply_button);
-    }
+    if (modal)
+        gtk_widget_hide (GTK_WIDGET(m_apply_button));
 
     /* glade doesn't support a notebook with zero pages */
-    hbox = GTK_WIDGET(gtk_builder_get_object (builder, "notebook_placeholder"));
-    retval->notebook = gtk_notebook_new();
+    auto hbox = GTK_WIDGET(gtk_builder_get_object (builder,
+                                                   "notebook_placeholder"));
+    m_notebook = gtk_notebook_new();
 
-    gtk_widget_set_vexpand (retval->notebook, TRUE);
+    gtk_widget_set_vexpand (m_notebook, TRUE);
 
-    gtk_widget_show(retval->notebook);
-    gtk_box_pack_start(GTK_BOX(hbox), retval->notebook, TRUE, TRUE, 5);
+    gtk_widget_show(m_notebook);
+    gtk_box_pack_start(GTK_BOX(hbox), m_notebook, TRUE, TRUE, 5);
 
-    component_id = gnc_register_gui_component (retval->component_class,
-                                               NULL, component_close_handler,
-                                               retval);
+    auto component_id = gnc_register_gui_component (m_component_class,
+                                                    nullptr,
+                                                    component_close_handler,
+                                                    this);
     gnc_gui_component_set_session (component_id, gnc_get_current_session());
 
-    g_signal_connect (retval->window, "destroy",
-                      G_CALLBACK(gnc_options_dialog_destroy_cb), retval);
+    g_signal_connect (m_window, "destroy", G_CALLBACK(dialog_destroy_cb), this);
 
-    g_signal_connect (retval->window, "key_press_event",
-                      G_CALLBACK(gnc_options_dialog_window_key_press_cb), retval);
+    g_signal_connect (m_window, "key_press_event",
+                      G_CALLBACK(dialog_window_key_press_cb), this);
 
     g_object_unref(G_OBJECT(builder));
+}
 
-    retval->destroyed = FALSE;
-    return retval;
+GncOptionsDialog::~GncOptionsDialog()
+{
+    if (m_destroying)
+        return;
+    m_destroying = true;
+    gnc_unregister_gui_component_by_data(m_component_class, this);
+    g_signal_handlers_disconnect_by_func(m_window, (gpointer)dialog_destroy_cb, this);
+    g_signal_handlers_disconnect_by_func(m_window, (gpointer)dialog_window_key_press_cb, this);
+    g_object_unref(m_window);
 }
 
 void
-gnc_options_dialog_set_apply_cb(GNCOptionWin * win, GNCOptionWinCallback cb,
-                                gpointer data)
+GncOptionsDialog::set_apply_cb(GncOptionsDialogCallback cb, gpointer data) noexcept
 {
-    win->apply_cb = cb;
-    win->apply_cb_data = data;
+    m_apply_cb = cb;
+    m_apply_cb_data = data;
 }
 
 void
-gnc_options_dialog_set_help_cb(GNCOptionWin * win, GNCOptionWinCallback cb,
-                               gpointer data)
+GncOptionsDialog::set_help_cb(GncOptionsDialogCallback cb, gpointer data) noexcept
 {
-    win->help_cb = cb;
-    win->help_cb_data = data;
+    m_help_cb = cb;
+    m_help_cb_data = data;
 }
 
 void
-gnc_options_dialog_set_close_cb(GNCOptionWin * win, GNCOptionWinCallback cb,
-                                gpointer data)
+GncOptionsDialog::set_close_cb( GncOptionsDialogCallback cb, gpointer data) noexcept
 {
-    win->close_cb = cb;
-    win->close_cb_data = data;
+    m_close_cb = cb;
+    m_close_cb_data = data;
 }
 
-/* This is for global program preferences. */
-void
-gnc_options_dialog_destroy(GNCOptionWin * win)
+
+static void
+gnc_book_options_help_cb (GncOptionsDialog *win, gpointer dat)
 {
-    if (!win) return;
+    gnc_gnome_help (GTK_WINDOW (win->get_widget()), HF_HELP, HL_BOOK_OPTIONS);
+}
 
-    gnc_unregister_gui_component_by_data(win->component_class, win);
+void
+GncOptionsDialog::set_book_help_cb() noexcept
+{
+    set_help_cb((GncOptionsDialogCallback)gnc_book_options_help_cb, nullptr);
+}
 
-    win->destroyed = TRUE;
-    gtk_widget_destroy(win->window);
+static void
+gnc_global_options_help_cb (GncOptionsDialog *win, gpointer dat)
+{
+    gnc_gnome_help (GTK_WINDOW(win->get_widget()), HF_HELP, HL_GLOBPREFS);
+}
 
-    win->window = NULL;
-    win->notebook = NULL;
-    win->apply_cb = NULL;
-    win->help_cb = NULL;
-    win->component_class = NULL;
+static void
+gnc_style_sheet_options_help_cb (GncOptionsDialog *win, gpointer dat)
+{
+    gnc_gnome_help (GTK_WINDOW(win->get_widget()), HF_HELP, HL_STYLE_SHEET);
+}
 
-    g_free(win);
+void
+GncOptionsDialog::set_style_sheet_help_cb () noexcept
+{
+    set_help_cb ((GncOptionsDialogCallback)gnc_style_sheet_options_help_cb,
+                 nullptr);
 }
 
+
 /* ****************************************************************/
 /* Option Widgets                                      */
 /* ***************************************************************/
@@ -1461,7 +1413,7 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box,
     }
 
     option.set_ui_item_from_option();
-    auto widget{gnc_option_get_gtk_widget(&option)};
+    auto widget{option_get_gtk_widget(&option)};
     if (type == GncOptionUIType::DATE_RELATIVE)
     {
         *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
@@ -1541,7 +1493,7 @@ account_select_all_cb(GtkWidget *widget, gpointer data)
     GncTreeViewAccount *tree_view;
     GtkTreeSelection *selection;
 
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
+    tree_view = GNC_TREE_VIEW_ACCOUNT(option_get_gtk_widget (option));
     gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
     gtk_tree_selection_select_all(selection);
@@ -1555,7 +1507,7 @@ account_clear_all_cb(GtkWidget *widget, gpointer data)
     GncTreeViewAccount *tree_view;
     GtkTreeSelection *selection;
 
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
+    tree_view = GNC_TREE_VIEW_ACCOUNT(option_get_gtk_widget (option));
     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
     gtk_tree_selection_unselect_all(selection);
     gnc_option_changed_widget_cb(widget, option);
@@ -1568,7 +1520,7 @@ account_select_children_cb(GtkWidget *widget, gpointer data)
     GncTreeViewAccount *tree_view;
     GList *acct_list = NULL, *acct_iter = NULL;
 
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
+    tree_view = GNC_TREE_VIEW_ACCOUNT(option_get_gtk_widget (option));
     acct_list = gnc_tree_view_account_get_selected_accounts (tree_view);
 
     for (acct_iter = acct_list; acct_iter; acct_iter = acct_iter->next)
@@ -1593,7 +1545,7 @@ show_hidden_toggled_cb(GtkWidget *widget, GncOption* option)
         option->get_ui_type() != GncOptionUIType::ACCOUNT_SEL)
         return;
 
-    auto tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget(option));
+    auto tree_view = GNC_TREE_VIEW_ACCOUNT(option_get_gtk_widget(option));
     AccountViewInfo avi;
     gnc_tree_view_account_get_view_info (tree_view, &avi);
     avi.show_hidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
@@ -1795,7 +1747,7 @@ create_option_widget<GncOptionUIType::ACCOUNT_LIST>(GncOption& option,
     gtk_grid_attach (GTK_GRID(page_box), *enclosing, 1, grid_row, 1, 1);
     *packed = TRUE;
 
-    auto widget = gnc_option_get_gtk_widget(&option);
+    auto widget = option_get_gtk_widget(&option);
 
     auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
     g_signal_connect(G_OBJECT(selection), "changed",
@@ -1858,7 +1810,7 @@ create_option_widget<GncOptionUIType::ACCOUNT_SEL> (GncOption& option,
 static void
 list_changed_cb(GtkTreeSelection *selection, GncOption* option)
 {
-    GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option));
+    GtkTreeView *view = GTK_TREE_VIEW(option_get_gtk_widget (option));
     gnc_option_changed_widget_cb(GTK_WIDGET(view), option);
 }
 
@@ -1869,7 +1821,7 @@ list_select_all_cb(GtkWidget *widget, gpointer data)
     GtkTreeView *view;
     GtkTreeSelection *selection;
 
-    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option));
+    view = GTK_TREE_VIEW(option_get_gtk_widget(option));
     selection = gtk_tree_view_get_selection(view);
     gtk_tree_selection_select_all(selection);
     gnc_option_changed_widget_cb(GTK_WIDGET(view), option);
@@ -1882,7 +1834,7 @@ list_clear_all_cb(GtkWidget *widget, gpointer data)
     GtkTreeView *view;
     GtkTreeSelection *selection;
 
-    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option));
+    view = GTK_TREE_VIEW(option_get_gtk_widget(option));
     selection = gtk_tree_view_get_selection(view);
     gtk_tree_selection_unselect_all(selection);
     gnc_option_changed_widget_cb(GTK_WIDGET(view), option);
@@ -2018,7 +1970,7 @@ create_option_widget<GncOptionUIType::LIST> (GncOption& option,
                                     (G_OBJECT(page_box), "options-grid-row"));
 
     *enclosing = create_list_widget(option, NULL);
-    auto value = gnc_option_get_gtk_widget(&option);
+    auto value = option_get_gtk_widget(&option);
 
     align_label (name_label);
 
@@ -2351,7 +2303,7 @@ radiobutton_set_cb(GtkWidget *w, gpointer data)
     gpointer _current, _new_value;
     gint current, new_value;
 
-    auto widget = gnc_option_get_gtk_widget(option);
+    auto widget = option_get_gtk_widget(option);
 
     _current = g_object_get_data(G_OBJECT(widget), "gnc_radiobutton_index");
     current = GPOINTER_TO_INT (_current);
@@ -2513,7 +2465,7 @@ gnc_plot_size_option_set_select_method(GncOption& option, bool set_buttons)
     GtkWidget *px_widget, *p_widget;
     GtkWidget *widget;
 
-    widget = gnc_option_get_gtk_widget(&option);
+    widget = option_get_gtk_widget(&option);
 
     widget_list = gtk_container_get_children(GTK_CONTAINER(widget));
     // px_button item 0
@@ -2724,8 +2676,8 @@ create_option_widget<GncOptionUIType::BUDGET> (GncOption& option,
     return widget;
 }
 
-void
-gnc_options_ui_initialize(void)
+static void
+gnc_options_ui_factory_initialize(void)
 {
     GncOptionUIFactory::set_func(GncOptionUIType::BOOLEAN,
                                  create_option_widget<GncOptionUIType::BOOLEAN>);
@@ -2781,43 +2733,9 @@ gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb)
     {
         auto option{odb->find_option(OPTION_SECTION_ACCOUNTS,
                                     OPTION_NAME_NUM_FIELD_SOURCE)};
-        auto num_source_button{gnc_option_get_gtk_widget(option)};
+        auto num_source_button{option_get_gtk_widget(option)};
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (num_source_button),
                                       num_split_action);
     }
 }
 
-
-static void
-gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat)
-{
-    gnc_gnome_help (GTK_WINDOW (win), HF_HELP, HL_BOOK_OPTIONS);
-}
-
-void
-gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win)
-{
-    gnc_options_dialog_set_help_cb(win,
-                                (GNCOptionWinCallback)gnc_book_options_help_cb,
-                                nullptr);
-}
-
-static void
-gnc_global_options_help_cb (GNCOptionWin *win, gpointer dat)
-{
-    gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_GLOBPREFS);
-}
-
-static void
-gnc_style_sheet_options_help_cb (GNCOptionWin *win, gpointer dat)
-{
-    gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_STYLE_SHEET);
-}
-
-void
-gnc_options_dialog_set_style_sheet_options_help_cb (GNCOptionWin *win)
-{
-    gnc_options_dialog_set_help_cb (win,
-                                   (GNCOptionWinCallback)gnc_style_sheet_options_help_cb,
-                                    NULL);
-}
diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h
deleted file mode 100644
index abf59dc50..000000000
--- a/gnucash/gnome-utils/dialog-options.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/********************************************************************\
- * dialog-options.h -- GNOME option handling                        *
- * Copyright (C) 1998-2000 Linas Vepstas                            *
- *                                                                  *
- * 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 OPTIONS_DIALOG_H
-#define OPTIONS_DIALOG_H
-
-#include <libguile.h>
-#ifdef __cplusplus
-class GncOption;
-class GncOptionDB;
-using GNCOption = GncOption;
-using GNCOptionDB = GncOptionDB;
-extern "C"
-{
-#else
-typedef struct GncOption GncOption;
-typedef struct GncOptionDB GncOptionDB;
-typedef GncOptionDB GNCOptionDB;
-#endif
-#include <guile-mappings.h>
-#include <gtk/gtk.h>
-
-
-/**
- * Retrieve the GtkWidget* used for packing the option control.
- *
- * This is not ncessarily the widget that has the input or handles signals.
- * @param option The option
- * @return a GtkWidget* const
- */
-GtkWidget* const gnc_option_get_gtk_widget (const GncOption* option);
-
-typedef struct gnc_option_win GNCOptionWin;
-
-typedef void (* GNCOptionWinCallback)(GNCOptionWin *, gpointer data);
-
-GNCOptionWin * gnc_options_dialog_new_modal (gboolean modal, gchar *title,
-                                             const char *component_class,
-                                             GtkWindow *parent);
-GNCOptionWin * gnc_options_dialog_new (gchar *title, GtkWindow *parent);
-void gnc_options_dialog_destroy (GNCOptionWin * win);
-void gnc_options_register_stocks (void);
-
-GtkWidget * gnc_options_dialog_widget (GNCOptionWin * win);
-GtkWidget * gnc_options_page_list (GNCOptionWin * win);
-GtkWidget * gnc_options_dialog_notebook (GNCOptionWin * win);
-
-void gnc_options_dialog_changed (GNCOptionWin *win);
-
-void gnc_option_changed_widget_cb (GtkWidget *widget, GncOption *option);
-void gnc_option_changed_option_cb (GtkWidget *dummy, GncOption *option);
-
-void gnc_options_dialog_set_apply_cb (GNCOptionWin * win,
-                                      GNCOptionWinCallback thunk,
-                                      gpointer cb_data);
-void gnc_options_dialog_set_help_cb (GNCOptionWin * win,
-                                     GNCOptionWinCallback thunk,
-                                     gpointer cb_data);
-void gnc_options_dialog_set_close_cb (GNCOptionWin * win,
-                                      GNCOptionWinCallback thunk,
-                                      gpointer cb_data);
-
-void gnc_options_dialog_build_contents (GNCOptionWin *win,
-                                        GNCOptionDB  *odb);
-void gnc_options_dialog_build_contents_full (GNCOptionWin *win,
-                                             GNCOptionDB  *odb,
-                                             gboolean show_dialog);
-void gnc_options_ui_initialize (void);
-
-/** Set the help callback to 'gnc_book_options_help_cb' to open a help browser
- *  and point it to the Book Options link in the Help file.
- */
-void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win);
-
-/** Set the initial values of new book options to values specified in user
- *  preferences.
- */
-void gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb);
-
-/** Set the help callback to 'gnc_book_options_help_cb' to open a help browser
- *  and point it to the Book Options link in the Help file.
- */
-void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win);
-
-/** Set the help callback to 'gnc_style_sheet_options_help_cb' to open a help browser
- *  and point it to the Style Sheet link in the Help file.
- */
-void gnc_options_dialog_set_style_sheet_options_help_cb (GNCOptionWin *win);
-
-/**
- * Initialize the option UI elements.
- */
-void gnc_options_ui_initialize(void);
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* OPTIONS_DIALOG_H */
diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp
index b0101c23d..71c02c39d 100644
--- a/gnucash/gnome-utils/dialog-options.hpp
+++ b/gnucash/gnome-utils/dialog-options.hpp
@@ -34,7 +34,6 @@
 
 #include <gnc-option-uitype.hpp>
 #include <gnc-option-ui.hpp>
-#include "dialog-options.h"
 
 /** @fn WidgetCreateFunc
  *  Function pointer for per-option-type GtkWidget constructors.
@@ -74,6 +73,7 @@ public:
                              GtkWidget**, bool*);
 private:
     static std::vector<WidgetCreateFunc> s_registry;
+    static bool s_initialized;
 };
 
 /** class GncOptionGtkUIItem
@@ -116,6 +116,84 @@ qof_instance_cast(Instance inst)
     static_assert(std::is_pointer_v<Instance>, "Pointers Only!");
     return reinterpret_cast<const QofInstance*>(inst);
 }
+class GncOptionsDialog;
+
+typedef void (* GncOptionsDialogCallback)(GncOptionsDialog*, void* data);
+
+class GncOptionsDialog
+{
+    GtkWidget  * m_window;
+    GtkWidget  * m_notebook;
+    GtkWidget  * m_page_list_view;
+    GtkWidget  * m_page_list;
+    GtkButton  * m_help_button;
+    GtkButton  * m_cancel_button;
+    GtkButton  * m_apply_button;
+    GtkButton  * m_ok_button;
+
+    bool toplevel;
+
+    GncOptionsDialogCallback m_apply_cb;
+    gpointer             m_apply_cb_data;
+
+    GncOptionsDialogCallback m_help_cb;
+    gpointer             m_help_cb_data;
+
+    GncOptionsDialogCallback m_close_cb;
+    gpointer             m_close_cb_data;
+
+    /* Hold onto this for a complete reset */
+    GncOptionDB* m_option_db;
+
+    /* Hold on to this to unregister the right class */
+    const char* m_component_class;
+
+    /* widget being destroyed */
+    bool m_destroying{false};
+
+public:
+    GncOptionsDialog(const char* title, GtkWindow* parent) :
+        GncOptionsDialog(false, title, nullptr, parent) {}
+    GncOptionsDialog(bool modal, const char* title, const char* component_class,
+                 GtkWindow* parent);
+    GncOptionsDialog(const GncOptionsDialog&) = default;
+    GncOptionsDialog(GncOptionsDialog&&) = default;
+    ~GncOptionsDialog();
+
+    GtkWidget* get_widget() const noexcept { return m_window; }
+    GtkWidget* get_page_list() const noexcept { return m_page_list; }
+    GtkWidget* get_page_list_view() const noexcept { return m_page_list_view; }
+    GtkWidget* get_notebook() const noexcept { return m_notebook; }
+    GncOptionDB* get_option_db() noexcept { return m_option_db; }
+    inline void build_contents(GncOptionDB* odb){
+        build_contents(odb, true); }
+    void build_contents(GncOptionDB* odb, bool show_dialog);
+    void set_sensitive(bool sensitive) noexcept;
+    void changed() noexcept;
+    void set_apply_cb(GncOptionsDialogCallback, void* cb_data) noexcept;
+    void call_apply_cb() noexcept;
+    void set_help_cb(GncOptionsDialogCallback, void* cb_data) noexcept;
+    void call_help_cb() noexcept;
+    void set_close_cb(GncOptionsDialogCallback, void* cb_data) noexcept;
+    void call_close_cb() noexcept;
+    void set_book_help_cb() noexcept;
+    void call_book_help_cb() noexcept;
+    void set_style_sheet_help_cb() noexcept;
+    void call_style_sheet_help_cb() noexcept;
+};
+
+void gnc_option_changed_widget_cb (GtkWidget *widget, GncOption *option);
+void gnc_option_changed_option_cb (GtkWidget *dummy, GncOption *option);
+
+/**
+ * Set the initial values of new book options to values specified in user
+ * preferences.
+
+ * Nothing to do with GncOptionsDialog, but it depends on Gtk and s used in
+ * both assistant-hierarchy and gnc-main-window.
+ * @param odb: The book's options database.
+ */
+void gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb);
 
 #endif // GNC_DIALOG_OPTIONS_HPP_
 /** @}
diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c
index 9adf77e82..9219ff32d 100644
--- a/gnucash/gnome-utils/gnc-gnome-utils.c
+++ b/gnucash/gnome-utils/gnc-gnome-utils.c
@@ -41,7 +41,6 @@
 #include "gnc-window.h"
 #include "gnc-icons.h"
 #include "dialog-doclink-utils.h"
-#include "dialog-options.h"
 #include "dialog-commodity.h"
 #include "dialog-totd.h"
 #include "gnc-ui-util.h"
@@ -77,7 +76,6 @@ void
 gnc_gnome_utils_init (void)
 {
     gnc_component_manager_init ();
-    gnc_options_ui_initialize ();
 
     scm_init_sw_gnome_utils_module();
     scm_c_use_module ("sw_gnome_utils");
diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp
index a8b2a255f..c61de3730 100644
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -86,7 +86,7 @@ extern "C"
 # include <sys/stat.h> // for stat(2)
 #endif
 }
-#include "dialog-options.h"
+#include "dialog-options.hpp"
 
 /** Names of signals generated by the main window. */
 enum
@@ -4220,7 +4220,7 @@ gnc_book_options_dialog_apply_helper(GncOptionDB * options)
 }
 
 static void
-gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin,
+gnc_book_options_dialog_apply_cb(GncOptionsDialog * optionwin,
                                  gpointer user_data)
 {
     auto options{static_cast<GncOptionDB *>(user_data)};
@@ -4232,12 +4232,12 @@ gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin,
 }
 
 static void
-gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin,
+gnc_book_options_dialog_close_cb(GncOptionsDialog * optionwin,
                                  gpointer user_data)
 {
     auto options{static_cast<GncOptionDB *>(user_data)};
 
-    gnc_options_dialog_destroy(optionwin);
+    delete optionwin;
     gnc_option_db_destroy(options);
 }
 
@@ -4264,14 +4264,12 @@ static gboolean
 show_handler (const char *class_name, gint component_id,
               gpointer user_data, gpointer iter_data)
 {
-    auto optwin{static_cast<GNCOptionWin*>(user_data)};
-    GtkWidget *widget;
+    auto optwin{static_cast<GncOptionsDialog*>(user_data)};
 
     if (!optwin)
         return(FALSE);
 
-    widget = gnc_options_dialog_widget(optwin);
-
+    auto widget = optwin->get_widget();
     gtk_window_present(GTK_WINDOW(widget));
     return(TRUE);
 }
@@ -4279,11 +4277,9 @@ show_handler (const char *class_name, gint component_id,
 GtkWidget *
 gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
 {
-    QofBook *book = gnc_get_current_book ();
-    GncOptionDB *options;
-    GNCOptionWin *optionwin;
+    auto book = gnc_get_current_book ();
 
-    options = gnc_option_db_new();
+    auto options = gnc_option_db_new();
     gnc_option_db_book_options(options);
     qof_book_load_options (book, gnc_option_db_load, options);
     gnc_option_db_clean (options);
@@ -4295,23 +4291,18 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
     {
         return nullptr;
     }
-    optionwin = gnc_options_dialog_new_modal (
-        modal,
-        (title ? title : _( "Book Options")),
-        DIALOG_BOOK_OPTIONS_CM_CLASS, parent);
-    gnc_options_dialog_build_contents (optionwin, options);
-
-    gnc_options_dialog_set_book_options_help_cb (optionwin);
-
-    gnc_options_dialog_set_apply_cb (optionwin,
-                                     gnc_book_options_dialog_apply_cb,
-                                     (gpointer)options);
-    gnc_options_dialog_set_close_cb (optionwin,
-                                     gnc_book_options_dialog_close_cb,
-                                     (gpointer)options);
+    auto optionwin = new GncOptionsDialog (modal,
+                                           (title ? title : _( "Book Options")),
+                                           DIALOG_BOOK_OPTIONS_CM_CLASS, parent);
+    optionwin->build_contents(options);
+    optionwin->set_book_help_cb();
+    optionwin->set_apply_cb(gnc_book_options_dialog_apply_cb,
+                            (gpointer)options);
+    optionwin->set_close_cb ( gnc_book_options_dialog_close_cb,
+                              (gpointer)options);
     if (modal)
         gnc_options_dialog_set_new_book_option_values (options);
-    return gnc_options_dialog_widget (optionwin);
+    return optionwin->get_widget();
 }
 
 static void
diff --git a/gnucash/gnome/assistant-hierarchy.cpp b/gnucash/gnome/assistant-hierarchy.cpp
index 6562552c4..5f60c1042 100644
--- a/gnucash/gnome/assistant-hierarchy.cpp
+++ b/gnucash/gnome/assistant-hierarchy.cpp
@@ -44,7 +44,6 @@ extern "C"
 #endif
 #include "gnc-account-merge.h"
 #include "dialog-new-user.h"
-#include "dialog-options.h"
 #include "dialog-utils.h"
 #include "dialog-file-access.h"
 #include "assistant-hierarchy.h"
@@ -68,7 +67,7 @@ extern "C"
 
 #include "gnc-engine.h"
 }
-
+#include <dialog-options.hpp>
 #include <gnc-optiondb.h>
 
 static QofLogModule log_module = GNC_MOD_IMPORT;
@@ -125,7 +124,7 @@ typedef struct
     gboolean new_book;  /* presumably only used for new book creation but we check*/
 
     GncOptionDB *options;
-    GNCOptionWin *optionwin;
+    GncOptionsDialog *optionwin;
 
     GncHierarchyAssistantFinishedCallback when_completed;
 
@@ -1397,7 +1396,7 @@ on_cancel (GtkAssistant      *gtkassistant,
 {
     gnc_suspend_gui_refresh ();
     if (data->new_book)
-        gnc_options_dialog_destroy (data->optionwin);
+        delete data->optionwin;
 
     delete_hierarchy_dialog (data);
     delete_our_account_tree (data);
@@ -1445,7 +1444,7 @@ on_finish (GtkAssistant  *gtkassistant,
 
     gnc_suspend_gui_refresh ();
     if (data->new_book)
-        gnc_options_dialog_destroy (data->optionwin);
+        delete data->optionwin;
 
     account_trees_merge(gnc_get_current_root_account(), data->our_account_tree);
 
@@ -1504,19 +1503,18 @@ on_select_currency_prepare (hierarchy_data  *data)
  * dialog to make it clean up after itself.
  */
 static void
-book_options_dialog_close_cb(GNCOptionWin * optionwin,
-                               gpointer user_data)
+book_options_dialog_close_cb(GncOptionsDialog *optionwin,
+                             gpointer user_data)
 {
     auto options{static_cast<GncOptionDB*>(user_data)};
 
-    gnc_options_dialog_destroy(optionwin);
+    delete optionwin;
     gnc_option_db_destroy(options);
 }
 
 static void
 assistant_insert_book_options_page (hierarchy_data *data)
 {
-    GtkWidget *options, *parent;
     GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
     gtk_box_set_homogeneous (GTK_BOX (vbox), FALSE);
 
@@ -1527,17 +1525,17 @@ assistant_insert_book_options_page (hierarchy_data *data)
     gnc_option_db_clean (data->options);
 
     /* The options dialog gets added to the notebook so it doesn't need a parent.*/
-    data->optionwin = gnc_options_dialog_new_modal (TRUE, _("New Book Options"),
-                                                    DIALOG_BOOK_OPTIONS_CM_CLASS, nullptr);
-    gnc_options_dialog_build_contents_full (data->optionwin, data->options, FALSE);
+    data->optionwin = new GncOptionsDialog(true, _("New Book Options"),
+                                           DIALOG_BOOK_OPTIONS_CM_CLASS,
+                                           nullptr);
+    data->optionwin->build_contents(data->options, false);
 
-    gnc_options_dialog_set_close_cb (data->optionwin,
-                                     book_options_dialog_close_cb,
-                                     (gpointer)data->options);
+    data->optionwin->set_close_cb(book_options_dialog_close_cb,
+                                  (gpointer)data->options);
     gnc_options_dialog_set_new_book_option_values (data->options);
 
-    options = gnc_options_dialog_notebook (data->optionwin);
-    parent = gtk_widget_get_parent (options);
+    auto options = data->optionwin->get_notebook();
+    auto parent = gtk_widget_get_parent (options);
 
     g_object_ref (options);
     gtk_container_remove (GTK_CONTAINER(parent), options);
diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp
index 9e1adef41..5c45dcea6 100644
--- a/gnucash/gnome/dialog-report-column-view.cpp
+++ b/gnucash/gnome/dialog-report-column-view.cpp
@@ -40,7 +40,7 @@ extern "C"
 }
 
 #include "dialog-report-column-view.hpp"
-#include "dialog-options.h"
+#include <dialog-options.hpp>
 #include <gnc-optiondb.h>
 
 enum available_cols
@@ -61,7 +61,7 @@ enum contents_cols
 
 struct gncp_column_view_edit
 {
-    GNCOptionWin * optwin;
+    GncOptionsDialog * optwin;
     GtkTreeView  * available;
     GtkTreeView  * contents;
 
@@ -95,7 +95,7 @@ gnc_column_view_set_option(GncOptionDB* odb, const char* section,
 static void
 gnc_column_view_edit_destroy(gnc_column_view_edit * view)
 {
-    gnc_options_dialog_destroy(view->optwin);
+    delete view->optwin;
     scm_gc_unprotect_object(view->view);
     gnc_option_db_destroy(view->odb);
     g_free(view);
@@ -273,18 +273,17 @@ gnc_column_view_update_buttons_cb (GtkTreeSelection *selection,
 }
 
 static void
-gnc_column_view_edit_apply_cb(GNCOptionWin * w, gpointer user_data)
+gnc_column_view_edit_apply_cb(GncOptionsDialog *dlg, gpointer user_data)
 {
     SCM  dirty_report = scm_c_eval_string("gnc:report-set-dirty?!");
     auto win{static_cast<gnc_column_view_edit*>(user_data)};
-    GList *results = nullptr, *iter;
 
     if (!win) return;
-    results = gnc_option_db_commit (win->odb);
-    for (iter = results; iter; iter = iter->next)
+    auto results = gnc_option_db_commit (dlg->get_option_db());
+    for (auto iter = results; iter; iter = iter->next)
     {
         GtkWidget *dialog =
-            gtk_message_dialog_new(GTK_WINDOW(gnc_options_dialog_widget(w)),
+            gtk_message_dialog_new(GTK_WINDOW(dlg->get_widget()),
                                    GTK_DIALOG_MODAL,
                                    GTK_MESSAGE_ERROR,
                                    GTK_BUTTONS_OK,
@@ -300,7 +299,7 @@ gnc_column_view_edit_apply_cb(GNCOptionWin * w, gpointer user_data)
 }
 
 static void
-gnc_column_view_edit_close_cb(GNCOptionWin * win, gpointer user_data)
+gnc_column_view_edit_close_cb(GncOptionsDialog *win, gpointer user_data)
 {
     auto r{static_cast<gnc_column_view_edit*>(user_data)};
     SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!");
@@ -340,10 +339,10 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
         gnc_column_view_edit * r = g_new0(gnc_column_view_edit, 1);
         GtkBuilder *builder;
 
-        r->optwin = gnc_options_dialog_new (nullptr, GTK_WINDOW(gnc_ui_get_main_window (nullptr)));
+        r->optwin = new GncOptionsDialog(nullptr, GTK_WINDOW(gnc_ui_get_main_window (nullptr)));
 
         /* Hide the generic dialog page list. */
-        gtk_widget_hide(gnc_options_page_list(r->optwin));
+        gtk_widget_hide(r->optwin->get_page_list());
 
         builder = gtk_builder_new();
         gnc_builder_add_from_file (builder, "dialog-report.glade", "view_contents_table");
@@ -364,10 +363,9 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
         r->contents_list = SCM_EOL;
         r->odb       = odb;
 
-        gnc_options_dialog_build_contents(r->optwin, r->odb);
+        r->optwin->build_contents(r->odb);
 
-        gtk_notebook_append_page(GTK_NOTEBOOK(gnc_options_dialog_notebook
-                                              (r->optwin)),
+        gtk_notebook_append_page(GTK_NOTEBOOK(r->optwin->get_notebook()),
                                  editor,
                                  gtk_label_new(_("Contents")));
 
@@ -424,18 +422,16 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
         update_available_lists(r);
         update_contents_lists(r);
 
-        gnc_options_dialog_set_apply_cb(r->optwin,
-                                        gnc_column_view_edit_apply_cb, r);
-        gnc_options_dialog_set_close_cb(r->optwin,
-                                        gnc_column_view_edit_close_cb, r);
+        r->optwin->set_apply_cb(gnc_column_view_edit_apply_cb, r);
+        r->optwin->set_close_cb(gnc_column_view_edit_close_cb, r);
 
-        gtk_widget_show(gnc_options_dialog_widget(r->optwin));
+        gtk_widget_show(r->optwin->get_widget());
 
         gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, r);
 
         g_object_unref(G_OBJECT(builder));
 
-        return gnc_options_dialog_widget(r->optwin);
+        return r->optwin->get_widget();
     }
 }
 
@@ -509,7 +505,7 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data)
 
         gnc_column_view_set_option(r->odb, "__general", "report-list",
                                    r->contents_list);
-        gnc_options_dialog_changed (r->optwin);
+        r->optwin->changed ();
     }
     g_free (guid_str);
     update_contents_lists(r);
@@ -552,7 +548,7 @@ gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data)
         gnc_column_view_set_option(r->odb, "__general", "report-list",
                                    r->contents_list);
 
-        gnc_options_dialog_changed (r->optwin);
+        r->optwin->changed();
     }
     update_contents_lists(r);
 }
@@ -589,7 +585,7 @@ gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data)
         gnc_column_view_set_option(r->odb, "__general", "report-list",
                                    r->contents_list);
 
-        gnc_options_dialog_changed (r->optwin);
+        r->optwin->changed();
 
         update_contents_lists(r);
     }
@@ -627,7 +623,7 @@ gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data)
         gnc_column_view_set_option(r->odb, "__general", "report-list",
                                    r->contents_list);
 
-        gnc_options_dialog_changed (r->optwin);
+        r->optwin->changed();
 
         update_contents_lists(r);
     }
@@ -684,7 +680,7 @@ gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data)
                                               scm_from_int (r->contents_selected),
                                               current);
             scm_gc_protect_object(r->contents_list);
-            gnc_options_dialog_changed (r->optwin);
+            r->optwin->changed();
             update_contents_lists(r);
         }
 
diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp
index 980d50794..a1a8d651b 100644
--- a/gnucash/gnome/dialog-report-style-sheet.cpp
+++ b/gnucash/gnome/dialog-report-style-sheet.cpp
@@ -39,8 +39,9 @@ extern "C"
 #include "gnc-guile-utils.h"
 #include "gnc-report.h"
 #include "gnc-ui.h"
+#include <guile-mappings.h>
 }
-#include "dialog-options.h"
+#include <dialog-options.hpp>
 #include <gnc-optiondb.h>
 
 #define DIALOG_STYLE_SHEETS_CM_CLASS "style-sheets-dialog"
@@ -60,7 +61,7 @@ struct _stylesheetdialog
 
 typedef struct ss_info
 {
-    GNCOptionWin  * odialog;
+    GncOptionsDialog  * odialog;
     GncOptionDB   * odb;
     SCM           stylesheet;
     GtkTreeRowReference *row_ref;
@@ -109,7 +110,7 @@ dirty_same_stylesheet (gpointer key, gpointer val, gpointer data)
 }
 
 static void
-gnc_style_sheet_options_apply_cb (GNCOptionWin * propertybox,
+gnc_style_sheet_options_apply_cb (GncOptionsDialog * propertybox,
                                   gpointer user_data)
 {
     ss_info * ssi = (ss_info *)user_data;
@@ -138,16 +139,16 @@ gnc_style_sheet_options_apply_cb (GNCOptionWin * propertybox,
 }
 
 static void
-gnc_style_sheet_options_close_cb (GNCOptionWin * propertybox,
+gnc_style_sheet_options_close_cb (GncOptionsDialog *opt_dialog,
                                   gpointer user_data)
 {
     auto ssi{static_cast<ss_info*>(user_data)};
-    GtkTreeIter iter;
 
     if (gtk_tree_row_reference_valid (ssi->row_ref))
     {
-        StyleSheetDialog * ss = gnc_style_sheet_dialog;
-        GtkTreePath *path = gtk_tree_row_reference_get_path (ssi->row_ref);
+        auto ss = gnc_style_sheet_dialog;
+        auto path = gtk_tree_row_reference_get_path (ssi->row_ref);
+        GtkTreeIter iter;
         if (gtk_tree_model_get_iter (GTK_TREE_MODEL(ss->list_store), &iter, path))
             gtk_list_store_set (ss->list_store, &iter,
                                 COLUMN_DIALOG, NULL,
@@ -155,7 +156,7 @@ gnc_style_sheet_options_close_cb (GNCOptionWin * propertybox,
         gtk_tree_path_free (path);
     }
     gtk_tree_row_reference_free (ssi->row_ref);
-    gnc_options_dialog_destroy (ssi->odialog);
+    delete ssi->odialog;
     gnc_option_db_destroy (ssi->odb);
     scm_gc_unprotect_object (ssi->stylesheet);
     g_free (ssi);
@@ -171,32 +172,24 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss,
 
     SCM            scm_dispatch = scm_call_1 (get_options, sheet_info);
     ss_info        * ssinfo = g_new0 (ss_info, 1);
-    GtkWidget      * window;
     gchar          * title;
     GtkWindow      * parent = GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(ss->list_view)));
 
     title = g_strdup_printf(_("HTML Style Sheet Properties: %s"), name);
-    ssinfo->odialog = gnc_options_dialog_new(title, parent);
+    ssinfo->odialog = new GncOptionsDialog(title, parent);
     ssinfo->odb     = gnc_get_optiondb_from_dispatcher(scm_dispatch);
     ssinfo->stylesheet = sheet_info;
     ssinfo->row_ref    = row_ref;
     g_free (title);
 
     scm_gc_protect_object (ssinfo->stylesheet);
-    g_object_ref (gnc_options_dialog_widget (ssinfo->odialog));
-
-    gnc_options_dialog_build_contents (ssinfo->odialog,
-                                       ssinfo->odb);
+    g_object_ref (ssinfo->odialog->get_widget());
 
-//    gnc_options_dialog_set_style_sheet_options_help_cb (ssinfo->odialog);
+    ssinfo->odialog->build_contents(ssinfo->odb);
 
-    gnc_options_dialog_set_apply_cb (ssinfo->odialog,
-                                     gnc_style_sheet_options_apply_cb,
-                                     ssinfo);
-    gnc_options_dialog_set_close_cb (ssinfo->odialog,
-                                     gnc_style_sheet_options_close_cb,
-                                     ssinfo);
-    window = gnc_options_dialog_widget (ssinfo->odialog);
+    ssinfo->odialog->set_apply_cb(gnc_style_sheet_options_apply_cb, ssinfo);
+    ssinfo->odialog->set_close_cb(gnc_style_sheet_options_close_cb, ssinfo);
+    auto window = ssinfo->odialog->get_widget();
     gtk_window_set_transient_for (GTK_WINDOW(window),
                                   GTK_WINDOW(gnc_style_sheet_dialog->toplevel));
     gtk_window_set_destroy_with_parent (GTK_WINDOW(window), TRUE);
diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp
index 0e27fa069..5a50e2201 100644
--- a/gnucash/gnome/window-report.cpp
+++ b/gnucash/gnome/window-report.cpp
@@ -36,7 +36,6 @@ extern "C"
 #include <sys/stat.h>
 
 #include "swig-runtime.h"
-#include "dialog-options.h"
 #include "gnc-guile-utils.h"
 #include "gnc-report.h"
 #include "gnc-ui.h"
@@ -45,6 +44,7 @@ extern "C"
 
 #include "gnc-plugin-page-report.h"
 }
+#include "dialog-options.hpp"
 #include "dialog-report-column-view.hpp"
 
 /********************************************************************
@@ -65,30 +65,29 @@ reportWindow(int report_id, GtkWindow *parent)
 
 struct report_default_params_data
 {
-    GNCOptionWin * win;
+    GncOptionsDialog * win;
     GncOptionDB  * odb;
     SCM          cur_report;
 };
 
 
 static void
-gnc_options_dialog_apply_cb(GNCOptionWin * propertybox,
+gnc_options_dialog_apply_cb(GncOptionsDialog *opt_dialog,
                             gpointer user_data)
 {
     SCM  dirty_report = scm_c_eval_string("gnc:report-set-dirty?!");
     auto win{static_cast<struct report_default_params_data*>(user_data)};
-    GList *results = nullptr, *iter;
 
     if (!win) return;
-    results = gnc_option_db_commit (win->odb);
-    for (iter = results; iter; iter = iter->next)
+    auto results = gnc_option_db_commit (win->odb);
+    for (auto iter = results; iter; iter = iter->next)
     {
-        GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW (win->win),
-                                                 static_cast<GtkDialogFlags>(0),
-                                                   GTK_MESSAGE_ERROR,
-                                                   GTK_BUTTONS_OK,
-                                                   "%s",
-                                                   (char*)iter->data);
+        auto dialog = gtk_message_dialog_new(GTK_WINDOW (win->win),
+                                             static_cast<GtkDialogFlags>(0),
+                                             GTK_MESSAGE_ERROR,
+                                             GTK_BUTTONS_OK,
+                                             "%s",
+                                             (char*)iter->data);
         gtk_dialog_run(GTK_DIALOG(dialog));
         gtk_widget_destroy(dialog);
         g_free (iter->data);
@@ -99,33 +98,32 @@ gnc_options_dialog_apply_cb(GNCOptionWin * propertybox,
 }
 
 static void
-gnc_options_dialog_help_cb(GNCOptionWin * propertybox,
+gnc_options_dialog_help_cb(GncOptionsDialog *opt_dialog,
                            gpointer user_data)
 {
-    GtkWidget *dialog, *parent;
     auto prm{static_cast<struct report_default_params_data*>(user_data)};
 
-    parent = gnc_options_dialog_widget(prm->win);
-    dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
-                                    GTK_DIALOG_DESTROY_WITH_PARENT,
-                                    GTK_MESSAGE_INFO,
-                                    GTK_BUTTONS_OK,
-                                    "%s",
-                                    _("Set the report options you want using this dialog."));
+    auto parent = prm->win->get_widget();
+    auto dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
+                                         GTK_DIALOG_DESTROY_WITH_PARENT,
+                                         GTK_MESSAGE_INFO,
+                                         GTK_BUTTONS_OK,
+                                         "%s",
+                                         _("Set the report options you want using this dialog."));
     g_signal_connect(G_OBJECT(dialog), "response",
                      (GCallback)gtk_widget_destroy, nullptr);
     gtk_widget_show(dialog);
 }
 
 static void
-gnc_options_dialog_close_cb(GNCOptionWin * propertybox,
+gnc_options_dialog_close_cb(GncOptionsDialog *opt_dialog,
                             gpointer user_data)
 {
     auto win{static_cast<struct report_default_params_data*>(user_data)};
     SCM    set_editor = scm_c_eval_string("gnc:report-set-editor-widget!");
 
     scm_call_2(set_editor, win->cur_report, SCM_BOOL_F);
-    gnc_options_dialog_destroy(win->win);
+    delete win->win;
     gnc_option_db_destroy(win->odb);
     g_free(win);
 }
@@ -183,24 +181,18 @@ gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report,
         }
 
         /* Don't forget to translate the window title */
-        prm->win  = gnc_options_dialog_new((gchar*) (title && *title ? _(title) : ""), parent);
+        prm->win  = new GncOptionsDialog((gchar*) (title && *title ? _(title) : ""), parent);
 
         g_free ((gpointer *) title);
 
         scm_gc_protect_object(prm->cur_report);
 
-        gnc_options_dialog_build_contents(prm->win, prm->odb);
-
-        gnc_options_dialog_set_apply_cb(prm->win,
-                                        gnc_options_dialog_apply_cb,
-                                        (gpointer)prm);
-        gnc_options_dialog_set_help_cb(prm->win,
-                                       gnc_options_dialog_help_cb,
-                                       (gpointer)prm);
-        gnc_options_dialog_set_close_cb(prm->win,
-                                        gnc_options_dialog_close_cb,
-                                        (gpointer)prm);
-        return gnc_options_dialog_widget(prm->win);
+        prm->win->build_contents(prm->odb);
+
+        prm->win->set_apply_cb(gnc_options_dialog_apply_cb, (gpointer)prm);
+        prm->win->set_help_cb(gnc_options_dialog_help_cb, (gpointer)prm);
+        prm->win->set_close_cb(gnc_options_dialog_close_cb, (gpointer)prm);
+        return prm->win->get_widget();
     }
 }
 

commit 5e84f118ddfe53452e2f1088ee4d5aea618b0aa6
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 16:10:13 2021 -0800

    Move gnc_options_dialog functions from gnc-gnome-utils to dialog-options.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 4d6516790..cc370730b 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -2801,3 +2801,23 @@ gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win)
                                 (GNCOptionWinCallback)gnc_book_options_help_cb,
                                 nullptr);
 }
+
+static void
+gnc_global_options_help_cb (GNCOptionWin *win, gpointer dat)
+{
+    gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_GLOBPREFS);
+}
+
+static void
+gnc_style_sheet_options_help_cb (GNCOptionWin *win, gpointer dat)
+{
+    gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_STYLE_SHEET);
+}
+
+void
+gnc_options_dialog_set_style_sheet_options_help_cb (GNCOptionWin *win)
+{
+    gnc_options_dialog_set_help_cb (win,
+                                   (GNCOptionWinCallback)gnc_style_sheet_options_help_cb,
+                                    NULL);
+}
diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h
index 010858a90..abf59dc50 100644
--- a/gnucash/gnome-utils/dialog-options.h
+++ b/gnucash/gnome-utils/dialog-options.h
@@ -96,6 +96,16 @@ void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win);
  */
 void gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb);
 
+/** Set the help callback to 'gnc_book_options_help_cb' to open a help browser
+ *  and point it to the Book Options link in the Help file.
+ */
+void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win);
+
+/** Set the help callback to 'gnc_style_sheet_options_help_cb' to open a help browser
+ *  and point it to the Style Sheet link in the Help file.
+ */
+void gnc_options_dialog_set_style_sheet_options_help_cb (GNCOptionWin *win);
+
 /**
  * Initialize the option UI elements.
  */
diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c
index 4b1074241..9adf77e82 100644
--- a/gnucash/gnome-utils/gnc-gnome-utils.c
+++ b/gnucash/gnome-utils/gnc-gnome-utils.c
@@ -85,32 +85,6 @@ gnc_gnome_utils_init (void)
 }
 
 
-static void
-gnc_global_options_help_cb (GNCOptionWin *win, gpointer dat)
-{
-    gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_GLOBPREFS);
-}
-
-static void
-gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat)
-{
-    gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_BOOK_OPTIONS);
-}
-
-static void
-gnc_style_sheet_options_help_cb (GNCOptionWin *win, gpointer dat)
-{
-    gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_STYLE_SHEET);
-}
-
-void
-gnc_options_dialog_set_style_sheet_options_help_cb (GNCOptionWin *win)
-{
-    gnc_options_dialog_set_help_cb (win,
-                                   (GNCOptionWinCallback)gnc_style_sheet_options_help_cb,
-                                    NULL);
-}
-
 /* gnc_configure_date_format
  *    sets dateFormat to the current value on the scheme side
  *
@@ -739,8 +713,6 @@ gnc_gui_init(void)
 
     gnc_file_set_shutdown_callback (gnc_shutdown);
 
-    gnc_options_dialog_set_global_help_cb (gnc_global_options_help_cb, NULL);
-
     main_window = gnc_main_window_new ();
     // Bug#350993:
     // gtk_widget_show (GTK_WIDGET (main_window));
diff --git a/gnucash/gnome-utils/gnc-gnome-utils.h b/gnucash/gnome-utils/gnc-gnome-utils.h
index f7f45b819..471d67189 100644
--- a/gnucash/gnome-utils/gnc-gnome-utils.h
+++ b/gnucash/gnome-utils/gnc-gnome-utils.h
@@ -36,7 +36,6 @@
 #define GNC_GNOME_UTILS_H
 
 #include <gnc-main-window.h>
-#include "dialog-options.h"
 
 /** Initialize the gnome-utils library
  *  Should be run once before using any gnome-utils features.
@@ -65,21 +64,6 @@ void gnc_gnome_help (GtkWindow *parent, const char *file_name,
  */
 void gnc_launch_doclink (GtkWindow *parent, const char *uri);
 
-/** Set the help callback to 'gnc_book_options_help_cb' to open a help browser
- *  and point it to the Book Options link in the Help file.
- */
-void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win);
-
-/** Set the initial values of new book options to values specified in user
- *  preferences.
- */
-void gnc_options_dialog_set_new_book_option_values (GNCOptionDB *odb);
-
-/** Set the help callback to 'gnc_style_sheet_options_help_cb' to open a help browser
- *  and point it to the Style Sheet link in the Help file.
- */
-void gnc_options_dialog_set_style_sheet_options_help_cb (GNCOptionWin *win);
-
 /** Given a file name, find and load the requested pixmap.  This
  *  routine will display an error message if it can't find the file or
  *  load the pixmap.
diff --git a/gnucash/gnome/dialog-custom-report.c b/gnucash/gnome/dialog-custom-report.c
index 670e8d3d9..7f72ef563 100644
--- a/gnucash/gnome/dialog-custom-report.c
+++ b/gnucash/gnome/dialog-custom-report.c
@@ -29,7 +29,6 @@
 #include "swig-runtime.h"
 
 #include "dialog-custom-report.h"
-#include "dialog-options.h"
 #include "dialog-utils.h"
 #include "gnc-main-window.h"
 #include "window-report.h"
diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index cffee8dc8..172ed2035 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -49,7 +49,6 @@
 #include "gnc-budget.h"
 #include "gnc-features.h"
 
-#include "dialog-options.h"
 #include "dialog-utils.h"
 #include "gnc-gnome-utils.h"
 #include "gnc-gobject-utils.h"
diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c
index e55b6df39..6eda5b34c 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.c
+++ b/gnucash/gnome/gnc-plugin-page-budget.c
@@ -44,13 +44,13 @@
 
 #include "swig-runtime.h"
 #include "libguile.h"
+#include <guile-mappings.h>
 
 #include "gnc-plugin-page-register.h"
 #include "gnc-plugin-page-report.h"
 #include "gnc-budget.h"
 #include "gnc-features.h"
 
-#include "dialog-options.h"
 #include "dialog-utils.h"
 #include "gnc-gnome-utils.h"
 #include "misc-gnome-utils.h"
diff --git a/gnucash/gnome/top-level.c b/gnucash/gnome/top-level.c
index 77080180e..d0a18f5e6 100644
--- a/gnucash/gnome/top-level.c
+++ b/gnucash/gnome/top-level.c
@@ -37,7 +37,6 @@
 #include "dialog-commodity.h"
 #include "dialog-invoice.h"
 #include "dialog-preferences.h"
-#include "dialog-options.h"
 #include "dialog-sx-editor.h"
 #include "dialog-transfer.h"
 #include "dialog-totd.h"
diff --git a/gnucash/html/gnc-html.i b/gnucash/html/gnc-html.i
index 4b76ff050..3f9b3a84f 100644
--- a/gnucash/html/gnc-html.i
+++ b/gnucash/html/gnc-html.i
@@ -24,7 +24,6 @@
 #include <config.h>
 #include <gtk/gtk.h>
 #include <glib-object.h>
-#include <dialog-options.h>
 #include <dialog-utils.h>
 #include <gnc-amount-edit.h>
 #include <gnc-date-edit.h>

commit 8eddb63ef1f5693253cb94df38cf8986f9290f2f
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 14:39:40 2021 -0800

    Remove unused global variables global_help_cb_data and global_help_cb.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index ca6e8fea8..4d6516790 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -205,9 +205,6 @@ GncOptionGtkUIItem::set_widget(GtkWidget* widget)
 }
 
 
-static GNCOptionWinCallback global_help_cb = NULL;
-gpointer global_help_cb_data = NULL;
-
 static void dialog_reset_cb(GtkWidget * w, gpointer data);
 void dialog_list_select_cb (GtkTreeSelection *selection,
                                         gpointer data);
@@ -862,14 +859,6 @@ gnc_options_dialog_set_close_cb(GNCOptionWin * win, GNCOptionWinCallback cb,
     win->close_cb_data = data;
 }
 
-void
-gnc_options_dialog_set_global_help_cb(GNCOptionWinCallback thunk,
-                                      gpointer cb_data)
-{
-    global_help_cb = thunk;
-    global_help_cb_data = cb_data;
-}
-
 /* This is for global program preferences. */
 void
 gnc_options_dialog_destroy(GNCOptionWin * win)
diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h
index 8c3890a78..010858a90 100644
--- a/gnucash/gnome-utils/dialog-options.h
+++ b/gnucash/gnome-utils/dialog-options.h
@@ -79,9 +79,6 @@ void gnc_options_dialog_set_close_cb (GNCOptionWin * win,
                                       GNCOptionWinCallback thunk,
                                       gpointer cb_data);
 
-void gnc_options_dialog_set_global_help_cb (GNCOptionWinCallback thunk,
-                                            gpointer cb_data);
-
 void gnc_options_dialog_build_contents (GNCOptionWin *win,
                                         GNCOptionDB  *odb);
 void gnc_options_dialog_build_contents_full (GNCOptionWin *win,
diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c
index 00f8efe38..4b1074241 100644
--- a/gnucash/gnome-utils/gnc-gnome-utils.c
+++ b/gnucash/gnome-utils/gnc-gnome-utils.c
@@ -73,8 +73,6 @@ const gchar *msg_no_help_reason =
     /* Translators: URI of missing help files */
 const gchar *msg_no_help_location = N_("Expected location");
 
-static void gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat);
-
 void
 gnc_gnome_utils_init (void)
 {

commit 19a2dd495272fd58751216c1b84b1645b956e688
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 14:34:55 2021 -0800

    dialog-options: Store the main buttons in GNCOptionWin.
    
    Instead of searching for them every time the dialog updates.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 88dc993ee..ca6e8fea8 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -105,6 +105,10 @@ struct gnc_option_win
     GtkWidget  * notebook;
     GtkWidget  * page_list_view;
     GtkWidget  * page_list;
+    GtkButton  * help_button;
+    GtkButton  * cancel_button;
+    GtkButton  * apply_button;
+    GtkButton  * ok_button;
 
     bool toplevel;
 
@@ -235,45 +239,14 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
         return;
     g_assert(toplevel && GTK_IS_WINDOW(toplevel));
 
-    widget = toplevel;
-    /* find the ok and cancel buttons, we know where they will be so do it
-       this way as opposed to using gtk_container_foreach, much less iteration */
-    if (GTK_IS_CONTAINER(widget))
-    {
-        GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
-        for (GList *it = children; it; it = it->next)
-        {
-            if (GTK_IS_BOX(GTK_WIDGET(it->data)))
-            {
-                GList *children = gtk_container_get_children(GTK_CONTAINER(it->data));
-                for (GList *it = children; it; it = it->next)
-                {
-                    if (GTK_IS_BUTTON_BOX (GTK_WIDGET(it->data)))
-                    {
-                        GList *children = gtk_container_get_children(GTK_CONTAINER(it->data));
-                        for (GList *it = children; it; it = it->next)
-                        {
-                            GtkWidget* widget = GTK_WIDGET(it->data);
-                            const gchar* name = gtk_widget_get_name(widget);
-
-                            if (g_strcmp0 (name, "ok_button") == 0 ||
-                                g_strcmp0 (name, "apply_button") == 0)
-                                gtk_widget_set_sensitive (widget, sensitive);
-                            else if (g_strcmp0 (name, "cancel_button") == 0)
-                                gtk_button_set_label (GTK_BUTTON (widget),
-                                                      sensitive ? _("_Cancel") :
-                                                      _("_Close"));
-                        }
-                        g_list_free (children);
-                        break; // Found the button-box, no need to continue.
-                    }
-                }
-                g_list_free(children);
-                break; // Found the box, no need to continue.
-            }
-        }
-        g_list_free (children);
-    }
+    /* Can't static cast, no inheritance relationship. */
+    auto option_win =
+        static_cast<GNCOptionWin*>(g_object_get_data(G_OBJECT(toplevel),
+                                                     "optionwin"));
+    gtk_widget_set_sensitive (GTK_WIDGET(option_win->apply_button), sensitive);
+    gtk_widget_set_sensitive (GTK_WIDGET(option_win->ok_button), sensitive);
+    gtk_button_set_label (option_win->cancel_button,
+                          sensitive ? _("_Cancel") : _("_Close"));
 }
 
 void
@@ -777,6 +750,7 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title,
     retval->window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window"));
     retval->page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll"));
     retval->component_class = component_class ? component_class : DIALOG_OPTIONS_CM_CLASS;
+    g_object_set_data(G_OBJECT(retval->window), "optionwin", retval);
 
     // Set the name for this dialog so it can be easily manipulated with css
     gtk_widget_set_name (GTK_WIDGET(retval->window), "gnc-id-options");
@@ -810,14 +784,14 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title,
                           G_CALLBACK (dialog_list_select_cb), retval);
     }
 
-    button = GTK_WIDGET(gtk_builder_get_object (builder, "helpbutton"));
-        g_signal_connect(button, "clicked", G_CALLBACK(gnc_options_dialog_help_button_cb), retval);
-    button = GTK_WIDGET(gtk_builder_get_object (builder, "cancelbutton"));
-        g_signal_connect(button, "clicked", G_CALLBACK(gnc_options_dialog_cancel_button_cb), retval);
-    button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton"));
-        g_signal_connect(button, "clicked", G_CALLBACK(gnc_options_dialog_apply_button_cb), retval);
-    button = GTK_WIDGET(gtk_builder_get_object (builder, "okbutton"));
-        g_signal_connect(button, "clicked", G_CALLBACK(gnc_options_dialog_ok_button_cb), retval);
+    retval->help_button = GTK_BUTTON(gtk_builder_get_object (builder, "helpbutton"));
+    g_signal_connect(retval->help_button, "clicked", G_CALLBACK(gnc_options_dialog_help_button_cb), retval);
+    retval->cancel_button = GTK_BUTTON(gtk_builder_get_object (builder, "cancelbutton"));
+    g_signal_connect(retval->cancel_button, "clicked", G_CALLBACK(gnc_options_dialog_cancel_button_cb), retval);
+    retval->apply_button = GTK_BUTTON(gtk_builder_get_object (builder, "applybutton"));
+    g_signal_connect(retval->apply_button, "clicked", G_CALLBACK(gnc_options_dialog_apply_button_cb), retval);
+    retval->ok_button = GTK_BUTTON(gtk_builder_get_object (builder, "okbutton"));
+    g_signal_connect(retval->ok_button, "clicked", G_CALLBACK(gnc_options_dialog_ok_button_cb), retval);
 
     gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, retval);
 
@@ -1061,7 +1035,6 @@ create_option_widget<GncOptionUIType::TEXT> (GncOption& option, GtkGrid *page_bo
     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD);
     gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), TRUE);
     gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW(widget), FALSE);
-    gtk_container_add (GTK_CONTAINER (scroll), widget);
 
     auto ui_item{std::make_unique<GncGtkTextUIItem>(widget)};
     auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
@@ -1070,6 +1043,7 @@ create_option_widget<GncOptionUIType::TEXT> (GncOption& option, GtkGrid *page_bo
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
+    gtk_container_add (GTK_CONTAINER (scroll), widget);
     gtk_box_pack_start(GTK_BOX(*enclosing), frame, TRUE, TRUE, 0);
     gtk_widget_show_all(*enclosing);
     return widget;
@@ -1740,7 +1714,6 @@ create_account_widget(GncOption& option, char *name)
 
     gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
     gtk_container_set_border_width(GTK_CONTAINER(scroll_win), 5);
-    gtk_container_add(GTK_CONTAINER(scroll_win), tree);
 
     bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
@@ -1799,6 +1772,7 @@ create_account_widget(GncOption& option, char *name)
     option.set_ui_item(std::make_unique<GncGtkAccountListUIItem>(tree));
     option.set_ui_item_from_option();
 
+    gtk_container_add(GTK_CONTAINER(scroll_win), tree);
     return frame;
 }
 
@@ -1997,6 +1971,9 @@ create_list_widget(GncOption& option, char *name)
         gtk_list_store_set(store, &iter, 0, string ? string : "", -1);
     }
 
+    option.set_ui_item(std::make_unique<GncGtkListUIItem>(GTK_WIDGET(view)));
+    option.set_ui_item_from_option();
+
     gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0);
 
     auto selection = gtk_tree_view_get_selection(view);
@@ -2034,6 +2011,8 @@ create_list_widget(GncOption& option, char *name)
     option.set_ui_item(std::make_unique<GncGtkListUIItem>(GTK_WIDGET(view)));
     option.set_ui_item_from_option();
 
+    gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0);
+
     return frame;
 }
 
@@ -2644,7 +2623,6 @@ create_option_widget<GncOptionUIType::PLOT_SIZE> (GncOption& option,
     gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
     g_object_set (G_OBJECT(hbox), "margin", 3, NULL);
 
-    gtk_container_add(GTK_CONTAINER(*enclosing), hbox);
     auto value_px = create_range_spinner(option);
 
     g_signal_connect(G_OBJECT(value_px), "changed",
@@ -2679,6 +2657,7 @@ create_option_widget<GncOptionUIType::PLOT_SIZE> (GncOption& option,
     option.set_ui_item(std::make_unique<GncGtkPlotSizeUIItem>(static_cast<GtkWidget*>(hbox)));
     option.set_ui_item_from_option();
 
+    gtk_container_add(GTK_CONTAINER(*enclosing), hbox);
     gtk_widget_show_all(*enclosing);
     return hbox;
 }

commit 9804928006b19552d00a9f1b478fcbd72abb2dda
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 11:58:22 2021 -0800

    Don't call gnc_option_db_clean after setting up the options dialog.
    
    The widgets have already been loaded and loading them again just sensitizes
    the buttons as if something had changed.

diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp
index 4898c9cb8..0e27fa069 100644
--- a/gnucash/gnome/window-report.cpp
+++ b/gnucash/gnome/window-report.cpp
@@ -190,7 +190,6 @@ gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report,
         scm_gc_protect_object(prm->cur_report);
 
         gnc_options_dialog_build_contents(prm->win, prm->odb);
-        gnc_option_db_clean(prm->odb);
 
         gnc_options_dialog_set_apply_cb(prm->win,
                                         gnc_options_dialog_apply_cb,

commit e17ee38c8049f64fcbf00faf83b742d99ebaac0a
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 11:56:25 2021 -0800

    dialog-options.cpp: Replace hand-rolled loop to find toplevel with gtk_widget_get_toplevel.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 755e932a7..88dc993ee 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -229,16 +229,13 @@ static void
 dialog_changed_internal (GtkWidget *widget, bool sensitive)
 {
     g_return_if_fail(widget);
-    while (TRUE)
-    {
-        auto new_widget = gtk_widget_get_parent(widget);
-        if (new_widget && GTK_IS_WIDGET(new_widget) &&
-            GTK_IS_WINDOW(new_widget))
-            widget = new_widget;
-        else
-            break;
-    }
 
+    auto toplevel{gtk_widget_get_toplevel(widget)};
+    if (toplevel == widget && !GTK_IS_WINDOW(toplevel))
+        return;
+    g_assert(toplevel && GTK_IS_WINDOW(toplevel));
+
+    widget = toplevel;
     /* find the ok and cancel buttons, we know where they will be so do it
        this way as opposed to using gtk_container_foreach, much less iteration */
     if (GTK_IS_CONTAINER(widget))
@@ -246,7 +243,7 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
         GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
         for (GList *it = children; it; it = it->next)
         {
-            if (GTK_IS_BOX (GTK_WIDGET(it->data)))
+            if (GTK_IS_BOX(GTK_WIDGET(it->data)))
             {
                 GList *children = gtk_container_get_children(GTK_CONTAINER(it->data));
                 for (GList *it = children; it; it = it->next)
@@ -271,7 +268,7 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
                         break; // Found the button-box, no need to continue.
                     }
                 }
-                g_list_free (children);
+                g_list_free(children);
                 break; // Found the box, no need to continue.
             }
         }

commit a3a381cfd0efdbbcf7c8bb1c28d2999502bad84f
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 11:52:44 2021 -0800

    dialog-options.cpp: Fix passing bad widget ptr to dialog_changed_internal.
    
    GtkTreeSelection is not a widget.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 7af187e75..755e932a7 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1805,6 +1805,14 @@ create_account_widget(GncOption& option, char *name)
     return frame;
 }
 
+static void
+option_account_sel_changed_cb(GtkTreeSelection *sel, gpointer data)
+{
+    auto tree_view{gtk_tree_selection_get_tree_view(sel)};
+    gnc_option_changed_widget_cb(GTK_WIDGET(tree_view),
+                                 static_cast<GncOption*>(data));
+}
+
 template<> GtkWidget*
 create_option_widget<GncOptionUIType::ACCOUNT_LIST>(GncOption& option,
                                                     GtkGrid *page_box,
@@ -1828,9 +1836,10 @@ create_option_widget<GncOptionUIType::ACCOUNT_LIST>(GncOption& option,
     *packed = TRUE;
 
     auto widget = gnc_option_get_gtk_widget(&option);
+
     auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
     g_signal_connect(G_OBJECT(selection), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
+                     G_CALLBACK(option_account_sel_changed_cb), &option);
 
     //  gtk_clist_set_row_height(GTK_CLIST(value), 0);
     //  gtk_widget_set_size_request(value, -1, GTK_CLIST(value)->row_height * 10);

commit dd7feb99887278982f5dfc898fac44feb770672c
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 11:45:54 2021 -0800

    options.scm: Remove second license comment.

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 0cdb63310..097a996e2 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -356,23 +356,6 @@
 ;;
 ;; Created by:	Derek Atkins <derek at ihtfp.com>
 ;;
-;; 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
-
 
 ;; Internally, values are always a guid. Externally, both guids and
 ;; invoice pointers may be used to set the value of the option. The

commit d1fe359e4722e168fcd3fda0be87538b4e2c7ed1
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 11:44:57 2021 -0800

    gnc_numeric_to_decimal: Change can't round warning to a debug.
    
    Routinely used as a check so a warning isn't appropriate.

diff --git a/libgnucash/engine/gnc-numeric.cpp b/libgnucash/engine/gnc-numeric.cpp
index 277e01e0f..0915f4156 100644
--- a/libgnucash/engine/gnc-numeric.cpp
+++ b/libgnucash/engine/gnc-numeric.cpp
@@ -1090,7 +1090,7 @@ gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
     }
     catch (const std::exception& err)
     {
-        PWARN("%s", err.what());
+        DEBUG("%s", err.what());
         return FALSE;
     }
 }

commit 74fd716afbf0b23d3b4bf4af0f18f935c58a8ea5
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 19 11:43:04 2021 -0800

    Clarify gnc_relative_date_to_time64 helper functions.

diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp
index f7d17b069..30847b2a5 100644
--- a/libgnucash/app-utils/gnc-option-date.cpp
+++ b/libgnucash/app-utils/gnc-option-date.cpp
@@ -425,31 +425,40 @@ reldate_offset(RelativeDatePeriod per)
 
 static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
+/* Normalize the modified struct tm computed in gnc_relative_date_to_time64
+ * before setting the time and perhaps beginning/end of the month. Using the
+ * gnc_date API would involve multiple conversions to and from struct tm.
+*/
 static void
 normalize_reldate_tm(struct tm& now)
 {
-    auto tmp_mon = now.tm_mon + (now.tm_mon < 0 ? 12 :
-                                 now.tm_mon > 11 ? -12 : 0);
-    if (now.tm_mday < 1)
-    {
-        --now.tm_mon;
-        now.tm_mday += days_in_month[tmp_mon - 1];
-    }
-    else if (now.tm_mday > days_in_month[tmp_mon])
-    {
-        ++now.tm_mon;
-        now.tm_mday -= days_in_month[tmp_mon];
-    }
-    if (now.tm_mon < 0)
+    auto factor{abs(now.tm_mon) / 12};
+    now.tm_mon /= factor > 0 ? factor : 1;
+    now.tm_year += now.tm_mon < 0 ? -factor : factor;
+
+    while (now.tm_mday < 1)
+        now.tm_mday += days_in_month[--now.tm_mon];
+
+    while (now.tm_mday > days_in_month[now.tm_mon])
+        now.tm_mday -= days_in_month[now.tm_mon++];
+
+    while (now.tm_mon < 0)
     {
         now.tm_mon += 12;
         --now.tm_year;
     }
-    else if (now.tm_mon > 11)
+    while (now.tm_mon > 11)
     {
         now.tm_mon -= 12;
         ++now.tm_year;
     }
+
+    /* This would happen only if we moved from Feb 29 in a leap year while
+     * adjusting the months so we don't need to worry about adjusting the year
+     * again.
+     */
+    if (now.tm_mday > days_in_month[now.tm_mon])
+        now.tm_mday -= days_in_month[now.tm_mon++];
 }
 
 static void
@@ -457,18 +466,13 @@ reldate_set_day_and_time(struct tm& now, RelativeDateType type)
 {
     if (type == RelativeDateType::START)
     {
-        now.tm_hour = now.tm_min = now.tm_sec = 0;
+        gnc_tm_set_day_start(&now);
         now.tm_mday = 1;
     }
     else if (type == RelativeDateType::END)
     {
-        now.tm_min = now.tm_sec = 59;
-        now.tm_hour = 23;
-        now.tm_mday = days_in_month[now.tm_mon];
-        // Check for Februrary in a leap year
-        if (int year = now.tm_year + 1900; now.tm_mon == 1 &&
-            year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
-            ++now.tm_mday;
+        now.tm_mday = gnc_date_get_last_mday(now.tm_mon, now.tm_year + 1900);
+        gnc_tm_set_day_end(&now);
     }
     // Do nothing for LAST and NEXT.
 };
@@ -510,14 +514,12 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
                 now.tm_mon = 0;
             else if (gnc_relative_date_is_ending(period))
                 now.tm_mon = 11;
-            now.tm_mday = days_in_month[now.tm_mon];
             break;
        case RelativeDateOffset::SIX:
             if (reldate_is_prev(period))
                 now.tm_mon -= 6;
             else if (reldate_is_next(period))
                 now.tm_mon += 6;
-            now.tm_mday = days_in_month[now.tm_mon];
             break;
         case RelativeDateOffset::QUARTER:
         {
@@ -534,14 +536,12 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
                 now.tm_mon += 3;
             if (gnc_relative_date_is_ending(period))
                 now.tm_mon += 2;
-            now.tm_mday = days_in_month[now.tm_mon];
             break;
        case RelativeDateOffset::MONTH:
             if (reldate_is_prev(period))
                 --now.tm_mon;
             else if (reldate_is_next(period))
                 ++now.tm_mon;
-            now.tm_mday = days_in_month[now.tm_mon];
             break;
         case RelativeDateOffset::WEEK:
             if (reldate_is_prev(period))

commit f6c9e63e3d8b130f79c39e6db693541e9fc01ba0
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Nov 14 15:28:22 2021 -0800

    Fix PR comments so far.
    
    Except for gnc-option-date's normalize_tm and set_day_and_time.

diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c
index 0180b62bf..00f8efe38 100644
--- a/gnucash/gnome-utils/gnc-gnome-utils.c
+++ b/gnucash/gnome-utils/gnc-gnome-utils.c
@@ -99,39 +99,6 @@ gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat)
     gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_BOOK_OPTIONS);
 }
 
-#if 0 // Reimplemented in dialog-options.cpp
-void
-gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win)
-{
-    gnc_options_dialog_set_help_cb(win,
-                                (GNCOptionWinCallback)gnc_book_options_help_cb,
-                                NULL);
-}
-
-void
-gnc_options_dialog_set_new_book_option_values (GNCOptionDB *odb)
-{
-    GNCOption *num_source_option;
-    GtkWidget *num_source_is_split_action_button;
-    gboolean num_source_is_split_action;
-
-    if (!odb) return;
-    num_source_is_split_action = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL,
-                                                    GNC_PREF_NUM_SOURCE);
-    if (num_source_is_split_action)
-    {
-        num_source_option = gnc_option_db_get_option_by_name(odb,
-                                                 OPTION_SECTION_ACCOUNTS,
-                                                 OPTION_NAME_NUM_FIELD_SOURCE);
-        num_source_is_split_action_button =
-                                gnc_option_get_gtk_widget (num_source_option);
-        gtk_toggle_button_set_active
-                    (GTK_TOGGLE_BUTTON (num_source_is_split_action_button),
-                        num_source_is_split_action);
-    }
-}
-#endif
-
 static void
 gnc_style_sheet_options_help_cb (GNCOptionWin *win, gpointer dat)
 {
diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp
index b371a7b9a..a8b2a255f 100644
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -1924,13 +1924,13 @@ gnc_main_window_update_radio_button (GncMainWindow *window)
     {
         first_action = static_cast<GtkAction*>(g_slist_last(action_list)->data);
         g_signal_handlers_block_by_func(G_OBJECT(first_action),
-                                        (void*)gnc_main_window_cmd_window_raise,
+                                        (gpointer)gnc_main_window_cmd_window_raise,
                                         window);
         DEBUG("blocked signal on %p, set %p active, window %p", first_action,
               action, window);
         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
         g_signal_handlers_unblock_by_func(G_OBJECT(first_action),
-                                          (void*)gnc_main_window_cmd_window_raise,
+                                          (gpointer)gnc_main_window_cmd_window_raise,
                                           window);
     }
     g_free(action_name);
diff --git a/gnucash/gnome/assistant-hierarchy.cpp b/gnucash/gnome/assistant-hierarchy.cpp
index 54980f102..6562552c4 100644
--- a/gnucash/gnome/assistant-hierarchy.cpp
+++ b/gnucash/gnome/assistant-hierarchy.cpp
@@ -131,8 +131,6 @@ typedef struct
 
 } hierarchy_data;
 
-extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options);
-
 void on_prepare (GtkAssistant  *assistant, GtkWidget *page,
                  hierarchy_data  *data);
 void on_choose_account_categories_prepare (hierarchy_data  *data);
diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp
index 2d47cd0df..ab5888207 100644
--- a/gnucash/gnome/gnc-plugin-page-report.cpp
+++ b/gnucash/gnome/gnc-plugin-page-report.cpp
@@ -1748,7 +1748,7 @@ gnc_plugin_page_report_export_cb( GtkAction *action, GncPluginPageReport *report
 	        gnc_error_dialog (parent, "%s",
 				  _("This report must be upgraded to return a "
 				    "document object with export-string or "
-				    "pexport-error."));
+				    "export-error."));
         }
         result = TRUE;
     }
diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i
index ab8aead4c..ac2b01f4f 100644
--- a/libgnucash/app-utils/app-utils.i
+++ b/libgnucash/app-utils/app-utils.i
@@ -65,10 +65,6 @@ PyObject* SWIG_init (void);
 
 %import "base-typemaps.i"
 
- /* OBSOLETE
-typedef void (*GNCOptionChangeCallback) (gpointer user_data);
-typedef int GNCOptionDBHandle;
- */
 void gnc_prefs_init();
 
 QofBook * gnc_get_current_book (void);
@@ -99,9 +95,6 @@ gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table * table);
 gnc_commodity * gnc_default_currency (void);
 gnc_commodity * gnc_default_report_currency (void);
 
-/* Obsolete: Options are C++ now, no need for convoluted callbacks.
-void gncp_option_invoke_callback(GNCOptionChangeCallback callback, void *data);
-*/
 GNCPrintAmountInfo gnc_default_print_info (gboolean use_symbol);
 GNCPrintAmountInfo gnc_account_print_info (const Account *account,
         gboolean use_symbol);
@@ -128,9 +121,6 @@ gnc_numeric gnc_convert_from_euro(const gnc_commodity * currency,
 time64 gnc_accounting_period_fiscal_start(void);
 time64 gnc_accounting_period_fiscal_end(void);
 
-/* OBSOLETE Options are C++, no SCM generators.
-void gnc_register_kvp_option_generator(QofIdType id_type, SCM generator);
-*/
 %typemap(out) GHashTable * {
   SCM table = scm_c_make_hash_table (g_hash_table_size($1) + 17);
   GHashTableIter iter;
diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp
index 1e6ae7560..f7d17b069 100644
--- a/libgnucash/app-utils/gnc-option-date.cpp
+++ b/libgnucash/app-utils/gnc-option-date.cpp
@@ -426,7 +426,7 @@ reldate_offset(RelativeDatePeriod per)
 static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
 static void
-normalize_tm(struct tm& now)
+normalize_reldate_tm(struct tm& now)
 {
     auto tmp_mon = now.tm_mon + (now.tm_mon < 0 ? 12 :
                                  now.tm_mon > 11 ? -12 : 0);
@@ -453,7 +453,7 @@ normalize_tm(struct tm& now)
 }
 
 static void
-set_day_and_time(struct tm& now, RelativeDateType type)
+reldate_set_day_and_time(struct tm& now, RelativeDateType type)
 {
     if (type == RelativeDateType::START)
     {
@@ -549,8 +549,8 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
             else if (reldate_is_next(period))
                 now.tm_mday += 7;
     }
-    normalize_tm(now);
-    set_day_and_time(now, checked_reldate(period).m_type);
+    normalize_reldate_tm(now);
+    reldate_set_day_and_time(now, checked_reldate(period).m_type);
     return static_cast<time64>(GncDateTime(now));
 }
 
diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp
index 395dee3aa..b87ea4d08 100644
--- a/libgnucash/app-utils/gnc-option-date.hpp
+++ b/libgnucash/app-utils/gnc-option-date.hpp
@@ -127,7 +127,7 @@ const char* gnc_relative_date_storage_string(RelativeDatePeriod);
 
 /**
  * Provide the string representation of a relative date for displaying
- * value to a user. This string is not localizable.
+ * value to a user. This string is localizable.
  *
  * @param period The relative date period.
  * @return A constant string or nullptr if the period is ABSOLUTE. The string's
@@ -158,7 +158,16 @@ const char* gnc_relative_date_description(RelativeDatePeriod);
 RelativeDatePeriod gnc_relative_date_from_storage_string(const char*);
 
 /**
- * Convert a RelativeDatePeriod value to a concrete time64 by applying the value to the current time. For example if it is now 3:15:42 PM local time 3 June, calling this with a period RelativeDatePeriod::ONE_WEEK_AHEAD will return a time64 representing 3:15:42 PM local time 10 June of this year.
+ * Convert a RelativeDatePeriod value to a concrete time64 by applying the value
+ * to the current time.
+ * For example if it is now 3:15:42 PM local time 3 June, calling this with a
+ * period RelativeDatePeriod::ONE_WEEK_AHEAD will return a time64 representing
+ * 3:15:42 PM local time 10 June of this year. Times for START periods are
+ * changed to midnight local time and for END periods to 23:59:59 local time so
+ * for example if the period is instead RelativeDatePeriod::START_THIS_MONTH the
+ * time64 will represent 00:00:00 1 June and if it is
+ * RelativeDatePeriod::END_THIS_MONTH the time64 will be for 23:59:59 30 June,
+ * both in the current time zone.
  *
  * @param period The relative date period to use to calculate the concrete date.
  * @return a time64.
diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index a20970303..5a7919466 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -104,7 +104,7 @@ GncOptionAccountListValue::account_type_list() const noexcept
 {
     if (m_allowed.empty())
         return nullptr;
-    GList* retval;
+    GList* retval{nullptr};
     for (auto type : m_allowed)
         retval = g_list_prepend(retval, GINT_TO_POINTER(type));
     return g_list_reverse(retval);
@@ -170,7 +170,7 @@ GncOptionAccountSelValue::account_type_list() const noexcept
 {
     if (m_allowed.empty())
         return nullptr;
-    GList* retval;
+    GList* retval{nullptr};
     for (auto type : m_allowed)
         retval = g_list_prepend(retval, GINT_TO_POINTER(type));
     return g_list_reverse(retval);
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index f71be8f0c..68e7b5859 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -443,9 +443,14 @@ using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
 /** @class GncOptionMultichoiceValue
  * Multichoice options have a vector of valid options
  * (GncMultichoiceOptionChoices) and validate the selection as being one of
- * those values. The value is the index of the selected item in the vector. The
- * tuple contains three strings, a key, and a display
- * name, which * should be localized at the point of use.
+ * those values. The value is the index of the selected item in the vector.
+
+ * GncMultichoiceOptionEntry is a tuple of two strings and a
+ * GncOptionMultichoiceKeyType value; the first string is the internal value of
+ * the option, the second is the display name that should be localized at the
+ * point of use (so mark it with N_() or (N_ ) when creating the multichoices)
+ * and the third is an enum value indicating whether the key should be
+ * interpreted as a Scheme symbol, a string, or a number.
  *
  *
  */
@@ -910,7 +915,7 @@ template<> inline std::istream&
 operator>> <GncOptionAccountSelValue>(std::istream& iss,
                                    GncOptionAccountSelValue& opt)
 {
-    const Account* value;
+    Account* value{nullptr};
     std::string str;
     std::getline(iss, str, ' ');
     if (!str.empty())

commit 4c43dac1b6b2d2e7840cc4d286c7637bc4c90042
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Nov 13 11:10:17 2021 -0800

    Restore setting swig engine initialization flag.

diff --git a/bindings/guile/gnc-guile-bindings.c b/bindings/guile/gnc-guile-bindings.c
index 77dbf384b..4d40df5cd 100644
--- a/bindings/guile/gnc-guile-bindings.c
+++ b/bindings/guile/gnc-guile-bindings.c
@@ -44,5 +44,6 @@ gnc_guile_bindings_init(void)
         scm_init_sw_core_utils_module();
         scm_init_sw_engine_module();
 
+        is_initialized = 1;
     }
 }

commit cf5da9fffdaf6d4ec703ab46febae8829e36c972
Merge: 984d816a5 96b09ded9
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 12 16:09:00 2021 -0800

    Resolve merge conflicts.

diff --cc gnucash/gnome-utils/gnc-main-window.cpp
index fdff025c1,91cb70620..b371a7b9a
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@@ -2494,11 -2502,11 +2503,11 @@@ main_window_update_page_set_read_only_i
      if (GTK_IS_EVENT_BOX(tab_widget))
          tab_widget = gtk_bin_get_child (GTK_BIN(tab_widget));
  
 +    children = gtk_container_get_children (GTK_CONTAINER(tab_widget));
      /* For each, walk the list of container children to get image widget */
 -    for (children = gtk_container_get_children (GTK_CONTAINER(tab_widget));
 -            children; children = children->next)
 +    for (GList *child = children; child; child = g_list_next (child))
      {
-         GtkWidget *widget = child->data;
 -        GtkWidget *widget = static_cast<GtkWidget*>(children->data);
++        GtkWidget *widget = static_cast<GtkWidget*>(child->data);
          if (GTK_IS_IMAGE(widget))
              image = widget;
      }
diff --cc gnucash/gnome/assistant-hierarchy.cpp
index 54873226d,6315e78a9..54980f102
--- a/gnucash/gnome/assistant-hierarchy.cpp
+++ b/gnucash/gnome/assistant-hierarchy.cpp
@@@ -166,12 -173,10 +173,12 @@@ gnc_hierarchy_destroy_cb (GtkWidget *ob
      hash = data->balance_hash;
      if (hash)
      {
-         g_hash_table_foreach (hash, destroy_hash_helper, NULL);
+         g_hash_table_foreach (hash, destroy_hash_helper, nullptr);
          g_hash_table_destroy (hash);
-         data->balance_hash = NULL;
+         data->balance_hash = nullptr;
      }
 +
 +    g_free (data->gnc_accounts_dir);
  }
  
  static gnc_numeric
diff --cc libgnucash/app-utils/CMakeLists.txt
index 2b27989bc,68379509b..e7b77fcb1
--- a/libgnucash/app-utils/CMakeLists.txt
+++ b/libgnucash/app-utils/CMakeLists.txt
@@@ -62,8 -69,12 +69,12 @@@ set (app_utils_SOURCE
    gnc-entry-quickfill.c
    gnc-euro.c
    gnc-exp-parser.c
 -  gnc-gsettings.c
 +  gnc-gsettings.cpp
    gnc-helpers.c
+   gnc-option-date.cpp
+   gnc-option.cpp
+   gnc-option-impl.cpp
+   gnc-optiondb.cpp
    gnc-prefs-utils.c
    gnc-sx-instance-model.c
    gnc-state.c
diff --cc libgnucash/app-utils/options.scm
index 8481572cb,81bbb26a6..0cdb63310
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@@ -28,2098 -28,91 +28,89 @@@
  (use-modules (gnucash utilities))
  (use-modules (srfi srfi-1))
  (use-modules (ice-9 regex))
+ (use-modules (ice-9 format))
+ (use-modules (ice-9 pretty-print))
  
- (export gnc:color->html)
- (export gnc:color-option->hex-string)
- (export gnc:color-option->html)
- (export gnc:currency-accounting-option-get-curr-doc-string)
- (export gnc:currency-accounting-option-get-default-curr)
- (export gnc:currency-accounting-option-get-default-policy)
- (export gnc:currency-accounting-option-get-gain-loss-account-doc-string)
- (export gnc:currency-accounting-option-get-policy-doc-string)
- (export gnc:currency-accounting-option-selected-currency)
- (export gnc:currency-accounting-option-selected-gain-loss-account)
- (export gnc:currency-accounting-option-selected-method)
- (export gnc:currency-accounting-option-selected-policy)
- (export gnc:date-option-absolute-time)
- (export gnc:date-option-get-subtype)
- (export gnc:date-option-relative-time)
- (export gnc:date-option-show-time?)
- (export gnc:date-option-value-type)
- (export gnc:dateformat-get-format)
- (export gnc:generate-restore-forms)
- (export gnc:get-rd-option-data-rd-list)
- (export gnc:get-rd-option-data-show-time)
- (export gnc:get-rd-option-data-subtype)
- (export gnc:lookup-option)
- (export gnc:make-account-list-limited-option)
- (export gnc:make-account-list-option)
- (export gnc:make-account-sel-limited-option)
- (export gnc:make-account-sel-option)
- (export gnc:make-budget-option)
- (export gnc:make-color-option)
- (export gnc:make-commodity-option)
- (export gnc:make-complex-boolean-option)
- (export gnc:make-currency-option)
- (export gnc:make-date-option)
- (export gnc:make-dateformat-option)
- (export gnc:make-font-option)
- (export gnc:make-internal-option)
- (export gnc:make-list-option)
- (export gnc:make-multichoice-callback-option)
- (export gnc:make-multichoice-option)
- (export gnc:make-number-plot-size-option)
- (export gnc:make-number-range-option)
- (export gnc:make-option)
- (export gnc:make-pixmap-option)
- (export gnc:make-query-option)
- (export gnc:make-radiobutton-callback-option)
- (export gnc:make-radiobutton-option)
- (export gnc:make-simple-boolean-option)
- (export gnc:make-string-option)
- (export gnc:make-text-option)
- (export gnc:multichoice-list-lookup)
- (export gnc:new-options)
- (export gnc:option-data)
- (export gnc:option-data-fns)
- (export gnc:option-default-getter)
- (export gnc:option-default-value)
- (export gnc:option-documentation)
- (export gnc:option-generate-restore-form)
- (export gnc:option-get-value)
- (export gnc:option-getter)
- (export gnc:option-index-get-name)
- (export gnc:option-index-get-value)
- (export gnc:option-kvp->scm)
- (export gnc:option-make-internal!)
- (export gnc:option-name)
- (export gnc:option-number-of-indices)
- (export gnc:option-scm->kvp)
- (export gnc:option-section)
- (export gnc:option-set-changed-callback)
- (export gnc:option-set-default-value)
- (export gnc:option-set-value)
- (export gnc:option-setter)
- (export gnc:option-sort-tag)
- (export gnc:option-strings-getter)
- (export gnc:option-type)
- (export gnc:option-value)
- (export gnc:option-value-get-index)
- (export gnc:option-value-validator)
- (export gnc:option-widget-changed-proc)
- (export gnc:options-clear-changes)
- (export gnc:options-copy-values)
- (export gnc:options-for-each)
- (export gnc:options-for-each-general)
- (export gnc:options-get-default-section)
- (export gnc:options-kvp->scm)
- (export gnc:options-make-date-interval!)
- (export gnc:options-make-end-date!)
- (export gnc:options-register-c-callback)
- (export gnc:options-register-callback)
- (export gnc:options-run-callbacks)
- (export gnc:options-scm->kvp)
- (export gnc:options-set-default-section)
- (export gnc:options-touch)
- (export gnc:options-unregister-callback-id)
- (export gnc:plot-size-option-value)
- (export gnc:plot-size-option-value-type)
- (export gnc:register-option)
- (export gnc:restore-form-generator)
- (export gnc:send-options)
- (export gnc:set-option-kvp->scm)
- (export gnc:set-option-scm->kvp)
- (export gnc:unregister-option)
- (export gnc:value->string)
- 
- (export gnc:*option-name-trading-accounts*)
- (export gnc:*option-name-book-currency*)
- (export gnc:*option-section-accounts*)
- (export gnc:*option-name-default-gains-policy*)
- (export gnc:*option-name-default-gain-loss-account*)
- 
- (define gnc:*option-section-accounts* OPTION-SECTION-ACCOUNTS)
- (define gnc:*option-name-trading-accounts* OPTION-NAME-TRADING-ACCOUNTS)
- (define gnc:*option-name-currency-accounting* OPTION-NAME-CURRENCY-ACCOUNTING)
- (define gnc:*option-name-book-currency* OPTION-NAME-BOOK-CURRENCY)
- (define gnc:*option-name-default-gains-policy* OPTION-NAME-DEFAULT-GAINS-POLICY)
- (define gnc:*option-name-default-gain-loss-account*
-   OPTION-NAME-DEFAULT-GAINS-LOSS-ACCT-GUID)
- 
- (define (gnc:option-get-value book category key)
-   (define acc (if (pair? key) cons list))
-   (qof-book-get-option book (acc category key)))
- 
- (define (rpterror-earlier type newoption fallback)
-   ;; Translators: the 3 ~a below refer to (1) option type (2) unknown
-   ;; new option name, (3) fallback option name. The order is
-   ;; important, and must not be changed.
-   (let* ((template (N_ "This report was saved using a later version of \
- GnuCash. One of the newer ~a options '~a' is not available, fallback to \
- the option '~a'."))
-          (console-msg (format #f template type newoption fallback))
-          (ui-msg (format #f (G_ template) type newoption fallback)))
-     (gnc:gui-warn console-msg ui-msg)))
- 
- (define (gnc:make-option
-          ;; The category of this option
-          section
-          name
-          ;; The sort-tag determines the relative ordering of options in
-          ;; this category. It is used by the gui for display.
-          sort-tag
-          type
-          documentation-string
-          getter
-          ;; The setter is responsible for ensuring that the value is valid.
-          setter
-          default-getter
-          ;; Restore form generator should generate an ascii representation
-          ;; of a function taking one argument. The argument will be an
-          ;; option. The function should restore the option to the original
-          ;; value.
-          generate-restore-form
-          ;; the scm->kvp and kvp->scm functions should save and load
-          ;; the option to the book.  The arguments to these function will be
-          ;; a book and a base key-path list for this option.
-          scm->kvp
-          kvp->scm
-          ;; Validation func should accept a value and return (#t value)
-          ;; on success, and (#f "failure-message") on failure. If #t,
-          ;; the supplied value will be used by the gui to set the option.
-          value-validator
-          ;;; free-form storage depending on type.
-          option-data 
-          ;; If this is a "multiple choice" type of option,
-          ;; this should be a vector of the following five functions:
-          ;; 
-          ;; Function 1: taking no arguments, giving the number of choices
-          ;;
-          ;; Function 2: taking one argument, a non-negative integer, that
-          ;; returns the scheme value (usually a symbol) matching the
-          ;; nth choice
-          ;;
-          ;; Function 3: taking one argument, a non-negative integer,
-          ;; that returns the string matching the nth choice
-          ;;
-          ;; Function 4: #f, this was the individual tool tip and not used now
-          ;;
-          ;; Function 5: giving a possible value and returning the index
-          ;; if an option doesn't use these,  this should just be a #f
-          option-data-fns
-          ;; This function should return a list of all the strings
-          ;; in the option other than the section, name, (define
-          ;; (list-lookup list item) and documentation-string that
-          ;; might be displayed to the user (and thus should be
-          ;; translated).
-          strings-getter
-          ;; This function will be called when the GUI representation
-          ;; of the option is changed.  This will normally occur before
-          ;; the setter is called, because setters are only called when
-          ;; the user selects "OK" or "Apply".  Therefore, this
-          ;; callback shouldn't be used to make changes to the actual
-          ;; options database.
-          option-widget-changed-proc)
-   (let ((changed-callback #f))
-     (vector section
-             name
-             sort-tag
-             type
-             documentation-string
-             getter            
-             (lambda args
-               (apply setter args)
-               (if changed-callback (changed-callback)))
-             default-getter
-             generate-restore-form
-             scm->kvp
-             kvp->scm
-             value-validator
-             option-data
-             option-data-fns
-             (lambda (callback) (set! changed-callback callback))
-             strings-getter
-             option-widget-changed-proc)))
- 
- (define (gnc:option-section option)
-   (vector-ref option 0))
- (define (gnc:option-name option)
-   (vector-ref option 1))
- (define (gnc:option-sort-tag option)
-   (vector-ref option 2))
- (define (gnc:option-type option)
-   (vector-ref option 3))
- (define (gnc:option-documentation option)
-   (vector-ref option 4))
- (define (gnc:option-getter option)
-   (vector-ref option 5))
- (define (gnc:option-setter option)
-   (vector-ref option 6))
- (define (gnc:option-default-getter option)
-   (vector-ref option 7))
- (define (gnc:option-generate-restore-form option)
-   (vector-ref option 8))
- (define (gnc:option-scm->kvp option)
-   (vector-ref option 9))
- (define (gnc:set-option-scm->kvp option v)
-   (vector-set! option 9 v))
- (define (gnc:option-kvp->scm option)
-   (vector-ref option 10))
- (define (gnc:set-option-kvp->scm option v)
-   (vector-set! option 10 v))
- (define (gnc:option-value-validator option)  
-   (vector-ref option 11))
- (define (gnc:option-data option)
-   (vector-ref option 12))
- (define (gnc:option-data-fns option)
-   (vector-ref option 13))
- 
- (define (gnc:option-set-changed-callback option callback)
-  (let ((cb-setter (vector-ref option 14)))
-     (cb-setter callback)))
- (define (gnc:option-strings-getter option)
-   (vector-ref option 15))
- (define (gnc:option-widget-changed-proc option)
-   (vector-ref option 16))
- 
- (define (gnc:option-value option)
-   (let ((getter (gnc:option-getter option)))
-     (getter)))
- 
- (define (gnc:option-set-value option value)
-   (let ((setter (gnc:option-setter option)))
-     (setter value)))
- 
- (define (gnc:option-index-get-name option index)
-   (let* ((option-data-fns (gnc:option-data-fns option))
-          (name-fn (vector-ref option-data-fns 2)))
-     (name-fn index)))
- 
- (define (gnc:option-index-get-value option index)
-   (let* ((option-data-fns (gnc:option-data-fns option))
-          (name-fn (vector-ref option-data-fns 1)))
-     (name-fn index)))
- 
- (define (gnc:option-value-get-index option value)
-   (let* ((option-data-fns (gnc:option-data-fns option))
-          (name-fn (vector-ref option-data-fns 4)))
-     (name-fn value)))
- 
- (define (gnc:option-number-of-indices option)
-   (let* ((option-data-fns (gnc:option-data-fns option))
-          (name-fn (vector-ref option-data-fns 0)))
-     (name-fn)))
- 
- (define (gnc:option-default-value option)
-   (let ((getter (gnc:option-default-getter option)))
-     (getter)))
- 
- ;; Attention: this function can only be used with restrictions
- ;; - only during option generation, not in arbitrary code
- ;; - only for option types for which no conversion is required
- ;;   between default-value and value. In the various gnc:make-option
- ;;   functions below this is ok when
- ;;   1. there's (value default-value) in the let* call
- ;;   2. default-getter is set to (lambda() default-value)
- (define (gnc:option-set-default-value option default-value)
-   (vector-set! option 7 (lambda() default-value))
-   (gnc:option-set-value option default-value))
- 
- 
- (define (gnc:restore-form-generator value->string)
-   (lambda ()
-     (string-append "(lambda (o) (if o (gnc:option-set-value o "
-                    (value->string) ")))")))
- 
- (define (gnc:value->string value)
-   (format #f "~s" value))
- 
- (define (gnc:make-string-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value)
-   (let* ((value default-value)
-          (value->string (lambda () (gnc:value->string value))))
-     (gnc:make-option
-      section name sort-tag 'string documentation-string
-      (lambda () value)
-      (lambda (x) (set! value x))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) (qof-book-set-option b value p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (string? v))
-              (set! value v))))
-      (lambda (x)
-        (cond ((string? x)(list #t x))
-              (else (list #f "string-option: not a string"))))
-      #f #f #f #f)))
- 
- (define (gnc:make-text-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value)
-   (let* ((value default-value)
-          (value->string (lambda () (gnc:value->string value))))
-     (gnc:make-option
-      section name sort-tag 'text documentation-string
-      (lambda () value)
-      (lambda (x) (set! value x))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) (qof-book-set-option b value p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (string? v))
-              (set! value v))))
-      (lambda (x)
-        (cond ((string? x)(list #t x))
-              (else (list #f "text-option: not a string"))))
-      #f #f #f #f)))
- 
- ;;; font options store fonts as strings a la the X Logical
- ;;; Font Description. You should always provide a default
- ;;; value, as currently there seems to be no way to go from
- ;;; an actual font to a logical font description, and thus
- ;;; there is no way for the gui to pick a default value.
- 
- (define (gnc:make-font-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value)
-   (let* ((value default-value)
-          (value->string (lambda () (gnc:value->string value))))
-     (gnc:make-option
-      section
-      name
-      sort-tag
-      'font
-      documentation-string
-      (lambda () value)
-      (lambda (x) (set! value x))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)     
-      (lambda (b p) (qof-book-set-option b value p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (string? v))
-              (set! value v))))
-      (lambda (x)
-        (cond ((string? x)(list #t x))
-              (else (list #f "font-option: not a string"))))
-      #f #f #f #f)))
- 
- ;; currency options use a specialized widget for entering currencies
- ;; in the GUI implementation.
- (define (gnc:make-currency-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value)
- 
-   (define (currency->scm currency)
-     (if (string? currency)
-         currency
-         (gnc-commodity-get-mnemonic currency)))
- 
-   (define (scm->currency currency)
-     (if (string? currency)
-         (gnc-commodity-table-lookup
-          (gnc-commodity-table-get-table (gnc-get-current-book))
-          GNC_COMMODITY_NS_CURRENCY currency)
-         currency))
- 
-    (let* ((value (currency->scm default-value))
-           (value->string (lambda () (gnc:value->string value))))
-      (gnc:make-option
-       section name sort-tag 'currency documentation-string
-       (lambda ()  (scm->currency value))
-       (lambda (x) (set! value (currency->scm x)))
-       (lambda ()  (scm->currency default-value))
-       (gnc:restore-form-generator value->string)
-       (lambda (b p) (qof-book-set-option b value p))
-       (lambda (b p)
-         (let ((v (qof-book-get-option b p)))
-           (if (and v (string? v))
-               (set! value v))))
-       (lambda (x) (list #t x))
-       #f #f #f #f)))
- 
- ;; budget option
- ;; TODO: need to double-check this proc (dates back to r11545 or eariler)
- ;;
- ;; Always takes/returns a budget
- ;; Stores the GUID in the KVP
- ;;
- (define (gnc:make-budget-option
-          section
-          name
-          sort-tag
-          documentation-string)
- 
-   (let* ((initial-budget (gnc-budget-get-default (gnc-get-current-book)))
- 	 (selection-budget initial-budget)
-          )
- 
-     (gnc:make-option
-      section 
-      name 
-      sort-tag 
-      'budget 
-      documentation-string
- 
-      ;; getter -- Return a budget pointer
-      (lambda () 
-        selection-budget)
- 
-      ;; setter -- takes a budget
-      (lambda (x)
-        (set! selection-budget x))
- 
-      ;; default-getter
-      ;; Default now is #f so saving is independent of book-level default
-      (lambda ()
-        #f)
- 
-      ;; generate-restore-form
-      ;; "return 'ascii represention of a function'
-      ;; that will set the option passed as its lone parameter
-      ;; to the value it was when the picker was first displayed"
-      ;;
-      ;; *This* is used to restore reports, not the KVP -- and is stored as text
-      ;; This does not run in closure with direct access to the option's
-      ;; internal variables, so the setter generally gets used
-      (lambda () 
-        (string-append
- 	"(lambda (option) "
- 	"(if option ((gnc:option-setter option) "
- 	"(gnc-budget-lookup "
- 	(gnc:value->string (gncBudgetGetGUID selection-budget))
- 	" (gnc-get-current-book)))))"))
- 
-      ;; scm->kvp -- commit the change
-      ;; b -- book;  p -- key-path
-      (lambda (b p) 
-        (qof-book-set-option 
- 	b (gncBudgetGetGUID selection-budget) p))
- 
-      ;; kvp->scm -- get the stored value
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (string? v))
- 	     (begin 
- 	       (set! selection-budget (gnc-budget-lookup v (gnc-get-current-book)))))))
- 
-      ;; value-validator -- returns (#t value) or (#f "failure message")
-      ;; As no user-generated input, this legacy hard-wire is probably ok
-      (lambda (x) 
-        (list #t x))
- 
-      ;; option-data
-      #f
-      
-      ;; option-data-fns -- used for multi-pick (this isn't one), or #f
-      ;; Vector of five functions
-      ;; 1) ()      => number of choices
-      ;; 2) (n)     => key for the nth choice
-      ;; 3) (n)     => string for the nth choice
-      ;; 4) (n)     => description for the nth choice
-      ;; 5) (key)   => n (assuming this is the reverse key lookup)
-      #f
- 
-      ;; strings-getter -- list of all translatable strings in the option
-      #f
- 
-      ;; options-widget-changed-proc -- callback for what it sounds like
-      #f
-      
-      ))) ;; completes gnc:make-budget-option
- 
- 
- ;; commodity options use a specialized widget for entering commodities
- ;; in the GUI implementation.
- (define (gnc:make-commodity-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value)
- 
-   (define (commodity->scm commodity)
-     (if (string? commodity)
-         (list 'commodity-scm
-               GNC_COMMODITY_NS_CURRENCY
-               commodity)
-         (list 'commodity-scm
-               (gnc-commodity-get-namespace commodity)
-               (gnc-commodity-get-mnemonic commodity))))
- 
-   (define (scm->commodity scm)
-     (gnc-commodity-table-lookup
-      (gnc-commodity-table-get-table (gnc-get-current-book))
-      (cadr scm) (caddr scm)))
- 
-    (let* ((value (commodity->scm default-value))
-           (value->string (lambda ()
-                            (string-append "'" (gnc:value->string value)))))
-      (gnc:make-option
-       section name sort-tag 'commodity documentation-string
-       (lambda () (scm->commodity value))
-       (lambda (x) (if (and (pair? x) (eqv? (car x) 'commodity-scm))
-                       (set! value x)
-                       (set! value (commodity->scm x))))
-       (lambda () default-value)
-       (gnc:restore-form-generator value->string)
-       (lambda (b p) 
-         (qof-book-set-option b (cadr value) (append p '("ns")))
-         (qof-book-set-option b (caddr value) (append p '("monic"))))
-       (lambda (b p)
-         (let ((ns (qof-book-get-option b (append p '("ns"))))
-               (monic (qof-book-get-option b (append p '("monic")))))
-           (if (and ns monic (string? ns) (string? monic))
-               (set! value (list 'commodity-scm ns monic)))))
-       (lambda (x) (list #t x))
-       #f #f #f #f)))
- 
- 
- (define (gnc:make-simple-boolean-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value)
-   (gnc:make-complex-boolean-option section
-                                    name
-                                    sort-tag
-                                    documentation-string
-                                    default-value
-                                    #f 
-                                    #f))
- 
- ;; Complex boolean options are the same as simple boolean options (see
- ;; above), with the addition of two function arguments. (If both of
- ;; them are #f, you have exactly a simple-boolean-option.) Both
- ;; functions should expect one boolean argument.  When the option's
- ;; value is changed, the function option-widget-changed-cb will be
- ;; called with the new option value at the time that the GUI widget
- ;; representing the option is changed, and the function
- ;; setter-function-called-cb will be called when the option's setter
- ;; is called (that is, when the user selects "OK" or "Apply").
- 
- ;; The option-widget-changed-cb is tested for procedurehood before
- ;; it is called, so it is not validated to be a procedure here.
- ;; However, since there could be an option-widget-changed-cb but not
- ;; a setter-function-called-cb, the procedurehood of the
- ;; setter-function-called-cb is checked here.
- (define (gnc:make-complex-boolean-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          setter-function-called-cb
-          option-widget-changed-cb)
-   (let* ((value default-value)
-          (value->string (lambda () (gnc:value->string value))))
-     (gnc:make-option
-      section name sort-tag 'boolean documentation-string
-      (lambda () value)
-      (lambda (x) (set! value x)
-              (if (procedure? setter-function-called-cb)
-                  (setter-function-called-cb x)))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) (qof-book-set-option b
- 		    ;; As no boolean KvpValue exists, as a workaround
- 		    ;; we store the string "t" for TRUE and "f" for
- 		    ;; FALSE in a string KvpValue.
-                     (if value "t" "f") 
-                     p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
- 	 ;; As no boolean KvpValue exists, as a workaround we store
- 	 ;; the string "t" for TRUE and "f" for FALSE.
-          (cond ((equal? v "t") (set! v #t))
-                ((equal? v "f") (set! v #f)))
-          (if (and v (boolean? v) (not (equal? v default-value)))
-              (set! value v))))
-      (lambda (x)
-        (if (boolean? x)
-            (list #t x)
-            (list #f "boolean-option: not a boolean")))
-      #f #f #f (and option-widget-changed-cb
-                    (lambda (x) (option-widget-changed-cb x))))))
- 
- 
- (define (gnc:make-pixmap-option 
-          section name sort-tag doc-string
-          default-value)
-   (let* ((value default-value))
-     (gnc:make-option
-      section name sort-tag 'pixmap doc-string
-      (lambda ()  value)
-      (lambda (x) (set! value x))
-      (lambda () default-value)
-      (gnc:restore-form-generator  (lambda () (gnc:value->string value)))
-      #f
-      #f
-      (lambda (x)
-        (if (string? x)
-            (begin 
-              (list #t x))
-            (begin 
-              (list #f "pixmap-option: not a string"))))
-      #f #f #f #f)))
- 
- ;; show-time is boolean
- ;; subtype should be one of 'relative 'absolute or 'both
- ;; if subtype is 'absolute then relative-date-list should be #f
- ;; relative-date-list should be the list of relative dates permitted
- ;; gnc:all-relative-dates contains a list of all relative dates.
- 
- (define (gnc:make-date-option
-          section
-          name
-          sort-tag 
-          documentation-string
-          default-getter
-          show-time
-          subtype
-          relative-date-list)
-   (define (date-legal date)
-     (and (pair? date)
-          (or
-           (and (eq? 'relative (car date)) (symbol? (cdr date)))
-           (and (eq? 'absolute (car date))
-                (or (and (pair? (cdr date))   ; we can still accept
-                         (exact? (cadr date)) ; old-style timepairs
-                         (exact? (cddr date)))
-                    (and (number? (cdr date))
-                         (exact? (cdr date))))))))
-   (define (maybe-convert-to-time64 date)
-     ;; compatibility shim. this is triggered only when date is type
-     ;; (cons 'absolute (cons sec nsec)) - we'll convert to
-     ;; (cons 'absolute sec). this shim must always be kept for gnucash
-     ;; to reload saved reports, or reload reports launched at startup,
-     ;; which had been saved as timepairs.
-     (if (pair? (cdr date))
-         (cons (car date) (cadr date))
-         date))
-   (define (list-lookup full-list item)
-     (or (list-index (lambda (i) (eq? i item)) full-list)
-         (begin
-           (rpterror-earlier "date" item (car full-list))
-           0)))
-   (if show-time
-       (issue-deprecation-warning
-        (format #f "Date options with time of day values are deprecated and will be removed in GnuCash 5.")))
- 
-  (let* ((value (default-getter))
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-      (gnc:make-option
-      section name sort-tag 'date documentation-string
-      (lambda () value)
-      (lambda (date)
-        (if (date-legal date)
-            (set! value (maybe-convert-to-time64 date))
-            (gnc:error "Illegal date value set:" date)))
-      default-getter
-      (gnc:restore-form-generator value->string)
-      (lambda (b p)
-        (qof-book-set-option b (symbol->string (car value))
-                                        (append p '("type")))
-        (qof-book-set-option b
-                                        (if (symbol? (cdr value))
-                                            (symbol->string (cdr value))
-                                            (cdr value))
-                                        (append p '("value"))))
-      (lambda (b p)
-        (let ((t (qof-book-get-option b (append p '("type"))))
-              (v (qof-book-get-option b (append p '("value")))))
-          (if (and t v (string? t))
-              (set! value (cons (string->symbol t)
-                                (if (string? v) (string->symbol v) v))))))
-      (lambda (date)
-        (if (date-legal date)
-            (list #t date)
-            (list #f "date-option: illegal date")))
-      (vector subtype show-time relative-date-list) 
-      (vector (lambda () (length relative-date-list))
-              (lambda (x) (list-ref relative-date-list x))
-              (lambda (x) (gnc:get-relative-date-string
-                           (list-ref relative-date-list x)))
-              (lambda (x) (gnc:get-relative-date-desc
-                           (list-ref relative-date-list x)))
-              (lambda (x) (list-lookup relative-date-list x)))
-      #f
-      #f)))
- 
- (define (gnc:get-rd-option-data-subtype option-data)
-   (vector-ref option-data 0))
- 
- (define (gnc:get-rd-option-data-show-time option-data)
-   (vector-ref option-data 1))
- 
- (define (gnc:get-rd-option-data-rd-list option-data)
-   (vector-ref option-data 2))
- 
- (define (gnc:date-option-get-subtype option)
-   (if (eq? (gnc:option-type option) 'date)
-       (gnc:get-rd-option-data-subtype (gnc:option-data option))
-       (gnc:error "Not a date option")))
- 
- (define (gnc:date-option-show-time? option)
-   (if (eq? (gnc:option-type option) 'date)
-       (gnc:get-rd-option-data-show-time (gnc:option-data option))
-       (gnc:error "Not a date option")))
- 
- (define (gnc:date-option-value-type option-value)
-   (car option-value))
- 
- (define (gnc:date-option-absolute-time option-value)
-   (if (eq? (car option-value) 'absolute)
-       (cdr option-value)
-       (gnc:get-absolute-from-relative-date (cdr option-value))))
- 
- (define (gnc:date-option-relative-time option-value)
-   (if (eq? (car option-value) 'absolute)
-       #f
-       (cdr option-value)))
- 
- ;; Just like gnc:make-account-list-limited-option except it
- ;; does not limit the types of accounts that are available
- ;; to the user.
- (define (gnc:make-account-list-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-getter
-          value-validator
-          multiple-selection)
- 
-   (gnc:make-account-list-limited-option
-    section name sort-tag documentation-string
-    default-getter value-validator multiple-selection '()))
- 
- ;; account-list options use the option-data as a pair; the car is
- ;; a boolean value, the cdr is a list of account-types. If the boolean is
- ;; true, the gui should allow the user to select multiple accounts.
- ;; If the cdr is an empty list, then all account types are shown.
- ;; Internally, values are always a list of guids. Externally, both
- ;; guids and account pointers may be used to set the value of the
- ;; option. The option always returns a list of account pointers.
- (define (gnc:make-account-list-limited-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-getter
-          value-validator
-          multiple-selection
-          acct-type-list)
- 
-   (define (convert-to-guid item)
-     (if (string? item)
-         item
-         (gncAccountGetGUID item)))
- 
-   (define (convert-to-account item)
-     (if (string? item)
-         (xaccAccountLookup item (gnc-get-current-book))
-         item))
- 
-   (let* ((option (map convert-to-guid (default-getter)))
-          (option-set #f)
-          (getter (lambda () (map convert-to-account
-                                  (if option-set
-                                      option
-                                      (default-getter)))))
-          (value->string (lambda ()
-                           (string-append
-                            "'" (gnc:value->string (if option-set option #f)))))
-          (validator
-           (if (not value-validator)
-               (lambda (account-list) (list #t account-list))
-               (lambda (account-list)
-                 (value-validator (map convert-to-account account-list))))))
-     (gnc:make-option
-      section name sort-tag 'account-list documentation-string getter
-      (lambda (account-list)
-        (if (or (not account-list) (null? account-list)) 
-            (set! account-list (default-getter)))
-        (set! account-list
-              (filter (lambda (x) (if (string? x)
-                                      (xaccAccountLookup
-                                       x (gnc-get-current-book))
-                                      x)) account-list))
-        (let* ((result (validator account-list))
-               (valid (car result))
-               (value (cadr result)))
-          (if valid
-              (begin
-                (set! option (map convert-to-guid value))
-                (set! option-set #t))
-              (gnc:error "Illegal account list value set"))))
-      (lambda () (map convert-to-account (default-getter)))
-      (gnc:restore-form-generator value->string)
-      (lambda (b p)
-        (when option-set
-          (qof-book-set-option b (length option) (append p '("len")))
-          (let loop ((option option) (idx 0))
-            (unless (null? option)
-              (qof-book-set-option
-               b (car option) (append p (list (format #f "acc~a" idx))))
-              (loop (cdr option) (1+ idx))))))
-      (lambda (b p)
-        (let ((len (qof-book-get-option b (append p '("len")))))
-          (when (and len (integer? len))
-            (set! option
-              (map
-               (lambda (idx)
-                 (qof-book-get-option b (append p (list (format #f "acc~a" idx)))))
-               (iota len)))
-            (set! option-set #t))))
-      validator
-      (cons multiple-selection acct-type-list) #f #f #f)))
- 
- ;; Just like gnc:make-account-sel-limited-option except it
- ;; does not limit the types of accounts that are available
- ;; to the user.
- (define (gnc:make-account-sel-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-getter
-          value-validator)
- 
-   (gnc:make-account-sel-limited-option
-    section name sort-tag documentation-string
-    default-getter value-validator '()))
- 
- ;; account-sel options use the option-data as a pair; the car is
- ;; ignored, the cdr is a list of account-types. If the cdr is an empty
- ;; list, then all account types are shown.  Internally, the value is
- ;; always a guid.  Externally, both guids and account pointers may be
- ;; used to set the value of the option. The option always returns the
- ;; "current" account pointer.
- (define (gnc:make-account-sel-limited-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-getter
-          value-validator
-          acct-type-list)
- 
-   (define (convert-to-guid item)
-     (if (string? item)
-         item
-         (gncAccountGetGUID item)))
- 
-   (define (convert-to-account item)
-     (if (string? item)
-         (xaccAccountLookup item (gnc-get-current-book))
-         item))
- 
-   (define (find-first-account)
-     (define (find-first account-list)
-       (if (null? account-list)
-           '()
-           (let* ((this-account (car account-list))
-                  (account-type (xaccAccountGetType this-account)))
-             (if (if (null? acct-type-list)
-                     #t
-                     (member account-type acct-type-list))
-                 this-account
-                 (find-first (cdr account-list))))))
- 
-     (let* ((current-root (gnc-get-current-root-account))
-            (account-list (gnc-account-get-descendants-sorted current-root)))
-       (find-first account-list)))
-   
-   (define (get-default)
-     (if default-getter
-         (default-getter)
-         (find-first-account)))
- 
-   (let* ((option (convert-to-guid (get-default)))
-          (option-set #f)
-          (getter (lambda () (convert-to-account
-                              (if option-set
-                                  option
-                                  (get-default)))))
-          (value->string (lambda ()
-                           (string-append
-                             (gnc:value->string (if option-set option #f)))))
-          (validator
-           (if (not value-validator)
-               (lambda (account) (list #t account))
-               (lambda (account)
-                 (value-validator (convert-to-account account))))))
-     (gnc:make-option
-      section name sort-tag 'account-sel documentation-string getter
-      (lambda (account)
-        (if (or (not account) (null? account)) (set! account (get-default)))
-        (set! account (convert-to-account account))
-        (let* ((result (validator account))
-               (valid (car result))
-               (value (cadr result)))
-          (if valid
-              (begin
-                (set! option (convert-to-guid value))
-                (set! option-set #t))
-              (gnc:error "Illegal account value set"))))
-      (lambda () (convert-to-account (get-default)))
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) (qof-book-set-option b option p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (string? v))
-              (set! option v))))
-      validator
-      (cons #f acct-type-list) #f #f #f)))
- 
- (define (gnc:multichoice-list-lookup full-lst item)
-   (or (list-index (lambda (i) (eq? (vector-ref i 0) item)) full-lst)
-       (begin
-         (rpterror-earlier "multichoice" item (car full-lst))
-         0)))
- 
- (define (check-ok-values ok-values fn)
-   (for-each
-    (lambda (ok-value)
-      (when (> (vector-length ok-value) 2)
-        (issue-deprecation-warning
-         (format #f "~a: the tooltip in ~a is not supported anymore. Please remove." fn ok-value))))
-    ok-values))
- 
- ;; multichoice options use the option-data as a list of vectors.
- ;; Each vector contains a permissible value (scheme symbol), a
- ;; name, and a description string.
- (define (gnc:make-multichoice-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          ok-values)
-   (gnc:make-multichoice-callback-option section
-                                         name
-                                         sort-tag
-                                         documentation-string
-                                         default-value
-                                         ok-values
-                                         #f
-                                         #f))
- 
- ;; The multichoice-option with callback function is the same as the
- ;; usual multichoice options (see above), with the addition of two
- ;; function arguments. (If both of them are #f, you have exactly a
- ;; multichoice-option.) Both functions should expect one argument.
- ;; When the option's value is changed, the function
- ;; option-widget-changed-cb will be called with the new option value
- ;; at the time that the GUI widget representing the option is changed,
- ;; and the function setter-function-called-cb will be called when the
- ;; option's setter is called (that is, when the user selects "OK" or
- ;; "Apply").
- (define (gnc:make-multichoice-callback-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          ok-values
-          setter-function-called-cb
-          option-widget-changed-cb)
-   (define (multichoice-legal val p-vals)
-     (cond ((null? p-vals) #f)
-           ((eq? val (vector-ref (car p-vals) 0)) #t)
-           (else (multichoice-legal val (cdr p-vals)))))
- 
-   (define (multichoice-strings p-vals)
-     (if (null? p-vals)
-         '()
-         (cons (vector-ref (car p-vals) 1)
-               (multichoice-strings (cdr p-vals)))))
- 
-   (check-ok-values ok-values "gnc:make-multichoice-[callback-]option")
- 
-   (let* ((value default-value)
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name sort-tag 'multichoice documentation-string
-      (lambda () value)
-      (lambda (x)
-        (cond
-         ((and (equal? section "Display")
-               (equal? name "Parent account subtotals")
-               (equal? x 'canonically-tabbed))
-          (gnc:warn "canonically-tabbed obsolete. switching to 't")
-          (set! value 't))
-         ((not (multichoice-legal x ok-values))
-          (rpterror-earlier "multichoice" x default-value))
-         (else
-          (set! value x)
-          (if (procedure? setter-function-called-cb)
-              (setter-function-called-cb x)))))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) (qof-book-set-option b (symbol->string value) p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (string? v))
-              (set! value (string->symbol v)))))
-      (lambda (x)
-        (if (multichoice-legal x ok-values)
-            (list #t x)
-            (list #f "multichoice-option: illegal choice")))
-      ok-values
-      (vector (lambda () (length ok-values))
-              (lambda (x) (vector-ref (list-ref ok-values x) 0))
-              (lambda (x) (vector-ref (list-ref ok-values x) 1))
-              #f                         ;old tooltip
-              (lambda (x)
-                (gnc:multichoice-list-lookup ok-values x)))
-      (lambda () (multichoice-strings ok-values)) 
-      (and option-widget-changed-cb
-           (lambda (x) (option-widget-changed-cb x))))))
- 
- 
- ;; radiobutton options use the option-data as a list of vectors.
- ;; Each vector contains a permissible value (scheme symbol), a
- ;; name, and a description string.
- (define (gnc:make-radiobutton-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          ok-values)
-   (gnc:make-radiobutton-callback-option section
-                                         name
-                                         sort-tag
-                                         documentation-string
-                                         default-value
-                                         ok-values
-                                         #f
-                                         #f))
- 
- ;; The radiobutton-option with callback function is the same as the
- ;; usual radiobutton options (see above), with the addition of two
- ;; function arguments. (If both of them are #f, you have exactly a
- ;; radiobutton-option.) Both functions should expect one argument.
- ;; When the option's value is changed, the function
- ;; option-widget-changed-cb will be called with the new option value
- ;; at the time that the GUI widget representing the option is changed,
- ;; and the function setter-function-called-cb will be called when the
- ;; option's setter is called (that is, when the user selects "OK" or
- ;; "Apply").
- (define (gnc:make-radiobutton-callback-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          ok-values
-          setter-function-called-cb
-          option-widget-changed-cb)
-   (define (radiobutton-legal val p-vals)
-     (cond ((null? p-vals) #f)
-           ((eq? val (vector-ref (car p-vals) 0)) #t)
-           (else (radiobutton-legal val (cdr p-vals)))))
- 
-   (define (radiobutton-strings p-vals)
-     (if (null? p-vals)
-         '()
-         (cons (vector-ref (car p-vals) 1)
-               (radiobutton-strings (cdr p-vals)))))
- 
-   (let* ((value default-value)
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name sort-tag 'radiobutton documentation-string
-      (lambda () value)
-      (lambda (x)
-        (if (radiobutton-legal x ok-values)
-            (begin
-              (set! value x)
-              (if (procedure? setter-function-called-cb)
-                  (setter-function-called-cb x)))
-            (rpterror-earlier "radiobutton" x default-value)))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) (qof-book-set-option b (symbol->string value) p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (string? v))
-              (set! value (string->symbol v)))))
-      (lambda (x)
-        (if (radiobutton-legal x ok-values)
-            (list #t x)
-            (list #f "radiobutton-option: illegal choice")))
-      ok-values
-      (vector (lambda () (length ok-values))
-              (lambda (x) (vector-ref (list-ref ok-values x) 0))
-              (lambda (x) (vector-ref (list-ref ok-values x) 1))
-              #f                         ;old tooltip
-              (lambda (x)
-                (gnc:multichoice-list-lookup ok-values x)))
-      (lambda () (radiobutton-strings ok-values)) 
-      (and option-widget-changed-cb
-           (lambda (x) (option-widget-changed-cb x))))))
- 
- 
- ;; list options use the option-data in the same way as multichoice
- ;; options. List options allow the user to select more than one option.
- (define (gnc:make-list-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          ok-values)
- 
-   (define (legal-value? value legal-values)
-     (cond ((null? legal-values) #f)
-           ((eq? value (vector-ref (car legal-values) 0)) #t)
-           (else (legal-value? value (cdr legal-values)))))
- 
-   (define (list-legal values)
-     (cond ((null? values) #t)
-           (else
-            (and
-             (legal-value? (car values) ok-values)
-             (list-legal (cdr values))))))
- 
-   (define (list-strings p-vals)
-     (if (null? p-vals)
-         '()
-         (cons (vector-ref (car p-vals) 1)
-               (list-strings (cdr p-vals)))))
- 
-   (check-ok-values ok-values "gnc:make-list-option")
- 
-   (let* ((value default-value)
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name sort-tag 'list documentation-string
-      (lambda () value)
-      (lambda (x)
-        (if (list-legal x)
-            (set! value x)
-            (rpterror-earlier "list" x default-value)))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      (lambda (b p)
-        (qof-book-set-option b (length value) (append p '("len")))
-        (let loop ((value value) (idx 0))
-          (unless (null? value)
-            (qof-book-set-option
-             b (caar value) (append p (list (format #f "item~a" idx))))
-            (loop (cdr value) (1+ idx)))))
-      (lambda (b p)
-        (let ((len (qof-book-get-option b (append p '("len")))))
-          (if (and len (integer? len))
-              (set! value
-                (map
-                 (lambda (idx)
-                   (qof-book-get-option b (append p (list (format #f "item~a" idx)))))
-                 (iota len))))))
-      (lambda (x)
-        (if (list-legal x)
-            (list #t x)
-            (list #f "list-option: illegal value")))
-      ok-values     
-      (vector (lambda () (length ok-values))
-              (lambda (x) (vector-ref (list-ref ok-values x) 0))
-              (lambda (x) (vector-ref (list-ref ok-values x) 1))
-              #f                         ;old tooltip
-              (lambda (x) (gnc:multichoice-list-lookup ok-values x)))
-      (lambda () (list-strings ok-values)) #f)))
- 
- ;; number range options use the option-data as a list whose
- ;; elements are: (lower-bound upper-bound num-decimals step-size)
- (define (gnc:make-number-range-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          lower-bound
-          upper-bound
-          num-decimals
-          step-size)
-   (let* ((value default-value)
-          (value->string (lambda () (number->string value))))
-     (gnc:make-option
-      section name sort-tag 'number-range documentation-string
-      (lambda () value)
-      (lambda (x) (set! value x))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) (qof-book-set-option b value p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (number? v))
-              (set! value v))))
-      (lambda (x)
-        (cond ((not (number? x)) (list #f "number-range-option: not a number"))
-              ((and (>= value lower-bound)
-                    (<= value upper-bound))
-               (list #t x))
-              (else (list #f "number-range-option: out of range"))))
-      (list lower-bound upper-bound num-decimals step-size)
-      #f #f #f)))
- 
- ;; number plot size options use the option-data as a list whose
- ;; elements are: (lower-bound upper-bound num-decimals step-size)
- ;; which is used for the valid pixel range
- (define (gnc:make-number-plot-size-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          lower-bound
-          upper-bound
-          num-decimals
-          step-size)
-   (let* ((value default-value)
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name sort-tag 'plot-size documentation-string
-      (lambda () value)  ;;getter
-      (lambda (x)
-              (if (number? x) ;; this is for old style plot size
-              (set! value (cons 'pixels x))
-              (set! value x)))  ;;setter
- 
-      (lambda () default-value)  ;;default-getter
-      (gnc:restore-form-generator value->string)  ;;restore-form
-      (lambda (b p)
-        (qof-book-set-option b (symbol->string (car value))
-                               (append p '("type")))
-        (qof-book-set-option b (if (symbol? (cdr value))
-                                   (symbol->string (cdr value))
-                                   (cdr value))
-                                   (append p '("value"))))  ;;scm->kvp
-      (lambda (b p)
-        (let ((t (qof-book-get-option b (append p '("type"))))
-              (v (qof-book-get-option b (append p '("value")))))
-          (if (and t v (string? t))
-              (set! value (cons (string->symbol t)
-                                (if (string? v) (string->number v) v))))))  ;;kvp->scm
-      (lambda (x)
-        (if (eq? 'pixels (car x))
-          (cond ((not (number? (cdr x))) (list #f "number-plot-size-option-pixels: not a number"))
-                ((and (>= (cdr x) lower-bound)
-                      (<= (cdr x) upper-bound))
-                 (list #t x))
-                (else (list #f "number-plot-size-option-pixels: out of range")))
-          (cond ((not (number? (cdr x))) (list #f "number-plot-size-option-percentage: not a number"))
-                ((and (>= (cdr x) 10)
-                      (<= (cdr x) 100))
-                 (list #t x))
-                (else (list #f "number-plot-size-option-percentage: out of range")))
-        )
-      )  ;;value-validator
-      (list lower-bound upper-bound num-decimals step-size)  ;;option-data
-      #f #f #f)))  ;;option-data-fns, strings-getter, option-widget-changed-proc
- 
- (define (gnc:plot-size-option-value-type option-value)
-   (car option-value))
- 
- (define (gnc:plot-size-option-value option-value)
-   (cdr option-value))
- 
- (define (gnc:make-internal-option
-          section
-          name
-          default-value)
-   (let* ((value default-value)
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name "" 'internal #f
-      (lambda () value)
-      (lambda (x) (set! value x))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      #f
-      #f
-      (lambda (x) (list #t x))
-      #f #f #f #f)))
- 
- 
- (define (gnc:make-query-option
-          section
-          name
-          default-value)
-   (let* ((value (if (list? default-value)
-                     default-value
-                     (gnc-query2scm default-value)))
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name "" 'query #f
-      (lambda () value)
-      (lambda (x) (set! value (if (list? x) x (gnc-query2scm x))))
-      (lambda () (if (list? default-value)
-                     default-value
-                     (gnc-query2scm default-value)))
-      (gnc:restore-form-generator value->string)
-      #f
-      #f
-      (lambda (x) (list #t x))
-      #f #f #f #f)))
- 
- 
- ;; Color options store rgba values in a list.
- ;; The option-data is a list, whose first element
- ;; is the range of possible rgba values and whose
- ;; second element is a boolean indicating whether
- ;; to use alpha transparency.
- (define (gnc:make-color-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value
-          range
-          use-alpha)
- 
-   (define (canonicalize values)
-     (map exact->inexact values))
- 
-   (define (values-in-range values)
-     (if (null? values)
-         #t
-         (let ((value (car values)))
-           (and (number? value)
-                (>= value 0)
-                (<= value range)
-                (values-in-range (cdr values))))))
- 
-   (define (validate-color color)
-     (cond ((not (list? color)) (list #f "color-option: not a list"))
-           ((not (= 4 (length color))) (list #f "color-option: wrong length"))
-           ((not (values-in-range color))
-            (list #f "color-option: bad color values"))
-           (else (list #t color))))
- 
-   (let* ((value (canonicalize default-value))
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name sort-tag 'color documentation-string
-      (lambda () value)
-      (lambda (x) (set! value (canonicalize x)))
-      (lambda () (canonicalize default-value))
-      (gnc:restore-form-generator value->string)
-      #f
-      #f
-      validate-color
-      (list range use-alpha)
-      #f #f #f)))
- 
- (define (gnc:color->hex-string color range)
-   (define (html-value value)
-     (inexact->exact
-      (min 255.0
-           (truncate (* (/ 255.0 range) value)))))
-   (define (number->hex-string number)
-     (let ((ret (number->string number 16)))
-       (cond ((< (string-length ret) 2) (string-append "0" ret))
-             (else ret))))
-   (let ((red (car color))
-         (green (cadr color))
-         (blue (caddr color)))
-     (string-append
-      (number->hex-string (html-value red))
-      (number->hex-string (html-value green))
-      (number->hex-string (html-value blue)))))
- 
- (define (gnc:color->html color range)
-   (string-append "#"
-                  (gnc:color->hex-string color range)))
- 
- (define (gnc:color-option->html color-option)
-   (let ((color (gnc:option-value color-option))
-         (range (car (gnc:option-data color-option))))
-     (gnc:color->html color range)))
- 
- (define (gnc:color-option->hex-string color-option)
-   (let ((color (gnc:option-value color-option))
-         (range (car (gnc:option-data color-option))))
-     (gnc:color->hex-string color range)))
- 
- ;;
- ;; dateformat option
- ;;
- (define (gnc:make-dateformat-option
-          section
-          name
-          sort-tag
-          documentation-string
-          default-value)
- 
-   (define (def-value)
-     (if (list? default-value)
-         default-value
-         (list 'unset 'number #t "")))
- 
-   (let* ((value (def-value))
-          (value->string (lambda () 
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name sort-tag 'dateformat documentation-string
-      (lambda () value)
-      (lambda (x) (set! value x))
-      (lambda () (def-value))
-      (gnc:restore-form-generator value->string)
-      (lambda (b p)
-        (if (eq? (car value) 'unset)
-            (qof-book-options-delete b p );; delete the kvp when unset
-        (begin
-          (qof-book-set-option
-           b (symbol->string (car value)) (append p '("fmt")))
-          (qof-book-set-option
-           b (symbol->string (cadr value)) (append p '("month")))
-          (qof-book-set-option
-           b (if (caddr value) 1 0) (append p '("years")))
-          (qof-book-set-option
-           b (cadddr value) (append p '("custom"))))))
-      (lambda (f p)
-        (let ((fmt (qof-book-get-option f (append p '("fmt"))))
-              (month (qof-book-get-option f (append p '("month"))))
-              (years (qof-book-get-option f (append p '("years"))))
-              (custom (qof-book-get-option f (append p '("custom")))))
-          (if (and
-               fmt (string? fmt)
-               month (string? month)
-               years (number? years)
-               custom (string? custom))
-              (set! value (list (string->symbol fmt) (string->symbol month)
-                                (if (= years 0) #f #t) custom)))))
-      (lambda (x)
-        (cond ((not (list? x)) (list #f "dateformat-option: not a list"))
-              ((not (= (length x) 4))
-               (list #f "dateformat-option: wrong list length" (length x)))
-              ((not (symbol? (car x)))
-               (list #f "dateformat-option: no format symbol"))
-              ((not (symbol? (cadr x)))
-               (list #f "dateformat-option: no months symbol"))
-              ((not (string? (cadddr x)))
-               (list #f "dateformat-option: no custom string"))
-              (else (list #t x))))
-      #f #f #f #f)))
- 
- (define (gnc:dateformat-get-format v)
-   (cadddr v))
- 
- (define (gnc:make-currency-accounting-option
-          section
-          name
-          sort-tag
-          radiobutton-documentation-string
-          default-radiobutton-value
-          ok-radiobutton-values
-          book-currency-documentation-string
-          default-book-currency-value
-          default-cap-gains-policy-documentation-string
-          default-cap-gains-policy-value
-          default-gains-loss-account-documentation-string
-         )
-   (define (legal-val val p-vals)
-     (cond ((null? p-vals) #f)
-           ((not (symbol? val)) #f)
-           ((eq? val (vector-ref (car p-vals) 0)) #t)
-           (else (legal-val val (cdr p-vals)))))
- 
-   (define (currency-lookup currency-string)
-     (if (string? currency-string)
-         (gnc-commodity-table-lookup
-          (gnc-commodity-table-get-table (gnc-get-current-book))
-          GNC_COMMODITY_NS_CURRENCY currency-string)
-         #f))
- 
-   (define (currency? val)
-     (gnc-commodity-is-currency (currency-lookup val)))
- 
-   (define (vector-strings p-vals)
-     (if (null? p-vals)
-         '()
-         (cons (vector-ref (car p-vals) 1)
-               (cons (vector-ref (car p-vals) 2)
-                     (vector-strings (cdr p-vals))))))
- 
-   (define (currency->scm currency)
-     (if (string? currency)
-         currency
-         (gnc-commodity-get-mnemonic currency)))
- 
-   (define (scm->currency currency)
-     (currency-lookup currency))
- 
-   (define (valid-gains-loss-account? book-currency gains-loss-account-guid)
-     ;; xaccAccountLookup returns Account if guid valid otherwise NULL; also must
-     ;; be in book-currency, income or expense, and not placeholder nor hidden
-     (let* ((account (xaccAccountLookup gains-loss-account-guid
-                                        (gnc-get-current-book))))
-       (and account
-            (not (null? account))
-            (not (xaccAccountIsHidden account))
-            (not (xaccAccountGetPlaceholder account))
-            (memv (xaccAccountGetType account)
-                  (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))
-            (gnc-commodity-equal
-             (currency-lookup book-currency)
-             (xaccAccountGetCommodity account)))))
- 
-   (let* ((value (if (eq? 'book-currency default-radiobutton-value)
-                     (list default-radiobutton-value
-                           default-book-currency-value
-                           default-cap-gains-policy-value)
-                     (list default-radiobutton-value)))
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string
-                                                (car value)))))
-          (trading-accounts-path (list gnc:*option-section-accounts*
-                                       gnc:*option-name-trading-accounts*))
-          (book-currency-path (list gnc:*option-section-accounts*
-                                    gnc:*option-name-book-currency*))
-          (gains-policy-path (list gnc:*option-section-accounts*
-                                   gnc:*option-name-default-gains-policy*))
-          (gains-loss-account-path (list gnc:*option-section-accounts*
-                                   gnc:*option-name-default-gain-loss-account*)))
-     (gnc:make-option
-      section name sort-tag 'currency-accounting
-      radiobutton-documentation-string
-      (lambda () value) ;; getter
-      (lambda (x)
-        (if (legal-val (car x) ok-radiobutton-values)
-            (set! value x)
-            (gnc:error "Illegal Radiobutton option set"))) ;;setter
-      (lambda () (if (eq? 'book-currency default-radiobutton-value)
-                     (list default-radiobutton-value
-                           default-book-currency-value
-                           default-cap-gains-policy-value)
-                     (list default-radiobutton-value))) ;; default-getter
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) ;; scm->kvp
-        (case (car value)
-          ((book-currency)
-           ;; Currency = selected currency
-           (qof-book-set-option b (currency->scm (cadr value))
-                                book-currency-path)
-           ;; Default Gains Policy = selected policy
-           (qof-book-set-option b (symbol->string (caddr value))
-                                gains-policy-path)
-           ;; Default Gains Account = if selected, selected account
-           (if (car (cdddr value))
-               (qof-book-set-option b (car (cdddr value))
-                                    gains-loss-account-path)))
-          ((trading)
-           ;; Use Trading Accounts = "t"
-           (qof-book-set-option b "t" trading-accounts-path))))
-      (lambda (b p) ;; kvp->scm
-        (let* ((trading-option-path-kvp?
-                        (qof-book-get-option b trading-accounts-path))
-               (trading? (and trading-option-path-kvp?
-                              (string=? "t" trading-option-path-kvp?)))
-               (book-currency #f)
-               (cap-gains-policy #f)
-               (gains-loss-account-guid #f)
-               (v (if trading?
-                      'trading
-                      (let* ((book-currency-option-path-kvp?
-                                  (qof-book-get-option
-                                      b book-currency-path))
-                             (gains-policy-option-path-kvp?
-                                  (qof-book-get-option
-                                      b gains-policy-path))
-                             (gains-loss-account-option-path-kvp?
-                                  (qof-book-get-option
-                                      b gains-loss-account-path))
-                             (book-currency?
-                                (if (and book-currency-option-path-kvp?
-                                         gains-policy-option-path-kvp?
-                                         (string?
-                                            book-currency-option-path-kvp?)
-                                         (string?
-                                            gains-policy-option-path-kvp?)
-                                         (if book-currency-option-path-kvp?
-                                             (currency?
-                                               book-currency-option-path-kvp?))
-                                         (if gains-policy-option-path-kvp?
-                                             (gnc-valid-policy-name
-                                               gains-policy-option-path-kvp?)))
-                                    (begin
-                                      (set! book-currency
-                                                book-currency-option-path-kvp?)
-                                      (set! cap-gains-policy
-                                                gains-policy-option-path-kvp?)
-                                      (if gains-loss-account-option-path-kvp?
-                                          (if (valid-gains-loss-account?
-                                                book-currency
-                                                gains-loss-account-option-path-kvp?)
-                                              (set! gains-loss-account-guid
-                                                gains-loss-account-option-path-kvp?)))
-                                      #t)
-                                     #f)))
-                            (if book-currency?
-                                'book-currency
-                                'neither)))))
-              (if (and v (symbol? v) (legal-val v ok-radiobutton-values))
-                  (set! value (cons v (if (eq? 'book-currency v)
-                                          (list (scm->currency book-currency)
-                                                (string->symbol cap-gains-policy)
-                                                gains-loss-account-guid)
-                                          '())))
-                  (set! value (list 'neither)))))
-      (lambda (x) ;; value validator
-        (cond
-         ((not (list? x))
-          (list #f "value not a list"))
-         ((not (legal-val (car x) ok-radiobutton-values))
-          (list #f "radiobutton-option: illegal choice"))
-         ((not (eq? 'book-currency (car x)))
-          (list #t x))
-         ((not (currency? (currency->scm (cadr x))))
-          (list #f "currency-option: illegal value"))
-         ((not (gnc-valid-policy-name (symbol->string (caddr x))))
-          (list #f "cap-gains-policy-option: illegal value"))
-         ((not (car (cdddr x)))
-          (list #t x))
-         ((not (valid-gains-loss-account? (currency->scm (cadr x))
-                                          (car (cdddr x))))
-          (list #f "gains-loss-account-option: illegal value"))
-         (else
-          (list #t x))))
-      (vector book-currency-documentation-string
-              default-book-currency-value
-              default-cap-gains-policy-documentation-string
-              default-cap-gains-policy-value
-              default-gains-loss-account-documentation-string)
-      (vector (lambda () (length ok-radiobutton-values))
-              (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 0))
-              (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 1))
-              (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 2))
-              (lambda (x)
-                (gnc:multichoice-list-lookup ok-radiobutton-values x)))
-      (lambda () (vector-strings ok-radiobutton-values))
-      #f)))
- 
- (define (gnc:get-currency-accounting-option-data-curr-doc-string option-data)
-   (vector-ref option-data 0))
- 
- (define (gnc:get-currency-accounting-option-data-default-curr option-data)
-   (vector-ref option-data 1))
- 
- (define (gnc:get-currency-accounting-option-data-policy-doc-string option-data)
-   (vector-ref option-data 2))
- 
- (define (gnc:get-currency-accounting-option-data-policy-default option-data)
-   (vector-ref option-data 3))
- 
- (define (gnc:get-currency-accounting-option-data-gain-loss-account-doc-string option-data)
-   (vector-ref option-data 4))
- 
- (define (gnc:currency-accounting-option-get-curr-doc-string option)
-   (if (eq? (gnc:option-type option) 'currency-accounting)
-       (gnc:get-currency-accounting-option-data-curr-doc-string
-         (gnc:option-data option))
-       (gnc:error "Not a currency accounting option")))
- 
- (define (gnc:currency-accounting-option-get-default-curr option)
-   (if (eq? (gnc:option-type option) 'currency-accounting)
-       (gnc:get-currency-accounting-option-data-default-curr
-         (gnc:option-data option))
-       (gnc:error "Not a currency accounting option")))
- 
- (define (gnc:currency-accounting-option-get-policy-doc-string option)
-   (if (eq? (gnc:option-type option) 'currency-accounting)
-       (gnc:get-currency-accounting-option-data-policy-doc-string
-         (gnc:option-data option))
-       (gnc:error "Not a currency accounting option")))
- 
- (define (gnc:currency-accounting-option-get-default-policy option)
-   (if (eq? (gnc:option-type option) 'currency-accounting)
-       (gnc:get-currency-accounting-option-data-policy-default
-         (gnc:option-data option))
-       (gnc:error "Not a currency accounting option")))
- 
- (define (gnc:currency-accounting-option-get-gain-loss-account-doc-string option)
-   (if (eq? (gnc:option-type option) 'currency-accounting)
-       (gnc:get-currency-accounting-option-data-gain-loss-account-doc-string
-         (gnc:option-data option))
-       (gnc:error "Not a currency accounting option")))
- 
- (define (gnc:currency-accounting-option-selected-method option-value)
-   (car option-value))
- 
- (define (gnc:currency-accounting-option-selected-currency option-value)
-   (if (eq? (car option-value) 'book-currency)
-       (cadr option-value)
-       #f))
--
- (define (gnc:currency-accounting-option-selected-policy option-value)
-   (if (eq? (car option-value) 'book-currency)
-       (caddr option-value)
-       #f))
--
- (define (gnc:currency-accounting-option-selected-gain-loss-account option-value)
-   (if (eq? (car option-value) 'book-currency)
-       (car (cdddr option-value))
+ (define-public (gnc:lookup-option options section name)
+   (if options
+       (gnc-lookup-option (options 'lookup) section name)
        #f))
  
- ;; Create a new options database
- (define (gnc:new-options)
-   (define option-hash (make-hash-table 23))
- 
-   (define options-changed #f)
-   (define changed-hash (make-hash-table 23))
- 
-   (define callback-hash (make-hash-table 23))
-   (define last-callback-id 0)
-   (define new-names-alist
-     '(("Accounts to include" #f "Accounts")
-       ("Exclude transactions between selected accounts?" #f
-        "Exclude transactions between selected accounts")
-       ("Filter Accounts" #f "Filter By...")
-       ("Flatten list to depth limit?" #f "Flatten list to depth limit")
-       ("From" #f "Start Date")
-       ("Report Accounts" #f "Accounts")
-       ("Report Currency" #f "Report's currency")
-       ("Show Account Code?" #f "Show Account Code")
-       ("Show Full Account Name?" #f "Show Full Account Name")
-       ("Show Multi-currency Totals?" #f "Show Multi-currency Totals")
-       ("Show zero balance items?" #f "Show zero balance items")
-       ("Sign Reverses?" #f "Sign Reverses")
-       ("To" #f "End Date")
-       ("Charge Type" #f "Action") ;easy-invoice.scm, renamed June 2018
-       ;; the following 4 options in income-gst-statement.scm renamed Dec 2018
-       ("Individual income columns" #f "Individual sales columns")
-       ("Individual expense columns" #f "Individual purchases columns")
-       ("Remittance amount" #f "Gross Balance")
-       ("Net Income" #f "Net Balance")
-       ;; transaction.scm:
-       ("Use Full Account Name?" #f "Use Full Account Name")
-       ("Use Full Other Account Name?" #f "Use Full Other Account Name")
-       ("Void Transactions?" "Filter" "Void Transactions")
-       ("Void Transactions" "Filter" "Void Transactions")
-       ("Account Substring" "Filter" "Account Name Filter")
-       ("Enable links" #f "Enable Links")
-       ;; trep-engine: moved currency options to own tab
-       ("Common Currency" "Currency" "Common Currency")
-       ("Show original currency amount" "Currency" "Show original currency amount")
-       ("Report's currency" "Currency" "Report's currency")
-       ("Reconcile Status" #f "Reconciled Status")
-       ;; new-owner-report.scm, renamed Oct 2020 to differentiate with
-       ;; Document Links:
-       ("Links" #f "Transaction Links")
-       ;; invoice.scm, renamed November 2018
-       ("Individual Taxes" #f "Use Detailed Tax Summary")
-       ;; renamed in several reports, April 2021
-       ("Show Accounts until level" #f "Levels of Subaccounts")
-       ;; receipt.scm, renamed July 2021
-       ("Invoice number" #f "Invoice Number")
-       ;; receipt.scm and taxinvoice.scm, renamed July 2021
-       ("Report title" #f "Report Title")
-       ("Extra notes" #f "Extra Notes")
-       ;; income-gst-statement.scm
-       ("default format" #f "Default Format")
-       ("Report format" #f "Report Format")
-       ))
+ (define-public (gnc:option-setter option)
+   (issue-deprecation-warning "gnc:option-setter is deprecated. Option values are set and retrieved by gnc-set-option and gnc-option-db-lookup.")
+   (lambda (value)
+     (GncOption-set-value-from-scm option value)
+     ))
  
-   (define (lookup-option section name)
-     (let ((section-hash (hash-ref option-hash section)))
-       (and section-hash
-            (or (hash-ref section-hash name)
-                ;; Option name was not found. Perhaps it was renamed?
-                ;; Let's try to map to a known new name.  The alist
-                ;; new-names-alist will try match names - car is the old
-                ;; name, cdr is the 2-element list describing
-                ;; 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 ((name-match (assoc-ref new-names-alist name)))
-                  (and name-match
-                       (let ((new-section (car name-match))
-                             (new-name (cadr name-match)))
-                         (gnc:warn
-                          (format #f "option ~a/~a has been renamed to ~a/~a\n"
-                                  section name new-section new-name))
-                         (cond
-                          ;; new-name only
-                          ((not new-section)
-                           (lookup-option section new-name))
-                          ;; new-section different to current section
-                          ;; name, and possibly new-name
-                          ((not (string=? new-section section))
-                           (lookup-option new-section new-name))
-                          ;; no match, return #f
-                          (else #f)))))))))
+ (define-public (gnc:option-set-value option value)
+     (issue-deprecation-warning "gnc:option-set-value and indeed all direct option access is deprecated. Use gnc-set-option instead.")
+     (GncOption-set-value-from-scm option value))
  
-   (define (option-changed section name)
-     (set! options-changed #t)
-     (let ((section-changed-hash (hash-ref changed-hash section)))
-       (if (not section-changed-hash)
-           (begin
-             (set! section-changed-hash (make-hash-table 23))
-             (hash-set! changed-hash section section-changed-hash)))
-       (hash-set! section-changed-hash name #t)))
+ (define-public (gnc:option-set-default-value option value)
+     (issue-deprecation-warning "gnc:option-set-default-value and indeed all direct option access is deprecated. Use gnc-set-option instead.")
+     (GncOption-set-default-value-from-scm option value))
  
-   (define (clear-changes)
-     (set! options-changed #f)
-     (set! changed-hash (make-hash-table 23)))
+ (define-public (gnc:option-section option)
+   (GncOption-get-section option))
  
-   (define (register-option new-option)
-     (let* ((name (gnc:option-name new-option))
-            (section (gnc:option-section new-option))
-            (section-hash (hash-ref option-hash section)))
-       (if (not section-hash)
-           (begin
-             (set! section-hash (make-hash-table 23))
-             (hash-set! option-hash section section-hash)))
-       (hash-set! section-hash name new-option)
-       (gnc:option-set-changed-callback
-        new-option
-        (lambda () (option-changed section name)))))
+ (define-public (gnc:option-name option)
+   (GncOption-get-name option))
  
-   (define (unregister-option section name)
-     (let* ((section-hash (hash-ref option-hash section)))
-       (if (and section-hash
-                (hash-ref section-hash name))
-           (begin
-             (hash-remove! section-hash name)
-             (if (zero? (hash-count (const #t) section-hash))
-                 (hash-remove! option-hash section)))
-           (gnc:error "options:unregister-option: no such option\n"))))
+ (define-public (gnc:option-default-value option)
+   (GncOption-get-scm-default-value option))
  
- ;   Call (thunk option) for each option in the database
-   (define (options-for-each thunk)
-     (define (section-for-each section-hash thunk)
-       (hash-for-each
-        (lambda (name option)
-          (thunk option))
-        section-hash))
-     (hash-for-each
-      (lambda (section hash)
-        (section-for-each hash thunk))
-      option-hash))
+ (define-public (gnc:option-value option)
+     (issue-deprecation-warning "gnc:option-value and indeed all direct option access is deprecated. Use gnc-option-db-lookup-option instead.")
+     (GncOption-get-scm-value option))
  
-   (define (options-for-each-general section-thunk option-thunk)
-     (define (section-for-each section-hash thunk)
-       (hash-for-each
-        (lambda (name option)
-          (thunk option))
-        section-hash))
-     (hash-for-each
-      (lambda (section hash)
-        (if section-thunk
-            (section-thunk section hash))
-        (if option-thunk
-            (section-for-each hash option-thunk)))
-      option-hash))
+ (define-public (gnc:color-option->html opt)
+   ;; HTML doesn't like alpha values.
+   (let* ((color (GncOption-get-scm-value opt))
+          (html-color (if (> (string-length color) 6)
+                          (substring color 0 6)
+                          color)))
+     (format #f "#~a" html-color)))
  
-   (define (generate-restore-forms options-string)
+ (define-public (gnc:color-option->hex-string opt)
+   (format #f "~a" (GncOption-get-scm-value opt)))
  
-     (define (generate-option-restore-form option restore-code)
-       (let* ((section (gnc:option-section option))
-              (name (gnc:option-name option)))
-         (string-append
-          "(let ((option (gnc:lookup-option " options-string "\n"
-          "                                 " (gnc:value->string section) "\n"
-          "                                 " (gnc:value->string name) ")))\n"
-          "  (" restore-code " option))\n\n")))
- 
-     (define (generate-forms port)
-       (options-for-each-general
-        (lambda (section hash)
-          (display
-           (string-append "\n; Section: " section "\n\n")
-           port))
-        (lambda (option)
-          (let ((value (gnc:option-value option))
-                (default-value (gnc:option-default-value option)))
-            (if (not (equal? value default-value))
-             (let* ((generator (gnc:option-generate-restore-form option))
-                    (restore-code (false-if-exception (generator))))
-               (if restore-code
-                   (display
-                    (generate-option-restore-form option restore-code)
-                    port))))))))
- 
-     (call-with-output-string generate-forms))
- 
-   (define (scm->kvp book)
-     (options-for-each
-      (lambda (option)
-        (let ((value (gnc:option-value option))
-              (default-value (gnc:option-default-value option))
-              (section (gnc:option-section option))
-              (name (gnc:option-name option)))
- ;;         (gnc:debug "value: " value "; default: " default-value
- ;;                   "; section: " section "; name: " name)
-          (if (not (equal? value default-value))
-              (let ((save-fcn (gnc:option-scm->kvp option)))
- ;;               (gnc:debug "save-fcn: " save-fcn)
-                (if save-fcn
-                    (save-fcn book (list section name)))))))))
- 
-   (define (kvp->scm book)
-     (options-for-each
-      (lambda (option)
-        (let ((section (gnc:option-section option))
-              (name (gnc:option-name option))
-              (load-fcn (gnc:option-kvp->scm option)))
-          (if load-fcn
-              (load-fcn book (list section name)))))))
- 
-   (define (register-callback section name callback)
-     (let ((id last-callback-id)
-           (data (list section name callback)))
-       (set! last-callback-id (+ last-callback-id 1))
-       (hashv-set! callback-hash id data)
-       id))
- 
-   (define (unregister-callback-id id)
-     (if (hashv-ref callback-hash id)
-         (hashv-remove! callback-hash id)
-         (gnc:error "options:unregister-callback-id: no such id\n")))
- 
-   (define (run-callbacks)
-     (define (run-callback id cbdata)
-       (let ((section  (car cbdata))
-             (name     (cadr cbdata))
-             (callback (caddr cbdata)))
-         (if (not section)
-             (callback)
-             (let ((section-changed-hash (hash-ref changed-hash section)))
-               (if section-changed-hash
-                   (if (not name)
-                       (callback)
-                       (if (hash-ref section-changed-hash name)
-                           (callback))))))))
- 
-     (if options-changed
-         (let ((cblist '()))
-           (hash-for-each 
-            (lambda (k v) (set! cblist (cons (cons k v) cblist)))
-            callback-hash)
-           (set! cblist (sort cblist 
-                              (lambda (a b) 
-                                (< (car a) (car b)))))
-           (for-each 
-            (lambda (elt) (run-callback (car elt) (cdr elt))) 
-            cblist)))
-     (clear-changes))
-   
-   (define default-section #f)
- 
-   (define (touch)
-     (set! options-changed #t)
-     (run-callbacks))
-   
-   (define (set-default-section section-name)
-     (set! default-section section-name))
- 
-   (define (get-default-section)
-     default-section)
- 
-   (define (dispatch key)
-     (case key
-       ((lookup) lookup-option)
-       ((register-option) register-option)
-       ((unregister-option) unregister-option)
-       ((register-callback) register-callback)
-       ((unregister-callback-id) unregister-callback-id)
-       ((for-each) options-for-each)
-       ((for-each-general) options-for-each-general)
-       ((generate-restore-forms) generate-restore-forms)
-       ((scm->kvp) scm->kvp)
-       ((kvp->scm) kvp->scm)
-       ((touch) touch)
-       ((clear-changes) clear-changes)
-       ((run-callbacks) run-callbacks)
-       ((set-default-section) set-default-section)
-       ((get-default-section) get-default-section)
-       (else (gnc:warn "options: bad key: " key "\n"))))
- 
-   dispatch)
- 
- (define (gnc:register-option options new-option)
-   ((options 'register-option) new-option))
- 
- (define (gnc:options-register-callback section name callback options)
-   ((options 'register-callback) section name callback))
- 
- (define (gnc:options-register-c-callback section name c-callback data options)
-   (let ((callback (lambda () (gncp-option-invoke-callback c-callback data))))
-     ((options 'register-callback) section name callback)))
- 
- (define (gnc:options-unregister-callback-id id options)
-   ((options 'unregister-callback-id) id))
- 
- (define (gnc:options-for-each thunk options)
-   ((options 'for-each) thunk))
- 
- (define (gnc:options-for-each-general section-thunk option-thunk options)
-   ((options 'for-each-general) section-thunk option-thunk))
- 
- (define (gnc:lookup-option options section name)
-   (if options
-       ((options 'lookup) section name)
-       #f))
- 
- (define (gnc:unregister-option options section name)
-   ((options 'unregister-option) section name))
- 
- (define (gnc:generate-restore-forms options options-string)
-   ((options 'generate-restore-forms) options-string))
+ (define-public (gnc:option-get-value book category key)
+   (define acc (if (pair? key) cons list))
+   (qof-book-get-option book (acc category key)))
  
- (define (gnc:options-scm->kvp options book clear-option?)
-   (if clear-option?
-       (qof-book-options-delete book '()))
-   ((options 'scm->kvp) book))
+ (define-public (gnc:option-make-internal! options section name)
+   (let ((option (gnc-lookup-option (options 'lookup) section name)))
+     (and option (GncOption-make-internal option))))
  
- (define (gnc:options-kvp->scm options book)
-   ((options 'kvp->scm) book))
+ (define-public (gnc:option-type option)
+   (GncOption-get-type option))
  
- (define (gnc:options-clear-changes options)
-   ((options 'clear-changes)))
+ ;; Used only by test-stress-options.scm
+ (define-public (gnc:option-data option)
+   (let ((num-values (GncOption-num-permissible-values option))
+         (retval '()))
+     (do ((i 0 (1+ i))) ((>= i num-values))
+       (let ((value (GncOption-permissible-value option i))
+             (name (GncOption-permissible-value-name option i)))
+         (set! retval (cons retval (vector value name)))))
+     retval))
  
- (define (gnc:options-touch options)
-   ((options 'touch)))
+ ;; Create the database and return a dispatch function.
+ (define-public (gnc:new-options)
+   (let ((optiondb (new-gnc-optiondb)))
+     (define (dispatch key)
+       optiondb)
+     dispatch))
  
- (define (gnc:options-run-callbacks options)
-   ((options 'run-callbacks)))
+ ;; Use the dispatch function to get the optiondb
+ (define-public (gnc:options-get dispatch)
+   (dispatch 'get))
  
- (define (gnc:options-set-default-section options section-name)
-   ((options 'set-default-section) section-name))
+ (define-public (gnc:options-set-default-section optiondb section)
+   (GncOptionDB-set-default-section (GncOptionDBPtr-get (optiondb 'set-default-section)) section))
  
- (define (gnc:options-get-default-section options)
-   ((options 'get-default-section)))
+ (define-public (gnc:options-for-each func optdb)
+   (gnc-optiondb-foreach (optdb 'foreach) func))
  
  ;; Copies all values from src-options to dest-options, that is, it
  ;; copies the values of all options from src which exist in dest to
diff --cc libgnucash/app-utils/test/CMakeLists.txt
index 77ba83976,37071adf2..0ffa2b6e5
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@@ -60,17 -79,37 +79,20 @@@ gnc_add_scheme_test_targets(scm-test-c-
  gnc_add_scheme_tests("${test_app_utils_scheme_SOURCES}")
  
  if (HAVE_SRFI64)
-     gnc_add_scheme_test_targets(scm-test-app-utils-srfi64
-         SOURCES "${test_app_utils_scheme_SRFI64_SOURCES}"
-         OUTPUT_DIR "tests"
-         DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras")
+   gnc_add_scheme_test_targets(scm-test-app-utils-srfi64
+     SOURCES "${test_app_utils_scheme_SRFI64_SOURCES}"
+     OUTPUT_DIR "tests"
+     DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras")
  
-     gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}")
+   gnc_add_scheme_test_targets(scm-test-gnc-optiondb
+     SOURCES "test-gnc-optiondb.scm" "test-gnc-option-scheme-output.scm"
+     OUTPUT_DIR "tests"
+     DEPENDS "swig-apputils-guile-cpp;scm-srfi64-extras")
+   gnc_add_scheme_tests("test-gnc-optiondb.scm")
+   gnc_add_scheme_tests("test-gnc-option-scheme-output.scm")
+   gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}")
  endif()
  
- # 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(test_autoclear_SOURCES
 -    test-autoclear.cpp
 -)
 -set(test_autoclear_INCLUDE_DIRS
 -    ${APP_UTILS_TEST_INCLUDE_DIRS}
 -    ${GTEST_INCLUDE_DIR}
 -)
 -set(test_autoclear_LIBS
 -    ${APP_UTILS_TEST_LIBS}
 -    gtest
 -)
 -
 -gnc_add_test(test-autoclear "${test_autoclear_SOURCES}"
 -    test_autoclear_INCLUDE_DIRS
 -    test_autoclear_LIBS
 -)
--
  set_dist_list(test_app_utils_DIST
    CMakeLists.txt
    test-exp-parser.c
diff --cc po/POTFILES.in
index 52dd27261,cdd4db4e4..f1b1f1e59
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@@ -526,9 -522,13 +523,13 @@@ libgnucash/app-utils/gnc-addr-quickfill
  libgnucash/app-utils/gnc-entry-quickfill.c
  libgnucash/app-utils/gnc-euro.c
  libgnucash/app-utils/gnc-exp-parser.c
 -libgnucash/app-utils/gnc-gsettings.c
 +libgnucash/app-utils/gnc-gsettings.cpp
  libgnucash/app-utils/gnc-helpers.c
  libgnucash/app-utils/gnc-help-utils.c
+ libgnucash/app-utils/gnc-option.cpp
+ libgnucash/app-utils/gnc-option-date.cpp
+ libgnucash/app-utils/gnc-optiondb.cpp
+ libgnucash/app-utils/gnc-option-impl.cpp
  libgnucash/app-utils/gnc-prefs-utils.c
  libgnucash/app-utils/gnc-state.c
  libgnucash/app-utils/gnc-sx-instance-model.c

commit 96b09ded9fedb0a97bab468124e1aeafdda93b43
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Nov 10 18:01:55 2021 -0800

    Doxygen documentation for new options classes.

diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp
index c2fb570c2..b0101c23d 100644
--- a/gnucash/gnome-utils/dialog-options.hpp
+++ b/gnucash/gnome-utils/dialog-options.hpp
@@ -3,6 +3,7 @@
  * Copyright (C) 1998-2000 Linas Vepstas                            *
  * Copyright (c) 2006 David Hampton <hampton at employees.org>         *
  * Copyright (c) 2011 Robert Fewell                                 *
+ * Copyright 2019-2021 John Ralls <jralls at ceridwen.us>              *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -21,6 +22,10 @@
  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
+/** @addtogroup GUI
+    @{ */
+/** @addtogroup GuiOptions Options Dialog
+    @{ */
 
 #ifndef GNC_DIALOG_OPTIONS_HPP_
 #define GNC_DIALOG_OPTIONS_HPP_
@@ -31,18 +36,49 @@
 #include <gnc-option-ui.hpp>
 #include "dialog-options.h"
 
+/** @fn WidgetCreateFunc
+ *  Function pointer for per-option-type GtkWidget constructors.
+ *  @param option The option to create an element for.
+ *  @param page_box The option dialog page's layout grid
+ *  @param name_label A GtkLabel to attach to the widget
+ *  @param documentation The string to use for the tooltip.
+ *  @param enclosing The parent widget
+ *  @param packed Whether the widget will be packed into an eventbox.
+ *  @return pointer to the widget.
+ */
+
 typedef GtkWidget* (*WidgetCreateFunc)(GncOption&, GtkGrid*, GtkLabel*, char*,
                                        GtkWidget**, bool*);
+/** @class GncOptionUIFactory
+ *  Factory class that keeps track of which GncOptionValueType needs which
+ *  WidgetCreateFunc and calls the appropriate one when required.
+ */
 class GncOptionUIFactory
 {
 public:
+/** Register a WidgetCreateFunc
+ *  @param type The UI type
+ *  @param func The function to register
+ */
     static void set_func(GncOptionUIType type, WidgetCreateFunc func);
+/** Create a widget
+ *  @param option The option for which to create the widget
+ *  @param page The Option dialog page in which to insert the widget
+ *  @param name The label to attach to the widget
+ *  @param description The text for the widget's tooltip
+ *  @param enclosing The widget's parent
+ *  @param packed Whether the widget will be packed into an eventbox.
+ *  @return pointer to the created widget.
+ */
     static GtkWidget* create(GncOption&, GtkGrid*, GtkLabel*, char*,
                              GtkWidget**, bool*);
 private:
     static std::vector<WidgetCreateFunc> s_registry;
 };
 
+/** class GncOptionGtkUIItem
+ *  Gtk-specific Interface class for Option Widget
+ */
 class GncOptionGtkUIItem : public GncOptionUIItem
 {
 public:
@@ -50,7 +86,9 @@ public:
     GncOptionGtkUIItem(const GncOptionGtkUIItem& item);
     GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default;
     virtual ~GncOptionGtkUIItem() override;
+/** Control wether the widget is sensitive */
     virtual void set_selectable(bool) const noexcept override;
+/** Clear the data from the widget. */
     void clear_ui_item() override;
     void set_widget(GtkWidget* widget);
     virtual GtkWidget* const get_widget() const { return m_widget; }
@@ -68,6 +106,10 @@ template<GncOptionUIType type> GtkWidget*
 create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**,
                      bool*);
 
+/** Templated cast to convert various QofInstance subtype ptrs into QofInstance*
+ * to placate the C++ type system. QofInstance is a GObject hierarchy so the
+ * usual C++ type substitution doesn't work.
+ */
 template <typename Instance> inline const QofInstance*
 qof_instance_cast(Instance inst)
 {
@@ -76,3 +118,5 @@ qof_instance_cast(Instance inst)
 }
 
 #endif // GNC_DIALOG_OPTIONS_HPP_
+/** @}
+    @} */
diff --git a/gnucash/gnome/business-options-gnome.h b/gnucash/gnome/business-options-gnome.h
index 47d518f22..fb454c0f8 100644
--- a/gnucash/gnome/business-options-gnome.h
+++ b/gnucash/gnome/business-options-gnome.h
@@ -21,10 +21,19 @@
  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
  * Boston, MA  02110-1301,  USA       gnu at gnu.org
  */
+/** @addtogroup GUI
+    @{ */
+/** @addtogroup GuiOptions Options Dialog
+    @{ */
 
 #ifndef GNC_BUSINESS_OPTIONS_H_
 #define GNC_BUSINESS_OPTIONS_H_
 
+/**
+ * Set up the business and counters pages in the File Preferences dialog.
+ */
 void gnc_business_options_gnome_initialize (void);
 
 #endif /* GNC_BUSINESS_OPTIONS_H_ */
+/** @}
+    @} */
diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp
index 82f963d60..395dee3aa 100644
--- a/libgnucash/app-utils/gnc-option-date.hpp
+++ b/libgnucash/app-utils/gnc-option-date.hpp
@@ -20,7 +20,14 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  *                                                                  *
 \********************************************************************/
-
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Options
+    @{ */
+/** @file gnc-option-date.hpp
+    @brief Relative date enumeration and manipulation functions.
+    @author Copyright 2019-2021 John Ralls <jralls at ceridwen.us>
+*/
 #ifndef GNC_OPTION_DATE_HPP_
 #define GNC_OPTION_DATE_HPP_
 
@@ -78,14 +85,96 @@ constexpr unsigned relative_date_periods =
 
 using RelativeDatePeriodVec = std::vector<RelativeDatePeriod>;
 
+/**
+ * Report whether the relative date represents a period offset to today's date
+ * rather than the beginning or end of a date range. For example ONE_MONTH_AGO
+ * will be made concrete as the same day as today in the previous month.
+ *
+ * @param period The Relative Date Period to check.
+ * @return true if the date is stand-alone.
+ */
 bool gnc_relative_date_is_single(RelativeDatePeriod);
+
+/**
+ * Report whether the relative date represents the beginning of a date
+ * range. For example START_LAST_MONTH is the beginning of a range.
+ *
+ * @param period The Relative Date Period to check.
+ * @return true if the date is the beginning of a date range
+ */
 bool gnc_relative_date_is_starting(RelativeDatePeriod);
+
+/**
+ * Report whether the relative date represents the end of a date range. For
+ * example END_LAST_MONTH is the end of a range.
+ *
+ * @param period The Relative Date Period to check.
+ * @return true if the date is the end of a date range.
+ */
 bool gnc_relative_date_is_ending(RelativeDatePeriod);
+
+/**
+ * Provide the string representation of a relative date for persisting the
+ * value. This string is not localizable.
+ *
+ * @param period The relative date period.
+ * @return A constant string or nullptr if the period is ABSOLUTE. The string's
+ * lifetime will be that of the Relative Date Period. It must not be freed and
+ * should be copied if the period might be destroyed before the using code is
+ * finished.
+ */
 const char* gnc_relative_date_storage_string(RelativeDatePeriod);
+
+/**
+ * Provide the string representation of a relative date for displaying
+ * value to a user. This string is not localizable.
+ *
+ * @param period The relative date period.
+ * @return A constant string or nullptr if the period is ABSOLUTE. The string's
+ * lifetime will be that of the Relative Date Period. It must not be freed and
+ * should be copied if the period might be destroyed before the using code is
+ * finished.
+ */
 const char* gnc_relative_date_display_string(RelativeDatePeriod);
+
+/**
+ * Provide the description of a relative date. This string is localizable.
+ *
+ * @param period The relative date period.
+ * @return A constant string or nullptr if the period is ABSOLUTE. The string's
+ * lifetime will be that of the Relative Date Period. It must not be freed and
+ * should be copied if the period might be destroyed before the using code is
+ * finished.
+ */
 const char* gnc_relative_date_description(RelativeDatePeriod);
+
+/**
+ * Convert a relative date storage string back to a RelativeDatePeriod value.
+ *
+ * @param A string representation obtained from
+ * gnc_relative_date_storage_string.
+ * @return A RelativeDatePeriod value.
+ */
 RelativeDatePeriod gnc_relative_date_from_storage_string(const char*);
+
+/**
+ * Convert a RelativeDatePeriod value to a concrete time64 by applying the value to the current time. For example if it is now 3:15:42 PM local time 3 June, calling this with a period RelativeDatePeriod::ONE_WEEK_AHEAD will return a time64 representing 3:15:42 PM local time 10 June of this year.
+ *
+ * @param period The relative date period to use to calculate the concrete date.
+ * @return a time64.
+ */
 time64 gnc_relative_date_to_time64(RelativeDatePeriod);
+
+/**
+ * Add the display string to the provided std::ostream.
+ *
+ * @param stream the std::ostream to which to write the period value
+ * @param period the period value to write
+ * @return A reference to stream so that the operator can be chained.
+ */
 std::ostream& operator<<(std::ostream&, const RelativeDatePeriod);
 
 #endif //GNC_OPTION_DATE_HPP_
+
+/** @}
+    @} */
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index f3294bd1e..f71be8f0c 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -21,6 +21,15 @@
  *                                                                  *
 \********************************************************************/
 
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Options
+    @{ */
+/** @file gnc-option-impl.hpp
+    @brief Implementation templates and specializtions for GncOption values.
+    Objecte created by these templates are wrapped by the GncOption variant.
+    @author Copyright 2019-2021 John Ralls <jralls at ceridwen.us>
+*/
 #ifndef GNC_OPTION_IMPL_HPP_
 #define GNC_OPTION_IMPL_HPP_
 
@@ -46,51 +55,6 @@ extern "C"
 
 #include "gnc-option-uitype.hpp"
 
-/*
- * Unused base class to document the structure of the current Scheme option
- * vector, re-expressed in C++. The comment-numbers on the right indicate which
- * item in the Scheme vector each item implements.
- *
- * Not everything here needs to be implemented, nor will it necessarily be
- * implemented the same way. For example, part of the purpose of this redesign
- * is to convert from saving options as strings of Scheme code to some form of
- * key-value pair in the book options, so generate_restore_form() will likely be
- * supplanted with save_to_book().
-
-template <typename ValueType>
-class GncOptionBase
-{
-public:
-    virtual ~GncOption = default;
-    virtual ValueType get_value() const = 0;                             //5
-    virtual ValueType get_default_value() = 0;
-    virtual SCM get_SCM_value() = 0;
-    virtual SCM get_SCM_default_value() const = 0;                       //7
-    virtual void set_value(ValueType) = 0;                               //6
-// generate_restore_form outputs a Scheme expression (a "form") that finds an
-// option and sets it to the current value. e.g.:
-//(let ((option (gnc:lookup-option options
-//                                 "Display"
-//                                 "Amount")))
-//  ((lambda (option) (if option ((gnc:option-setter option) 'none))) option))
-// it uses gnc:value->string to generate the "'none" (or whatever the option's
-// value would be as input to the scheme interpreter).
-
-    virtual std::string generate_restore_form();                         //8
-    virtual void save_to_book(QofBook*) const noexcept;                  //9
-    virtual void read_from_book(QofBook*);                               //10
-    virtual std::vector<std::string> get_option_strings();               //15
-    virtual set_changed_callback(std::function<void(void*)>);            //14
-protected:
-    const std::string m_section;                                         //0
-    const std::string m_name;                                            //1
-    const std::string m_sort_tag;                                        //2
-    const std::type_info m_kvp_type;                                     //3
-    const std::string m_doc_string;                                      //4
-    std::function<void(void*)> m_changed_callback;   //Part of the make-option closure
-    std::function<void(void*)>m_option_widget_changed_callback;          //16
-};
-*/
 
 static const char* commodity_scm_intro{"'(commodity-scm "};
 #ifndef SWIG
@@ -98,6 +62,12 @@ size_t constexpr classifier_size_max{50};
 size_t constexpr sort_tag_size_max{10};
 #endif
 
+/** @struct OptionClassifier
+ * This class is the parent of all option implmentations. It contains the
+ * elements that the optiondb uses to retrieve option values and that the
+ * options dialog determines on which tab to place the option, in what order,
+ * and what string to display as a tooltip.
+ */
 struct OptionClassifier
 {
     std::string m_section;
@@ -112,6 +82,9 @@ struct OptionClassifier
 auto constexpr size_t_max = std::numeric_limits<std::size_t>::max();
 #endif
 
+/** @class GncOptionValue
+ *  The generic option-value class. Most option types can use this template.
+ */
 template <typename ValueType>
 class GncOptionValue : public OptionClassifier
 {
@@ -167,6 +140,12 @@ private:
     ValueType m_default_value;
 };
 
+/** class GncOptionValidatedValue
+ *  Validated values have an additional member function, provided as a
+ *  constructor argument, that checks value parameters for some property before
+ *  setting the object's value member. If the function returns false a
+ *  std::invalid_argument exception is thrown.
+ */
 template <typename ValueType>
 class GncOptionValidatedValue : public OptionClassifier
 {
@@ -353,7 +332,7 @@ operator>> <GncOptionValue<bool>>(std::istream& iss,
 }
 #endif // SWIG
 
-/**
+/** @class GncOptionRangeValue
  * Used for numeric ranges and plot sizes.
  */
 
@@ -461,7 +440,8 @@ using GncMultichoiceOptionEntry = std::tuple<const std::string,
 using GncMultichoiceOptionIndexVec = std::vector<std::size_t>;
 using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
 
-/** Multichoice options have a vector of valid options
+/** @class GncOptionMultichoiceValue
+ * Multichoice options have a vector of valid options
  * (GncMultichoiceOptionChoices) and validate the selection as being one of
  * those values. The value is the index of the selected item in the vector. The
  * tuple contains three strings, a key, and a display
@@ -731,7 +711,7 @@ using GncOptionAccountList = std::vector<const Account*>;
 
 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
 
-/** Account options
+/** @class GncOptionAccountListValue
  *
  * Set one or more accounts on which to report, optionally restricted to certain
  * account types. Many calls to make-account-list-option will pass a get-default
@@ -853,6 +833,10 @@ operator>> <GncOptionAccountListValue>(std::istream& iss,
     return iss;
 }
 
+/* @class GncOptionAccountSelValue
+ * Like GncOptionAccountListValue but contains only a single account.
+ */
+
 class GncOptionAccountSelValue : public OptionClassifier
 {
 public:
@@ -936,8 +920,8 @@ operator>> <GncOptionAccountSelValue>(std::istream& iss,
     return iss;
 }
 
-/** Date options
- * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
+/** @class GncOptionDateValue
+ * A legal date value is a pair of either a RelativeDatePeriod, the absolute
  * flag and a time64, or for legacy purposes the absolute flag and a timespec.
  */
 /*
@@ -1076,7 +1060,7 @@ operator>> <GncOptionDateValue>(std::istream& iss,
     return opt.in_stream(iss);
 }
 
-/** QofQuery Options
- */
 
 #endif //GNC_OPTION_IMPL_HPP_
+/**@}
+   @} */
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index 22098bb72..76c6e48e0 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -23,6 +23,19 @@
 #ifndef GNC_OPTION_UITYPE_HPP__
 #define GNC_OPTION_UITYPE_HPP__
 
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Options
+    @{ */
+/** @file gnc-option-uitype.hpp
+    @brief OptionUITypes
+    @author Copyright 2019-2021 John Ralls <jralls at ceridwen.us>
+*/
+
+/** @enum GncOptionUIType
+ *  Used by GncOptionClassifier to indicate to dialog-options what control
+ *  should be displayed for the option.
+ */
 enum class GncOptionUIType : unsigned int
 {
     INTERNAL,
@@ -59,3 +72,5 @@ enum class GncOptionUIType : unsigned int
 };
 
 #endif // GNC_OPTION_UITYPE_H__
+/** @}
+    @} */
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 9374959e2..0913f6d6d 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -20,6 +20,15 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  *                                                                  *
 \********************************************************************/
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Options
+    @{ */
+
+/** @file gnc-option.hpp
+    @brief C++ Public interface for individual options.
+    @author Copyright 2020-2021 John Ralls <jralls at ceridwen.us>
+*/
 
 #ifndef GNC_OPTION_HPP_
 #define GNC_OPTION_HPP_
@@ -110,6 +119,12 @@ enum class GncOptionMultichoiceKeyType
     NUMBER,
 };
 
+/** @class GncOption
+ *  @brief Represents the public interface for an option.
+ *  Polymorphism is provided by a std::variant member containing GncOptionValue
+ *  types.
+*/
+
 class GncOption
 {
 public:
@@ -144,14 +159,24 @@ public:
     void set_option_from_ui_item();
     void make_internal();
     bool is_changed() const noexcept;
+/** @returns false unless m_option contains a GncOptionMultiselectValue or
+ * GncOptionAccountListValue for which multiple selections have been enabled.
+ */
     bool is_multiselect() const noexcept;
+/** Implemented only for GncOptionNumericRange */
     template <typename ValueType> void get_limits(ValueType&, ValueType&,
                                                   ValueType&) const noexcept;
+/** Not implemented for GncOptionValue. */
     template <typename ValueType> bool validate(ValueType value) const;
+/** Implemented only for GncOptionMultiselectValue. */
     std::size_t num_permissible_values() const;
+/** Implemented only for GncOptionMultiselectValue. */
     std::size_t permissible_value_index(const char* value) const;
+/** Implemented only for GncOptionMultiselectValue. */
     const char* permissible_value(std::size_t index) const;
+/** Implemented only for GncOptionMultiselectValue. */
     const char* permissible_value_name(std::size_t index) const;
+/** Implemented only for GncOptionAccountListValue. */
     GList* account_type_list() const noexcept;
     bool is_alternate() const noexcept;
     void set_alternate(bool) noexcept;
@@ -202,6 +227,11 @@ output_color_value(std::ostream& oss, const std::string& value)
     return oss;
 }
 
+/**
+ * Free function wrapping GncOption's constructor. The type of GncOptionValue to
+ * create is determined from the UI type. Some GncOptionValue types require more
+ * parameters for their constructors and can't be created with this function.
+ */
 template<typename ValueType> GncOption*
 gnc_make_option(const char* section, const char* name,
                 const char* key, const char* doc_string,
@@ -210,9 +240,15 @@ gnc_make_option(const char* section, const char* name,
     return new GncOption(section, name, key, doc_string, value, ui_type);
 }
 
-/* To work around SWIG_Guile's typedef of SCM to unsigned long: */
+/**
+ * Free function wrapping GncOption's constructor using an SCM value.
+ * To work around SWIG_Guile's typedef of SCM to unsigned long
+ */
 GncOption* gnc_make_SCM_option(const char* section, const char* name,
                                const char* key, const char* doc_string,
                                SCM value, GncOptionUIType ui_type);
 
 #endif //GNC_OPTION_HPP_
+
+/** @}
+    @} */
diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 5fa811470..5083531d9 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -20,6 +20,14 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  *                                                                  *
 \********************************************************************/
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Options
+    @{ */
+/** @file gnc-optiondb-impl.hpp
+    @brief Implementation details for GncOptionDB.
+    @author Copyright 2019-2021 John Ralls <jralls at ceridwen.us>
+*/
 
 #ifndef GNC_OPTIONDB_P_HPP_
 #define GNC_OPTIONDB_P_HPP_
@@ -45,6 +53,10 @@ extern "C"
 
 using GncOptionVec = std::vector<GncOption>;
 
+/** class GncOptionSection
+ *  The upper-level classification implmentation. Contains the options for a
+ *  section; sections are displayed as separate tabs on the option dialog.
+ */
 class GncOptionSection
 {
     std::string m_name;
@@ -90,6 +102,10 @@ struct GncOptionDBCallback
 
 using GncCallbackVec = std::vector<GncOptionDBCallback>;
 
+/** @class GncOptionDB
+ *  Holds all of the options for a book, report, or stylesheet, organized by
+ *  GncOptionSections.
+ */
 class GncOptionDB
 {
 public:
@@ -171,3 +187,5 @@ private:
 
 
 #endif // GNC_OPTIONDB_P_HPP_
+/** @}
+    @} */
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index d2fe65540..f828536e9 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -20,7 +20,37 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  *                                                                  *
 \********************************************************************/
-
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Options
+    GnuCash Options System for Book, Report, and Stylesheet Options.
+
+    The GnuCash Options System supports two somewhat different purposes:
+    - File properties, such as business information and whether to use automatic
+      trading accounts.
+
+    - Report options for configuring and customizing reports. Most report
+      options are user-configurable through a report options dialog but some are
+      used as a way of passing enumeration values between the scheme modules
+      that generate the report.
+
+    The options system is centered on an options database or optiondb. A
+    separate optionsdb is created and instantiated for every use, so the book
+    gets one at the beginning of the session with its values loaded from KVP,
+    and every report gets one when the report is run, as do report stylesheets
+    when they are edited. Customized report and stylesheet options are saved as
+    Scheme code fragments in files in the user's GnuCash Config directory.
+
+    @note
+    Persistence via text scheme code is a security vulnerability as it
+    enables an attacker to make GnuCash execute arbitrary code. The Guile
+    interpreter affords full system access with at least the user's privileges.
+
+    @{ */
+/** @file gnc-optiondb.h
+    @brief C public interface for the Options Database.
+    @author Copyright 2019-2021 John Ralls <jralls at ceridwen.us>
+*/
 #ifndef GNC_OPTIONDB_H_
 #define GNC_OPTIONDB_H_
 
@@ -179,3 +209,6 @@ void gnc_option_db_set_scm_value(GncOptionDB*, const char*,
 }
 #endif
 #endif //GNC_OPTIONDB_H_
+
+/** @}
+    @} */
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 002efa9e3..b274155ad 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -20,6 +20,15 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  *                                                                  *
 \********************************************************************/
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Options
+    @{ */
+/** @file gnc-optiondb.hpp
+ *  @brief The primary C++ interface to options for books, reports, and
+ *  stylesheets.
+ *  @author Copyright 2019-2021 John Ralls <jralls at ceridwen.us>
+*/
 
 #ifndef GNC_OPTIONDB_HPP_
 #define GNC_OPTIONDB_HPP_

commit 00a982d97d6a628d2750611fe8a5b533cb682535
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Aug 30 13:46:10 2021 -0700

    Use Scheme to generate and parse saved option files.
    
    The saved option files being Scheme executables.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 1fbbf575a..39a9f1860 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -54,6 +54,8 @@ static const QofLogModule log_module = "gnc.optiondb";
 SCM scm_init_sw_gnc_optiondb_module(void);
 %}
 
+%ignore gnc_get_current_session(void);
+
 %include <std_string.i>
 %import <base-typemaps.i>
 %import (module="sw_engine") <gnc-budget.h>
@@ -550,6 +552,7 @@ gnc_option_test_book_destroy(QofBook* book)
 %typemap(in) GncOptionAccountList& (GncOptionAccountList acclist)
 {
     auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
+    acclist.reserve(len);
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));
@@ -737,7 +740,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             reldate_str = scm_to_utf8_string(scm_symbol_to_string(reldate_scm));
         else
             reldate_str = scm_to_utf8_string(reldate_scm);
-        
+
         auto date_iter =
             std::find_if(reldate_values.begin(), reldate_values.end(),
                          [&reldate_scm](auto val)->bool {
@@ -971,6 +974,12 @@ inline SCM return_scm_value(ValueType value)
 %template(gnc_make_owner_option) gnc_make_option<const GncOwner*>;
 
 %extend GncOption {
+    bool is_budget_option()
+    {
+        auto uitype{$self->get_ui_type()};
+        return uitype == GncOptionUIType::BUDGET;
+    }
+
     SCM get_scm_value()
     {
         if (!$self)
@@ -994,6 +1003,41 @@ inline SCM return_scm_value(ValueType value)
             }, swig_get_option($self));
     }
 
+    SCM save_scm_value()
+    {
+        const SCM plain_format_str{scm_from_utf8_string("~s")};
+        const SCM ticked_format_str{scm_from_utf8_string("'~a")};
+//scm_simple_format needs a scheme list of arguments to match the format
+//placeholders.
+        auto value{scm_list_1(GncOption_get_scm_value($self))};
+        auto uitype{$self->get_ui_type()};
+        if (uitype == GncOptionUIType::STRING ||
+            uitype == GncOptionUIType::TEXT ||
+            uitype == GncOptionUIType::FONT ||
+            uitype == GncOptionUIType::BOOLEAN ||
+            uitype == GncOptionUIType::PIXMAP
+            )
+        {
+            return scm_simple_format(SCM_BOOL_F, plain_format_str, value);
+        }
+        else if (uitype == GncOptionUIType::ACCOUNT_LIST ||
+                 uitype == GncOptionUIType::ACCOUNT_SEL ||
+                 uitype == GncOptionUIType::INVOICE ||
+                 uitype == GncOptionUIType::TAX_TABLE ||
+                 uitype == GncOptionUIType::OWNER)
+        {
+            if (value && scm_is_true(value))
+                return scm_simple_format(SCM_BOOL_F, plain_format_str, value);
+            else
+                return scm_simple_format(SCM_BOOL_F, plain_format_str,
+                                         SCM_BOOL_F);
+        }
+        else
+        {
+            return scm_simple_format(SCM_BOOL_F, ticked_format_str, value);
+        }
+    }
+
     void set_value_from_scm(SCM new_value)
     {
         if (!$self)
@@ -1066,7 +1110,7 @@ inline SCM return_scm_value(ValueType value)
                             option.set_default_value(scm_absolute_date_to_time64(new_value));
                         else
                             option.set_default_value(scm_relative_date_get_period(new_value));
-                        return;
+                       return;
                     }
                     if constexpr (is_same_decayed_v<decltype(option),
                                   GncOptionMultichoiceValue>)
@@ -1137,6 +1181,30 @@ inline SCM return_scm_value(ValueType value)
 %template(gnc_register_number_range_option_int) gnc_register_number_range_option<int>;
 
 %inline %{
+    /* qof_book_set_data isn't exported by sw-engine and we need it to set up a
+     * commodity namespace table to test currencies.*/
+    static void
+    test_book_set_data(QofBook* book, const char* key, void* data)
+    {
+        qof_book_set_data(book, key, data);
+    }
+
+    static void
+    test_book_clear_data(QofBook* book, const char* key)
+    {
+        qof_book_set_data(book, key, nullptr);
+    }
+
+    static void
+    test_book_set_default_budget(QofBook* book, GncBudget* budget)
+    {
+        auto budget_guid{gnc_budget_get_guid(budget)};
+        qof_book_begin_edit(book);
+        qof_instance_set(QOF_INSTANCE(book), "default-budget",
+                         budget_guid, nullptr);
+        qof_book_commit_edit(book);
+    }
+
     static GncOption*
     gnc_make_account_list_option(const char* section,
                                  const char* name, const char* key,
@@ -1473,10 +1541,28 @@ inline SCM return_scm_value(ValueType value)
             });
     }
 
-    std::string
-    gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog)
+    /** Tailred for gnc:generate-restore-forms.
+     * @param section_op A function to be called on each section name
+     * @param option_op a function to be called on each option
+     */
+    static void
+    gnc_optiondb_foreach2(GncOptionDBPtr& odb, SCM section_op,
+                          SCM option_op)
     {
-        return prolog;
+        odb->foreach_section(
+            [&section_op, &option_op](const GncOptionSectionPtr& section)
+            {
+                auto scm_name{scm_from_utf8_string(section->get_name().c_str())};
+                scm_call_1(section_op, scm_name);
+                section->foreach_option(
+                    [&option_op](auto& option)
+                    {
+                        auto optvoidptr{reinterpret_cast<void*>(
+                                const_cast<GncOption*>(&option))};
+                        auto scm_opt{scm_from_pointer(optvoidptr, nullptr)};
+                        scm_call_1(option_op, scm_opt);
+                    });
+            });
     }
 %}
 
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 33ee587bf..81bbb26a6 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -32,12 +32,6 @@
 (use-modules (ice-9 pretty-print))
 
 
-(define-public (gnc:value->string value)
-  (format #f "~s" value))
-
-(define-public (gnc:generate-restore-forms options name)
-  (let ((optiondb (options 'generate-restore-forms)))
-    (gnc-optiondb-save-to-scheme optiondb name)))
 
 (define-public (gnc:lookup-option options section name)
   (if options
@@ -136,6 +130,57 @@
                                   (gnc:option-value src-option)))))
     src-options)))
 
+;; Get scheme commands to set changed options, used to write a file that will
+;; restore a customized report or stylesheet.
+(define-public (gnc:value->string value)
+  (format #f "~s" value))
+
+(define-public (gnc:generate-restore-forms options toplevel-name)
+  (define (section-op section-name)
+    (display
+     (string-append "\n; Section: " section-name "\n\n")))
+
+  (define (gnc:option-is-budget? option)
+    (GncOption-is-budget-option option))
+
+  (define (option-op option)
+    (let ((value (gnc:option-value option))
+          (default-value (gnc:option-default-value option)))
+      (if (not (equal? value default-value))
+          (display (string-append
+                      "(let ((option (gnc:lookup-option " toplevel-name "\n"
+                      "                                 "
+                      (gnc:value->string (gnc:option-section option)) "\n"
+                      "                                 "
+                      (gnc:value->string (gnc:option-name option)) ")))\n"
+                      "  ("
+                      (cond
+                       ((gnc:option-is-budget? option)
+                       (let* ((budget (gnc:option-value option))
+                              (guid (gncBudgetGetGUID budget))
+                              (guid-string (gnc:value->string guid)))
+                              (if (string? guid-string)
+                                  (string-append
+                                   "(lambda (option) "
+                                   "(if option ((gnc:option-setter option) "
+                                   "(gnc-budget-lookup " guid-string
+                                   " (gnc-get-current-book)))))"
+                                   )
+                                  ("Failed to get GUID for budget option."))))
+                       (else
+                        (string-append
+                         "(lambda (o) (if o (gnc:option-set-value o "
+                         (GncOption-save-scm-value option) ")))"
+                        )))
+                      " option))\n\n")))))
+
+  (define (generate-forms)
+    (let ((odb (options 'generate-restore-forms)))
+      (gnc-optiondb-foreach2 odb section-op option-op)))
+
+  (with-output-to-string generate-forms))
+
+
 ;; FIXME: Fake callback functions for boolean-complex and multichoice-callback
 
 (define-public (gnc:options-register-callback section name callback options) (options 'register-callback) 1)
@@ -169,7 +214,7 @@
 (define-public (gnc:make-budget-option section name key docstring)
   (issue-deprecation-warning "gnc:make-budget-option is deprecated. Make and register the option in one command with gnc-register-color-option.")
   (let ((option (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-BUDGET))))
-    (gnc:option-set-value option
+    (gnc:option-set-default-value option
                           (gnc-budget-get-default (gnc-get-current-book)))
     option))
 (define-public (gnc:make-commodity-option section name key docstring default)
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index f4c7425da..37071adf2 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -85,10 +85,11 @@ if (HAVE_SRFI64)
     DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras")
 
   gnc_add_scheme_test_targets(scm-test-gnc-optiondb
-    SOURCES "test-gnc-optiondb.scm"
+    SOURCES "test-gnc-optiondb.scm" "test-gnc-option-scheme-output.scm"
     OUTPUT_DIR "tests"
     DEPENDS "swig-apputils-guile-cpp;scm-srfi64-extras")
   gnc_add_scheme_tests("test-gnc-optiondb.scm")
+  gnc_add_scheme_tests("test-gnc-option-scheme-output.scm")
   gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}")
 endif()
 
diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
new file mode 100644
index 000000000..d0b3147e1
--- /dev/null
+++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
@@ -0,0 +1,432 @@
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ; test-gnc-option-scheme-output.scm -- Test Scheme option i/o.     ;
+ ; Copyright (C) 2021 John Ralls <jralls at ceridwen.us>               ;
+ ;                                                                  ;
+ ; This program is free software; you can redistribute it and/or    ;
+ ; modify it under the terms of the GNU General Public License as   ;
+ ; published by the Free Software Foundation; either version 2 of   ;
+ ; the License, or (at your option) any later version.              ;
+ ;                                                                  ;
+ ; This program is distributed in the hope that it will be useful,  ;
+ ; but WITHOUT ANY WARRANTY; without even the implied warranty of   ;
+ ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    ;
+ ; GNU General Public License for more details.                     ;
+ ;                                                                  ;
+ ; You should have received a copy of the GNU General Public License;
+ ; along with this program; if not, contact:                        ;
+ ;                                                                  ;
+ ; Free Software Foundation           Voice:  +1-617-542-5942       ;
+ ; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       ;
+ ; Boston, MA  02110-1301,  USA       gnu at gnu.org                   ;
+ ;                                                                  ;
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-modules (srfi srfi-64))
+(use-modules (tests srfi64-extras))
+(use-modules (gnucash app-utils options))
+(use-modules (sw_app_utils))
+(use-modules (sw_engine))
+
+(define (run-test)
+  (test-runner-factory gnc:test-runner)
+  (test-begin "test-gnc-option-scheme-io")
+  (test-gnc-string-option-to-scheme)
+  (test-gnc-text-option-to-scheme)
+  (test-gnc-pixmap-option-to-scheme)
+  (test-gnc-currency-option-to-scheme)
+  (test-gnc-budget-option-to-scheme)
+  (test-gnc-font-option-to-scheme)
+  (test-gnc-commodity-option-to-scheme)
+  (test-gnc-date-option-to-scheme)
+  (test-gnc-multichoice-option-to-scheme)
+  (test-end "test-gnc-option-scheme-io"))
+
+(define test-unchanged-section-output-template
+  "
+; Section: foo
+
+"
+  )
+
+(define (test-string-output-template value)
+  (format #f "
+; Section: foo
+
+(let ((option (gnc:lookup-option options
+                                 \"foo\"
+                                 \"bar\")))
+  ((lambda (o) (if o (gnc:option-set-value o ~s))) option))
+
+" value))
+
+(define (test-literal-output-template value)
+  (format #f "
+; Section: foo
+
+(let ((option (gnc:lookup-option options
+                                 \"foo\"
+                                 \"bar\")))
+  ((lambda (o) (if o (gnc:option-set-value o '~s))) option))
+
+" value))
+
+(define (test-budget-output-template value)
+  (format #f "
+; Section: foo
+
+(let ((option (gnc:lookup-option options
+                                 \"foo\"
+                                 \"bar\")))
+  ((lambda (option) (if option ((gnc:option-setter option) (gnc-budget-lookup ~s (gnc-get-current-book))))) option))
+
+"
+          (gncBudgetGetGUID value)))
+
+
+(define (test-option-scheme-output make-option-func test-template default value)
+  (let ((odb (gnc:new-options))
+        (option (make-option-func "foo" "bar" "baz" "Test Option" default)))
+    (gnc:register-option odb option)
+    (test-equal test-unchanged-section-output-template
+                (gnc:generate-restore-forms odb "options"))
+    (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") value)
+    (test-equal (test-template value)
+                (gnc:generate-restore-forms odb "options"))))
+
+(define (test-gnc-string-option-to-scheme)
+  (test-begin "test-gnc-string-option-to-scheme")
+  (test-option-scheme-output gnc:make-string-option test-string-output-template
+                             "waldo" "pepper")
+  (test-end "test-gnc-string-option-to-scheme"))
+
+(define (test-gnc-text-option-to-scheme)
+  (test-begin "test-gnc-text-option-to-scheme")
+  (test-option-scheme-output gnc:make-string-option test-string-output-template
+                             ""
+"Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium
+doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore
+veritatis et quasi architecto beatae vitae dicta sunt, explicabo.")
+  (test-end "test-gnc-text-option-to-scheme"))
+
+(define (test-gnc-font-option-to-scheme)
+  (test-begin "test-gnc-font-option-to-scheme")
+  (test-option-scheme-output gnc:make-font-option test-string-output-template
+                             "URW Bookman L Bold Italic 12"
+                             "Helvetica 12")
+  (test-end "test-gnc-font-option-to-scheme"))
+
+(define (test-gnc-currency-option-to-scheme)
+  (test-begin "test-gnc-currency-option-to-scheme")
+  (let ((session (gnc-get-current-session))
+        (book (gnc-get-current-book))
+        (table (gnc-commodity-table-new)))
+    (test-book-set-data book "gnc-commodity-table" table)
+    (let ((USD (gnc-commodity-new book "United States Dollar" "CURRENCY" "USD" "" 100))
+          (EUR (gnc-commodity-new book "European Union Euro" "CURRENCY" "EUR" "" 100)))
+      (gnc-commodity-table-insert table USD)
+      (gnc-commodity-table-insert table EUR)
+      (test-option-scheme-output gnc:make-currency-option test-literal-output-template
+                               USD EUR)
+;; Garbage collection has already eaten USD and EUR.
+      (test-book-clear-data book "gnc-commodity-table")
+      (gnc-commodity-table-destroy table)
+      (gnc-clear-current-session)))
+  (test-end "test-gnc-currency-option-to-scheme"))
+
+(define (test-gnc-budget-option-to-scheme)
+ (test-begin "test-gnc-budget-option-to-scheme")
+  (let* ((session (gnc-get-current-session))
+        (book (gnc-get-current-book))
+        (budget2 (gnc-budget-new book))
+        (budget1 (gnc-budget-new book))
+        (guid1 (gncBudgetGetGUID budget1))
+        (guid2 (gncBudgetGetGUID budget2)))
+
+    (test-book-set-default-budget book budget1)
+    (gnc-budget-set-name budget1 "First Budget")
+    (gnc-budget-set-name budget2 "Second Budget")
+
+    (let ((odb (gnc:new-options))
+          (option (gnc:make-budget-option "foo" "bar" "baz" "Test Option")))
+    (gnc:register-option odb option)
+    (test-equal test-unchanged-section-output-template
+                (gnc:generate-restore-forms odb "options"))
+    (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") budget2)
+    (test-equal (gnc-budget-get-default book) budget1)
+    (test-equal (test-budget-output-template budget2)
+                (gnc:generate-restore-forms odb "options")))
+    (gnc-clear-current-session))
+  (test-end "test-gnc-budget-option-to-scheme"))
+
+(define (test-gnc-commodity-option-to-scheme)
+  (test-begin "test-gnc-commodity-option-to-scheme")
+  (let* ((book (gnc-option-test-book-new))
+         (AAPL (gnc-commodity-new book "Apple" "NASDAQ" "AAPL" "" 1))
+         (FMAGX (gnc-commodity-new book "Fidelity Magellan Fund" "FUND" "FMAGX" "" 1000)))
+  (test-option-scheme-output gnc:make-commodity-option test-literal-output-template
+                             AAPL FMAGX))
+  (test-end "test-gnc-commodity-option-to-scheme"))
+
+(define (test-gnc-bool-option-to-scheme)
+  (test-begin "test-gnc-bool-option-to-scheme")
+  (test-option-scheme-output gnc:make-simple-boolean-option test-string-output-template #f #t)
+  (test-end "test-gnc-bool-option-to-scheme"))
+
+(define (test-gnc-pixmap-option-to-scheme)
+  (test-begin "test-gnc-pixmap-option-to-scheme")
+  (test-option-scheme-output gnc:make-pixmap-option test-string-output-template "" "~/mybusiness/mylogo.png")
+  (test-end "test-gnc-pixmap-option-to-scheme"))
+
+(define (test-gnc-date-option-to-scheme)
+  (test-begin "test-gnc-date-option-to-scheme")
+  (let ((odb (gnc:new-options)))
+    (gnc:options-make-end-date! odb "foo" "bar" "baz" "Phoney Option")
+    (test-equal test-unchanged-section-output-template
+                (gnc:generate-restore-forms odb "options"))
+    (let* ((option (gnc:lookup-option odb "foo" "bar"))
+          (test-template test-literal-output-template)
+          (time (gnc-dmy2time64 25 12 2020))
+          (value `(absolute . ,time)))
+      (gnc:option-set-value option value)
+      (test-equal (test-template (GncOption-get-scm-value option))
+                  (gnc:generate-restore-forms odb "options"))
+      (set! value '(relative . end-prev-year))
+      (gnc:option-set-value option value)
+      (test-equal value (GncOption-get-scm-value option))
+      (test-equal (test-template (GncOption-get-scm-value option))
+                  (gnc:generate-restore-forms odb "options"))))
+  (test-end "test-gnc-date-option-to-scheme"))
+
+(define (test-gnc-account-options-to-scheme)
+  (define (create-account book parent type name)
+    (let ((account (xaccMallocAccount book)))
+      (xaccAccountBeginEdit account)
+      (xaccAccountSetType account type)
+      (xaccAccountSetName account name)
+      (xaccAccountBeginEdit parent)
+      (gnc-account-append-child parent account)
+      (xaccAccountCommitEdit parent)
+      (xaccAccountCommitEdit account)
+      account))
+
+  (define (make-account-tree book root)
+    (let* ((assets (create-account book root ACCT-TYPE-ASSET "Assets"))
+           (liabilities  (create-account book root ACCT-TYPE-LIABILITY "Liabilities"))
+           (equity  (create-account book root ACCT-TYPE-EQUITY "Equity"))
+           (expenses  (create-account book root ACCT-TYPE-EXPENSE "Expenses"))
+           (equity  (create-account book root ACCT-TYPE-INCOME "Income"))
+           (broker  (create-account book assets ACCT-TYPE-EQUITY "broker"))
+           (stocks  (create-account book broker ACCT-TYPE-STOCK "Stocks")))
+      (create-account book assets ACCT-TYPE-BANK "Bank")
+      (create-account book stocks ACCT-TYPE-STOCK "AAPL")
+      (create-account book stocks ACCT-TYPE-STOCK "MSFT")
+      (create-account book stocks ACCT-TYPE-STOCK "HPE")
+      (create-account book broker ACCT-TYPE-BANK "Cash Management")
+      (create-account book expenses ACCT-TYPE-EXPENSE "Food")
+      (create-account book expenses ACCT-TYPE-EXPENSE "Gas")
+      (create-account book expenses ACCT-TYPE-EXPENSE "Rent")))
+
+  (define (cleanup book root)
+;; Destroying the book destroys the account tree too
+    (gnc-option-test-book-destroy book))
+
+  (define (test-gnc-account-list-option-to-scheme)
+    (test-begin "test-gnc-account-list-option-to-scheme")
+    (let ((odb (gnc:new-options))
+          (acctlist (gnc-account-list-from-types book
+                                                 (list ACCT-TYPE-STOCK))))
+      (gnc-register-option odb
+                           (gnc:make-account-list-option
+                            "foo" "bar" "a" "baz" acctlist
+                            (lambda (ac)
+                              (let ((type (xaccAccountGetAccountType ac)))
+                                (or (eq type ACCT-TYPE-STOCK)
+                                    (eq type ACCT-TYPE-BANK)))) #t))
+      (test-equal test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let ((option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-literal-output-template)
+            (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK))))
+        (gnc-option-set-value option new-acclist)
+        (test-equal (test-template (GncOption-get-scm-value option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
+    (test-end  "test-gnc-account-list-option-to-scheme"))
+
+  (define (test-gnc-account-sel-option-to-scheme)
+    (test-begin "test-gnc-account-sel-option-to-scheme")
+    (let ((odb (gnc:new-options))
+          (acctlist (gnc-account-list-from-types book
+                                                 (list ACCT-TYPE-STOCK))))
+      (gnc-register-option odb
+                           (gnc:make-account-list-option
+                            "foo" "bar" "a" "baz" acctlist
+                            (lambda (ac)
+                              (let ((type (xaccAccountGetAccountType ac)))
+                                (or (eq type ACCT-TYPE-STOCK)
+                                    (eq type ACCT-TYPE-BANK)))) #t))
+      (test-equal test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let ((option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-literal-output-template)
+            (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK))))
+        (gnc-option-set-value option new-acclist)
+        (test-equal (test-template (GncOption-get-scm-value option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
+    (test-end  "test-gnc-account-sel-option-to-scheme"))
+
+  (let* ((book (gnc-option-test-book-new))
+         (root-account (gnc-account-create-root book)))
+    (test-group-with-cleanup "test-gnc-account-options-to-schemes"
+                             (make-account-tree book root-account)
+                             (test-gnc-account-list-option-to-scheme)
+                             (test-gnc-account-sel-option-to-scheme)
+                             (cleanup book root-account))))
+
+
+(define (test-gnc-multichoice-option-to-scheme)
+  (test-begin "test-gnc-multichoice-option-to-scheme")
+  (let ((odb (gnc:new-options))
+        (test-template test-literal-output-template)
+        (value "5"))
+    (gnc:register-option
+     odb
+     (gnc:make-multichoice-option
+          "foo" "bar" "baz" "Phoney Option" 3
+          (list (vector 'all "All")
+          (vector 1 "1") (vector 2 "2") (vector 3 "3")
+          (vector 4 "4") (vector 5 "5") (vector 6 "6"))))
+    (test-equal test-unchanged-section-output-template
+                (gnc:generate-restore-forms odb "options"))
+    (let ((option (gnc:lookup-option odb "foo" "bar")))
+      (gnc:option-set-value option value)
+      (test-equal (test-template (GncOption-get-scm-value option))
+                  (gnc:generate-restore-forms odb "options"))))
+  (test-end "test-gnc-multichoice-option-to-scheme"))
+
+(define (test-gnc-list-option-to-scheme)
+    (test-begin "test-gnc-list-option-to-scheme")
+    (let ((odb (gnc:new-options))
+          (choices (list (vector 'good "The Good")
+                         (vector 'bad  "The Bad")
+                         (vector 'ugly "The Ugly"))))
+      (gnc-register-option odb
+                           (gnc:make-list-option
+                            "foo" "bar" "a" "baz" '(bad) choices))
+      (test-equal test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let ((option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-literal-output-template))
+        (gnc-option-set-value option '(ugly))
+        (test-equal (test-template (GncOption-get-scm-value option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
+    (test-end  "test-gnc-list-option-to-scheme"))
+
+(define (test-gnc-number-range-option-to-scheme)
+    (test-begin "test-gnc-number-range-option-to-scheme")
+    (let ((odb (gnc:new-options))
+          (min-value 0.0)
+          (max-value 100.0)
+          (dec-places 2.0)
+          (step 0.10))
+      (gnc-register-option odb
+                           (gnc:make-number-range-option
+                            "foo" "bar" "a" "baz" 49.0 min-value
+                            max-value dec-places step))
+      (test-equal test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let ((option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-literal-output-template))
+        (gnc-option-set-value option 42.0)
+        (test-equal (test-template (GncOption-get-scm-value option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
+    (test-end  "test-gnc-number-range-option-to-scheme"))
+
+(define (test-gnc-number-plot-size-option-to-scheme)
+    (test-begin "test-gnc-number-plot-size-option-to-scheme")
+    (let ((odb (gnc:new-options))
+          (min-value 100)
+          (max-value 10000)
+          (dec-places 0)
+          (step 5))
+      (gnc-register-option odb
+                           (gnc:make-number-plot-size-option
+                            "foo" "bar" "a" "baz" 490 min-value
+                            max-value dec-places step))
+      (test-equal test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let ((option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-literal-output-template))
+        (gnc-option-set-value option 420)
+        (test-equal (test-template (GncOption-get-scm-value option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
+    (test-end  "test-gnc-number-plot-size-option-to-scheme"))
+
+(define (test-gnc-query-option-to-scheme)
+  (test-begin "test-gnc-number-plot-size-option-to-scheme")
+  (let ((odb (gnc:new-options))
+        (query-scm '(query-v2
+                     (terms (((("book" "guid") #f guid 3 1 ("3a5a4bc736d84b879b776ea8caadd3b2"))
+                              (("account" "guid") #f guid 3 1 ("b7e4ca23652049fca62a0e4f95296a15")))))
+                     (search-for Split)
+                     (primary-sort (("QofQueryDefaultSort") 0 #t))
+                     (secondary-sort #f)
+                     (tertiary-sort #f)
+                     (max-results -1))))
+    (gnc-register-option odb
+                         (gnc:make-query-option "__reg" "query" '()))
+      (test-equal test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let ((option (gnc:lookup-option odb "__reg" "query"))
+            (test-template test-literal-output-template))
+        (gnc-option-set-value option (gnc-scm2query query-scm))
+        (test-equal (test-template (GncOption-get-scm-value option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
+  (test-end "test-gnc-number-plot-size-option-to-scheme"))
+
+(define (test-gnc-color-option-to-scheme)
+    (test-begin "test-gnc-coloroption-to-scheme")
+    (let ((odb (gnc:new-options))
+          (default-color (list #xb2 #x22 $x22 #xff))
+          (new-color (list #x00 #xca #x3b #xff)))
+      (test-option-scheme-output gnc:make-color-option
+                                 test-literal-output-template
+                                 default-color new-color))
+    (test-end  "test-gnc-color-option-to-scheme"))
+
+(define (test-gnc-invoice-option-to-scheme)
+    (test-begin "test-gnc-invoice-option-to-scheme")
+    (let ((odb (gnc:new-options))
+          (invoice '"13b305236443451a86c5366b7f890ecb"))
+      (test-option-scheme-output gnc:make-color-option
+                                 test-literal-output-template
+                                 (lambda () '()) invoice))
+    (test-end  "test-gnc-invoice-option-to-scheme"))
+
+(define (test-gnc-owner-option-to-scheme)
+    (test-begin "test-owner-option-to-scheme")
+    (let ((odb (gnc:new-options)))
+      (gnc-register-option odb
+                           (gnc:make-owner-option "foo" "bar" "a" "baz"
+                                                  (lambda () '()) #f
+                                                  'GNC-OWNER-CUSTOMER))
+      (test-equal test-unchanged-section-output-template
+                  (gnc:generate-restore-forms odb "options"))
+      (let ((option (gnc:lookup-option odb "foo" "bar"))
+            (test-template test-literal-output-template))
+        (gnc-option-set-value option '"13b305236443451a86c5366b7f890ecb")
+        (test-equal (test-template (GncOption-get-scm-value option))
+                    (gnc:generate-restore-forms odb "options"))
+        ))
+    (test-end  "test-gnc-owner-option-to-scheme"))
+
+;; The following are saved only to KVP, no Scheme generator needed:
+;;(define (test-gnc-dateformat-option-to-scheme)
+;;(define (test-gnc-taxtable-option-to-scheme) 
+;;(define (test-gnc-counter-option-to-scheme)
+;;(define (test-gnc-counter-format-option-to-scheme)

commit 00c2e99d2ee866f21f4c78d7b01cd65711723ef9
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Aug 30 13:14:19 2021 -0700

    Convert the Scheme RelativeDatePeriod lookup table to a std::vector.
    
    From a Scheme alist. The vector can be used to find the scheme symbol
    as a direct lookup, which isn't possible with an alist, and can be
    searched for the Scheme symbol match more quickly than an alist can.

diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp
index 8d4e65529..82f963d60 100644
--- a/libgnucash/app-utils/gnc-option-date.hpp
+++ b/libgnucash/app-utils/gnc-option-date.hpp
@@ -73,6 +73,9 @@ enum class RelativeDatePeriod : int
     END_ACCOUNTING_PERIOD,
 };
 
+constexpr unsigned relative_date_periods =
+    static_cast<unsigned>(RelativeDatePeriod::END_ACCOUNTING_PERIOD) + 2;
+
 using RelativeDatePeriodVec = std::vector<RelativeDatePeriod>;
 
 bool gnc_relative_date_is_single(RelativeDatePeriod);
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 9bc3c1341..1fbbf575a 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -427,6 +427,18 @@ gnc_option_test_book_destroy(QofBook* book)
     $1 = scm_is_signed_integer($input, INT64_MAX, INT64_MIN);
 }
 
+%typemap(in) RelativeDatePeriod (RelativeDatePeriod rdp)
+{
+      if (scm_is_integer($input))
+          rdp = (RelativeDatePeriod) scm_to_int($input);
+      else if (scm_is_symbol($input))
+          rdp = scm_relative_date_get_period($input);
+      else
+          rdp = RelativeDatePeriod::TODAY;
+
+      $1 = rdp;
+}
+
 %typemap(in) RelativeDatePeriodVec& (RelativeDatePeriodVec period_set)
 {
     auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
@@ -626,61 +638,149 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
 %header %{
 
+    static std::vector<SCM> reldate_values{};
+    inline size_t index_of(RelativeDatePeriod per)
+    {
+        return static_cast<size_t>(per) + 1;
+    }
+    
+    static void init_reldate_values()
+    {
+        if (!reldate_values.empty())
+            return;
+        std::vector<SCM> tmp (relative_date_periods, SCM_BOOL_F);
+        using rdp = RelativeDatePeriod;
+        tmp[index_of(rdp::ABSOLUTE)] =
+            scm_from_utf8_symbol("absolute");
+        tmp[index_of(rdp::TODAY)] =
+            scm_from_utf8_symbol("today");
+        tmp[index_of(rdp::ONE_WEEK_AGO)] =
+            scm_from_utf8_symbol("one-week-ago");
+        tmp[index_of(rdp::ONE_WEEK_AHEAD)] =
+            scm_from_utf8_symbol("one-week-ahead");
+        tmp[index_of(rdp::ONE_MONTH_AGO)] =
+            scm_from_utf8_symbol("one-month-ago");
+        tmp[index_of(rdp::ONE_MONTH_AHEAD)] =
+            scm_from_utf8_symbol("one-month-ahead");
+        tmp[index_of(rdp::THREE_MONTHS_AGO)] =
+            scm_from_utf8_symbol("three-months-ago");
+        tmp[index_of(rdp::THREE_MONTHS_AHEAD)] =
+            scm_from_utf8_symbol("three-months-ahead");
+        tmp[index_of(rdp::SIX_MONTHS_AGO)] =
+            scm_from_utf8_symbol("six-months-ago");
+        tmp[index_of(rdp::SIX_MONTHS_AHEAD)] =
+            scm_from_utf8_symbol("six-months-ahead");
+        tmp[index_of(rdp::ONE_YEAR_AGO)] =
+            scm_from_utf8_symbol("one-year-ago");
+        tmp[index_of(rdp::ONE_YEAR_AHEAD)] =
+            scm_from_utf8_symbol("one-year-ahead");
+        tmp[index_of(rdp::START_THIS_MONTH)] =
+            scm_from_utf8_symbol("start-this-month");
+        tmp[index_of(rdp::END_THIS_MONTH)] =
+            scm_from_utf8_symbol("end-this-month");
+        tmp[index_of(rdp::START_PREV_MONTH)] =
+            scm_from_utf8_symbol("start-prev-month");
+        tmp[index_of(rdp::END_PREV_MONTH)] =
+            scm_from_utf8_symbol("end-prev-month");
+        tmp[index_of(rdp::START_NEXT_MONTH)] =
+            scm_from_utf8_symbol("start-next-month");
+        tmp[index_of(rdp::END_NEXT_MONTH)] =
+            scm_from_utf8_symbol("end-next-month");
+        tmp[index_of(rdp::START_CURRENT_QUARTER)] =
+            scm_from_utf8_symbol("start-current-quarter");
+        tmp[index_of(rdp::END_CURRENT_QUARTER)] =
+            scm_from_utf8_symbol("end-current-quarter");
+        tmp[index_of(rdp::START_PREV_QUARTER)] =
+            scm_from_utf8_symbol("start-prev-quarter");
+        tmp[index_of(rdp::END_PREV_QUARTER)] =
+            scm_from_utf8_symbol("end-prev-quarter");
+        tmp[index_of(rdp::START_NEXT_QUARTER)] =
+            scm_from_utf8_symbol("start-next-quarter");
+        tmp[index_of(rdp::END_NEXT_QUARTER)] =
+            scm_from_utf8_symbol("end-next-quarter");
+        tmp[index_of(rdp::START_CAL_YEAR)] =
+            scm_from_utf8_symbol("start-cal-year");
+        tmp[index_of(rdp::END_CAL_YEAR)] =
+            scm_from_utf8_symbol("end-cal-year");
+        tmp[index_of(rdp::START_PREV_YEAR)] =
+            scm_from_utf8_symbol("start-prev-year");
+        tmp[index_of(rdp::END_PREV_YEAR)] =
+            scm_from_utf8_symbol("end-prev-year");
+        tmp[index_of(rdp::START_NEXT_YEAR)] =
+            scm_from_utf8_symbol("start-next-year");
+        tmp[index_of(rdp::END_NEXT_YEAR)] =
+            scm_from_utf8_symbol("end-next-year");
+        tmp[index_of(rdp::START_ACCOUNTING_PERIOD)] =
+            scm_from_utf8_symbol("start-accounting-period");
+        tmp[index_of(rdp::END_ACCOUNTING_PERIOD)] =
+            scm_from_utf8_symbol("end-accounting-period");
+        reldate_values = std::move(tmp);
+    }
+
     inline static RelativeDatePeriod scm_relative_date_get_period(SCM date)
     {
-    static SCM reldate_values = SCM_BOOL_F;
-
-    if (scm_is_false(reldate_values))
-        reldate_values = scm_c_eval_string(
-            "'((absolute RelativeDatePeriod-ABSOLUTE)"
-            "(today RelativeDatePeriod-TODAY)"
-            "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)"
-            "(one-week-ahead RelativeDatePeriod-ONE-WEEK-AHEAD)"
-            "(one-month-ago RelativeDatePeriod-ONE-MONTH-AGO)"
-            "(one-month-ahead RelativeDatePeriod-ONE-MONTH-AHEAD)"
-            "(three-months-ago RelativeDatePeriod-THREE-MONTHS-AGO)"
-            "(three-months-ahead RelativeDatePeriod-THREE-MONTHS-AHEAD)"
-            "(six-months-ago RelativeDatePeriod-SIX-MONTHS-AGO)"
-            "(six-months-ahead RelativeDatePeriod-SIX-MONTHS-AHEAD)"
-            "(one-year-ago RelativeDatePeriod-ONE-YEAR-AGO)"
-            "(one-year-ahead RelativeDatePeriod-ONE-YEAR-AHEAD)"
-            "(start-this-month RelativeDatePeriod-START-THIS-MONTH)"
-            "(end-this-month RelativeDatePeriod-END-THIS-MONTH)"
-            "(start-prev-month RelativeDatePeriod-START-PREV-MONTH)"
-            "(end-prev-month RelativeDatePeriod-END-PREV-MONTH)"
-            "(start-next-month RelativeDatePeriod-START-NEXT-MONTH)"
-            "(end-next-month RelativeDatePeriod-END-NEXT-MONTH)"
-            "(start-current-quarter RelativeDatePeriod-START-CURRENT-QUARTER)"
-            "(end-current-quarter RelativeDatePeriod-END-CURRENT-QUARTER)"
-            "(start-prev-quarter RelativeDatePeriod-START-PREV-QUARTER)"
-            "(end-prev-quarter RelativeDatePeriod-END-PREV-QUARTER)"
-            "(start-next-quarter RelativeDatePeriod-START-NEXT-QUARTER)"
-            "(end-next-quarter RelativeDatePeriod-END-NEXT-QUARTER)"
-            "(start-cal-year RelativeDatePeriod-START-CAL-YEAR)"
-            "(end-cal-year RelativeDatePeriod-END-CAL-YEAR)"
-            "(start-prev-year RelativeDatePeriod-START-PREV-YEAR)"
-            "(end-prev-year RelativeDatePeriod-END-PREV-YEAR)"
-            "(start-next-year RelativeDatePeriod-START-NEXT-YEAR)"
-            "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)"
-            "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)"
-            "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))");
-
-    auto reldate_scm{scm_is_pair(date) ? scm_cdr(date) : date};
-    auto reldate{scm_primitive_eval(scm_assq_ref(reldate_values,
-                                                 reldate_scm))};
-    return static_cast<RelativeDatePeriod>(scm_to_int(reldate));
+        init_reldate_values();
+        auto reldate_scm{scm_is_pair(date) ? scm_cdr(date) : date};
+        SCM reldate_val{SCM_BOOL_F};
+        if (scm_is_procedure(reldate_scm))
+            reldate_val = scm_call_0(reldate_scm);
+        if (scm_is_number(reldate_scm))
+            reldate_val = reldate_scm;
+        if (scm_is_number(reldate_val))
+        {
+            auto reldate_index = scm_to_int(reldate_val);
+            assert(reldate_index >= static_cast<int>(RelativeDatePeriod::ABSOLUTE) && reldate_index < static_cast<int>(relative_date_periods - 1));
+            return static_cast<RelativeDatePeriod>(reldate_index);
+        }
+        const char* reldate_str;
+        if (scm_is_symbol(reldate_scm))
+            reldate_str = scm_to_utf8_string(scm_symbol_to_string(reldate_scm));
+        else
+            reldate_str = scm_to_utf8_string(reldate_scm);
+        
+        auto date_iter =
+            std::find_if(reldate_values.begin(), reldate_values.end(),
+                         [&reldate_scm](auto val)->bool {
+                             return scm_is_eq(val, reldate_scm) == 1;
+                         });
+        if (date_iter == reldate_values.end())
+            return RelativeDatePeriod::ABSOLUTE;
+        return static_cast<RelativeDatePeriod>(date_iter - reldate_values.begin() - 1);
+    }
 
+    inline static SCM scm_relative_date_from_period(RelativeDatePeriod period)
+    {
+        init_reldate_values();
+        return reldate_values[static_cast<size_t>(period) + 1];
     }
 
     inline static bool scm_date_absolute(SCM date)
     {
         if (scm_is_pair(date))
         {
-            auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(date)))};
-            if (strcmp(car, "relative") == 0)
-                return false;
+            if (scm_is_symbol(scm_car(date)))
+            {
+                auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(date)))};
+                auto cdr{scm_cdr(date)};
+                if (strcmp(car, "relative") == 0)
+                    return false;
+                if (strcmp(car, "absolute") == 0)
+                    return true;
+
+                assert(false);
+            }
+            else
+            {
+                auto cdr{scm_cdr(date)};
+                if (scm_is_symbol(cdr))
+                    return false;
+                if (scm_is_number(cdr))
+                    return true;
+
+                assert(false);
+            }
         }
-        return true;
+        return (!(scm_is_symbol(date) || scm_is_string(date)));
     }
 
     inline static time64 scm_absolute_date_to_time64(SCM date)
@@ -808,16 +908,39 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return scm_cons(desig, scm_from_int(val));
     }
 
+    static SCM
+    get_scm_value(const GncOptionDateValue& option)
+    {
+        if (option.get_period() == RelativeDatePeriod::ABSOLUTE)
+            return scm_cons(scm_from_utf8_symbol("absolute"),
+                            scm_from_value(option.get_value()));
+        else
+            return scm_cons(scm_from_utf8_symbol("relative"),
+                            scm_relative_date_from_period(option.get_period()));
+    }
+
+    static SCM
+    get_scm_default_value(const GncOptionDateValue& option)
+    {
+        if (option.get_default_period() == RelativeDatePeriod::ABSOLUTE)
+            return scm_cons(scm_from_utf8_symbol("absolute"),
+                            scm_from_value(option.get_default_value()));
+        else
+            return scm_cons(scm_from_utf8_symbol("relative"),
+                            scm_relative_date_from_period(option.get_default_period()));
+    }
+
 template <typename T>
-struct is_MultichoiceOrRange
+struct is_MultichoiceDateOrRange
 {
     static constexpr bool value =
         is_same_decayed_v<T, GncOptionMultichoiceValue> ||
-        is_same_decayed_v<T, GncOptionRangeValue<int>>;
+        is_same_decayed_v<T, GncOptionRangeValue<int>> ||
+        is_same_decayed_v<T, GncOptionDateValue>;
 };
 
 template <typename T>
-inline constexpr bool is_MultichoiceOrRange_v = is_MultichoiceOrRange<T>::value;
+inline constexpr bool is_MultichoiceDateOrRange_v = is_MultichoiceDateOrRange<T>::value;
 
 template <typename ValueType>
 inline SCM return_scm_value(ValueType value)
@@ -853,7 +976,7 @@ inline SCM return_scm_value(ValueType value)
         if (!$self)
             return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
-                if constexpr (is_MultichoiceOrRange_v<decltype(option)>)
+                if constexpr (is_MultichoiceDateOrRange_v<decltype(option)>)
                     return get_scm_value(option);
                 auto value{option.get_value()};
                 return return_scm_value(value);
@@ -864,7 +987,7 @@ inline SCM return_scm_value(ValueType value)
         if (!$self)
             return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
-                if constexpr (is_MultichoiceOrRange_v<decltype(option)>)
+                if constexpr (is_MultichoiceDateOrRange_v<decltype(option)>)
                     return get_scm_default_value(option);
                 auto value{option.get_default_value()};
                 return return_scm_value(value);
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index ad8882463..30b5da1d0 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -202,9 +202,9 @@
                                              "baz" "Phony Option"
                                              (RelativeDatePeriod-TODAY)))
          (a-time (gnc-dmy2time64 11 07 2019)))
-    (test-equal (current-time) (gnc-option-value option-db "foo" "bar"))
+    (test-equal '(relative . today) (gnc-option-value option-db "foo" "bar"))
     (gnc-set-option option-db "foo" "bar" a-time)
-    (test-equal a-time (gnc-option-value option-db "foo" "bar")))
+    (test-equal `(absolute . ,a-time) (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-test-date-option"))
 
 (define (test-gnc-make-date-set-option)
@@ -212,7 +212,7 @@
   (let* ((option-db (new-gnc-optiondb))
          (date-opt (gnc-register-date-option-set
                     option-db "foo" "bar" "baz" "Phony Option"
-                    `(today
+                    '(today
                           start-this-month
                           start-prev-month
                           start-current-quarter
@@ -221,7 +221,7 @@
                           start-cal-year
                           start-prev-year
                           start-accounting-period) #t)))
-    (test-equal (gnc-accounting-period-fiscal-start)
+    (test-equal '(relative . start-accounting-period)
                 (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-test-date-set-option"))
 

commit bed44f404f0c30ca6a36f9863250134dfb9e6612
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Aug 28 16:52:36 2021 -0700

    Remove the always questionable Scheme generation and parsing code.
    
    It's more reasonable to do that in Scheme than in C++.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 373409be4..f3294bd1e 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -220,8 +220,6 @@ public:
     }
     void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
-    std::ostream& to_scheme(std::ostream&) const;
-    std::istream& from_scheme(std::istream&);
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
 private:
@@ -353,85 +351,6 @@ operator>> <GncOptionValue<bool>>(std::istream& iss,
     opt.set_value(instr == "#t" ? true : false);
     return iss;
 }
-template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                GncOptionValidatedValue<const QofInstance*>> ||
-                                   std::is_same_v<std::decay_t<OptType>,
-                                      GncOptionValue<const QofInstance*>>,
-                                   int> = 0>
-inline std::ostream&
-gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
-{
-    auto value = opt.get_value();
-    auto type = opt.get_ui_type();
-    if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
-    {
-        if (type == GncOptionUIType::COMMODITY)
-        {
-            oss << commodity_scm_intro;
-            oss << "\"" <<
-                gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" ";
-        }
-
-        oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\"";
-
-        if (type == GncOptionUIType::COMMODITY)
-        {
-            oss << ")";
-        }
-        return oss;
-    }
-
-    if constexpr (std::is_same_v<std::decay_t<decltype(value)>, std::string>)
-    {
-        if (type == GncOptionUIType::COLOR)
-            return output_color_value(oss, value);
-    }
-
-    oss << "\"" << qof_instance_to_string(value) << "\"";
-    return oss;
-}
-
-template<class OptType,
-         typename std::enable_if_t<
-             std::is_same_v<std::decay_t<OptType>,
-                            GncOptionValidatedValue<const QofInstance*>> ||
-             std::is_same_v<std::decay_t<OptType>,
-                            GncOptionValue<const QofInstance*>>, int> = 0>
-inline std::istream&
-gnc_option_from_scheme (std::istream& iss, OptType& opt)
-{
-    std::string instr;
-    auto type = opt.get_ui_type();
-    if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
-    {
-        std::string name_space, mnemonic;
-        if (type == GncOptionUIType::COMMODITY)
-        {
-            iss.ignore(strlen(commodity_scm_intro) + 1, '"');
-            std::getline(iss, name_space, '"');
-        }
-        else
-            name_space = GNC_COMMODITY_NS_CURRENCY;
-        iss.ignore(2, '"');
-        std::getline(iss, mnemonic, '"');
-
-        if (type == GncOptionUIType::COMMODITY)
-            iss.ignore(2, ')');
-        else
-            iss.ignore(1, '"');
-
-        instr = name_space + ":";
-        instr += mnemonic;
-     }
-    else
-    {
-        iss.ignore(1, '"');
-        std::getline(iss, instr, '"');
-    }
-    opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
-    return iss;
-}
 #endif // SWIG
 
 /**
@@ -807,70 +726,6 @@ operator>> <GncOptionMultichoiceValue>(std::istream& iss,
     return iss;
 }
 
-template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionMultichoiceValue>,
-                                   int> = 0>
-inline std::ostream&
-gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
-{
-    auto indexes{opt.get_multiple()};
-    if (indexes.size() > 1)
-        oss << "'(";
-    bool first = true;
-    for (auto index : indexes)
-    {
-        if (first)
-            first = false;
-        else
-            oss << " ";
-        oss << "'" << opt.permissible_value(index);
-    }
-    if (indexes.size() > 1)
-        oss << ')';
-    return oss;
-}
-
-template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionMultichoiceValue>,
-                                   int> = 0>
-inline std::istream&
-gnc_option_from_scheme(std::istream& iss, OptType& opt)
-{
-    iss.ignore(3, '\'');
-    auto c{iss.peek()};
-    if (static_cast<char>(c) == '(')
-    {
-        GncMultichoiceOptionIndexVec values;
-        iss.ignore(3, '\'');
-        while (true)
-        {
-            std::string str;
-            std::getline(iss, str, ' ');
-            if (!str.empty())
-            {
-                if (str.back() == ')')
-                {
-                    str.pop_back();
-                    break;
-                }
-                values.push_back(opt.permissible_value_index(str.c_str()));
-                iss.ignore(2, '\'');
-            }
-            else
-                break;
-        }
-        opt.set_multiple(values);
-    }
-    else
-    {
-        std::string str;
-        std::getline(iss, str, ' ');
-        opt.set_value(str);
-    }
-    return iss;
-}
 
 using GncOptionAccountList = std::vector<const Account*>;
 
@@ -998,54 +853,6 @@ operator>> <GncOptionAccountListValue>(std::istream& iss,
     return iss;
 }
 
-template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionAccountListValue>,
-                                   int> = 0>
-inline std::ostream&
-gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
-{
-    auto values{opt.get_value()};
-    oss << "'(\"";
-    bool first = true;
-    for (auto value : values)
-    {
-        if (first)
-            first = false;
-        else
-            oss << " \"";
-        oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"';
-    }
-    oss << ')';
-    return oss;
-}
-
-template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionAccountListValue>,
-                                   int> = 0>
-inline std::istream&
-gnc_option_from_scheme(std::istream& iss, OptType& opt)
-{
-    GncOptionAccountList values;
-    iss.ignore(3, '"');
-    while (true)
-    {
-        std::string str;
-        std::getline(iss, str, '"');
-        if (!str.empty())
-        {
-            values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type()));
-            iss.ignore(2, '"');
-        }
-        else
-            break;
-    }
-    opt.set_value(values);
-    iss.ignore(1, ')');
-    return iss;
-}
-
 class GncOptionAccountSelValue : public OptionClassifier
 {
 public:
@@ -1129,46 +936,6 @@ operator>> <GncOptionAccountSelValue>(std::istream& iss,
     return iss;
 }
 
-template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionAccountSelValue>,
-                                   int> = 0>
-inline std::ostream&
-gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
-{
-    auto value{opt.get_value()};
-    oss << "'(\"";
-        oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"';
-    oss << ')';
-    return oss;
-}
-
-template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionAccountSelValue>,
-                                   int> = 0>
-inline std::istream&
-gnc_option_from_scheme(std::istream& iss, OptType& opt)
-{
-    const Account* value;
-    iss.ignore(3, '"');
-    while (true)
-    {
-        std::string str;
-        std::getline(iss, str, '"');
-        if (!str.empty())
-        {
-            value = (Account*)qof_instance_from_string(str, opt.get_ui_type());
-            iss.ignore(2, '"');
-        }
-        else
-            break;
-    }
-    opt.set_value(value);
-    iss.ignore(1, ')');
-    return iss;
-}
-
 /** Date options
  * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
  * flag and a time64, or for legacy purposes the absolute flag and a timespec.
@@ -1312,45 +1079,4 @@ operator>> <GncOptionDateValue>(std::istream& iss,
 /** QofQuery Options
  */
 
-inline std::istream&
-gnc_option_from_scheme(std::istream& iss, GncOptionValue<const GncOwner*>& opt)
-{
-//FIXME: Implement or maybe rethink.
-    return iss;
-}
-
-inline std::ostream&
-gnc_option_to_scheme(std::ostream& oss, GncOptionValue<const GncOwner*>& opt)
-{
-//FIXME: Implement or maybe rethink.
-    return oss;
-}
-
-inline std::istream&
-gnc_option_from_scheme(std::istream& iss, GncOptionValue<const QofQuery*>& opt)
-{
-//FIXME: Implement or maybe rethink.
-    return iss;
-}
-
-inline std::ostream&
-gnc_option_to_scheme(std::ostream& oss, GncOptionValue<const QofQuery*>& opt)
-{
-//FIXME: Implement or maybe rethink.
-    return oss;
-}
-
-inline std::istream&
-gnc_option_from_scheme(std::istream& iss, GncOptionValidatedValue<const QofQuery*>& opt)
-{
-//FIXME: Implement or maybe rethink.
-    return iss;
-}
-
-inline std::ostream&
-gnc_option_to_scheme(std::ostream& oss, GncOptionValidatedValue<const QofQuery*>& opt)
-{
-//FIXME: Implement or maybe rethink.
-    return oss;
-}
 #endif //GNC_OPTION_IMPL_HPP_
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index b28a92579..b96405de0 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -52,28 +52,28 @@ GncOption::get_value() const
         [](const auto option)->ValueType {
             if constexpr (is_same_decayed_v<decltype(option.get_value()),
                           ValueType>)
-                                           return option.get_value();
+                return option.get_value();
             if constexpr (is_same_decayed_v<decltype(option),
                           GncOptionDateValue>)
             {
                 if constexpr (is_same_decayed_v<ValueType,
-                                        RelativeDatePeriod>)
-                              return option.get_period();
+                              RelativeDatePeriod>)
+                    return option.get_period();
                 if constexpr (std::is_same_v<ValueType, size_t>)
-                              return option.get_period_index();
+                    return option.get_period_index();
                 return ValueType{};
             }
             if constexpr (is_same_decayed_v<decltype(option),
                           GncOptionMultichoiceValue>)
             {
                 if constexpr (std::is_same_v<ValueType, size_t>)
-                              return option.get_index();
+                    return option.get_index();
                 if constexpr (is_same_decayed_v<ValueType,
-                               GncMultichoiceOptionIndexVec>)
-                              return option.get_multiple();
+                              GncMultichoiceOptionIndexVec>)
+                    return option.get_multiple();
             }
-                          return ValueType {};
-                      }, *m_option);
+            return ValueType {};
+        }, *m_option);
 }
 
 template <typename ValueType> ValueType
@@ -83,25 +83,25 @@ GncOption::get_default_value() const
         [](const auto option)->ValueType {
             if constexpr (is_same_decayed_v<decltype(option.get_value()),
                           ValueType>)
-                                           return option.get_default_value();
+                return option.get_default_value();
             if constexpr (is_same_decayed_v<decltype(option),
                           GncOptionDateValue>)
             {
                 if constexpr (is_same_decayed_v<ValueType,
-                                        RelativeDatePeriod>)
-                              return option.get_default_period();
+                              RelativeDatePeriod>)
+                    return option.get_default_period();
                 if constexpr (std::is_same_v<ValueType, size_t>)
-                              return option.get_default_period_index();
+                    return option.get_default_period_index();
                 return ValueType{};
             }
-                          if constexpr
+            if constexpr
                 (is_same_decayed_v<decltype(option),
-                               GncOptionMultichoiceValue> &&
+                 GncOptionMultichoiceValue> &&
                  is_same_decayed_v<ValueType,
-                               GncMultichoiceOptionIndexVec>)
-                              return option.get_default_multiple();
-                          return ValueType {};
-                      }, *m_option);
+                 GncMultichoiceOptionIndexVec>)
+                return option.get_default_multiple();
+            return ValueType {};
+        }, *m_option);
 
 }
 
@@ -110,26 +110,26 @@ GncOption::set_value(ValueType value)
 {
     std::visit(
         [value](auto& option) {
-                   if constexpr
+            if constexpr
                 (is_same_decayed_v<decltype(option.get_value()), ValueType> ||
                  (is_same_decayed_v<decltype(option),
-                         GncOptionDateValue> &&
+                  GncOptionDateValue> &&
                   (is_same_decayed_v<ValueType, RelativeDatePeriod> ||
                    std::is_same_v<ValueType, size_t>)))
-                       option.set_value(value);
+                option.set_value(value);
             if constexpr (is_same_decayed_v<decltype(option),
-                        GncOptionMultichoiceValue>)
-                   {
+                          GncOptionMultichoiceValue>)
+            {
                 if constexpr (is_same_decayed_v<ValueType,
-                                     GncMultichoiceOptionIndexVec>)
-                           option.set_multiple(value);
-                       else if constexpr
-                           (std::is_same_v<ValueType, size_t> ||
+                              GncMultichoiceOptionIndexVec>)
+                    option.set_multiple(value);
+                else if constexpr
+                    (std::is_same_v<ValueType, size_t> ||
                      is_same_decayed_v<ValueType, std::string> ||
-                            std::is_same_v<std::remove_cv<ValueType>, char*>)
-                           option.set_value(value);
-                   }
-               }, *m_option);
+                     std::is_same_v<std::remove_cv<ValueType>, char*>)
+                    option.set_value(value);
+            }
+        }, *m_option);
 }
 
 template <typename ValueType> void
@@ -137,25 +137,25 @@ GncOption::set_default_value(ValueType value)
 {
     std::visit(
         [value](auto& option) {
-                   if constexpr
+            if constexpr
                 (is_same_decayed_v<decltype(option.get_value()), ValueType>||
                  (is_same_decayed_v<decltype(option), GncOptionDateValue> &&
                   (is_same_decayed_v<ValueType, RelativeDatePeriod> ||
                    std::is_same_v<ValueType, size_t>)))
-                       option.set_default_value(value);
+                option.set_default_value(value);
             if constexpr (is_same_decayed_v<decltype(option),
-                        GncOptionMultichoiceValue>)
-                   {
+                          GncOptionMultichoiceValue>)
+            {
                 if constexpr (is_same_decayed_v<ValueType,
-                            GncMultichoiceOptionIndexVec>)
+                              GncMultichoiceOptionIndexVec>)
                     option.set_default_multiple(value);
-                       else if constexpr
-                           (std::is_same_v<ValueType, size_t> ||
+                else if constexpr
+                    (std::is_same_v<ValueType, size_t> ||
                      is_same_decayed_v<ValueType, std::string> ||
-                            std::is_same_v<std::remove_cv<ValueType>, char*>)
-                           option.set_default_value(value);
-                   }
-               }, *m_option);
+                     std::is_same_v<std::remove_cv<ValueType>, char*>)
+                    option.set_default_value(value);
+            }
+        }, *m_option);
 }
 void
 GncOption::reset_default_value()
@@ -289,11 +289,11 @@ GncOption::is_multiselect() const noexcept
     return std::visit(
         [](const auto& option)->bool {
             if constexpr (is_same_decayed_v<decltype(option),
-                                        GncOptionAccountListValue>)
-                              return option.is_multiselect();
-                          else
-                              return false;
-                      }, *m_option);
+                          GncOptionAccountListValue>)
+                return option.is_multiselect();
+            else
+                return false;
+        }, *m_option);
 }
 
 template<typename ValueType> bool
@@ -302,19 +302,19 @@ GncOption::validate(ValueType value) const
     return std::visit(
         [value] (const auto& option) -> bool {
             if constexpr ((is_same_decayed_v<decltype(option),
-                                         GncOptionMultichoiceValue> &&
+                           GncOptionMultichoiceValue> &&
                            is_same_decayed_v<ValueType,
-                                         std::string>) ||
+                           std::string>) ||
                           (is_same_decayed_v<decltype(option),
-                                         GncOptionMultichoiceValue> &&
+                           GncOptionMultichoiceValue> &&
                            is_same_decayed_v<ValueType,
-                                         GncMultichoiceOptionIndexVec>) ||
+                           GncMultichoiceOptionIndexVec>) ||
                           is_same_decayed_v<decltype(option),
-                                        GncOptionValidatedValue<ValueType>>)
-                                           return option.validate(value);
-                          else
-                              return false;
-                      }, *m_option);
+                          GncOptionValidatedValue<ValueType>>)
+                return option.validate(value);
+            else
+                return false;
+        }, *m_option);
 }
 
 std::size_t
@@ -323,13 +323,13 @@ GncOption::num_permissible_values() const
     return std::visit(
         [] (const auto& option) -> size_t {
             if constexpr (is_same_decayed_v<decltype(option),
-                                        GncOptionMultichoiceValue>  ||
+                          GncOptionMultichoiceValue>  ||
                           is_same_decayed_v<decltype(option),
-                                        GncOptionDateValue>)
-                                           return option.num_permissible_values();
-                          else
-                              return size_t_max;
-                      }, *m_option);
+                          GncOptionDateValue>)
+                return option.num_permissible_values();
+            else
+                return size_t_max;
+        }, *m_option);
 }
 
 std::size_t
@@ -338,13 +338,13 @@ GncOption::permissible_value_index(const char* value) const
     return std::visit(
         [&value] (const auto& option) -> size_t {
             if constexpr (is_same_decayed_v<decltype(option),
-                                        GncOptionMultichoiceValue>  ||
+                                            GncOptionMultichoiceValue> ||
                           is_same_decayed_v<decltype(option),
-                                        GncOptionDateValue>)
-                                           return option.permissible_value_index(value);
-                          else
-                              return size_t_max;;
-                      }, *m_option);
+                                            GncOptionDateValue>)
+                return option.permissible_value_index(value);
+            else
+                return size_t_max;;
+        }, *m_option);
 }
 
 const char*
@@ -424,125 +424,6 @@ GncOption::in_stream(std::istream& iss)
                       }, *m_option);
 }
 
-std::ostream&
-GncOption::to_scheme(std::ostream& oss) const
-{
-    return std::visit([&oss](auto& option) ->std::ostream& {
-                          if constexpr
-                              ((std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionAccountListValue>) ||
-                               (std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionMultichoiceValue>) ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionValue<const QofInstance*>> ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionValidatedValue<const QofInstance*>>)
-                              gnc_option_to_scheme(oss, option);
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionDateValue>)
-                                  oss << "'(" << option << ")";
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                               std::string>)
-                          {
-                              if (option.get_ui_type() == GncOptionUIType::COLOR)
-                                  output_color_value(oss, option.get_value());
-                              else
-                                  oss << '"' << option << '"';
-                          }
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionRangeValue<int>> ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionRangeValue<double>>)
-                          {
-                              if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
-                                  oss << "'(" << (option.is_alternate() ?
-                                          "\"percent\" . " : "\"pixels\" . ");
-                              oss << option.get_value();
-                              if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
-                                  oss << ")";
-                          }
-                          else
-                              oss << option;
-                          return oss;
-                      }, *m_option);
-}
-
-std::istream&
-GncOption::from_scheme(std::istream& iss)
-{
-    return std::visit([&iss](auto& option) -> std::istream& {
-                          if constexpr
-                              ((std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionAccountListValue>) ||
-                               (std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionMultichoiceValue>) ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionValue<const GncOwner*>> ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionValue<const QofQuery*>> ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionValue<const QofInstance*>> ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionValidatedValue<const QofQuery*>> ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionValidatedValue<const QofInstance*>>)
-                              gnc_option_from_scheme(iss, option);
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionDateValue>)
-                          {
-                              iss.ignore(2, '(');
-                              iss >> option;
-                              //operator >> clears the trailing ')'
-                          }
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                               std::string>)
-                          {
-                              iss.ignore(1, '"');
-                              std::string input;
-                              std::getline(iss, input, '"');
-                              option.set_value(input);
-                          }
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionRangeValue<int>> ||
-                               std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionRangeValue<double>>)
-                          {
-                              if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
-                              {
-                                  iss.ignore(3, '"');
-                                  std::string alt;
-                                  iss >> alt;
-                                  option.set_alternate(
-                                      strncmp(alt.c_str(), "pixels",
-                                              strlen("pixels")) == 0);
-                                  iss.ignore(4, ' ');
-                              }
-                              if constexpr(std::is_same_v<std::decay_t<decltype(option)>,
-                                           GncOptionRangeValue<int>>)
-                              {
-                                  int val;
-                                  iss >> val;
-                                  option.set_value(val);
-                              }
-                              else
-                              {
-                                  double val;
-                                  iss >> val;
-                                  option.set_value(val);
-                              }
-                          }
-                          else
-                              iss >> option;
-                          return iss;
-                      }, *m_option);
-}
-
 GncOption*
 gnc_make_SCM_option(const char* section, const char* name,
                     const char* key, const char* doc_string,
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index e873b0d70..9374959e2 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -157,9 +157,6 @@ public:
     void set_alternate(bool) noexcept;
     std::ostream& out_stream(std::ostream& oss) const;
     std::istream& in_stream(std::istream& iss);
-    std::ostream& to_scheme(std::ostream& oss) const;
-    std::istream& from_scheme(std::istream& iss);
-
 
     friend GncOptionVariant& swig_get_option(GncOption*);
 
diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 97885c0fe..5fa811470 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -148,18 +148,10 @@ public:
         return const_cast<GncOption*>(static_cast<const GncOptionDB&>(*this).find_option(section, name));
     }
     const GncOption* find_option(const std::string& section, const char* name) const;
-    std::ostream& save_to_scheme(std::ostream& oss,
-                                 const char* options_prolog) const noexcept;
-    std::istream& load_from_scheme(std::istream& iss) noexcept;
     std::ostream& save_to_key_value(std::ostream& oss) const noexcept;
     std::istream& load_from_key_value(std::istream& iss);
     void save_to_kvp(QofBook* book, bool clear_book) const noexcept;
     void load_from_kvp(QofBook* book) noexcept;
-    std::ostream& save_option_scheme(std::ostream& oss,
-                                     const char* option_prolog,
-                                     const std::string& section,
-                                     const std::string& name) const noexcept;
-    std::istream& load_option_scheme(std::istream& iss);
     std::ostream& save_option_key_value(std::ostream& oss,
                                         const std::string& section,
                                         const std::string& name) const noexcept;
@@ -175,14 +167,6 @@ private:
 
     std::function<GncOptionUIItem*()> m_get_ui_value;
     std::function<void(GncOptionUIItem*)> m_set_ui_value;
-    static constexpr char const* const scheme_tags[]
-    {
-        "(let ((option (gnc:lookup-option ",
-        "                                 ",
-        ")))",
-        "   ((lambda (o) (if o (gnc:option-set-value o ",
-        "))) option))"
-        };
 };
 
 
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index be005b1c1..133949c6a 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -261,26 +261,6 @@ GncOptionDB::make_internal(const char* section, const char* name)
         db_opt->make_internal();
 }
 
-std::ostream&
-GncOptionDB::save_option_scheme(std::ostream& oss,
-                                const char* option_prolog,
-                                const std::string& section,
-                                const std::string& name) const noexcept
-{
-    auto db_opt = find_option(section, name.c_str());
-
-    if (!db_opt || !db_opt->is_changed())
-        return oss;
-    oss << scheme_tags[0] << option_prolog << "\n";
-    oss << scheme_tags[1] << '"' << section.substr(0, classifier_size_max) << "\"\n";
-    oss << scheme_tags[1] << '"' << name.substr(0, classifier_size_max) << '"';
-    oss  <<  scheme_tags[2] << "\n" << scheme_tags[3];
-    db_opt->to_scheme(oss);
-    oss << scheme_tags[4] << "\n\n";
-
-    return oss;
-}
-
 static inline bool constexpr
 is_eol(char c)
 {
@@ -330,273 +310,6 @@ is_delim(char c)
         is_single_quote(c) || is_double_quote(c) || is_semicolon(c);
 }
 
-static std::string
-scan_scheme_symbol_from_streambuf(std::streambuf* sbuf)
-{
-    std::string retval;
-    while(sbuf->in_avail() && !is_delim(sbuf->sgetc()))
-        retval += sbuf->sbumpc();
-    return retval;
-}
-
-#ifdef _LIBCPP_VERSION
-static inline void constexpr
-#else
-static inline void
-#endif
-consume_scheme_comment(std::streambuf* sbuf)
-{
-    while (sbuf->in_avail() && !is_eol(sbuf->sgetc()))
-           sbuf->sbumpc();
-}
-
-static inline std::string
-scan_scheme_string_from_streambuf(std::streambuf* sbuf)
-{
-    std::string retval{static_cast<char>(sbuf->sbumpc())};
-    while(sbuf->in_avail() && !is_double_quote(sbuf->sgetc()))
-        retval += sbuf->sbumpc();
-    retval += sbuf->sbumpc(); // Add the closing quote.
-    return retval;
-}
-
-#ifdef _LIBCPP_VERSION
-static inline void constexpr
-#else
-static inline void
-#endif
-consume_scheme_whitespace(std::streambuf* sbuf)
-{
-    while (sbuf->in_avail() && is_whitespace(sbuf->sgetc()))
-           sbuf->sbumpc();
-}
-
-enum class IdentType
-{
-    NAME, //no introducing mark
-    CONST, //introduced with single quote
-    STRING, //delimited by double-quotes.
-    LIST, //introduced ' and delimited by parentheses
-    FORM //delimited by parentheses without ' introduction.
-};
-
-struct SchemeId
-{
-    IdentType m_type;
-    std::string m_name;
-    std::vector<SchemeId> m_ids;
-};
-
-/**
- * Scheme Parse Tree
- * An identifier is a string and a type (name, const, string, or form).
- */
-
-static void scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id);
-
-static void
-scan_scheme_form_from_streambuf(std::streambuf* sbuf, SchemeId& id)
-{
-    sbuf->sbumpc();
-    if (!sbuf->in_avail())
-        return;
-    char c = sbuf->sgetc();
-    while (sbuf->in_avail() && !is_end_paren(c))
-    {
-        SchemeId next_id;
-        scan_scheme_id_from_streambuf(sbuf, next_id);
-        if (id.m_name.empty() && next_id.m_type == IdentType::NAME)
-        {
-            id.m_name = std::move(next_id.m_name);
-            continue;
-        }
-        id.m_ids.emplace_back(std::move(next_id));
-        if (!sbuf->in_avail())
-        {
-            std::string err{"End of streambuf before end of form "};
-            err += id.m_name;
-            throw std::runtime_error(err);
-        }
-        c = sbuf->sgetc();
-    }
-    sbuf->sbumpc();
-}
-
-static void
-scan_scheme_list_from_streambuf(std::streambuf* sbuf, std::string& str)
-{
-
-    consume_scheme_whitespace(sbuf);
-    if (!sbuf->in_avail())
-        return;
-    char c = sbuf->sgetc();
-    while (sbuf->in_avail() && !is_end_paren(c))
-    {
-        str += static_cast<char>(sbuf->sbumpc());
-        if (!sbuf->in_avail())
-            return;
-        c = sbuf->sgetc();
-    }
-    str += static_cast<char>(sbuf->sbumpc());
-}
-
-static void
-scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id)
-{
-    consume_scheme_whitespace(sbuf);
-    if (!sbuf->in_avail())
-        return;
-    auto c{sbuf->sgetc()};
-    switch(c)
-    {
-        case ';':
-            consume_scheme_comment(sbuf);
-            break;
-        case '"':
-            id.m_type = IdentType::STRING;
-            id.m_name = scan_scheme_string_from_streambuf(sbuf);
-            break;
-        case '\'':
-        {
-            std::string value{static_cast<char>(sbuf->sbumpc())};
-            if (sbuf->sgetc() == '(')
-            {
-                id.m_type == IdentType::LIST;
-                scan_scheme_list_from_streambuf(sbuf, value);
-                if (value.back() != ')')
-                    throw std::runtime_error("End of streambuf before end of form ");
-            }
-            else if (sbuf->sgetc() == '"')
-                throw std::runtime_error("Malformed scheme particle starts '\"");
-            else
-            {
-                id.m_type = IdentType::CONST;
-                value += scan_scheme_symbol_from_streambuf(sbuf);
-            }
-            id.m_name = std::move(value);
-            break;
-        }
-        case '(':
-            id.m_type = IdentType::FORM;
-            scan_scheme_form_from_streambuf(sbuf, id);
-            break;
-        default:
-            id.m_type = IdentType::NAME;
-            id.m_name = scan_scheme_symbol_from_streambuf(sbuf);
-            break;
-    }
-    return;
-}
-
-static inline std::string
-unquote_scheme_string(const std::string& str)
-{
-    if (str.front() == '"' && str.back() == '"')
-       return str.substr(1, str.size() - 2);
-
-    return str;
-}
-
-static std::optional<std::reference_wrapper<const SchemeId>>
-find_form(const SchemeId& toplevel, IdentType type, const char* name)
-{
-    if (toplevel.m_type == type && toplevel.m_name == name)
-        return std::ref(toplevel);
-    for (const auto& id : toplevel.m_ids)
-    {
-        if (id.m_type == type && id.m_name == name)
-            return std::ref(id);
-        auto child{find_form(id, type, name)};
-        if (child)
-            return child;
-    }
-    return std::nullopt;
-}
-
-std::istream&
-GncOptionDB::load_option_scheme(std::istream& iss)
-{
-    auto sbuf{iss.rdbuf()};
-    SchemeId toplevel;
-    std::optional<std::reference_wrapper<const SchemeId>> lookup_id;
-    bool form_found = false;
-    while (sbuf->in_avail()  && !lookup_id)
-    {
-        scan_scheme_id_from_streambuf(sbuf, toplevel);
-        lookup_id = find_form(toplevel, IdentType::FORM, "gnc:lookup-option");
-    }
-
-    if (!lookup_id)
-    {
-        iss.setstate(std::ios_base::eofbit);
-        return iss; // No options
-    }
-    const auto& classifier = lookup_id->get().m_ids;
-    if (classifier.size() != 3)
-        throw std::runtime_error("Malformed option classifier.");
-    const auto& section = unquote_scheme_string(classifier[1].m_name);
-    const auto& name = unquote_scheme_string(classifier[2].m_name);
-    auto option = find_option(section, name.c_str());
-    std::string option_str{section};
-    option_str += ':';
-    option_str += name;
-    if (!option)
-    {
-        std::string err{"Option not found: "};
-        err += option_str;
-        throw std::runtime_error(err);
-    }
-    auto value_id = find_form(toplevel, IdentType::FORM, "gnc:option-set-value");
-    if (!(value_id && value_id->get().m_ids.size() == 2))
-    {
-        std::string err{"Option "};
-        err += option_str;
-        throw std::runtime_error(err + " malformed value lambda form.");
-    }
-    std::istringstream value_iss{value_id->get().m_ids[1].m_name};
-    option->from_scheme(value_iss);
-    return iss;
-}
-
-std::ostream&
-GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept
-{
-    foreach_section(
-        [&oss, options_prolog](const GncOptionSectionPtr& section)
-        {
-            oss << "\n; Section: " << section->get_name() << "\n\n";
-            section->foreach_option(
-                [&oss, options_prolog, &section](auto& option)
-                {
-                    if (!option.is_changed())
-                        return;
-                    oss << scheme_tags[0] << options_prolog << "\n";
-                    oss << scheme_tags[1] << '"' << section->get_name().substr(0, classifier_size_max) << "\"\n";
-                    oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"';
-                    oss  <<  scheme_tags[2] << "\n" << scheme_tags[3];
-                    option.to_scheme(oss);
-                    oss << scheme_tags[4] << "\n\n";
-                });
-        });
-    return oss;
-}
-
-std::istream&
-GncOptionDB::load_from_scheme(std::istream& iss) noexcept
-{
-    try {
-        while (iss.good())
-            load_option_scheme(iss);
-        iss.clear(); //unset eofbit and maybe failbit
-    }
-    catch (const std::runtime_error& err)
-    {
-        std::cerr << "Load of options from Scheme failed: " <<
-            err.what() << std::endl;
-    }
-    return iss;
-}
-
 std::ostream&
 GncOptionDB::save_option_key_value(std::ostream& oss,
                                    const std::string& section,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 09595b1b2..9bc3c1341 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -584,18 +584,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     }
 %}
 
-%ignore gnc_option_to_scheme;
-%ignore gnc_option_from_scheme;
 /* GncOptionDB::register_option comes in GncOption* and GncOption&&
  * overloads. The latter isn't useful to SWIG, ignore it.
  */
 %ignore GncOptionDB::register_option(const char*, GncOption&&);
-/* GncOptionDB::save_to_scheme takes and returns a std::stream. Good luck
- * converting *that* to anything useful!
- */
-%ignore GncOptionDB::save_to_scheme(std::ostream&, const char*);
-%ignore GncOptionDB::save_option_scheme(std::ostream&, const char*, const std::string&, const std::string&);
-%ignore GncOptionDB::load_option_scheme(std::itream&);
 /* The following functions are overloaded in gnc-optiondb.hpp to provide both
  * GncOptionDB* and GncOptionDBPtr& versions. That confuses SWIG so ignore the
  * raw-ptr version.
@@ -837,6 +829,8 @@ inline SCM return_scm_value(ValueType value)
 
 %}
 %ignore GncOptionDBCallback;
+%ignore operator<(const GncOption&, const GncOption&);
+%ignore operator<(const GncOptionSectionPtr&, const GncOptionSectionPtr&);
 
 %include "gnc-option-date.hpp"
 %include "gnc-option.hpp"
@@ -1356,12 +1350,10 @@ inline SCM return_scm_value(ValueType value)
             });
     }
 
-    static std::string
+    std::string
     gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog)
     {
-        std::ostringstream oss;
-        odb->save_to_scheme(oss, prolog);
-        return oss.str();
+        return prolog;
     }
 %}
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 2edc94af5..a6de6ebe2 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -91,26 +91,6 @@ TEST(GncOption, test_string_stream_in)
     EXPECT_EQ(pepper, option.get_value<std::string>());
 }
 
-TEST(GncOption, test_string_to_scheme)
-{
-    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
-    std::ostringstream oss;
-    option.to_scheme(oss);
-    std::string scheme_str{"\""};
-    scheme_str += option.get_value<std::string>() + "\"";
-    EXPECT_EQ(oss.str(), scheme_str);
-}
-
-TEST(GncOption, test_string_from_scheme)
-{
-    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
-    std::string pepper{"pepper"};
-    std::string scheme_str{"\"pepper\""};
-    std::istringstream iss{scheme_str};
-    option.from_scheme(iss);
-    EXPECT_EQ(pepper, option.get_value<std::string>());
-}
-
 TEST(GncOption, test_int64_t_value)
 {
     GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
@@ -136,23 +116,6 @@ TEST(GncOption, test_int64_stream_in)
     EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
 }
 
-TEST(GncOption, test_int64_to_scheme)
-{
-    GncOption option("foo", "bar", "baz", "Phony Option",  INT64_C(123456789));
-    std::ostringstream oss;
-    option.to_scheme(oss);
-    EXPECT_STREQ(oss.str().c_str(), "123456789");
-}
-
-TEST(GncOption, test_int64_from_scheme)
-{
-    GncOption option("foo", "bar", "baz", "Phony Option",  INT64_C(123456789));
-    std::string number{"987654321"};
-    std::istringstream iss{number};
-    option.from_scheme(iss);
-    EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
-}
-
 TEST(GncOption, test_bool_stream_out)
 {
     GncOption option("foo", "bar", "baz", "Phony Option", false);
@@ -176,28 +139,6 @@ TEST(GncOption, test_bool_stream_in)
     EXPECT_FALSE(option.get_value<bool>());
 }
 
-TEST(GncOption, test_bool_to_scheme)
-{
-    GncOption option("foo", "bar", "baz", "Phony Option", false);
-    std::ostringstream oss;
-    oss << option;
-    EXPECT_STREQ(oss.str().c_str(), "#f");
-    oss.str("");
-    option.set_value(true);
-    option.to_scheme(oss);
-    EXPECT_STREQ(oss.str().c_str(), "#t");
-}
-
-TEST(GncOption, test_bool_from_scheme)
-{
-    GncOption option("foo", "bar", "baz", "Phony Option", false);
-    std::istringstream iss("#t");
-    iss >> option;
-    EXPECT_TRUE(option.get_value<bool>());
-    iss.str("#f");
-    option.from_scheme(iss);
-    EXPECT_FALSE(option.get_value<bool>());
-}
 
 class GncOptionTest : public ::testing::Test
 {
@@ -242,33 +183,6 @@ TEST_F(GncOptionTest, test_budget_in)
     gnc_budget_destroy(budget);
 }
 
-TEST_F(GncOptionTest, test_budget_to_scheme)
-{
-    auto budget = gnc_budget_new(m_book);
-    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)budget};
-
-    auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
-    std::ostringstream oss;
-    budget_guid.insert(0, "\"");
-    budget_guid += "\"";
-    option.to_scheme(oss);
-    EXPECT_EQ(budget_guid, oss.str());
-    gnc_budget_destroy(budget);
-}
-
-TEST_F(GncOptionTest, test_budget_from_scheme)
-{
-    auto budget = gnc_budget_new(m_book);
-    auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
-    budget_guid.insert(0, "\"");
-    budget_guid += "\"";
-    std::istringstream iss{budget_guid};
-    GncOption option{GncOptionValue<const QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
-    option.from_scheme(iss);
-    EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<const QofInstance*>());
-    gnc_budget_destroy(budget);
-}
-
 TEST_F(GncOptionTest, test_commodity_ctor)
 {
     auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
@@ -462,50 +376,6 @@ TEST_F(GncOptionCommodityTest, test_commodity_in)
     EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
 }
 
-TEST_F(GncOptionCommodityTest, test_currency_to_scheme)
-{
-    auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
-                                       m_eur, true);
-
-    std::string eur_str{make_currency_SCM_str(m_eur)};
-    std::ostringstream oss;
-    option.to_scheme(oss);
-    EXPECT_EQ(eur_str, oss.str());
-}
-
-TEST_F(GncOptionCommodityTest, test_commodity_to_scheme)
-{
-    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe,
-                     GncOptionUIType::COMMODITY};
-
-    std::string hpe_str{make_commodity_SCM_str(m_hpe)};
-    std::ostringstream oss;
-    option.to_scheme(oss);
-    EXPECT_EQ(hpe_str, oss.str());
-}
-
-TEST_F(GncOptionCommodityTest, test_currency_from_scheme)
-{
-    auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
-                                       m_eur, true);
-
-    std::string usd_str{make_currency_SCM_str(m_usd)};
-    std::istringstream iss{usd_str};
-    option.from_scheme(iss);
-    EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<const QofInstance*>());
-}
-
-TEST_F(GncOptionCommodityTest, test_commodity_from_scheme)
-{
-    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl,
-                     GncOptionUIType::COMMODITY};
-
-    std::string hpe_str{make_commodity_SCM_str(m_hpe)};
-    std::istringstream iss{hpe_str};
-    option.from_scheme(iss);
-    EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
-}
-
 class GncUIType
 {
 public:
@@ -891,64 +761,6 @@ make_account_list_SCM_str(const GncOptionAccountList& acclist)
     return retval;
 }
 
-TEST_F(GncOptionAccountTest, test_account_list_to_scheme)
-{
-    GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOption option{GncOptionAccountListValue {"foo", "bar", "baz", "Bogus Option",
-                                            GncOptionUIType::ACCOUNT_LIST,
-                                            acclist}};
-    std::ostringstream oss;
-    std::string acc_guids{make_account_list_SCM_str(acclist)};
-
-    option.to_scheme(oss);
-    EXPECT_EQ(acc_guids, oss.str());
-
-    GncOptionAccountList accsel{acclist[0]};
-    GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz",
-                                               "Bogus Option",
-                                               GncOptionUIType::ACCOUNT_LIST,
-                                               accsel,
-                                               GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
-    acc_guids = make_account_list_SCM_str(accsel);
-
-    oss.str("");
-    sel_option.to_scheme(oss);
-    EXPECT_EQ(acc_guids, oss.str());
-}
-
-TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
-{
-    GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option",
-                                           GncOptionUIType::ACCOUNT_LIST,
-                                           acclist}};
-
-    std::string acc_guids{make_account_list_SCM_str(acclist)};
-    std::istringstream iss{acc_guids};
-    option.from_scheme(iss);
-    EXPECT_EQ(acclist, option.get_value<GncOptionAccountList>());
-
-    GncOptionAccountList accsel{acclist[0]};
-    GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz",
-                                               "Bogus Option",
-                                               GncOptionUIType::ACCOUNT_LIST,
-                                               accsel,
-                                               GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
-    GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
-    acc_guids = make_account_list_SCM_str(acclistbad);
-    iss.clear();
-    iss.str(acc_guids);
-    sel_option.from_scheme(iss);
-    EXPECT_EQ(accsel, sel_option.get_value<GncOptionAccountList>());
-
-    iss.clear();  //Reset the failedbit from the invalid selection type.
-    acc_guids = make_account_list_SCM_str(GncOptionAccountList{acclist[1]});
-    EXPECT_NO_THROW({
-            iss.str(acc_guids);
-            sel_option.from_scheme(iss);
-        });
-    EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[0]);
-}
 
 using KT = GncOptionMultichoiceKeyType;
 class GncOptionMultichoiceTest : public ::testing::Test
@@ -1031,22 +843,6 @@ TEST_F(GncMultichoiceOption, test_multichoice_in)
     EXPECT_EQ(iss.str(), m_option.get_value<std::string>());
 }
 
-TEST_F(GncMultichoiceOption, test_multichoice_scheme_out)
-{
-    std::ostringstream oss;
-    m_option.to_scheme(oss);
-    std::string scm_str{'\''};
-    scm_str += m_option.get_value<std::string>();
-    EXPECT_EQ(scm_str, oss.str());
-}
-
-TEST_F(GncMultichoiceOption, test_multichoice_scheme_in)
-{
-    std::istringstream iss{"'pork"};
-    m_option.from_scheme(iss);
-    EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
-}
-
 class GncOptionListTest : public ::testing::Test
 {
 protected:
@@ -1116,26 +912,6 @@ TEST_F(GncListOption, test_list_in)
     EXPECT_EQ(iss.str(), m_option.get_value<std::string>());
 }
 
-TEST_F(GncListOption, test_list_scheme_out)
-{
-    std::ostringstream oss;
-    m_option.to_scheme(oss);
-    std::string value{"'('"};
-    auto vec{m_option.get_value<GncMultichoiceOptionIndexVec>()};
-    value += m_option.permissible_value(vec[0]);
-    value += " '";
-    value += m_option.permissible_value(vec[1]);
-    value += ")";
-    EXPECT_EQ(value, oss.str());
-}
-
-TEST_F(GncListOption, test_list_scheme_in)
-{
-    std::istringstream iss{"'('pork 'waldo)"};
-    m_option.from_scheme(iss);
-    EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
-}
-
 static time64
 time64_from_gdate(const GDate* g_date, DayPart when)
 {
@@ -1572,63 +1348,3 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end)
     EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
-/* We only need wiggle tests for to_scheme and from_scheme as they just wrap or
-unwrap the normal stream output with "'(" and ")".
-*/
-
-TEST_F(GncDateOption, test_date_option_to_scheme)
-{
-    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
-    m_option.set_value(time1);
-    std::ostringstream oss;
-    oss << time1;
-    std::string timestr{"'(absolute . "};
-    timestr += oss.str();
-    timestr += ')';
-    oss.str("");
-    m_option.to_scheme(oss);
-    EXPECT_EQ(oss.str(), timestr);
-
-    m_option.set_value(RelativeDatePeriod::TODAY);
-    oss.str("");
-    m_option.to_scheme(oss);
-    EXPECT_STREQ(oss.str().c_str(), "'(relative . today)");
-
-    m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
-    oss.str("");
-    m_option.to_scheme(oss);
-    EXPECT_STREQ(oss.str().c_str(), "'(relative . start-this-month)");
-
-}
-
-TEST_F(GncDateOption, test_date_option_from_scheme)
-{
-    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
-    std::ostringstream oss;
-    oss << time1;
-    std::string timestr{"'(absolute . "};
-    timestr += oss.str();
-    timestr += ')';
-
-    std::istringstream iss{timestr};
-    m_option.from_scheme(iss);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-
-    GDate month_start;
-    g_date_set_time_t(&month_start, time(nullptr));
-    gnc_gdate_set_month_start(&month_start);
-    time1 = time64_from_gdate(&month_start, DayPart::start);
-    iss.clear();
-    iss.str("'(relative . start-this-month)");
-    m_option.from_scheme(iss);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-
-    GDate date;
-    g_date_set_time_t(&date, time(nullptr));
-    gnc_gdate_set_month_end(&date);
-    time1 = time64_from_gdate(&date, DayPart::end);
-    iss.clear();
-    iss.str("'(relative . end-this-month)");
-    m_option.from_scheme(iss);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index e00f0894a..15a3cca4e 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -337,115 +337,6 @@ protected:
     GncOptionDBPtr m_db;
 };
 
-TEST_F(GncOptionDBIOTest, test_option_scheme_output)
-{
-    std::ostringstream oss;
-    m_db->save_option_scheme(oss, "option", "foo", "sausage");
-    EXPECT_STREQ("", oss.str().c_str());
-    oss.clear();
-    m_db->set_option("foo", "sausage", std::string{"pepper"});
-    EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
-    EXPECT_TRUE(m_db->find_option("foo", "sausage")->is_changed());
-    oss.flush();
-    m_db->save_option_scheme(oss, "option", "foo", "sausage");
-    EXPECT_STREQ("(let ((option (gnc:lookup-option option\n"
-                 "                                 \"foo\"\n"
-                 "                                 \"sausage\")))\n"
-                 "   ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
-                 "))) option))\n\n", oss.str().c_str());
-}
-
-TEST_F(GncOptionDBIOTest, test_string_option_scheme_input)
-{
-    const char* input{"(let ((option (gnc:lookup-option option\n"
-                 "                                 \"foo\"\n"
-                 "                                 \"sausage\")))\n"
-                 "   ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
-            "))) option))\n\n"};
-    std::istringstream iss{input};
-    EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "sausage").c_str());
-    m_db->load_option_scheme(iss);
-    EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
-}
-
-TEST_F(GncOptionDBIOTest, test_date_interval_option_scheme_input)
-{
-    const char* input{"(let ((option (gnc:lookup-option option\n"
-                 "                                 \"pork\"\n"
-                 "                                 \"garply\")))\n"
-                 "   ((lambda (o) (if o (gnc:option-set-value o "
-            "'(relative . end-prev-month)"
-            "))) option))\n\n"};
-    std::istringstream iss{input};
-    GDate month_end;
-    g_date_set_time_t(&month_end, time(nullptr));
-    g_date_subtract_months(&month_end, 1);
-    gnc_gdate_set_month_end(&month_end);
-    auto time1 = time64_from_gdate(&month_end, DayPart::end);
-    m_db->load_option_scheme(iss);
-    EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value<time64>());
-
-}
-
-TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input)
-{
-    auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
-    auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))};
-    auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))};
-    std::string input{"(let ((option (gnc:lookup-option option\n"
-                            "                                 \"quux\"\n"
-                            "                                 \"xyzzy\")))\n"
-                            "   ((lambda (o) (if o (gnc:option-set-value o '(\""};
-    input += hpe_guid + "\" \"";
-    input += msft_guid + "\")))) option))\n\n";
-    std::istringstream iss{input};
-    EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[0]);
-    m_db->load_option_scheme(iss);
-    EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
-
-}
-
-TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input)
-{
-    auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
-    auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))};
-    auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))};
-    std::string input{";; Foo\n\n"
-                      "(let ((option (gnc:lookup-option option\n"
-                      "                                 \"foo\"\n"
-                      "                                 \"sausage\")))\n"
-                      "   ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
-                      "))) option))\n\n"
-                      ";; Pork\n\n"
-                      "(let ((option (gnc:lookup-option option\n"
-                      "                                 \"pork\"\n"
-                      "                                 \"garply\")))\n"
-                      "   ((lambda (o) (if o (gnc:option-set-value o "
-                      "'(relative . end-prev-month)"
-                      "))) option))\n\n"
-                      ";; Quux\n\n"
-                      "(let ((option (gnc:lookup-option option\n"
-                      "                                 \"quux\"\n"
-                      "                                 \"xyzzy\")))\n"
-                      "   ((lambda (o) (if o (gnc:option-set-value o '(\""};
-    input += hpe_guid + "\" \"";
-    input += msft_guid + "\")))) option))\n\n";
-    std::istringstream iss{input};
-    GDate month_end;
-    g_date_set_time_t(&month_end, time(nullptr));
-    g_date_subtract_months(&month_end, 1);
-    gnc_gdate_set_month_end(&month_end);
-    auto time1 = time64_from_gdate(&month_end, DayPart::end);
-    EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[0]);
-    m_db->load_from_scheme(iss);
-    EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
-    EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value<time64>());
-    EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
-
-}
-
 TEST_F(GncOptionDBIOTest, test_option_key_value_output)
 {
     std::ostringstream oss;

commit a487ca3f9822cc8e65f0d28e2c267b64a2a2c697
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Aug 25 12:16:15 2021 -0700

    Improve template conditional readability with custom traits values.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 5aaf2e178..373409be4 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -237,6 +237,18 @@ QofInstance* qof_instance_from_string(const std::string& str,
 QofInstance* qof_instance_from_guid(GncGUID*, GncOptionUIType type);
 std::string qof_instance_to_string(const QofInstance* inst);
 
+template <typename T>
+struct is_QofInstanceValue
+{
+    static constexpr bool value =
+         (std::is_same_v<std::decay_t<T>, GncOptionValue<const QofInstance*>> ||
+          std::is_same_v<std::decay_t<T>,
+          GncOptionValidatedValue<const QofInstance*>>);
+};
+
+template <typename T> inline constexpr bool
+is_QofInstanceValue_v = is_QofInstanceValue<T>::value;
+
 /* These will work when m_value is a built-in class; GnuCash class and container
  * values will need specialization unless they happen to define operators << and
  * >>.
@@ -246,16 +258,9 @@ std::string qof_instance_to_string(const QofInstance* inst);
  */
 #ifndef SWIG
 template<class OptType,
-         typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
-                                                     std::decay_t<OptType>> &&
-                                   ! (std::is_same_v<std::decay_t<OptType>,
-                                      GncOptionValue<const QofInstance*>> ||
-                                      std::is_same_v<std::decay_t<OptType>,
-                                      GncOptionValidatedValue<const QofInstance*>> ||
-                                      std::is_same_v<std::decay_t<OptType>,
-                                      GncOptionRangeValue<int>> ||
-                                      std::is_same_v<std::decay_t<OptType>,
-                                      GncOptionRangeValue<double>>), int> = 0>
+         typename std::enable_if_t<is_OptionClassifier_v<OptType> &&
+                                   ! (is_QofInstanceValue_v<OptType> ||
+                                      is_RangeValue_v<OptType>), int> = 0>
 std::ostream& operator<<(std::ostream& oss, const OptType& opt)
 {
     oss << opt.get_value();
@@ -271,10 +276,7 @@ operator<< <GncOptionValue<bool>>(std::ostream& oss,
 }
 
 template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionValidatedValue<const QofInstance*>> ||
-                                   std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionValue<const QofInstance*> >, int> = 0>
+         typename std::enable_if_t<is_QofInstanceValue_v<OptType>, int> = 0>
 inline std::ostream&
 operator<< (std::ostream& oss, const OptType& opt)
 {
@@ -296,16 +298,9 @@ operator<< (std::ostream& oss, const OptType& opt)
 }
 
 template<class OptType,
-         typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
-                                                     std::decay_t<OptType>> &&
-                                   !(std::is_same_v<std::decay_t<OptType>,
-                                     GncOptionValue<const QofInstance*>> ||
-                                     std::is_same_v<std::decay_t<OptType>,
-                                     GncOptionValidatedValue<const QofInstance*>> ||
-                                      std::is_same_v<std::decay_t<OptType>,
-                                      GncOptionRangeValue<int>> ||
-                                      std::is_same_v<std::decay_t<OptType>,
-                                      GncOptionRangeValue<double>>), int> = 0>
+         typename std::enable_if_t<is_OptionClassifier_v<OptType> &&
+                                   !(is_QofInstanceValue_v<OptType> ||
+                                     is_RangeValue_v<OptType>), int> = 0>
 std::istream& operator>>(std::istream& iss, OptType& opt)
 {
     std::decay_t<decltype(opt.get_value())> value;
@@ -322,11 +317,7 @@ std::istream& operator>>(std::istream& iss, OptType& opt)
 }
 
 template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionValidatedValue<const QofInstance*>> ||
-                                   std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionValue<const QofInstance*> >,
-                                   int> = 0>
+         typename std::enable_if_t<is_QofInstanceValue_v<OptType>, int> = 0>
 std::istream&
 operator>> (std::istream& iss, OptType& opt)
 {
@@ -507,11 +498,7 @@ private:
 };
 
 template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionRangeValue<int>> ||
-                                   std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionRangeValue<double>>,
-                                   int> = 0>
+         typename std::enable_if_t<is_RangeValue_v<OptType>, int> = 0>
 inline std::ostream&
 operator<< (std::ostream& oss, const OptType& opt)
 {
@@ -522,11 +509,7 @@ operator<< (std::ostream& oss, const OptType& opt)
 }
 
 template<class OptType,
-         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionRangeValue<int>> ||
-                                   std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionRangeValue<double>>,
-                                   int> = 0>
+         typename std::enable_if_t<is_RangeValue_v<OptType>, int> = 0>
 inline std::istream&
 operator>> (std::istream& iss, OptType& opt)
 {
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index f249ff06b..b28a92579 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -34,8 +34,7 @@ extern "C"
 }
 
 template <typename ValueType,
-          typename std::enable_if_t<!std::is_base_of_v<OptionClassifier,
-                                                       std::decay_t<ValueType>>,
+          typename std::enable_if_t<!is_OptionClassifier_v<ValueType>,
                                int>>
 GncOption::GncOption(const char* section, const char* name,
                      const char* key, const char* doc_string,
@@ -49,31 +48,30 @@ GncOption::GncOption(const char* section, const char* name,
 template <typename ValueType> ValueType
 GncOption::get_value() const
 {
-    return std::visit([](const auto option)->ValueType {
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
+    return std::visit(
+        [](const auto option)->ValueType {
+            if constexpr (is_same_decayed_v<decltype(option.get_value()),
+                          ValueType>)
                                            return option.get_value();
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionDateValue> &&
-                                        std::is_same_v<std::decay_t<ValueType>,
+            if constexpr (is_same_decayed_v<decltype(option),
+                          GncOptionDateValue>)
+            {
+                if constexpr (is_same_decayed_v<ValueType,
                                         RelativeDatePeriod>)
                               return option.get_period();
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionDateValue> &&
-                                        std::is_same_v<std::decay_t<ValueType>,
-                                        size_t>)
+                if constexpr (std::is_same_v<ValueType, size_t>)
                               return option.get_period_index();
-                          if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionMultichoiceValue> &&
-                               std::is_same_v<std::decay_t<ValueType>,
-                               size_t>)
+                return ValueType{};
+            }
+            if constexpr (is_same_decayed_v<decltype(option),
+                          GncOptionMultichoiceValue>)
+            {
+                if constexpr (std::is_same_v<ValueType, size_t>)
                               return option.get_index();
-                          if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionMultichoiceValue> &&
-                               std::is_same_v<std::decay_t<ValueType>,
+                if constexpr (is_same_decayed_v<ValueType,
                                GncMultichoiceOptionIndexVec>)
                               return option.get_multiple();
+            }
                           return ValueType {};
                       }, *m_option);
 }
@@ -81,23 +79,25 @@ GncOption::get_value() const
 template <typename ValueType> ValueType
 GncOption::get_default_value() const
 {
-    return std::visit([](const auto option)->ValueType {
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
+    return std::visit(
+        [](const auto option)->ValueType {
+            if constexpr (is_same_decayed_v<decltype(option.get_value()),
+                          ValueType>)
                                            return option.get_default_value();
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionDateValue> &&
-                                        std::is_same_v<std::decay_t<ValueType>,
+            if constexpr (is_same_decayed_v<decltype(option),
+                          GncOptionDateValue>)
+            {
+                if constexpr (is_same_decayed_v<ValueType,
                                         RelativeDatePeriod>)
                               return option.get_default_period();
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionDateValue> &&
-                                        std::is_same_v<std::decay_t<ValueType>,
-                                        size_t>)
+                if constexpr (std::is_same_v<ValueType, size_t>)
                               return option.get_default_period_index();
+                return ValueType{};
+            }
                           if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
+                (is_same_decayed_v<decltype(option),
                                GncOptionMultichoiceValue> &&
-                               std::is_same_v<std::decay_t<ValueType>,
+                 is_same_decayed_v<ValueType,
                                GncMultichoiceOptionIndexVec>)
                               return option.get_default_multiple();
                           return ValueType {};
@@ -108,26 +108,24 @@ GncOption::get_default_value() const
 template <typename ValueType> void
 GncOption::set_value(ValueType value)
 {
-    std::visit([value](auto& option) {
+    std::visit(
+        [value](auto& option) {
                    if constexpr
-                       (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                        std::decay_t<ValueType>> ||
-                        (std::is_same_v<std::decay_t<decltype(option)>,
+                (is_same_decayed_v<decltype(option.get_value()), ValueType> ||
+                 (is_same_decayed_v<decltype(option),
                          GncOptionDateValue> &&
-                         (std::is_same_v<std::decay_t<ValueType>,
-                         RelativeDatePeriod> ||
-                          std::is_same_v<std::decay_t<ValueType>, size_t>)))
+                  (is_same_decayed_v<ValueType, RelativeDatePeriod> ||
+                   std::is_same_v<ValueType, size_t>)))
                        option.set_value(value);
-                   if constexpr
-                       (std::is_same_v<std::decay_t<decltype(option)>,
+            if constexpr (is_same_decayed_v<decltype(option),
                         GncOptionMultichoiceValue>)
                    {
-                       if constexpr (std::is_same_v<std::decay_t<ValueType>,
+                if constexpr (is_same_decayed_v<ValueType,
                                      GncMultichoiceOptionIndexVec>)
                            option.set_multiple(value);
                        else if constexpr
                            (std::is_same_v<ValueType, size_t> ||
-                            std::is_same_v<std::decay_t<ValueType>, std::string> ||
+                     is_same_decayed_v<ValueType, std::string> ||
                             std::is_same_v<std::remove_cv<ValueType>, char*>)
                            option.set_value(value);
                    }
@@ -137,27 +135,23 @@ GncOption::set_value(ValueType value)
 template <typename ValueType> void
 GncOption::set_default_value(ValueType value)
 {
-    std::visit([value](auto& option) {
+    std::visit(
+        [value](auto& option) {
                    if constexpr
-                       (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                        std::decay_t<ValueType>> ||
-                        (std::is_same_v<std::decay_t<decltype(option)>,
-                         GncOptionDateValue> &&
-                         (std::is_same_v<std::decay_t<ValueType>,
-                         RelativeDatePeriod> ||
-                          std::is_same_v<std::decay_t<ValueType>, size_t>)))
+                (is_same_decayed_v<decltype(option.get_value()), ValueType>||
+                 (is_same_decayed_v<decltype(option), GncOptionDateValue> &&
+                  (is_same_decayed_v<ValueType, RelativeDatePeriod> ||
+                   std::is_same_v<ValueType, size_t>)))
                        option.set_default_value(value);
-                   if constexpr
-                       (std::is_same_v<std::decay_t<decltype(option)>,
+            if constexpr (is_same_decayed_v<decltype(option),
                         GncOptionMultichoiceValue>)
                    {
-                       if constexpr
-                           (std::is_same_v<std::decay_t<ValueType>,
+                if constexpr (is_same_decayed_v<ValueType,
                             GncMultichoiceOptionIndexVec>)
-                           option.set_multiple(value);
+                    option.set_default_multiple(value);
                        else if constexpr
                            (std::is_same_v<ValueType, size_t> ||
-                            std::is_same_v<std::decay_t<ValueType>, std::string> ||
+                     is_same_decayed_v<ValueType, std::string> ||
                             std::is_same_v<std::remove_cv<ValueType>, char*>)
                            option.set_default_value(value);
                    }
@@ -174,7 +168,7 @@ GncOption::get_limits(ValueType& max, ValueType& min, ValueType& step) const noe
 {
     std::visit([&max, &min, &step](const auto& option) {
                    if constexpr
-                       (std::is_same_v<std::decay_t<decltype(option)>,
+                       (is_same_decayed_v<decltype(option),
                         GncOptionRangeValue<ValueType>>)
                        option.get_limits(max, min, step);
                }, *m_option);
@@ -292,8 +286,9 @@ GncOption::is_changed() const noexcept
 bool
 GncOption::is_multiselect() const noexcept
 {
-    return std::visit([](const auto& option)->bool {
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+    return std::visit(
+        [](const auto& option)->bool {
+            if constexpr (is_same_decayed_v<decltype(option),
                                         GncOptionAccountListValue>)
                               return option.is_multiselect();
                           else
@@ -304,16 +299,17 @@ GncOption::is_multiselect() const noexcept
 template<typename ValueType> bool
 GncOption::validate(ValueType value) const
 {
-    return std::visit([value] (const auto& option) -> bool {
-                          if constexpr ((std::is_same_v<std::decay_t<decltype(option)>,
+    return std::visit(
+        [value] (const auto& option) -> bool {
+            if constexpr ((is_same_decayed_v<decltype(option),
                                          GncOptionMultichoiceValue> &&
-                                         std::is_same_v<std::decay_t<ValueType>,
+                           is_same_decayed_v<ValueType,
                                          std::string>) ||
-                                        (std::is_same_v<std::decay_t<decltype(option)>,
+                          (is_same_decayed_v<decltype(option),
                                          GncOptionMultichoiceValue> &&
-                                         std::is_same_v<std::decay_t<ValueType>,
+                           is_same_decayed_v<ValueType,
                                          GncMultichoiceOptionIndexVec>) ||
-                                        std::is_same_v<std::decay_t<decltype(option)>,
+                          is_same_decayed_v<decltype(option),
                                         GncOptionValidatedValue<ValueType>>)
                                            return option.validate(value);
                           else
@@ -324,10 +320,11 @@ GncOption::validate(ValueType value) const
 std::size_t
 GncOption::num_permissible_values() const
 {
-    return std::visit([] (const auto& option) -> size_t {
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+    return std::visit(
+        [] (const auto& option) -> size_t {
+            if constexpr (is_same_decayed_v<decltype(option),
                                         GncOptionMultichoiceValue>  ||
-                                        std::is_same_v<std::decay_t<decltype(option)>,
+                          is_same_decayed_v<decltype(option),
                                         GncOptionDateValue>)
                                            return option.num_permissible_values();
                           else
@@ -338,10 +335,11 @@ GncOption::num_permissible_values() const
 std::size_t
 GncOption::permissible_value_index(const char* value) const
 {
-    return std::visit([&value] (const auto& option) -> size_t {
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+    return std::visit(
+        [&value] (const auto& option) -> size_t {
+            if constexpr (is_same_decayed_v<decltype(option),
                                         GncOptionMultichoiceValue>  ||
-                                        std::is_same_v<std::decay_t<decltype(option)>,
+                          is_same_decayed_v<decltype(option),
                                         GncOptionDateValue>)
                                            return option.permissible_value_index(value);
                           else
@@ -393,10 +391,7 @@ bool
 GncOption::is_alternate() const noexcept
 {
     return std::visit([](auto& option) -> bool {
-                          if constexpr(std::is_same_v<std::decay_t<decltype(option)>,
-                                       GncOptionRangeValue<int>> ||
-                       std::is_same_v<std::decay_t<decltype(option)>,
-                                       GncOptionRangeValue<double>>)
+                          if constexpr(is_RangeValue_v<decltype(option)>)
                               return option.is_alternate();
                           return false;
                       }, *m_option);
@@ -406,10 +401,7 @@ void
 GncOption::set_alternate(bool alt) noexcept
 {
     std::visit([alt](auto& option) {
-                   if constexpr(std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionRangeValue<int>> ||
-                       std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionRangeValue<double>>)
+                   if constexpr(is_RangeValue_v<decltype(option)>)
                        option.set_alternate(alt);
                }, *m_option);
 }
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 414d7e39f..e873b0d70 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -53,6 +53,38 @@ template <typename ValueType> class GncOptionRangeValue;
 template <typename ValueType> class GncOptionValidatedValue;
 class GncOptionDateValue;
 
+template <typename T>
+struct is_OptionClassifier
+{
+    static constexpr bool value =
+        std::is_base_of_v<OptionClassifier, std::decay_t<T>>;
+};
+
+template <typename T> inline constexpr bool
+is_OptionClassifier_v = is_OptionClassifier<T>::value;
+
+template <typename T, typename U>
+struct is_same_decayed
+{
+    static constexpr bool value = std::is_same_v<std::decay_t<T>,
+                                                 std::decay_t<U>>;
+};
+
+template <typename T, typename U> inline constexpr bool
+is_same_decayed_v = is_same_decayed<T, U>::value;
+
+template <typename T>
+struct is_RangeValue
+{
+    static constexpr bool value =
+         (is_same_decayed_v<T, GncOptionRangeValue<int>> ||
+          is_same_decayed_v<T, GncOptionRangeValue<double>>);
+};
+
+template <typename T> inline constexpr bool
+is_RangeValue_v = is_RangeValue<T>::value;
+
+
 using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<bool>,
                                       GncOptionValue<int64_t>,
@@ -82,14 +114,13 @@ class GncOption
 {
 public:
     template <typename OptionType,
-              typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
-                                                          std::decay_t<OptionType>>,
+              typename std::enable_if_t<is_OptionClassifier_v<OptionType>,
                                int>  = 0>
+
     GncOption(OptionType option) :
         m_option{std::make_unique<GncOptionVariant>(option)} {}
     template <typename ValueType,
-              typename std::enable_if_t<!std::is_base_of_v<OptionClassifier,
-                                                          std::decay_t<ValueType>>,
+              typename std::enable_if_t<!is_OptionClassifier_v<ValueType>,
                                int>  = 0>
     GncOption(const char* section, const char* name,
               const char* key, const char* doc_string,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index d53cc0242..09595b1b2 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -263,7 +263,7 @@ scm_color_list_to_string(SCM list)
 template <typename ValueType> inline ValueType
 scm_to_value(SCM new_value)
 {
-    if constexpr (std::is_same_v<std::decay_t<ValueType>, SCM>)
+    if constexpr (is_same_decayed_v<ValueType, SCM>)
         return new_value;
     return ValueType{};
 }
@@ -816,7 +816,26 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return scm_cons(desig, scm_from_int(val));
     }
 
-    %}
+template <typename T>
+struct is_MultichoiceOrRange
+{
+    static constexpr bool value =
+        is_same_decayed_v<T, GncOptionMultichoiceValue> ||
+        is_same_decayed_v<T, GncOptionRangeValue<int>>;
+};
+
+template <typename T>
+inline constexpr bool is_MultichoiceOrRange_v = is_MultichoiceOrRange<T>::value;
+
+template <typename ValueType>
+inline SCM return_scm_value(ValueType value)
+{
+    if constexpr (is_same_decayed_v<ValueType, SCM>)
+        return value;
+    return scm_from_value(static_cast<ValueType>(value));
+}
+
+%}
 %ignore GncOptionDBCallback;
 
 %include "gnc-option-date.hpp"
@@ -840,16 +859,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         if (!$self)
             return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                              GncOptionMultichoiceValue> ||
-                              std::is_same_v<std::decay_t<decltype(option)>,
-                              GncOptionRangeValue<int>>)
+                if constexpr (is_MultichoiceOrRange_v<decltype(option)>)
                     return get_scm_value(option);
                 auto value{option.get_value()};
-                if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
-                              SCM>)
-                    return value;
-                return scm_from_value(static_cast<decltype(value)>(value));
+                return return_scm_value(value);
             }, swig_get_option($self));
     }
     SCM get_scm_default_value()
@@ -857,25 +870,20 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         if (!$self)
             return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                              GncOptionMultichoiceValue> ||
-                              std::is_same_v<std::decay_t<decltype(option)>,
-                              GncOptionRangeValue<int>>)
+                if constexpr (is_MultichoiceOrRange_v<decltype(option)>)
                     return get_scm_default_value(option);
                 auto value{option.get_default_value()};
-                if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
-                              SCM>)
-                    return value;
-                return scm_from_value(static_cast<decltype(value)>(value));
+                return return_scm_value(value);
             }, swig_get_option($self));
     }
+
     void set_value_from_scm(SCM new_value)
     {
         if (!$self)
             return;
         try {
             std::visit([new_value](auto& option) {
-                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                    if constexpr (is_same_decayed_v<decltype(option),
                                   GncOptionDateValue>)
                     {
                         if (scm_date_absolute(new_value))
@@ -885,14 +893,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                         return;
                     }
 
-                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                    if constexpr (is_same_decayed_v<decltype(option),
                                   GncOptionMultichoiceValue>)
                     {
                         option.set_multiple(scm_to_multichoices(new_value, option));
                         return;
                     }
 
-                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                    if constexpr (is_same_decayed_v<decltype(option),
                                   GncOptionRangeValue<int>>)
                     {
                         if (scm_is_pair(new_value))
@@ -901,10 +909,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                             option.set_value(scm_to_int(new_value));
                         return;
                     }
-                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                  GncOptionValue<const QofInstance*>> ||
-                                  std::is_same_v<std::decay_t<decltype(option)>,
-                                  GncOptionValidatedValue<const QofInstance*>>)
+                    if constexpr (is_QofInstanceValue_v<decltype(option)>)
                     {
                         if (scm_is_string(new_value))
                         {
@@ -937,7 +942,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             return;
         try {
             std::visit([new_value](auto& option) {
-                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                    if constexpr (is_same_decayed_v<decltype(option),
                                   GncOptionDateValue>)
                     {
                         if (scm_date_absolute(new_value))
@@ -946,14 +951,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                             option.set_default_value(scm_relative_date_get_period(new_value));
                         return;
                     }
-                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                    if constexpr (is_same_decayed_v<decltype(option),
                                   GncOptionMultichoiceValue>)
                     {
                         option.set_default_multiple(scm_to_multichoices(new_value,
                                                                         option));
                         return;
                     }
-                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                    if constexpr (is_same_decayed_v<decltype(option),
                                   GncOptionRangeValue<int>>)
                     {
                         if (scm_is_pair(new_value))
@@ -962,10 +967,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                             option.set_default_value(scm_to_int(new_value));
                         return;
                     }
-                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                  GncOptionValue<const QofInstance*>> ||
-                                  std::is_same_v<std::decay_t<decltype(option)>,
-                                  GncOptionValidatedValue<const QofInstance*>>)
+                    if constexpr (is_QofInstanceValue_v<decltype(option)>)
                     {
                         if (scm_is_string(new_value))
                         {
@@ -996,11 +998,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         if (!self)
             return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                    GncOptionMultichoiceValue>)
+                if constexpr (is_same_decayed_v<decltype(option),
+                              GncOptionMultichoiceValue>)
                     return scm_c_eval_string("'multichoice");
                 else if constexpr (std::is_same_v<decltype(option.get_value()),
-                    bool>)
+                                   bool>)
                     return scm_c_eval_string("'boolean");
                 else
                     return SCM_BOOL_F;

commit 3935f1a91b53fd8d4013b44d14704882609effdc
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 19 11:13:29 2021 -0700

    Don't put labels on checkboxes, they have their own.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index fddf9400e..7af187e75 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -348,13 +348,15 @@ gnc_option_set_ui_widget(GncOption& option, GtkGrid *page_box, gint grid_row)
     auto widget = GncOptionUIFactory::create(option, page_box, name_label,
                                              documentation, &enclosing,
                                              &packed);
-    /* attach the name label to the first column of the grid and
-       align to the end */
-    gtk_grid_attach (GTK_GRID(page_box), GTK_WIDGET(name_label),
-                     0, grid_row, // left, top
-                     1, 1);  // width, height
-    gtk_widget_set_halign (GTK_WIDGET(name_label), GTK_ALIGN_END);
-
+    /* Attach the name label to the first column of the grid and
+       align to the end unless it's a check button, which has its own label. */
+    if (!GTK_IS_CHECK_BUTTON(widget))
+    {
+        gtk_grid_attach (GTK_GRID(page_box), GTK_WIDGET(name_label),
+                         0, grid_row, // left, top
+                         1, 1);  // width, height
+        gtk_widget_set_halign (GTK_WIDGET(name_label), GTK_ALIGN_END);
+    }
     if (!packed && (enclosing != NULL))
     {
         /* Pack option widget into an extra eventbox because otherwise the

commit 62ab148a3e3b7be1852a940e6209b9cdbfecdd1a
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 19 09:48:44 2021 -0700

    Output a pair when writing relative date values to scheme file.
    
    So that the can be properly recognized on input.

diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp
index 332c6bcd7..1e6ae7560 100644
--- a/libgnucash/app-utils/gnc-option-date.cpp
+++ b/libgnucash/app-utils/gnc-option-date.cpp
@@ -557,6 +557,6 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
 std::ostream&
 operator<<(std::ostream& ostr, RelativeDatePeriod per)
 {
-    ostr << gnc_relative_date_display_string(per);
+    ostr << "'reldate . " << gnc_relative_date_display_string(per);
     return ostr;
 }

commit b3f96701a0603a0a84bc425f6e81c1f2363a6219
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Aug 18 16:14:21 2021 -0700

    GncOptionAccountListValue: Make setter param cv match template.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 663fba128..5aaf2e178 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -947,15 +947,20 @@ public:
             m_default_value = std::move(value);
         }
 
+    /* These aren't const& because if m_default_value hasn't been set
+     * get_default_value finds the first account that matches the allowed types
+     * and returns a GncOptionAccountList containing it. That's a stack variable
+     * and must be returned by value.
+     */
     GncOptionAccountList get_value() const;
     GncOptionAccountList get_default_value() const;
     bool validate (const GncOptionAccountList& values) const;
-    void set_value (const GncOptionAccountList& values) {
+    void set_value (GncOptionAccountList values) {
         if (validate(values))
             //throw!
             m_value = values;
     }
-    void set_default_value (const GncOptionAccountList& values) {
+    void set_default_value (GncOptionAccountList values) {
         if (validate(values))
             //throw!
             m_value = m_default_value = values;

commit 7392ac6fcf853299d9c16fbf2c89dd337b0bbd44
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Aug 18 15:51:19 2021 -0700

    Prevent running off the end of the GncOptionUI widget's parent tree.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 7702c5485..fddf9400e 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -228,9 +228,16 @@ gnc_option_get_gtk_widget (const GncOption* option)
 static void
 dialog_changed_internal (GtkWidget *widget, bool sensitive)
 {
-    while (widget && !GTK_IS_WINDOW(widget))
-        widget = gtk_widget_get_parent(widget);
-    if (!widget) return;
+    g_return_if_fail(widget);
+    while (TRUE)
+    {
+        auto new_widget = gtk_widget_get_parent(widget);
+        if (new_widget && GTK_IS_WIDGET(new_widget) &&
+            GTK_IS_WINDOW(new_widget))
+            widget = new_widget;
+        else
+            break;
+    }
 
     /* find the ok and cancel buttons, we know where they will be so do it
        this way as opposed to using gtk_container_foreach, much less iteration */

commit b361582cf22f13c34758befe848468569cfa98b1
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Aug 18 14:37:20 2021 -0700

    Ensure option UI is sorted: Tabs by names, items by keys.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 1677e7b5d..414d7e39f 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -138,6 +138,12 @@ private:
     GncOptionUIItemPtr m_ui_item{nullptr};
 };
 
+inline bool
+operator<(const GncOption& right, const GncOption& left)
+{
+    return right.get_key() < left.get_key();
+}
+
 inline std::ostream&
 operator<<(std::ostream& oss, const GncOption& opt)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index d06f4a452..97885c0fe 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -63,6 +63,13 @@ public:
 };
 
 using GncOptionSectionPtr = std::shared_ptr<GncOptionSection>;
+
+inline bool
+operator<(const GncOptionSectionPtr& right, const GncOptionSectionPtr& left)
+{
+    return right->get_name() < left->get_name();
+}
+
 using GncOptionDBChangeCallback = void (*)(void* user_data);
 
 struct GncOptionDBCallback
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 763e7ec78..be005b1c1 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -132,7 +132,9 @@ GncOptionSection::foreach_option(std::function<void(const GncOption&)> func) con
 void
 GncOptionSection::add_option(GncOption&& option)
 {
-    m_options.emplace_back(std::move(option));
+    m_options.push_back(std::move(option));
+    if (!std::is_sorted(m_options.begin(), m_options.end()))
+        std::sort(m_options.begin(), m_options.end());
 }
 
 void
@@ -176,8 +178,10 @@ GncOptionDB::register_option(const char* sectname, GncOption&& option)
         return;
     }
 
-    m_sections.emplace_back(std::make_shared<GncOptionSection>(sectname));
+    m_sections.push_back(std::make_shared<GncOptionSection>(sectname));
     m_sections.back()->add_option(std::move(option));
+    if (!std::is_sorted(m_sections.begin(), m_sections.end()))
+        std::sort(m_sections.begin(), m_sections.end());
 }
 
 void

commit 10381d42e078658d685ac3ccdf994fe9f1bc2650
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Aug 18 13:38:06 2021 -0700

    Fix color option handling.
    
    Read and write color options the way legacy code does. Pass only values
    with no alpha to gdk_rgba_parse because it fails if a hex-string has
    alpha.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index ee14d4934..7702c5485 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -2140,8 +2140,12 @@ public:
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         GdkRGBA color;
-        auto rgba_str{g_strdup_printf("#%s",
-                                      option.get_value<std::string>().c_str())};
+        /* gdk_rgba_parse uses pango_color_parse for hex color strings instead
+         * of pango_color_parse_with_alpha and that fails if the string length
+         * is 8.
+        */
+        auto value{option.get_value<std::string>().substr(0,6)};
+        auto rgba_str{g_strdup_printf("#%s", value.c_str())};
         if (gdk_rgba_parse(&color, rgba_str))
         {
             auto color_button = GTK_COLOR_CHOOSER(get_widget());
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index ae386afe3..663fba128 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -388,11 +388,16 @@ gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
         {
             oss << ")";
         }
+        return oss;
     }
-    else
+
+    if constexpr (std::is_same_v<std::decay_t<decltype(value)>, std::string>)
     {
-        oss << "\"" << qof_instance_to_string(value) << "\"";
+        if (type == GncOptionUIType::COLOR)
+            return output_color_value(oss, value);
     }
+
+    oss << "\"" << qof_instance_to_string(value) << "\"";
     return oss;
 }
 
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 784a65b1d..f249ff06b 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -453,7 +453,12 @@ GncOption::to_scheme(std::ostream& oss) const
                           else if constexpr
                               (std::is_same_v<std::decay_t<decltype(option.get_value())>,
                                std::string>)
+                          {
+                              if (option.get_ui_type() == GncOptionUIType::COLOR)
+                                  output_color_value(oss, option.get_value());
+                              else
                                   oss << '"' << option << '"';
+                          }
                           else if constexpr
                               (std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionRangeValue<int>> ||
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 089ff0825..1677e7b5d 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -28,6 +28,7 @@
 #include <libguile.h>
 #include <string>
 #include <iostream>
+#include <iomanip>
 #include <variant>
 #include <memory>
 #include "gnc-option-ui.hpp"
@@ -149,6 +150,24 @@ operator>>(std::istream& iss, GncOption& opt)
     return opt.in_stream(iss);
 }
 
+inline std::ostream&
+output_color_value(std::ostream& oss, const std::string& value)
+{
+    oss << "'(";
+    oss << std::fixed << std::showpoint << std::setprecision(1);
+    auto len{value.length() > 8 ? 8 : value.length()};
+    for (size_t i{}; i < len; i += 2)
+    {
+        oss << static_cast<float>(stoi(value.substr(i, 2), nullptr, 16));
+        if (i < 6)
+            oss << " ";
+    }
+    if (len < 8)
+        oss << 256.0;
+    oss << ")";
+    return oss;
+}
+
 template<typename ValueType> GncOption*
 gnc_make_option(const char* section, const char* name,
                 const char* key, const char* doc_string,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 4350f1c83..d53cc0242 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -46,6 +46,8 @@ namespace std {
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
 #include "gnc-option-date.hpp"
+#include <sstream>
+#include <iomanip>
 
 static const QofLogModule log_module = "gnc.optiondb";
 
@@ -240,6 +242,24 @@ scm_from_value<GncOwner*>(GncOwner* value)
     return scm_from_value<const GncOwner*>(value);
 }
 
+static std::string
+scm_color_list_to_string(SCM list)
+{
+    std::ostringstream oss{};
+    oss << std::hex << std::setfill('0');
+    SCM cdr = list;
+    while (scm_is_pair(cdr))
+    {
+        if (scm_is_rational(SCM_CAR(cdr)))
+            oss << std::setw(2) <<
+                static_cast<unsigned int>(scm_to_double(SCM_CAR(cdr)));
+        cdr = SCM_CDR(cdr);
+    }
+    if (scm_is_rational(cdr))
+        oss << std::setw(2) << static_cast<unsigned int>(scm_to_double(cdr));
+    return oss.str();
+}
+
 template <typename ValueType> inline ValueType
 scm_to_value(SCM new_value)
 {
@@ -257,6 +277,8 @@ scm_to_value<bool>(SCM new_value)
 template <> inline std::string
 scm_to_value<std::string>(SCM new_value)
 {
+    if (scm_is_true(scm_list_p(new_value)))
+        return scm_color_list_to_string(new_value);
     auto strval = scm_to_utf8_stringn(new_value, nullptr);
     std::string retval{strval};
     free(strval);
@@ -552,7 +574,6 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 #include <algorithm>
 #include <array>
 #include <string>
-#include <sstream>
 #include "gnc-option.hpp"
 #include "gnc-option-ui.hpp"
 

commit 97a317b50cafbb507bdf53206e748aae277befb1
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 17 15:47:11 2021 -0700

    Ensure that alpha values in colors aren't passed to html.
    
    html color tags don't like them.

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 1dd35f963..33ee587bf 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -72,7 +72,12 @@
     (GncOption-get-scm-value option))
 
 (define-public (gnc:color-option->html opt)
-  (format #f "#~a" (GncOption-get-scm-value opt)))
+  ;; HTML doesn't like alpha values.
+  (let* ((color (GncOption-get-scm-value opt))
+         (html-color (if (> (string-length color) 6)
+                         (substring color 0 6)
+                         color)))
+    (format #f "#~a" html-color)))
 
 (define-public (gnc:color-option->hex-string opt)
   (format #f "~a" (GncOption-get-scm-value opt)))

commit 3d1812aacd340193480542f2946461b27d4c18d3
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 17 15:41:26 2021 -0700

    Explicitly cast a value to the decltype of get_value().
    
    Can't always rely on implicit conversion working.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 969d8ec77..4350f1c83 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -899,7 +899,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                         return;
                     }
                     auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
-                    option.set_value(value);
+                    option.set_value(static_cast<decltype(option.get_value())>(value));
                 }, swig_get_option($self));
         }
         catch(const std::invalid_argument& err)

commit bbe8ebbc15eb64b92a1f0e1d526270e136f801f0
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 17 15:38:07 2021 -0700

    Reserve space for the accounts in their vector.
    
    Instead of creating an empty vector and then adding the accounts to the
    end of it.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index b31810fec..ee14d4934 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1653,7 +1653,8 @@ public:
     {
         auto widget{GNC_TREE_VIEW_ACCOUNT(get_widget())};
         auto acc_list = gnc_tree_view_account_get_selected_accounts(widget);
-        GncOptionAccountList acc_vec(g_list_length(acc_list));
+        GncOptionAccountList acc_vec;
+        acc_vec.reserve(g_list_length(acc_list));
         for (auto node = acc_list; node; node = g_list_next(node))
             acc_vec.push_back(static_cast<const Account*>(node->data));
         g_list_free(acc_list);

commit 42b4755aea92134211c3c1ea09c59aac651779c0
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 17 15:36:51 2021 -0700

    Set the initial value on Relative Date combo boxes.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 70e1c4c06..b31810fec 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1310,6 +1310,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option)
 
     /* Create the new Combo with tooltip and add the store */
     m_entry = GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)));
+    gtk_combo_box_set_active(GTK_COMBO_BOX(m_entry), 0);
     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(m_entry), renderer, TRUE);
     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(m_entry),
                                    renderer, "text", 0);

commit c06924622ced87b4abfb38fb6f21f7e013db2f54
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 17 15:34:45 2021 -0700

    Null-value protection for GUID and account values.

diff --git a/gnucash/report/html-utilities.scm b/gnucash/report/html-utilities.scm
index 81abad44b..bd185dac6 100644
--- a/gnucash/report/html-utilities.scm
+++ b/gnucash/report/html-utilities.scm
@@ -79,10 +79,12 @@
       (list)))
 
 (define (gnc:register-guid type guid)
-  (gnc-build-url URL-TYPE-REGISTER (string-append type guid) ""))
+  (and guid  (gnc-build-url URL-TYPE-REGISTER (string-append type guid) "")))
 
 (define (gnc:account-anchor-text acct)
-  (gnc:register-guid "acct-guid=" (gncAccountGetGUID acct)))
+  (if acct
+      (gnc:register-guid "acct-guid=" (gncAccountGetGUID acct))
+      (format #t "No Account!")))
 
 (define (gnc:split-anchor-text split)
   (gnc:register-guid "split-guid=" (gncSplitGetGUID split)))

commit 53fac914c01f354475c36996d33e3a6245c476fd
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 17 15:30:44 2021 -0700

    Re-enable connecting the buttons on the Stylesheet Edit select dialog.

diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp
index b56564a4c..e92bf4a10 100644
--- a/gnucash/gnome/dialog-report-style-sheet.cpp
+++ b/gnucash/gnome/dialog-report-style-sheet.cpp
@@ -73,13 +73,14 @@ enum
     COLUMN_DIALOG,
     N_COLUMNS
 };
-
+extern "C" // So that gtk_builder_connect_full can find them.
+{
 void gnc_style_sheet_select_dialog_new_cb (GtkWidget *widget, gpointer user_data);
 void gnc_style_sheet_select_dialog_edit_cb (GtkWidget *widget, gpointer user_data);
 void gnc_style_sheet_select_dialog_delete_cb (GtkWidget *widget, gpointer user_data);
 void gnc_style_sheet_select_dialog_close_cb (GtkWidget *widget, gpointer user_data);
 void gnc_style_sheet_select_dialog_destroy_cb (GtkWidget *widget, gpointer user_data);
-
+}
 /************************************************************
  *     Style Sheet Edit Dialog (I.E. an options dialog)     *
  ************************************************************/

commit e77b5ec03bb375765427c57da2b6ab23eb4b1aef
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 15 14:27:59 2021 -0700

    Fix RelativeDateValue GtkListModel spec: No tooltips anymore.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 23c0ff7e3..70e1c4c06 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1297,7 +1297,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option)
 {
 
     auto renderer = gtk_cell_renderer_text_new();
-    auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+    auto store = gtk_list_store_new(1, G_TYPE_STRING);
     /* Add values to the list store, entry and tooltip */
     auto num = option.num_permissible_values();
     for (decltype(num) index = 0; index < num; ++index)

commit 474bc360f4cb731099bdb1dab837579c43813280
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 15 14:25:52 2021 -0700

    Improve converting vectors to SCM lists.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 5462a0d2a..969d8ec77 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -358,12 +358,9 @@ scm_from_value<GncOptionAccountList>(GncOptionAccountList value)
 {
     SCM s_list = SCM_EOL;
     for (auto acct : value)
-    {
-        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0));
-        s_list = scm_append(scm_list_2(s_list, elem));
-    }
-
-    return s_list;
+        s_list = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0),
+                          s_list);
+    return scm_reverse(s_list);
 }
 
 QofBook* gnc_option_test_book_new();
@@ -533,20 +530,18 @@ gnc_option_test_book_destroy(QofBook* book)
 {
     $result = SCM_EOL;
     for (auto acct : $1)
-    {
-        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0));
-        $result = scm_append(scm_list_2($result, elem));
-    }
+        $result = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0),
+                           $result);
+    $result = scm_reverse($result);
 }
 
 %typemap(out) GncOptionAccountList&
 {
     $result = SCM_EOL;
     for (auto acct : *$1)
-    {
-        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0));
-        $result = scm_append(scm_list_2($result, elem));
-    }
+        $result = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0),
+                           $result);
+    $result = scm_reverse ($result)
 }
 
 wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);

commit 1cd2cf211c4e1d46c81d9c119d4d52d12aad4445
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Aug 13 14:05:49 2021 -0700

    Specialize QofInstance* options set_value, catch validation exceptions.
    
    GncOptionValue<const QofInstance*>.get_value() returns a QofInstance* but
    reports store them as strings, either commodity mnemonics or GUIDs.
    Specialize set_value/set_default_value from scheme to handle those
    possibilities.
    
    GncOptionValidatedValue throws an invalid_argument exception if it's
    fed an invalid argument. Catch that so that it doesn't crash the program.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 2ca72e6ca..5462a0d2a 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -47,6 +47,8 @@ namespace std {
 #include "gnc-optiondb-impl.hpp"
 #include "gnc-option-date.hpp"
 
+static const QofLogModule log_module = "gnc.optiondb";
+
 SCM scm_init_sw_gnc_optiondb_module(void);
 %}
 
@@ -855,72 +857,122 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         if (!$self)
             return;
-        std::visit([new_value](auto& option) {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionDateValue>)
-                {
-                    if (scm_date_absolute(new_value))
-                        option.set_value(scm_absolute_date_to_time64(new_value));
-                    else
-                        option.set_value(scm_relative_date_get_period(new_value));
-                    return;
-                }
-
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionMultichoiceValue>)
-                {
-                    option.set_multiple(scm_to_multichoices(new_value, option));
-                    return;
-                }
+        try {
+            std::visit([new_value](auto& option) {
+                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionDateValue>)
+                    {
+                        if (scm_date_absolute(new_value))
+                            option.set_value(scm_absolute_date_to_time64(new_value));
+                        else
+                            option.set_value(scm_relative_date_get_period(new_value));
+                        return;
+                    }
+
+                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionMultichoiceValue>)
+                    {
+                        option.set_multiple(scm_to_multichoices(new_value, option));
+                        return;
+                    }
 
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionRangeValue<int>>)
-                {
-                    if (scm_is_pair(new_value))
-                        option.set_value(scm_to_int(scm_cdr(new_value)));
-                    else
-                        option.set_value(scm_to_int(new_value));
-                    return;
-                }
-
-                auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
-                option.set_value(value);
-            }, swig_get_option($self));
+                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionRangeValue<int>>)
+                    {
+                        if (scm_is_pair(new_value))
+                            option.set_value(scm_to_int(scm_cdr(new_value)));
+                        else
+                            option.set_value(scm_to_int(new_value));
+                        return;
+                    }
+                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionValue<const QofInstance*>> ||
+                                  std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionValidatedValue<const QofInstance*>>)
+                    {
+                        if (scm_is_string(new_value))
+                        {
+                            auto strval{scm_to_utf8_string(new_value)};
+                            auto val{qof_instance_from_string(strval, option.get_ui_type())};
+                            option.set_value(val);
+                        }
+                        else
+                        {
+                            auto val{scm_to_value<const QofInstance*>(new_value)};
+                            option.set_value(val);
+                        }
+                        return;
+                    }
+                    auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
+                    option.set_value(value);
+                }, swig_get_option($self));
+        }
+        catch(const std::invalid_argument& err)
+        {
+            PERR("Option %s:%s failed to set value: %s",
+                 $self->get_section().c_str(), $self->get_name().c_str(),
+                 err.what());
+        }
     }
 
     void set_default_value_from_scm(SCM new_value)
     {
         if (!$self)
             return;
-        std::visit([new_value](auto& option) {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionDateValue>)
-                {
-                    if (scm_date_absolute(new_value))
-                        option.set_default_value(scm_absolute_date_to_time64(new_value));
-                    else
-                        option.set_default_value(scm_relative_date_get_period(new_value));
-                    return;
-                }
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionMultichoiceValue>)
-                {
-                    option.set_default_multiple(scm_to_multichoices(new_value,
-                                                                    option));
-                    return;
-                }
-                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionRangeValue<int>>)
-                {
-                    if (scm_is_pair(new_value))
-                        option.set_default_value(scm_to_int(scm_cdr(new_value)));
-                    else
-                        option.set_default_value(scm_to_int(new_value));
-                    return;
-                }
-                auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
-                option.set_default_value(value);
-            }, swig_get_option($self));
+        try {
+            std::visit([new_value](auto& option) {
+                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionDateValue>)
+                    {
+                        if (scm_date_absolute(new_value))
+                            option.set_default_value(scm_absolute_date_to_time64(new_value));
+                        else
+                            option.set_default_value(scm_relative_date_get_period(new_value));
+                        return;
+                    }
+                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionMultichoiceValue>)
+                    {
+                        option.set_default_multiple(scm_to_multichoices(new_value,
+                                                                        option));
+                        return;
+                    }
+                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionRangeValue<int>>)
+                    {
+                        if (scm_is_pair(new_value))
+                            option.set_default_value(scm_to_int(scm_cdr(new_value)));
+                        else
+                            option.set_default_value(scm_to_int(new_value));
+                        return;
+                    }
+                    if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionValue<const QofInstance*>> ||
+                                  std::is_same_v<std::decay_t<decltype(option)>,
+                                  GncOptionValidatedValue<const QofInstance*>>)
+                    {
+                        if (scm_is_string(new_value))
+                        {
+                            auto strval{scm_to_utf8_string(new_value)};
+                            auto val{qof_instance_from_string(strval, option.get_ui_type())};
+                            option.set_default_value(val);
+                        }
+                        else
+                        {
+                            auto val{scm_to_value<const QofInstance*>(new_value)};
+                            option.set_default_value(val);
+                        }
+                        return;
+                    }
+                    auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
+                    option.set_default_value(value);
+                }, swig_get_option($self));
+        }
+        catch(const std::invalid_argument& err)
+        {
+            PERR("Option %s:%s failed to set default value: %s",
+                 $self->get_section().c_str(), $self->get_name().c_str(), err.what());
+        }
     }
 
     SCM get_type()

commit 852b2ffc2effe61d13585141cd62505eb1770fdd
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Aug 13 14:01:27 2021 -0700

    Handle bare currency mnemonics, catch invalid GUID string exceptions.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index b0d413251..a20970303 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -33,6 +33,8 @@ extern "C"
 #include "gnc-ui-util.h"
 }
 
+static const QofLogModule log_module{"gnc.options"};
+
 const std::string GncOptionMultichoiceValue::c_empty_string{""};
 const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
 
@@ -348,20 +350,40 @@ qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
 QofInstance*
 qof_instance_from_string(const std::string& str, GncOptionUIType type)
 {
+    QofInstance* retval{nullptr};
+    // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic
     if (type == GncOptionUIType::CURRENCY ||
         type == GncOptionUIType::COMMODITY)
     {
         auto book{gnc_get_current_book()};
-        auto sep{str.find(":")};
-        auto name_space{str.substr(0, sep)};
-        auto mnemonic{str.substr(sep + 1, -1)};
         auto table = gnc_commodity_table_get_table(book);
-        return QOF_INSTANCE(gnc_commodity_table_lookup(table,
-                                                       name_space.c_str(),
-                                                       mnemonic.c_str()));
+        auto sep{str.find(":")};
+        if (sep != std::string::npos)
+        {
+            auto name_space{str.substr(0, sep)};
+            auto mnemonic{str.substr(sep + 1, -1)};
+            retval = QOF_INSTANCE(gnc_commodity_table_lookup(table,
+                                                             name_space.c_str(),
+                                                             mnemonic.c_str()));
+        }
+        if (!retval && type == GncOptionUIType::CURRENCY)
+            retval = QOF_INSTANCE(gnc_commodity_table_lookup(table,
+                                                             "CURRENCY",
+                                                             str.c_str()));
+    }
+
+    if (!retval)
+    {
+        try {
+            auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
+            retval = qof_instance_from_guid(&guid, type);
+        }
+        catch (const gnc::guid_syntax_exception& err)
+        {
+            PWARN("Failed to convert %s to a GUID", str.c_str());
+        }
     }
-    auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
-    return qof_instance_from_guid(&guid, type);
+    return retval;
 }
 
 std::string

commit e43ff93279be4d9ea2cf2d73bf89d6b1dcf0ca05
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 12 14:30:13 2021 -0700

    Fix some overly-long lines.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index a1051aa6b..784a65b1d 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -125,9 +125,10 @@ GncOption::set_value(ValueType value)
                        if constexpr (std::is_same_v<std::decay_t<ValueType>,
                                      GncMultichoiceOptionIndexVec>)
                            option.set_multiple(value);
-                       else if constexpr (std::is_same_v<ValueType, size_t> ||
-                                          std::is_same_v<std::decay_t<ValueType>, std::string> ||
-                                          std::is_same_v<std::remove_cv<ValueType>, char*>)
+                       else if constexpr
+                           (std::is_same_v<ValueType, size_t> ||
+                            std::is_same_v<std::decay_t<ValueType>, std::string> ||
+                            std::is_same_v<std::remove_cv<ValueType>, char*>)
                            option.set_value(value);
                    }
                }, *m_option);
@@ -150,12 +151,14 @@ GncOption::set_default_value(ValueType value)
                        (std::is_same_v<std::decay_t<decltype(option)>,
                         GncOptionMultichoiceValue>)
                    {
-                       if constexpr (std::is_same_v<std::decay_t<ValueType>,
-                                     GncMultichoiceOptionIndexVec>)
+                       if constexpr
+                           (std::is_same_v<std::decay_t<ValueType>,
+                            GncMultichoiceOptionIndexVec>)
                            option.set_multiple(value);
-                       else if constexpr (std::is_same_v<ValueType, size_t> ||
-                                          std::is_same_v<std::decay_t<ValueType>, std::string> ||
-                                          std::is_same_v<std::remove_cv<ValueType>, char*>)
+                       else if constexpr
+                           (std::is_same_v<ValueType, size_t> ||
+                            std::is_same_v<std::decay_t<ValueType>, std::string> ||
+                            std::is_same_v<std::remove_cv<ValueType>, char*>)
                            option.set_default_value(value);
                    }
                }, *m_option);

commit 1af97ebb9ad43ce39402108ab8adc1b2f3fe6f33
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 12 14:29:56 2021 -0700

    Implement registering/unregistering/calling change callbacks.
    
    Drives reloading the report when the Apply or OK button is pressed.

diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp
index 2095142b5..2d47cd0df 100644
--- a/gnucash/gnome/gnc-plugin-page-report.cpp
+++ b/gnucash/gnome/gnc-plugin-page-report.cpp
@@ -79,6 +79,7 @@ extern "C"
 
 
 #include <memory>
+#include <gnc-optiondb-impl.hpp>
 
 /* NW: you can add GNC_MOD_REPORT to gnc-engine.h
 or simply define it locally. Any unique string with
@@ -111,14 +112,14 @@ typedef struct GncPluginPageReportPrivate
     SCM cur_report;
     /// The Option DB for this report.
     GncOptionDB *cur_odb;
-    SCM option_change_cb_id;
+    size_t option_change_cb_id = 0;
 
     /* initial_report is special; it's the one that's saved and
      * restored.  The name_change_callback only gets called when
      * the initial_report name is changed. */
     SCM          initial_report;
     GncOptionDB  * initial_odb;
-    SCM          name_change_cb_id;
+    size_t       name_change_cb_id;
 
     /* keep a list of edited reports so that we can destroy them when
      * the window is closed. */
@@ -550,7 +551,7 @@ gnc_plugin_page_report_setup( GncPluginPage *ppage )
     priv->cur_report        = SCM_BOOL_F;
     priv->initial_report    = SCM_BOOL_F;
     priv->edited_reports    = SCM_EOL;
-    priv->name_change_cb_id = SCM_BOOL_F;
+    priv->name_change_cb_id = 0;
 
     g_object_get( ppage, "report-id", &report_id, nullptr );
 
@@ -643,22 +644,17 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
         scm_call_2(set_needs_save, inst_report, SCM_BOOL_T);
 
         priv->initial_odb = gnc_get_report_optiondb(inst_report);
-/*
+
         priv->name_change_cb_id =
-            gnc_option_db_register_change_callback(priv->initial_odb,
-                    gnc_plugin_page_report_refresh,
-                    priv,
-                    "General", "Report name");
-*/
+            priv->initial_odb->register_callback(
+                gnc_plugin_page_report_refresh, priv);
+
     }
 
     if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != nullptr))
     {
-/*
-        gnc_option_db_unregister_change_callback_id(priv->cur_odb,
-                priv->option_change_cb_id);
-*/
-        gnc_option_db_destroy(priv->cur_odb);
+        priv->cur_odb->unregister_callback(priv->option_change_cb_id);
+        priv->option_change_cb_id = 0;
         priv->cur_odb = nullptr;
     }
 
@@ -668,12 +664,11 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
     scm_gc_protect_object(priv->cur_report);
 
     priv->cur_odb = gnc_get_report_optiondb(inst_report);
-/*
+
     priv->option_change_cb_id =
-        gnc_option_db_register_change_callback(priv->cur_odb,
-                gnc_plugin_page_report_option_change_cb,
-                report, nullptr, nullptr);
-*/
+        priv->cur_odb->register_callback(
+            gnc_plugin_page_report_option_change_cb, report);
+
     if (gnc_html_history_forward_p(gnc_html_get_history(priv->html)))
     {
         gnc_plugin_page_report_set_fwd_button(report, TRUE);
diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 95f77a34b..d06f4a452 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -63,6 +63,25 @@ public:
 };
 
 using GncOptionSectionPtr = std::shared_ptr<GncOptionSection>;
+using GncOptionDBChangeCallback = void (*)(void* user_data);
+
+struct GncOptionDBCallback
+{
+    GncOptionDBCallback(size_t id, GncOptionDBChangeCallback func,
+                              void* data) :
+        m_id{id}, m_func{func}, m_data{data} {}
+    ~GncOptionDBCallback() = default;
+    GncOptionDBCallback(const GncOptionDBCallback&) = delete;
+    GncOptionDBCallback(GncOptionDBCallback&&) = default;
+    GncOptionDBCallback& operator=(const GncOptionDBCallback&) = default;
+    GncOptionDBCallback& operator=(GncOptionDBCallback&&) = default;
+
+    size_t m_id;
+    GncOptionDBChangeCallback m_func;
+    void* m_data;
+};
+
+using GncCallbackVec = std::vector<GncOptionDBCallback>;
 
 class GncOptionDB
 {
@@ -138,10 +157,14 @@ public:
                                         const std::string& section,
                                         const std::string& name) const noexcept;
     std::istream& load_option_key_value(std::istream& iss);
+    size_t register_callback(GncOptionDBChangeCallback, void*);
+    void unregister_callback(size_t);
+    void run_callbacks();
 private:
     GncOptionSection* m_default_section;
     std::vector<GncOptionSectionPtr> m_sections;
     bool m_dirty = false;
+    GncCallbackVec m_callbacks{};
 
     std::function<GncOptionUIItem*()> m_get_ui_value;
     std::function<void(GncOptionUIItem*)> m_set_ui_value;
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index c0629f7c4..763e7ec78 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -22,6 +22,7 @@
 \********************************************************************/
 
 #include <libguile.h>
+#include <functional>
 #include <string>
 #include <limits>
 #include <sstream>
@@ -666,6 +667,31 @@ GncOptionDB::load_from_key_value(std::istream& iss)
     return iss;
 }
 
+size_t
+GncOptionDB::register_callback(GncOptionDBChangeCallback cb, void* data)
+{
+    constexpr std::hash<GncOptionDBChangeCallback> cb_hash;
+    auto id{cb_hash(cb)};
+    if (std::find_if(m_callbacks.begin(), m_callbacks.end(),
+                     [id](auto&cb)->bool{ return cb.m_id == id; }) == m_callbacks.end())
+        m_callbacks.emplace_back(id, cb, data);
+    return id;
+}
+
+void
+GncOptionDB::unregister_callback(size_t id)
+{
+    std::remove_if(m_callbacks.begin(), m_callbacks.end(),
+                   [id](auto& cb)->bool { return cb.m_id == id; });
+}
+
+void
+GncOptionDB::run_callbacks()
+{
+    std::for_each(m_callbacks.begin(), m_callbacks.end(),
+                  [](auto& cb)->void { cb.m_func(cb.m_data); });
+}
+
 static inline void
 counter_option_path(const GncOption& option, GSList* list, std::string& name)
 {
@@ -1306,6 +1332,8 @@ gnc_option_db_commit(GncOptionDB* odb)
                                                 (void*)option.get_name().c_str());
                     } });
         });
+    if (!errors)
+        odb->run_callbacks();
     return errors;
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index f081157b8..2ca72e6ca 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -799,6 +799,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     }
 
     %}
+%ignore GncOptionDBCallback;
 
 %include "gnc-option-date.hpp"
 %include "gnc-option.hpp"

commit 86da12d844a4749a0334cf129ded53140b5cc351
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 12 14:28:44 2021 -0700

    Use RGB instead of RGBA for color set for color

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index b5275c46e..23c0ff7e3 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -261,12 +261,12 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
                                                       _("_Close"));
                         }
                         g_list_free (children);
-                    break; // Found the button-box, no need to continue.
+                        break; // Found the button-box, no need to continue.
+                    }
                 }
-            }
                 g_list_free (children);
-            break; // Found the box, no need to continue.
-        }
+                break; // Found the box, no need to continue.
+            }
         }
         g_list_free (children);
     }
@@ -2157,7 +2157,12 @@ public:
                                         (uint8_t)(color.green * 255),
                                         (uint8_t)(color.blue * 255),
                                         (uint8_t)(color.alpha * 255));
-        option.set_value(std::string{rgba_str});
+        auto rgb_str = g_strdup_printf("%2x%2x%2x",
+                                       (uint8_t)(color.red * 255),
+                                       (uint8_t)(color.green * 255),
+                                       (uint8_t)(color.blue * 255));
+// Hello World uses an old HTML4 attribute that doesn't understand alpha.
+        option.set_value(std::string{rgb_str});
         g_free(rgba_str);
     }
 };
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index e3707ff39..1dd35f963 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -157,7 +157,7 @@
   (gnc-make-string-option section name key docstring default (GncOptionUIType-FONT)))
 (define-public (gnc:make-color-option section name key docstring colors range use-alpha)
   (issue-deprecation-warning "gnc:make-color-option is deprecated. Make and register the option in one command with gnc-register-color-option.")
-  (let ((color-str (if use-alpha
+  (let ((color-str (if use-alpha ;; It's always false...
                       (format #f "~x~x~x~x" (car colors) (cadr colors) (caddr colors) (cadddr colors))
                       (format #f "~x~x~x" (car colors) (cadr colors) (caddr colors)))))
   (gnc-make-string-option section name key docstring color-str (GncOptionUIType-COLOR))))

commit a7f0476af91688d381fa43afd6f1a70ceb5cad53
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 8 15:25:09 2021 -0700

    GncMultichoiceUIItem: Cast the selection index to a size_t.
    
    To get correct match with template type.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 4f8c353dc..b5275c46e 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1204,7 +1204,7 @@ public:
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
         auto widget{GTK_COMBO_BOX(get_widget())};
-        option.set_value<size_t>(gtk_combo_box_get_active(widget));
+        option.set_value<size_t>(static_cast<size_t>(gtk_combo_box_get_active(widget)));
     }
 };
 

commit e8f855e6043f674c5d28a35a41ae672d825410d9
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 8 15:24:08 2021 -0700

    Oops, bailed on failure when finding the dialog box buttons.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 007b34e15..4f8c353dc 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -261,13 +261,13 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
                                                       _("_Close"));
                         }
                         g_list_free (children);
-                    }
                     break; // Found the button-box, no need to continue.
                 }
-                g_list_free (children);
             }
+                g_list_free (children);
             break; // Found the box, no need to continue.
         }
+        }
         g_list_free (children);
     }
 }

commit 86d7637160b4ec77f568ebc34da40b3866957ae9
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 8 15:22:53 2021 -0700

    Handle GncOptionMultichoiceValue::set_value parameters
    
    that don't match the get_value return type.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index cebd526ed..a1051aa6b 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -117,13 +117,19 @@ GncOption::set_value(ValueType value)
                          (std::is_same_v<std::decay_t<ValueType>,
                          RelativeDatePeriod> ||
                           std::is_same_v<std::decay_t<ValueType>, size_t>)))
-                           option.set_value(value);
+                       option.set_value(value);
                    if constexpr
                        (std::is_same_v<std::decay_t<decltype(option)>,
-                        GncOptionMultichoiceValue> &&
-                        std::is_same_v<std::decay_t<ValueType>,
-                        GncMultichoiceOptionIndexVec>)
-                       option.set_multiple(value);
+                        GncOptionMultichoiceValue>)
+                   {
+                       if constexpr (std::is_same_v<std::decay_t<ValueType>,
+                                     GncMultichoiceOptionIndexVec>)
+                           option.set_multiple(value);
+                       else if constexpr (std::is_same_v<ValueType, size_t> ||
+                                          std::is_same_v<std::decay_t<ValueType>, std::string> ||
+                                          std::is_same_v<std::remove_cv<ValueType>, char*>)
+                           option.set_value(value);
+                   }
                }, *m_option);
 }
 
@@ -139,13 +145,19 @@ GncOption::set_default_value(ValueType value)
                          (std::is_same_v<std::decay_t<ValueType>,
                          RelativeDatePeriod> ||
                           std::is_same_v<std::decay_t<ValueType>, size_t>)))
-                           option.set_default_value(value);
+                       option.set_default_value(value);
                    if constexpr
                        (std::is_same_v<std::decay_t<decltype(option)>,
-                        GncOptionMultichoiceValue> &&
-                        std::is_same_v<std::decay_t<ValueType>,
-                        GncMultichoiceOptionIndexVec>)
-                       option.set_multiple(value);
+                        GncOptionMultichoiceValue>)
+                   {
+                       if constexpr (std::is_same_v<std::decay_t<ValueType>,
+                                     GncMultichoiceOptionIndexVec>)
+                           option.set_multiple(value);
+                       else if constexpr (std::is_same_v<ValueType, size_t> ||
+                                          std::is_same_v<std::decay_t<ValueType>, std::string> ||
+                                          std::is_same_v<std::remove_cv<ValueType>, char*>)
+                           option.set_default_value(value);
+                   }
                }, *m_option);
 }
 void

commit 8acf52a69beaf5dfa7a4560831b2fae46a4294c9
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 5 18:22:37 2021 -0700

    Clean up variable declarations in create_list_widget.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index e0ad8f302..007b34e15 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1955,36 +1955,25 @@ public:
 static GtkWidget *
 create_list_widget(GncOption& option, char *name)
 {
-    GtkListStore *store;
-    GtkTreeView *view;
-    GtkTreeIter iter;
-    GtkTreeViewColumn *column;
-    GtkCellRenderer *renderer;
-    GtkTreeSelection *selection;
-
-    GtkWidget *button;
-    GtkWidget *frame;
-    GtkWidget *hbox;
-    GtkWidget *bbox;
-
-    frame = gtk_frame_new(name);
-    hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+    auto frame = gtk_frame_new(name);
+    auto hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
     gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
     gtk_container_add(GTK_CONTAINER(frame), hbox);
 
-    store = gtk_list_store_new(1, G_TYPE_STRING);
-    view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
+    auto store = gtk_list_store_new(1, G_TYPE_STRING);
+    auto view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
     g_object_unref(store);
-    renderer = gtk_cell_renderer_text_new();
-    column = gtk_tree_view_column_new_with_attributes("", renderer,
-             "text", 0,
-             NULL);
+    auto renderer = gtk_cell_renderer_text_new();
+    auto column = gtk_tree_view_column_new_with_attributes("", renderer,
+                                                           "text", 0,
+                                                           NULL);
     gtk_tree_view_append_column(view, column);
     gtk_tree_view_set_headers_visible(view, FALSE);
 
     auto num_values = option.num_permissible_values();
     for (decltype(num_values) i = 0; i < num_values; i++)
     {
+        GtkTreeIter iter;
         auto raw_string = option.permissible_value_name(i);
         auto string = (raw_string && *raw_string) ? _(raw_string) : "";
         gtk_list_store_append(store, &iter);
@@ -1993,16 +1982,16 @@ create_list_widget(GncOption& option, char *name)
 
     gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0);
 
-    selection = gtk_tree_view_get_selection(view);
+    auto selection = gtk_tree_view_get_selection(view);
     gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
     g_signal_connect(selection, "changed",
                      G_CALLBACK(list_changed_cb), &option);
 
-    bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+    auto bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
     gtk_box_pack_end(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
 
-    button = gtk_button_new_with_label(_("Select All"));
+    auto button = gtk_button_new_with_label(_("Select All"));
     gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
     gtk_widget_set_tooltip_text(button, _("Select all entries."));
 

commit e4c9900c9e7f0c6328b13feae80112f55bbee7be
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 5 18:21:53 2021 -0700

    Avoid infinite recursion in toggle buttons.
    
    Calling activate on a toggle button toggles it again. No need for that.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 29017452d..e0ad8f302 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1391,18 +1391,6 @@ BothDateEntry::get_entry()
 void
 BothDateEntry::toggle_relative(bool use_absolute)
 {
-    if (use_absolute)
-    {
-        gtk_widget_set_sensitive(m_abs_entry->get_entry(), TRUE);
-        gtk_widget_set_sensitive(m_rel_entry->get_entry(), FALSE);
-        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_abs_button), TRUE);
-    }
-    else
-    {
-        gtk_widget_set_sensitive(m_rel_entry->get_entry(), TRUE);
-        gtk_widget_set_sensitive(m_abs_entry->get_entry(), FALSE);
-        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_rel_button), TRUE);
-    }
     m_use_absolute = use_absolute;
 }
 

commit 3c95ad8a0015f3605ac7516b140322378c9fdb87
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 5 18:20:19 2021 -0700

    Create and use renderer for multichoice option widgets.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index d7864f3e5..29017452d 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1167,14 +1167,8 @@ create_multichoice_widget(GncOption& option)
     auto num_values = option.num_permissible_values();
 
     g_return_val_if_fail(num_values >= 0, NULL);
-
-    /* GtkComboBox still does not support per-item tooltips, so have
-       created a basic one called Combott implemented in gnc-combott.
-       Have highlighted changes in this file with comments for when
-       the feature of per-item tooltips is implemented in gtk,
-       see https://bugs.gnucash.org/show_bug.cgi?id=303717 */
-
-    auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+    auto renderer = gtk_cell_renderer_text_new();
+    auto store = gtk_list_store_new(1, G_TYPE_STRING);
     /* Add values to the list store, entry and tooltip */
     for (decltype(num_values) i = 0; i < num_values; i++)
     {
@@ -1186,6 +1180,9 @@ create_multichoice_widget(GncOption& option)
     }
     /* Create the new Combo with tooltip and add the store */
     auto widget{GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)))};
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(widget), renderer, TRUE);
+    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(widget),
+                                   renderer, "text", 0);
     g_object_unref(store);
 
     g_signal_connect(G_OBJECT(widget), "changed",
@@ -1223,7 +1220,6 @@ create_option_widget<GncOptionUIType::MULTICHOICE> (GncOption& option, GtkGrid *
     auto widget = create_multichoice_widget(option);
     auto ui_item{std::make_unique<GncGtkMultichoiceUIItem>(widget)};
 
-//GncCombott doesn't emit a changed signal
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
@@ -1300,12 +1296,7 @@ private:
 RelativeDateEntry::RelativeDateEntry(GncOption& option)
 {
 
-    /* GtkComboBox still does not support per-item tooltips, so have
-       created a basic one called Combott implemented in gnc-combott.
-       Have highlighted changes in this file with comments for when
-       the feature of per-item tooltips is implemented in gtk,
-       see https://bugs.gnucash.org/show_bug.cgi?id=303717 */
-
+    auto renderer = gtk_cell_renderer_text_new();
     auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
     /* Add values to the list store, entry and tooltip */
     auto num = option.num_permissible_values();
@@ -1319,6 +1310,10 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option)
 
     /* Create the new Combo with tooltip and add the store */
     m_entry = GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)));
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(m_entry), renderer, TRUE);
+    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(m_entry),
+                                   renderer, "text", 0);
+
     g_object_unref(store);
 
     g_signal_connect(G_OBJECT(m_entry), "changed",

commit 8079470c8aeed8601522a3336055aaec2760ae37
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 5 17:26:04 2021 -0700

    Fix Color chooser option value setting and getting.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 5502ab575..d7864f3e5 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -2166,19 +2166,25 @@ public:
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         GdkRGBA color;
-        if (gdk_rgba_parse(&color,
-                           option.get_value<std::string>().c_str()))
+        auto rgba_str{g_strdup_printf("#%s",
+                                      option.get_value<std::string>().c_str())};
+        if (gdk_rgba_parse(&color, rgba_str))
         {
             auto color_button = GTK_COLOR_CHOOSER(get_widget());
             gtk_color_chooser_set_rgba(color_button, &color);
         }
+        g_free(rgba_str);
      }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
         GdkRGBA color;
         auto color_button = GTK_COLOR_CHOOSER(get_widget());
         gtk_color_chooser_get_rgba(color_button, &color);
-        auto rgba_str = gdk_rgba_to_string(&color);
+        auto rgba_str = g_strdup_printf("%2x%2x%2x%2x",
+                                        (uint8_t)(color.red * 255),
+                                        (uint8_t)(color.green * 255),
+                                        (uint8_t)(color.blue * 255),
+                                        (uint8_t)(color.alpha * 255));
         option.set_value(std::string{rgba_str});
         g_free(rgba_str);
     }

commit c83a3f44dd0774a28c6e1bb22e809f7dce33c391
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 5 15:41:50 2021 -0700

    Fix paste error. Call get, not set, when we want to get.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 03aa01069..5502ab575 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -2177,7 +2177,7 @@ public:
     {
         GdkRGBA color;
         auto color_button = GTK_COLOR_CHOOSER(get_widget());
-        gtk_color_chooser_set_rgba(color_button, &color);
+        gtk_color_chooser_get_rgba(color_button, &color);
         auto rgba_str = gdk_rgba_to_string(&color);
         option.set_value(std::string{rgba_str});
         g_free(rgba_str);

commit f0ecc0e2ebd33f7286d22adf1b12c77f45fba771
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 5 15:41:03 2021 -0700

    std::string(char*) crashes if it's given a nullptr.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index bc632648b..03aa01069 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -2320,8 +2320,11 @@ public:
     {
         auto string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_widget()));
         DEBUG("filename %s", string ? string : "(null)");
-        option.set_value(std::string{string});
-        g_free(string);
+        if (string)
+        {
+            option.set_value(std::string{string});
+            g_free(string);
+        }
     }
 };
 

commit b6622a386b8a3c6aa0b7dd42b296784202691922
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Aug 4 17:13:23 2021 -0700

    Pick up option aliases recently added in maint.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 60f7ccd90..c0629f7c4 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -101,6 +101,10 @@ const OptionAliases Aliases::c_option_aliases
     {"Links", {nullptr, "Transaction Links"}},
     // invoice.scm, renamed November 2018
     {"Individual Taxes", {nullptr, "Use Detailed Tax Summary"}},
+    {"Show Accounts until level", {nullptr, "Levels of Subaccounts"}},
+    {"Invoice number", {nullptr, "Invoice Number"}},
+    {"Report title", {nullptr, "Report Title"}},
+    {"Extra notes", {nullptr, "Extra Notes"}},
     // income-gst-statement.scm
     {"default format", {nullptr, "Default Format"}},
     {"Report format", {nullptr, "Report Format"}},

commit 26946d792c63aca947e77a9c01dccb3a84be302a
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Aug 4 12:05:32 2021 -0700

    Configure the book options before trying to load them.

diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp
index 68b337977..91cb70620 100644
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -4282,6 +4282,7 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
     GNCOptionWin *optionwin;
 
     options = gnc_option_db_new();
+    gnc_option_db_book_options(options);
     qof_book_load_options (book, gnc_option_db_load, options);
     gnc_option_db_clean (options);
 

commit 914f5f359bcc486c40dd941f5f17908ed794dfa7
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Aug 4 12:05:02 2021 -0700

    Don't unref option UI elements.
    
    They're wrapped in unique_ptr and will take care of themselves.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 43d14f6a8..bc632648b 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1254,7 +1254,7 @@ class AbsoluteDateEntry : public GncDateEntry
 {
 public:
     AbsoluteDateEntry(GncOption& option);
-    ~AbsoluteDateEntry() { g_object_unref(G_OBJECT(m_entry)); }
+    ~AbsoluteDateEntry() = default;
     void set_entry_from_option(GncOption& option) override;
     void set_option_from_entry(GncOption& option) override;
     GtkWidget* get_entry() override { return GTK_WIDGET(m_entry); }
@@ -1287,7 +1287,7 @@ class RelativeDateEntry : public GncDateEntry
 {
 public:
     RelativeDateEntry(GncOption& option);
-    ~RelativeDateEntry() { g_object_unref(G_OBJECT(m_entry)); };
+    ~RelativeDateEntry() = default;
     void set_entry_from_option(GncOption& option) override;
     void set_option_from_entry(GncOption& option) override;
     GtkWidget* get_widget() override { return m_entry; }
@@ -1344,7 +1344,7 @@ class BothDateEntry : public GncDateEntry
 {
 public:
     BothDateEntry(GncOption& option);
-    ~BothDateEntry(); //The GncOptionGtkUIItem owns the widget
+    ~BothDateEntry() = default; //The GncOptionGtkUIItem owns the widget
     void set_entry_from_option(GncOption& option) override;
     void set_option_from_entry(GncOption& option) override;
     GtkWidget* get_widget() override { return m_widget; }
@@ -1387,12 +1387,6 @@ BothDateEntry::BothDateEntry(GncOption& option) :
 
 }
 
-BothDateEntry::~BothDateEntry()
-{
-    g_object_unref(G_OBJECT(m_abs_button));
-    g_object_unref(G_OBJECT(m_rel_button));
-}
-
 GtkWidget*
 BothDateEntry::get_entry()
 {

commit 71955cc326fda6897af12e3093df0417d0598ed2
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 3 17:06:22 2021 -0700

    Correctly terminate gtk_list_store_set().

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index b3e387bc2..43d14f6a8 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1182,7 +1182,7 @@ create_multichoice_widget(GncOption& option)
         auto itemstring = option.permissible_value_name(i);
         gtk_list_store_append (store, &iter);
         gtk_list_store_set(store, &iter, 0,
-                           (itemstring && *itemstring) ? _(itemstring) : "", 1);
+                           (itemstring && *itemstring) ? _(itemstring) : "", -1);
     }
     /* Create the new Combo with tooltip and add the store */
     auto widget{GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)))};
@@ -1314,7 +1314,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option)
         GtkTreeIter  iter;
         gtk_list_store_append (store, &iter);
         gtk_list_store_set (store, &iter, 0,
-                            option.permissible_value_name(index), 1);
+                            option.permissible_value_name(index), -1);
     }
 
     /* Create the new Combo with tooltip and add the store */

commit a97b3e0c6d21cd146d60326a6c59cc8143a804c5
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 3 17:05:04 2021 -0700

    Don't let C code destroy the ODB, it's owned by Guile.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 7d804c230..60f7ccd90 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1278,7 +1278,7 @@ gnc_option_db_new(void)
 void
 gnc_option_db_destroy(GncOptionDB* odb)
 {
-    delete odb;
+    PWARN("Direct Destroy called on GncOptionDB %" G_GUINT64_FORMAT, (uint64_t)odb);
 }
 
 GList*

commit 58d090ff326aebe685dfaf4b726601a8d30f70b3
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 3 17:03:49 2021 -0700

    Protect strncmp from empty string. It crashes if given one.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index dfef57057..b3e387bc2 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -454,7 +454,7 @@ gnc_options_dialog_append_page(GNCOptionWin * propertybox,
                                GncOptionSectionPtr& section)
 {
     auto name = section->get_name().c_str();
-    if (!name)
+    if (!name || *name == '\0')
         return -1;
 
     if (strncmp(name, "__", 2) == 0)

commit a2e1a3e1b8641968709ee2d82990152c8022990c
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Aug 2 16:52:34 2021 -0700

    Extract the GncOptionDB* from the Scheme dispatch function closure.
    
    Scheme code keeps the GncOptionDB in a closure. Extract it for C/C++
    use. Ownership remains with the closure, don't free the GncOptionDB*.

diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp
index 68b90b383..b56564a4c 100644
--- a/gnucash/gnome/dialog-report-style-sheet.cpp
+++ b/gnucash/gnome/dialog-report-style-sheet.cpp
@@ -168,7 +168,7 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss,
 {
     SCM get_options = scm_c_eval_string ("gnc:html-style-sheet-options");
 
-    SCM            scm_options = scm_call_1 (get_options, sheet_info);
+    SCM            scm_dispatch = scm_call_1 (get_options, sheet_info);
     ss_info        * ssinfo = g_new0 (ss_info, 1);
     GtkWidget      * window;
     gchar          * title;
@@ -176,7 +176,7 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss,
 
     title = g_strdup_printf(_("HTML Style Sheet Properties: %s"), name);
     ssinfo->odialog = gnc_options_dialog_new(title, parent);
-    ssinfo->odb     = (GncOptionDB *)scm_to_pointer(scm_options);
+    ssinfo->odb     = gnc_get_optiondb_from_dispatcher(scm_dispatch);
     ssinfo->stylesheet = sheet_info;
     ssinfo->row_ref    = row_ref;
     g_free (title);
diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp
index 1365815cd..2095142b5 100644
--- a/gnucash/gnome/gnc-plugin-page-report.cpp
+++ b/gnucash/gnome/gnc-plugin-page-report.cpp
@@ -77,6 +77,9 @@ extern "C"
 #include "print-session.h"
 }
 
+
+#include <memory>
+
 /* NW: you can add GNC_MOD_REPORT to gnc-engine.h
 or simply define it locally. Any unique string with
 a gnucash- prefix will do. Then just set a log level
@@ -584,7 +587,6 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
     GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(data);
     GncPluginPageReportPrivate *priv;
     int  report_id;
-    SCM  get_options    = scm_c_eval_string("gnc:report-options");
     SCM  set_needs_save = scm_c_eval_string("gnc:report-set-needs-save?!");
     SCM  inst_report;
 
@@ -640,8 +642,7 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
         DEBUG("calling set_needs_save for report with id=%d", report_id);
         scm_call_2(set_needs_save, inst_report, SCM_BOOL_T);
 
-        priv->initial_odb =
-            (GncOptionDB *)scm_to_pointer(scm_call_1(get_options, inst_report));
+        priv->initial_odb = gnc_get_report_optiondb(inst_report);
 /*
         priv->name_change_cb_id =
             gnc_option_db_register_change_callback(priv->initial_odb,
@@ -666,8 +667,7 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
     priv->cur_report = inst_report;
     scm_gc_protect_object(priv->cur_report);
 
-    priv->cur_odb = (GncOptionDB *)scm_to_pointer(scm_call_1(get_options,
-                                                             inst_report));
+    priv->cur_odb = gnc_get_report_optiondb(inst_report);
 /*
     priv->option_change_cb_id =
         gnc_option_db_register_change_callback(priv->cur_odb,
@@ -1032,20 +1032,25 @@ gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name)
     ENTER("page %p, name %s", page, name);
     priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page);
 
-    /* Is this a redundant call? */
-    old_name = gnc_option_db_lookup_string_value(priv->cur_odb, "General",
-                                                 "Report name");
-    DEBUG("Comparing old name '%s' to new name '%s'",
-          old_name ? old_name : "(null)", name);
-    if (old_name && (strcmp(old_name, name) == 0))
+    if (priv->cur_odb)
     {
-        LEAVE("no change");
-        return;
-    }
 
-    /* Store the new name for the report. */
-    gnc_option_db_set_string_value(priv->cur_odb, "General",
-                                    "Report name", name);
+        /* Is this a redundant call? */
+        old_name = gnc_option_db_lookup_string_value(priv->cur_odb, "General",
+                                                     "Report name");
+        DEBUG("Comparing old name '%s' to new name '%s'",
+              old_name ? old_name : "(null)", name);
+        if (old_name && (strcmp(old_name, name) == 0))
+        {
+            LEAVE("no change");
+            return;
+        }
+
+        /* Store the new name for the report. */
+        gnc_option_db_set_string_value(priv->cur_odb, "General",
+                                       "Report name", name);
+
+    }
 
     /* Have to manually call the option change hook. */
     gnc_plugin_page_report_option_change_cb(page);
diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp
index b5cc9393c..4898c9cb8 100644
--- a/gnucash/gnome/window-report.cpp
+++ b/gnucash/gnome/window-report.cpp
@@ -26,6 +26,7 @@
  ********************************************************************/
 #include <libguile.h>
 #include <glib/gi18n.h>
+#include <memory>
 
 extern "C"
 {
@@ -208,10 +209,8 @@ gboolean
 gnc_report_edit_options(SCM report, GtkWindow *parent)
 {
     SCM set_editor        = scm_c_eval_string("gnc:report-set-editor-widget!");
-    SCM get_options       = scm_c_eval_string("gnc:report-options");
     SCM get_report_type   = scm_c_eval_string("gnc:report-type");
     SCM ptr;
-    SCM options;
     GncOptionDB* odb;
     GtkWidget *options_widget = nullptr;
 
@@ -220,14 +219,13 @@ gnc_report_edit_options(SCM report, GtkWindow *parent)
         return TRUE;
 
     /* Check if this report has options to edit */
-    options = scm_call_1(get_options, report);
-    if (options == SCM_BOOL_F)
+    odb = gnc_get_report_optiondb(report);
+    if (!odb)
     {
         gnc_warning_dialog (parent, "%s",
                             _("There are no options for this report."));
         return FALSE;
     }
-    odb = (GncOptionDB*)scm_to_pointer(options);
 
     /* Multi-column type reports need a special options dialog */
     ptr = scm_call_1(get_report_type, report);
@@ -249,3 +247,11 @@ gnc_report_edit_options(SCM report, GtkWindow *parent)
 
     return TRUE;
 }
+
+GncOptionDB*
+gnc_get_report_optiondb(SCM report_instance)
+{
+    SCM  get_report_options    = scm_c_eval_string("gnc:report-options");
+    auto scm_dispatch{scm_call_1(get_report_options, report_instance)};
+    return gnc_get_optiondb_from_dispatcher(scm_dispatch);
+}
diff --git a/gnucash/gnome/window-report.h b/gnucash/gnome/window-report.h
index 1ecaf626e..e41cea70f 100644
--- a/gnucash/gnome/window-report.h
+++ b/gnucash/gnome/window-report.h
@@ -47,5 +47,6 @@ gboolean   gnc_report_edit_options(SCM report, GtkWindow *parent);
 
 #ifdef __cplusplus
 }
+GncOptionDB* gnc_get_report_optiondb(SCM report_instance);
 #endif
 #endif
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index ed7603c28..7d804c230 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -824,6 +824,29 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
         });
 }
 
+GncOptionDB*
+gnc_get_optiondb_from_dispatcher(SCM dispatcher)
+{
+    SCM  get_options = scm_c_eval_string("gnc:options-get");
+    if (dispatcher == SCM_BOOL_F)
+        return nullptr;
+    auto scm_ptr{scm_call_1(get_options, dispatcher)};
+    auto smob{!scm_is_null(scm_ptr) && SCM_INSTANCEP(scm_ptr) &&
+              scm_is_true(scm_slot_exists_p(scm_ptr, SCM_EOL)) ?
+              scm_slot_ref(scm_ptr, SCM_EOL) : (scm_ptr)};
+
+    void *c_ptr{nullptr};
+    if (!SCM_NULLP(smob))
+    {
+        if (SCM_POINTER_P(smob))
+            c_ptr = SCM_POINTER_VALUE(smob);
+        else
+            c_ptr = reinterpret_cast<void*>(SCM_CELL_WORD_1(smob));
+    }
+    auto u_ptr{static_cast<std::unique_ptr<GncOptionDB>*>(c_ptr)};
+    return u_ptr->get();
+}
+
 void
 gnc_register_string_option(GncOptionDB* db, const char* section,
                            const char* name, const char* key,
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index 007bfd1f4..d2fe65540 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -59,6 +59,20 @@ GncOptionDB* gnc_option_db_new(void);
  */
 void gnc_option_db_destroy(GncOptionDB* odb);
 
+/**
+ * Obtain a GncOptionDB* from Scheme
+ *
+ * When report or stylesheet options are generated in Scheme the GncObjectDB is
+ * wrapped in a std::unique_ptr and then in a Guile SMOB by SWIG. The GUI code
+ * needs a reference to the GncObjectDB and we don't want to introduce swig
+ * library dependencies.
+ *
+ * @param dispatch The scheme dispatch function returned by gnc:new-options
+ * @return GncOptiondDB* Do not free this pointer!
+ */
+GncOptionDB*
+gnc_get_optiondb_from_dispatcher(SCM dispatcher);
+
 /**
  * Write all changed ui_item values to their options.
  * @param odb The GncOptionDB.
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 0e84c17f2..e3707ff39 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -103,7 +103,11 @@
   (let ((optiondb (new-gnc-optiondb)))
     (define (dispatch key)
       optiondb)
-  dispatch))
+    dispatch))
+
+;; Use the dispatch function to get the optiondb
+(define-public (gnc:options-get dispatch)
+  (dispatch 'get))
 
 (define-public (gnc:options-set-default-section optiondb section)
   (GncOptionDB-set-default-section (GncOptionDBPtr-get (optiondb 'set-default-section)) section))

commit 99eaa81a280e195a465290d6b9098db13b22a102
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 1 12:43:14 2021 -0700

    Fix parse of commodity namespace from scheme istream.
    
    Just ignore 2 instead of ignoring 1 twice. No ifdef required.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index d614b0dd2..ae386afe3 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -414,14 +414,10 @@ gnc_option_from_scheme (std::istream& iss, OptType& opt)
         {
             iss.ignore(strlen(commodity_scm_intro) + 1, '"');
             std::getline(iss, name_space, '"');
- // libc++ doesn't consume the end character, libstdc++ does
-#ifdef _LIBCPP_VERSION
-            iss.ignore(1, '"');
-#endif
         }
         else
             name_space = GNC_COMMODITY_NS_CURRENCY;
-        iss.ignore(1, '"');
+        iss.ignore(2, '"');
         std::getline(iss, mnemonic, '"');
 
         if (type == GncOptionUIType::COMMODITY)

commit 0bd9033bb3081ad94f80c507d455cf4ed04b54d8
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 1 11:11:20 2021 -0700

    Move includes of glib and gtk out or extern C in new cpp files.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 975b13543..dfef57057 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -30,11 +30,12 @@ extern "C"
 
 #include <Account.h> // To include as C++ overriding later indirect includes
 #include <libguile.h>
-extern "C"
-{
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
 #include <glib/gi18n.h>
+
+extern "C"
+{
 #include "swig-runtime.h"
 
 #include "gnc-tree-model-budget.h" //FIXME?
diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp
index df59ee33f..68b337977 100644
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -33,15 +33,16 @@
     @author Copyright (C) 2003,2005,2006 David Hampton <hampton at employees.org>
 */
 #include <libguile.h>
-extern "C"
-{
-#include <config.h>
-
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
 #include <gdk/gdkkeysyms.h>
 
+extern "C"
+{
+#include <config.h>
+
+
 #include "gnc-plugin.h"
 #include "gnc-plugin-manager.h"
 #include "gnc-main-window.h"
diff --git a/gnucash/gnome/assistant-hierarchy.cpp b/gnucash/gnome/assistant-hierarchy.cpp
index 3dd7c5529..6315e78a9 100644
--- a/gnucash/gnome/assistant-hierarchy.cpp
+++ b/gnucash/gnome/assistant-hierarchy.cpp
@@ -23,6 +23,10 @@
 \********************************************************************/
 
 #include <libguile.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
 extern "C"
 {
 #include <config.h>
@@ -32,9 +36,6 @@ extern "C"
 #include <windows.h>
 #endif
 
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp
index 519df3889..48144d929 100644
--- a/gnucash/gnome/business-options-gnome.cpp
+++ b/gnucash/gnome/business-options-gnome.cpp
@@ -23,13 +23,13 @@
  */
 
 #include <libguile.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
 
 extern "C"
 {
 #include <config.h>
 
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
 #include "swig-runtime.h"
 #include "guile-mappings.h"
 
diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp
index cf1426859..9e1adef41 100644
--- a/gnucash/gnome/dialog-report-column-view.cpp
+++ b/gnucash/gnome/dialog-report-column-view.cpp
@@ -22,12 +22,13 @@
  ********************************************************************/
 
 #include <libguile.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
 extern "C"
 {
 #include <config.h>
 
-#include <glib/gi18n.h>
-#include <gtk/gtk.h>
 #include "swig-runtime.h"
 
 #include "dialog-utils.h"
diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp
index 60d4287d7..68b90b383 100644
--- a/gnucash/gnome/dialog-report-style-sheet.cpp
+++ b/gnucash/gnome/dialog-report-style-sheet.cpp
@@ -23,13 +23,13 @@
  ********************************************************************/
 
 #include <libguile.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
 extern "C"
 {
 #include <config.h>
 
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-
 #include "dialog-report-style-sheet.h"
 #include "dialog-utils.h"
 #include "gnc-component-manager.h"
diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp
index 155496223..1365815cd 100644
--- a/gnucash/gnome/gnc-plugin-page-report.cpp
+++ b/gnucash/gnome/gnc-plugin-page-report.cpp
@@ -37,13 +37,14 @@
     @author Copyright (C) 2005 David Hampton <hampton at employees.org>
 */
 #include <libguile.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
 extern "C"
 {
 #include <config.h>
 
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
 #include <sys/stat.h>
 #include <errno.h>
 
diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp
index 10a3d23f1..b5cc9393c 100644
--- a/gnucash/gnome/window-report.cpp
+++ b/gnucash/gnome/window-report.cpp
@@ -25,11 +25,12 @@
  *                                                                  *
  ********************************************************************/
 #include <libguile.h>
+#include <glib/gi18n.h>
+
 extern "C"
 {
 #include <config.h>
 
-#include <glib/gi18n.h>
 #include <errno.h>
 #include <sys/stat.h>
 
diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i
index 2fc313df6..ab8aead4c 100644
--- a/libgnucash/app-utils/app-utils.i
+++ b/libgnucash/app-utils/app-utils.i
@@ -21,6 +21,7 @@
 %module sw_app_utils
 %{
 /* Includes the header in the wrapper code */
+#include <glib.h>
 #ifdef __cplusplus
 extern "C"
 {
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 482501fd9..d614b0dd2 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -42,6 +42,7 @@ extern "C"
 #include <functional>
 #include <variant>
 #include <iostream>
+#include <limits>
 
 #include "gnc-option-uitype.hpp"
 
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 158fdea5a..089ff0825 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -24,11 +24,7 @@
 #ifndef GNC_OPTION_HPP_
 #define GNC_OPTION_HPP_
 
-extern "C"
-{
 #include <glib.h>
-}
-
 #include <libguile.h>
 #include <string>
 #include <iostream>
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index ce0017646..e00f0894a 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -26,9 +26,10 @@
 #include <gnc-optiondb-impl.hpp>
 #include <gnc-option-ui.hpp>
 #include <kvp-value.hpp>
+#include <glib-2.0/glib.h>
+
 extern "C"
 {
-#include <glib-2.0/glib.h>
 #include <gnc-ui-util.h>
 #include <gnc-session.h>
 }

commit 0f02236ebe751f32609b64b1a67fdad1ab4aa5e5
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Aug 1 10:27:50 2021 -0700

    Fix gcc warnings.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 7e2720679..975b13543 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -150,7 +150,7 @@ GncOptionUIFactory::create(GncOption& option, GtkGrid* page, GtkLabel* name,
     auto func{s_registry[static_cast<size_t>(type)]};
     if (func)
         return func(option, page, name, description, enclosing, packed);
-    PERR("No function registered for type %d", type);
+    PERR("No function registered for type %d", static_cast<int>(type));
     return nullptr;
 }
 
@@ -1506,7 +1506,8 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box,
             option.set_ui_item(std::make_unique<GncOptionDateUIItem>(std::make_unique<BothDateEntry>(option), type));
             break;
         default:
-            PERR("Attempted to create date option widget with wrong UI type %d", type);
+            PERR("Attempted to create date option widget with wrong UI type %d",
+                 static_cast<int>(type));
             return nullptr;
             break;
     }
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 31b5acdda..f081157b8 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -760,7 +760,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return scm_reverse(values);
     }
 
-    SCM get_scm_value(const GncOptionMultichoiceValue& option)
+    static SCM
+    get_scm_value(const GncOptionMultichoiceValue& option)
     {
 
         auto indexes = option.get_multiple();
@@ -771,7 +772,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return scm_from_multichoices(indexes, option);
      }
 
-    SCM get_scm_default_value(const GncOptionMultichoiceValue& option)
+    static SCM
+    get_scm_default_value(const GncOptionMultichoiceValue& option)
     {
 
         auto indexes = option.get_default_multiple();
@@ -780,14 +782,16 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return scm_from_multichoices(indexes, option);
      }
 
-    SCM get_scm_value(const GncOptionRangeValue<int>& option)
+    static SCM
+    get_scm_value(const GncOptionRangeValue<int>& option)
     {
         auto val{option.get_value()};
         auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")};
         return scm_cons(desig, scm_from_int(val));
     }
 
-    SCM get_scm_default_value(const GncOptionRangeValue<int>& option)
+    static SCM
+    get_scm_default_value(const GncOptionRangeValue<int>& option)
     {
         auto val{option.get_default_value()};
         auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")};
@@ -945,10 +949,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %template(gnc_register_number_range_option_int) gnc_register_number_range_option<int>;
 
 %inline %{
-    GncOption* gnc_make_account_list_option(const char* section,
-                                            const char* name, const char* key,
-                                            const char* doc_string,
-                                            const GncOptionAccountList& value)
+    static GncOption*
+    gnc_make_account_list_option(const char* section,
+                                 const char* name, const char* key,
+                                 const char* doc_string,
+                                 const GncOptionAccountList& value)
     {
         try {
             return new GncOption{GncOptionAccountListValue{section, name, key,
@@ -961,12 +966,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
-    GncOption* gnc_make_account_list_limited_option(const char* section,
-                                                    const char* name,
-                                                    const char* key,
-                                                    const char* doc_string,
-                                                    const GncOptionAccountList& value,
-                                                    GncOptionAccountTypeList&& allowed)
+    static GncOption*
+    gnc_make_account_list_limited_option(const char* section,
+                                         const char* name,
+                                         const char* key,
+                                         const char* doc_string,
+                                         const GncOptionAccountList& value,
+                                         GncOptionAccountTypeList&& allowed)
     {
         try
         {
@@ -981,12 +987,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
-    GncOption* gnc_make_account_sel_limited_option(const char* section,
-                                                   const char* name,
-                                                   const char* key,
-                                                   const char* doc_string,
-                                                   const Account* value,
-                                                   GncOptionAccountTypeList&& allowed)
+    static GncOption*
+    gnc_make_account_sel_limited_option(const char* section, const char* name,
+                                        const char* key, const char* doc_string,
+                                        const Account* value,
+                                        GncOptionAccountTypeList&& allowed)
     {
         try
         {
@@ -1001,12 +1006,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
-    GncOption* gnc_make_date_option(const char* section,
-                                    const char* name, const char* key,
-                                    const char* doc_string,
-                                    const SCM default_val,
-                                    RelativeDatePeriodVec& period_set,
-                                    bool both)
+    static GncOption*
+    gnc_make_date_option(const char* section, const char* name, const char* key,
+                         const char* doc_string, const SCM default_val,
+                         RelativeDatePeriodVec& period_set, bool both)
     {
 
         try {
@@ -1046,11 +1049,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
-    GncOption* gnc_make_multichoice_option(const char* section,
-                                           const char* name, const char* key,
-                                           const char* doc_string,
-                                           const char* default_val,
-                                           GncMultichoiceOptionChoices&& choices)
+    static GncOption*
+    gnc_make_multichoice_option(const char* section, const char* name,
+                                const char* key, const char* doc_string,
+                                const char* default_val,
+                                GncMultichoiceOptionChoices&& choices)
     {
         try {
             std::string defval{default_val};
@@ -1072,11 +1075,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
-    GncOption* gnc_make_list_option(const char* section,
-                                    const char* name, const char* key,
-                                    const char* doc_string,
-                                    GncMultichoiceOptionIndexVec indexes,
-                                    GncMultichoiceOptionChoices&& list)
+    static GncOption*
+    gnc_make_list_option(const char* section, const char* name, const char* key,
+                         const char* doc_string,
+                         GncMultichoiceOptionIndexVec indexes,
+                         GncMultichoiceOptionChoices&& list)
     {
         try {
             return new GncOption{GncOptionMultichoiceValue{section, name, key,
@@ -1090,10 +1093,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
-    GncOption* gnc_make_range_value_option(const char* section,
-                                           const char* name, const char* key,
-                                           const char* doc_string, double value,
-                                           double min, double max, double step)
+    static GncOption*
+    gnc_make_range_value_option(const char* section, const char* name,
+                                const char* key, const char* doc_string,
+                                double value, double min, double max,
+                                double step)
     {
         try
         {
@@ -1108,10 +1112,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
-    GncOption* gnc_make_plot_size_option(const char* section,
-                                         const char* name, const char* key,
-                                         const char* doc_string, int value,
-                                         int min, int max, int step)
+    static GncOption*
+    gnc_make_plot_size_option(const char* section, const char* name,
+                              const char* key, const char* doc_string,
+                              int value, int min, int max, int step)
     {
         try
         {
@@ -1126,19 +1130,19 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
-    GncOption* gnc_make_commodity_option(const char* section,
-                                        const char* name, const char* key,
-                                        const char* doc_string,
-                                        gnc_commodity *value)
+    static GncOption*
+    gnc_make_commodity_option(const char* section, const char* name,
+                              const char* key, const char* doc_string,
+                              gnc_commodity *value)
     {
         return new GncOption{GncOptionValue<const QofInstance*>{
                 section, name, key, doc_string, (const QofInstance*)value}};
     }
 
-    GncOption* gnc_make_commodity_option(const char* section,
-                                        const char* name, const char* key,
-                                        const char* doc_string,
-                                        const char *value)
+    static GncOption*
+    gnc_make_commodity_option(const char* section, const char* name,
+                              const char* key, const char* doc_string,
+                              const char *value)
     {
         gnc_commodity* commodity{};
         const auto book{qof_session_get_book(gnc_get_current_session())};
@@ -1156,10 +1160,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return nullptr;
     }
 
-    GncOption* gnc_make_currency_option(const char* section,
-                                        const char* name, const char* key,
-                                        const char* doc_string,
-                                        gnc_commodity *value)
+    static GncOption*
+    gnc_make_currency_option(const char* section, const char* name,
+                             const char* key, const char* doc_string,
+                             gnc_commodity *value)
     {
         try
         {
@@ -1254,7 +1258,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return optiondb->find_option(section, name);
     }
 
-    void
+    static void
     gnc_option_db_set_option_selectable_by_name(GncOptionDBPtr& odb,
                                                 const char* section,
                                                 const char* name,
@@ -1264,7 +1268,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         option->set_ui_item_selectable(selectable);
     }
 
-    void
+    static void
     gnc_optiondb_foreach(GncOptionDBPtr& odb, SCM thunk)
     {
         odb->foreach_section(
@@ -1281,7 +1285,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             });
     }
 
-    std::string
+    static std::string
     gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog)
     {
         std::ostringstream oss;

commit 18997db7202972be99d7d19ae7b367355219ef8e
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jul 31 17:43:48 2021 -0700

    Separate GncOptionAccountValue into GncOptionAccountListValue and GncOptionAccountSelValue.
    
    They have different get_value() return types so can't cohabit comfortably.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index ea4e52e23..b0d413251 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -37,7 +37,7 @@ const std::string GncOptionMultichoiceValue::c_empty_string{""};
 const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
 
 bool
-GncOptionAccountValue::validate(const GncOptionAccountList& values) const
+GncOptionAccountListValue::validate(const GncOptionAccountList& values) const
 {
     if (values.empty())
         return true;
@@ -54,6 +54,108 @@ GncOptionAccountValue::validate(const GncOptionAccountList& values) const
     return true;
 }
 
+GncOptionAccountList
+GncOptionAccountListValue::get_value() const
+{
+    return !m_value.empty() ? m_value : get_default_value();
+}
+
+GncOptionAccountList
+GncOptionAccountListValue::get_default_value() const
+{
+    if (!m_default_value.empty())
+        return m_default_value;
+
+    /* If no default has been set and there's an allowed set then find the first
+     * account that matches one of the allowed account types.
+     */
+    GncOptionAccountList retval{};
+    if (m_allowed.empty())
+        return retval;
+
+    auto root{gnc_get_current_root_account()};
+    auto account_list{gnc_account_get_descendants_sorted(root)};
+    if (!account_list)
+        return retval;
+
+    for (auto node = account_list; node; node = g_list_next (node))
+        if (std::find(m_allowed.begin(), m_allowed.end(),
+                      xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
+        {
+            retval.push_back(GNC_ACCOUNT(node->data));
+            break;
+        }
+    g_list_free(account_list);
+    return retval;
+}
+
+
+/**
+ * Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
+ * gnc_account_sel_set_acct_filters copies the list so the intermediary caller
+ * is responsible for freeing the list.
+ *
+ * @return an allocated GList* or nullptr if the list is empty.
+ */
+GList*
+GncOptionAccountListValue::account_type_list() const noexcept
+{
+    if (m_allowed.empty())
+        return nullptr;
+    GList* retval;
+    for (auto type : m_allowed)
+        retval = g_list_prepend(retval, GINT_TO_POINTER(type));
+    return g_list_reverse(retval);
+}
+
+bool
+GncOptionAccountSelValue::validate(const Account* value) const
+{
+    if (m_allowed.empty() || !value)
+        return true;
+    if (std::find(m_allowed.begin(), m_allowed.end(),
+                  xaccAccountGetType(value)) == m_allowed.end())
+            return false;
+    return true;
+}
+
+const Account*
+GncOptionAccountSelValue::get_value() const
+{
+    return m_value ? m_value : get_default_value();
+}
+
+const Account*
+GncOptionAccountSelValue::get_default_value() const
+{
+
+    if (m_default_value)
+        return m_default_value;
+
+    /* If no default has been set and there's an allowed set then find the first
+     * account that matches one of the allowed account types.
+     */
+    if (m_allowed.empty())
+        return nullptr;
+
+    const Account* retval{nullptr};
+    auto root{gnc_get_current_root_account()};
+    auto account_list{gnc_account_get_descendants_sorted(root)};
+    if (!account_list)
+        return nullptr;
+
+    for (auto node = account_list; node; node = g_list_next (node))
+        if (std::find(m_allowed.begin(), m_allowed.end(),
+                      xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
+        {
+            retval = GNC_ACCOUNT(node->data);
+            break;
+        }
+    g_list_free(account_list);
+    return retval;
+}
+
+
 /**
  * Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
  * gnc_account_sel_set_acct_filters copies the list so the intermediary caller
@@ -62,7 +164,7 @@ GncOptionAccountValue::validate(const GncOptionAccountList& values) const
  * @return an allocated GList* or nullptr if the list is empty.
  */
 GList*
-GncOptionAccountValue::account_type_list() const noexcept
+GncOptionAccountSelValue::account_type_list() const noexcept
 {
     if (m_allowed.empty())
         return nullptr;
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 485b1fcce..482501fd9 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -908,30 +908,30 @@ using GncOptionAccountTypeList = std::vector<GNCAccountType>;
 
  */
 
-class GncOptionAccountValue : public OptionClassifier
+class GncOptionAccountListValue : public OptionClassifier
 {
 public:
-    GncOptionAccountValue(const char* section, const char* name,
+    GncOptionAccountListValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type, bool multi=true) :
         OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
         m_value{}, m_default_value{}, m_allowed{}, m_multiselect{multi} {}
 
-    GncOptionAccountValue(const char* section, const char* name,
+    GncOptionAccountListValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           const GncOptionAccountList& value, bool multi=true) :
         OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
         m_value{value}, m_default_value{std::move(value)}, m_allowed{},
         m_multiselect{multi}  {}
-    GncOptionAccountValue(const char* section, const char* name,
+    GncOptionAccountListValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           GncOptionAccountTypeList&& allowed, bool multi=true) :
         OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
         m_value{}, m_default_value{}, m_allowed{std::move(allowed)},
         m_multiselect{multi} {}
-    GncOptionAccountValue(const char* section, const char* name,
+    GncOptionAccountListValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           const GncOptionAccountList& value,
@@ -945,8 +945,8 @@ public:
             m_default_value = std::move(value);
         }
 
-    const GncOptionAccountList& get_value() const { return m_value; }
-    const GncOptionAccountList& get_default_value() const { return m_default_value; }
+    GncOptionAccountList get_value() const;
+    GncOptionAccountList get_default_value() const;
     bool validate (const GncOptionAccountList& values) const;
     void set_value (const GncOptionAccountList& values) {
         if (validate(values))
@@ -973,8 +973,8 @@ private:
 };
 
 template<> inline std::ostream&
-operator<< <GncOptionAccountValue>(std::ostream& oss,
-                                       const GncOptionAccountValue& opt)
+operator<< <GncOptionAccountListValue>(std::ostream& oss,
+                                       const GncOptionAccountListValue& opt)
 {
     auto values{opt.get_value()};
     bool first = true;
@@ -990,8 +990,8 @@ operator<< <GncOptionAccountValue>(std::ostream& oss,
 }
 
 template<> inline std::istream&
-operator>> <GncOptionAccountValue>(std::istream& iss,
-                                   GncOptionAccountValue& opt)
+operator>> <GncOptionAccountListValue>(std::istream& iss,
+                                   GncOptionAccountListValue& opt)
 {
     GncOptionAccountList values;
     while (true)
@@ -1010,7 +1010,7 @@ operator>> <GncOptionAccountValue>(std::istream& iss,
 
 template<class OptType,
          typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionAccountValue>,
+                                                  GncOptionAccountListValue>,
                                    int> = 0>
 inline std::ostream&
 gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
@@ -1032,7 +1032,7 @@ gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
 
 template<class OptType,
          typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
-                                                  GncOptionAccountValue>,
+                                                  GncOptionAccountListValue>,
                                    int> = 0>
 inline std::istream&
 gnc_option_from_scheme(std::istream& iss, OptType& opt)
@@ -1056,6 +1056,129 @@ gnc_option_from_scheme(std::istream& iss, OptType& opt)
     return iss;
 }
 
+class GncOptionAccountSelValue : public OptionClassifier
+{
+public:
+    GncOptionAccountSelValue(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          GncOptionUIType ui_type) :
+        OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
+        m_value{}, m_default_value{}, m_allowed{} {}
+
+    GncOptionAccountSelValue(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          GncOptionUIType ui_type,
+                          const Account* value) :
+        OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
+        m_value{const_cast<Account*>(value)},
+        m_default_value{const_cast<Account*>(value)}, m_allowed{} {}
+    GncOptionAccountSelValue(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          GncOptionUIType ui_type,
+                          GncOptionAccountTypeList&& allowed) :
+        OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
+        m_value{}, m_default_value{}, m_allowed{std::move(allowed)} {}
+    GncOptionAccountSelValue(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          GncOptionUIType ui_type,
+                          const Account* value,
+                          GncOptionAccountTypeList&& allowed) :
+        OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
+        m_value{}, m_default_value{}, m_allowed{std::move(allowed)} {
+            if (!validate(value))
+                throw std::invalid_argument("Supplied Value not in allowed set.");
+            m_value = const_cast<Account*>(value);
+            m_default_value = const_cast<Account*>(value);
+        }
+
+    const Account* get_value() const;
+    const Account* get_default_value() const;
+    bool validate (const Account* value) const;
+    void set_value (const Account* value) {
+        if (validate(value))
+            //throw!
+            m_value = const_cast<Account*>(value);
+    }
+    void set_default_value (const Account* value) {
+        if (validate(value))
+            //throw!
+            m_value = m_default_value = const_cast<Account*>(value);
+    }
+    GList* account_type_list() const noexcept;
+    void reset_default_value() { m_value = m_default_value; }
+    bool is_changed() const noexcept { return m_value != m_default_value; }
+    GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
+    void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+private:
+    GncOptionUIType m_ui_type;
+    Account* m_value;
+    Account* m_default_value;
+    GncOptionAccountTypeList m_allowed;
+};
+
+template<> inline std::ostream&
+operator<< <GncOptionAccountSelValue>(std::ostream& oss,
+                                       const GncOptionAccountSelValue& opt)
+{
+    auto value{opt.get_value()};
+    oss << qof_instance_to_string(QOF_INSTANCE(value));
+    return oss;
+}
+
+template<> inline std::istream&
+operator>> <GncOptionAccountSelValue>(std::istream& iss,
+                                   GncOptionAccountSelValue& opt)
+{
+    const Account* value;
+    std::string str;
+    std::getline(iss, str, ' ');
+    if (!str.empty())
+        value = (Account*)qof_instance_from_string(str, opt.get_ui_type());
+    opt.set_value(value);
+    iss.clear();
+    return iss;
+}
+
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionAccountSelValue>,
+                                   int> = 0>
+inline std::ostream&
+gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
+{
+    auto value{opt.get_value()};
+    oss << "'(\"";
+        oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"';
+    oss << ')';
+    return oss;
+}
+
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionAccountSelValue>,
+                                   int> = 0>
+inline std::istream&
+gnc_option_from_scheme(std::istream& iss, OptType& opt)
+{
+    const Account* value;
+    iss.ignore(3, '"');
+    while (true)
+    {
+        std::string str;
+        std::getline(iss, str, '"');
+        if (!str.empty())
+        {
+            value = (Account*)qof_instance_from_string(str, opt.get_ui_type());
+            iss.ignore(2, '"');
+        }
+        else
+            break;
+    }
+    opt.set_value(value);
+    iss.ignore(1, ')');
+    return iss;
+}
+
 /** Date options
  * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
  * flag and a time64, or for legacy purposes the absolute flag and a timespec.
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 812a2c1b7..cebd526ed 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -279,7 +279,7 @@ GncOption::is_multiselect() const noexcept
 {
     return std::visit([](const auto& option)->bool {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionAccountValue>)
+                                        GncOptionAccountListValue>)
                               return option.is_multiselect();
                           else
                               return false;
@@ -367,7 +367,7 @@ GncOption::account_type_list() const noexcept
 {
     return std::visit([] (const auto& option) -> GList* {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionAccountValue>)
+                                        GncOptionAccountListValue>)
                               return option.account_type_list();
                           else
                               return nullptr;
@@ -423,7 +423,7 @@ GncOption::to_scheme(std::ostream& oss) const
     return std::visit([&oss](auto& option) ->std::ostream& {
                           if constexpr
                               ((std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionAccountValue>) ||
+                                GncOptionAccountListValue>) ||
                                (std::is_same_v<std::decay_t<decltype(option)>,
                                 GncOptionMultichoiceValue>) ||
                                std::is_same_v<std::decay_t<decltype(option)>,
@@ -464,7 +464,7 @@ GncOption::from_scheme(std::istream& iss)
     return std::visit([&iss](auto& option) -> std::istream& {
                           if constexpr
                               ((std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionAccountValue>) ||
+                                GncOptionAccountListValue>) ||
                                (std::is_same_v<std::decay_t<decltype(option)>,
                                 GncOptionMultichoiceValue>) ||
                                std::is_same_v<std::decay_t<decltype(option)>,
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 66e2ee25f..158fdea5a 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -49,7 +49,8 @@ using QofQuery = _QofQuery;
 struct QofInstance_s;
 using QofInstance = QofInstance_s;
 template <typename ValueType> class GncOptionValue;
-class GncOptionAccountValue;
+class GncOptionAccountListValue;
+class GncOptionAccountSelValue;
 class GncOptionMultichoiceValue;
 template <typename ValueType> class GncOptionRangeValue;
 template <typename ValueType> class GncOptionValidatedValue;
@@ -62,7 +63,8 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<const QofQuery*>,
                                       GncOptionValue<const GncOwner*>,
                                       GncOptionValue<SCM>,
-                                      GncOptionAccountValue,
+                                      GncOptionAccountListValue,
+                                      GncOptionAccountSelValue,
                                       GncOptionMultichoiceValue,
                                       GncOptionRangeValue<int>,
                                       GncOptionRangeValue<double>,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 3c0c1c79d..ed7603c28 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -923,7 +923,7 @@ gnc_register_account_list_option(GncOptionDB* db, const char* section,
                                  const char* doc_string,
                                  const GncOptionAccountList& value)
 {
-    GncOption option{GncOptionAccountValue{section, name, key, doc_string,
+    GncOption option{GncOptionAccountListValue{section, name, key, doc_string,
                 GncOptionUIType::ACCOUNT_LIST, value}};
     db->register_option(section, std::move(option));
 }
@@ -938,7 +938,7 @@ gnc_register_account_list_limited_option(GncOptionDB* db,
 {
     try
     {
-        GncOption option{GncOptionAccountValue{section, name, key, doc_string,
+        GncOption option{GncOptionAccountListValue{section, name, key, doc_string,
                     GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}};
         db->register_option(section, std::move(option));
     }
@@ -979,12 +979,12 @@ void
 gnc_register_account_sel_limited_option(GncOptionDB* db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
-                                        const GncOptionAccountList& value,
+                                        const Account* value,
                                         GncOptionAccountTypeList&& allowed)
 {
     try
     {
-        GncOption option{GncOptionAccountValue{section, name, key, doc_string,
+        GncOption option{GncOptionAccountSelValue{section, name, key, doc_string,
                     GncOptionUIType::ACCOUNT_SEL, value, std::move(allowed)}};
     db->register_option(section, std::move(option));
     }
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index e445f0837..002efa9e3 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -351,7 +351,7 @@ void gnc_register_account_sel_limited_option(GncOptionDB* db,
                                              const char* section,
                                              const char* name, const char* key,
                                              const char* doc_string,
-                                             const GncOptionAccountList& value,
+                                             const Account* value,
                                              GncOptionAccountTypeList&& allowed);
 
 /**
@@ -361,7 +361,7 @@ inline void gnc_register_account_sel_limited_option(GncOptionDBPtr& db,
                                              const char* section,
                                              const char* name, const char* key,
                                              const char* doc_string,
-                                             const GncOptionAccountList& value,
+                                             const Account* value,
                                              GncOptionAccountTypeList&& allowed)
 {
     gnc_register_account_sel_limited_option(db.get(), section, name, key,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index bf991c481..31b5acdda 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -206,6 +206,12 @@ scm_from_value<QofInstance*>(QofInstance* value)
     return scm_from_value<const QofInstance*>(value);
 }
 
+template <> inline SCM
+scm_from_value<const Account*>(const Account* value)
+{
+    return scm_from_value<const QofInstance*>(QOF_INSTANCE(value));
+}
+
 template <> inline SCM
 scm_from_value<const QofQuery*>(const QofQuery* value)
 {
@@ -299,6 +305,12 @@ scm_to_value<const QofInstance*>(SCM new_value)
     return static_cast<const QofInstance*>(ptr);
 }
 
+template <> inline const Account*
+scm_to_value<const Account*>(SCM new_value)
+{
+    return GNC_ACCOUNT(scm_to_value<const QofInstance*>(new_value));
+}
+
 template <> inline const QofQuery*
 scm_to_value<const QofQuery*>(SCM new_value)
 {
@@ -580,7 +592,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %ignore gnc_register_pixmap_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
 %ignore gnc_register_account_list_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&, GncOptionAccountTypeList&&);
 %ignore gnc_register_account_list_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&);
-%ignore gnc_register_account_sel_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&, GncOptionAccountTypeList&&);
+%ignore gnc_register_account_sel_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const Account*, GncOptionAccountTypeList&&);
 %ignore gnc_register_multichoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&);
 %ignore gnc_register_list_option(GncOptionDB*, const char*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&);
 %ignore gnc_register_number_Plot_size_option(GncOptionDB*, const char*, const char*, const char*, const char*, int);
@@ -939,7 +951,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                                             const GncOptionAccountList& value)
     {
         try {
-            return new GncOption{GncOptionAccountValue{section, name, key,
+            return new GncOption{GncOptionAccountListValue{section, name, key,
                         doc_string, GncOptionUIType::ACCOUNT_LIST, value}};
         }
         catch (const std::exception& err)
@@ -958,7 +970,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         try
         {
-            return new GncOption{GncOptionAccountValue{section, name, key,
+            return new GncOption{GncOptionAccountListValue{section, name, key,
                         doc_string, GncOptionUIType::ACCOUNT_LIST, value,
                         std::move(allowed)}};
         }
@@ -973,12 +985,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                                                    const char* name,
                                                    const char* key,
                                                    const char* doc_string,
-                                                   const GncOptionAccountList& value,
+                                                   const Account* value,
                                                    GncOptionAccountTypeList&& allowed)
     {
         try
         {
-            return new GncOption{GncOptionAccountValue{section, name, key,
+            return new GncOption{GncOptionAccountSelValue{section, name, key,
                         doc_string, GncOptionUIType::ACCOUNT_SEL, value,
                         std::move(allowed)}};
         }
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 214458dec..0e84c17f2 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -184,10 +184,10 @@
   (gnc-make-account-list-limited-option section name key docstring (default) permitted))
 (define-public (gnc:make-account-sel-limited-option section name key docstring default validator permitted)
   (issue-deprecation-warning "gnc:make-account-sel-limited-option is deprecated. Make and register the option in one command with gnc-register-account-sel-limited-option.")
-  (let ((defval (if default (default) #f)))
+  (let ((defval (if default (default) '())))
     (gnc-make-account-sel-limited-option section name key docstring defval permitted)))
 (define-public (gnc:make-account-sel-option section name key docstring default validator)
-  (let ((defval (if default (default) #f)))
+  (let ((defval (if default (default) '())))
   (gnc-make-account-sel-limited-option section name key docstring defval '())))
 (define-public (gnc:make-multichoice-option section name key docstring default multichoice)
   (issue-deprecation-warning "gnc:make-multichoice-option is deprecated. Make and register the option in one command with gnc-register-multichoice-option.")
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 379617e13..2edc94af5 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -736,7 +736,7 @@ TEST_F(GncOptionAccountTest, test_test_constructor_and_destructor)
 
 TEST_F(GncOptionAccountTest, test_option_no_value_constructor)
 {
-    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
+    GncOptionAccountListValue option{"foo", "bar", "baz", "Bogus Option",
             GncOptionUIType::ACCOUNT_LIST};
     EXPECT_TRUE(option.get_value().empty());
     EXPECT_TRUE(option.get_default_value().empty());
@@ -745,7 +745,7 @@ TEST_F(GncOptionAccountTest, test_option_no_value_constructor)
 TEST_F(GncOptionAccountTest, test_option_value_constructor)
 {
     GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
+    GncOptionAccountListValue option{"foo", "bar", "baz", "Bogus Option",
             GncOptionUIType::ACCOUNT_LIST, acclist};
     EXPECT_EQ(2U, option.get_value().size());
     EXPECT_EQ(2U, option.get_default_value().size());
@@ -756,11 +756,11 @@ TEST_F(GncOptionAccountTest, test_option_no_value_limited_constructor)
 {
     GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
-    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
+    GncOptionAccountListValue option{"foo", "bar", "baz", "Bogus Option",
             GncOptionUIType::ACCOUNT_LIST,
             GncOptionAccountTypeList{ACCT_TYPE_BANK}};
-    EXPECT_TRUE(option.get_value().empty());
-    EXPECT_TRUE(option.get_default_value().empty());
+    EXPECT_EQ(1U, option.get_value().size());
+    EXPECT_EQ(1U, option.get_default_value().size());
     EXPECT_EQ(true, option.validate(acclistgood));
     EXPECT_EQ(false, option.validate(acclistbad));
 }
@@ -770,21 +770,21 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
     GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
     EXPECT_THROW({
-            GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
+            GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option",
                                          GncOptionUIType::ACCOUNT_LIST,
                                          acclistbad,
                                          GncOptionAccountTypeList{ACCT_TYPE_BANK});
         }, std::invalid_argument);
 
     EXPECT_THROW({
-            GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
+            GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option",
                                          GncOptionUIType::ACCOUNT_SEL,
                                          acclistgood,
                                          GncOptionAccountTypeList{ACCT_TYPE_BANK});
         }, std::invalid_argument);
 
     EXPECT_NO_THROW({
-            GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
+            GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option",
                                          GncOptionUIType::ACCOUNT_LIST,
                                          acclistgood,
                                          GncOptionAccountTypeList{ACCT_TYPE_BANK});
@@ -792,12 +792,12 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
 
     EXPECT_NO_THROW({
             GncOptionAccountList accsel{acclistgood[0]};
-            GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
+            GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option",
                                          GncOptionUIType::ACCOUNT_LIST,
                                          accsel,
                                          GncOptionAccountTypeList{ACCT_TYPE_BANK});
         });
-    GncOptionAccountValue option {"foo", "bar", "baz", "Bogus Option",
+    GncOptionAccountListValue option {"foo", "bar", "baz", "Bogus Option",
                                   GncOptionUIType::ACCOUNT_LIST, acclistgood,
                                   GncOptionAccountTypeList{ACCT_TYPE_BANK}};
     EXPECT_FALSE(option.get_value().empty());
@@ -809,7 +809,7 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
 TEST_F(GncOptionAccountTest, test_account_list_out)
 {
     GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option",
+    GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option",
                                            GncOptionUIType::ACCOUNT_LIST,
                                            acclist}};
     std::ostringstream oss;
@@ -821,7 +821,7 @@ TEST_F(GncOptionAccountTest, test_account_list_out)
     EXPECT_EQ(acc_guids, oss.str());
 
     GncOptionAccountList accsel{acclist[0]};
-    GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
+    GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz",
                                                "Bogus Option",
                                                GncOptionUIType::ACCOUNT_LIST,
                                                accsel,
@@ -836,7 +836,7 @@ TEST_F(GncOptionAccountTest, test_account_list_out)
 TEST_F(GncOptionAccountTest, test_account_list_in)
 {
     GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option",
+    GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option",
                                            GncOptionUIType::ACCOUNT_LIST,
                                            acclist}};
     std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
@@ -848,7 +848,7 @@ TEST_F(GncOptionAccountTest, test_account_list_in)
     EXPECT_EQ(acclist, option.get_value<GncOptionAccountList>());
 
     GncOptionAccountList accsel{acclist[0]};
-    GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
+    GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz",
                                                "Bogus Option",
                                                GncOptionUIType::ACCOUNT_LIST,
                                                accsel,
@@ -894,7 +894,7 @@ make_account_list_SCM_str(const GncOptionAccountList& acclist)
 TEST_F(GncOptionAccountTest, test_account_list_to_scheme)
 {
     GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOption option{GncOptionAccountValue {"foo", "bar", "baz", "Bogus Option",
+    GncOption option{GncOptionAccountListValue {"foo", "bar", "baz", "Bogus Option",
                                             GncOptionUIType::ACCOUNT_LIST,
                                             acclist}};
     std::ostringstream oss;
@@ -904,7 +904,7 @@ TEST_F(GncOptionAccountTest, test_account_list_to_scheme)
     EXPECT_EQ(acc_guids, oss.str());
 
     GncOptionAccountList accsel{acclist[0]};
-    GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
+    GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz",
                                                "Bogus Option",
                                                GncOptionUIType::ACCOUNT_LIST,
                                                accsel,
@@ -919,7 +919,7 @@ TEST_F(GncOptionAccountTest, test_account_list_to_scheme)
 TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
 {
     GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option",
+    GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option",
                                            GncOptionUIType::ACCOUNT_LIST,
                                            acclist}};
 
@@ -929,7 +929,7 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
     EXPECT_EQ(acclist, option.get_value<GncOptionAccountList>());
 
     GncOptionAccountList accsel{acclist[0]};
-    GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
+    GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz",
                                                "Bogus Option",
                                                GncOptionUIType::ACCOUNT_LIST,
                                                accsel,
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 38215ce20..ad8882463 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -135,9 +135,9 @@
                                (list ACCT-TYPE-STOCK))))
       (gnc-register-account-sel-limited-option
        option-db "salt" "pork" "baz"
-       "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK))
+       "Phony Option" (cadr acctlist) (list ACCT-TYPE-STOCK))
       (let ((acct (gnc-option-value option-db "salt" "pork")))
-        (test-equal (list (cadr acctlist)) acct)))))
+        (test-equal (cadr acctlist) acct)))))
 
   (let* ((book (gnc-option-test-book-new))
          (root-account (gnc-account-create-root book)))

commit dd8e8b4efaa04dfa7f6c085f568116686cba4ff6
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jul 30 13:20:48 2021 -0700

    Evaluate the getter for making invoice options.

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 1ade4370f..214458dec 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -250,7 +250,8 @@
     (gnc-make-owner-option section name key docstring defval ui-type)))
 (define-public (gnc:make-invoice-option section name key docstring getter validator)
   (issue-deprecation-warning "gnc:make-invoice-option is deprecated. Make and register the option in one command with gnc-register-ionvoice-option.")
-  (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-INVOICE)))
+  (let ((defval (if getter (getter) #f)))
+    (gnc-make-qofinstance-option section name key docstring defval (GncOptionUIType-INVOICE))))
 (define-public (gnc:make-taxtable-option section name key docstring default)
   (issue-deprecation-warning "gnc:make-taxtable-option is deprecated. Make and register the option in one command with gnc-register-taxtable-option.")
   (gnc-make-qofinstance-option section name key docstring default (GncOptionUIType-TAX_TABLE)))

commit 05ac05644976aa8ada12961d93ed8874da39fbe2
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jul 30 13:19:17 2021 -0700

    Make separate option type for GncOwner.
    
    GncOwner is not a QofInstance subclass.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 0f0c56664..ea4e52e23 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -326,6 +326,7 @@ template GncOptionValue<const char*>::GncOptionValue(const GncOptionValue<const
 template GncOptionValue<std::string>::GncOptionValue(const GncOptionValue<std::string>&);
 template GncOptionValue<const QofInstance*>::GncOptionValue(const GncOptionValue<const QofInstance*>&);
 template GncOptionValue<const QofQuery*>::GncOptionValue(const GncOptionValue<const QofQuery*>&);
+template GncOptionValue<const GncOwner*>::GncOptionValue(const GncOptionValue<const GncOwner*>&);
 template GncOptionValue<RelativeDatePeriod>::GncOptionValue(const GncOptionValue<RelativeDatePeriod>&);
 template GncOptionValue<size_t>::GncOptionValue(const GncOptionValue<size_t>&);
 template GncOptionValue<GncOptionAccountList>::GncOptionValue(const GncOptionValue<GncOptionAccountList>&);
@@ -340,6 +341,7 @@ template void GncOptionValue<const char*>::set_value(const char*);
 template void GncOptionValue<std::string>::set_value(std::string);
 template void GncOptionValue<const QofInstance*>::set_value(const QofInstance*);
 template void GncOptionValue<const QofQuery*>::set_value(const QofQuery*);
+template void GncOptionValue<const GncOwner*>::set_value(const GncOwner*);
 template void GncOptionValue<RelativeDatePeriod>::set_value(RelativeDatePeriod);
 template void GncOptionValue<size_t>::set_value(size_t);
 template void GncOptionValue<GncOptionAccountList>::set_value(GncOptionAccountList);
@@ -353,6 +355,7 @@ template void GncOptionValue<const char*>::set_default_value(const char*);
 template void GncOptionValue<std::string>::set_default_value(std::string);
 template void GncOptionValue<const QofInstance*>::set_default_value(const QofInstance*);
 template void GncOptionValue<const QofQuery*>::set_default_value(const QofQuery*);
+template void GncOptionValue<const GncOwner*>::set_default_value(const GncOwner*);
 template void GncOptionValue<RelativeDatePeriod>::set_default_value(RelativeDatePeriod);
 template void GncOptionValue<size_t>::set_default_value(size_t);
 template void GncOptionValue<GncOptionAccountList>::set_default_value(GncOptionAccountList);
@@ -366,6 +369,7 @@ template void GncOptionValue<const char*>::reset_default_value();
 template void GncOptionValue<std::string>::reset_default_value();
 template void GncOptionValue<const QofInstance*>::reset_default_value();
 template void GncOptionValue<const QofQuery*>::reset_default_value();
+template void GncOptionValue<const GncOwner*>::reset_default_value();
 template void GncOptionValue<RelativeDatePeriod>::reset_default_value();
 template void GncOptionValue<size_t>::reset_default_value();
 template void GncOptionValue<GncOptionAccountList>::reset_default_value();
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 20d33f4c6..485b1fcce 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -309,6 +309,7 @@ std::istream& operator>>(std::istream& iss, OptType& opt)
 {
     std::decay_t<decltype(opt.get_value())> value;
     if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, SCM> ||
+                  std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _gncOwner*> ||
                   std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _QofQuery*>)
         return iss;
     else
@@ -1198,6 +1199,20 @@ operator>> <GncOptionDateValue>(std::istream& iss,
 /** QofQuery Options
  */
 
+inline std::istream&
+gnc_option_from_scheme(std::istream& iss, GncOptionValue<const GncOwner*>& opt)
+{
+//FIXME: Implement or maybe rethink.
+    return iss;
+}
+
+inline std::ostream&
+gnc_option_to_scheme(std::ostream& oss, GncOptionValue<const GncOwner*>& opt)
+{
+//FIXME: Implement or maybe rethink.
+    return oss;
+}
+
 inline std::istream&
 gnc_option_from_scheme(std::istream& iss, GncOptionValue<const QofQuery*>& opt)
 {
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 1153e0c94..812a2c1b7 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -468,6 +468,8 @@ GncOption::from_scheme(std::istream& iss)
                                (std::is_same_v<std::decay_t<decltype(option)>,
                                 GncOptionMultichoiceValue>) ||
                                std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionValue<const GncOwner*>> ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionValue<const QofQuery*>> ||
                                std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionValue<const QofInstance*>> ||
@@ -564,6 +566,8 @@ template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, SCM, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, const QofQuery*, GncOptionUIType);
+template GncOption::GncOption(const char*, const char*, const char*,
+                              const char*, const GncOwner*, GncOptionUIType);
 
 template bool GncOption::get_value<bool>() const;
 template int GncOption::get_value<int>() const;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index dfc875bef..66e2ee25f 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -40,6 +40,10 @@ extern "C"
 struct OptionClassifier;
 class GncOptionUIItem;
 using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
+#ifndef SWIG //SWIG pulls in GncOwner from swig-engine.
+struct _gncOwner;
+using GncOwner = _gncOwner;
+#endif
 struct _QofQuery;
 using QofQuery = _QofQuery;
 struct QofInstance_s;
@@ -56,6 +60,7 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<int64_t>,
                                       GncOptionValue<const QofInstance*>,
                                       GncOptionValue<const QofQuery*>,
+                                      GncOptionValue<const GncOwner*>,
                                       GncOptionValue<SCM>,
                                       GncOptionAccountValue,
                                       GncOptionMultichoiceValue,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index abb80efed..3c0c1c79d 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1061,9 +1061,19 @@ gnc_register_number_plot_size_option(GncOptionDB* db,
 void
 gnc_register_query_option(GncOptionDB* db, const char* section,
                           const char* name, const char* key,
-                          const char* doc_string, QofQuery* value)
+                          const char* doc_string, const QofQuery* value)
 {
-    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::INTERNAL};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_owner_option(GncOptionDB* db, const char* section,
+                          const char* name, const char* key,
+                          const char* doc_string, const GncOwner* value)
+{
+    GncOption option{section, name, key, doc_string, value,
             GncOptionUIType::INTERNAL};
     db->register_option(section, std::move(option));
 }
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 92e04cf5e..e445f0837 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -501,18 +501,43 @@ inline void gnc_register_number_plot_size_option(const GncOptionDBPtr& db,
  */
 void gnc_register_query_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
-                               const char* doc_string, QofQuery* value);
+                               const char* doc_string, const QofQuery* value);
 
 /**
  * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
  */
 inline void gnc_register_query_option(GncOptionDBPtr& db, const char* section,
                                       const char* name, const char* key,
-                                      const char* doc_string, QofQuery* value)
+                                      const char* doc_string,
+                                      const QofQuery* value)
 {
     gnc_register_query_option(db.get(), section, name, key, doc_string, value);
 }
 
+/**
+ * Create a new GncOwner option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
+void gnc_register_owner_option(GncOptionDB* db, const char* section,
+                               const char* name, const char* key,
+                               const char* doc_string, const GncOwner* value);
+
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_owner_option(GncOptionDBPtr& db, const char* section,
+                                      const char* name, const char* key,
+                                      const char* doc_string,
+                                      const GncOwner* value)
+{
+    gnc_register_owner_option(db.get(), section, name, key, doc_string, value);
+}
+
 /**
  * Create a new color option and register it in the options database.
  *
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 9fbfa9083..bf991c481 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -53,6 +53,7 @@ SCM scm_init_sw_gnc_optiondb_module(void);
 %include <std_string.i>
 %import <base-typemaps.i>
 %import (module="sw_engine") <gnc-budget.h>
+%import (module="sw_engine") <gncOwner.h>
 %import (module="sw_engine") <gncCustomer.h>
 %import (module="sw_engine") <gncEmployee.h>
 %import (module="sw_engine") <gncVendor.h>
@@ -195,13 +196,16 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
         type = SWIGTYPE_p__gncJob;
     else if (GNC_IS_VENDOR(value))
         type = SWIGTYPE_p__gncVendor;
-/* There is no type macro for QofQuery, it's not a GObject.
-    else if (GNC_IS_QOFQUERY(value))
-        type = SWIGTYPE_p_Query;
-*/
+
     return SWIG_NewPointerObj(ptr, type, FALSE);
 }
 
+template <> inline SCM
+scm_from_value<QofInstance*>(QofInstance* value)
+{
+    return scm_from_value<const QofInstance*>(value);
+}
+
 template <> inline SCM
 scm_from_value<const QofQuery*>(const QofQuery* value)
 {
@@ -216,9 +220,16 @@ scm_from_value<QofQuery*>(QofQuery* value)
 }
 
 template <> inline SCM
-scm_from_value<QofInstance*>(QofInstance* value)
+scm_from_value<const GncOwner*>(const GncOwner* value)
 {
-    return scm_from_value<const QofInstance*>(value);
+    auto ptr{static_cast<void*>(const_cast<GncOwner*>(value))};
+    return SWIG_NewPointerObj(ptr, SWIGTYPE_p__gncOwner, FALSE);
+}
+
+template <> inline SCM
+scm_from_value<GncOwner*>(GncOwner* value)
+{
+    return scm_from_value<const GncOwner*>(value);
 }
 
 template <typename ValueType> inline ValueType
@@ -267,16 +278,16 @@ scm_to_value<const QofInstance*>(SCM new_value)
 {
     if (new_value == SCM_BOOL_F)
         return nullptr;
-    
+
     auto info = SWIG_PointerType(new_value);
 
-    static const std::array<swig_type_info*, 10> types{
-            SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
-            SWIGTYPE_p_budget_s, SWIGTYPE_p__gncInvoice,
-            SWIGTYPE_p__gncTaxTable, SWIGTYPE_p_Account,
-            SWIGTYPE_p__gncCustomer, SWIGTYPE_p__gncEmployee,
-            SWIGTYPE_p__gncJob, SWIGTYPE_p__gncVendor
-                };
+    static const std::array<swig_type_info*, 11> types{
+        SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
+        SWIGTYPE_p_budget_s, SWIGTYPE_p__gncInvoice,
+        SWIGTYPE_p__gncTaxTable, SWIGTYPE_p_Account,
+        SWIGTYPE_p__gncCustomer, SWIGTYPE_p__gncEmployee,
+        SWIGTYPE_p__gncJob, SWIGTYPE_p__gncVendor
+            };
     void* ptr{};
     auto pos = std::find_if(types.begin(), types.end(),
                             [&new_value, &ptr](auto type){
@@ -298,6 +309,16 @@ scm_to_value<const QofQuery*>(SCM new_value)
     return static_cast<const QofQuery*>(ptr);
 }
 
+template <> inline const GncOwner*
+scm_to_value<const GncOwner*>(SCM new_value)
+{
+    if (new_value == SCM_BOOL_F)
+        return nullptr;
+    void* ptr{};
+    SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p__gncOwner, 0);
+    return static_cast<const GncOwner*>(ptr);
+}
+
 template <>inline GncOptionAccountList
 scm_to_value<GncOptionAccountList>(SCM new_value)
 {
@@ -776,6 +797,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %template(gnc_make_int64_option) gnc_make_option<int64_t>;
 %template(gnc_make_qofinstance_option) gnc_make_option<const QofInstance*>;
 %template(gnc_make_query_option) gnc_make_option<const QofQuery*>;
+%template(gnc_make_owner_option) gnc_make_option<const GncOwner*>;
 
 %extend GncOption {
     SCM get_scm_value()
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 97905c5db..1ade4370f 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -232,15 +232,22 @@
     (gnc-make-SCM-option section name key desc default type)))
 (define-public (gnc:make-owner-option section name key docstring getter validator owner-type)
   (issue-deprecation-warning "gnc:make-owner-option is deprecated. Make and register the option in one command with gnc-register-owner-option.")
-  (let ((ui-type (cond
+  (let* ((ui-type (cond
                   ((eqv? owner-type GNC-OWNER-CUSTOMER) (GncOptionUIType-CUSTOMER))
                   ((eqv? owner-type GNC-OWNER-VENDOR) (GncOptionUIType-VENDOR))
                   ((eqv? owner-type GNC-OWNER-EMPLOYEE) (GncOptionUIType-EMPLOYEE))
                   ((eqv? owner-type GNC-OWNER-JOB) (GncOptionUIType-JOB))
                   (else (GncOptionUIType-INTERNAL))))
-        (defval (if getter (getter) #f)))
-    (format #t "Making owner option ~a:~a ~a ~a~%"  section name defval ui-type)(force-output)
-    (gnc-make-qofinstance-option section name key docstring defval ui-type)))
+
+         (guid (gncOwnerReturnGUID (getter)))
+         (book (gnc-get-current-book))
+         (defval (cond
+                  ((eqv? owner-type GNC-OWNER-CUSTOMER) (gncCustomerLookupFlip guid book))
+                  ((eqv? owner-type GNC-OWNER-VENDOR) (gncVendorLookupFlip guid book))
+                  ((eqv? owner-type GNC-OWNER-EMPLOYEE) (gncEmployeeLookupFlip guid book))
+                  ((eqv? owner-type GNC-OWNER-JOB) (gncJobLookupFlip guid book)))))
+
+    (gnc-make-owner-option section name key docstring defval ui-type)))
 (define-public (gnc:make-invoice-option section name key docstring getter validator)
   (issue-deprecation-warning "gnc:make-invoice-option is deprecated. Make and register the option in one command with gnc-register-ionvoice-option.")
   (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-INVOICE)))

commit 9d150b1ae70d2ad51f22054b7a681a4d67adc3e1
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jul 19 11:30:13 2021 -0700

    Always set budget option to default budget.

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 4e26f0dc5..97905c5db 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -159,7 +159,10 @@
   (gnc-make-string-option section name key docstring color-str (GncOptionUIType-COLOR))))
 (define-public (gnc:make-budget-option section name key docstring)
   (issue-deprecation-warning "gnc:make-budget-option is deprecated. Make and register the option in one command with gnc-register-color-option.")
-  (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-BUDGET)))
+  (let ((option (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-BUDGET))))
+    (gnc:option-set-value option
+                          (gnc-budget-get-default (gnc-get-current-book)))
+    option))
 (define-public (gnc:make-commodity-option section name key docstring default)
   (issue-deprecation-warning "gnc:make-commodity-option is deprecated. Make and register the option in one command with gnc-register-commodity-option.")
   (gnc-make-commodity-option section name key docstring default))

commit f5d8d508eec31bf1fe900a967551d36edd383953
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jul 19 10:42:24 2021 -0700

    Create the GncOptionVariant's GncOptionValue in place.
    
    Saves a temporary, prevents premature unprotecting of Scheme objects.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 8aa240622..1153e0c94 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -40,8 +40,9 @@ template <typename ValueType,
 GncOption::GncOption(const char* section, const char* name,
                      const char* key, const char* doc_string,
                      ValueType value, GncOptionUIType ui_type) :
-    m_option{std::make_unique<GncOptionVariant>(GncOptionValue<ValueType> {
-            section, name, key, doc_string, value, ui_type})}
+    m_option{std::make_unique<GncOptionVariant>(
+            std::in_place_type<GncOptionValue<ValueType>>,
+            section, name, key, doc_string, value, ui_type)}
 {
 }
 

commit c034a26ae75f25e9fc78e764a250da6d5c5c69e4
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jul 19 10:40:48 2021 -0700

    Protect stored scheme option values from the garbage collector.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index b12ca23aa..0f0c56664 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -268,3 +268,105 @@ qof_instance_to_string(const QofInstance* inst)
     gnc::GUID guid{*qof_instance_get_guid(inst)};
     return guid.to_string();
 }
+
+template <typename ValueType> void
+GncOptionValue<ValueType>::set_value(ValueType new_value)
+{
+    m_value = new_value;
+}
+
+template <typename ValueType> void
+GncOptionValue<ValueType>::set_default_value(ValueType new_value)
+{
+    m_value = m_default_value = new_value;
+}
+
+template <typename ValueType> void
+GncOptionValue<ValueType>::reset_default_value()
+{
+    m_value = m_default_value;
+}
+
+template <> void
+GncOptionValue<SCM>::set_value(SCM new_value)
+{
+    if (m_value)
+        scm_gc_unprotect_object(m_value);
+    m_value = new_value;
+    scm_gc_protect_object(m_value);
+}
+
+template <> void
+GncOptionValue<SCM>::set_default_value(SCM new_value)
+{
+    if (m_value)
+        scm_gc_unprotect_object(m_value);
+    if (m_default_value)
+        scm_gc_unprotect_object(m_default_value);
+    m_value = m_default_value = new_value;
+    scm_gc_protect_object(m_value);
+    scm_gc_protect_object(m_default_value);
+}
+
+template <> void
+GncOptionValue<SCM>::reset_default_value()
+{
+    if (m_value)
+        scm_gc_unprotect_object(m_value);
+    m_value = m_default_value;
+    scm_gc_protect_object(m_value);
+}
+
+template GncOptionValue<bool>::GncOptionValue(const GncOptionValue<bool>&);
+template GncOptionValue<int>::GncOptionValue(const GncOptionValue<int>&);
+template GncOptionValue<int64_t>::GncOptionValue(const GncOptionValue<int64_t>&);
+template GncOptionValue<double>::GncOptionValue(const GncOptionValue<double>&);
+template GncOptionValue<char*>::GncOptionValue(const GncOptionValue<char*>&);
+template GncOptionValue<const char*>::GncOptionValue(const GncOptionValue<const char*>&);
+template GncOptionValue<std::string>::GncOptionValue(const GncOptionValue<std::string>&);
+template GncOptionValue<const QofInstance*>::GncOptionValue(const GncOptionValue<const QofInstance*>&);
+template GncOptionValue<const QofQuery*>::GncOptionValue(const GncOptionValue<const QofQuery*>&);
+template GncOptionValue<RelativeDatePeriod>::GncOptionValue(const GncOptionValue<RelativeDatePeriod>&);
+template GncOptionValue<size_t>::GncOptionValue(const GncOptionValue<size_t>&);
+template GncOptionValue<GncOptionAccountList>::GncOptionValue(const GncOptionValue<GncOptionAccountList>&);
+template GncOptionValue<GncMultichoiceOptionIndexVec>::GncOptionValue(const GncOptionValue<GncMultichoiceOptionIndexVec>&);
+template GncOptionValue<SCM>::GncOptionValue(const GncOptionValue<SCM>&);
+template void GncOptionValue<bool>::set_value(bool);
+template void GncOptionValue<int>::set_value(int);
+template void GncOptionValue<int64_t>::set_value(int64_t);
+template void GncOptionValue<double>::set_value(double);
+template void GncOptionValue<char*>::set_value(char*);
+template void GncOptionValue<const char*>::set_value(const char*);
+template void GncOptionValue<std::string>::set_value(std::string);
+template void GncOptionValue<const QofInstance*>::set_value(const QofInstance*);
+template void GncOptionValue<const QofQuery*>::set_value(const QofQuery*);
+template void GncOptionValue<RelativeDatePeriod>::set_value(RelativeDatePeriod);
+template void GncOptionValue<size_t>::set_value(size_t);
+template void GncOptionValue<GncOptionAccountList>::set_value(GncOptionAccountList);
+template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_value(GncMultichoiceOptionIndexVec);
+template void GncOptionValue<bool>::set_default_value(bool);
+template void GncOptionValue<int>::set_default_value(int);
+template void GncOptionValue<int64_t>::set_default_value(int64_t);
+template void GncOptionValue<double>::set_default_value(double);
+template void GncOptionValue<char*>::set_default_value(char*);
+template void GncOptionValue<const char*>::set_default_value(const char*);
+template void GncOptionValue<std::string>::set_default_value(std::string);
+template void GncOptionValue<const QofInstance*>::set_default_value(const QofInstance*);
+template void GncOptionValue<const QofQuery*>::set_default_value(const QofQuery*);
+template void GncOptionValue<RelativeDatePeriod>::set_default_value(RelativeDatePeriod);
+template void GncOptionValue<size_t>::set_default_value(size_t);
+template void GncOptionValue<GncOptionAccountList>::set_default_value(GncOptionAccountList);
+template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_default_value(GncMultichoiceOptionIndexVec);
+template void GncOptionValue<bool>::reset_default_value();
+template void GncOptionValue<int>::reset_default_value();
+template void GncOptionValue<int64_t>::reset_default_value();
+template void GncOptionValue<double>::reset_default_value();
+template void GncOptionValue<char*>::reset_default_value();
+template void GncOptionValue<const char*>::reset_default_value();
+template void GncOptionValue<std::string>::reset_default_value();
+template void GncOptionValue<const QofInstance*>::reset_default_value();
+template void GncOptionValue<const QofQuery*>::reset_default_value();
+template void GncOptionValue<RelativeDatePeriod>::reset_default_value();
+template void GncOptionValue<size_t>::reset_default_value();
+template void GncOptionValue<GncOptionAccountList>::reset_default_value();
+template void GncOptionValue<GncMultichoiceOptionIndexVec>::reset_default_value();
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 38c9c0b67..20d33f4c6 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -120,18 +120,43 @@ public:
                               ValueType value,
                               GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
         OptionClassifier{section, name, key, doc_string},
-        m_ui_type(ui_type), m_value{value}, m_default_value{value} {}
-    GncOptionValue<ValueType>(const GncOptionValue<ValueType>&) = default;
+        m_ui_type(ui_type), m_value{value}, m_default_value{value} {
+            if constexpr(std::is_same_v<std::decay_t<ValueType>, SCM>) {
+                if (value) {
+                    scm_gc_protect_object(m_value);
+                    scm_gc_protect_object(m_default_value);
+                }
+            }
+        }
+    GncOptionValue<ValueType>(const GncOptionValue<ValueType>& from) :
+        OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
+                         from.m_doc_string},
+        m_ui_type(from.get_ui_type()), m_value{from.get_value()},
+        m_default_value{from.get_default_value()}
+    {
+        if constexpr(std::is_same_v<std::decay_t<ValueType>, SCM>) {
+            if (m_value)
+                scm_gc_protect_object(m_value);
+            if (m_default_value)
+                scm_gc_protect_object(m_default_value);
+        }
+    }
     GncOptionValue<ValueType>(GncOptionValue<ValueType>&&) = default;
     GncOptionValue<ValueType>& operator=(const GncOptionValue<ValueType>&) = default;
     GncOptionValue<ValueType>& operator=(GncOptionValue<ValueType>&&) = default;
+    ~GncOptionValue<ValueType>() {
+            if constexpr(std::is_same_v<std::decay_t<ValueType>, SCM>) {
+                if (m_value)
+                    scm_gc_unprotect_object(m_value);
+                if (m_default_value)
+                    scm_gc_unprotect_object(m_default_value);
+            }
+        }
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
-    void set_value(ValueType new_value) { m_value = new_value; }
-    void set_default_value(ValueType new_value) {
-        m_value = m_default_value = new_value;
-    }
-    void reset_default_value() { m_value = m_default_value; }
+    void set_value(ValueType new_value);
+    void set_default_value(ValueType new_value);
+    void reset_default_value();
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }

commit 67dab6b320f8c93665b97bda9f9d00c5002c26bc
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Jul 18 14:13:29 2021 -0700

    Remove business-options.scm and business-prefs.scm.
    
    Their functions have been reimplemented elsewhere.

diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt
index 573c93db4..68379509b 100644
--- a/libgnucash/app-utils/CMakeLists.txt
+++ b/libgnucash/app-utils/CMakeLists.txt
@@ -42,10 +42,11 @@ set (app_utils_HEADERS
 
 # Command to generate the swig-app-utils-guile.c wrapper file
 set(SWIG_ARGS "-c++" "-procdoc" "sw-gnc-option-doc" "-procdocformat" "plain")
-gnc_add_swig_guile_command (swig-apputils-guile-cpp
-    SWIG_APP_UTILS_GUILE_CPP swig-app-utils-guile.cpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/app-utils.i
-    ${CMAKE_CURRENT_SOURCE_DIR}/gnc-optiondb.i ""
+gnc_add_swig_guile_command (swig-apputils-guile-cpp #target
+    SWIG_APP_UTILS_GUILE_CPP swig-app-utils-guile.cpp #outvar, output
+    ${CMAKE_CURRENT_SOURCE_DIR}/app-utils.i #input
+    "${CMAKE_SOURCE_DIR}/bindings;${CMAKE_SOURCE_DIR}/bindings/guile" #includes
+    ${CMAKE_CURRENT_SOURCE_DIR}/gnc-optiondb.i #additional dependencies
 )
 unset(SWIG_ARGS)
 
@@ -188,14 +189,6 @@ set (app_utils_SCHEME_1a
     options.scm
 )
 
-set (app_utils_SCHEME_1b
-    business-options.scm
-)
-
-set (app_utils_SCHEME_1c
-    business-prefs.scm
-)
-
 set (app_utils_SCHEME_2
     app-utils.scm
 )
@@ -217,22 +210,10 @@ gnc_add_scheme_targets(scm-app-utils-1a
     DEPENDS "scm-app-utils-1"
     MAKE_LINKS)
 
-gnc_add_scheme_targets(scm-app-utils-1b
-    SOURCES "${app_utils_SCHEME_1b}"
-    OUTPUT_DIR "gnucash/app-utils"
-    DEPENDS "scm-app-utils-1a"
-    MAKE_LINKS)
-
-gnc_add_scheme_targets(scm-bus-prefs
-    SOURCES "${app_utils_SCHEME_1c}"
-    OUTPUT_DIR "gnucash/app-utils"
-    DEPENDS "scm-app-utils-1b"
-    MAKE_LINKS)
-
 gnc_add_scheme_targets(scm-app-utils-2
     SOURCES "${app_utils_SCHEME_2}"
     OUTPUT_DIR "gnucash"
-    DEPENDS "scm-bus-prefs"
+    DEPENDS "scm-app-utils-1a"
     MAKE_LINKS)
 
 add_custom_target(scm-app-utils ALL DEPENDS scm-app-utils-2 scm-app-utils-1)
diff --git a/libgnucash/app-utils/app-utils.scm b/libgnucash/app-utils/app-utils.scm
index d0ed9635f..be5c5d9db 100644
--- a/libgnucash/app-utils/app-utils.scm
+++ b/libgnucash/app-utils/app-utils.scm
@@ -28,7 +28,6 @@
 
 (load-and-reexport (sw_app_utils)
                    (gnucash app-utils date-utilities)
-                   (gnucash app-utils business-options)
                    (gnucash app-utils options)
                    (gnucash app-utils c-interface))
 ;; gw-engine-spec.scm
diff --git a/libgnucash/app-utils/business-options.scm b/libgnucash/app-utils/business-options.scm
deleted file mode 100644
index 96a4e4087..000000000
--- a/libgnucash/app-utils/business-options.scm
+++ /dev/null
@@ -1,386 +0,0 @@
-;; Scheme code for supporting options for the business modules
-;;
-;; Created by:	Derek Atkins <derek at ihtfp.com>
-;;
-;; 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
-
-
-;; Internally, values are always a guid. Externally, both guids and
-;; invoice pointers may be used to set the value of the option. The
-;; option always returns a single invoice pointer.
-
-(define-module (gnucash app-utils business-options))
-
-(eval-when (compile load eval expand)
-  (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module"))
-
-(use-modules (gnucash core-utils))
-(use-modules (gnucash engine))
-(use-modules (gnucash utilities))
-(use-modules (gnucash app-utils options))
-(use-modules (sw_app_utils))
-
-(export gnc:*business-label*)
-(export gnc:*company-name*)
-(export gnc:*company-addy*)
-(export gnc:*company-id*)
-(export gnc:*company-phone*)
-(export gnc:*company-fax*)
-(export gnc:*company-url*)
-(export gnc:*company-email*)
-(export gnc:*company-contact*)
-(export gnc:*fancy-date-label*)
-(export gnc:*fancy-date-format*)
-(export gnc:*tax-label*)
-(export gnc:*tax-nr-label*)
-(export gnc:company-info)
-(export gnc:fancy-date-info)
-(export gnc:*option-section-budgeting*)
-(export gnc:*option-name-auto-readonly-days*)
-(export gnc:*option-name-num-field-source*)
-(export gnc:*kvp-option-path*)
-(export gnc:options-fancy-date)
-(export gnc:*option-name-default-budget*)
-
-(define gnc:*kvp-option-path* (list KVP-OPTION-PATH))
-(define gnc:*option-name-auto-readonly-days* OPTION-NAME-AUTO-READONLY-DAYS)
-(define gnc:*option-name-num-field-source* OPTION-NAME-NUM-FIELD-SOURCE)
-
-(define gnc:*option-section-budgeting* OPTION-SECTION-BUDGETING)
-(define gnc:*option-name-default-budget* OPTION-NAME-DEFAULT-BUDGET)
-
-(define gnc:*business-label* (N_ "Business"))
-(define gnc:*company-name* (N_ "Company Name"))
-(define gnc:*company-addy* (N_ "Company Address"))
-(define gnc:*company-id* (N_ "Company ID"))
-(define gnc:*company-phone* (N_ "Company Phone Number"))
-(define gnc:*company-fax* (N_ "Company Fax Number"))
-(define gnc:*company-url* (N_ "Company Website URL"))
-(define gnc:*company-email* (N_ "Company Email Address"))
-(define gnc:*company-contact* (N_ "Company Contact Person"))
-(define gnc:*fancy-date-label* (N_ "Fancy Date Format"))
-(define gnc:*fancy-date-format* (N_ "custom"))
-(define gnc:*tax-label* (N_ "Tax"))
-(define gnc:*tax-nr-label* (N_ "Tax Number"))
-
-
-(define (gnc:options-fancy-date book)
-  (let ((date-format (gnc:fancy-date-info book gnc:*fancy-date-format*)))
-    (if (boolean? date-format) ;; date-format does not exist
-        (qof-date-format-get-string (qof-date-format-get))
-       date-format)))
-
-(define (gnc:company-info book key)
-  ;; Access company info from key-value pairs for current book
- (gnc:option-get-value book gnc:*business-label* key))
-
-(define (gnc:fancy-date-info book key)
-  ;; Access fancy date info from key-value pairs for current book
- (gnc:option-get-value book gnc:*business-label* (list gnc:*fancy-date-label* key)))
-
-(define (gnc:make-invoice-option
-	 section
-	 name
-	 sort-tag
-	 documentation-string
-	 default-getter
-	 value-validator)
-
-  (define (convert-to-guid item)
-    (if (string? item)
-        item
-        (gncInvoiceReturnGUID item)))
-
-  (define (convert-to-invoice item)
-    (if (string? item)
-        (gncInvoiceLookupFlip item (gnc-get-current-book))
-        item))
-
-  (let* ((option (convert-to-guid (default-getter)))
-         (option-set #f)
-         (getter (lambda () (convert-to-invoice
-			     (if option-set
-				 option
-				 (default-getter)))))
-         (value->string (lambda ()
-                          (string-append
-                           "'" (gnc:value->string (if option-set option #f)))))
-         (validator
-          (if (not value-validator)
-              (lambda (invoice) (list #t invoice))
-              (lambda (invoice)
-                (value-validator (convert-to-invoice invoice))))))
-    (gnc:make-option
-     section name sort-tag 'invoice documentation-string getter
-     (lambda (invoice) ;; setter
-       (if (null? invoice) (set! invoice (default-getter)))
-       (set! invoice (convert-to-invoice invoice))
-       (let* ((result (validator invoice))
-	      (valid (car result))
-	      (value (cadr result)))
-	 (if valid
-	     (begin
-	       (set! option (convert-to-guid value))
-	       (set! option-set #t))
-	     (gnc:error "Illegal invoice value set"))))
-     (lambda () (convert-to-invoice (default-getter)))
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b option p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-	 (if (and v (string? v))
-	     (begin
-	       (set! option v)
-	       (set! option-set #t)))))
-     validator
-     #f #f #f #f)))
-
-;; Internally, values are always a guid. Externally, both guids and
-;; customer pointers may be used to set the value of the option. The
-;; option always returns a single customer pointer.
-(define (gnc:make-owner-option
-	 section
-	 name
-	 sort-tag
-	 documentation-string
-	 default-getter
-	 value-validator
-	 owner-type)
-
-  (let ((option-value (gncOwnerNew)))
-
-    (define (convert-to-pair item)
-      (if (pair? item)
-	  item
-	  (cons (gncOwnerGetType item)
-		(gncOwnerReturnGUID item))))
-
-    (define (convert-to-owner pair)
-      (if (pair? pair)
-	  (let ((type (car pair)))
-	    (cond
-	      ((eqv? type GNC-OWNER-CUSTOMER)
-	       (gncOwnerInitCustomer
-		option-value
-		(gncCustomerLookupFlip (cdr pair) (gnc-get-current-book)))
-	       option-value)
-
-	       ((eqv? type GNC-OWNER-VENDOR)
-		(gncOwnerInitVendor
-		 option-value
-		 (gncVendorLookupFlip (cdr pair) (gnc-get-current-book)))
-		option-value)
-
-	       ((eqv? type GNC-OWNER-EMPLOYEE)
-		(gncOwnerInitEmployee
-		 option-value
-		 (gncEmployeeLookupFlip (cdr pair) (gnc-get-current-book)))
-		option-value)
-
-	       ((eqv? type GNC-OWNER-JOB)
-		(gncOwnerInitJob
-		 option-value
-		 (gncJobLookupFlip (cdr pair) (gnc-get-current-book)))
-		option-value)
-
-	       (else '())))
-	  pair))
-
-    (let* ((option (convert-to-pair (default-getter)))
-	   (option-set #f)
-	   (getter (lambda () (convert-to-owner
-			       (if option-set
-				   option
-				   (default-getter)))))
-	   (value->string (lambda ()
-			    (string-append
-			     "'" (gnc:value->string
-				  (if option-set option #f)))))
-	   (validator
-	    (if (not value-validator)
-		(lambda (owner)
-		  (let ((type (if (pair? owner)
-                                  (car owner)
-                                  (gncOwnerGetType owner))))
-		    (if (equal? type owner-type)
-			(list #t owner)
-			(list #f "Owner-Type Mismatch"))))
-		(lambda (owner)
-		  (value-validator (convert-to-owner owner))))))
-
-      (gnc:make-option
-       section name sort-tag 'owner documentation-string getter
-       (lambda (owner)
-	 (if (null? owner) (set! owner (default-getter)))
-         (set! owner (convert-to-owner owner))
-         (let* ((result (validator owner))
-		(valid (car result))
-		(value (cadr result)))
-	   (if valid
-	       (begin
-		 (set! option (convert-to-pair value))
-		 (set! option-set #t))
-	       (gnc:error "Illegal owner value set"))))
-       (lambda () (convert-to-owner (default-getter)))
-       (gnc:restore-form-generator value->string)
-       (lambda (b p)
-	 (qof-book-set-option b (symbol->string (car option))
-				      (append p '("type")))
-	 (qof-book-set-option b (cdr option)
-				      (append p '("value"))))
-       (lambda (b p)
-	 (let ((t (qof-book-get-option b (append p '("type"))))
-	       (v (qof-book-get-option b (append p '("value")))))
-	   (if (and t v (string? t) (string? v))
-	       (begin
-		 (set! option (cons (string->symbol t) v))
-		 (set! option-set #t)))))
-       validator
-       owner-type #f #f #f))))
-
-
-;; Internally, values are always a guid. Externally, both guids and
-;; taxtable pointers may be used to set the value of the option. The
-;; option always returns a single taxtable pointer.
-
-(define (gnc:make-taxtable-option
-	 section
-	 name
-	 sort-tag
-	 documentation-string
-	 default-getter
-	 value-validator)
-
-  (define (convert-to-guid item)
-    (if (string? item)
-        item
-        (gncTaxTableReturnGUID item)))
-
-  (define (convert-to-taxtable item)
-    (if (string? item)
-        (gncTaxTableLookupFlip item (gnc-get-current-book))
-        item))
-
-  (let* ((option (convert-to-guid (default-getter)))
-         (option-set #f)
-         (getter (lambda () (convert-to-taxtable
-			     (if option-set
-				 option
-				 (default-getter)))))
-         (value->string (lambda ()
-                          (string-append
-                           "'" (gnc:value->string (if option-set option #f)))))
-         (validator
-          (if (not value-validator)
-              (lambda (taxtable) (list #t taxtable))
-              (lambda (taxtable)
-                (value-validator (convert-to-taxtable taxtable))))))
-    (gnc:make-option
-     section name sort-tag 'taxtable documentation-string getter
-     (lambda (taxtable)
-       (if (null? taxtable) (set! taxtable (default-getter)))
-       (set! taxtable (convert-to-taxtable taxtable))
-       (let* ((result (validator taxtable))
-	      (valid (car result))
-	      (value (cadr result)))
-	 (if valid
-	     (begin
-	       (set! option (convert-to-guid value))
-	       (set! option-set #t))
-	     (gnc:error "Illegal taxtable value set"))))
-     (lambda () (convert-to-taxtable (default-getter)))
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b option p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-	 (if (and v (string? v))
-	     (begin
-	       (set! option v)
-	       (set! option-set #t)))))
-     validator
-     #f #f #f #f)))
-
-;; This defines an option to set a counter value. This is a slightly
-;; different kind of option: Unlike all other options, the values edited
-;; by this option are not saved in the "options"/<section> kvm slot, but
-;; in the "counters" slot. This is mostly due to backwards compatibility
-;; and partly because counters are a bit different from other options
-;; anyway.
-;;
-;; This is implemented by overriding the scm->kvp and kvp->scm methods
-;; to ignore the kvp path passed and replace it with a hardcoded
-;; "counters".
-(define (gnc:make-counter-option
-         section
-         name
-	 key
-         sort-tag
-         documentation-string
-         default-value)
-  (let ((option (gnc:make-number-range-option
-                 section name sort-tag documentation-string
-                 default-value 0 999999999 0 1)))
-    (gnc:set-option-scm->kvp
-     option
-     (lambda (b p)
-       (qof-book-set-option
-        b (inexact->exact ((gnc:option-getter option)))
-        (list "counters" key))))
-    (gnc:set-option-kvp->scm
-     option
-     (lambda (b p)
-       (let ((v (qof-book-get-option b (list "counters" key))))
-         (if (and v (integer? v))
-             ((gnc:option-setter option) v)))))
-    option))
-
-;; This defines an option to set a counter format, which has the same
-;; exception as gnc:make-counter-option above.
-;; Note this function uses a hack to make sure there never is a default value
-;; (default-value is set to #f and value subsequently set to whatever was passed as default-value)
-;; This hack was introduced to fix https://bugs.gnucash.org/show_bug.cgi?id=687504
-(define (gnc:make-counter-format-option
-         section
-         name
-         key
-         sort-tag
-         documentation-string
-         default-value)
-  (let ((option (gnc:make-string-option
-                 section name sort-tag documentation-string #f)))
-    (gnc:option-set-value option default-value)
-    (gnc:set-option-scm->kvp
-     option
-     (lambda (b p)
-       (let ((value ((gnc:option-getter option)))
-             (path (string-concatenate (list "counter_formats/" key))))
-         (qof-book-set-string-option b path value))))
-    (gnc:set-option-kvp->scm
-     option
-     (lambda (b p)
-       (let* ((path (string-concatenate (list "counter_formats/" key)))
-              (v (qof-book-get-string-option b path)))
-         (if (and v (string? v))
-             ((gnc:option-setter option) v)))))
-    option))
-
-(export gnc:make-invoice-option)
-(export gnc:make-owner-option)
-(export gnc:make-taxtable-option)
-(export gnc:make-counter-option)
-(export gnc:make-counter-format-option)
diff --git a/libgnucash/app-utils/business-prefs.scm b/libgnucash/app-utils/business-prefs.scm
deleted file mode 100644
index 84fc701fd..000000000
--- a/libgnucash/app-utils/business-prefs.scm
+++ /dev/null
@@ -1,199 +0,0 @@
-;; Business Preferences
-;;
-;; Created by:	Derek Atkins <derek at ihtfp.com>
-;;
-;; 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
-
-(define-module (gnucash app-utils business-prefs))
-
-(eval-when (compile load eval expand)
-  (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module"))
-
-(use-modules (sw_app_utils))
-(use-modules (gnucash core-utils))
-(use-modules (gnucash engine))
-(use-modules (gnucash app-utils options))
-(use-modules (gnucash app-utils business-options))
-
-(define gnc:*option-section-counters* (N_ "Counters"))
-
-;; This defines all available counter types to show options for. This a
-;; list that contains a sublist for each counter type, containing: The
-;; (untranslated) counter name, the format label, the previous number
-;; label, the format help text and the previous number help text.
-(define counter-types
-  (list (list "gncCustomer"     (N_ "Customer number format")
-                                (N_ "Customer number")
-                                (N_ "The format string to use for generating customer numbers. This is a printf-style format string.")
-                                (N_ "The previous customer number generated. This number will be incremented to generate the next customer number."))
-        (list "gncEmployee"     (N_ "Employee number format")
-                                (N_ "Employee number")
-                                (N_ "The format string to use for generating employee numbers. This is a printf-style format string.")
-                                (N_ "The previous employee number generated. This number will be incremented to generate the next employee number."))
-        (list "gncInvoice"      (N_ "Invoice number format")
-                                (N_ "Invoice number")
-                                (N_ "The format string to use for generating invoice numbers. This is a printf-style format string.")
-                                (N_ "The previous invoice number generated. This number will be incremented to generate the next invoice number."))
-        (list "gncBill"         (N_ "Bill number format")
-                                (N_ "Bill number")
-                                (N_ "The format string to use for generating bill numbers. This is a printf-style format string.")
-                                (N_ "The previous bill number generated. This number will be incremented to generate the next bill number."))
-        (list "gncExpVoucher"   (N_ "Expense voucher number format")
-                                (N_ "Expense voucher number")
-                                (N_ "The format string to use for generating expense voucher numbers. This is a printf-style format string.")
-                                (N_ "The previous expense voucher number generated. This number will be incremented to generate the next voucher number."))
-        (list "gncJob"          (N_ "Job number format")
-                                (N_ "Job number")
-                                (N_ "The format string to use for generating job numbers. This is a printf-style format string.")
-                                (N_ "The previous job number generated. This number will be incremented to generate the next job number."))
-        (list "gncOrder"        (N_ "Order number format")
-                                (N_ "Order number")
-                                (N_ "The format string to use for generating order numbers. This is a printf-style format string.")
-                                (N_ "The previous order number generated. This number will be incremented to generate the next order number."))
-        (list "gncVendor"       (N_ "Vendor number format")
-                                (N_ "Vendor number")
-                                (N_ "The format string to use for generating vendor numbers. This is a printf-style format string.")
-                                (N_ "The previous vendor number generated. This number will be incremented to generate the next vendor number."))
-))
-
-(define (book-options-generator options)
-  (define (reg-option new-option)
-    (gnc:register-option options new-option))
-
-  (reg-option
-   (gnc:make-string-option
-    gnc:*business-label* gnc:*company-name*
-    "a" (N_ "The name of your business.") ""))
-
-  (reg-option
-   (gnc:make-text-option
-    gnc:*business-label* gnc:*company-addy*
-    "b1" (N_ "The address of your business.") ""))
-
-  (reg-option
-   (gnc:make-string-option
-    gnc:*business-label* gnc:*company-contact*
-    "b2" (N_ "The contact person to print on invoices.") ""))
-
-  (reg-option
-   (gnc:make-string-option
-    gnc:*business-label* gnc:*company-phone*
-    "c1" (N_ "The phone number of your business.") ""))
-
-  (reg-option
-   (gnc:make-string-option
-    gnc:*business-label* gnc:*company-fax*
-    "c2" (N_ "The fax number of your business.") ""))
-
-  (reg-option
-   (gnc:make-string-option
-    gnc:*business-label* gnc:*company-email*
-    "c3" (N_ "The email address of your business.") ""))
-
-  (reg-option
-   (gnc:make-string-option
-    gnc:*business-label* gnc:*company-url*
-    "c4" (N_ "The URL address of your website.") ""))
-
-  (reg-option
-   (gnc:make-string-option
-    gnc:*business-label* gnc:*company-id*
-    "c5" (N_ "The ID for your company (eg 'Tax-ID: 00-000000).")
-    ""))
- 
-  (reg-option
-   (gnc:make-taxtable-option
-    gnc:*business-label* (N_ "Default Customer TaxTable")
-    "e" (N_ "The default tax table to apply to customers.")
-    (lambda () '()) #f))
-
-  (reg-option
-   (gnc:make-taxtable-option
-    gnc:*business-label* (N_ "Default Vendor TaxTable")
-    "f" (N_ "The default tax table to apply to vendors.")
-    (lambda () '()) #f))
-
-  (reg-option
-   (gnc:make-dateformat-option
-    gnc:*business-label* gnc:*fancy-date-label*
-    "g" (N_ "The default date format used for fancy printed dates.")
-    #f))
-
-  ;; Accounts tab
-
-  (reg-option
-   (gnc:make-number-range-option
-	gnc:*option-section-accounts* gnc:*option-name-auto-readonly-days*
-	"a" (N_ "Choose the number of days after which transactions will be read-only and cannot be edited anymore. This threshold is marked by a red line in the account register windows. If zero, all transactions can be edited and none are read-only.")
-	0 ;; default
-	0 ;; lower bound
-	3650 ;; upper bound
-	0 ;; number of decimals
-	1 ;; step size
-	))
-
-  (reg-option 
-   (gnc:make-simple-boolean-option
-    gnc:*option-section-accounts* gnc:*option-name-num-field-source*
-    "b" (N_ "Check to have split action field used in registers for 'Num' field in place of transaction number; transaction number shown as 'T-Num' on second line of register. Has corresponding effect on business features, reporting and imports/exports.")
-    #f))
-
-  (reg-option 
-   (gnc:make-simple-boolean-option
-    gnc:*option-section-accounts* gnc:*option-name-trading-accounts*
-    "a" (N_ "Check to have trading accounts used for transactions involving more than one currency or commodity.")
-    #f))
-
-  ;; Budgeting Tab
-
-  (reg-option
-   (gnc:make-budget-option
-    gnc:*option-section-budgeting* gnc:*option-name-default-budget*
-    "a" (N_ "Budget to be used when none has been otherwise specified.")))
-
-  ;; Tax Tab
-  (reg-option
-   (gnc:make-string-option
-    gnc:*tax-label* gnc:*tax-nr-label*
-    "a" (N_ "The electronic tax number of your business") ""))
-
-  ;; Counters Tab
-  (for-each
-   (lambda (vals)
-     ;; Unpack the list of strings for this counter type
-     (let* ((key (car vals))
-            (format-label (cadr vals))
-            (number-label (caddr vals))
-            (format-description (cadddr vals))
-            (number-description (cadddr (cdr vals))))
-       ;; For each counter-type we create an option for the last used
-       ;; number and the format string to use.
-       (reg-option
-        (gnc:make-counter-option
-         gnc:*option-section-counters* number-label key
-         (string-append key "a") number-description 0))
-
-       (reg-option
-        (gnc:make-counter-format-option
-         gnc:*option-section-counters* format-label key
-         (string-append key "b") format-description ""))))
-   ;; Make counter and format option for each defined counter
-   counter-types))
-
-
-;;(gnc-register-kvp-option-generator QOF-ID-BOOK-SCM book-options-generator)
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index b22fd050c..9fbfa9083 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -41,7 +41,6 @@ namespace std {
 %typemap(in) std::size_t "$1 = scm_to_ulong($input);";
 %typemap(out) std::size_t "$result = scm_from_ulong($1);";
 
- //%module sw_gnc_optiondb
 %{
 #include "gnc-optiondb.h"
 #include "gnc-optiondb.hpp"
@@ -53,6 +52,13 @@ SCM scm_init_sw_gnc_optiondb_module(void);
 
 %include <std_string.i>
 %import <base-typemaps.i>
+%import (module="sw_engine") <gnc-budget.h>
+%import (module="sw_engine") <gncCustomer.h>
+%import (module="sw_engine") <gncEmployee.h>
+%import (module="sw_engine") <gncVendor.h>
+%import (module="sw_engine") <gncTaxTable.h>
+%import (module="sw_engine") <gncInvoice.h>
+%import (module="sw_engine") <gncJob.h>
 
  /* Implementation Note: Plain overloads failed to compile because
   *    auto value{option.get_value()};
@@ -133,18 +139,6 @@ scm_from_value<SCM>(SCM value)
     return value;
 }
 
-template <> inline SCM
-scm_from_value<QofQuery*>(QofQuery* value)
-{
-        return SCM_BOOL_F;
-}
-
-template <> inline SCM
-scm_from_value<QofInstance*>(QofInstance* value)
-{
-        return SCM_BOOL_F;
-}
-
 template <> inline SCM
 scm_from_value<std::string>(std::string value)
 {
@@ -188,11 +182,11 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
     else if (GNC_IS_ACCOUNT(value))
         type = SWIGTYPE_p_Account;
     else if (GNC_IS_BUDGET(value))
-        type = SWIGTYPE_p_GncBudget;
+        type = SWIGTYPE_p_budget_s;
     else if (GNC_IS_INVOICE(value))
-        type = SWIGTYPE_p_GncInvoice;
+        type = SWIGTYPE_p__gncInvoice;
     else if (GNC_IS_TAXTABLE(value))
-        type = SWIGTYPE_p_GncTaxTable;
+        type = SWIGTYPE_p__gncTaxTable;
     else if (GNC_IS_CUSTOMER(value))
         type = SWIGTYPE_p__gncCustomer;
     else if (GNC_IS_EMPLOYEE(value))
@@ -215,6 +209,18 @@ scm_from_value<const QofQuery*>(const QofQuery* value)
     return SWIG_NewPointerObj(ptr, SWIGTYPE_p__QofQuery, FALSE);
 }
 
+template <> inline SCM
+scm_from_value<QofQuery*>(QofQuery* value)
+{
+    return scm_from_value<const QofQuery*>(value);
+}
+
+template <> inline SCM
+scm_from_value<QofInstance*>(QofInstance* value)
+{
+    return scm_from_value<const QofInstance*>(value);
+}
+
 template <typename ValueType> inline ValueType
 scm_to_value(SCM new_value)
 {
@@ -261,11 +267,13 @@ scm_to_value<const QofInstance*>(SCM new_value)
 {
     if (new_value == SCM_BOOL_F)
         return nullptr;
+    
+    auto info = SWIG_PointerType(new_value);
 
     static const std::array<swig_type_info*, 10> types{
             SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
-            SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice,
-            SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account,
+            SWIGTYPE_p_budget_s, SWIGTYPE_p__gncInvoice,
+            SWIGTYPE_p__gncTaxTable, SWIGTYPE_p_Account,
             SWIGTYPE_p__gncCustomer, SWIGTYPE_p__gncEmployee,
             SWIGTYPE_p__gncJob, SWIGTYPE_p__gncVendor
                 };
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 75b34e8e6..cdd4db4e4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -509,8 +509,6 @@ gnucash/report/stylesheets/head-or-tail.scm
 gnucash/report/stylesheets/plain.scm
 gnucash/report/trep-engine.scm
 libgnucash/app-utils/app-utils.scm
-libgnucash/app-utils/business-options.scm
-libgnucash/app-utils/business-prefs.scm
 libgnucash/app-utils/calculation/expression_parser.c
 libgnucash/app-utils/calculation/fin.c
 libgnucash/app-utils/c-interface.scm

commit a1af86ed403d97c8f932c94c37537a43bcf87fb0
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jul 13 12:44:13 2021 -0700

    Implement gnc:generate-restore-forms, gnc-optiondb-save-to-scheme.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 52be11160..b22fd050c 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -514,6 +514,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 #include <algorithm>
 #include <array>
 #include <string>
+#include <sstream>
 #include "gnc-option.hpp"
 #include "gnc-option-ui.hpp"
 
@@ -530,7 +531,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
  * overloads. The latter isn't useful to SWIG, ignore it.
  */
 %ignore GncOptionDB::register_option(const char*, GncOption&&);
-
+/* GncOptionDB::save_to_scheme takes and returns a std::stream. Good luck
+ * converting *that* to anything useful!
+ */
+%ignore GncOptionDB::save_to_scheme(std::ostream&, const char*);
+%ignore GncOptionDB::save_option_scheme(std::ostream&, const char*, const std::string&, const std::string&);
+%ignore GncOptionDB::load_option_scheme(std::itream&);
 /* The following functions are overloaded in gnc-optiondb.hpp to provide both
  * GncOptionDB* and GncOptionDBPtr& versions. That confuses SWIG so ignore the
  * raw-ptr version.
@@ -1232,6 +1238,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                     });
             });
     }
+
+    std::string
+    gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog)
+    {
+        std::ostringstream oss;
+        odb->save_to_scheme(oss, prolog);
+        return oss.str();
+    }
 %}
 
 #endif //SWIGGUILE
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 0faa0a065..4e26f0dc5 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -35,6 +35,10 @@
 (define-public (gnc:value->string value)
   (format #f "~s" value))
 
+(define-public (gnc:generate-restore-forms options name)
+  (let ((optiondb (options 'generate-restore-forms)))
+    (gnc-optiondb-save-to-scheme optiondb name)))
+
 (define-public (gnc:lookup-option options section name)
   (if options
       (gnc-lookup-option (options 'lookup) section name)

commit c34986dad4dd2d07db1545585291bdb28ab331c4
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jul 13 12:43:27 2021 -0700

    Change gnc:new-options to return a fake dispatch function.
    
    To placate test-report.scm

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index b70a099fb..0faa0a065 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -37,7 +37,7 @@
 
 (define-public (gnc:lookup-option options section name)
   (if options
-      (gnc-lookup-option options section name)
+      (gnc-lookup-option (options 'lookup) section name)
       #f))
 
 (define-public (gnc:option-setter option)
@@ -78,7 +78,7 @@
   (qof-book-get-option book (acc category key)))
 
 (define-public (gnc:option-make-internal! options section name)
-  (let ((option (gnc-lookup-option options section name)))
+  (let ((option (gnc-lookup-option (options 'lookup) section name)))
     (and option (GncOption-make-internal option))))
 
 (define-public (gnc:option-type option)
@@ -94,14 +94,18 @@
         (set! retval (cons retval (vector value name)))))
     retval))
 
+;; Create the database and return a dispatch function.
 (define-public (gnc:new-options)
-  (new-gnc-optiondb))
+  (let ((optiondb (new-gnc-optiondb)))
+    (define (dispatch key)
+      optiondb)
+  dispatch))
 
 (define-public (gnc:options-set-default-section optiondb section)
-  (GncOptionDB-set-default-section (GncOptionDBPtr-get optiondb) section))
+  (GncOptionDB-set-default-section (GncOptionDBPtr-get (optiondb 'set-default-section)) section))
 
 (define-public (gnc:options-for-each func optdb)
-  (gnc-optiondb-foreach optdb func))
+  (gnc-optiondb-foreach (optdb 'foreach) func))
 
 ;; Copies all values from src-options to dest-options, that is, it
 ;; copies the values of all options from src which exist in dest to
@@ -121,18 +125,18 @@
 
 ;; FIXME: Fake callback functions for boolean-complex and multichoice-callback
 
-(define-public (gnc:options-register-callback section name callback options) 1)
-(define-public (gnc:options-register-c-callback section name callback data options) 1)
-(define-public (gnc:options-unregister-callback-id id) 0 options)
+(define-public (gnc:options-register-callback section name callback options) (options 'register-callback) 1)
+(define-public (gnc:options-register-c-callback section name callback data options) (options 'register-c-callback) 1)
+(define-public (gnc:options-unregister-callback-id id) 0 (options 'unregister-callback-id))
 
 ;; The following implement the old API that separated creation from registration.
 (define-public (gnc:register-option optdb opt)
   (issue-deprecation-warning "gnc:register-option is deprecated. Use gnc-register-foo-option instead.")
-  (GncOptionDB-register-option (GncOptionDBPtr-get optdb)
+  (GncOptionDB-register-option (GncOptionDBPtr-get (optdb 'register-option))
                                (GncOption-get-section opt) opt))
 
 (define-public (gnc:unregister-option optdb section name)
-  (GncOptionDB-unregister-option (GncOptionDBPtr-get optdb) section name))
+  (GncOptionDB-unregister-option (GncOptionDBPtr-get (optdb 'unregister-option)) section name))
 
 (define-public (gnc:make-string-option section name key docstring default)
   (issue-deprecation-warning "gnc:make-string-option is deprecated. Make and register the option in one command with gnc-register-string-option.")
@@ -256,12 +260,12 @@
     (gnc-make-date-option section name key docstring default relative-date-list both)))
 
 (define-public (gnc:options-make-end-date! optiondb pagename optname sort-tag docstring)
-  (gnc-register-end-date-option optiondb pagename optname sort-tag docstring))
+  (gnc-register-end-date-option (optiondb 'make-option) pagename optname sort-tag docstring))
 
 (define-public (gnc:options-make-date-interval! optiondb pagename name-from info-from name-to info-to sort-tag)
-  (gnc-register-start-date-option optiondb pagename name-from
+  (gnc-register-start-date-option (optiondb 'make-option) pagename name-from
                                   (string-append sort-tag "a") info-from)
-  (gnc-register-end-date-option optiondb pagename name-to
+  (gnc-register-end-date-option (optiondb 'make-option) pagename name-to
                                 (string-append sort-tag "b") info-to))
 (define-public (gnc:date-option-absolute-time option-value)
   (if (pair? option-value)

commit 3f89d063ebbac5d4dbc51feed65ebdce7de52c04
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jul 13 09:49:27 2021 -0700

    Evaluate default function in gnc:make-account-sel-limited-option.

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 849a06e47..b70a099fb 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -173,7 +173,8 @@
   (gnc-make-account-list-limited-option section name key docstring (default) permitted))
 (define-public (gnc:make-account-sel-limited-option section name key docstring default validator permitted)
   (issue-deprecation-warning "gnc:make-account-sel-limited-option is deprecated. Make and register the option in one command with gnc-register-account-sel-limited-option.")
-  (gnc-make-account-sel-limited-option section name key docstring (default) permitted))
+  (let ((defval (if default (default) #f)))
+    (gnc-make-account-sel-limited-option section name key docstring defval permitted)))
 (define-public (gnc:make-account-sel-option section name key docstring default validator)
   (let ((defval (if default (default) #f)))
   (gnc-make-account-sel-limited-option section name key docstring defval '())))

commit 6eb5dfed513e323d5781dba4039af364104fdcc1
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jul 13 09:47:58 2021 -0700

    make-internal fail if there is a ui_item, not if there isn't.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 4ab4a1ddb..8aa240622 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -254,7 +254,7 @@ GncOption::set_option_from_ui_item()
 void
 GncOption::make_internal()
 {
-    if (!m_ui_item)
+    if (m_ui_item)
     {
         PERR("Option %s:%s has a UI Element, can't be INTERNAL.",
              get_section().c_str(), get_name().c_str());

commit 29a2365fdf17161f05c5cb2012b8f9ef450e6e06
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jul 13 09:46:48 2021 -0700

    Avoid infinite recursion when the alias changes only the section name.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 8f8688fb8..abb80efed 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -227,7 +227,7 @@ GncOptionDB::find_option(const std::string& section, const char* name) const
      * nullptr. GncOptionSection::find_option already checked if the alias
      * should have been in the same section.
      */
-    if (alias && alias->first)
+    if (alias && alias->first && section != alias->first)
         return find_option(alias->first, alias->second);
     return nullptr;
 }

commit 7885353fe31665013f23294d2a7fd1079d47a9c5
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jul 13 09:46:07 2021 -0700

    Bring option aliases up to date.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 3bbd4e405..8f8688fb8 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -76,13 +76,13 @@ const OptionAliases Aliases::c_option_aliases
     {"Sign Reverses?", {nullptr, "Sign Reverses"}},
     {"To", {nullptr, "End Date"}},
     {"Charge Type", {nullptr, "Action"}}, // easy-invoice.scm, renamed June 2018
-        // the following 4 options in income-gst-statement.scm renamed Dec 2018
+    // the following 4 options in income-gst-statement.scm renamed Dec 2018
     {"Individual income columns", {nullptr, "Individual sales columns"}},
     {"Individual expense columns",
         {nullptr, "Individual purchases columns"}},
     {"Remittance amount", {nullptr, "Gross Balance"}},
     {"Net Income", {nullptr, "Net Balance"}},
-        // transaction.scm:
+    // transaction.scm:
     {"Use Full Account Name?", {nullptr, "Use Full Account Name"}},
     {"Use Full Other Account Name?",
         {nullptr, "Use Full Other Account Name"}},
@@ -90,10 +90,20 @@ const OptionAliases Aliases::c_option_aliases
     {"Void Transactions", {"Filter", "Void Transactions"}},
     {"Account Substring", {"Filter", "Account Name Filter"}},
     {"Enable links", {nullptr, "Enable Links"}},
-        // invoice.scm, renamed November 2018
+    // trep-engine: moved currency options to own tab
+    {"Common Currency", {"Currency", "Common Currency"}},
+    {"Show original currency amount",
+        {"Currency", "Show original currency amount"}},
+    {"Report's currency", {"Currency", "Report's currency"}},
+    {"Reconcile Status", {nullptr, "Reconciled Status"}},
+    // new-owner-report.scm, renamed Oct 2020 to differentiate with
+    // Document Links:
+    {"Links", {nullptr, "Transaction Links"}},
+    // invoice.scm, renamed November 2018
     {"Individual Taxes", {nullptr, "Use Detailed Tax Summary"}},
-        // income-gst-statement.scm
-    {"default format", {nullptr, "Default Format"}}
+    // income-gst-statement.scm
+    {"default format", {nullptr, "Default Format"}},
+    {"Report format", {nullptr, "Report Format"}},
 };
 
 static bool

commit bbe74aa0864aa1ef896372f410a04a96c8f73d6f
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jul 12 13:34:43 2021 -0700

    Remove tooltips from multichoice option values.
    
    This is the c++options equivalent to 02a6a0ae and e1525721.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 87c2f2b71..7e2720679 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -46,7 +46,6 @@ extern "C"
 #include "glib-guile.h"
 #include "gnc-account-sel.h"
 #include "gnc-tree-view-account.h"
-#include "gnc-combott.h"
 #include "gnc-commodity-edit.h"
 #include "gnc-component-manager.h"
 #include "gnc-general-select.h"
@@ -1180,15 +1179,12 @@ create_multichoice_widget(GncOption& option)
     {
         GtkTreeIter iter;
         auto itemstring = option.permissible_value_name(i);
-        auto description = option.permissible_value_description(i);
         gtk_list_store_append (store, &iter);
-        gtk_list_store_set (store, &iter, 0,
-                            (itemstring && *itemstring) ? _(itemstring) : "", 1,
-                            (description && *description) ? _(description) : "", -1);
+        gtk_list_store_set(store, &iter, 0,
+                           (itemstring && *itemstring) ? _(itemstring) : "", 1);
     }
     /* Create the new Combo with tooltip and add the store */
-    auto widget = GTK_WIDGET(gnc_combott_new());
-    g_object_set( G_OBJECT( widget ), "model", GTK_TREE_MODEL(store), NULL );
+    auto widget{GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)))};
     g_object_unref(store);
 
     g_signal_connect(G_OBJECT(widget), "changed",
@@ -1204,13 +1200,13 @@ public:
         GncOptionGtkUIItem{widget, GncOptionUIType::MULTICHOICE} {}
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
-        auto widget{GNC_COMBOTT(get_widget())};
-        gnc_combott_set_active(widget, option.get_value<size_t>());
+        auto widget{GTK_COMBO_BOX(get_widget())};
+        gtk_combo_box_set_active(widget, option.get_value<size_t>());
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
-        auto widget{GNC_COMBOTT(get_widget())};
-        option.set_value<size_t>(gnc_combott_get_active(widget));
+        auto widget{GTK_COMBO_BOX(get_widget())};
+        option.set_value<size_t>(gtk_combo_box_get_active(widget));
     }
 };
 
@@ -1300,9 +1296,7 @@ private:
 };
 
 
-RelativeDateEntry::RelativeDateEntry(GncOption& option) :
-    m_entry{GTK_WIDGET(gnc_combott_new())}
-
+RelativeDateEntry::RelativeDateEntry(GncOption& option)
 {
 
     /* GtkComboBox still does not support per-item tooltips, so have
@@ -1319,12 +1313,11 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) :
         GtkTreeIter  iter;
         gtk_list_store_append (store, &iter);
         gtk_list_store_set (store, &iter, 0,
-                            option.permissible_value_name(index), 1,
-                            option.permissible_value_description(index), -1);
+                            option.permissible_value_name(index), 1);
     }
 
     /* Create the new Combo with tooltip and add the store */
-    g_object_set( G_OBJECT(m_entry), "model", GTK_TREE_MODEL(store), nullptr);
+    m_entry = GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)));
     g_object_unref(store);
 
     g_signal_connect(G_OBJECT(m_entry), "changed",
@@ -1334,13 +1327,13 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) :
 void
 RelativeDateEntry::set_entry_from_option(GncOption& option)
 {
-    gnc_combott_set_active(GNC_COMBOTT(m_entry), option.get_value<size_t>());
+    gtk_combo_box_set_active(GTK_COMBO_BOX(m_entry), option.get_value<size_t>());
 }
 
 void
 RelativeDateEntry::set_option_from_entry(GncOption& option)
 {
-    option.set_value<size_t>(gnc_combott_get_active(GNC_COMBOTT(m_entry)));
+    option.set_value<size_t>(gtk_combo_box_get_active(GTK_COMBO_BOX(m_entry)));
 }
 
 using AbsoluteDateEntryPtr = std::unique_ptr<AbsoluteDateEntry>;
@@ -2465,7 +2458,6 @@ create_radiobutton_widget(char *name, GncOption& option)
     for (decltype(num_values) i = 0; i < num_values; i++)
     {
         auto label = option.permissible_value_name(i);
-        auto tip = option.permissible_value_description(i);
 
         widget =
             gtk_radio_button_new_with_label_from_widget (widget ?
@@ -2474,7 +2466,6 @@ create_radiobutton_widget(char *name, GncOption& option)
                     label && *label ? _(label) : "");
         g_object_set_data (G_OBJECT (widget), "gnc_radiobutton_index",
                            GINT_TO_POINTER (i));
-        gtk_widget_set_tooltip_text(widget, tip && *tip ? _(tip) : "");
         g_signal_connect(G_OBJECT(widget), "toggled",
                          G_CALLBACK(radiobutton_set_cb), &option);
         gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index bcd0cc365..38c9c0b67 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -526,7 +526,6 @@ operator>> (std::istream& iss, OptType& opt)
 }
 
 using GncMultichoiceOptionEntry = std::tuple<const std::string,
-                                             const std::string,
                                              const std::string,
                                              GncOptionMultichoiceKeyType>;
 using GncMultichoiceOptionIndexVec = std::vector<std::size_t>;
@@ -535,9 +534,8 @@ using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
 /** Multichoice options have a vector of valid options
  * (GncMultichoiceOptionChoices) and validate the selection as being one of
  * those values. The value is the index of the selected item in the vector. The
- * tuple contains three strings, a key, a display
- * name and a brief description for the tooltip. Both name and description
- * should be localized at the point of use. 
+ * tuple contains three strings, a key, and a display
+ * name, which * should be localized at the point of use.
  *
  *
  */
@@ -726,15 +724,11 @@ public:
     {
         return std::get<1>(m_choices.at(index)).c_str();
     }
-    const char* permissible_value_description(std::size_t index) const
-    {
-        return std::get<2>(m_choices.at(index)).c_str();
-    }
     void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
-    GncOptionMultichoiceKeyType get_keytype(unsigned i) const { return std::get<3>(m_choices.at(i)); }
+    GncOptionMultichoiceKeyType get_keytype(unsigned i) const { return std::get<2>(m_choices.at(i)); }
 private:
     std::size_t find_key (const std::string& key) const noexcept
     {
@@ -1144,10 +1138,6 @@ public:
     {
         return gnc_relative_date_display_string(m_period_set.at(index));
     }
-    const char* permissible_value_description(std::size_t index) const
-    {
-        return gnc_relative_date_description(m_period_set.at(index));
-    }
     void reset_default_value() {
         m_period = m_default_period;
         m_date = m_default_date;
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 6f708681b..4ab4a1ddb 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -361,20 +361,6 @@ GncOption::permissible_value_name(std::size_t index) const
                       }, *m_option);
 }
 
-const char*
-GncOption::permissible_value_description(std::size_t index) const
-{
-    return std::visit([index] (const auto& option) -> const char* {
-                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionMultichoiceValue>  ||
-                                        std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionDateValue>)
-                                           return option.permissible_value_description(index);
-                          else
-                              return "";
-                      }, *m_option);
-}
-
 GList*
 GncOption::account_type_list() const noexcept
 {
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 5edbc9404..dfc875bef 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -117,7 +117,6 @@ public:
     std::size_t permissible_value_index(const char* value) const;
     const char* permissible_value(std::size_t index) const;
     const char* permissible_value_name(std::size_t index) const;
-    const char* permissible_value_description(std::size_t index) const;
     GList* account_type_list() const noexcept;
     bool is_alternate() const noexcept;
     void set_alternate(bool) noexcept;
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index a5b6c427b..92e04cf5e 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -49,7 +49,6 @@ using GncOptionAccountList = std::vector<const Account*>;
 
 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
 using GncMultichoiceOptionEntry = std::tuple<const std::string,
-                                             const std::string,
                                              const std::string,
                                              GncOptionMultichoiceKeyType>;
 using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 95c145696..52be11160 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -419,8 +419,7 @@ gnc_option_test_book_destroy(QofBook* book)
             throw std::invalid_argument("Unsupported key type in multichoice option.");
         std::string key{scm_to_utf8_string(keyval)};
         std::string name{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 1))};
-        std::string desc{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 2))};
-        choices.push_back({std::move(key), std::move(name), std::move(desc), keytype});
+        choices.push_back({std::move(key), std::move(name), keytype});
     }
     $1 = &choices;
  }
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 4acf907ba..849a06e47 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -90,9 +90,8 @@
         (retval '()))
     (do ((i 0 (1+ i))) ((>= i num-values))
       (let ((value (GncOption-permissible-value option i))
-            (name (GncOption-permissible-value-name option i))
-            (desc (GncOption-permissible-value-description option i)))
-        (set! retval (cons retval (vector value name desc)))))
+            (name (GncOption-permissible-value-name option i)))
+        (set! retval (cons retval (vector value name)))))
     retval))
 
 (define-public (gnc:new-options)
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index d715c268e..379617e13 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -959,10 +959,10 @@ protected:
                  {"foo", "bar", "baz",
                   "Phony Option", "plugh",
                   {
-                      {"plugh", "xyzzy", "thud", KT::STRING},
-                      {"waldo", "pepper", "salt", KT::STRING},
-                      {"pork", "sausage", "links", KT::STRING},
-                      {"corge", "grault", "garply", KT::STRING}
+                      {"plugh", "xyzzy", KT::STRING},
+                      {"waldo", "pepper", KT::STRING},
+                      {"pork", "sausage", KT::STRING},
+                      {"corge", "grault", KT::STRING}
                   }}} {}
     GncOption m_option;
 };
@@ -1003,15 +1003,11 @@ TEST_F(GncMultichoiceOption, test_permissible_value_stuff)
             EXPECT_EQ(3U, m_option.permissible_value_index("corge"));
             EXPECT_STREQ("waldo", m_option.permissible_value(1));
             EXPECT_STREQ("sausage", m_option.permissible_value_name(2));
-            EXPECT_STREQ("thud",
-                         m_option.permissible_value_description(0));
         });
     EXPECT_THROW({ auto result = m_option.permissible_value(7); },
                  std::out_of_range);
     EXPECT_THROW({ auto result = m_option.permissible_value_name(9); },
         std::out_of_range);
-    EXPECT_THROW({ auto result = m_option.permissible_value_description(4); },
-        std::out_of_range);
     EXPECT_EQ(std::numeric_limits<std::size_t>::max(),
               m_option.permissible_value_index("xyzzy"));
 }
@@ -1059,10 +1055,10 @@ protected:
             "foo", "bar", "baz", "Phony Option",
             GncMultichoiceOptionIndexVec{0, 2},
             {
-                {"plugh", "xyzzy", "thud", KT::STRING},
-                {"waldo", "pepper", "salt", KT::STRING},
-                {"pork", "sausage", "links", KT::STRING},
-                {"corge", "grault", "garply", KT::STRING}
+                {"plugh", "xyzzy", KT::STRING},
+                {"waldo", "pepper", KT::STRING},
+                {"pork", "sausage", KT::STRING},
+                {"corge", "grault", KT::STRING}
             }}} {}
     GncOption m_option;
 };
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index bae79b04a..ce0017646 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -178,10 +178,10 @@ using KT = GncOptionMultichoiceKeyType;
 TEST_F(GncOptionDBTest, test_register_multichoice_option)
 {
     GncMultichoiceOptionChoices choices{
-        { "plugh", "xyzzy", "thud", KT::STRING},
-        { "waldo", "pepper", "salt", KT::STRING},
-        { "pork", "sausage", "links", KT::STRING},
-        { "corge", "grault", "garply", KT::STRING}};
+        { "plugh", "xyzzy", KT::STRING},
+        { "waldo", "pepper", KT::STRING},
+        { "pork", "sausage", KT::STRING},
+        { "corge", "grault", KT::STRING}};
     gnc_register_multichoice_option(m_db, "foo", "bar", "baz",
                                     "Phony Option", "waldo",
                                     std::move(choices));

commit 5a2ba091a3ef5f2f88ca20470232a0f1bba915bf
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jul 12 13:09:25 2021 -0700

    Fix compilation error from latest rebase.

diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp
index 454f74fab..df59ee33f 100644
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -1788,9 +1788,6 @@ static gchar *generate_statusbar_lastmodified_message()
             g_free(filepath);
             g_object_unref (info);
             g_object_unref (file);
-#else
-            return nullptr;
-#endif
         }
         // If the URI is not a file but a database, we can maybe also show
         // something useful, but I have no idea how to obtain this information.

commit 7c6ecafd610ebe263131e538f7eabc06eacffdbd
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Jul 11 15:03:13 2021 -0700

    Rewrite options.scm to wrap options.hpp functions where needed.

diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 2188c4eaa..4acf907ba 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -25,1831 +25,89 @@
 (use-modules (gnucash core-utils))
 (use-modules (gnucash engine))
 (use-modules (sw_app_utils))
-(use-modules (gnucash app-utils date-utilities))
 (use-modules (gnucash utilities))
 (use-modules (srfi srfi-1))
 (use-modules (ice-9 regex))
+(use-modules (ice-9 format))
+(use-modules (ice-9 pretty-print))
 
-(export gnc:color->html)
-(export gnc:color-option->hex-string)
-(export gnc:color-option->html)
-(export gnc:currency-accounting-option-get-curr-doc-string)
-(export gnc:currency-accounting-option-get-default-curr)
-(export gnc:currency-accounting-option-get-default-policy)
-(export gnc:currency-accounting-option-get-gain-loss-account-doc-string)
-(export gnc:currency-accounting-option-get-policy-doc-string)
-(export gnc:currency-accounting-option-selected-currency)
-(export gnc:currency-accounting-option-selected-gain-loss-account)
-(export gnc:currency-accounting-option-selected-method)
-(export gnc:currency-accounting-option-selected-policy)
-(export gnc:date-option-absolute-time)
-(export gnc:date-option-get-subtype)
-(export gnc:date-option-relative-time)
-(export gnc:date-option-show-time?)
-(export gnc:date-option-value-type)
-(export gnc:dateformat-get-format)
-(export gnc:generate-restore-forms)
-(export gnc:get-rd-option-data-rd-list)
-(export gnc:get-rd-option-data-show-time)
-(export gnc:get-rd-option-data-subtype)
-(export gnc:lookup-option)
-(export gnc:make-account-list-limited-option)
-(export gnc:make-account-list-option)
-(export gnc:make-account-sel-limited-option)
-(export gnc:make-account-sel-option)
-(export gnc:make-budget-option)
-(export gnc:make-color-option)
-(export gnc:make-commodity-option)
-(export gnc:make-complex-boolean-option)
-(export gnc:make-currency-option)
-(export gnc:make-date-option)
-(export gnc:make-dateformat-option)
-(export gnc:make-font-option)
-(export gnc:make-internal-option)
-(export gnc:make-list-option)
-(export gnc:make-multichoice-callback-option)
-(export gnc:make-multichoice-option)
-(export gnc:make-number-plot-size-option)
-(export gnc:make-number-range-option)
-(export gnc:make-option)
-(export gnc:make-pixmap-option)
-(export gnc:make-query-option)
-(export gnc:make-radiobutton-callback-option)
-(export gnc:make-radiobutton-option)
-(export gnc:make-simple-boolean-option)
-(export gnc:make-string-option)
-(export gnc:make-text-option)
-(export gnc:multichoice-list-lookup)
-(export gnc:new-options)
-(export gnc:option-data)
-(export gnc:option-data-fns)
-(export gnc:option-default-getter)
-(export gnc:option-default-value)
-(export gnc:option-documentation)
-(export gnc:option-generate-restore-form)
-(export gnc:option-get-value)
-(export gnc:option-getter)
-(export gnc:option-index-get-name)
-(export gnc:option-index-get-value)
-(export gnc:option-kvp->scm)
-(export gnc:option-make-internal!)
-(export gnc:option-name)
-(export gnc:option-number-of-indices)
-(export gnc:option-scm->kvp)
-(export gnc:option-section)
-(export gnc:option-set-changed-callback)
-(export gnc:option-set-default-value)
-(export gnc:option-set-value)
-(export gnc:option-setter)
-(export gnc:option-sort-tag)
-(export gnc:option-strings-getter)
-(export gnc:option-type)
-(export gnc:option-value)
-(export gnc:option-value-get-index)
-(export gnc:option-value-validator)
-(export gnc:option-widget-changed-proc)
-(export gnc:options-clear-changes)
-(export gnc:options-copy-values)
-(export gnc:options-for-each)
-(export gnc:options-for-each-general)
-(export gnc:options-get-default-section)
-(export gnc:options-kvp->scm)
-(export gnc:options-make-date-interval!)
-(export gnc:options-make-end-date!)
-(export gnc:options-register-c-callback)
-(export gnc:options-register-callback)
-(export gnc:options-run-callbacks)
-(export gnc:options-scm->kvp)
-(export gnc:options-set-default-section)
-(export gnc:options-touch)
-(export gnc:options-unregister-callback-id)
-(export gnc:plot-size-option-value)
-(export gnc:plot-size-option-value-type)
-(export gnc:register-option)
-(export gnc:restore-form-generator)
-(export gnc:send-options)
-(export gnc:set-option-kvp->scm)
-(export gnc:set-option-scm->kvp)
-(export gnc:unregister-option)
-(export gnc:value->string)
 
-(export gnc:*option-name-trading-accounts*)
-(export gnc:*option-name-book-currency*)
-(export gnc:*option-section-accounts*)
-(export gnc:*option-name-default-gains-policy*)
-(export gnc:*option-name-default-gain-loss-account*)
-
-(define gnc:*option-section-accounts* OPTION-SECTION-ACCOUNTS)
-(define gnc:*option-name-trading-accounts* OPTION-NAME-TRADING-ACCOUNTS)
-
-(define (gnc:option-get-value book category key)
-  (define acc (if (pair? key) cons list))
-  (qof-book-get-option book (acc category key)))
-
-(define (rpterror-earlier type newoption fallback)
-  ;; Translators: the 3 ~a below refer to (1) option type (2) unknown
-  ;; new option name, (3) fallback option name. The order is
-  ;; important, and must not be changed.
-  (let* ((template (N_ "This report was saved using a later version of \
-GnuCash. One of the newer ~a options '~a' is not available, fallback to \
-the option '~a'."))
-         (console-msg (format #f template type newoption fallback))
-         (ui-msg (format #f (G_ template) type newoption fallback)))
-    (gnc:gui-warn console-msg ui-msg)))
-
-(define (gnc:make-option
-         ;; The category of this option
-         section
-         name
-         ;; The sort-tag determines the relative ordering of options in
-         ;; this category. It is used by the gui for display.
-         sort-tag
-         type
-         documentation-string
-         getter
-         ;; The setter is responsible for ensuring that the value is valid.
-         setter
-         default-getter
-         ;; Restore form generator should generate an ascii representation
-         ;; of a function taking one argument. The argument will be an
-         ;; option. The function should restore the option to the original
-         ;; value.
-         generate-restore-form
-         ;; the scm->kvp and kvp->scm functions should save and load
-         ;; the option to the book.  The arguments to these function will be
-         ;; a book and a base key-path list for this option.
-         scm->kvp
-         kvp->scm
-         ;; Validation func should accept a value and return (#t value)
-         ;; on success, and (#f "failure-message") on failure. If #t,
-         ;; the supplied value will be used by the gui to set the option.
-         value-validator
-         ;;; free-form storage depending on type.
-         option-data 
-         ;; If this is a "multiple choice" type of option,
-         ;; this should be a vector of the following five functions:
-         ;; 
-         ;; Function 1: taking no arguments, giving the number of choices
-         ;;
-         ;; Function 2: taking one argument, a non-negative integer, that
-         ;; returns the scheme value (usually a symbol) matching the
-         ;; nth choice
-         ;;
-         ;; Function 3: taking one argument, a non-negative integer,
-         ;; that returns the string matching the nth choice
-         ;;
-         ;; Function 4: #f, this was the individual tool tip and not used now
-         ;;
-         ;; Function 5: giving a possible value and returning the index
-         ;; if an option doesn't use these,  this should just be a #f
-         option-data-fns
-         ;; This function should return a list of all the strings
-         ;; in the option other than the section, name, (define
-         ;; (list-lookup list item) and documentation-string that
-         ;; might be displayed to the user (and thus should be
-         ;; translated).
-         strings-getter
-         ;; This function will be called when the GUI representation
-         ;; of the option is changed.  This will normally occur before
-         ;; the setter is called, because setters are only called when
-         ;; the user selects "OK" or "Apply".  Therefore, this
-         ;; callback shouldn't be used to make changes to the actual
-         ;; options database.
-         option-widget-changed-proc)
-  (let ((changed-callback #f))
-    (vector section
-            name
-            sort-tag
-            type
-            documentation-string
-            getter            
-            (lambda args
-              (apply setter args)
-              (if changed-callback (changed-callback)))
-            default-getter
-            generate-restore-form
-            scm->kvp
-            kvp->scm
-            value-validator
-            option-data
-            option-data-fns
-            (lambda (callback) (set! changed-callback callback))
-            strings-getter
-            option-widget-changed-proc)))
-
-(define (gnc:option-section option)
-  (vector-ref option 0))
-(define (gnc:option-name option)
-  (vector-ref option 1))
-(define (gnc:option-sort-tag option)
-  (vector-ref option 2))
-(define (gnc:option-type option)
-  (vector-ref option 3))
-(define (gnc:option-documentation option)
-  (vector-ref option 4))
-(define (gnc:option-getter option)
-  (vector-ref option 5))
-(define (gnc:option-setter option)
-  (vector-ref option 6))
-(define (gnc:option-default-getter option)
-  (vector-ref option 7))
-(define (gnc:option-generate-restore-form option)
-  (vector-ref option 8))
-(define (gnc:option-scm->kvp option)
-  (vector-ref option 9))
-(define (gnc:set-option-scm->kvp option v)
-  (vector-set! option 9 v))
-(define (gnc:option-kvp->scm option)
-  (vector-ref option 10))
-(define (gnc:set-option-kvp->scm option v)
-  (vector-set! option 10 v))
-(define (gnc:option-value-validator option)  
-  (vector-ref option 11))
-(define (gnc:option-data option)
-  (vector-ref option 12))
-(define (gnc:option-data-fns option)
-  (vector-ref option 13))
-
-(define (gnc:option-set-changed-callback option callback)
- (let ((cb-setter (vector-ref option 14)))
-    (cb-setter callback)))
-(define (gnc:option-strings-getter option)
-  (vector-ref option 15))
-(define (gnc:option-widget-changed-proc option)
-  (vector-ref option 16))
-
-(define (gnc:option-value option)
-  (let ((getter (gnc:option-getter option)))
-    (getter)))
-
-(define (gnc:option-set-value option value)
-  (let ((setter (gnc:option-setter option)))
-    (setter value)))
-
-(define (gnc:option-index-get-name option index)
-  (let* ((option-data-fns (gnc:option-data-fns option))
-         (name-fn (vector-ref option-data-fns 2)))
-    (name-fn index)))
-
-(define (gnc:option-index-get-value option index)
-  (let* ((option-data-fns (gnc:option-data-fns option))
-         (name-fn (vector-ref option-data-fns 1)))
-    (name-fn index)))
-
-(define (gnc:option-value-get-index option value)
-  (let* ((option-data-fns (gnc:option-data-fns option))
-         (name-fn (vector-ref option-data-fns 4)))
-    (name-fn value)))
-
-(define (gnc:option-number-of-indices option)
-  (let* ((option-data-fns (gnc:option-data-fns option))
-         (name-fn (vector-ref option-data-fns 0)))
-    (name-fn)))
-
-(define (gnc:option-default-value option)
-  (let ((getter (gnc:option-default-getter option)))
-    (getter)))
-
-;; Attention: this function can only be used with restrictions
-;; - only during option generation, not in arbitrary code
-;; - only for option types for which no conversion is required
-;;   between default-value and value. In the various gnc:make-option
-;;   functions below this is ok when
-;;   1. there's (value default-value) in the let* call
-;;   2. default-getter is set to (lambda() default-value)
-(define (gnc:option-set-default-value option default-value)
-  (vector-set! option 7 (lambda() default-value))
-  (gnc:option-set-value option default-value))
-
-
-(define (gnc:restore-form-generator value->string)
-  (lambda ()
-    (string-append "(lambda (o) (if o (gnc:option-set-value o "
-                   (value->string) ")))")))
-
-(define (gnc:value->string value)
+(define-public (gnc:value->string value)
   (format #f "~s" value))
 
-(define (gnc:make-string-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value)
-  (let* ((value default-value)
-         (value->string (lambda () (gnc:value->string value))))
-    (gnc:make-option
-     section name sort-tag 'string documentation-string
-     (lambda () value)
-     (lambda (x) (set! value x))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b value p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-         (if (and v (string? v))
-             (set! value v))))
-     (lambda (x)
-       (cond ((string? x)(list #t x))
-             (else (list #f "string-option: not a string"))))
-     #f #f #f #f)))
-
-(define (gnc:make-text-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value)
-  (let* ((value default-value)
-         (value->string (lambda () (gnc:value->string value))))
-    (gnc:make-option
-     section name sort-tag 'text documentation-string
-     (lambda () value)
-     (lambda (x) (set! value x))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b value p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-         (if (and v (string? v))
-             (set! value v))))
-     (lambda (x)
-       (cond ((string? x)(list #t x))
-             (else (list #f "text-option: not a string"))))
-     #f #f #f #f)))
-
-;;; font options store fonts as strings a la the X Logical
-;;; Font Description. You should always provide a default
-;;; value, as currently there seems to be no way to go from
-;;; an actual font to a logical font description, and thus
-;;; there is no way for the gui to pick a default value.
-
-(define (gnc:make-font-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value)
-  (let* ((value default-value)
-         (value->string (lambda () (gnc:value->string value))))
-    (gnc:make-option
-     section
-     name
-     sort-tag
-     'font
-     documentation-string
-     (lambda () value)
-     (lambda (x) (set! value x))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)     
-     (lambda (b p) (qof-book-set-option b value p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-         (if (and v (string? v))
-             (set! value v))))
-     (lambda (x)
-       (cond ((string? x)(list #t x))
-             (else (list #f "font-option: not a string"))))
-     #f #f #f #f)))
-
-;; currency options use a specialized widget for entering currencies
-;; in the GUI implementation.
-(define (gnc:make-currency-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value)
-
-  (define (currency->scm currency)
-    (if (string? currency)
-        currency
-        (gnc-commodity-get-mnemonic currency)))
-
-  (define (scm->currency currency)
-    (if (string? currency)
-        (gnc-commodity-table-lookup
-         (gnc-commodity-table-get-table (gnc-get-current-book))
-         GNC_COMMODITY_NS_CURRENCY currency)
-        currency))
-
-   (let* ((value (currency->scm default-value))
-          (value->string (lambda () (gnc:value->string value))))
-     (gnc:make-option
-      section name sort-tag 'currency documentation-string
-      (lambda ()  (scm->currency value))
-      (lambda (x) (set! value (currency->scm x)))
-      (lambda ()  (scm->currency default-value))
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) (qof-book-set-option b value p))
-      (lambda (b p)
-        (let ((v (qof-book-get-option b p)))
-          (if (and v (string? v))
-              (set! value v))))
-      (lambda (x) (list #t x))
-      #f #f #f #f)))
-
-;; budget option
-;; TODO: need to double-check this proc (dates back to r11545 or eariler)
-;;
-;; Always takes/returns a budget
-;; Stores the GUID in the KVP
-;;
-(define (gnc:make-budget-option
-         section
-         name
-         sort-tag
-         documentation-string)
-
-  (let* ((initial-budget (gnc-budget-get-default (gnc-get-current-book)))
-	 (selection-budget initial-budget)
-         )
-
-    (gnc:make-option
-     section 
-     name 
-     sort-tag 
-     'budget 
-     documentation-string
-
-     ;; getter -- Return a budget pointer
-     (lambda () 
-       selection-budget)
-
-     ;; setter -- takes a budget
-     (lambda (x)
-       (set! selection-budget x))
-
-     ;; default-getter
-     ;; Default now is #f so saving is independent of book-level default
-     (lambda ()
-       #f)
-
-     ;; generate-restore-form
-     ;; "return 'ascii represention of a function'
-     ;; that will set the option passed as its lone parameter
-     ;; to the value it was when the picker was first displayed"
-     ;;
-     ;; *This* is used to restore reports, not the KVP -- and is stored as text
-     ;; This does not run in closure with direct access to the option's
-     ;; internal variables, so the setter generally gets used
-     (lambda () 
-       (string-append
-	"(lambda (option) "
-	"(if option ((gnc:option-setter option) "
-	"(gnc-budget-lookup "
-	(gnc:value->string (gncBudgetGetGUID selection-budget))
-	" (gnc-get-current-book)))))"))
-
-     ;; scm->kvp -- commit the change
-     ;; b -- book;  p -- key-path
-     (lambda (b p) 
-       (qof-book-set-option 
-	b (gncBudgetGetGUID selection-budget) p))
-
-     ;; kvp->scm -- get the stored value
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-         (if (and v (string? v))
-	     (begin 
-	       (set! selection-budget (gnc-budget-lookup v (gnc-get-current-book)))))))
-
-     ;; value-validator -- returns (#t value) or (#f "failure message")
-     ;; As no user-generated input, this legacy hard-wire is probably ok
-     (lambda (x) 
-       (list #t x))
-
-     ;; option-data
-     #f
-     
-     ;; option-data-fns -- used for multi-pick (this isn't one), or #f
-     ;; Vector of five functions
-     ;; 1) ()      => number of choices
-     ;; 2) (n)     => key for the nth choice
-     ;; 3) (n)     => string for the nth choice
-     ;; 4) (n)     => description for the nth choice
-     ;; 5) (key)   => n (assuming this is the reverse key lookup)
-     #f
-
-     ;; strings-getter -- list of all translatable strings in the option
-     #f
-
-     ;; options-widget-changed-proc -- callback for what it sounds like
-     #f
-     
-     ))) ;; completes gnc:make-budget-option
-
-
-;; commodity options use a specialized widget for entering commodities
-;; in the GUI implementation.
-(define (gnc:make-commodity-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value)
-
-  (define (commodity->scm commodity)
-    (if (string? commodity)
-        (list 'commodity-scm
-              GNC_COMMODITY_NS_CURRENCY
-              commodity)
-        (list 'commodity-scm
-              (gnc-commodity-get-namespace commodity)
-              (gnc-commodity-get-mnemonic commodity))))
-
-  (define (scm->commodity scm)
-    (gnc-commodity-table-lookup
-     (gnc-commodity-table-get-table (gnc-get-current-book))
-     (cadr scm) (caddr scm)))
-
-   (let* ((value (commodity->scm default-value))
-          (value->string (lambda ()
-                           (string-append "'" (gnc:value->string value)))))
-     (gnc:make-option
-      section name sort-tag 'commodity documentation-string
-      (lambda () (scm->commodity value))
-      (lambda (x) (if (and (pair? x) (eqv? (car x) 'commodity-scm))
-                      (set! value x)
-                      (set! value (commodity->scm x))))
-      (lambda () default-value)
-      (gnc:restore-form-generator value->string)
-      (lambda (b p) 
-        (qof-book-set-option b (cadr value) (append p '("ns")))
-        (qof-book-set-option b (caddr value) (append p '("monic"))))
-      (lambda (b p)
-        (let ((ns (qof-book-get-option b (append p '("ns"))))
-              (monic (qof-book-get-option b (append p '("monic")))))
-          (if (and ns monic (string? ns) (string? monic))
-              (set! value (list 'commodity-scm ns monic)))))
-      (lambda (x) (list #t x))
-      #f #f #f #f)))
-
-
-(define (gnc:make-simple-boolean-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value)
-  (gnc:make-complex-boolean-option section
-                                   name
-                                   sort-tag
-                                   documentation-string
-                                   default-value
-                                   #f 
-                                   #f))
-
-;; Complex boolean options are the same as simple boolean options (see
-;; above), with the addition of two function arguments. (If both of
-;; them are #f, you have exactly a simple-boolean-option.) Both
-;; functions should expect one boolean argument.  When the option's
-;; value is changed, the function option-widget-changed-cb will be
-;; called with the new option value at the time that the GUI widget
-;; representing the option is changed, and the function
-;; setter-function-called-cb will be called when the option's setter
-;; is called (that is, when the user selects "OK" or "Apply").
-
-;; The option-widget-changed-cb is tested for procedurehood before
-;; it is called, so it is not validated to be a procedure here.
-;; However, since there could be an option-widget-changed-cb but not
-;; a setter-function-called-cb, the procedurehood of the
-;; setter-function-called-cb is checked here.
-(define (gnc:make-complex-boolean-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         setter-function-called-cb
-         option-widget-changed-cb)
-  (let* ((value default-value)
-         (value->string (lambda () (gnc:value->string value))))
-    (gnc:make-option
-     section name sort-tag 'boolean documentation-string
-     (lambda () value)
-     (lambda (x) (set! value x)
-             (if (procedure? setter-function-called-cb)
-                 (setter-function-called-cb x)))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b
-		    ;; As no boolean KvpValue exists, as a workaround
-		    ;; we store the string "t" for TRUE and "f" for
-		    ;; FALSE in a string KvpValue.
-                    (if value "t" "f") 
-                    p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-	 ;; As no boolean KvpValue exists, as a workaround we store
-	 ;; the string "t" for TRUE and "f" for FALSE.
-         (cond ((equal? v "t") (set! v #t))
-               ((equal? v "f") (set! v #f)))
-         (if (and v (boolean? v) (not (equal? v default-value)))
-             (set! value v))))
-     (lambda (x)
-       (if (boolean? x)
-           (list #t x)
-           (list #f "boolean-option: not a boolean")))
-     #f #f #f (and option-widget-changed-cb
-                   (lambda (x) (option-widget-changed-cb x))))))
-
-
-(define (gnc:make-pixmap-option 
-         section name sort-tag doc-string
-         default-value)
-  (let* ((value default-value))
-    (gnc:make-option
-     section name sort-tag 'pixmap doc-string
-     (lambda ()  value)
-     (lambda (x) (set! value x))
-     (lambda () default-value)
-     (gnc:restore-form-generator  (lambda () (gnc:value->string value)))
-     #f
-     #f
-     (lambda (x)
-       (if (string? x)
-           (begin 
-             (list #t x))
-           (begin 
-             (list #f "pixmap-option: not a string"))))
-     #f #f #f #f)))
-
-;; show-time is boolean
-;; subtype should be one of 'relative 'absolute or 'both
-;; if subtype is 'absolute then relative-date-list should be #f
-;; relative-date-list should be the list of relative dates permitted
-;; gnc:all-relative-dates contains a list of all relative dates.
-
-(define (gnc:make-date-option
-         section
-         name
-         sort-tag 
-         documentation-string
-         default-getter
-         show-time
-         subtype
-         relative-date-list)
-  (define (date-legal date)
-    (and (pair? date)
-         (or
-          (and (eq? 'relative (car date)) (symbol? (cdr date)))
-          (and (eq? 'absolute (car date))
-               (or (and (pair? (cdr date))   ; we can still accept
-                        (exact? (cadr date)) ; old-style timepairs
-                        (exact? (cddr date)))
-                   (and (number? (cdr date))
-                        (exact? (cdr date))))))))
-  (define (maybe-convert-to-time64 date)
-    ;; compatibility shim. this is triggered only when date is type
-    ;; (cons 'absolute (cons sec nsec)) - we'll convert to
-    ;; (cons 'absolute sec). this shim must always be kept for gnucash
-    ;; to reload saved reports, or reload reports launched at startup,
-    ;; which had been saved as timepairs.
-    (if (pair? (cdr date))
-        (cons (car date) (cadr date))
-        date))
-  (define (list-lookup full-list item)
-    (or (list-index (lambda (i) (eq? i item)) full-list)
-        (begin
-          (rpterror-earlier "date" item (car full-list))
-          0)))
-  (let* ((value (default-getter))
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name sort-tag 'date documentation-string
-     (lambda () value)
-     (lambda (date)
-       (if (date-legal date)
-           (set! value (maybe-convert-to-time64 date))
-           (gnc:error "Illegal date value set:" date)))
-     default-getter
-     (gnc:restore-form-generator value->string)
-     (lambda (b p)
-       (qof-book-set-option b (symbol->string (car value))
-                                       (append p '("type")))
-       (qof-book-set-option b
-                                       (if (symbol? (cdr value))
-                                           (symbol->string (cdr value))
-                                           (cdr value))
-                                       (append p '("value"))))
-     (lambda (b p)
-       (let ((t (qof-book-get-option b (append p '("type"))))
-             (v (qof-book-get-option b (append p '("value")))))
-         (if (and t v (string? t))
-             (set! value (cons (string->symbol t)
-                               (if (string? v) (string->symbol v) v))))))
-     (lambda (date)
-       (if (date-legal date)
-           (list #t date)
-           (list #f "date-option: illegal date")))
-     (vector subtype show-time relative-date-list) 
-     (vector (lambda () (length relative-date-list))
-             (lambda (x) (list-ref relative-date-list x))
-             (lambda (x) (gnc:get-relative-date-string
-                          (list-ref relative-date-list x)))
-             (lambda (x) (gnc:get-relative-date-desc
-                          (list-ref relative-date-list x)))
-             (lambda (x) (list-lookup relative-date-list x)))
-     #f
-     #f)))
-
-(define (gnc:get-rd-option-data-subtype option-data)
-  (vector-ref option-data 0))
-
-(define (gnc:get-rd-option-data-show-time option-data)
-  (vector-ref option-data 1))
-
-(define (gnc:get-rd-option-data-rd-list option-data)
-  (vector-ref option-data 2))
-
-(define (gnc:date-option-get-subtype option)
-  (if (eq? (gnc:option-type option) 'date)
-      (gnc:get-rd-option-data-subtype (gnc:option-data option))
-      (gnc:error "Not a date option")))
-
-(define (gnc:date-option-show-time? option)
-  (if (eq? (gnc:option-type option) 'date)
-      (gnc:get-rd-option-data-show-time (gnc:option-data option))
-      (gnc:error "Not a date option")))
-
-(define (gnc:date-option-value-type option-value)
-  (car option-value))
-
-(define (gnc:date-option-absolute-time option-value)
-  (if (eq? (car option-value) 'absolute)
-      (cdr option-value)
-      (gnc:get-absolute-from-relative-date (cdr option-value))))
-
-(define (gnc:date-option-relative-time option-value)
-  (if (eq? (car option-value) 'absolute)
-      #f
-      (cdr option-value)))
-
-;; Just like gnc:make-account-list-limited-option except it
-;; does not limit the types of accounts that are available
-;; to the user.
-(define (gnc:make-account-list-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-getter
-         value-validator
-         multiple-selection)
-
-  (gnc:make-account-list-limited-option
-   section name sort-tag documentation-string
-   default-getter value-validator multiple-selection '()))
-
-;; account-list options use the option-data as a pair; the car is
-;; a boolean value, the cdr is a list of account-types. If the boolean is
-;; true, the gui should allow the user to select multiple accounts.
-;; If the cdr is an empty list, then all account types are shown.
-;; Internally, values are always a list of guids. Externally, both
-;; guids and account pointers may be used to set the value of the
-;; option. The option always returns a list of account pointers.
-(define (gnc:make-account-list-limited-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-getter
-         value-validator
-         multiple-selection
-         acct-type-list)
-
-  (define (convert-to-guid item)
-    (if (string? item)
-        item
-        (gncAccountGetGUID item)))
-
-  (define (convert-to-account item)
-    (if (string? item)
-        (xaccAccountLookup item (gnc-get-current-book))
-        item))
-
-  (let* ((option (map convert-to-guid (default-getter)))
-         (option-set #f)
-         (getter (lambda () (map convert-to-account
-                                 (if option-set
-                                     option
-                                     (default-getter)))))
-         (value->string (lambda ()
-                          (string-append
-                           "'" (gnc:value->string (if option-set option #f)))))
-         (validator
-          (if (not value-validator)
-              (lambda (account-list) (list #t account-list))
-              (lambda (account-list)
-                (value-validator (map convert-to-account account-list))))))
-    (gnc:make-option
-     section name sort-tag 'account-list documentation-string getter
-     (lambda (account-list)
-       (if (or (not account-list) (null? account-list)) 
-           (set! account-list (default-getter)))
-       (set! account-list
-             (filter (lambda (x) (if (string? x)
-                                     (xaccAccountLookup
-                                      x (gnc-get-current-book))
-                                     x)) account-list))
-       (let* ((result (validator account-list))
-              (valid (car result))
-              (value (cadr result)))
-         (if valid
-             (begin
-               (set! option (map convert-to-guid value))
-               (set! option-set #t))
-             (gnc:error "Illegal account list value set"))))
-     (lambda () (map convert-to-account (default-getter)))
-     (gnc:restore-form-generator value->string)
-     (lambda (b p)
-       (when option-set
-         (qof-book-set-option b (length option) (append p '("len")))
-         (let loop ((option option) (idx 0))
-           (unless (null? option)
-             (qof-book-set-option
-              b (car option) (append p (list (format #f "acc~a" idx))))
-             (loop (cdr option) (1+ idx))))))
-     (lambda (b p)
-       (let ((len (qof-book-get-option b (append p '("len")))))
-         (when (and len (integer? len))
-           (set! option
-             (map
-              (lambda (idx)
-                (qof-book-get-option b (append p (list (format #f "acc~a" idx)))))
-              (iota len)))
-           (set! option-set #t))))
-     validator
-     (cons multiple-selection acct-type-list) #f #f #f)))
-
-;; Just like gnc:make-account-sel-limited-option except it
-;; does not limit the types of accounts that are available
-;; to the user.
-(define (gnc:make-account-sel-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-getter
-         value-validator)
-
-  (gnc:make-account-sel-limited-option
-   section name sort-tag documentation-string
-   default-getter value-validator '()))
-
-;; account-sel options use the option-data as a pair; the car is
-;; ignored, the cdr is a list of account-types. If the cdr is an empty
-;; list, then all account types are shown.  Internally, the value is
-;; always a guid.  Externally, both guids and account pointers may be
-;; used to set the value of the option. The option always returns the
-;; "current" account pointer.
-(define (gnc:make-account-sel-limited-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-getter
-         value-validator
-         acct-type-list)
-
-  (define (convert-to-guid item)
-    (if (string? item)
-        item
-        (gncAccountGetGUID item)))
-
-  (define (convert-to-account item)
-    (if (string? item)
-        (xaccAccountLookup item (gnc-get-current-book))
-        item))
-
-  (define (find-first-account)
-    (define (find-first account-list)
-      (if (null? account-list)
-          '()
-          (let* ((this-account (car account-list))
-                 (account-type (xaccAccountGetType this-account)))
-            (if (if (null? acct-type-list)
-                    #t
-                    (member account-type acct-type-list))
-                this-account
-                (find-first (cdr account-list))))))
-
-    (let* ((current-root (gnc-get-current-root-account))
-           (account-list (gnc-account-get-descendants-sorted current-root)))
-      (find-first account-list)))
-  
-  (define (get-default)
-    (if default-getter
-        (default-getter)
-        (find-first-account)))
-
-  (let* ((option (convert-to-guid (get-default)))
-         (option-set #f)
-         (getter (lambda () (convert-to-account
-                             (if option-set
-                                 option
-                                 (get-default)))))
-         (value->string (lambda ()
-                          (string-append
-                            (gnc:value->string (if option-set option #f)))))
-         (validator
-          (if (not value-validator)
-              (lambda (account) (list #t account))
-              (lambda (account)
-                (value-validator (convert-to-account account))))))
-    (gnc:make-option
-     section name sort-tag 'account-sel documentation-string getter
-     (lambda (account)
-       (if (or (not account) (null? account)) (set! account (get-default)))
-       (set! account (convert-to-account account))
-       (let* ((result (validator account))
-              (valid (car result))
-              (value (cadr result)))
-         (if valid
-             (begin
-               (set! option (convert-to-guid value))
-               (set! option-set #t))
-             (gnc:error "Illegal account value set"))))
-     (lambda () (convert-to-account (get-default)))
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b option p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-         (if (and v (string? v))
-             (set! option v))))
-     validator
-     (cons #f acct-type-list) #f #f #f)))
-
-(define (gnc:multichoice-list-lookup full-lst item)
-  (or (list-index (lambda (i) (eq? (vector-ref i 0) item)) full-lst)
-      (begin
-        (rpterror-earlier "multichoice" item (car full-lst))
-        0)))
-
-(define (check-ok-values ok-values fn)
-  (for-each
-   (lambda (ok-value)
-     (when (> (vector-length ok-value) 2)
-       (issue-deprecation-warning
-        (format #f "~a: the tooltip in ~a is not supported anymore. Please remove." fn ok-value))))
-   ok-values))
-
-;; multichoice options use the option-data as a list of vectors.
-;; Each vector contains a permissible value (scheme symbol), a
-;; name, and a description string.
-(define (gnc:make-multichoice-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         ok-values)
-  (gnc:make-multichoice-callback-option section
-                                        name
-                                        sort-tag
-                                        documentation-string
-                                        default-value
-                                        ok-values
-                                        #f
-                                        #f))
-
-;; The multichoice-option with callback function is the same as the
-;; usual multichoice options (see above), with the addition of two
-;; function arguments. (If both of them are #f, you have exactly a
-;; multichoice-option.) Both functions should expect one argument.
-;; When the option's value is changed, the function
-;; option-widget-changed-cb will be called with the new option value
-;; at the time that the GUI widget representing the option is changed,
-;; and the function setter-function-called-cb will be called when the
-;; option's setter is called (that is, when the user selects "OK" or
-;; "Apply").
-(define (gnc:make-multichoice-callback-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         ok-values
-         setter-function-called-cb
-         option-widget-changed-cb)
-  (define (multichoice-legal val p-vals)
-    (cond ((null? p-vals) #f)
-          ((eq? val (vector-ref (car p-vals) 0)) #t)
-          (else (multichoice-legal val (cdr p-vals)))))
-
-  (define (multichoice-strings p-vals)
-    (if (null? p-vals)
-        '()
-        (cons (vector-ref (car p-vals) 1)
-              (multichoice-strings (cdr p-vals)))))
-
-  (check-ok-values ok-values "gnc:make-multichoice-[callback-]option")
-
-  (let* ((value default-value)
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name sort-tag 'multichoice documentation-string
-     (lambda () value)
-     (lambda (x)
-       (cond
-        ((and (equal? section "Display")
-              (equal? name "Parent account subtotals")
-              (equal? x 'canonically-tabbed))
-         (gnc:warn "canonically-tabbed obsolete. switching to 't")
-         (set! value 't))
-        ((not (multichoice-legal x ok-values))
-         (rpterror-earlier "multichoice" x default-value))
-        (else
-         (set! value x)
-         (if (procedure? setter-function-called-cb)
-             (setter-function-called-cb x)))))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b (symbol->string value) p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-         (if (and v (string? v))
-             (set! value (string->symbol v)))))
-     (lambda (x)
-       (if (multichoice-legal x ok-values)
-           (list #t x)
-           (list #f "multichoice-option: illegal choice")))
-     ok-values
-     (vector (lambda () (length ok-values))
-             (lambda (x) (vector-ref (list-ref ok-values x) 0))
-             (lambda (x) (vector-ref (list-ref ok-values x) 1))
-             #f                         ;old tooltip
-             (lambda (x)
-               (gnc:multichoice-list-lookup ok-values x)))
-     (lambda () (multichoice-strings ok-values)) 
-     (and option-widget-changed-cb
-          (lambda (x) (option-widget-changed-cb x))))))
-
-
-;; radiobutton options use the option-data as a list of vectors.
-;; Each vector contains a permissible value (scheme symbol), a
-;; name, and a description string.
-(define (gnc:make-radiobutton-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         ok-values)
-  (gnc:make-radiobutton-callback-option section
-                                        name
-                                        sort-tag
-                                        documentation-string
-                                        default-value
-                                        ok-values
-                                        #f
-                                        #f))
-
-;; The radiobutton-option with callback function is the same as the
-;; usual radiobutton options (see above), with the addition of two
-;; function arguments. (If both of them are #f, you have exactly a
-;; radiobutton-option.) Both functions should expect one argument.
-;; When the option's value is changed, the function
-;; option-widget-changed-cb will be called with the new option value
-;; at the time that the GUI widget representing the option is changed,
-;; and the function setter-function-called-cb will be called when the
-;; option's setter is called (that is, when the user selects "OK" or
-;; "Apply").
-(define (gnc:make-radiobutton-callback-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         ok-values
-         setter-function-called-cb
-         option-widget-changed-cb)
-  (define (radiobutton-legal val p-vals)
-    (cond ((null? p-vals) #f)
-          ((eq? val (vector-ref (car p-vals) 0)) #t)
-          (else (radiobutton-legal val (cdr p-vals)))))
-
-  (define (radiobutton-strings p-vals)
-    (if (null? p-vals)
-        '()
-        (cons (vector-ref (car p-vals) 1)
-              (radiobutton-strings (cdr p-vals)))))
-
-  (let* ((value default-value)
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name sort-tag 'radiobutton documentation-string
-     (lambda () value)
-     (lambda (x)
-       (if (radiobutton-legal x ok-values)
-           (begin
-             (set! value x)
-             (if (procedure? setter-function-called-cb)
-                 (setter-function-called-cb x)))
-           (rpterror-earlier "radiobutton" x default-value)))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b (symbol->string value) p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-         (if (and v (string? v))
-             (set! value (string->symbol v)))))
-     (lambda (x)
-       (if (radiobutton-legal x ok-values)
-           (list #t x)
-           (list #f "radiobutton-option: illegal choice")))
-     ok-values
-     (vector (lambda () (length ok-values))
-             (lambda (x) (vector-ref (list-ref ok-values x) 0))
-             (lambda (x) (vector-ref (list-ref ok-values x) 1))
-             #f                         ;old tooltip
-             (lambda (x)
-               (gnc:multichoice-list-lookup ok-values x)))
-     (lambda () (radiobutton-strings ok-values)) 
-     (and option-widget-changed-cb
-          (lambda (x) (option-widget-changed-cb x))))))
-
-
-;; list options use the option-data in the same way as multichoice
-;; options. List options allow the user to select more than one option.
-(define (gnc:make-list-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         ok-values)
-
-  (define (legal-value? value legal-values)
-    (cond ((null? legal-values) #f)
-          ((eq? value (vector-ref (car legal-values) 0)) #t)
-          (else (legal-value? value (cdr legal-values)))))
-
-  (define (list-legal values)
-    (cond ((null? values) #t)
-          (else
-           (and
-            (legal-value? (car values) ok-values)
-            (list-legal (cdr values))))))
-
-  (define (list-strings p-vals)
-    (if (null? p-vals)
-        '()
-        (cons (vector-ref (car p-vals) 1)
-              (list-strings (cdr p-vals)))))
-
-  (check-ok-values ok-values "gnc:make-list-option")
-
-  (let* ((value default-value)
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name sort-tag 'list documentation-string
-     (lambda () value)
-     (lambda (x)
-       (if (list-legal x)
-           (set! value x)
-           (rpterror-earlier "list" x default-value)))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)
-     (lambda (b p)
-       (qof-book-set-option b (length value) (append p '("len")))
-       (let loop ((value value) (idx 0))
-         (unless (null? value)
-           (qof-book-set-option
-            b (caar value) (append p (list (format #f "item~a" idx))))
-           (loop (cdr value) (1+ idx)))))
-     (lambda (b p)
-       (let ((len (qof-book-get-option b (append p '("len")))))
-         (if (and len (integer? len))
-             (set! value
-               (map
-                (lambda (idx)
-                  (qof-book-get-option b (append p (list (format #f "item~a" idx)))))
-                (iota len))))))
-     (lambda (x)
-       (if (list-legal x)
-           (list #t x)
-           (list #f "list-option: illegal value")))
-     ok-values     
-     (vector (lambda () (length ok-values))
-             (lambda (x) (vector-ref (list-ref ok-values x) 0))
-             (lambda (x) (vector-ref (list-ref ok-values x) 1))
-             #f                         ;old tooltip
-             (lambda (x) (gnc:multichoice-list-lookup ok-values x)))
-     (lambda () (list-strings ok-values)) #f)))
-
-;; number range options use the option-data as a list whose
-;; elements are: (lower-bound upper-bound num-decimals step-size)
-(define (gnc:make-number-range-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         lower-bound
-         upper-bound
-         num-decimals
-         step-size)
-  (let* ((value default-value)
-         (value->string (lambda () (number->string value))))
-    (gnc:make-option
-     section name sort-tag 'number-range documentation-string
-     (lambda () value)
-     (lambda (x) (set! value x))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) (qof-book-set-option b value p))
-     (lambda (b p)
-       (let ((v (qof-book-get-option b p)))
-         (if (and v (number? v))
-             (set! value v))))
-     (lambda (x)
-       (cond ((not (number? x)) (list #f "number-range-option: not a number"))
-             ((and (>= value lower-bound)
-                   (<= value upper-bound))
-              (list #t x))
-             (else (list #f "number-range-option: out of range"))))
-     (list lower-bound upper-bound num-decimals step-size)
-     #f #f #f)))
-
-;; number plot size options use the option-data as a list whose
-;; elements are: (lower-bound upper-bound num-decimals step-size)
-;; which is used for the valid pixel range
-(define (gnc:make-number-plot-size-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         lower-bound
-         upper-bound
-         num-decimals
-         step-size)
-  (let* ((value default-value)
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name sort-tag 'plot-size documentation-string
-     (lambda () value)  ;;getter
-     (lambda (x)
-             (if (number? x) ;; this is for old style plot size
-             (set! value (cons 'pixels x))
-             (set! value x)))  ;;setter
-
-     (lambda () default-value)  ;;default-getter
-     (gnc:restore-form-generator value->string)  ;;restore-form
-     (lambda (b p)
-       (qof-book-set-option b (symbol->string (car value))
-                              (append p '("type")))
-       (qof-book-set-option b (if (symbol? (cdr value))
-                                  (symbol->string (cdr value))
-                                  (cdr value))
-                                  (append p '("value"))))  ;;scm->kvp
-     (lambda (b p)
-       (let ((t (qof-book-get-option b (append p '("type"))))
-             (v (qof-book-get-option b (append p '("value")))))
-         (if (and t v (string? t))
-             (set! value (cons (string->symbol t)
-                               (if (string? v) (string->number v) v))))))  ;;kvp->scm
-     (lambda (x)
-       (if (eq? 'pixels (car x))
-         (cond ((not (number? (cdr x))) (list #f "number-plot-size-option-pixels: not a number"))
-               ((and (>= (cdr x) lower-bound)
-                     (<= (cdr x) upper-bound))
-                (list #t x))
-               (else (list #f "number-plot-size-option-pixels: out of range")))
-         (cond ((not (number? (cdr x))) (list #f "number-plot-size-option-percentage: not a number"))
-               ((and (>= (cdr x) 10)
-                     (<= (cdr x) 100))
-                (list #t x))
-               (else (list #f "number-plot-size-option-percentage: out of range")))
-       )
-     )  ;;value-validator
-     (list lower-bound upper-bound num-decimals step-size)  ;;option-data
-     #f #f #f)))  ;;option-data-fns, strings-getter, option-widget-changed-proc
-
-(define (gnc:plot-size-option-value-type option-value)
-  (car option-value))
-
-(define (gnc:plot-size-option-value option-value)
-  (cdr option-value))
-
-(define (gnc:make-internal-option
-         section
-         name
-         default-value)
-  (let* ((value default-value)
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name "" 'internal #f
-     (lambda () value)
-     (lambda (x) (set! value x))
-     (lambda () default-value)
-     (gnc:restore-form-generator value->string)
-     #f
-     #f
-     (lambda (x) (list #t x))
-     #f #f #f #f)))
-
-
-(define (gnc:make-query-option
-         section
-         name
-         default-value)
-  (let* ((value (if (list? default-value)
-                    default-value
-                    (gnc-query2scm default-value)))
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name "" 'query #f
-     (lambda () value)
-     (lambda (x) (set! value (if (list? x) x (gnc-query2scm x))))
-     (lambda () (if (list? default-value)
-                    default-value
-                    (gnc-query2scm default-value)))
-     (gnc:restore-form-generator value->string)
-     #f
-     #f
-     (lambda (x) (list #t x))
-     #f #f #f #f)))
-
-
-;; Color options store rgba values in a list.
-;; The option-data is a list, whose first element
-;; is the range of possible rgba values and whose
-;; second element is a boolean indicating whether
-;; to use alpha transparency.
-(define (gnc:make-color-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value
-         range
-         use-alpha)
-
-  (define (canonicalize values)
-    (map exact->inexact values))
-
-  (define (values-in-range values)
-    (if (null? values)
-        #t
-        (let ((value (car values)))
-          (and (number? value)
-               (>= value 0)
-               (<= value range)
-               (values-in-range (cdr values))))))
-
-  (define (validate-color color)
-    (cond ((not (list? color)) (list #f "color-option: not a list"))
-          ((not (= 4 (length color))) (list #f "color-option: wrong length"))
-          ((not (values-in-range color))
-           (list #f "color-option: bad color values"))
-          (else (list #t color))))
-
-  (let* ((value (canonicalize default-value))
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name sort-tag 'color documentation-string
-     (lambda () value)
-     (lambda (x) (set! value (canonicalize x)))
-     (lambda () (canonicalize default-value))
-     (gnc:restore-form-generator value->string)
-     #f
-     #f
-     validate-color
-     (list range use-alpha)
-     #f #f #f)))
-
-(define (gnc:color->hex-string color range)
-  (define (html-value value)
-    (inexact->exact
-     (min 255.0
-          (truncate (* (/ 255.0 range) value)))))
-  (define (number->hex-string number)
-    (let ((ret (number->string number 16)))
-      (cond ((< (string-length ret) 2) (string-append "0" ret))
-            (else ret))))
-  (let ((red (car color))
-        (green (cadr color))
-        (blue (caddr color)))
-    (string-append
-     (number->hex-string (html-value red))
-     (number->hex-string (html-value green))
-     (number->hex-string (html-value blue)))))
-
-(define (gnc:color->html color range)
-  (string-append "#"
-                 (gnc:color->hex-string color range)))
-
-(define (gnc:color-option->html color-option)
-  (let ((color (gnc:option-value color-option))
-        (range (car (gnc:option-data color-option))))
-    (gnc:color->html color range)))
-
-(define (gnc:color-option->hex-string color-option)
-  (let ((color (gnc:option-value color-option))
-        (range (car (gnc:option-data color-option))))
-    (gnc:color->hex-string color range)))
-
-;;
-;; dateformat option
-;;
-(define (gnc:make-dateformat-option
-         section
-         name
-         sort-tag
-         documentation-string
-         default-value)
-
-  (define (def-value)
-    (if (list? default-value)
-        default-value
-        (list 'unset 'number #t "")))
-
-  (let* ((value (def-value))
-         (value->string (lambda () 
-                          (string-append "'" (gnc:value->string value)))))
-    (gnc:make-option
-     section name sort-tag 'dateformat documentation-string
-     (lambda () value)
-     (lambda (x) (set! value x))
-     (lambda () (def-value))
-     (gnc:restore-form-generator value->string)
-     (lambda (b p)
-       (if (eq? (car value) 'unset)
-           (qof-book-options-delete b p );; delete the kvp when unset
-       (begin
-         (qof-book-set-option
-          b (symbol->string (car value)) (append p '("fmt")))
-         (qof-book-set-option
-          b (symbol->string (cadr value)) (append p '("month")))
-         (qof-book-set-option
-          b (if (caddr value) 1 0) (append p '("years")))
-         (qof-book-set-option
-          b (cadddr value) (append p '("custom"))))))
-     (lambda (f p)
-       (let ((fmt (qof-book-get-option f (append p '("fmt"))))
-             (month (qof-book-get-option f (append p '("month"))))
-             (years (qof-book-get-option f (append p '("years"))))
-             (custom (qof-book-get-option f (append p '("custom")))))
-         (if (and
-              fmt (string? fmt)
-              month (string? month)
-              years (number? years)
-              custom (string? custom))
-             (set! value (list (string->symbol fmt) (string->symbol month)
-                               (if (= years 0) #f #t) custom)))))
-     (lambda (x)
-       (cond ((not (list? x)) (list #f "dateformat-option: not a list"))
-             ((not (= (length x) 4))
-              (list #f "dateformat-option: wrong list length" (length x)))
-             ((not (symbol? (car x)))
-              (list #f "dateformat-option: no format symbol"))
-             ((not (symbol? (cadr x)))
-              (list #f "dateformat-option: no months symbol"))
-             ((not (string? (cadddr x)))
-              (list #f "dateformat-option: no custom string"))
-             (else (list #t x))))
-     #f #f #f #f)))
-
-(define (gnc:dateformat-get-format v)
-  (cadddr v))
-
-;; Create a new options database
-(define (gnc:new-options)
-  (define option-hash (make-hash-table 23))
-
-  (define options-changed #f)
-  (define changed-hash (make-hash-table 23))
-
-  (define callback-hash (make-hash-table 23))
-  (define last-callback-id 0)
-  (define new-names-alist
-    '(("Accounts to include" #f "Accounts")
-      ("Exclude transactions between selected accounts?" #f
-       "Exclude transactions between selected accounts")
-      ("Filter Accounts" #f "Filter By...")
-      ("Flatten list to depth limit?" #f "Flatten list to depth limit")
-      ("From" #f "Start Date")
-      ("Report Accounts" #f "Accounts")
-      ("Report Currency" #f "Report's currency")
-      ("Show Account Code?" #f "Show Account Code")
-      ("Show Full Account Name?" #f "Show Full Account Name")
-      ("Show Multi-currency Totals?" #f "Show Multi-currency Totals")
-      ("Show zero balance items?" #f "Show zero balance items")
-      ("Sign Reverses?" #f "Sign Reverses")
-      ("To" #f "End Date")
-      ("Charge Type" #f "Action") ;easy-invoice.scm, renamed June 2018
-      ;; the following 4 options in income-gst-statement.scm renamed Dec 2018
-      ("Individual income columns" #f "Individual sales columns")
-      ("Individual expense columns" #f "Individual purchases columns")
-      ("Remittance amount" #f "Gross Balance")
-      ("Net Income" #f "Net Balance")
-      ;; transaction.scm:
-      ("Use Full Account Name?" #f "Use Full Account Name")
-      ("Use Full Other Account Name?" #f "Use Full Other Account Name")
-      ("Void Transactions?" "Filter" "Void Transactions")
-      ("Void Transactions" "Filter" "Void Transactions")
-      ("Account Substring" "Filter" "Account Name Filter")
-      ("Enable links" #f "Enable Links")
-      ;; trep-engine: moved currency options to own tab
-      ("Common Currency" "Currency" "Common Currency")
-      ("Show original currency amount" "Currency" "Show original currency amount")
-      ("Report's currency" "Currency" "Report's currency")
-      ("Reconcile Status" #f "Reconciled Status")
-      ;; new-owner-report.scm, renamed Oct 2020 to differentiate with
-      ;; Document Links:
-      ("Links" #f "Transaction Links")
-      ;; invoice.scm, renamed November 2018
-      ("Individual Taxes" #f "Use Detailed Tax Summary")
-      ;; income-gst-statement.scm
-      ("default format" #f "Default Format")
-      ("Report format" #f "Report Format")
-      ))
-
-  (define (lookup-option section name)
-    (let ((section-hash (hash-ref option-hash section)))
-      (and section-hash
-           (or (hash-ref section-hash name)
-               ;; Option name was not found. Perhaps it was renamed?
-               ;; Let's try to map to a known new name.  The alist
-               ;; new-names-alist will try match names - car is the old
-               ;; name, cdr is the 2-element list describing
-               ;; 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 ((name-match (assoc-ref new-names-alist name)))
-                 (and name-match
-                      (let ((new-section (car name-match))
-                            (new-name (cadr name-match)))
-                        (gnc:warn
-                         (format #f "option ~a/~a has been renamed to ~a/~a\n"
-                                 section name new-section new-name))
-                        (cond
-                         ;; new-name only
-                         ((not new-section)
-                          (lookup-option section new-name))
-                         ;; new-section different to current section
-                         ;; name, and possibly new-name
-                         ((not (string=? new-section section))
-                          (lookup-option new-section new-name))
-                         ;; no match, return #f
-                         (else #f)))))))))
-
-  (define (option-changed section name)
-    (set! options-changed #t)
-    (let ((section-changed-hash (hash-ref changed-hash section)))
-      (if (not section-changed-hash)
-          (begin
-            (set! section-changed-hash (make-hash-table 23))
-            (hash-set! changed-hash section section-changed-hash)))
-      (hash-set! section-changed-hash name #t)))
-
-  (define (clear-changes)
-    (set! options-changed #f)
-    (set! changed-hash (make-hash-table 23)))
-
-  (define (register-option new-option)
-    (let* ((name (gnc:option-name new-option))
-           (section (gnc:option-section new-option))
-           (section-hash (hash-ref option-hash section)))
-      (if (not section-hash)
-          (begin
-            (set! section-hash (make-hash-table 23))
-            (hash-set! option-hash section section-hash)))
-      (hash-set! section-hash name new-option)
-      (gnc:option-set-changed-callback
-       new-option
-       (lambda () (option-changed section name)))))
-
-  (define (unregister-option section name)
-    (let* ((section-hash (hash-ref option-hash section)))
-      (if (and section-hash
-               (hash-ref section-hash name))
-          (begin
-            (hash-remove! section-hash name)
-            (if (zero? (hash-count (const #t) section-hash))
-                (hash-remove! option-hash section)))
-          (gnc:error "options:unregister-option: no such option\n"))))
-
-;   Call (thunk option) for each option in the database
-  (define (options-for-each thunk)
-    (define (section-for-each section-hash thunk)
-      (hash-for-each
-       (lambda (name option)
-         (thunk option))
-       section-hash))
-    (hash-for-each
-     (lambda (section hash)
-       (section-for-each hash thunk))
-     option-hash))
-
-  (define (options-for-each-general section-thunk option-thunk)
-    (define (section-for-each section-hash thunk)
-      (hash-for-each
-       (lambda (name option)
-         (thunk option))
-       section-hash))
-    (hash-for-each
-     (lambda (section hash)
-       (if section-thunk
-           (section-thunk section hash))
-       (if option-thunk
-           (section-for-each hash option-thunk)))
-     option-hash))
-
-  (define (generate-restore-forms options-string)
-
-    (define (generate-option-restore-form option restore-code)
-      (let* ((section (gnc:option-section option))
-             (name (gnc:option-name option)))
-        (string-append
-         "(let ((option (gnc:lookup-option " options-string "\n"
-         "                                 " (gnc:value->string section) "\n"
-         "                                 " (gnc:value->string name) ")))\n"
-         "  (" restore-code " option))\n\n")))
-
-    (define (generate-forms port)
-      (options-for-each-general
-       (lambda (section hash)
-         (display
-          (string-append "\n; Section: " section "\n\n")
-          port))
-       (lambda (option)
-         (let ((value (gnc:option-value option))
-               (default-value (gnc:option-default-value option)))
-           (if (not (equal? value default-value))
-            (let* ((generator (gnc:option-generate-restore-form option))
-                   (restore-code (false-if-exception (generator))))
-              (if restore-code
-                  (display
-                   (generate-option-restore-form option restore-code)
-                   port))))))))
-
-    (call-with-output-string generate-forms))
-
-  (define (scm->kvp book)
-    (options-for-each
-     (lambda (option)
-       (let ((value (gnc:option-value option))
-             (default-value (gnc:option-default-value option))
-             (section (gnc:option-section option))
-             (name (gnc:option-name option)))
-         (if (not (equal? value default-value))
-             (let ((save-fcn (gnc:option-scm->kvp option)))
-               (if save-fcn
-                   (save-fcn book (list section name)))))))))
-
-  (define (kvp->scm book)
-    (options-for-each
-     (lambda (option)
-       (let ((section (gnc:option-section option))
-             (name (gnc:option-name option))
-             (load-fcn (gnc:option-kvp->scm option)))
-         (if load-fcn
-             (load-fcn book (list section name)))))))
-
-  (define (register-callback section name callback)
-    (let ((id last-callback-id)
-          (data (list section name callback)))
-      (set! last-callback-id (+ last-callback-id 1))
-      (hashv-set! callback-hash id data)
-      id))
-
-  (define (unregister-callback-id id)
-    (if (hashv-ref callback-hash id)
-        (hashv-remove! callback-hash id)
-        (gnc:error "options:unregister-callback-id: no such id\n")))
-
-  (define (run-callbacks)
-    (define (run-callback id cbdata)
-      (let ((section  (car cbdata))
-            (name     (cadr cbdata))
-            (callback (caddr cbdata)))
-        (if (not section)
-            (callback)
-            (let ((section-changed-hash (hash-ref changed-hash section)))
-              (if section-changed-hash
-                  (if (not name)
-                      (callback)
-                      (if (hash-ref section-changed-hash name)
-                          (callback))))))))
-
-    (if options-changed
-        (let ((cblist '()))
-          (hash-for-each 
-           (lambda (k v) (set! cblist (cons (cons k v) cblist)))
-           callback-hash)
-          (set! cblist (sort cblist 
-                             (lambda (a b) 
-                               (< (car a) (car b)))))
-          (for-each 
-           (lambda (elt) (run-callback (car elt) (cdr elt))) 
-           cblist)))
-    (clear-changes))
-  
-  (define default-section #f)
-
-  (define (touch)
-    (set! options-changed #t)
-    (run-callbacks))
-  
-  (define (set-default-section section-name)
-    (set! default-section section-name))
-
-  (define (get-default-section)
-    default-section)
-
-  (define (dispatch key)
-    (case key
-      ((lookup) lookup-option)
-      ((register-option) register-option)
-      ((unregister-option) unregister-option)
-      ((register-callback) register-callback)
-      ((unregister-callback-id) unregister-callback-id)
-      ((for-each) options-for-each)
-      ((for-each-general) options-for-each-general)
-      ((generate-restore-forms) generate-restore-forms)
-      ((scm->kvp) scm->kvp)
-      ((kvp->scm) kvp->scm)
-      ((touch) touch)
-      ((clear-changes) clear-changes)
-      ((run-callbacks) run-callbacks)
-      ((set-default-section) set-default-section)
-      ((get-default-section) get-default-section)
-      (else (gnc:warn "options: bad key: " key "\n"))))
+(define-public (gnc:lookup-option options section name)
+  (if options
+      (gnc-lookup-option options section name)
+      #f))
 
-  dispatch)
+(define-public (gnc:option-setter option)
+  (issue-deprecation-warning "gnc:option-setter is deprecated. Option values are set and retrieved by gnc-set-option and gnc-option-db-lookup.")
+  (lambda (value)
+    (GncOption-set-value-from-scm option value)
+    ))
 
-(define (gnc:register-option options new-option)
-  ((options 'register-option) new-option))
+(define-public (gnc:option-set-value option value)
+    (issue-deprecation-warning "gnc:option-set-value and indeed all direct option access is deprecated. Use gnc-set-option instead.")
+    (GncOption-set-value-from-scm option value))
 
-(define (gnc:options-register-callback section name callback options)
-  ((options 'register-callback) section name callback))
+(define-public (gnc:option-set-default-value option value)
+    (issue-deprecation-warning "gnc:option-set-default-value and indeed all direct option access is deprecated. Use gnc-set-option instead.")
+    (GncOption-set-default-value-from-scm option value))
 
-(define (gnc:options-register-c-callback section name c-callback data options)
-  (let ((callback (lambda () (gncp-option-invoke-callback c-callback data))))
-    ((options 'register-callback) section name callback)))
+(define-public (gnc:option-section option)
+  (GncOption-get-section option))
 
-(define (gnc:options-unregister-callback-id id options)
-  ((options 'unregister-callback-id) id))
+(define-public (gnc:option-name option)
+  (GncOption-get-name option))
 
-(define (gnc:options-for-each thunk options)
-  ((options 'for-each) thunk))
+(define-public (gnc:option-default-value option)
+  (GncOption-get-scm-default-value option))
 
-(define (gnc:options-for-each-general section-thunk option-thunk options)
-  ((options 'for-each-general) section-thunk option-thunk))
+(define-public (gnc:option-value option)
+    (issue-deprecation-warning "gnc:option-value and indeed all direct option access is deprecated. Use gnc-option-db-lookup-option instead.")
+    (GncOption-get-scm-value option))
 
-(define (gnc:lookup-option options section name)
-  (if options
-      ((options 'lookup) section name)
-      #f))
+(define-public (gnc:color-option->html opt)
+  (format #f "#~a" (GncOption-get-scm-value opt)))
 
-(define (gnc:unregister-option options section name)
-  ((options 'unregister-option) section name))
+(define-public (gnc:color-option->hex-string opt)
+  (format #f "~a" (GncOption-get-scm-value opt)))
 
-(define (gnc:generate-restore-forms options options-string)
-  ((options 'generate-restore-forms) options-string))
-
-(define (gnc:options-scm->kvp options book clear-option?)
-  (if clear-option?
-      (qof-book-options-delete book '()))
-  ((options 'scm->kvp) book))
+(define-public (gnc:option-get-value book category key)
+  (define acc (if (pair? key) cons list))
+  (qof-book-get-option book (acc category key)))
 
-(define (gnc:options-kvp->scm options book)
-  ((options 'kvp->scm) book))
+(define-public (gnc:option-make-internal! options section name)
+  (let ((option (gnc-lookup-option options section name)))
+    (and option (GncOption-make-internal option))))
 
-(define (gnc:options-clear-changes options)
-  ((options 'clear-changes)))
+(define-public (gnc:option-type option)
+  (GncOption-get-type option))
 
-(define (gnc:options-touch options)
-  ((options 'touch)))
+;; Used only by test-stress-options.scm
+(define-public (gnc:option-data option)
+  (let ((num-values (GncOption-num-permissible-values option))
+        (retval '()))
+    (do ((i 0 (1+ i))) ((>= i num-values))
+      (let ((value (GncOption-permissible-value option i))
+            (name (GncOption-permissible-value-name option i))
+            (desc (GncOption-permissible-value-description option i)))
+        (set! retval (cons retval (vector value name desc)))))
+    retval))
 
-(define (gnc:options-run-callbacks options)
-  ((options 'run-callbacks)))
+(define-public (gnc:new-options)
+  (new-gnc-optiondb))
 
-(define (gnc:options-set-default-section options section-name)
-  ((options 'set-default-section) section-name))
+(define-public (gnc:options-set-default-section optiondb section)
+  (GncOptionDB-set-default-section (GncOptionDBPtr-get optiondb) section))
 
-(define (gnc:options-get-default-section options)
-  ((options 'get-default-section)))
+(define-public (gnc:options-for-each func optdb)
+  (gnc-optiondb-foreach optdb func))
 
 ;; Copies all values from src-options to dest-options, that is, it
 ;; copies the values of all options from src which exist in dest to
 ;; there.
-(define (gnc:options-copy-values src-options dest-options)
+(define-public (gnc:options-copy-values src-options dest-options)
   (if 
    dest-options
    (gnc:options-for-each 
@@ -1862,58 +120,261 @@ the option '~a'."))
                                   (gnc:option-value src-option)))))
     src-options)))
 
-(define (gnc:send-options db_handle options)
-  (gnc:options-for-each
-   (lambda (option)
-     (gnc-option-db-register-option db_handle option))
-   options))
+;; FIXME: Fake callback functions for boolean-complex and multichoice-callback
+
+(define-public (gnc:options-register-callback section name callback options) 1)
+(define-public (gnc:options-register-c-callback section name callback data options) 1)
+(define-public (gnc:options-unregister-callback-id id) 0 options)
+
+;; The following implement the old API that separated creation from registration.
+(define-public (gnc:register-option optdb opt)
+  (issue-deprecation-warning "gnc:register-option is deprecated. Use gnc-register-foo-option instead.")
+  (GncOptionDB-register-option (GncOptionDBPtr-get optdb)
+                               (GncOption-get-section opt) opt))
+
+(define-public (gnc:unregister-option optdb section name)
+  (GncOptionDB-unregister-option (GncOptionDBPtr-get optdb) section name))
+
+(define-public (gnc:make-string-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-string-option is deprecated. Make and register the option in one command with gnc-register-string-option.")
+  (gnc-make-string-option section name key docstring default (GncOptionUIType-STRING)))
+(define-public (gnc:make-text-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-text-option is deprecated. Make and register the option in one command with gnc-register-text-option.")
+  (gnc-make-string-option section name key docstring default (GncOptionUIType-TEXT)))
+(define-public (gnc:make-font-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-font-option is deprecated. Make and register the option in one command with gnc-register-font-option.")
+  (gnc-make-string-option section name key docstring default (GncOptionUIType-FONT)))
+(define-public (gnc:make-color-option section name key docstring colors range use-alpha)
+  (issue-deprecation-warning "gnc:make-color-option is deprecated. Make and register the option in one command with gnc-register-color-option.")
+  (let ((color-str (if use-alpha
+                      (format #f "~x~x~x~x" (car colors) (cadr colors) (caddr colors) (cadddr colors))
+                      (format #f "~x~x~x" (car colors) (cadr colors) (caddr colors)))))
+  (gnc-make-string-option section name key docstring color-str (GncOptionUIType-COLOR))))
+(define-public (gnc:make-budget-option section name key docstring)
+  (issue-deprecation-warning "gnc:make-budget-option is deprecated. Make and register the option in one command with gnc-register-color-option.")
+  (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-BUDGET)))
+(define-public (gnc:make-commodity-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-commodity-option is deprecated. Make and register the option in one command with gnc-register-commodity-option.")
+  (gnc-make-commodity-option section name key docstring default))
+(define-public (gnc:make-simple-boolean-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-simple-boolean-option is deprecated. Make and register the option in one command with gnc-register-simple-boolean-option.")
+  (gnc-make-bool-option section name key docstring default (GncOptionUIType-BOOLEAN)))
+(define-public (gnc:make-complex-boolean-option section name key docstring default setter-cb widget-changed-cb)
+  (issue-deprecation-warning "gnc:make-complex-boolean-option is deprecated and its functionality removed. Make and register a simple-boolean in one command with gnc-register-simple-boolean-option and figure out some other way to change widget sensitivity.")
+  (gnc-make-bool-option section name key docstring default (GncOptionUIType-BOOLEAN)))
+(define-public (gnc:make-pixmap-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-pixmap-option is deprecated. Make and register the option in one command with gnc-register-pixmap-option.")
+  (gnc-make-string-option section name key docstring default (GncOptionUIType-PIXMAP)))
+;; gnc:make-account-list-option's getter and validator parameters are functions. 
+(define-public (gnc:make-account-list-option section name key docstring default validator multi)
+  (issue-deprecation-warning "gnc:make-account-list-option is deprecated. Make and register the option in one command with gnc-register-account-list-option.")
+  (gnc-make-account-list-option section name key docstring (default)))
+(define-public (gnc:make-account-list-limited-option section name key docstring default validator multi permitted)
+  (issue-deprecation-warning "gnc:make-account-list-limited-option is deprecated. Make and register the option in one command with gnc-register-account-list-limited-option.")
+  (gnc-make-account-list-limited-option section name key docstring (default) permitted))
+(define-public (gnc:make-account-sel-limited-option section name key docstring default validator permitted)
+  (issue-deprecation-warning "gnc:make-account-sel-limited-option is deprecated. Make and register the option in one command with gnc-register-account-sel-limited-option.")
+  (gnc-make-account-sel-limited-option section name key docstring (default) permitted))
+(define-public (gnc:make-account-sel-option section name key docstring default validator)
+  (let ((defval (if default (default) #f)))
+  (gnc-make-account-sel-limited-option section name key docstring defval '())))
+(define-public (gnc:make-multichoice-option section name key docstring default multichoice)
+  (issue-deprecation-warning "gnc:make-multichoice-option is deprecated. Make and register the option in one command with gnc-register-multichoice-option.")
+  (let ((defval (cond ((symbol? default)
+                       (symbol->string default))
+                      ((number? default)
+                       (number->string default))
+                     (else default))))
+  (gnc-make-multichoice-option section name key docstring defval multichoice)))
+(define-public (gnc:make-multichoice-callback-option section name key docstring default multichoice setter-cb widget-changed-cb)
+  (issue-deprecation-warning "gnc:make-multichoice-callback-option is deprecated in favor of a not-yet-written but more sensible way to conditionally enable and disable option widgets.")
+  (gnc:make-multichoice-option section name key docstring default multichoice))
+
+(define-public (gnc:make-list-option section name key docstring default multichoice)
+  (issue-deprecation-warning "gnc:make-list-option is deprecated. Make and register the option in one command with gnc-register-list-option.")
+  (let ((indexes (if default (map (lambda (def-item)
+                                    (list-index (lambda (choice)
+                                                  (eq? def-item
+                                                       (vector-ref choice 0)))
+                                                multichoice))
+                                  default) 0)))
+    (gnc-make-list-option section name key docstring indexes multichoice)))
+(define-public (gnc:make-radiobutton-option section name key docstring default multichoice)
+  (gnc:warn "gnc:make-radiobutton-option is no longer available. Using gnc:make-multichoice-option instead.")
+  (gnc:make-multichoice-option section name key docstring default multichoice))
+(define-public (gnc:make-number-range-option section name key docstring default min max dec-places step)
+  (issue-deprecation-warning "gnc:make-number-range-option is deprecated. Make and register the option in one command with gnc-register-number-range-option.")
+  (gnc-make-range-value-option section name key docstring default min max step))
+(define-public (gnc:make-number-plot-size-option section name key docstring default min max dec-places step)
+  (issue-deprecation-warning "gnc:make-number-plot-size-option is deprecated. Make and register the option in one command with gnc-register-plot-size-range-option.")
+  ;; Ignore what the call asks for, only 10-100% makes sense.
+  (gnc-make-plot-size-option section name key docstring 100 10 100 1))
+(define-public (gnc:make-query-option section name default)
+  (issue-deprecation-warning "gnc:make-query-option is deprecated. Make and register the option in one command with gnc-register-query-option.")
+  (let ((defv (if (list? default) default (gnc-query2scm default))))
+    (gnc-make-SCM-option section name "" "query" defv (GncOptionUIType-INTERNAL))))
+(define-public (gnc:make-internal-option section name default)
+  (issue-deprecation-warning "gnc:make-internal-option is deprecated. Make and register the option in one command with gnc-register-internal-option.")
+  (let ((type (GncOptionUIType-INTERNAL))
+        (key "_")
+        (desc "internal"))
+    (gnc-make-SCM-option section name key desc default type)))
+(define-public (gnc:make-owner-option section name key docstring getter validator owner-type)
+  (issue-deprecation-warning "gnc:make-owner-option is deprecated. Make and register the option in one command with gnc-register-owner-option.")
+  (let ((ui-type (cond
+                  ((eqv? owner-type GNC-OWNER-CUSTOMER) (GncOptionUIType-CUSTOMER))
+                  ((eqv? owner-type GNC-OWNER-VENDOR) (GncOptionUIType-VENDOR))
+                  ((eqv? owner-type GNC-OWNER-EMPLOYEE) (GncOptionUIType-EMPLOYEE))
+                  ((eqv? owner-type GNC-OWNER-JOB) (GncOptionUIType-JOB))
+                  (else (GncOptionUIType-INTERNAL))))
+        (defval (if getter (getter) #f)))
+    (format #t "Making owner option ~a:~a ~a ~a~%"  section name defval ui-type)(force-output)
+    (gnc-make-qofinstance-option section name key docstring defval ui-type)))
+(define-public (gnc:make-invoice-option section name key docstring getter validator)
+  (issue-deprecation-warning "gnc:make-invoice-option is deprecated. Make and register the option in one command with gnc-register-ionvoice-option.")
+  (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-INVOICE)))
+(define-public (gnc:make-taxtable-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-taxtable-option is deprecated. Make and register the option in one command with gnc-register-taxtable-option.")
+  (gnc-make-qofinstance-option section name key docstring default (GncOptionUIType-TAX_TABLE)))
+(define-public (gnc:make-counter-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-number-range-option is deprecated. Make and register the option in one command with gnc-register-number-range-option.")
+  (gnc-make-range-value-option section name key docstring default 0.0 999999999.0, 1.0))
+
+(define-public (gnc:make-counter-format-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-counter-format-option is deprecated. Make and register the option in one command with gnc-register-counter-format-option.")
+  (gnc-make-string-option section name key docstring default (GncOptionUIType-STRING)))
+(define-public (gnc:make-date-format-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-date-format-option is deprecated. Make and register the option in one command with gnc-register-date-format-option.")
+  (gnc-make-string-option section name key docstring default (GncOptionUIType-DATE_FORMAT)))
+(define-public (gnc:make-currency-option section name key docstring default)
+  (issue-deprecation-warning "gnc:make-currency-option is deprecated. Make and register the option in one command with gnc-register-currency-option.")
+  (gnc-make-currency-option section name key docstring default))
+(define-public (gnc:make-date-option section name key docstring getter showtime
+                                     subtype relative-date-list)
+  (let ((default (getter))
+        (both (if (eq? subtype 'both) #t #f)))
+    (gnc-make-date-option section name key docstring default relative-date-list both)))
+
+(define-public (gnc:options-make-end-date! optiondb pagename optname sort-tag docstring)
+  (gnc-register-end-date-option optiondb pagename optname sort-tag docstring))
+
+(define-public (gnc:options-make-date-interval! optiondb pagename name-from info-from name-to info-to sort-tag)
+  (gnc-register-start-date-option optiondb pagename name-from
+                                  (string-append sort-tag "a") info-from)
+  (gnc-register-end-date-option optiondb pagename name-to
+                                (string-append sort-tag "b") info-to))
+(define-public (gnc:date-option-absolute-time option-value)
+  (if (pair? option-value)
+          (if (eq? (car option-value) 'absolute)
+              (cdr option-value)
+              (gnc-relative-date-to-time64 (cdr option-value)))
+          option-value))
+;; This is a special case where we can't use the exported registration function
+;; because we need to transform the default argument first depending on its
+;; Scheme type.
+(define-public (gnc:register-multichoice-option options section name key docstring default multichoice)
+  (issue-deprecation-warning "gnc:make-multichoice-option is deprecated. Make and register the option in one command with gnc-register-multichoice-option.")
+  (let ((defval (cond ((symbol? default)
+                       (symbol->string default))
+                      ((number? default)
+                       (number->string default))
+                     (else default))))
+  (gnc-register-multichoice-option options section name key docstring defval multichoice)))
+
+;; Scheme code for supporting options for the business modules
+;;
+;; Created by:	Derek Atkins <derek at ihtfp.com>
+;;
+;; 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
 
-(define (gnc:options-make-end-date! options pagename optname sort-tag info)
-  (gnc:register-option 
-   options  
-   (gnc:make-date-option
-    pagename optname 
-    sort-tag info
-    (lambda () 
-      (cons 'relative 'end-accounting-period))
-    #f 'both 
-    '(
-      today 
-      end-this-month
-      end-prev-month 
-      end-current-quarter 
-      end-prev-quarter
-      end-cal-year 
-      end-prev-year
-      end-accounting-period
-      ))))
 
-(define (gnc:options-make-date-interval! options pagename name-from info-from
-                                         name-to info-to sort-tag)
-  (gnc:register-option 
-   options  
-   (gnc:make-date-option
-    pagename name-from
-    (string-append sort-tag "a") info-from
-    (lambda () (cons 'relative 'start-accounting-period))
-    #f 'both 
-    '(
-      today
-      start-this-month 
-      start-prev-month 
-      start-current-quarter
-      start-prev-quarter
-      start-cal-year 
-      start-prev-year
-      start-accounting-period
-      )))
-  (gnc:options-make-end-date! options pagename name-to
-                              (string-append sort-tag "b") info-to))
+;; Internally, values are always a guid. Externally, both guids and
+;; invoice pointers may be used to set the value of the option. The
+;; option always returns a single invoice pointer.
+
+(use-modules (gnucash core-utils))
+(use-modules (gnucash engine))
+(use-modules (gnucash utilities))
+(use-modules (gnucash app-utils options))
+(use-modules (sw_app_utils))
 
-(define (gnc:option-make-internal! options section name)
-  ;; this function will hide the option specified
-  ;; the option functionality is unchanged
-  (let ((opt (gnc:lookup-option options section name)))
-    (if opt
-        (vector-set! opt 3 'internal)
-        (gnc:error "gnc:option-make-internal! cannot find " section " / " name))))
+(export gnc:*business-label*)
+(export gnc:*company-name*)
+(export gnc:*company-addy*)
+(export gnc:*company-id*)
+(export gnc:*company-phone*)
+(export gnc:*company-fax*)
+(export gnc:*company-url*)
+(export gnc:*company-email*)
+(export gnc:*company-contact*)
+(export gnc:*fancy-date-label*)
+(export gnc:*fancy-date-format*)
+(export gnc:*tax-label*)
+(export gnc:*tax-nr-label*)
+(export gnc:company-info)
+(export gnc:fancy-date-info)
+(export gnc:*option-section-budgeting*)
+(export gnc:*option-name-auto-readonly-days*)
+(export gnc:*option-name-num-field-source*)
+(export gnc:*kvp-option-path*)
+(export gnc:options-fancy-date)
+(export gnc:*option-name-default-budget*)
+
+(define gnc:*kvp-option-path* (list KVP-OPTION-PATH))
+(define gnc:*option-name-auto-readonly-days* OPTION-NAME-AUTO-READONLY-DAYS)
+(define gnc:*option-name-num-field-source* OPTION-NAME-NUM-FIELD-SOURCE)
+
+(define gnc:*option-section-budgeting* OPTION-SECTION-BUDGETING)
+(define gnc:*option-name-default-budget* OPTION-NAME-DEFAULT-BUDGET)
+
+(define gnc:*business-label* (N_ "Business"))
+(define gnc:*company-name* (N_ "Company Name"))
+(define gnc:*company-addy* (N_ "Company Address"))
+(define gnc:*company-id* (N_ "Company ID"))
+(define gnc:*company-phone* (N_ "Company Phone Number"))
+(define gnc:*company-fax* (N_ "Company Fax Number"))
+(define gnc:*company-url* (N_ "Company Website URL"))
+(define gnc:*company-email* (N_ "Company Email Address"))
+(define gnc:*company-contact* (N_ "Company Contact Person"))
+(define gnc:*fancy-date-label* (N_ "Fancy Date Format"))
+(define gnc:*fancy-date-format* (N_ "custom"))
+(define gnc:*tax-label* (N_ "Tax"))
+(define gnc:*tax-nr-label* (N_ "Tax Number"))
+
+
+(define (gnc:options-fancy-date book)
+  (let ((date-format (gnc:fancy-date-info book gnc:*fancy-date-format*)))
+    (if (boolean? date-format) ;; date-format does not exist
+        (qof-date-format-get-string (qof-date-format-get))
+       date-format)))
+
+(define (gnc:company-info book key)
+  ;; Access company info from key-value pairs for current book
+ (gnc:option-get-value book gnc:*business-label* key))
+
+(define (gnc:fancy-date-info book key)
+  ;; Access fancy date info from key-value pairs for current book
+ (gnc:option-get-value book gnc:*business-label* (list gnc:*fancy-date-label* key)))
+
+
+
+(define (gnc:options-fancy-date book)
+  (let ((date-format (gnc:fancy-date-info book gnc:*fancy-date-format*)))
+    (if (boolean? date-format) ;; date-format does not exist
+        (qof-date-format-get-string (qof-date-format-get))
+       date-format)))

commit ce5fe577bf1b13ad55779109d0766b5de496fd86
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat May 15 10:22:02 2021 -0700

    Fix failure to pass accounts list to gnc:make-account-list-option
    
    The Scheme way of generating options is somehow able to obtain the
    account-list even when running the generator before creating the account
    tree. The C++ options object doesn't have that ability so one must make
    sure to run the account creation first in a let* statement.

diff --git a/gnucash/report/reports/standard/test/test-equity-statement.scm b/gnucash/report/reports/standard/test/test-equity-statement.scm
index 90aff7ef3..835af3393 100644
--- a/gnucash/report/reports/standard/test/test-equity-statement.scm
+++ b/gnucash/report/reports/standard/test/test-equity-statement.scm
@@ -51,8 +51,8 @@
   (gnc:options->sxml uuid options "test-equity-statement" test-title))
 
 (define (test-equity-statement)
-  (let* ((options (gnc:make-report-options uuid))
-         (account-alist (create-test-data))
+  (let* ((account-alist (create-test-data))
+         (options (gnc:make-report-options uuid))
          (gbp-bank (assoc-ref account-alist "GBP Bank"))
          (usd-bank (assoc-ref account-alist "Bank"))
          (expense (assoc-ref account-alist "Expenses"))
diff --git a/gnucash/report/reports/standard/test/test-portfolios.scm b/gnucash/report/reports/standard/test/test-portfolios.scm
index 298d072e0..e89329a5f 100644
--- a/gnucash/report/reports/standard/test/test-portfolios.scm
+++ b/gnucash/report/reports/standard/test/test-portfolios.scm
@@ -97,8 +97,8 @@
 
 (define (advanced-tests)
   (test-group-with-cleanup "advanced-portfolio-tests"
-    (let ((account-alist (create-stock-test-data))
-          (options (gnc:make-report-options advanced-uuid)))
+    (let* ((account-alist (create-stock-test-data))
+           (options (gnc:make-report-options advanced-uuid)))
       (let ((sxml (options->sxml advanced-uuid options "basic average")))
         (test-equal "advanced: average basis"
           '("AAPL" "AAPL" "NASDAQ" "42.00" "$6.0000" "$484.88" "$252.00" "$800.00"
diff --git a/gnucash/report/reports/standard/test/test-register.scm b/gnucash/report/reports/standard/test/test-register.scm
index 12d6d1d77..fdbda3ea4 100644
--- a/gnucash/report/reports/standard/test/test-register.scm
+++ b/gnucash/report/reports/standard/test/test-register.scm
@@ -51,8 +51,8 @@
   (gnc:options->sxml uuid options "test-register" test-title))
 
 (define (test-register)
-  (let* ((options (gnc:make-report-options uuid))
-         (account-alist (create-test-data))
+  (let* ((account-alist (create-test-data))
+         (options (gnc:make-report-options uuid))
          (bank (cdr (assoc "Bank" account-alist))))
 
     (gnc-commodity-set-user-symbol
diff --git a/gnucash/report/reports/standard/test/test-trial-balance.scm b/gnucash/report/reports/standard/test/test-trial-balance.scm
index a6c0a2151..dd1ed98f5 100644
--- a/gnucash/report/reports/standard/test/test-trial-balance.scm
+++ b/gnucash/report/reports/standard/test/test-trial-balance.scm
@@ -51,8 +51,8 @@
   (gnc:options->sxml uuid options "test-trial-balance" test-title))
 
 (define (test-trial-balance)
-  (let* ((options (gnc:make-report-options uuid))
-         (account-alist (create-test-data))
+  (let* ((account-alist (create-test-data))
+         (options (gnc:make-report-options uuid))
          (gbp-bank (assoc-ref account-alist "GBP Bank"))
          (usd-bank (assoc-ref account-alist "Bank"))
          (expense (assoc-ref account-alist "Expenses"))

commit 52ef53a40523587d8621ed18c5c8175a42a61a98
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon May 3 09:46:57 2021 -0700

    Implement multichoice selection options.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 9193a22ed..bcd0cc365 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -283,7 +283,8 @@ template<class OptType,
 std::istream& operator>>(std::istream& iss, OptType& opt)
 {
     std::decay_t<decltype(opt.get_value())> value;
-    if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, SCM>)
+    if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, SCM> ||
+                  std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _QofQuery*>)
         return iss;
     else
     {
@@ -809,10 +810,10 @@ inline std::ostream&
 gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
 {
     auto indexes{opt.get_multiple()};
-    if (indexes.m_vec.size() > 1)
+    if (indexes.size() > 1)
         oss << "'(";
     bool first = true;
-    for (auto index : indexes.m_vec)
+    for (auto index : indexes)
     {
         if (first)
             first = false;
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 88094f127..95c145696 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -376,6 +376,21 @@ gnc_option_test_book_destroy(QofBook* book)
     $1 = &period_set;
 }
 
+%typemap(in) GncMultichoiceOptionIndexVec (GncMultichoiceOptionIndexVec indexes)
+{
+    if (scm_is_true($input))
+    {
+        auto len{scm_to_size_t(scm_length($input))};
+        for (std::size_t i = 0; i < len; ++i)
+        {
+            auto val{scm_list_ref($input, scm_from_size_t(i))};
+            if (scm_is_unsigned_integer(val, 0, UINT_MAX))
+                indexes.push_back(scm_to_unsigned_integer(val, 0, UINT_MAX));
+        }
+    }
+    $1 = indexes;
+}
+
 %typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices)
 {
     using KeyType = GncOptionMultichoiceKeyType;
@@ -618,22 +633,54 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             return scm_to_int64(scm_is_pair(date) ? scm_cdr(date) : date);
 
         return gnc_relative_date_to_time64(scm_relative_date_get_period(date));
-        }
+    }
 
     %}
 
 %ignore GncOptionMultichoiceKeyType;
-%ignore pixels;
-%ignore percent;
-
-%header %{
-    static const SCM pixels{(scm_from_utf8_symbol("pixels"))};
-    static const SCM percent{(scm_from_utf8_symbol("percent"))};
-    %}
 
 %inline %{
-    inline SCM scm_from_multichoices (const GncMultichoiceOptionIndexVec& indexes,
-                                      const GncOptionMultichoiceValue& option)
+    inline GncMultichoiceOptionIndexVec
+    scm_to_multichoices(const SCM new_value,
+                        const GncOptionMultichoiceValue& option)
+    {
+        static const auto size_t_max = std::numeric_limits<std::size_t>::max();
+        static const char* empty{""};
+        auto scm_to_str = [](auto item)->const char* {
+                if (scm_is_integer(item))
+                    scm_number_to_string(item, scm_from_uint(10u));
+                if (scm_is_symbol(item))
+                    return scm_to_utf8_string(scm_symbol_to_string(item));
+                else if (scm_is_string(item))
+                    return scm_to_utf8_string(item);
+                else return empty;
+            };
+        GncMultichoiceOptionIndexVec vec;
+        auto choice_is_list{option.get_ui_type() == GncOptionUIType::LIST}; 
+        if (scm_is_list(new_value))
+        {
+            if (!choice_is_list)
+              throw std::invalid_argument{"Attempt to set multichoice with a list of values."};
+            auto len{scm_to_size_t(scm_length(new_value))};
+            for (std::size_t i = 0; i < len; ++i)
+            {
+                auto item{scm_list_ref(new_value, scm_from_size_t(i))};
+                auto index{option.permissible_value_index(scm_to_str(item))};
+                if (index < size_t_max)
+                    vec.push_back(index);
+            }
+        }
+        else
+        {
+            auto index{option.permissible_value_index(scm_to_str(new_value))};
+            if (index < size_t_max)
+                vec.push_back(index);
+        }
+        return vec;
+    }
+
+    inline SCM scm_from_multichoices(const GncMultichoiceOptionIndexVec& indexes,
+                                     const GncOptionMultichoiceValue& option)
     {
         using KeyType = GncOptionMultichoiceKeyType;
         auto scm_value = [](const char* value, KeyType keytype) -> SCM {
@@ -651,20 +698,22 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             return SCM_BOOL_F;
         };
 
-        if (indexes.size() == 1) // FIXME: Should use bool member to decide
+        if (option.get_ui_type() == GncOptionUIType::MULTICHOICE)
             return scm_value(option.permissible_value(indexes[0]),
                              option.get_keytype(indexes[0]));
-        auto values{scm_list_1(SCM_UNDEFINED)};
+        auto values{SCM_BOOL_F};
         for(auto index : indexes)
         {
             auto val{scm_list_1(scm_value(option.permissible_value(index),
                                           option.get_keytype(index)))};
-            values = scm_append(scm_list_2(val, values));
+            if (scm_is_true(values))
+                values = scm_append(scm_list_2(val, values));
+            else
+                values = val;
         }
         return scm_reverse(values);
     }
 
-
     SCM get_scm_value(const GncOptionMultichoiceValue& option)
     {
 
@@ -688,14 +737,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     SCM get_scm_value(const GncOptionRangeValue<int>& option)
     {
         auto val{option.get_value()};
-        auto desig{val > 100 ? pixels : percent};
+        auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")};
         return scm_cons(desig, scm_from_int(val));
     }
 
     SCM get_scm_default_value(const GncOptionRangeValue<int>& option)
     {
         auto val{option.get_default_value()};
-        auto desig{val > 100 ? pixels : percent};
+        auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")};
         return scm_cons(desig, scm_from_int(val));
     }
 
@@ -764,23 +813,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                         option.set_value(scm_relative_date_get_period(new_value));
                     return;
                 }
+
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionMultichoiceValue>)
                 {
-                    if (scm_is_integer(new_value))
-                    {
-                        option.set_value(scm_to_int(new_value));
-                        return;
-                    }
-                    std::string new_value_str{};
-                    if (scm_is_symbol(new_value))
-                        new_value_str = scm_to_utf8_string(scm_symbol_to_string(new_value));
-                    else if (scm_is_string(new_value))
-                        new_value_str = scm_to_utf8_string(new_value);
-                    if (!new_value_str.empty())
-                        option.set_value(new_value_str);
+                    option.set_multiple(scm_to_multichoices(new_value, option));
                     return;
                 }
+
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionRangeValue<int>>)
                 {
@@ -790,6 +830,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                         option.set_value(scm_to_int(new_value));
                     return;
                 }
+
                 auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
                 option.set_value(value);
             }, swig_get_option($self));
@@ -812,18 +853,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionMultichoiceValue>)
                 {
-                    if (scm_is_integer(new_value))
-                    {
-                        option.set_default_value(scm_to_int(new_value));
-                        return;
-                    }
-                    std::string new_value_str{};
-                    if (scm_is_symbol(new_value))
-                        new_value_str = scm_to_utf8_string(scm_symbol_to_string(new_value));
-                    else if (scm_is_string(new_value))
-                        new_value_str = scm_to_utf8_string(new_value);
-                    if (!new_value_str.empty())
-                        option.set_default_value(new_value_str);
+                    option.set_default_multiple(scm_to_multichoices(new_value,
+                                                                    option));
                     return;
                 }
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
@@ -984,7 +1015,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                 defval = (choices.empty() ? std::string{"None"} :
                           std::get<0>(choices.at(0)));
             return new GncOption{GncOptionMultichoiceValue{section, name, key,
-                        doc_string, defval.c_str(), std::move(choices)}};
+                        doc_string, defval.c_str(), std::move(choices),
+                        GncOptionUIType::MULTICHOICE}};
         }
         catch (const std::exception& err)
         {
@@ -995,12 +1027,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
     GncOption* gnc_make_list_option(const char* section,
                                     const char* name, const char* key,
-                                    const char* doc_string, const char* value,
+                                    const char* doc_string,
+                                    GncMultichoiceOptionIndexVec indexes,
                                     GncMultichoiceOptionChoices&& list)
     {
         try {
             return new GncOption{GncOptionMultichoiceValue{section, name, key,
-                        doc_string, value,  std::move(list),
+                        doc_string, std::move(indexes), std::move(list),
                         GncOptionUIType::LIST}};
         }
         catch (const std::exception& err)

commit 7239eb809ac4b807aebd05020f9b317e4a5c341b
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon May 3 09:39:19 2021 -0700

    Test more natural way of designating relative dates.

diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 0cf743c43..38215ce20 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -212,15 +212,15 @@
   (let* ((option-db (new-gnc-optiondb))
          (date-opt (gnc-register-date-option-set
                     option-db "foo" "bar" "baz" "Phony Option"
-                    (list (RelativeDatePeriod-TODAY)
-                          (RelativeDatePeriod-START-THIS-MONTH)
-                          (RelativeDatePeriod-START-PREV-MONTH)
-                          (RelativeDatePeriod-START-CURRENT-QUARTER)
-                          (RelativeDatePeriod-START-PREV-QUARTER)
-                          (RelativeDatePeriod-START-CAL-YEAR)
-                          (RelativeDatePeriod-START-CAL-YEAR)
-                          (RelativeDatePeriod-START-PREV-YEAR)
-                          (RelativeDatePeriod-START-ACCOUNTING-PERIOD)) #t)))
+                    `(today
+                          start-this-month
+                          start-prev-month
+                          start-current-quarter
+                          start-prev-quarter
+                          start-cal-year
+                          start-cal-year
+                          start-prev-year
+                          start-accounting-period) #t)))
     (test-equal (gnc-accounting-period-fiscal-start)
                 (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-test-date-set-option"))

commit 23461f1d6c1ee029b751e893956be87abf5dc8d8
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon May 3 09:38:43 2021 -0700

    The value of a list-option is a list. Duh.

diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index ae01a460c..0cf743c43 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -189,10 +189,10 @@
          (list-op (gnc-register-list-option option-db "foo" "bar" "baz"
                                             "Phony Option" "AvgBalPlot"
                                             value-list)))
-    (test-equal "AvgBalPlot" (gnc-option-value option-db "foo" "bar"))
-    (gnc-set-option option-db "foo" "bar" "GLPlot")
-    (test-equal "GLPlot" (gnc-option-value option-db "foo" "bar"))
-    (test-equal "AvgBalPlot" (gnc-option-default-value option-db "foo" "bar")))
+    (test-equal '("AvgBalPlot") (gnc-option-value option-db "foo" "bar"))
+    (gnc-set-option option-db "foo" "bar" '("GainPlot" "GLPlot"))
+    (test-equal '("GainPlot" "GLPlot") (gnc-option-value option-db "foo" "bar"))
+    (test-equal '("AvgBalPlot") (gnc-option-default-value option-db "foo" "bar")))
   (test-end "test-gnc-test-list-option"))
 
 (define (test-gnc-make-date-option)

commit 12f8df1eab092bf6326e25a69497562f2eb087bc
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Apr 27 16:08:36 2021 -0700

    Fix test failure: Clear the stream before reusing it.

diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index e4667d7f1..d715c268e 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -936,6 +936,7 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
                                                GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
     acc_guids = make_account_list_SCM_str(acclistbad);
+    iss.clear();
     iss.str(acc_guids);
     sel_option.from_scheme(iss);
     EXPECT_EQ(accsel, sel_option.get_value<GncOptionAccountList>());

commit be322a0d7cf863696e7c355517972f056bdc5ce1
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 11 18:15:57 2021 -0800

    Group GncOptionMultChoiceValue::set_multiple with set_default_multiple.
    
    Better readability.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index edbf9e191..9193a22ed 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -668,13 +668,6 @@ public:
             throw std::invalid_argument("Value not a valid choice.");
 
     }
-    void set_multiple(const GncMultichoiceOptionIndexVec& indexes)
-    {
-        if (validate(indexes))
-            m_value = indexes;
-        else
-            throw std::invalid_argument("One of the supplied indexes was out of range.");
-    }
     void set_default_value(const std::string& value)
     {
         auto index = find_key(value);
@@ -702,6 +695,13 @@ public:
             throw std::invalid_argument("Value not a valid choice.");
 
     }
+    void set_multiple(const GncMultichoiceOptionIndexVec& indexes)
+    {
+        if (validate(indexes))
+            m_value = indexes;
+        else
+            throw std::invalid_argument("One of the supplied indexes was out of range.");
+    }
     void set_default_multiple(const GncMultichoiceOptionIndexVec& indexes)
     {
         if (validate(indexes))

commit 27670a6e98865d2b8cb6c0702b8130f6a463af06
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 11 18:13:35 2021 -0800

    Make the 'pixel and 'percent SCM symbols C++ constants.
    
    Improves efficiency and conciseness.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index f46446c26..88094f127 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -623,6 +623,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     %}
 
 %ignore GncOptionMultichoiceKeyType;
+%ignore pixels;
+%ignore percent;
+
+%header %{
+    static const SCM pixels{(scm_from_utf8_symbol("pixels"))};
+    static const SCM percent{(scm_from_utf8_symbol("percent"))};
+    %}
 
 %inline %{
     inline SCM scm_from_multichoices (const GncMultichoiceOptionIndexVec& indexes,
@@ -681,14 +688,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     SCM get_scm_value(const GncOptionRangeValue<int>& option)
     {
         auto val{option.get_value()};
-        auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")};
+        auto desig{val > 100 ? pixels : percent};
         return scm_cons(desig, scm_from_int(val));
     }
 
     SCM get_scm_default_value(const GncOptionRangeValue<int>& option)
     {
         auto val{option.get_default_value()};
-        auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")};
+        auto desig{val > 100 ? pixels : percent};
         return scm_cons(desig, scm_from_int(val));
     }
 

commit a94f69d6e04329e92d94e844d4ca06e9c74d70fc
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 11 18:06:31 2021 -0800

    Pretty up some SFINAE formatting for easier reading.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 09eb8d20d..edbf9e191 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -334,7 +334,12 @@ operator>> <GncOptionValue<bool>>(std::istream& iss,
     opt.set_value(instr == "#t" ? true : false);
     return iss;
 }
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*>>, int> = 0>
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                GncOptionValidatedValue<const QofInstance*>> ||
+                                   std::is_same_v<std::decay_t<OptType>,
+                                      GncOptionValue<const QofInstance*>>,
+                                   int> = 0>
 inline std::ostream&
 gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
 {
@@ -363,7 +368,12 @@ gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
     return oss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*>>, int> = 0>
+template<class OptType,
+         typename std::enable_if_t<
+             std::is_same_v<std::decay_t<OptType>,
+                            GncOptionValidatedValue<const QofInstance*>> ||
+             std::is_same_v<std::decay_t<OptType>,
+                            GncOptionValue<const QofInstance*>>, int> = 0>
 inline std::istream&
 gnc_option_from_scheme (std::istream& iss, OptType& opt)
 {
@@ -791,15 +801,18 @@ operator>> <GncOptionMultichoiceValue>(std::istream& iss,
     return iss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionMultichoiceValue>, int> = 0>
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionMultichoiceValue>,
+                                   int> = 0>
 inline std::ostream&
 gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
 {
     auto indexes{opt.get_multiple()};
-    if (indexes.size() > 1)
+    if (indexes.m_vec.size() > 1)
         oss << "'(";
     bool first = true;
-    for (auto index : indexes)
+    for (auto index : indexes.m_vec)
     {
         if (first)
             first = false;
@@ -812,7 +825,10 @@ gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
     return oss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionMultichoiceValue>, int> = 0>
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionMultichoiceValue>,
+                                   int> = 0>
 inline std::istream&
 gnc_option_from_scheme(std::istream& iss, OptType& opt)
 {
@@ -971,7 +987,10 @@ operator>> <GncOptionAccountValue>(std::istream& iss,
     return iss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionAccountValue>, int> = 0>
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionAccountValue>,
+                                   int> = 0>
 inline std::ostream&
 gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
 {
@@ -990,7 +1009,10 @@ gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
     return oss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionAccountValue>, int> = 0>
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionAccountValue>,
+                                   int> = 0>
 inline std::istream&
 gnc_option_from_scheme(std::istream& iss, OptType& opt)
 {

commit 0f8446a1bb550c6ae62d61576d168b7c08df7d99
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 11 18:04:12 2021 -0800

    Remove some no longer needed diagnostics.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 5588499e0..3bbd4e405 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -51,11 +51,8 @@ public:
                              return std::strcmp(old_name, alias.first) == 0;
                          });
         if (alias == c_option_aliases.end())
-        {
-            std::cerr << "No alias for " << old_name << " found.\n";
             return nullptr;
-        }
-        std::cerr << "Found " << alias->second.second << " as alias for " << old_name << ".\n";
+
         return &alias->second;
     }
 };

commit f66a918be463bc36a3ecd6c5c8d28eac05837df8
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 11 18:03:36 2021 -0800

    Implement GncOptionValue<const QofQuery*>
    
    QofQuery isn't a QofInstance and doesn't have a GUID.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 4b2f84de3..09eb8d20d 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -1157,4 +1157,34 @@ operator>> <GncOptionDateValue>(std::istream& iss,
     return opt.in_stream(iss);
 }
 
+/** QofQuery Options
+ */
+
+inline std::istream&
+gnc_option_from_scheme(std::istream& iss, GncOptionValue<const QofQuery*>& opt)
+{
+//FIXME: Implement or maybe rethink.
+    return iss;
+}
+
+inline std::ostream&
+gnc_option_to_scheme(std::ostream& oss, GncOptionValue<const QofQuery*>& opt)
+{
+//FIXME: Implement or maybe rethink.
+    return oss;
+}
+
+inline std::istream&
+gnc_option_from_scheme(std::istream& iss, GncOptionValidatedValue<const QofQuery*>& opt)
+{
+//FIXME: Implement or maybe rethink.
+    return iss;
+}
+
+inline std::ostream&
+gnc_option_to_scheme(std::ostream& oss, GncOptionValidatedValue<const QofQuery*>& opt)
+{
+//FIXME: Implement or maybe rethink.
+    return oss;
+}
 #endif //GNC_OPTION_IMPL_HPP_
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 4ef3af971..6f708681b 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -481,8 +481,12 @@ GncOption::from_scheme(std::istream& iss)
                                (std::is_same_v<std::decay_t<decltype(option)>,
                                 GncOptionMultichoiceValue>) ||
                                std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionValue<const QofQuery*>> ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionValue<const QofInstance*>> ||
                                std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionValidatedValue<const QofQuery*>> ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionValidatedValue<const QofInstance*>>)
                               gnc_option_from_scheme(iss, option);
                           else if constexpr
@@ -571,6 +575,8 @@ template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, const QofInstance*, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, SCM, GncOptionUIType);
+template GncOption::GncOption(const char*, const char*, const char*,
+                              const char*, const QofQuery*, GncOptionUIType);
 
 template bool GncOption::get_value<bool>() const;
 template int GncOption::get_value<int>() const;
@@ -634,6 +640,7 @@ template bool GncOption::validate(double) const;
 template bool GncOption::validate(const char*) const;
 template bool GncOption::validate(std::string) const;
 template bool GncOption::validate(const QofInstance*) const;
+template bool GncOption::validate(const QofQuery*) const;
 template bool GncOption::validate(RelativeDatePeriod) const;
 template bool GncOption::validate(GncMultichoiceOptionIndexVec) const;
 
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 401c84f3a..5edbc9404 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -40,6 +40,8 @@ extern "C"
 struct OptionClassifier;
 class GncOptionUIItem;
 using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
+struct _QofQuery;
+using QofQuery = _QofQuery;
 struct QofInstance_s;
 using QofInstance = QofInstance_s;
 template <typename ValueType> class GncOptionValue;
@@ -53,12 +55,14 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<bool>,
                                       GncOptionValue<int64_t>,
                                       GncOptionValue<const QofInstance*>,
+                                      GncOptionValue<const QofQuery*>,
                                       GncOptionValue<SCM>,
                                       GncOptionAccountValue,
                                       GncOptionMultichoiceValue,
                                       GncOptionRangeValue<int>,
                                       GncOptionRangeValue<double>,
                                       GncOptionValidatedValue<const QofInstance*>,
+                                      GncOptionValidatedValue<const QofQuery*>,
                                       GncOptionDateValue>;
 
 using GncOptionVariantPtr = std::unique_ptr<GncOptionVariant>;
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 0541644b3..f46446c26 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -208,6 +208,13 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
     return SWIG_NewPointerObj(ptr, type, FALSE);
 }
 
+template <> inline SCM
+scm_from_value<const QofQuery*>(const QofQuery* value)
+{
+    auto ptr{static_cast<void*>(const_cast<QofQuery*>(value))};
+    return SWIG_NewPointerObj(ptr, SWIGTYPE_p__QofQuery, FALSE);
+}
+
 template <typename ValueType> inline ValueType
 scm_to_value(SCM new_value)
 {
@@ -273,6 +280,16 @@ scm_to_value<const QofInstance*>(SCM new_value)
     return static_cast<const QofInstance*>(ptr);
 }
 
+template <> inline const QofQuery*
+scm_to_value<const QofQuery*>(SCM new_value)
+{
+    if (new_value == SCM_BOOL_F)
+        return nullptr;
+    void* ptr{};
+    SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p__QofQuery, 0);
+    return static_cast<const QofQuery*>(ptr);
+}
+
 template <>inline GncOptionAccountList
 scm_to_value<GncOptionAccountList>(SCM new_value)
 {
@@ -689,6 +706,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %template(gnc_make_bool_option) gnc_make_option<bool>;
 %template(gnc_make_int64_option) gnc_make_option<int64_t>;
 %template(gnc_make_qofinstance_option) gnc_make_option<const QofInstance*>;
+%template(gnc_make_query_option) gnc_make_option<const QofQuery*>;
 
 %extend GncOption {
     SCM get_scm_value()

commit 0a8f66ee9048bdffb75840f76ec3928e394e50af
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 11 17:56:53 2021 -0800

    Fold GncOptionGncOwnerValue into GncOptionValue<const QofQuery*>.
    
    Reflecting the way Scheme options handles it.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 210ef9967..b12ca23aa 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -211,8 +211,8 @@ qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
         case GncOptionUIType::BUDGET:
             qof_type = "Budget";
             break;
-        case GncOptionUIType::OWNER:
-            qof_type = "gncOwner";
+        case GncOptionUIType::JOB:
+            qof_type = "gncJob";
             break;
         case GncOptionUIType::CUSTOMER:
             qof_type = "gncCustomer";
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index b1e784a45..22098bb72 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -51,6 +51,7 @@ enum class GncOptionUIType : unsigned int
     VENDOR,
     EMPLOYEE,
     INVOICE,
+    JOB,
     TAX_TABLE,
     QUERY,
     REPORT_LIST,
diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 472b1ca24..95f77a34b 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -36,7 +36,10 @@ extern "C"
 #include <config.h>
 #include <qof.h>
 #include <gncInvoice.h>
-#include <gncOwner.h>
+#include <gncCustomer.h>
+#include <gncEmployee.h>
+#include <gncJob.h>
+#include <gncVendor.h>
 #include <gncTaxTable.h>
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 82b52928a..5588499e0 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1070,38 +1070,6 @@ gnc_register_internal_option(GncOptionDB* db, const char* section,
             GncOptionUIType::INTERNAL};
     db->register_option(section, std::move(option));
 }
-
-static inline GncOptionUIType
-owner_type_to_ui_type(GncOwnerType type)
-{
-    switch (type)
-    {
-        case GNC_OWNER_NONE:
-        case GNC_OWNER_UNDEFINED:
-        case GNC_OWNER_JOB:
-            return GncOptionUIType::INTERNAL;
-        case GNC_OWNER_CUSTOMER:
-            return GncOptionUIType::CUSTOMER;
-        case GNC_OWNER_VENDOR:
-            return GncOptionUIType::VENDOR;
-        case GNC_OWNER_EMPLOYEE:
-            return GncOptionUIType::EMPLOYEE;
-    }
-    return GncOptionUIType::INTERNAL;
-}
-
-void
-gnc_register_owner_option(GncOptionDB* db, const char* section,
-                          const char* name, const char* key,
-                          const char* doc_string, GncOwner* value,
-                          GncOwnerType type)
-{
-    GncOption option{section, name, key, doc_string,
-                     (const QofInstance*)value->owner.undefined,
-                     owner_type_to_ui_type(type)};
-    db->register_option(section, std::move(option));
-}
-
 void
 gnc_register_invoice_option(GncOptionDB* db, const char* section,
                             const char* name, const char* key,
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index 885715622..007bfd1f4 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -44,7 +44,6 @@ extern "C"
 #include <gnc-budget.h>
 #include <gnc-commodity.h>
 #include <gncInvoice.h>
-#include <gncOwner.h>
 #include <gncTaxTable.h>
 
 /**
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 3e8a96ee5..a5b6c427b 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -37,7 +37,6 @@ extern "C"
 #include <gnc-budget.h>
 #include <gnc-commodity.h>
 #include <gncInvoice.h>
-#include <gncOwner.h>
 #include <gncTaxTable.h>
 }
 #include "gnc-option.hpp"
@@ -620,33 +619,6 @@ inline void gnc_register_invoice_option(const GncOptionDBPtr& db,
                                 doc_string, value);
 }
 
-/**
- * Create a new owner-type option and register it in the options database.
- *
- * @param db A GncOptionDB* for calling from C. Caller retains ownership.
- * @param section The database section for the option.
- * @param name The option name.
- * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
- * @param value The initial and default value for the option.
- * @param type The Owner-type (Customer, Employee, or Vendor)
- */
-void gnc_register_owner_option(GncOptionDB* db, const char* section,
-                               const char* name, const char* key,
-                               const char* doc_string, GncOwner* value,
-                               GncOwnerType type);
-
-/**
- * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
- */
-inline void gnc_register_owner_option(const GncOptionDBPtr& db,
-                                      const char* section, const char* name,
-                                      const char* key, const char* doc_string,
-                                      GncOwner* value, GncOwnerType type)
-{
-    gnc_register_owner_option(db.get(), section, name, key,
-                              doc_string, value, type);
-}
-
 /**
  * Create a new taxtable option and register it in the options database.
  *
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 130e50878..0541644b3 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -64,25 +64,33 @@ SCM scm_init_sw_gnc_optiondb_module(void);
   */
 
 %typemap (out) QofInstance_s* {
+    swig_type_info *type = $descriptor(QofInstance_s);
     if ($1 == nullptr)
     {
-        $result = SCM_BOOL_F;
+        $result = SWIG_NewPointerObj(nullptr, type, FALSE);
     }
     else
     {
 
         auto ptr{static_cast<void*>(const_cast<QofInstance*>($1))};
-        swig_type_info *type = SWIGTYPE_p_QofInstance_s;
         if (GNC_IS_COMMODITY($1))
-            type = SWIGTYPE_p_gnc_commodity;
+            type = $descriptor(gnc_commodity*);
         else if (GNC_IS_ACCOUNT($1))
-            type = SWIGTYPE_p_Account;
+            type = $descriptor(Account*);
         else if (GNC_IS_BUDGET($1))
-            type = SWIGTYPE_p_GncBudget;
+            type = $descriptor(GncBudget*);
         else if (GNC_IS_INVOICE($1))
-            type = SWIGTYPE_p_GncInvoice;
+            type = $descriptor(GncInvoice*);
         else if (GNC_IS_TAXTABLE($1))
-            type = SWIGTYPE_p_GncTaxTable;
+            type = $descriptor(GncTaxTable*);
+        else if (GNC_IS_CUSTOMER($1))
+            type = $descriptor(_gncCustomer*);
+        else if (GNC_IS_EMPLOYEE($1))
+            type = $descriptor(_gncEmployee*);
+        else if (GNC_IS_JOB($1))
+            type = $descriptor(_gncJob*);
+        else if (GNC_IS_VENDOR($1))
+            type = $descriptor(_gncVendor*);
         $result = SWIG_NewPointerObj(ptr, type, FALSE);
     }
 }
@@ -90,10 +98,12 @@ SCM scm_init_sw_gnc_optiondb_module(void);
 %typemap (in) QofInstance_s* {
     if (scm_is_true($input))
     {
-        static const std::array<swig_type_info*, 6> types {
-            SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
-                SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice,
-                SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account
+        static const std::array<swig_type_info*, 10> types {
+            $descriptor(QofInstance_s*), $descriptor(gnc_commodity*),
+                $descriptor(GncBudget*), $descriptor(GncInvoice*),
+                $descriptor(GncTaxTable*), $descriptor(Account*),
+                $descriptor(_gncCustomer*), $descriptor(_gncEmployee*),
+                $descriptor(_gncJob*), $descriptor(_gncVendor*)
                 };
         void* ptr{};
         auto pos = std::find_if(types.begin(), types.end(),
@@ -168,10 +178,11 @@ scm_from_value<double>(double value)
 template <> inline SCM
 scm_from_value<const QofInstance*>(const QofInstance* value)
 {
-    if (!value) return SCM_BOOL_F;
+    swig_type_info *type = SWIGTYPE_p_QofInstance_s;
+    if (!value)
+        return SWIG_NewPointerObj(nullptr, type, FALSE);
 
     auto ptr{static_cast<void*>(const_cast<QofInstance*>(value))};
-    swig_type_info *type = SWIGTYPE_p_QofInstance_s;
     if (GNC_IS_COMMODITY(value))
         type = SWIGTYPE_p_gnc_commodity;
     else if (GNC_IS_ACCOUNT(value))
@@ -182,6 +193,14 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
         type = SWIGTYPE_p_GncInvoice;
     else if (GNC_IS_TAXTABLE(value))
         type = SWIGTYPE_p_GncTaxTable;
+    else if (GNC_IS_CUSTOMER(value))
+        type = SWIGTYPE_p__gncCustomer;
+    else if (GNC_IS_EMPLOYEE(value))
+        type = SWIGTYPE_p__gncEmployee;
+    else if (GNC_IS_JOB(value))
+        type = SWIGTYPE_p__gncJob;
+    else if (GNC_IS_VENDOR(value))
+        type = SWIGTYPE_p__gncVendor;
 /* There is no type macro for QofQuery, it's not a GObject.
     else if (GNC_IS_QOFQUERY(value))
         type = SWIGTYPE_p_Query;
@@ -236,10 +255,12 @@ scm_to_value<const QofInstance*>(SCM new_value)
     if (new_value == SCM_BOOL_F)
         return nullptr;
 
-    static const std::array<swig_type_info*, 6> types{
+    static const std::array<swig_type_info*, 10> types{
             SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
             SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice,
-            SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account
+            SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account,
+            SWIGTYPE_p__gncCustomer, SWIGTYPE_p__gncEmployee,
+            SWIGTYPE_p__gncJob, SWIGTYPE_p__gncVendor
                 };
     void* ptr{};
     auto pos = std::find_if(types.begin(), types.end(),
@@ -502,7 +523,6 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %ignore gnc_register_internal_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
 %ignore gnc_register_currency_option(GncOptionDB*, const char*, const char*, const char*, const char*, gnc_commodity*);
 %ignore gnc_register_invoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncInvoice*);
-%ignore gnc_register_owner_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncOwner*, GncOwnerType);
 %ignore gnc_register_taxtable_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncTaxTable*);
 %ignore gnc_register_counter_option(GncOptionDB*, const char*, const char*, const char*, const char*, double);
 %ignore gnc_register_counter_format_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);

commit 418eb066206c26c37f4d7d7af63b8d6f8cde7faf
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 11 17:50:57 2021 -0800

    Remove GncOptionDateValue::set_default_value(size_t)
    
    Because it's ambiguous with set_default_value(time64).

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 961769de7..210ef9967 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -131,15 +131,6 @@ GncOptionDateValue::set_value(size_t index) noexcept
     m_period = m_period_set[index];
 }
 
-void
-GncOptionDateValue::set_default_value(size_t index) noexcept
-{
-    assert(!m_period_set.empty());
-    assert(index < m_period_set.size());
-    m_date = m_default_date = INT64_MAX;
-    m_period = m_default_period = m_period_set[index];
-}
-
 size_t
 GncOptionDateValue::permissible_value_index(const char* key) const noexcept
 {
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 02f58c212..4b2f84de3 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -1108,7 +1108,6 @@ public:
             m_date = m_default_date = time;
         }
     }
-    void set_default_value(size_t index) noexcept;
     std::size_t num_permissible_values() const noexcept
     {
         return m_period_set.size();

commit 7d0cdf7c94099aac372409c86d26ec004d0e7f01
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Mar 8 11:16:35 2021 -0800

    Handle #f input to option typemap(in)s.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 1dc5fd05d..130e50878 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -341,7 +341,7 @@ gnc_option_test_book_destroy(QofBook* book)
 %typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices)
 {
     using KeyType = GncOptionMultichoiceKeyType;
-    auto len = scm_to_size_t(scm_length($input));
+    auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM vec = scm_list_ref($input, scm_from_size_t(i));
@@ -375,7 +375,7 @@ gnc_option_test_book_destroy(QofBook* book)
 
 %typemap(in) GncOptionAccountList
 {
-    auto len = scm_to_size_t(scm_length($input));
+    auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));
@@ -387,7 +387,7 @@ gnc_option_test_book_destroy(QofBook* book)
 
 %typemap(in) GncOptionAccountTypeList& (GncOptionAccountTypeList types)
 {
-    auto len = scm_to_size_t(scm_length($input));
+    auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM s_type = scm_list_ref($input, scm_from_size_t(i));
@@ -399,7 +399,7 @@ gnc_option_test_book_destroy(QofBook* book)
 
 %typemap(in) GncOptionAccountTypeList&& (GncOptionAccountTypeList types)
 {
-    auto len = scm_to_size_t(scm_length($input));
+    auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM s_type = scm_list_ref($input, scm_from_size_t(i));
@@ -411,7 +411,7 @@ gnc_option_test_book_destroy(QofBook* book)
 
 %typemap(in) GncOptionAccountList
 {
-    auto len = scm_to_size_t(scm_length($input));
+    auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));
@@ -423,7 +423,7 @@ gnc_option_test_book_destroy(QofBook* book)
 
 %typemap(in) GncOptionAccountList& (GncOptionAccountList acclist)
 {
-    auto len = scm_to_size_t(scm_length($input));
+    auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));

commit 86a2f1551fc5910096f9c21e87711598abee876f
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Mar 8 11:14:01 2021 -0800

    Implement gnc-make-date-option.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 60d86eea0..1dc5fd05d 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -878,6 +878,51 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
+    GncOption* gnc_make_date_option(const char* section,
+                                    const char* name, const char* key,
+                                    const char* doc_string,
+                                    const SCM default_val,
+                                    RelativeDatePeriodVec& period_set,
+                                    bool both)
+    {
+
+        try {
+            auto absolute{scm_date_absolute(default_val)};
+            auto ui_type = both ? GncOptionUIType::DATE_BOTH : absolute ?
+                GncOptionUIType::DATE_ABSOLUTE : GncOptionUIType::DATE_RELATIVE;
+            if (!period_set.empty())
+            {
+                auto retval{new GncOption{GncOptionDateValue(section, name, key,
+                                                             doc_string, ui_type,
+                                                             period_set)}};
+                if (absolute)
+                    retval->set_default_value(scm_absolute_date_to_time64(default_val));
+                else
+                    retval->set_default_value(scm_relative_date_get_period(default_val));
+                return retval;
+            }
+
+            if (absolute)
+            {
+                auto value{scm_absolute_date_to_time64(default_val)};
+                auto retval{new GncOption{GncOptionDateValue(section, name, key,
+                                                             doc_string, ui_type,
+                                                             value)}};
+                return retval;
+            }
+            auto value{scm_relative_date_get_period(default_val)};
+            auto retval{new GncOption{GncOptionDateValue(section, name, key,
+                                                         doc_string, ui_type,
+                                                         period_set)}};
+            return retval;
+        }
+        catch (const std::invalid_argument& err)
+        {
+            std::cerr <<"Date Option, value failed validation, option not creted.\n";
+            return nullptr;
+        }
+    }
+
     GncOption* gnc_make_multichoice_option(const char* section,
                                            const char* name, const char* key,
                                            const char* doc_string,

commit 693e966bf958b540017e6cc0c1e6c20d3c10f5fd
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Mar 8 11:13:26 2021 -0800

    Extract Scheme date handling functions and apply them.
    
    Including in RelativeDatePeriodVec typemap, making it work correctly.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index cfd4ec771..60d86eea0 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -329,11 +329,11 @@ gnc_option_test_book_destroy(QofBook* book)
 
 %typemap(in) RelativeDatePeriodVec& (RelativeDatePeriodVec period_set)
 {
-    auto len = scm_to_size_t(scm_length($input));
+    auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0;
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM s_reldateperiod = scm_list_ref($input, scm_from_size_t(i));
-        period_set.push_back((RelativeDatePeriod)scm_to_int(s_reldateperiod));
+        period_set.push_back(scm_relative_date_get_period(s_reldateperiod));
     }
     $1 = &period_set;
 }
@@ -517,8 +517,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %typemap(out) GncOption* "$result = ($1) ? scm_from_pointer($1, nullptr) : SCM_BOOL_F;"
 
 %header %{
-    static const SCM get_reldate_values() {
 
+    inline static RelativeDatePeriod scm_relative_date_get_period(SCM date)
+    {
     static SCM reldate_values = SCM_BOOL_F;
 
     if (scm_is_false(reldate_values))
@@ -556,8 +557,32 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)"
             "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))");
 
-    return reldate_values;
+    auto reldate_scm{scm_is_pair(date) ? scm_cdr(date) : date};
+    auto reldate{scm_primitive_eval(scm_assq_ref(reldate_values,
+                                                 reldate_scm))};
+    return static_cast<RelativeDatePeriod>(scm_to_int(reldate));
+
+    }
+
+    inline static bool scm_date_absolute(SCM date)
+    {
+        if (scm_is_pair(date))
+        {
+            auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(date)))};
+            if (strcmp(car, "relative") == 0)
+                return false;
         }
+        return true;
+    }
+
+    inline static time64 scm_absolute_date_to_time64(SCM date)
+    {
+        if (scm_date_absolute(date))
+            return scm_to_int64(scm_is_pair(date) ? scm_cdr(date) : date);
+
+        return gnc_relative_date_to_time64(scm_relative_date_get_period(date));
+        }
+
     %}
 
 %ignore GncOptionMultichoiceKeyType;
@@ -684,28 +709,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         if (!$self)
             return;
-        auto reldate_values{get_reldate_values()};
-        std::visit([new_value, reldate_values](auto& option) {
+        std::visit([new_value](auto& option) {
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionDateValue>)
                 {
-                    if (scm_is_pair(new_value))
-                    {
-                        auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(new_value)))};
-                        if (strcmp(car, "relative") == 0)
-                        {
-                            auto lookup{scm_assq_ref(reldate_values,
-                                                     scm_cdr(new_value))};
-                            auto reldate{scm_primitive_eval(lookup)};
-                            option.set_value(static_cast<RelativeDatePeriod>(scm_to_int(reldate)));
-                        }
-                        else
-                        {
-                            option.set_value(scm_to_int64(scm_cdr(new_value)));
-                        }
-                    }
+                    if (scm_date_absolute(new_value))
+                        option.set_value(scm_absolute_date_to_time64(new_value));
                     else
-                    option.set_value(scm_to_int64(new_value));
+                        option.set_value(scm_relative_date_get_period(new_value));
                     return;
                 }
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
@@ -743,28 +754,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         if (!$self)
             return;
-        auto reldate_values{get_reldate_values()};
-        std::visit([new_value, reldate_values](auto& option) {
+        std::visit([new_value](auto& option) {
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionDateValue>)
                 {
-                    if (scm_is_pair(new_value))
-                    {
-                        auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(new_value)))};
-                        if (strcmp(car, "relative") == 0)
-                        {
-                            auto lookup{scm_assq_ref(reldate_values,
-                                                     scm_cdr(new_value))};
-                            auto reldate{scm_primitive_eval(lookup)};
-                            option.set_default_value(static_cast<RelativeDatePeriod>(scm_to_int(reldate)));
-                        }
-                        else
-                        {
-                            option.set_value(scm_to_int64(scm_cdr(new_value)));
-                        }
-                    }
+                    if (scm_date_absolute(new_value))
+                        option.set_default_value(scm_absolute_date_to_time64(new_value));
                     else
-                        option.set_default_value(scm_to_int64(new_value));
+                        option.set_default_value(scm_relative_date_get_period(new_value));
                     return;
                 }
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,

commit 63502900f3dc9992a38bbd140a3e1a4830aa32cd
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Mar 5 16:15:12 2021 -0800

    Permit GncOptionAccountValue to have a nil default value.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 16310597f..961769de7 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -40,7 +40,7 @@ bool
 GncOptionAccountValue::validate(const GncOptionAccountList& values) const
 {
     if (values.empty())
-        return false;
+        return true;
     if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) &&
         values.size() != 1)
         return false;

commit 42185c0ec8dc926912a440ced0fae83755d97086
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Mar 5 16:13:09 2021 -0800

    typemap for std::size_t
    
    Unaccountably missing from swig_guile.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index dcf957136..cfd4ec771 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -38,6 +38,9 @@ namespace std {
 
 %enddef
 
+%typemap(in) std::size_t "$1 = scm_to_ulong($input);";
+%typemap(out) std::size_t "$result = scm_from_ulong($1);";
+
  //%module sw_gnc_optiondb
 %{
 #include "gnc-optiondb.h"

commit 5c743378107ea6f34d2920f025efc07e0be60d51
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Mar 5 15:56:34 2021 -0800

    Add function GncOption::set_default_value.
    
    Allows reports to derive from other reports and then change the option
    default values to suit. If they change only the values and not the defaults
    then it's possible to create saved report configs that don't include the
    options whose values are changed to the base report's default value. See
    https://bugs.gnucash.org/show_bug.cgi?id=642292.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 5c92c214f..16310597f 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -131,6 +131,15 @@ GncOptionDateValue::set_value(size_t index) noexcept
     m_period = m_period_set[index];
 }
 
+void
+GncOptionDateValue::set_default_value(size_t index) noexcept
+{
+    assert(!m_period_set.empty());
+    assert(index < m_period_set.size());
+    m_date = m_default_date = INT64_MAX;
+    m_period = m_default_period = m_period_set[index];
+}
+
 size_t
 GncOptionDateValue::permissible_value_index(const char* key) const noexcept
 {
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 9e09b1413..02f58c212 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -91,7 +91,6 @@ protected:
 };
 */
 
-
 static const char* commodity_scm_intro{"'(commodity-scm "};
 #ifndef SWIG
 size_t constexpr classifier_size_max{50};
@@ -129,6 +128,9 @@ public:
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
     void set_value(ValueType new_value) { m_value = new_value; }
+    void set_default_value(ValueType new_value) {
+        m_value = m_default_value = new_value;
+    }
     void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
@@ -183,6 +185,13 @@ public:
         else
             throw std::invalid_argument("Validation failed, value not set.");
     }
+    void set_default_value(ValueType value)
+    {
+        if (this->validate(value))
+            m_value = m_default_value = value;
+        else
+            throw std::invalid_argument("Validation failed, value not set.");
+    }
     void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     std::ostream& to_scheme(std::ostream&) const;
@@ -426,6 +435,13 @@ public:
         else
             throw std::invalid_argument("Validation failed, value not set.");
     }
+    void set_default_value(ValueType value)
+    {
+        if (this->validate(value))
+            m_value = m_default_value = value;
+        else
+            throw std::invalid_argument("Validation failed, value not set.");
+    }
     void get_limits(ValueType& upper, ValueType& lower, ValueType& step) const noexcept
     {
         upper = m_max;
@@ -649,6 +665,40 @@ public:
         else
             throw std::invalid_argument("One of the supplied indexes was out of range.");
     }
+    void set_default_value(const std::string& value)
+    {
+        auto index = find_key(value);
+        if (index != size_t_max)
+        {
+            m_value.clear();
+            m_value.push_back(index);
+            m_default_value.clear();
+            m_default_value.push_back(index);
+        }
+        else
+            throw std::invalid_argument("Value not a valid choice.");
+
+    }
+    void set_default_value(size_t index)
+    {
+        if (index < m_choices.size())
+        {
+            m_value.clear();
+            m_value.push_back(index);
+            m_default_value.clear();
+            m_default_value.push_back(index);
+        }
+        else
+            throw std::invalid_argument("Value not a valid choice.");
+
+    }
+    void set_default_multiple(const GncMultichoiceOptionIndexVec& indexes)
+    {
+        if (validate(indexes))
+            m_value = m_default_value = indexes;
+        else
+            throw std::invalid_argument("One of the supplied indexes was out of range.");
+    }
     std::size_t num_permissible_values() const noexcept
     {
         return m_choices.size();
@@ -866,6 +916,11 @@ public:
             //throw!
             m_value = values;
     }
+    void set_default_value (const GncOptionAccountList& values) {
+        if (validate(values))
+            //throw!
+            m_value = m_default_value = values;
+    }
     GList* account_type_list() const noexcept;
     void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
@@ -1039,6 +1094,21 @@ public:
         }
     }
     void set_value(size_t index) noexcept;
+    void set_default_value(RelativeDatePeriod value) {
+        if (validate(value))
+        {
+            m_period = m_default_period = value;
+            m_date = m_default_date = INT64_MAX;
+        }
+    }
+    void set_default_value(time64 time) {
+        if (validate(time))
+        {
+            m_period = m_default_period = RelativeDatePeriod::ABSOLUTE;
+            m_date = m_default_date = time;
+        }
+    }
+    void set_default_value(size_t index) noexcept;
     std::size_t num_permissible_values() const noexcept
     {
         return m_period_set.size();
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 701b80541..4ef3af971 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -126,6 +126,27 @@ GncOption::set_value(ValueType value)
                }, *m_option);
 }
 
+template <typename ValueType> void
+GncOption::set_default_value(ValueType value)
+{
+    std::visit([value](auto& option) {
+                   if constexpr
+                       (std::is_same_v<std::decay_t<decltype(option.get_value())>,
+                        std::decay_t<ValueType>> ||
+                        (std::is_same_v<std::decay_t<decltype(option)>,
+                         GncOptionDateValue> &&
+                         (std::is_same_v<std::decay_t<ValueType>,
+                         RelativeDatePeriod> ||
+                          std::is_same_v<std::decay_t<ValueType>, size_t>)))
+                           option.set_default_value(value);
+                   if constexpr
+                       (std::is_same_v<std::decay_t<decltype(option)>,
+                        GncOptionMultichoiceValue> &&
+                        std::is_same_v<std::decay_t<ValueType>,
+                        GncMultichoiceOptionIndexVec>)
+                       option.set_multiple(value);
+               }, *m_option);
+}
 void
 GncOption::reset_default_value()
 {
@@ -590,6 +611,20 @@ template void GncOption::set_value(GncOptionAccountList);
 template void GncOption::set_value(GncMultichoiceOptionIndexVec);
 template void GncOption::set_value(SCM);
 
+template void GncOption::set_default_value(bool);
+template void GncOption::set_default_value(int);
+template void GncOption::set_default_value(int64_t);
+template void GncOption::set_default_value(double);
+template void GncOption::set_default_value(char*);
+template void GncOption::set_default_value(const char*);
+template void GncOption::set_default_value(std::string);
+template void GncOption::set_default_value(const QofInstance*);
+template void GncOption::set_default_value(RelativeDatePeriod);
+template void GncOption::set_default_value(size_t);
+template void GncOption::set_default_value(GncOptionAccountList);
+template void GncOption::set_default_value(GncMultichoiceOptionIndexVec);
+template void GncOption::set_default_value(SCM);
+
 template void GncOption::get_limits(double&, double&, double&) const noexcept;
 template void GncOption::get_limits(int&, int&, int&) const noexcept;
 template bool GncOption::validate(bool) const;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index aa7fc3746..401c84f3a 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -88,6 +88,7 @@ public:
               ValueType value,
               GncOptionUIType ui_type = GncOptionUIType::INTERNAL);
     template <typename ValueType> void set_value(ValueType value);
+    template <typename ValueType> void set_default_value(ValueType value);
     template <typename ValueType> ValueType get_default_value() const;
     template <typename ValueType> ValueType get_value() const;
     void reset_default_value();
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index ac893a53a..dcf957136 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -735,6 +735,81 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                 option.set_value(value);
             }, swig_get_option($self));
     }
+
+    void set_default_value_from_scm(SCM new_value)
+    {
+        if (!$self)
+            return;
+        auto reldate_values{get_reldate_values()};
+        std::visit([new_value, reldate_values](auto& option) {
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionDateValue>)
+                {
+                    if (scm_is_pair(new_value))
+                    {
+                        auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(new_value)))};
+                        if (strcmp(car, "relative") == 0)
+                        {
+                            auto lookup{scm_assq_ref(reldate_values,
+                                                     scm_cdr(new_value))};
+                            auto reldate{scm_primitive_eval(lookup)};
+                            option.set_default_value(static_cast<RelativeDatePeriod>(scm_to_int(reldate)));
+                        }
+                        else
+                        {
+                            option.set_value(scm_to_int64(scm_cdr(new_value)));
+                        }
+                    }
+                    else
+                        option.set_default_value(scm_to_int64(new_value));
+                    return;
+                }
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionMultichoiceValue>)
+                {
+                    if (scm_is_integer(new_value))
+                    {
+                        option.set_default_value(scm_to_int(new_value));
+                        return;
+                    }
+                    std::string new_value_str{};
+                    if (scm_is_symbol(new_value))
+                        new_value_str = scm_to_utf8_string(scm_symbol_to_string(new_value));
+                    else if (scm_is_string(new_value))
+                        new_value_str = scm_to_utf8_string(new_value);
+                    if (!new_value_str.empty())
+                        option.set_default_value(new_value_str);
+                    return;
+                }
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionRangeValue<int>>)
+                {
+                    if (scm_is_pair(new_value))
+                        option.set_default_value(scm_to_int(scm_cdr(new_value)));
+                    else
+                        option.set_default_value(scm_to_int(new_value));
+                    return;
+                }
+                auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
+                option.set_default_value(value);
+            }, swig_get_option($self));
+    }
+
+    SCM get_type()
+    {
+        if (!self)
+            return SCM_BOOL_F;
+        return std::visit([](const auto& option)->SCM {
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                    GncOptionMultichoiceValue>)
+                    return scm_c_eval_string("'multichoice");
+                else if constexpr (std::is_same_v<decltype(option.get_value()),
+                    bool>)
+                    return scm_c_eval_string("'boolean");
+                else
+                    return SCM_BOOL_F;
+            }, swig_get_option($self));
+    }
 };
 
 %extend GncOptionDB {

commit c04f4a00e0d34d66ed5409fd9e3875e40c13d3c4
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 2 14:24:15 2021 -0800

    Replace gnc_make_option<SCM> with gnc_make_scm_option
    
    To finesse SWIG_Guile's typedef unsigned long SCM, which causes SFINAE
    issues when trying to resolve the template.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 5053c6297..701b80541 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -517,6 +517,16 @@ GncOption::from_scheme(std::istream& iss)
                       }, *m_option);
 }
 
+GncOption*
+gnc_make_SCM_option(const char* section, const char* name,
+                    const char* key, const char* doc_string,
+                    SCM value, GncOptionUIType ui_type)
+{
+    return new GncOption(section, name, key, doc_string,
+                         reinterpret_cast<SCM>(value), ui_type);
+}
+
+
 /* We must instantiate all of the templates we need here because we don't expose
  * the template implementation in the public header.
  */
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 4e88f3412..aa7fc3746 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -150,4 +150,9 @@ gnc_make_option(const char* section, const char* name,
     return new GncOption(section, name, key, doc_string, value, ui_type);
 }
 
+/* To work around SWIG_Guile's typedef of SCM to unsigned long: */
+GncOption* gnc_make_SCM_option(const char* section, const char* name,
+                               const char* key, const char* doc_string,
+                               SCM value, GncOptionUIType ui_type);
+
 #endif //GNC_OPTION_HPP_

commit e7309f077bb47768071088a57580164b158c6721
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 2 14:20:12 2021 -0800

    Replace gnc_make_option<gnc_commodity*> with gnc_make_commodity_option.
    
    To handle cases where we try to create commodity options with a symbol as
    the default instead of a gnc_commodity*.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 64773857f..ac893a53a 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -881,6 +881,36 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
     }
 
+    GncOption* gnc_make_commodity_option(const char* section,
+                                        const char* name, const char* key,
+                                        const char* doc_string,
+                                        gnc_commodity *value)
+    {
+        return new GncOption{GncOptionValue<const QofInstance*>{
+                section, name, key, doc_string, (const QofInstance*)value}};
+    }
+
+    GncOption* gnc_make_commodity_option(const char* section,
+                                        const char* name, const char* key,
+                                        const char* doc_string,
+                                        const char *value)
+    {
+        gnc_commodity* commodity{};
+        const auto book{qof_session_get_book(gnc_get_current_session())};
+        const auto commodity_table{gnc_commodity_table_get_table(book)};
+        const auto namespaces{gnc_commodity_table_get_namespaces(commodity_table)};
+        for (auto node = namespaces; node && commodity == nullptr; node = g_list_next(node))
+            commodity = gnc_commodity_table_lookup(commodity_table,
+                                                   (const char*)(node->data),
+                                                   value);
+
+        if (commodity)
+            return gnc_make_commodity_option(section, name, key, doc_string,
+                                            commodity);
+
+        return nullptr;
+    }
+
     GncOption* gnc_make_currency_option(const char* section,
                                         const char* name, const char* key,
                                         const char* doc_string,

commit 3aa86ca9925b85ea9316a54c6e2e64e35c4f0708
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 2 14:15:43 2021 -0800

    QofInstance* needs a typemap to match the from/to SCM logic.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 20aa66e0c..64773857f 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -59,6 +59,55 @@ SCM scm_init_sw_gnc_optiondb_module(void);
   * resolution is more strict so the overload prefers the exact decltype(value)
   * to implicit conversion candidates.
   */
+
+%typemap (out) QofInstance_s* {
+    if ($1 == nullptr)
+    {
+        $result = SCM_BOOL_F;
+    }
+    else
+    {
+
+        auto ptr{static_cast<void*>(const_cast<QofInstance*>($1))};
+        swig_type_info *type = SWIGTYPE_p_QofInstance_s;
+        if (GNC_IS_COMMODITY($1))
+            type = SWIGTYPE_p_gnc_commodity;
+        else if (GNC_IS_ACCOUNT($1))
+            type = SWIGTYPE_p_Account;
+        else if (GNC_IS_BUDGET($1))
+            type = SWIGTYPE_p_GncBudget;
+        else if (GNC_IS_INVOICE($1))
+            type = SWIGTYPE_p_GncInvoice;
+        else if (GNC_IS_TAXTABLE($1))
+            type = SWIGTYPE_p_GncTaxTable;
+        $result = SWIG_NewPointerObj(ptr, type, FALSE);
+    }
+}
+
+%typemap (in) QofInstance_s* {
+    if (scm_is_true($input))
+    {
+        static const std::array<swig_type_info*, 6> types {
+            SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
+                SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice,
+                SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account
+                };
+        void* ptr{};
+        auto pos = std::find_if(types.begin(), types.end(),
+                                [&$input, &ptr](auto type){
+                                    SWIG_ConvertPtr($input, &ptr, type, 0);
+                                    return ptr != nullptr; });
+        if (pos == types.end())
+            $1 = nullptr;
+        else
+            $1 = static_cast<QofInstance*>(ptr);
+    }
+    else
+    {
+        $1 = nullptr;
+    }
+ }
+
 %inline %{
 template <typename ValueType> inline SCM
     scm_from_value(ValueType value);
@@ -184,7 +233,7 @@ scm_to_value<const QofInstance*>(SCM new_value)
     if (new_value == SCM_BOOL_F)
         return nullptr;
 
-    static const std::array<swig_type_info*, 9> types{
+    static const std::array<swig_type_info*, 6> types{
             SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
             SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice,
             SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account

commit 4a305998e4cb41b19de80df4c0d424715be4e5f3
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 2 14:14:06 2021 -0800

    GncOptionDateValue set_value_from_scm: 'else' required.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 85db0e16e..20aa66e0c 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -652,6 +652,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                             option.set_value(scm_to_int64(scm_cdr(new_value)));
                         }
                     }
+                    else
                     option.set_value(scm_to_int64(new_value));
                     return;
                 }

commit d9984f75ab8a91ef23ee85af099ca0901c9c2553
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 25 10:42:58 2021 -0800

    Update test-gnc-optiondb.scm for not renaming the RelativeDatePeriod enums.

diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 0d26222ae..ae01a460c 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -200,7 +200,7 @@
   (let* ((option-db (new-gnc-optiondb))
          (date-opt (gnc-register-date-option option-db "foo" "bar"
                                              "baz" "Phony Option"
-                                             (RelativeDatePeriod-today)))
+                                             (RelativeDatePeriod-TODAY)))
          (a-time (gnc-dmy2time64 11 07 2019)))
     (test-equal (current-time) (gnc-option-value option-db "foo" "bar"))
     (gnc-set-option option-db "foo" "bar" a-time)
@@ -212,14 +212,15 @@
   (let* ((option-db (new-gnc-optiondb))
          (date-opt (gnc-register-date-option-set
                     option-db "foo" "bar" "baz" "Phony Option"
-                    (list (RelativeDatePeriod-today)
-                          (RelativeDatePeriod-start-this-month)
-                          (RelativeDatePeriod-start-prev-month)
-                          (RelativeDatePeriod-start-current-quarter)
-                          (RelativeDatePeriod-start-prev-quarter)
-                          (RelativeDatePeriod-start-cal-year)
-                          (RelativeDatePeriod-start-prev-year)
-                          (RelativeDatePeriod-start-accounting-period)) #t)))
+                    (list (RelativeDatePeriod-TODAY)
+                          (RelativeDatePeriod-START-THIS-MONTH)
+                          (RelativeDatePeriod-START-PREV-MONTH)
+                          (RelativeDatePeriod-START-CURRENT-QUARTER)
+                          (RelativeDatePeriod-START-PREV-QUARTER)
+                          (RelativeDatePeriod-START-CAL-YEAR)
+                          (RelativeDatePeriod-START-CAL-YEAR)
+                          (RelativeDatePeriod-START-PREV-YEAR)
+                          (RelativeDatePeriod-START-ACCOUNTING-PERIOD)) #t)))
     (test-equal (gnc-accounting-period-fiscal-start)
                 (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-test-date-set-option"))

commit 43cd81ba78b235c7cc333e0c987f962f60085949
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 25 10:41:44 2021 -0800

    Fix a couple of RangeValue retrieval issues.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 40ac77483..85db0e16e 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -409,6 +409,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 #include <cassert>
 #include <algorithm>
 #include <array>
+#include <string>
 #include "gnc-option.hpp"
 #include "gnc-option-ui.hpp"
 
@@ -470,38 +471,38 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
     if (scm_is_false(reldate_values))
         reldate_values = scm_c_eval_string(
-        "'((absolute RelativeDatePeriod-ABSOLUTE)"
-        "(today RelativeDatePeriod-TODAY)"
-        "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)"
-        "(one-week-ahead RelativeDatePeriod-ONE-WEEK-AHEAD)"
-        "(one-month-ago RelativeDatePeriod-ONE-MONTH-AGO)"
-        "(one-month-ahead RelativeDatePeriod-ONE-MONTH-AHEAD)"
-        "(three-months-ago RelativeDatePeriod-THREE-MONTHS-AGO)"
-        "(three-months-ahead RelativeDatePeriod-THREE-MONTHS-AHEAD)"
-        "(six-months-ago RelativeDatePeriod-SIX-MONTHS-AGO)"
-        "(six-months-ahead RelativeDatePeriod-SIX-MONTHS-AHEAD)"
-        "(one-year-ago RelativeDatePeriod-ONE-YEAR-AGO)"
-        "(one-year-ahead RelativeDatePeriod-ONE-YEAR-AHEAD)"
-        "(start-this-month RelativeDatePeriod-START-THIS-MONTH)"
-        "(end-this-month RelativeDatePeriod-END-THIS-MONTH)"
-        "(start-prev-month RelativeDatePeriod-START-PREV-MONTH)"
-        "(end-prev-month RelativeDatePeriod-END-PREV-MONTH)"
-        "(start-next-month RelativeDatePeriod-START-NEXT-MONTH)"
-        "(end-next-month RelativeDatePeriod-END-NEXT-MONTH)"
-        "(start-current-quarter RelativeDatePeriod-START-CURRENT-QUARTER)"
-        "(end-current-quarter RelativeDatePeriod-END-CURRENT-QUARTER)"
-        "(start-prev-quarter RelativeDatePeriod-START-PREV-QUARTER)"
-        "(end-prev-quarter RelativeDatePeriod-END-PREV-QUARTER)"
-        "(start-next-quarter RelativeDatePeriod-START-NEXT-QUARTER)"
-        "(end-next-quarter RelativeDatePeriod-END-NEXT-QUARTER)"
-        "(start-cal-year RelativeDatePeriod-START-CAL-YEAR)"
-        "(end-cal-year RelativeDatePeriod-END-CAL-YEAR)"
-        "(start-prev-year RelativeDatePeriod-START-PREV-YEAR)"
-        "(end-prev-year RelativeDatePeriod-END-PREV-YEAR)"
-        "(start-next-year RelativeDatePeriod-START-NEXT-YEAR)"
-        "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)"
-        "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)"
-        "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))");
+            "'((absolute RelativeDatePeriod-ABSOLUTE)"
+            "(today RelativeDatePeriod-TODAY)"
+            "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)"
+            "(one-week-ahead RelativeDatePeriod-ONE-WEEK-AHEAD)"
+            "(one-month-ago RelativeDatePeriod-ONE-MONTH-AGO)"
+            "(one-month-ahead RelativeDatePeriod-ONE-MONTH-AHEAD)"
+            "(three-months-ago RelativeDatePeriod-THREE-MONTHS-AGO)"
+            "(three-months-ahead RelativeDatePeriod-THREE-MONTHS-AHEAD)"
+            "(six-months-ago RelativeDatePeriod-SIX-MONTHS-AGO)"
+            "(six-months-ahead RelativeDatePeriod-SIX-MONTHS-AHEAD)"
+            "(one-year-ago RelativeDatePeriod-ONE-YEAR-AGO)"
+            "(one-year-ahead RelativeDatePeriod-ONE-YEAR-AHEAD)"
+            "(start-this-month RelativeDatePeriod-START-THIS-MONTH)"
+            "(end-this-month RelativeDatePeriod-END-THIS-MONTH)"
+            "(start-prev-month RelativeDatePeriod-START-PREV-MONTH)"
+            "(end-prev-month RelativeDatePeriod-END-PREV-MONTH)"
+            "(start-next-month RelativeDatePeriod-START-NEXT-MONTH)"
+            "(end-next-month RelativeDatePeriod-END-NEXT-MONTH)"
+            "(start-current-quarter RelativeDatePeriod-START-CURRENT-QUARTER)"
+            "(end-current-quarter RelativeDatePeriod-END-CURRENT-QUARTER)"
+            "(start-prev-quarter RelativeDatePeriod-START-PREV-QUARTER)"
+            "(end-prev-quarter RelativeDatePeriod-END-PREV-QUARTER)"
+            "(start-next-quarter RelativeDatePeriod-START-NEXT-QUARTER)"
+            "(end-next-quarter RelativeDatePeriod-END-NEXT-QUARTER)"
+            "(start-cal-year RelativeDatePeriod-START-CAL-YEAR)"
+            "(end-cal-year RelativeDatePeriod-END-CAL-YEAR)"
+            "(start-prev-year RelativeDatePeriod-START-PREV-YEAR)"
+            "(end-prev-year RelativeDatePeriod-END-PREV-YEAR)"
+            "(start-next-year RelativeDatePeriod-START-NEXT-YEAR)"
+            "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)"
+            "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)"
+            "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))");
 
     return reldate_values;
         }
@@ -519,9 +520,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             switch (keytype)
             {
                 case KeyType::SYMBOL:
-                return scm_string_to_symbol(scm_str);
+                    return scm_string_to_symbol(scm_str);
                 case KeyType::STRING:
-                return scm_str;
+                    return scm_str;
                 case KeyType::NUMBER:
                     return scm_string_to_number(scm_str,
                                                 scm_from_int(10));
@@ -651,6 +652,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                             option.set_value(scm_to_int64(scm_cdr(new_value)));
                         }
                     }
+                    option.set_value(scm_to_int64(new_value));
                     return;
                 }
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
@@ -670,6 +672,15 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                         option.set_value(new_value_str);
                     return;
                 }
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionRangeValue<int>>)
+                {
+                    if (scm_is_pair(new_value))
+                        option.set_value(scm_to_int(scm_cdr(new_value)));
+                    else
+                        option.set_value(scm_to_int(new_value));
+                    return;
+                }
                 auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
                 option.set_value(value);
             }, swig_get_option($self));

commit 9bd3baff63d41186aff4f3341f6373098d19e1fd
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 25 10:40:18 2021 -0800

    Change the reldate_values alist from a static to a singleton.
    
    A static has to be initialized at library load and Guile
    isn't necessarily running then. The singleton gets evaluated at
    runtime only if the getter is called from Guile.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 65d0b2a4d..40ac77483 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -464,7 +464,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %typemap(out) GncOption* "$result = ($1) ? scm_from_pointer($1, nullptr) : SCM_BOOL_F;"
 
 %header %{
-    static const SCM reldate_values = scm_c_eval_string(
+    static const SCM get_reldate_values() {
+
+    static SCM reldate_values = SCM_BOOL_F;
+
+    if (scm_is_false(reldate_values))
+        reldate_values = scm_c_eval_string(
         "'((absolute RelativeDatePeriod-ABSOLUTE)"
         "(today RelativeDatePeriod-TODAY)"
         "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)"
@@ -626,7 +631,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         if (!$self)
             return;
-        std::visit([new_value](auto& option) {
+        auto reldate_values{get_reldate_values()};
+        std::visit([new_value, reldate_values](auto& option) {
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionDateValue>)
                 {

commit 534a7c28937de165a2c287df86e3778b4f7a3a24
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 25 10:34:43 2021 -0800

    GncOptionMultichoiceValue allow setting a default selection.
    
    Instead of arbitrarily using the first allowed value.
    Also update tests for the Scheme type addition to
    GncMultichoiceOptionChoices, intercept more cases where the value
    needs to be transformed, and go back to emitting a string instead of
    throwing in GncOptionMultichoiceValue::get_value when m_values has more
    than one value.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index d2ae693dd..9e09b1413 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -576,7 +576,7 @@ public:
         if (vec.size() == 1)
             return std::get<0>(m_choices.at(vec[0]));
         else
-            throw std::length_error("Retrieving multiple values from a multichoice isn't implemented.");
+            return c_list_string;
 
     }
     const std::string& get_default_value() const
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index ecd1ce838..82b52928a 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -990,11 +990,19 @@ gnc_register_account_sel_limited_option(GncOptionDB* db,
 void
 gnc_register_multichoice_option(GncOptionDB* db, const char* section,
                                 const char* name, const char* key,
-                                const char* doc_string,
+                                const char* doc_string, const char* default_val,
                                 GncMultichoiceOptionChoices&& choices)
 {
+    std::string defval{default_val};
+    auto found{std::find_if(choices.begin(), choices.end(),
+                            [&defval](auto& choice)->bool {
+                                return defval == std::get<0>(choice);
+                            })};
+    if (found == choices.end())
+        defval = (choices.empty() ? std::string{"None"} :
+                  std::get<0>(choices.at(0)));
     GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
-                std::get<0>(choices.at(0)).c_str(), std::move(choices)}};
+                defval.c_str(), std::move(choices)}};
     db->register_option(section, std::move(option));
 }
 
@@ -1471,7 +1479,7 @@ gnc_option_db_lookup_string_value(GncOptionDB* odb, const char* section, const c
 {
     auto value{odb->lookup_string_option(section, name)};
     if (value.empty())
-    return nullptr;
+        return nullptr;
     return strdup(value.c_str());
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 0d05c1ab7..3e8a96ee5 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -383,6 +383,7 @@ inline void gnc_register_account_sel_limited_option(GncOptionDBPtr& db,
 void gnc_register_multichoice_option(GncOptionDB* db,
                                      const char* section, const char* name,
                                      const char* key, const char* doc_string,
+                                     const char* default_val,
                                      GncMultichoiceOptionChoices&& value);
 
 /**
@@ -391,10 +392,12 @@ void gnc_register_multichoice_option(GncOptionDB* db,
 inline void gnc_register_multichoice_option(GncOptionDBPtr& db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
+                                        const char* default_val,
                                         GncMultichoiceOptionChoices&& value)
 {
     gnc_register_multichoice_option(db.get(), section, name,
-                                    key, doc_string, std::move(value));
+                                    key, doc_string, default_val,
+                                    std::move(value));
 }
 
 /**
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index c7c45817e..65d0b2a4d 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -441,7 +441,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %ignore gnc_register_account_list_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&, GncOptionAccountTypeList&&);
 %ignore gnc_register_account_list_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&);
 %ignore gnc_register_account_sel_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&, GncOptionAccountTypeList&&);
-%ignore gnc_register_multichoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&);
+%ignore gnc_register_multichoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&);
 %ignore gnc_register_list_option(GncOptionDB*, const char*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&);
 %ignore gnc_register_number_Plot_size_option(GncOptionDB*, const char*, const char*, const char*, const char*, int);
 %ignore gnc_register_query_option(GncOptionDB*, const char*, const char*, const char*, const char*, QofQuery*);
@@ -497,12 +497,16 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)"
         "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)"
         "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))");
+
+    return reldate_values;
+        }
     %}
 
 %ignore GncOptionMultichoiceKeyType;
 
 %inline %{
-    SCM get_scm_value(const GncOptionMultichoiceValue& option)
+    inline SCM scm_from_multichoices (const GncMultichoiceOptionIndexVec& indexes,
+                                      const GncOptionMultichoiceValue& option)
     {
         using KeyType = GncOptionMultichoiceKeyType;
         auto scm_value = [](const char* value, KeyType keytype) -> SCM {
@@ -514,15 +518,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                 case KeyType::STRING:
                 return scm_str;
                 case KeyType::NUMBER:
-                    return scm_string_to_number(scm_str, scm_from_int(10));
+                    return scm_string_to_number(scm_str,
+                                                scm_from_int(10));
             };
+            return SCM_BOOL_F;
         };
 
-        auto indexes = option.get_multiple();
-        if (indexes.empty())
-            indexes = option.get_default_multiple();
-        if (indexes.empty())
-            return SCM_BOOL_F;
         if (indexes.size() == 1) // FIXME: Should use bool member to decide
             return scm_value(option.permissible_value(indexes[0]),
                              option.get_keytype(indexes[0]));
@@ -536,6 +537,27 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return scm_reverse(values);
     }
 
+
+    SCM get_scm_value(const GncOptionMultichoiceValue& option)
+    {
+
+        auto indexes = option.get_multiple();
+        if (indexes.empty())
+            indexes = option.get_default_multiple();
+        if (indexes.empty())
+            return SCM_BOOL_F;
+        return scm_from_multichoices(indexes, option);
+     }
+
+    SCM get_scm_default_value(const GncOptionMultichoiceValue& option)
+    {
+
+        auto indexes = option.get_default_multiple();
+        if (indexes.empty())
+            return SCM_BOOL_F;
+        return scm_from_multichoices(indexes, option);
+     }
+
     SCM get_scm_value(const GncOptionRangeValue<int>& option)
     {
         auto val{option.get_value()};
@@ -543,6 +565,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return scm_cons(desig, scm_from_int(val));
     }
 
+    SCM get_scm_default_value(const GncOptionRangeValue<int>& option)
+    {
+        auto val{option.get_default_value()};
+        auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")};
+        return scm_cons(desig, scm_from_int(val));
+    }
+
     %}
 
 %include "gnc-option-date.hpp"
@@ -581,6 +610,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         if (!$self)
             return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                              GncOptionMultichoiceValue> ||
+                              std::is_same_v<std::decay_t<decltype(option)>,
+                              GncOptionRangeValue<int>>)
+                    return get_scm_default_value(option);
                 auto value{option.get_default_value()};
                 if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
                               SCM>)
@@ -705,14 +739,20 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     GncOption* gnc_make_multichoice_option(const char* section,
                                            const char* name, const char* key,
                                            const char* doc_string,
+                                           const char* default_val,
                                            GncMultichoiceOptionChoices&& choices)
     {
         try {
+            std::string defval{default_val};
+            auto found{std::find_if(choices.begin(), choices.end(),
+                                    [&defval](auto& choice)->bool {
+                                        return defval == std::get<0>(choice);
+                                    })};
+            if (found == choices.end())
+                defval = (choices.empty() ? std::string{"None"} :
+                          std::get<0>(choices.at(0)));
             return new GncOption{GncOptionMultichoiceValue{section, name, key,
-                        doc_string,
-                        choices.empty() ? "None" :
-                        std::get<0>(choices.at(0)).c_str(),
-                        std::move(choices)}};
+                        doc_string, defval.c_str(), std::move(choices)}};
         }
         catch (const std::exception& err)
         {
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 39f1aee6a..e4667d7f1 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -949,18 +949,20 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
     EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[0]);
 }
 
+using KT = GncOptionMultichoiceKeyType;
 class GncOptionMultichoiceTest : public ::testing::Test
 {
 protected:
     GncOptionMultichoiceTest() :
-        m_option{GncOptionMultichoiceValue{"foo", "bar", "baz",
-                                           "Phony Option", "plugh",
-                                           {
-                                               {"plugh", "xyzzy", "thud"},
-                                               {"waldo", "pepper", "salt"},
-                                               {"pork", "sausage", "links"},
-                                               {"corge", "grault", "garply"}
-                                           }}} {}
+        m_option{GncOptionMultichoiceValue
+                 {"foo", "bar", "baz",
+                  "Phony Option", "plugh",
+                  {
+                      {"plugh", "xyzzy", "thud", KT::STRING},
+                      {"waldo", "pepper", "salt", KT::STRING},
+                      {"pork", "sausage", "links", KT::STRING},
+                      {"corge", "grault", "garply", KT::STRING}
+                  }}} {}
     GncOption m_option;
 };
 
@@ -1052,15 +1054,15 @@ class GncOptionListTest : public ::testing::Test
 {
 protected:
     GncOptionListTest() :
-        m_option{GncOptionMultichoiceValue{"foo", "bar", "baz",
-                                           "Phony Option",
-                                           GncMultichoiceOptionIndexVec{0, 2},
-                                           {
-                                               {"plugh", "xyzzy", "thud"},
-                                               {"waldo", "pepper", "salt"},
-                                               {"pork", "sausage", "links"},
-                                               {"corge", "grault", "garply"}
-                                           }}} {}
+        m_option{GncOptionMultichoiceValue{
+            "foo", "bar", "baz", "Phony Option",
+            GncMultichoiceOptionIndexVec{0, 2},
+            {
+                {"plugh", "xyzzy", "thud", KT::STRING},
+                {"waldo", "pepper", "salt", KT::STRING},
+                {"pork", "sausage", "links", KT::STRING},
+                {"corge", "grault", "garply", KT::STRING}
+            }}} {}
     GncOption m_option;
 };
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 357537286..bae79b04a 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -174,15 +174,18 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct)
     EXPECT_FALSE(m_db->find_option("foo", "bar"));
 }
 
+using KT = GncOptionMultichoiceKeyType;
 TEST_F(GncOptionDBTest, test_register_multichoice_option)
 {
     GncMultichoiceOptionChoices choices{
-        { "plugh", "xyzzy", "thud"},
-        { "waldo", "pepper", "salt"},
-        { "pork", "sausage", "links"},
-        { "corge", "grault", "garply"}};
+        { "plugh", "xyzzy", "thud", KT::STRING},
+        { "waldo", "pepper", "salt", KT::STRING},
+        { "pork", "sausage", "links", KT::STRING},
+        { "corge", "grault", "garply", KT::STRING}};
     gnc_register_multichoice_option(m_db, "foo", "bar", "baz",
-                                    "Phony Option", std::move(choices));
+                                    "Phony Option", "waldo",
+                                    std::move(choices));
+    EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
     ASSERT_TRUE(m_db->set_option("foo", "bar", std::string{"corge"}));
     EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str());
 }
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 2da34c98b..0d26222ae 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -23,6 +23,19 @@
 
 (use-modules (srfi srfi-64))
 (use-modules (tests srfi64-extras))
+
+;; This is a special case where we can't use the exported registration function
+;; because we need to transform the default argument first depending on its
+;; Scheme type.
+(define (gnc:register-multichoice-option options section name key docstring default multichoice)
+  (issue-deprecation-warning "gnc:make-multichoice-option is deprecated. Make and register the option in one command with gnc-register-multichoice-option.")
+  (let ((defval (cond ((symbol? default)
+                       (symbol->string default))
+                      ((number? default)
+                       (number->string default))
+                     (else default))))
+  (gnc-register-multichoice-option options section name key docstring defval multichoice)))
+
 ;; Load the C++ option implementation, avoiding the options.scm ones.
 (eval-when
  (compile load eval expand)
@@ -154,17 +167,17 @@
   (let* ((option-db (new-gnc-optiondb))
          (multilist (list
                        (list "plugh" (cons 'text "xyzzy") (cons 'tip "thud"))
-                       (list "waldo" (cons 'text "pepper") (cons 'tip "salt"))
+                       (list 'waldo (cons 'text "pepper") (cons 'tip "salt"))
                        (list "pork" (cons 'text "sausage") (cons 'tip "links"))
                        (list "corge" (cons 'text "grault") (cons 'tip "garply"))))
          (multichoice (keylist->vectorlist multilist))
-         (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz"
-                                                     "Phony Option" multichoice)))
+         (multi-opt (gnc:register-multichoice-option option-db "foo" "bar" "baz"
+                                                     "Phony Option" 'waldo multichoice)))
 
-    (test-equal "plugh" (gnc-option-value option-db "foo" "bar"))
+    (test-equal 'waldo (gnc-option-value option-db "foo" "bar"))
     (gnc-set-option option-db "foo" "bar" "corge")
     (test-equal "corge" (gnc-option-value option-db "foo" "bar"))
-    (test-equal "plugh" (gnc-option-default-value option-db "foo" "bar")))
+    (test-equal 'waldo (gnc-option-default-value option-db "foo" "bar")))
     (test-end "test-gnc-test-multichoice-option"))
 
 (define (test-gnc-make-list-option)

commit c62b526ba0cc29974f5eaee4a1c45975939020f4
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Feb 23 10:36:58 2021 -0800

    Implement scm_to_value<bool>.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 1d5a85b3b..c7c45817e 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -145,6 +145,12 @@ scm_to_value(SCM new_value)
     return ValueType{};
 }
 
+template <> inline bool
+scm_to_value<bool>(SCM new_value)
+{
+    return scm_is_true(new_value);
+}
+
 template <> inline std::string
 scm_to_value<std::string>(SCM new_value)
 {

commit efc734649015917fd8628dd3d49a7a57ad844689
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Feb 22 14:08:14 2021 -0800

    Implement scm_to_value<GncOptionAccountList>.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 2e7d6ea91..1d5a85b3b 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -194,6 +194,26 @@ scm_to_value<const QofInstance*>(SCM new_value)
     return static_cast<const QofInstance*>(ptr);
 }
 
+template <>inline GncOptionAccountList
+scm_to_value<GncOptionAccountList>(SCM new_value)
+{
+    GncOptionAccountList retval{};
+    if (scm_is_false(scm_list_p(new_value)) || scm_is_null(new_value))
+        return retval;
+    auto next{new_value};
+    while (auto node{scm_car(next)})
+    {
+        void* account{};
+        SWIG_ConvertPtr(node, &account, SWIGTYPE_p_Account, 0);
+        if (account)
+            retval.push_back(static_cast<Account*>(account));
+        next = scm_cdr(next);
+        if (scm_is_null(next))
+            break;
+    }
+    return retval;
+}
+
 template <>inline SCM
 scm_from_value<GncOptionAccountList>(GncOptionAccountList value)
 {
@@ -604,7 +624,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                         option.set_value(new_value_str);
                     return;
                 }
-                option.set_value(scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value));
+                auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)};  //Can't inline, set_value takes arg by reference.
+                option.set_value(value);
             }, swig_get_option($self));
     }
 };

commit f20c358ce1856f7e8c51bdebff8aaf80ded6e90a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Feb 21 14:30:30 2021 -0800

    Return a pair in scheme for GncOptionRangeValue<int>.
    
    This is a bit of a hack to handle PlotSize options. They're the only
    RangeValues that use ints; the rest use doubles because the control is a
    GtkSpinButton and that uses doubles. The chart code expects a pair with
    either 'pixel or 'percent saying what to put in front of the number. We
    hack that too: if value <= 100 then it's percent because 100px is about
    3cm on modern monitors and 15mm on HiDPI ones. Bigger numbers are pixels.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 110be68a4..2e7d6ea91 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -183,7 +183,7 @@ scm_to_value<const QofInstance*>(SCM new_value)
             SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice,
             SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account
                 };
-        void* ptr{};
+    void* ptr{};
     auto pos = std::find_if(types.begin(), types.end(),
                             [&new_value, &ptr](auto type){
                                 SWIG_ConvertPtr(new_value, &ptr, type, 0);
@@ -509,7 +509,15 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
         return scm_reverse(values);
     }
- %}
+
+    SCM get_scm_value(const GncOptionRangeValue<int>& option)
+    {
+        auto val{option.get_value()};
+        auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")};
+        return scm_cons(desig, scm_from_int(val));
+    }
+
+    %}
 
 %include "gnc-option-date.hpp"
 %include "gnc-option.hpp"
@@ -531,7 +539,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                              GncOptionMultichoiceValue>)
+                              GncOptionMultichoiceValue> ||
+                              std::is_same_v<std::decay_t<decltype(option)>,
+                              GncOptionRangeValue<int>>)
                     return get_scm_value(option);
                 auto value{option.get_value()};
                 if constexpr (std::is_same_v<std::decay_t<decltype(value)>,

commit b5c0477143850f0aae7ff393c2da0f22909aa979
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Feb 21 14:25:20 2021 -0800

    Use the SWIGGED pointer for the SCM value of QofInstance* Values.
    
    This effectively reverts b7dd7f.
    
    Note that two cases aren't handled because the types aren't GObjects and
    so don't have type macros to decipher them: GncOwner and QofQuery. Since
    they're not GObjects they're obviously not QofInstances either and we
    need to rethink this value type a bit.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 58019706a..110be68a4 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -117,12 +117,24 @@ template <> inline SCM
 scm_from_value<const QofInstance*>(const QofInstance* value)
 {
     if (!value) return SCM_BOOL_F;
-    auto type{qof_collection_get_type(qof_instance_get_collection(value))};
-    auto guid_str{guid_to_string(qof_instance_get_guid(value))};
-    auto scm_guid = scm_from_utf8_string(guid_str);
-    auto scm_type = scm_from_utf8_string(type);
-    free(guid_str);
-    return scm_cons(scm_type, scm_guid);
+
+    auto ptr{static_cast<void*>(const_cast<QofInstance*>(value))};
+    swig_type_info *type = SWIGTYPE_p_QofInstance_s;
+    if (GNC_IS_COMMODITY(value))
+        type = SWIGTYPE_p_gnc_commodity;
+    else if (GNC_IS_ACCOUNT(value))
+        type = SWIGTYPE_p_Account;
+    else if (GNC_IS_BUDGET(value))
+        type = SWIGTYPE_p_GncBudget;
+    else if (GNC_IS_INVOICE(value))
+        type = SWIGTYPE_p_GncInvoice;
+    else if (GNC_IS_TAXTABLE(value))
+        type = SWIGTYPE_p_GncTaxTable;
+/* There is no type macro for QofQuery, it's not a GObject.
+    else if (GNC_IS_QOFQUERY(value))
+        type = SWIGTYPE_p_Query;
+*/
+    return SWIG_NewPointerObj(ptr, type, FALSE);
 }
 
 template <typename ValueType> inline ValueType
@@ -163,25 +175,23 @@ scm_to_value<double>(SCM new_value)
 template <> inline const QofInstance*
 scm_to_value<const QofInstance*>(SCM new_value)
 {
-    if (new_value == SCM_BOOL_F || !scm_is_pair(new_value))
-    {
+    if (new_value == SCM_BOOL_F)
+        return nullptr;
+
+    static const std::array<swig_type_info*, 9> types{
+            SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity,
+            SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice,
+            SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account
+                };
         void* ptr{};
-        SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p_QofInstance_s, 0);
-        if (ptr)
-            return static_cast<const QofInstance*>(ptr);
-        SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p_gnc_commodity, 0);
-        if (ptr)
-            return static_cast<const QofInstance*>(ptr);
-    }
-
-    auto guid_str{scm_to_utf8_stringn(scm_car(new_value), nullptr)};
-    auto type{scm_to_utf8_stringn(scm_cdr(new_value), nullptr)};
-    GncGUID new_guid;
-    string_to_guid(guid_str, &new_guid);
-    free(guid_str);
-    auto coll{qof_book_get_collection(qof_session_get_book(gnc_get_current_session()), type)};
-    free(type);
-    return qof_collection_lookup_entity(coll, &new_guid);
+    auto pos = std::find_if(types.begin(), types.end(),
+                            [&new_value, &ptr](auto type){
+                                SWIG_ConvertPtr(new_value, &ptr, type, 0);
+                                return ptr != nullptr; });
+    if (pos == types.end())
+        return nullptr;
+
+    return static_cast<const QofInstance*>(ptr);
 }
 
 template <>inline SCM
@@ -371,6 +381,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %ignore swig_get_option(GncOption&);
 %inline %{
 #include <cassert>
+#include <algorithm>
+#include <array>
 #include "gnc-option.hpp"
 #include "gnc-option-ui.hpp"
 

commit f7f2d22909c8a205018d07dcc944db242bcffd80
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Feb 20 11:42:42 2021 -0800

    Implement gnc-optiondb-foreach.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 0380a597d..58019706a 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -832,6 +832,23 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         auto option{odb->find_option(section, name)};
         option->set_ui_item_selectable(selectable);
     }
+
+    void
+    gnc_optiondb_foreach(GncOptionDBPtr& odb, SCM thunk)
+    {
+        odb->foreach_section(
+            [&thunk](const GncOptionSectionPtr& section)
+            {
+                section->foreach_option(
+                    [&thunk](auto& option)
+                    {
+                        auto optvoidptr{reinterpret_cast<void*>(
+                                const_cast<GncOption*>(&option))};
+                        auto scm_opt{scm_from_pointer(optvoidptr, nullptr)};
+                        scm_call_1(thunk, scm_opt);
+                    });
+            });
+    }
 %}
 
 #endif //SWIGGUILE

commit 81d261897e9f48227e9b06e672a12c9036eea30a
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Feb 19 16:13:59 2021 -0800

    scm_to_value<QofInstance*>() handle Swigged ptrs as well as GUIDs.
    
    Some swigged engine functions used to set options return Swigged ptrs.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index d07ebefcd..0380a597d 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -164,7 +164,16 @@ template <> inline const QofInstance*
 scm_to_value<const QofInstance*>(SCM new_value)
 {
     if (new_value == SCM_BOOL_F || !scm_is_pair(new_value))
-        return nullptr;
+    {
+        void* ptr{};
+        SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p_QofInstance_s, 0);
+        if (ptr)
+            return static_cast<const QofInstance*>(ptr);
+        SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p_gnc_commodity, 0);
+        if (ptr)
+            return static_cast<const QofInstance*>(ptr);
+    }
+
     auto guid_str{scm_to_utf8_stringn(scm_car(new_value), nullptr)};
     auto type{scm_to_utf8_stringn(scm_cdr(new_value), nullptr)};
     GncGUID new_guid;

commit 16a36d91044b00ef653073bdb7ed193a992b3de3
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Feb 19 13:50:13 2021 -0800

    Correctly set value of a GncOptionMultichoiceValue.
    
    Accounting for the 3 types of SCM object that we might be handed.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 89e0e8122..d07ebefcd 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -556,6 +556,23 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                     }
                     return;
                 }
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionMultichoiceValue>)
+                {
+                    if (scm_is_integer(new_value))
+                    {
+                        option.set_value(scm_to_int(new_value));
+                        return;
+                    }
+                    std::string new_value_str{};
+                    if (scm_is_symbol(new_value))
+                        new_value_str = scm_to_utf8_string(scm_symbol_to_string(new_value));
+                    else if (scm_is_string(new_value))
+                        new_value_str = scm_to_utf8_string(new_value);
+                    if (!new_value_str.empty())
+                        option.set_value(new_value_str);
+                    return;
+                }
                 option.set_value(scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value));
             }, swig_get_option($self));
     }

commit 7c1b4b794a74f018417b0eed4bd043eaf45fcb55
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Feb 19 13:18:39 2021 -0800

    Convert from Scheme date-period symbols to C++ RelativeDatePeriod enum.
    
    Create an alist mapping the Scheme symbols to the corresponding Swig
    functions that alias the enumerations. When a symbol is received look
    up the corresponding function and evaluate it, retrieving the enumeration.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 7f6caed50..89e0e8122 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -216,41 +216,10 @@ gnc_option_test_book_destroy(QofBook* book)
 %ignore GncOptionDateValue::operator=(GncOptionDateValue&&);
 %ignore GncOptionDateValue::set_value(size_t); // Used only by dialog-options
 %ignore operator<<(std::ostream&, const GncOption&);
+%ignore operator<<(std::ostream&, const RelativeDatePeriod);
 %ignore operator>>(std::istream&, GncOption&);
 %ignore GncOption::_get_option();
 
-%rename(absolute) RelativeDatePeriod::ABSOLUTE;
-%rename(today) RelativeDatePeriod::TODAY;
-%rename(one_week_ago) RelativeDatePeriod::ONE_WEEK_AGO;
-%rename(one_week_ahead) RelativeDatePeriod::ONE_WEEK_AHEAD;
-%rename(one_month_ago) RelativeDatePeriod::ONE_MONTH_AGO;
-%rename(one_month_ahead) RelativeDatePeriod::ONE_MONTH_AHEAD;
-%rename(three_months_ago) RelativeDatePeriod::THREE_MONTHS_AGO;
-%rename(three_months_ahead) RelativeDatePeriod::THREE_MONTHS_AHEAD;
-%rename(six_months_ago) RelativeDatePeriod::SIX_MONTHS_AGO;
-%rename(six_months_ahead) RelativeDatePeriod::SIX_MONTHS_AHEAD;
-%rename(one_year_ago) RelativeDatePeriod::ONE_YEAR_AGO;
-%rename(one_year_ahead) RelativeDatePeriod::ONE_YEAR_AHEAD;
-%rename(start_this_month) RelativeDatePeriod::START_THIS_MONTH;
-%rename(end_this_month) RelativeDatePeriod::END_THIS_MONTH;
-%rename(start_prev_month) RelativeDatePeriod::START_PREV_MONTH;
-%rename(end_prev_month) RelativeDatePeriod::END_PREV_MONTH;
-%rename(start_next_month) RelativeDatePeriod::START_NEXT_MONTH;
-%rename(end_next_month) RelativeDatePeriod::END_NEXT_MONTH;
-%rename(start_current_quarter) RelativeDatePeriod::START_CURRENT_QUARTER;
-%rename(end_current_quarter) RelativeDatePeriod::END_CURRENT_QUARTER;
-%rename(start_prev_quarter) RelativeDatePeriod::START_PREV_QUARTER;
-%rename(end_prev_quarter) RelativeDatePeriod::END_PREV_QUARTER;
-%rename(start_next_quarter) RelativeDatePeriod::START_NEXT_QUARTER;
-%rename(end_next_quarter) RelativeDatePeriod::END_NEXT_QUARTER;
-%rename(start_cal_year) RelativeDatePeriod::START_CAL_YEAR;
-%rename(end_cal_year) RelativeDatePeriod::END_CAL_YEAR;
-%rename(start_prev_year) RelativeDatePeriod::START_PREV_YEAR;
-%rename(end_prev_year) RelativeDatePeriod::END_PREV_YEAR;
-%rename(start_next_year) RelativeDatePeriod::START_NEXT_YEAR;
-%rename(end_next_year) RelativeDatePeriod::END_NEXT_YEAR;
-%rename(start_accounting_period) RelativeDatePeriod::START_ACCOUNTING_PERIOD;
-%rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD;
 
 %rename(gnc_register_date_option_set)
     gnc_register_date_option(GncOptionDBPtr&, const char*, const char*,
@@ -447,8 +416,44 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %typemap(in) GncOption* "$1 = scm_is_true($input) ? static_cast<GncOption*>(scm_to_pointer($input)) : nullptr;"
 %typemap(out) GncOption* "$result = ($1) ? scm_from_pointer($1, nullptr) : SCM_BOOL_F;"
 
+%header %{
+    static const SCM reldate_values = scm_c_eval_string(
+        "'((absolute RelativeDatePeriod-ABSOLUTE)"
+        "(today RelativeDatePeriod-TODAY)"
+        "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)"
+        "(one-week-ahead RelativeDatePeriod-ONE-WEEK-AHEAD)"
+        "(one-month-ago RelativeDatePeriod-ONE-MONTH-AGO)"
+        "(one-month-ahead RelativeDatePeriod-ONE-MONTH-AHEAD)"
+        "(three-months-ago RelativeDatePeriod-THREE-MONTHS-AGO)"
+        "(three-months-ahead RelativeDatePeriod-THREE-MONTHS-AHEAD)"
+        "(six-months-ago RelativeDatePeriod-SIX-MONTHS-AGO)"
+        "(six-months-ahead RelativeDatePeriod-SIX-MONTHS-AHEAD)"
+        "(one-year-ago RelativeDatePeriod-ONE-YEAR-AGO)"
+        "(one-year-ahead RelativeDatePeriod-ONE-YEAR-AHEAD)"
+        "(start-this-month RelativeDatePeriod-START-THIS-MONTH)"
+        "(end-this-month RelativeDatePeriod-END-THIS-MONTH)"
+        "(start-prev-month RelativeDatePeriod-START-PREV-MONTH)"
+        "(end-prev-month RelativeDatePeriod-END-PREV-MONTH)"
+        "(start-next-month RelativeDatePeriod-START-NEXT-MONTH)"
+        "(end-next-month RelativeDatePeriod-END-NEXT-MONTH)"
+        "(start-current-quarter RelativeDatePeriod-START-CURRENT-QUARTER)"
+        "(end-current-quarter RelativeDatePeriod-END-CURRENT-QUARTER)"
+        "(start-prev-quarter RelativeDatePeriod-START-PREV-QUARTER)"
+        "(end-prev-quarter RelativeDatePeriod-END-PREV-QUARTER)"
+        "(start-next-quarter RelativeDatePeriod-START-NEXT-QUARTER)"
+        "(end-next-quarter RelativeDatePeriod-END-NEXT-QUARTER)"
+        "(start-cal-year RelativeDatePeriod-START-CAL-YEAR)"
+        "(end-cal-year RelativeDatePeriod-END-CAL-YEAR)"
+        "(start-prev-year RelativeDatePeriod-START-PREV-YEAR)"
+        "(end-prev-year RelativeDatePeriod-END-PREV-YEAR)"
+        "(start-next-year RelativeDatePeriod-START-NEXT-YEAR)"
+        "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)"
+        "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)"
+        "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))");
+    %}
+
 %ignore GncOptionMultichoiceKeyType;
- /* Replace GncOptionMultichoiceValue::get_value with one that restores the keytype that Scheme sent it. */
+
 %inline %{
     SCM get_scm_value(const GncOptionMultichoiceValue& option)
     {
@@ -528,11 +533,34 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     }
     void set_value_from_scm(SCM new_value)
     {
+        if (!$self)
+            return;
         std::visit([new_value](auto& option) {
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionDateValue>)
+                {
+                    if (scm_is_pair(new_value))
+                    {
+                        auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(new_value)))};
+                        if (strcmp(car, "relative") == 0)
+                        {
+                            auto lookup{scm_assq_ref(reldate_values,
+                                                     scm_cdr(new_value))};
+                            auto reldate{scm_primitive_eval(lookup)};
+                            option.set_value(static_cast<RelativeDatePeriod>(scm_to_int(reldate)));
+                        }
+                        else
+                        {
+                            option.set_value(scm_to_int64(scm_cdr(new_value)));
+                        }
+                    }
+                    return;
+                }
                 option.set_value(scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value));
             }, swig_get_option($self));
     }
 };
+
 %extend GncOptionDB {
     %template(set_option_string) set_option<std::string>;
     %template(set_option_int) set_option<int>;

commit d41292fdddc91ca2e754f838179dd56ca22c2132
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 18 17:13:09 2021 -0800

    Make set_selectable a GncOptionsGtkUIItem member function.
    
    Callable from the option object with set_ui_item_selectable and from
    Scheme as gnc-option-db-set-option-selectable-by-name.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 0cb06fa98..87c2f2b71 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -170,6 +170,13 @@ GncOptionGtkUIItem::~GncOptionGtkUIItem()
         g_object_unref(m_widget);
 }
 
+void
+GncOptionGtkUIItem::set_selectable(bool selectable) const noexcept
+{
+    if (m_widget)
+        gtk_widget_set_sensitive (m_widget, selectable);
+}
+
 void
 GncOptionGtkUIItem::clear_ui_item()
 {
@@ -290,24 +297,6 @@ gnc_option_changed_option_cb(GtkWidget *dummy, GncOption* option)
 }
 
 
-/*
- * set_selectable                               *
- *   Change the selectable state of the widget that represents a
- *   GUI option.
- *
- * option      - option to change widget state for
- * selectable  - if false, update the widget so that it
- *                     cannot be selected by the user.  If true,
- *                     update the widget so that it can be selected.
- */
-static void
-set_selectable (GncOption& option, bool selectable)
-{
-    auto widget = gnc_option_get_gtk_widget(&option);
-    if (widget)
-        gtk_widget_set_sensitive (widget, selectable);
-}
-
 // This do-nothing template is specialized for each GncOptionUIType.
 template<GncOptionUIType type> GtkWidget*
 create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**,
diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp
index 7daef2351..c2fb570c2 100644
--- a/gnucash/gnome-utils/dialog-options.hpp
+++ b/gnucash/gnome-utils/dialog-options.hpp
@@ -50,6 +50,7 @@ public:
     GncOptionGtkUIItem(const GncOptionGtkUIItem& item);
     GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default;
     virtual ~GncOptionGtkUIItem() override;
+    virtual void set_selectable(bool) const noexcept override;
     void clear_ui_item() override;
     void set_widget(GtkWidget* widget);
     virtual GtkWidget* const get_widget() const { return m_widget; }
diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp
index dbd44e8b9..a40381bba 100644
--- a/libgnucash/app-utils/gnc-option-ui.hpp
+++ b/libgnucash/app-utils/gnc-option-ui.hpp
@@ -51,6 +51,7 @@ public:
     GncOptionUIType get_ui_type() const noexcept { return m_type; }
     virtual void set_dirty(bool status) noexcept { m_dirty = status; }
     virtual bool get_dirty() const noexcept { return m_dirty; }
+    virtual void set_selectable(bool selectable) const noexcept = 0;
     virtual void clear_ui_item() = 0;
     virtual void set_ui_item_from_option(GncOption& option) noexcept = 0;
     virtual void set_option_from_ui_item(GncOption& option) noexcept = 0;
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index da5bc4036..5053c6297 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -193,6 +193,13 @@ GncOption::set_ui_item(GncOptionUIItemPtr&& ui_item)
     m_ui_item = std::move(ui_item);
 }
 
+void
+GncOption::set_ui_item_selectable(bool selectable) const noexcept
+{
+    if (m_ui_item)
+        m_ui_item->set_selectable(selectable);
+}
+
 const GncOptionUIType
 GncOption::get_ui_type() const
 {
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 342cda4fa..4e88f3412 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -98,6 +98,7 @@ public:
     const std::string& get_docstring() const;
     void set_ui_item(GncOptionUIItemPtr&& ui_elem);
     const GncOptionUIType get_ui_type() const;
+    void set_ui_item_selectable(bool) const noexcept;
     GncOptionUIItem* const get_ui_item() const;
     void set_ui_item_from_option();
     void set_option_from_ui_item();
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index c7b294600..7f6caed50 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -768,6 +768,16 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         return optiondb->find_option(section, name);
     }
+
+    void
+    gnc_option_db_set_option_selectable_by_name(GncOptionDBPtr& odb,
+                                                const char* section,
+                                                const char* name,
+                                                bool selectable)
+    {
+        auto option{odb->find_option(section, name)};
+        option->set_ui_item_selectable(selectable);
+    }
 %}
 
 #endif //SWIGGUILE
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 503576ebc..39f1aee6a 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -526,6 +526,7 @@ public:
     ~OptionUIItem() = default;
     void set_dirty(bool status) noexcept override { m_dirty = status; }
     bool get_dirty() const noexcept override { return m_dirty; }
+    void set_selectable(bool selectable) const noexcept override {}
     void clear_ui_item() override { m_widget.clear(); }
     void set_ui_item_from_option(GncOption& option) noexcept override
     {

commit a21f329b1e3b45273b0f85a12ade98a3ee04655c
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 18 17:38:10 2021 -0800

    Use 100% instead of 20000 px for default Pixmap size.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index e6b82fa2a..ecd1ce838 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1037,8 +1037,9 @@ gnc_register_number_plot_size_option(GncOptionDB* db,
                                      const char* key, const char* doc_string,
                                      int value)
 {
+    // Pixel values don't make much sense so always use percent.
     GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
-                value, 100, 20000, 5}};
+                value, 10, 100, 1}};
     db->register_option(section, std::move(option));
 }
 

commit 8c2a8edbed5f68987d77cb96a403ce44e57740a8
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 18 17:36:52 2021 -0800

    Implement gnc_option_db_set_string_value and gnc_optiondb_lookup_string_value.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index fd465a859..e6b82fa2a 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1466,15 +1466,19 @@ gnc_option_db_book_options(GncOptionDB* odb)
 }
 
 const char*
-gnc_option_db_lookup_string_value(GncOptionDB*, const char*, const char*)
+gnc_option_db_lookup_string_value(GncOptionDB* odb, const char* section, const char* name)
 {
+    auto value{odb->lookup_string_option(section, name)};
+    if (value.empty())
     return nullptr;
+    return strdup(value.c_str());
 }
 
 void
-gnc_option_db_set_string_value(GncOptionDB*, const char*,
-                               const char*, const char*)
+gnc_option_db_set_string_value(GncOptionDB* odb, const char* section,
+                               const char* name, const char* value)
 {
+    odb->set_option<std::string>(section, name, value);
 }
 
 const QofInstance*

commit 86102e1be7b1b4742c2acc194190af72dad43931
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 18 17:32:09 2021 -0800

    Return SCM_BOOL_F instead of nullptr to Scheme optiondb-lookup.
    
    Because unlike C, Scheme thinks (if 0) should return true. Besides, Swig
    hides pointers so a null check doesn't even work.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 699a8eadf..fd465a859 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1486,14 +1486,17 @@ gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section,
 }
 
 SCM
-gnc_option_db_lookup_scm_value(GncOptionDB*, const char*, const char*)
+gnc_option_db_lookup_scm_value(GncOptionDB* odb, const char* section,
+                               const char* name)
 {
-    return nullptr;
+    std::cerr << "Use gnc_option_db_lookup_value." << std::endl;
+    return SCM_BOOL_F;
 }
 
 void
 gnc_option_db_set_scm_value(GncOptionDB*, const char*, const char*, SCM)
 {
+    std::cerr << "Use gnc_set_option." << std::endl;
 }
 
 // Force creation of templates
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 37626d660..c7b294600 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -392,11 +392,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
 %ignore swig_get_option(GncOption&);
 %inline %{
+#include <cassert>
 #include "gnc-option.hpp"
 #include "gnc-option-ui.hpp"
 
     GncOptionVariant& swig_get_option(GncOption* option)
     {
+        assert(option);
         return *option->m_option;
     }
 %}
@@ -442,6 +444,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %ignore gnc_register_start_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
 %ignore gnc_register_end_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
 
+%typemap(in) GncOption* "$1 = scm_is_true($input) ? static_cast<GncOption*>(scm_to_pointer($input)) : nullptr;"
+%typemap(out) GncOption* "$result = ($1) ? scm_from_pointer($1, nullptr) : SCM_BOOL_F;"
 
 %ignore GncOptionMultichoiceKeyType;
  /* Replace GncOptionMultichoiceValue::get_value with one that restores the keytype that Scheme sent it. */
@@ -497,6 +501,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %extend GncOption {
     SCM get_scm_value()
     {
+        if (!$self)
+            return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
                 if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                               GncOptionMultichoiceValue>)
@@ -510,6 +516,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     }
     SCM get_scm_default_value()
     {
+        if (!$self)
+            return SCM_BOOL_F;
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_default_value()};
                 if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
@@ -705,11 +713,21 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return GncOption_get_scm_value(db_opt);
     }
 
+    static SCM
+    gnc_option_db_lookup_value(const GncOptionDB* optiondb, const char* section,
+                               const char* name)
+    {
+        auto db_opt = optiondb->find_option(section, name);
+        if (!db_opt)
+            return SCM_BOOL_F;
+        return GncOption_get_scm_value(const_cast<GncOption*>(db_opt));
+    }
+
     static SCM
     gnc_option_default_value(const GncOptionDBPtr& optiondb,
                              const char* section, const char* name)
     {
-        auto db_opt = optiondb->find_option(section, name);
+        auto db_opt{optiondb->find_option(section, name)};
         if (!db_opt)
             return SCM_BOOL_F;
         return GncOption_get_scm_default_value(db_opt);
@@ -719,7 +737,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     gnc_set_option(const GncOptionDBPtr& optiondb, const char* section,
                    const char* name, SCM new_value)
     {
-        auto db_opt = optiondb->find_option(section, name);
+        auto db_opt{optiondb->find_option(section, name)};
         if (!db_opt)
         {
             std::cerr <<"Attempt to write non-existent option " << section

commit 53ad0ba440e6bd088f05916db257539bb4343813
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 18 17:28:17 2021 -0800

    Prevent SWIG from trying to call delete on a std::unique_ptr.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index dcc8de6b7..37626d660 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -24,7 +24,7 @@ namespace std {
      pointer get () const;
 //     operator bool () const;
 
-     ~unique_ptr();
+      ~unique_ptr() = delete; //Otherwise swig takes the unique_ptr and calls delete on it.
   };
 }
 

commit 08d1eebba251d123f68a02fa2a5ab7b1208e836d
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 18 17:27:37 2021 -0800

    Use GUID strings instead of QofInstance* for scheme value of QofInstance.
    
    Because that's what gets used everywhere else.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index d2f64c252..699a8eadf 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1478,9 +1478,11 @@ gnc_option_db_set_string_value(GncOptionDB*, const char*,
 }
 
 const QofInstance*
-gnc_option_db_lookup_qofinstance_value(GncOptionDB*, const char*, const char*)
+gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section,
+                                       const char* name)
 {
-    return nullptr;
+    auto option{odb->find_option(section, name)};
+    return option->get_value<const QofInstance*>();
 }
 
 SCM
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 95016fda8..dcc8de6b7 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -116,10 +116,13 @@ scm_from_value<double>(double value)
 template <> inline SCM
 scm_from_value<const QofInstance*>(const QofInstance* value)
 {
-    auto guid = guid_to_string(qof_instance_get_guid(value));
-    auto scm_guid = scm_from_utf8_string(guid);
-    g_free(guid);
-    return scm_guid;
+    if (!value) return SCM_BOOL_F;
+    auto type{qof_collection_get_type(qof_instance_get_collection(value))};
+    auto guid_str{guid_to_string(qof_instance_get_guid(value))};
+    auto scm_guid = scm_from_utf8_string(guid_str);
+    auto scm_type = scm_from_utf8_string(type);
+    free(guid_str);
+    return scm_cons(scm_type, scm_guid);
 }
 
 template <typename ValueType> inline ValueType
@@ -157,6 +160,21 @@ scm_to_value<double>(SCM new_value)
     return scm_to_double(new_value);
 }
 
+template <> inline const QofInstance*
+scm_to_value<const QofInstance*>(SCM new_value)
+{
+    if (new_value == SCM_BOOL_F || !scm_is_pair(new_value))
+        return nullptr;
+    auto guid_str{scm_to_utf8_stringn(scm_car(new_value), nullptr)};
+    auto type{scm_to_utf8_stringn(scm_cdr(new_value), nullptr)};
+    GncGUID new_guid;
+    string_to_guid(guid_str, &new_guid);
+    free(guid_str);
+    auto coll{qof_book_get_collection(qof_session_get_book(gnc_get_current_session()), type)};
+    free(type);
+    return qof_collection_lookup_entity(coll, &new_guid);
+}
+
 template <>inline SCM
 scm_from_value<GncOptionAccountList>(GncOptionAccountList value)
 {

commit fba024854821ab5ee7dbd2ddb3c3137fd8bf3cc5
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 18 17:10:16 2021 -0800

    Support different GncOptionMultichoiceValue key types.
    
    Scheme can use strings, symbols, or ints as keys in multichoice options,
    but C++ can handle only strings. Add conversion and tracking so that the
    right key type gets sent back to Scheme.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 0abd8314f..d2ae693dd 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -500,7 +500,8 @@ operator>> (std::istream& iss, OptType& opt)
 
 using GncMultichoiceOptionEntry = std::tuple<const std::string,
                                              const std::string,
-                                             const std::string>;
+                                             const std::string,
+                                             GncOptionMultichoiceKeyType>;
 using GncMultichoiceOptionIndexVec = std::vector<std::size_t>;
 using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
 
@@ -575,7 +576,8 @@ public:
         if (vec.size() == 1)
             return std::get<0>(m_choices.at(vec[0]));
         else
-            return c_list_string;
+            throw std::length_error("Retrieving multiple values from a multichoice isn't implemented.");
+
     }
     const std::string& get_default_value() const
     {
@@ -671,6 +673,7 @@ public:
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+    GncOptionMultichoiceKeyType get_keytype(unsigned i) const { return std::get<3>(m_choices.at(i)); }
 private:
     std::size_t find_key (const std::string& key) const noexcept
     {
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 798e539a3..342cda4fa 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -63,6 +63,13 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
 
 using GncOptionVariantPtr = std::unique_ptr<GncOptionVariant>;
 
+enum class GncOptionMultichoiceKeyType
+{
+    SYMBOL,
+    STRING,
+    NUMBER,
+};
+
 class GncOption
 {
 public:
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index de0f13aff..0d05c1ab7 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -51,7 +51,8 @@ using GncOptionAccountList = std::vector<const Account*>;
 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
 using GncMultichoiceOptionEntry = std::tuple<const std::string,
                                              const std::string,
-                                             const std::string>;
+                                             const std::string,
+                                             GncOptionMultichoiceKeyType>;
 using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
 
 /**
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 0626f105e..95016fda8 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -256,14 +256,34 @@ gnc_option_test_book_destroy(QofBook* book)
 
 %typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices)
 {
+    using KeyType = GncOptionMultichoiceKeyType;
     auto len = scm_to_size_t(scm_length($input));
     for (std::size_t i = 0; i < len; ++i)
     {
         SCM vec = scm_list_ref($input, scm_from_size_t(i));
-        std::string key{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 0))};
+        SCM keyval, v_ref_0 = SCM_SIMPLE_VECTOR_REF(vec, 0);
+        GncOptionMultichoiceKeyType keytype;
+        if (scm_is_symbol(v_ref_0))
+        {
+            keyval = scm_symbol_to_string(SCM_SIMPLE_VECTOR_REF(vec, 0));
+            keytype = KeyType::SYMBOL;
+        }
+        else if (scm_is_string(v_ref_0))
+        {
+            keyval = SCM_SIMPLE_VECTOR_REF(vec, 0);
+            keytype = KeyType::STRING;
+        }
+        else if (scm_is_integer(v_ref_0))
+        {
+            keyval = scm_number_to_string(v_ref_0, scm_from_uint(10u));
+            keytype = KeyType::NUMBER;
+        }
+        else
+            throw std::invalid_argument("Unsupported key type in multichoice option.");
+        std::string key{scm_to_utf8_string(keyval)};
         std::string name{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 1))};
         std::string desc{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 2))};
-        choices.push_back({std::move(key), std::move(name), std::move(desc)});
+        choices.push_back({std::move(key), std::move(name), std::move(desc), keytype});
     }
     $1 = &choices;
  }
@@ -404,6 +424,45 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %ignore gnc_register_start_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
 %ignore gnc_register_end_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
 
+
+%ignore GncOptionMultichoiceKeyType;
+ /* Replace GncOptionMultichoiceValue::get_value with one that restores the keytype that Scheme sent it. */
+%inline %{
+    SCM get_scm_value(const GncOptionMultichoiceValue& option)
+    {
+        using KeyType = GncOptionMultichoiceKeyType;
+        auto scm_value = [](const char* value, KeyType keytype) -> SCM {
+            auto scm_str{scm_from_utf8_string(value)};
+            switch (keytype)
+            {
+                case KeyType::SYMBOL:
+                return scm_string_to_symbol(scm_str);
+                case KeyType::STRING:
+                return scm_str;
+                case KeyType::NUMBER:
+                    return scm_string_to_number(scm_str, scm_from_int(10));
+            };
+        };
+
+        auto indexes = option.get_multiple();
+        if (indexes.empty())
+            indexes = option.get_default_multiple();
+        if (indexes.empty())
+            return SCM_BOOL_F;
+        if (indexes.size() == 1) // FIXME: Should use bool member to decide
+            return scm_value(option.permissible_value(indexes[0]),
+                             option.get_keytype(indexes[0]));
+        auto values{scm_list_1(SCM_UNDEFINED)};
+        for(auto index : indexes)
+        {
+            auto val{scm_list_1(scm_value(option.permissible_value(index),
+                                          option.get_keytype(index)))};
+            values = scm_append(scm_list_2(val, values));
+        }
+        return scm_reverse(values);
+    }
+ %}
+
 %include "gnc-option-date.hpp"
 %include "gnc-option.hpp"
 %include "gnc-option-impl.hpp"
@@ -421,6 +480,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     SCM get_scm_value()
     {
         return std::visit([](const auto& option)->SCM {
+                if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                              GncOptionMultichoiceValue>)
+                    return get_scm_value(option);
                 auto value{option.get_value()};
                 if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
                               SCM>)

commit 55a2ed1df8e2a2a170f754555723c9cf1ebaf2bd
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Feb 15 16:08:46 2021 -0800

    gnc-optiondb.i uses functions declared in gnc-optiondb.h.
    
    So it needs it declared in the swig file.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 7a8f0ceb5..0626f105e 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -40,6 +40,7 @@ namespace std {
 
  //%module sw_gnc_optiondb
 %{
+#include "gnc-optiondb.h"
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
 #include "gnc-option-date.hpp"

commit 18b83874fdb08cd691a02de814e17a775f8ca570
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Feb 15 15:49:03 2021 -0800

    Another bit of Remove test-option-utils

diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index f5358e733..f4c7425da 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -92,9 +92,6 @@ if (HAVE_SRFI64)
   gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}")
 endif()
 
-# 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(test_autoclear_SOURCES
     test-autoclear.cpp
 )

commit 0b7ccfbd5bc6ceaea40ec026ffa074f496fe4eb9
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 4 17:21:31 2021 -0800

    Create gnc-make-foo-option functions.
    
    For compatibility with current Scheme code.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 71c52377e..da5bc4036 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -584,3 +584,21 @@ template bool GncOption::validate(std::string) const;
 template bool GncOption::validate(const QofInstance*) const;
 template bool GncOption::validate(RelativeDatePeriod) const;
 template bool GncOption::validate(GncMultichoiceOptionIndexVec) const;
+
+template GncOption* gnc_make_option<const std::string&>(const char*,
+                                                        const char*,
+                                                        const char*,
+                                                        const char*,
+                                                        const std::string&,
+                                                        GncOptionUIType);
+template GncOption* gnc_make_option<bool>(const char*, const char*, const char*,
+                                          const char*, bool, GncOptionUIType);
+template GncOption* gnc_make_option<int64_t>(const char*, const char*,
+                                             const char*, const char*, int64_t,
+                                             GncOptionUIType);
+template GncOption* gnc_make_option<const QofInstance*>(const char*,
+                                                        const char*,
+                                                        const char*,
+                                                        const char*,
+                                                        const QofInstance*,
+                                                        GncOptionUIType);
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index ce3f2f020..798e539a3 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -134,5 +134,12 @@ operator>>(std::istream& iss, GncOption& opt)
     return opt.in_stream(iss);
 }
 
+template<typename ValueType> GncOption*
+gnc_make_option(const char* section, const char* name,
+                const char* key, const char* doc_string,
+                ValueType value, GncOptionUIType ui_type)
+{
+    return new GncOption(section, name, key, doc_string, value, ui_type);
+}
 
 #endif //GNC_OPTION_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 4aca8778a..7a8f0ceb5 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -406,11 +406,17 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %include "gnc-option-date.hpp"
 %include "gnc-option.hpp"
 %include "gnc-option-impl.hpp"
+%include "gnc-optiondb.h"
 %include "gnc-optiondb.hpp"
 %include "gnc-optiondb-impl.hpp"
+%include "gnc-option-uitype.hpp"
 
-%extend GncOption {
+%template(gnc_make_string_option) gnc_make_option<std::string>;
+%template(gnc_make_bool_option) gnc_make_option<bool>;
+%template(gnc_make_int64_option) gnc_make_option<int64_t>;
+%template(gnc_make_qofinstance_option) gnc_make_option<const QofInstance*>;
 
+%extend GncOption {
     SCM get_scm_value()
     {
         return std::visit([](const auto& option)->SCM {
@@ -448,6 +454,160 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %template(gnc_register_number_range_option_int) gnc_register_number_range_option<int>;
 
 %inline %{
+    GncOption* gnc_make_account_list_option(const char* section,
+                                            const char* name, const char* key,
+                                            const char* doc_string,
+                                            const GncOptionAccountList& value)
+    {
+        try {
+            return new GncOption{GncOptionAccountValue{section, name, key,
+                        doc_string, GncOptionUIType::ACCOUNT_LIST, value}};
+        }
+        catch (const std::exception& err)
+        {
+            std::cerr << "Make account list option threw unexpected exception " << err.what() << ", option not created." << std::endl;
+            return nullptr;
+        }
+    }
+
+    GncOption* gnc_make_account_list_limited_option(const char* section,
+                                                    const char* name,
+                                                    const char* key,
+                                                    const char* doc_string,
+                                                    const GncOptionAccountList& value,
+                                                    GncOptionAccountTypeList&& allowed)
+    {
+        try
+        {
+            return new GncOption{GncOptionAccountValue{section, name, key,
+                        doc_string, GncOptionUIType::ACCOUNT_LIST, value,
+                        std::move(allowed)}};
+        }
+        catch (const std::invalid_argument& err)
+        {
+            std::cerr << "Account List Limited Option, value failed validation, option not created.\n";
+            return nullptr;
+        }
+    }
+
+    GncOption* gnc_make_account_sel_limited_option(const char* section,
+                                                   const char* name,
+                                                   const char* key,
+                                                   const char* doc_string,
+                                                   const GncOptionAccountList& value,
+                                                   GncOptionAccountTypeList&& allowed)
+    {
+        try
+        {
+            return new GncOption{GncOptionAccountValue{section, name, key,
+                        doc_string, GncOptionUIType::ACCOUNT_SEL, value,
+                        std::move(allowed)}};
+        }
+        catch (const std::invalid_argument& err)
+        {
+            std::cerr <<"Account Sel Limited Option, value failed validation, option not creted.\n";
+            return nullptr;
+        }
+    }
+
+    GncOption* gnc_make_multichoice_option(const char* section,
+                                           const char* name, const char* key,
+                                           const char* doc_string,
+                                           GncMultichoiceOptionChoices&& choices)
+    {
+        try {
+            return new GncOption{GncOptionMultichoiceValue{section, name, key,
+                        doc_string,
+                        choices.empty() ? "None" :
+                        std::get<0>(choices.at(0)).c_str(),
+                        std::move(choices)}};
+        }
+        catch (const std::exception& err)
+        {
+            std::cerr << "Make multichoice option threw unexpected exception " << err.what() << ", option not created." << std::endl;
+            return nullptr;
+        }
+    }
+
+    GncOption* gnc_make_list_option(const char* section,
+                                    const char* name, const char* key,
+                                    const char* doc_string, const char* value,
+                                    GncMultichoiceOptionChoices&& list)
+    {
+        try {
+            return new GncOption{GncOptionMultichoiceValue{section, name, key,
+                        doc_string, value,  std::move(list),
+                        GncOptionUIType::LIST}};
+        }
+        catch (const std::exception& err)
+        {
+            std::cerr << "Make list option threw unexpected exception " << err.what() << ", option not created." << std::endl;
+            return nullptr;
+        }
+    }
+
+    GncOption* gnc_make_range_value_option(const char* section,
+                                           const char* name, const char* key,
+                                           const char* doc_string, double value,
+                                           double min, double max, double step)
+    {
+        try
+        {
+            return new GncOption{GncOptionRangeValue<double>{section, name, key,
+                        doc_string, value, min,
+                        max, step}};
+        }
+        catch(const std::invalid_argument& err)
+        {
+            std::cerr <<"Number Range Option " << err.what() << ", option not created.\n";
+            return nullptr;
+        }
+    }
+
+    GncOption* gnc_make_plot_size_option(const char* section,
+                                         const char* name, const char* key,
+                                         const char* doc_string, int value,
+                                         int min, int max, int step)
+    {
+        try
+        {
+            return new GncOption{GncOptionRangeValue<int>{section, name, key,
+                        doc_string, value, min,
+                        max, step}};
+        }
+        catch(const std::invalid_argument& err)
+        {
+            std::cerr <<"Plot Size Option " << err.what() << ", option not created.\n";
+            return nullptr;
+        }
+    }
+
+    GncOption* gnc_make_currency_option(const char* section,
+                                        const char* name, const char* key,
+                                        const char* doc_string,
+                                        gnc_commodity *value)
+    {
+        try
+        {
+            return new GncOption{GncOptionValidatedValue<const QofInstance*>{
+                    section, name, key, doc_string, (const QofInstance*)value,
+                        [](const QofInstance* new_value) -> bool
+                    {
+                        return GNC_IS_COMMODITY (new_value) &&
+                            gnc_commodity_is_currency(GNC_COMMODITY(new_value));
+                    },
+                        GncOptionUIType::CURRENCY
+                            }
+            };
+        }
+        catch (const std::exception& err)
+        {
+            std::cerr << "gnc_make_currency_option threw " << err.what() <<
+                ", option not created." << std::endl;
+            return nullptr;
+        }
+    }
+
     using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
 /* Forward decls */
     GncOptionDBPtr new_gnc_optiondb();

commit a602f64b170290d05febe8d5e8ae89b8e6f45b2f
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 4 17:19:02 2021 -0800

    SFINAE-constrain the GncOption constructor templates.
    
    So that one can't instantiate an invalid constructor.
    
    Unfortunately Swig doesn't understand SFINAE and will try to create the
    invalid constructors anyway but at least this generates a compile-time
    error when it tries to.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 5ba5b5731..71c52377e 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -33,7 +33,10 @@ extern "C"
 #include <qoflog.h>
 }
 
-template <typename ValueType>
+template <typename ValueType,
+          typename std::enable_if_t<!std::is_base_of_v<OptionClassifier,
+                                                       std::decay_t<ValueType>>,
+                               int>>
 GncOption::GncOption(const char* section, const char* name,
                      const char* key, const char* doc_string,
                      ValueType value, GncOptionUIType ui_type) :
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index a39c91c24..ce3f2f020 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -37,6 +37,7 @@ extern "C"
 #include "gnc-option-ui.hpp"
 #include "gnc-option-date.hpp"
 
+struct OptionClassifier;
 class GncOptionUIItem;
 using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
 struct QofInstance_s;
@@ -65,10 +66,16 @@ using GncOptionVariantPtr = std::unique_ptr<GncOptionVariant>;
 class GncOption
 {
 public:
-    template <typename OptionType>
+    template <typename OptionType,
+              typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
+                                                          std::decay_t<OptionType>>,
+                               int>  = 0>
     GncOption(OptionType option) :
         m_option{std::make_unique<GncOptionVariant>(option)} {}
-    template <typename ValueType>
+    template <typename ValueType,
+              typename std::enable_if_t<!std::is_base_of_v<OptionClassifier,
+                                                          std::decay_t<ValueType>>,
+                               int>  = 0>
     GncOption(const char* section, const char* name,
               const char* key, const char* doc_string,
               ValueType value,

commit 43f4bcb610ca24830b7b87b38800c458153a78fe
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 4 17:13:53 2021 -0800

    Make GncOptionUIType an enum class.
    
    So that Swig will create properly constrained identifiers for its values.

diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp
index fc0204a71..519df3889 100644
--- a/gnucash/gnome/business-options-gnome.cpp
+++ b/gnucash/gnome/business-options-gnome.cpp
@@ -63,7 +63,7 @@ ui_type_to_owner_type(GncOptionUIType ui_type)
         return GNC_OWNER_EMPLOYEE;
 
     std::ostringstream oss;
-    oss << "UI type " << ui_type << " could not be converted to owner type\n";
+    oss << "UI type " << static_cast<unsigned int>(ui_type) << " could not be converted to owner type\n";
     throw std::invalid_argument(oss.str());
 }
 
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index 5cc07c058..b1e784a45 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -23,7 +23,7 @@
 #ifndef GNC_OPTION_UITYPE_HPP__
 #define GNC_OPTION_UITYPE_HPP__
 
-enum GncOptionUIType
+enum class GncOptionUIType : unsigned int
 {
     INTERNAL,
     BOOLEAN,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index eae05d49b..d2f64c252 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -690,17 +690,17 @@ is_qofinstance_ui_type(GncOptionUIType type)
 {
     switch (type)
     {
-        case CURRENCY:
-        case COMMODITY:
-        case ACCOUNT_SEL:
-        case BUDGET:
-        case OWNER:
-        case CUSTOMER:
-        case VENDOR:
-        case EMPLOYEE:
-        case INVOICE:
-        case TAX_TABLE:
-        case QUERY:
+        case GncOptionUIType::CURRENCY:
+        case GncOptionUIType::COMMODITY:
+        case GncOptionUIType::ACCOUNT_SEL:
+        case GncOptionUIType::BUDGET:
+        case GncOptionUIType::OWNER:
+        case GncOptionUIType::CUSTOMER:
+        case GncOptionUIType::VENDOR:
+        case GncOptionUIType::EMPLOYEE:
+        case GncOptionUIType::INVOICE:
+        case GncOptionUIType::TAX_TABLE:
+        case GncOptionUIType::QUERY:
             return true;
         default:
             return false;

commit 7fa6778b4b71bca4129d60c98b1746ba0bcddae7
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 4 17:08:03 2021 -0800

    Add a register_option overload to GncOptionDB
    
    For compatibility with existing scheme code.

diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 058f308be..472b1ca24 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -84,6 +84,7 @@ public:
     size_t num_sections() const noexcept { return m_sections.size(); }
     bool get_changed() const noexcept { return m_dirty; }
     void register_option(const char* section, GncOption&& option);
+    void register_option(const char* section, GncOption* option);
     void unregister_option(const char* section, const char* name);
     void set_default_section(const char* section);
     const GncOptionSection* const get_default_section() const noexcept;
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 452e9e62a..eae05d49b 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -168,6 +168,13 @@ GncOptionDB::register_option(const char* sectname, GncOption&& option)
     m_sections.back()->add_option(std::move(option));
 }
 
+void
+GncOptionDB::register_option(const char* sectname, GncOption* option)
+{
+    register_option(sectname, std::move(*option));
+    delete option;
+}
+
 void
 GncOptionDB::unregister_option(const char* sectname, const char* name)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index e5cc55674..4aca8778a 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -364,6 +364,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
 %ignore gnc_option_to_scheme;
 %ignore gnc_option_from_scheme;
+/* GncOptionDB::register_option comes in GncOption* and GncOption&&
+ * overloads. The latter isn't useful to SWIG, ignore it.
+ */
+%ignore GncOptionDB::register_option(const char*, GncOption&&);
+
 /* The following functions are overloaded in gnc-optiondb.hpp to provide both
  * GncOptionDB* and GncOptionDBPtr& versions. That confuses SWIG so ignore the
  * raw-ptr version.

commit cb7270cafe53189b6d88374658ae097f4adcad24
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 22 16:24:15 2020 -0800

    Fix myriad gcc10 complaints.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index c117eb63d..0cb06fa98 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1187,7 +1187,7 @@ create_multichoice_widget(GncOption& option)
 
     auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
     /* Add values to the list store, entry and tooltip */
-    for (auto i = 0; i < num_values; i++)
+    for (decltype(num_values) i = 0; i < num_values; i++)
     {
         GtkTreeIter iter;
         auto itemstring = option.permissible_value_name(i);
@@ -1325,7 +1325,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) :
     auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
     /* Add values to the list store, entry and tooltip */
     auto num = option.num_permissible_values();
-    for (auto index = 0; index < num; ++index)
+    for (decltype(num) index = 0; index < num; ++index)
     {
         GtkTreeIter  iter;
         gtk_list_store_append (store, &iter);
@@ -1503,7 +1503,7 @@ date_set_relative_cb(GtkWidget *widget, gpointer data1)
     }
 }
 
-GtkWidget*
+static GtkWidget*
 create_date_option_widget(GncOption& option, GtkGrid *page_box,
                           GtkLabel *name_label, char *documentation,
                                /* Return values */
@@ -1682,8 +1682,8 @@ public:
         const GncOptionAccountList& accounts =
             option.get_value<GncOptionAccountList>();
         for (auto account : accounts)
-            g_list_prepend(acc_list, static_cast<void*>(const_cast<Account*>(account)));
-        g_list_reverse(acc_list);
+            acc_list = g_list_prepend(acc_list, static_cast<void*>(const_cast<Account*>(account)));
+        acc_list = g_list_reverse(acc_list);
         gnc_tree_view_account_set_selected_accounts(widget, acc_list, TRUE);
         g_list_free(acc_list);
     }
@@ -1699,7 +1699,7 @@ public:
     }
 };
 
-GtkWidget*
+static GtkWidget*
 create_account_widget(GncOption& option, char *name)
 {
     bool multiple_selection;
@@ -2022,7 +2022,7 @@ create_list_widget(GncOption& option, char *name)
     gtk_tree_view_set_headers_visible(view, FALSE);
 
     auto num_values = option.num_permissible_values();
-    for (auto i = 0; i < num_values; i++)
+    for (decltype(num_values) i = 0; i < num_values; i++)
     {
         auto raw_string = option.permissible_value_name(i);
         auto string = (raw_string && *raw_string) ? _(raw_string) : "";
@@ -2442,7 +2442,7 @@ public:
         g_list_free(list);
         auto val{g_object_get_data (G_OBJECT (button),
                                     "gnc_radiobutton_index")};
-        g_return_if_fail (GPOINTER_TO_INT (val) == index);
+        g_return_if_fail (GPOINTER_TO_UINT (val) == index);
 
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
     }
@@ -2473,7 +2473,7 @@ create_radiobutton_widget(char *name, GncOption& option)
     gtk_container_add (GTK_CONTAINER (frame), box);
 
     /* Iterate over the options and create a radio button for each one */
-    for (auto i = 0; i < num_values; i++)
+    for (decltype(num_values) i = 0; i < num_values; i++)
     {
         auto label = option.permissible_value_name(i);
         auto tip = option.permissible_value_description(i);
@@ -2850,13 +2850,3 @@ gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win)
                                 (GNCOptionWinCallback)gnc_book_options_help_cb,
                                 nullptr);
 }
-
-/* Dummy function impls. The following functions are declared in
- * dialog_options.h and used by clients but they're made obsolete by the new
- * options system. They're here to satisfy the linker.
- */
-GtkWidget* const
-gnc_option_get_gtk_widget (GncOption *option)
-{
-    return nullptr;
-}
diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp
index 8f4707ac2..454f74fab 100644
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -456,7 +456,7 @@ static GtkRadioActionEntry radio_entries [] =
 };
 
 /** The number of radio actions provided by the main window. */
-static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
+static gsize n_radio_entries = G_N_ELEMENTS (radio_entries);
 #endif
 
 /** These are the "important" actions provided by the main window.
@@ -685,7 +685,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
     gsize length;
     gboolean max, visible, desired_visibility;
     gchar *window_group;
-    gint page_start, page_count, i;
+    gsize page_start, page_count, i;
     GError *error = nullptr;
 
     /* Setup */
@@ -903,7 +903,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
     }
     else if (length != page_count)
     {
-        g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %d",
+        g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %" G_GSIZE_FORMAT,
                   window_group, WINDOW_PAGEORDER, length, page_count);
     }
     else
@@ -1902,7 +1902,7 @@ gnc_main_window_update_radio_button (GncMainWindow *window)
     GtkAction *action, *first_action;
     GSList *action_list;
     gchar *action_name;
-    gint index;
+    gsize index;
 
     ENTER("window %p", window);
 
@@ -1910,12 +1910,12 @@ gnc_main_window_update_radio_button (GncMainWindow *window)
     index = g_list_index(active_windows, window);
     if (index >= n_radio_entries)
     {
-        LEAVE("window %d, only %d actions", index, n_radio_entries);
+        LEAVE("window  %" G_GSIZE_FORMAT ", only %" G_GSIZE_FORMAT " actions", index, n_radio_entries);
         return;
     }
 
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    action_name = g_strdup_printf("Window%dAction", index);
+    action_name = g_strdup_printf("Window%" G_GSIZE_FORMAT "Action", index);
     action = gtk_action_group_get_action(priv->action_group, action_name);
 
     /* Block the signal so as not to affect window ordering (top to
@@ -1923,15 +1923,15 @@ gnc_main_window_update_radio_button (GncMainWindow *window)
     action_list = gtk_radio_action_get_group(GTK_RADIO_ACTION(action));
     if (action_list)
     {
-        first_action = g_slist_last(action_list)->data;
+        first_action = static_cast<GtkAction*>(g_slist_last(action_list)->data);
         g_signal_handlers_block_by_func(G_OBJECT(first_action),
-                                        G_CALLBACK(gnc_main_window_cmd_window_raise),
+                                        (void*)gnc_main_window_cmd_window_raise,
                                         window);
         DEBUG("blocked signal on %p, set %p active, window %p", first_action,
               action, window);
         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
         g_signal_handlers_unblock_by_func(G_OBJECT(first_action),
-                                          G_CALLBACK(gnc_main_window_cmd_window_raise),
+                                          (void*)gnc_main_window_cmd_window_raise,
                                           window);
     }
     g_free(action_name);
@@ -1956,13 +1956,13 @@ gnc_main_window_update_menu_item (GncMainWindow *window)
 {
     struct menu_update data;
     gchar **strings, *title, *expanded;
-    gint index;
+    gsize index;
 
     ENTER("window %p", window);
     index = g_list_index(active_windows, window);
     if (index > n_radio_entries)
     {
-        LEAVE("skip window %d (only %d entries)", index, n_radio_entries);
+        LEAVE("skip window %" G_GSIZE_FORMAT " (only %" G_GSIZE_FORMAT " entries)", index, n_radio_entries);
         return;
     }
 
@@ -1973,7 +1973,7 @@ gnc_main_window_update_menu_item (GncMainWindow *window)
     expanded = g_strjoinv("__", strings);
     if (index < 10)
     {
-        data.label = g_strdup_printf("_%d %s", (index + 1) % 10, expanded);
+        data.label = g_strdup_printf("_%" G_GSIZE_FORMAT " %s", (index + 1) % 10, expanded);
         g_free(expanded);
     }
     else
@@ -1983,7 +1983,7 @@ gnc_main_window_update_menu_item (GncMainWindow *window)
     g_strfreev(strings);
 
     data.visible = TRUE;
-    data.action_name = g_strdup_printf("Window%dAction", index);
+    data.action_name = g_strdup_printf("Window%" G_GSIZE_FORMAT "Action", index);
     g_list_foreach(active_windows,
                    (GFunc)gnc_main_window_update_one_menu_action,
                    &data);
@@ -2008,7 +2008,6 @@ gnc_main_window_update_all_menu_items (void)
 {
     struct menu_update data;
     gchar *label;
-    gint i;
 
     ENTER("");
     /* First update the entries for all existing windows */
@@ -2021,10 +2020,10 @@ gnc_main_window_update_all_menu_items (void)
 
     /* Now hide any entries that aren't being used. */
     data.visible = FALSE;
-    for (i = g_list_length(active_windows); i < n_radio_entries; i++)
+    for (gsize i = g_list_length(active_windows); i < n_radio_entries; i++)
     {
-        data.action_name = g_strdup_printf("Window%dAction", i);
-        label = g_strdup_printf("Window _%d", (i - 1) % 10);
+        data.action_name = g_strdup_printf("Window%" G_GSIZE_FORMAT "Action", i);
+        label = g_strdup_printf("Window _%" G_GSIZE_FORMAT, (i - 1) % 10);
         data.label = gettext(label);
 
         g_list_foreach(active_windows,
@@ -4588,7 +4587,7 @@ gnc_main_window_cmd_window_raise (GtkAction *action,
 
     ENTER("action %p, current %p, window %p", action, current, old_window);
     value = gtk_radio_action_get_current_value(current);
-    new_window = g_list_nth_data(active_windows, value);
+    new_window = static_cast<GncMainWindow*>(g_list_nth_data(active_windows, value));
     gtk_window_present(GTK_WINDOW(new_window));
     /* revert the change in the radio group
      * impossible while handling "changed" (G_SIGNAL_NO_RECURSE) */
diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp
index 8b6137854..155496223 100644
--- a/gnucash/gnome/gnc-plugin-page-report.cpp
+++ b/gnucash/gnome/gnc-plugin-page-report.cpp
@@ -1156,14 +1156,13 @@ gnc_plugin_page_report_constructor(GType this_type, guint n_properties, GObjectC
     GncPluginPageReportClass *our_class;
     GObjectClass *parent_class;
     gint reportId = -42;
-    int i;
 
     our_class = GNC_PLUGIN_PAGE_REPORT_CLASS (
                     g_type_class_peek (GNC_TYPE_PLUGIN_PAGE_REPORT));
     parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (our_class));
     obj = parent_class->constructor(this_type, n_properties, properties);
 
-    for (i = 0; i < n_properties; i++)
+    for (decltype(n_properties) i = 0; i < n_properties; i++)
     {
         GObjectConstructParam prop = properties[i];
         if (strcmp(prop.pspec->name, "report-id") == 0)
@@ -1719,8 +1718,10 @@ gnc_plugin_page_report_export_cb( GtkAction *action, GncPluginPageReport *report
         SCM get_export_error = scm_c_eval_string ("gnc:html-document-export-error");
 
         if (scm_is_false (scm_call_1 (query_result, document)))
-            gnc_error_dialog (parent, _("This report must be upgraded to \
-return a document object with export-string or export-error."));
+            gnc_error_dialog (parent, "%s",
+                              _("This report must be upgraded to return a "
+                                "document object with export-string or "
+                                "export-error."));
         else
         {
             SCM export_string = scm_call_1 (get_export_string, document);
@@ -1743,8 +1744,10 @@ return a document object with export-string or export-error."));
                 g_free (str);
             }
             else
-                gnc_error_dialog (parent, _("This report must be upgraded to \
-return a document object with export-string or export-error."));
+	        gnc_error_dialog (parent, "%s",
+				  _("This report must be upgraded to return a "
+				    "document object with export-string or "
+				    "pexport-error."));
         }
         result = TRUE;
     }
diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp
index e7f113226..332c6bcd7 100644
--- a/libgnucash/app-utils/gnc-option-date.cpp
+++ b/libgnucash/app-utils/gnc-option-date.cpp
@@ -24,6 +24,8 @@
 #include <array>
 #include <gnc-datetime.hpp>
 #include <iostream>
+#include <cassert>
+#include <algorithm>
 
 extern "C"
 {
@@ -551,3 +553,10 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
     set_day_and_time(now, checked_reldate(period).m_type);
     return static_cast<time64>(GncDateTime(now));
 }
+
+std::ostream&
+operator<<(std::ostream& ostr, RelativeDatePeriod per)
+{
+    ostr << gnc_relative_date_display_string(per);
+    return ostr;
+}
diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp
index 954e0a1cc..8d4e65529 100644
--- a/libgnucash/app-utils/gnc-option-date.hpp
+++ b/libgnucash/app-utils/gnc-option-date.hpp
@@ -30,7 +30,7 @@ extern "C"
 }
 
 #include <vector>
-
+#include <iostream>
 /**
  * Reporting periods relative to the current date.
  *
@@ -83,6 +83,6 @@ const char* gnc_relative_date_display_string(RelativeDatePeriod);
 const char* gnc_relative_date_description(RelativeDatePeriod);
 RelativeDatePeriod gnc_relative_date_from_storage_string(const char*);
 time64 gnc_relative_date_to_time64(RelativeDatePeriod);
-
+std::ostream& operator<<(std::ostream&, const RelativeDatePeriod);
 
 #endif //GNC_OPTION_DATE_HPP_
diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 38b1f8e04..5c92c214f 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -25,6 +25,7 @@
 #include "gnc-option-impl.hpp"
 #include <gnc-datetime.hpp>
 #include <guid.hpp>
+#include <cassert>
 
 extern "C"
 {
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 5a9af1093..a39c91c24 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -34,7 +34,7 @@ extern "C"
 #include <iostream>
 #include <variant>
 #include <memory>
-#include "gnc-option-uitype.hpp"
+#include "gnc-option-ui.hpp"
 #include "gnc-option-date.hpp"
 
 class GncOptionUIItem;
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 12aa71d8a..452e9e62a 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1071,6 +1071,7 @@ owner_type_to_ui_type(GncOwnerType type)
         case GNC_OWNER_EMPLOYEE:
             return GncOptionUIType::EMPLOYEE;
     }
+    return GncOptionUIType::INTERNAL;
 }
 
 void
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index cd752e7ce..e5cc55674 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -444,6 +444,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
 %inline %{
     using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
+/* Forward decls */
+    GncOptionDBPtr new_gnc_optiondb();
+    GncOption* gnc_lookup_option(const GncOptionDBPtr& optiondb,
+                                 const char* section, const char* name);
 
     static SCM
     gnc_option_value(const GncOptionDBPtr& optiondb, const char* section,
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 790303b7f..503576ebc 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -1198,7 +1198,7 @@ TEST(GncOptionDate, test_gnc_relative_date_description)
 
 TEST(GncOptionDate, test_gnc_relative_date_from_storage_string)
 {
-    EXPECT_EQ(RelativeDatePeriod::ABSOLUTE, gnc_relative_date_from_storage_string("foo"));
+    //   EXPECT_EQ(RelativeDatePeriod::ABSOLUTE, gnc_relative_date_from_storage_string("foo"));
     EXPECT_EQ(RelativeDatePeriod::ONE_MONTH_AHEAD,  gnc_relative_date_from_storage_string("one-month-ahead"));
     EXPECT_EQ(RelativeDatePeriod::START_CURRENT_QUARTER,  gnc_relative_date_from_storage_string("start-current-quarter"));
     EXPECT_EQ(RelativeDatePeriod::END_ACCOUNTING_PERIOD,  gnc_relative_date_from_storage_string("end-prev-fin-year"));
@@ -1327,7 +1327,7 @@ TEST_F(GncDateOptionList, test_set_and_get_relative)
     m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
     EXPECT_EQ(time1, m_option.get_value<time64>());
     EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, m_option.get_value<RelativeDatePeriod>());
-    auto index(std::find(c_begin_dates.begin(), c_begin_dates.end(),
+    size_t index(std::find(c_begin_dates.begin(), c_begin_dates.end(),
                          RelativeDatePeriod::START_THIS_MONTH) - c_begin_dates.begin());
     EXPECT_EQ(index, m_option.get_value<size_t>());
     // And check that nothing happens when we try to set m_option to an end date
@@ -1335,7 +1335,7 @@ TEST_F(GncDateOptionList, test_set_and_get_relative)
     EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, m_option.get_value<RelativeDatePeriod>());
     m_option.set_value(static_cast<size_t>(5));
     EXPECT_EQ(RelativeDatePeriod::START_CAL_YEAR, m_option.get_value<RelativeDatePeriod>());
-    EXPECT_EQ(5, m_option.get_value<size_t>());
+    EXPECT_EQ(5u, m_option.get_value<size_t>());
 }
 
 TEST_F(GncDateOption, test_stream_out)
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 0acbb633b..357537286 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -53,7 +53,7 @@ TEST_F(GncOptionDBTest, test_register_option)
     GncOption option1{"foo", "bar", "baz", "Phony Option",
                       std::string{"waldo"}};
     m_db->register_option("foo", std::move(option1));
-    EXPECT_EQ(m_db->num_sections(), 1);
+    EXPECT_EQ(1u, m_db->num_sections());
 }
 
 TEST_F(GncOptionDBTest, test_lookup_string_option)
@@ -144,7 +144,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_STOCK});
-    EXPECT_EQ(4, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(4u, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
     EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(3));
 }
 
@@ -156,7 +156,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", accsel,
                                              {ACCT_TYPE_STOCK});
-    EXPECT_EQ(1, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(1u, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
     EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(0));
 }
 
@@ -256,19 +256,19 @@ TEST_F(GncOptionDBTest, test_register_start_date_option)
     m_db->set_option("foo", "bar", RelativeDatePeriod::START_THIS_MONTH);
     EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH,
               m_db->find_option("foo", "bar")->get_value<RelativeDatePeriod>());
-
-    auto index(std::find(begin_dates.begin(), begin_dates.end(),
-                         RelativeDatePeriod::START_THIS_MONTH) - begin_dates.begin());
-    /* If this fails check that the begin_dates vector above matches the one in
+    const RelativeDatePeriod start_this_month {RelativeDatePeriod::START_THIS_MONTH};
+    auto index =
+        std::find(begin_dates.begin(), begin_dates.end(), start_this_month);
+     /* If this fails check that the begin_dates vector above matches the one in
      * gnc-optiondb.cpp.
      */
-    EXPECT_EQ(index,
+    EXPECT_EQ(static_cast<unsigned int>(std::distance(begin_dates.begin(), index)),
               m_db->find_option("foo", "bar")->get_value<size_t>());
     m_db->set_option("foo", "bar", RelativeDatePeriod::END_THIS_MONTH);
     EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH,
               m_db->find_option("foo", "bar")->get_value<RelativeDatePeriod>());
     m_db->set_option("foo", "bar", static_cast<size_t>(5));
-    EXPECT_EQ(5, m_db->find_option("foo", "bar")->get_value<size_t>());
+    EXPECT_EQ(5u, m_db->find_option("foo", "bar")->get_value<size_t>());
 
 }
 
@@ -398,7 +398,7 @@ TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input)
     EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[0]);
     m_db->load_option_scheme(iss);
     EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
 
 }
 
@@ -438,7 +438,7 @@ TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input)
     EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
     EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value<time64>());
     EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
 
 }
 

commit 76b0001cbe6547136f40a1bea8712baf0c7f26c5
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Dec 18 16:02:11 2020 -0800

    Remove option-util.

diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp
index 5c4aba19e..8f4707ac2 100644
--- a/gnucash/gnome-utils/gnc-main-window.cpp
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -4284,7 +4284,7 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
     GncOptionDB *options;
     GNCOptionWin *optionwin;
 
-    options = gnc_option_db_new_for_type (QOF_ID_BOOK);
+    options = gnc_option_db_new();
     qof_book_load_options (book, gnc_option_db_load, options);
     gnc_option_db_clean (options);
 
diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt
index 142976109..573c93db4 100644
--- a/libgnucash/app-utils/CMakeLists.txt
+++ b/libgnucash/app-utils/CMakeLists.txt
@@ -38,7 +38,6 @@ set (app_utils_HEADERS
   gnc-sx-instance-model.h
   gnc-ui-util.h
   gnc-ui-balances.h
-  option-util.h
 )
 
 # Command to generate the swig-app-utils-guile.c wrapper file
@@ -80,7 +79,6 @@ set (app_utils_SOURCES
   gnc-state.c
   gnc-ui-util.c
   gnc-ui-balances.c
-  option-util.c
   )
 
 set_source_files_properties (${app_utils_SOURCES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H})
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 8ad69ddb8..12aa71d8a 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1250,16 +1250,6 @@ gnc_option_db_new(void)
     return new GncOptionDB;
 }
 
-GncOptionDB*
-gnc_option_db_new_for_type(QofIdType type)
-{
-    if (strcmp(type, QOF_ID_BOOK))
-        return nullptr;
-    auto db = new GncOptionDB;
-    gnc_option_db_book_options(db);
-    return db;
-}
-
 void
 gnc_option_db_destroy(GncOptionDB* odb)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index 6c5dfa9b5..885715622 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -54,13 +54,6 @@ extern "C"
  */
 GncOptionDB* gnc_option_db_new(void);
 
-/**
- * Convenence function duplicating an option-util function. We need this temporarily to make gnc-main-window and assistant-hierarchy happy.
- * @param type The QofType
- * @return a new GncOptionDB*. Transfers ownership.
- */
-GncOptionDB* gnc_option_db_new_for_type(QofIdType type);
-
 /**
  * Destruct and release a GncOptionDB.
  * @param odb The GncOptionDB.
diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c
deleted file mode 100644
index ee0e1d64a..000000000
--- a/libgnucash/app-utils/option-util.c
+++ /dev/null
@@ -1,2295 +0,0 @@
-/********************************************************************\
- * option-util.c -- GNOME<->guile option interface                  *
- * Copyright (C) 2000 Dave Peticolas                                *
- * Copyright (C) 2017 Aaron Laws                                    *
- *                                                                  *
- * 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 <config.h>
-
-#include <glib/gi18n.h>
-#include <time.h>
-#include <string.h>
-
-#include "Account.h"
-#include "option-util.h"
-#include "gnc-guile-utils.h"
-#include "qof.h"
-#include "swig-runtime.h"
-#include "guile-mappings.h"
-
-
-/* TODO:
-
-  - for make-date-option, there seems to be only support for getting,
-    not for setting.
-*/
-
-
-/****** Structures *************************************************/
-
-struct gnc_option
-{
-    /* Handle to the scheme-side option */
-    SCM guile_option;
-
-    /* Flag to indicate change by the UI */
-    gboolean changed;
-
-    /* The widget which is holding this option */
-    gpointer widget;
-
-    /* The option db which holds this option */
-    GNCOptionDB *odb;
-};
-
-struct gnc_option_section
-{
-    char * section_name;
-
-    GSList * options;
-};
-
-struct gnc_option_db
-{
-    SCM guile_options;
-
-    GSList *option_sections;
-
-    gboolean options_dirty;
-
-    GNCOptionDBHandle handle;
-
-    GNCOptionGetUIValue get_ui_value;
-    GNCOptionSetUIValue set_ui_value;
-    GNCOptionSetSelectable set_selectable;
-};
-
-typedef struct _Getters Getters;
-struct _Getters
-{
-    SCM section;
-    SCM name;
-    SCM type;
-    SCM sort_tag;
-    SCM documentation;
-    SCM getter;
-    SCM setter;
-    SCM default_getter;
-    SCM value_validator;
-    SCM option_data;
-    SCM index_to_name;
-    SCM index_to_value;
-    SCM value_to_index;
-    SCM number_of_indices;
-    SCM option_widget_changed_cb;
-    SCM date_option_subtype;
-    SCM date_option_show_time;
-    SCM date_option_value_type;
-    SCM date_option_value_absolute;
-    SCM date_option_value_relative;
-    SCM plot_size_option_value_type;
-    SCM plot_size_option_value;
-};
-
-
-/****** Globals ****************************************************/
-
-static Getters getters = {0, 0, 0, 0, 0, 0, 0, 0, 0,
-                          0, 0, 0, 0, 0, 0, 0, 0, 0
-                         };
-
-/* This static indicates the debugging module this .o belongs to.  */
-static QofLogModule log_module = GNC_MOD_GUI;
-
-static GHashTable *option_dbs = NULL;
-static int last_db_handle = 0;
-
-
-/*******************************************************************/
-void
-gnc_option_set_changed (GNCOption *option, gboolean changed)
-{
-    g_return_if_fail (option != NULL);
-    option->changed = changed;
-}
-
-gpointer
-gnc_option_get_widget (GNCOption *option)
-{
-    if (!option) return NULL;
-    return option->widget;
-}
-
-void
-gnc_option_set_widget (GNCOption *option, gpointer widget)
-{
-    g_return_if_fail (option != NULL);
-    option->widget = widget;
-}
-
-SCM
-gnc_option_get_ui_value (GNCOption *option)
-{
-    g_return_val_if_fail (option != NULL, SCM_UNDEFINED);
-    g_return_val_if_fail (option->odb != NULL, SCM_UNDEFINED);
-    g_return_val_if_fail (option->odb->get_ui_value != NULL, SCM_UNDEFINED);
-
-    return option->odb->get_ui_value (option);
-}
-
-void
-gnc_option_set_ui_value (GNCOption *option, gboolean use_default)
-{
-    g_return_if_fail (option != NULL);
-    g_return_if_fail (option->odb != NULL);
-
-    if (!option->odb->set_ui_value)
-        return;
-
-    option->odb->set_ui_value (option, use_default);
-}
-
-void
-gnc_option_set_selectable (GNCOption *option, gboolean selectable)
-{
-    g_return_if_fail (option != NULL);
-    g_return_if_fail (option->odb != NULL);
-    g_return_if_fail (option->odb->set_selectable != NULL);
-
-    option->odb->set_selectable (option, selectable);
-}
-
-/********************************************************************\
- * gnc_option_db_init                                               *
- *   initialize the options structures from the guile side          *
- *                                                                  *
- * Args: odb     - the option database to initialize                *
- * Returns: nothing                                                 *
-\********************************************************************/
-static void
-gnc_option_db_init (GNCOptionDB *odb)
-{
-    SCM func = scm_c_eval_string ("gnc:send-options");
-
-    scm_call_2 (func, scm_from_int (odb->handle), odb->guile_options);
-}
-
-#if 0
-/********************************************************************\
- * gnc_option_db_new                                                *
- *   allocate a new option database and initialize its values       *
- *                                                                  *
- * Args: guile_options - SCM handle to options                      *
- * Returns: a new option database                                   *
-\********************************************************************/
-GNCOptionDB *
-gnc_option_db_new (SCM guile_options)
-{
-    GNCOptionDB *odb;
-    GNCOptionDB *lookup;
-
-    odb = g_new0 (GNCOptionDB, 1);
-
-    odb->guile_options = guile_options;
-    scm_gc_protect_object (guile_options);
-
-    odb->option_sections = NULL;
-    odb->options_dirty = FALSE;
-
-    if (option_dbs == NULL)
-        option_dbs = g_hash_table_new (g_int_hash, g_int_equal);
-
-    do
-    {
-        odb->handle = last_db_handle++;
-        lookup = g_hash_table_lookup (option_dbs, &odb->handle);
-    }
-    while (lookup != NULL);
-
-    g_hash_table_insert (option_dbs, &odb->handle, odb);
-
-    gnc_option_db_init (odb);
-
-    return odb;
-}
-#endif
-/* Create an option DB for a particular data type */
-/* For now, this is global, just like when it was in guile.
-   But, it should be make per-book. */
-static GHashTable *kvp_registry = NULL;
-
-static void
-init_table(void)
-{
-    if (!kvp_registry)
-        kvp_registry = g_hash_table_new (g_str_hash, g_str_equal);
-}
-
-
-/*  create a new options object for the requested type */
-static SCM
-gnc_make_kvp_options (QofIdType id_type)
-{
-    GList *list, *p;
-    SCM gnc_new_options = SCM_UNDEFINED;
-    SCM options = SCM_UNDEFINED;
-
-    init_table();
-    list = g_hash_table_lookup (kvp_registry, id_type);
-    gnc_new_options = scm_c_eval_string ("gnc:new-options");
-    options = scm_call_0 (gnc_new_options);
-
-    for (p = list; p; p = p->next)
-    {
-        SCM generator = p->data;
-        scm_call_1 (generator, options);
-    }
-    return options;
-}
-#if 0
-GNCOptionDB *
-gnc_option_db_new_for_type (QofIdType id_type)
-{
-    SCM options;
-
-    if (!id_type) return NULL;
-    options = gnc_make_kvp_options (id_type);
-    return gnc_option_db_new (options);
-}
-
-void
-gnc_option_db_load (GNCOptionDB* odb, QofBook *book)
-{
-    static SCM kvp_to_scm = SCM_UNDEFINED;
-    SCM scm_book;
-
-    if (!odb || !book) return;
-
-    if (kvp_to_scm == SCM_UNDEFINED)
-    {
-        kvp_to_scm = scm_c_eval_string ("gnc:options-kvp->scm");
-        if (!scm_is_procedure (kvp_to_scm))
-        {
-            PERR ("not a procedure\n");
-            kvp_to_scm = SCM_UNDEFINED;
-            return;
-        }
-    }
-
-    scm_book = SWIG_NewPointerObj (book, SWIG_TypeQuery ("_p_QofBook"), 0);
-
-    scm_call_2 (kvp_to_scm, odb->guile_options, scm_book);
-}
-
-void
-gnc_option_db_save (GNCOptionDB* odb, QofBook *book, gboolean clear_all)
-{
-    static SCM scm_to_kvp = SCM_UNDEFINED;
-    SCM scm_book;
-    SCM scm_clear_all;
-
-    if (!odb || !book) return;
-
-    if (scm_to_kvp == SCM_UNDEFINED)
-    {
-        scm_to_kvp = scm_c_eval_string ("gnc:options-scm->kvp");
-        if (!scm_is_procedure (scm_to_kvp))
-        {
-            PERR ("not a procedure\n");
-            scm_to_kvp = SCM_UNDEFINED;
-            return;
-        }
-    }
-
-    scm_book = SWIG_NewPointerObj (book, SWIG_TypeQuery ("_p_QofBook"), 0);
-    scm_clear_all = scm_from_bool (clear_all);
-
-    scm_call_3 (scm_to_kvp, odb->guile_options, scm_book, scm_clear_all);
-}
-
-/********************************************************************\
- * gnc_option_db_destroy                                            *
- *   unregister the scheme options and free all the memory          *
- *   associated with an option database, including the database     *
- *   itself                                                         *
- *                                                                  *
- * Args: options database to destroy                                *
- * Returns: nothing                                                 *
-\********************************************************************/
-void
-gnc_option_db_destroy (GNCOptionDB *odb)
-{
-    GSList *snode;
-
-    if (odb == NULL)
-        return;
-
-    for (snode = odb->option_sections; snode; snode = snode->next)
-    {
-        GNCOptionSection *section = snode->data;
-        GSList *onode;
-
-        for (onode = section->options; onode; onode = onode->next)
-        {
-            GNCOption *option = onode->data;
-
-            scm_gc_unprotect_object (option->guile_option);
-            g_free (option);
-        }
-
-        /* Free the option list */
-        g_slist_free (section->options);
-        section->options = NULL;
-
-        if (section->section_name != NULL)
-            free (section->section_name);
-        section->section_name = NULL;
-
-        g_free (section);
-    }
-
-    g_slist_free (odb->option_sections);
-
-    odb->option_sections = NULL;
-    odb->options_dirty = FALSE;
-
-    g_hash_table_remove (option_dbs, &odb->handle);
-
-    if (g_hash_table_size (option_dbs) == 0)
-    {
-        g_hash_table_destroy (option_dbs);
-        option_dbs = NULL;
-    }
-
-    scm_gc_unprotect_object (odb->guile_options);
-    odb->guile_options = SCM_UNDEFINED;
-
-    g_free(odb);
-}
-#endif
-void
-gnc_option_db_set_ui_callbacks (GNCOptionDB *odb,
-                                GNCOptionGetUIValue get_ui_value,
-                                GNCOptionSetUIValue set_ui_value,
-                                GNCOptionSetSelectable set_selectable)
-{
-    g_return_if_fail (odb != NULL);
-
-    odb->get_ui_value = get_ui_value;
-    odb->set_ui_value = set_ui_value;
-    odb->set_selectable = set_selectable;
-}
-
-/********************************************************************\
- * gnc_option_db_register_change_callback                           *
- *   register a callback to be called whenever an option changes    *
- *                                                                  *
- * Args: odb       - the option database to register with           *
- *       callback  - the callback function to register              *
- *       user_data - the user data for the callback                 *
- *       section   - the section to get callbacks for.              *
- *                   If NULL, get callbacks for any section changes.*
- *       name      - the option name to get callbacks for.          *
- *                   If NULL, get callbacks for any option in the   *
- *                   section. Only used if section is non-NULL.     *
- * Returns: SCM handle for unregistering                            *
-\********************************************************************/
-SCM
-gnc_option_db_register_change_callback (GNCOptionDB *odb,
-                                        GNCOptionChangeCallback callback,
-                                        gpointer data,
-                                        const char *section,
-                                        const char *name)
-{
-    SCM register_proc;
-    SCM arg;
-    SCM args;
-
-    if (!odb || !callback)
-        return SCM_UNDEFINED;
-
-    /* Get the register procedure */
-    register_proc = scm_c_eval_string ("gnc:options-register-c-callback");
-    if (!scm_is_procedure (register_proc))
-    {
-        PERR("not a procedure\n");
-        return SCM_UNDEFINED;
-    }
-
-    /* Now build the args list for apply */
-    args = SCM_EOL;
-
-    /* first the guile options database */
-    args = scm_cons (odb->guile_options, args);
-
-    /* next the data */
-    arg = SWIG_NewPointerObj (data, SWIG_TypeQuery ("_p_void"), 0);
-    args = scm_cons (arg, args);
-
-    /* next the callback */
-    arg = SWIG_NewPointerObj (
-              callback, SWIG_TypeQuery ("GNCOptionChangeCallback"), 0);
-    args = scm_cons (arg, args);
-
-    /* next the name */
-    if (name == NULL)
-    {
-        arg = SCM_BOOL_F;
-    }
-    else
-    {
-        arg = scm_from_utf8_string (name);
-    }
-    args = scm_cons (arg, args);
-
-    /* next the section */
-    if (section == NULL)
-    {
-        arg = SCM_BOOL_F;
-    }
-    else
-    {
-        arg = scm_from_utf8_string (section);
-    }
-    args = scm_cons (arg, args);
-
-    /* now apply the procedure */
-    return scm_apply (register_proc, args, SCM_EOL);
-}
-
-/********************************************************************\
- * gnc_option_db_unregister_change_callback_id                      *
- *   unregister the change callback associated with the given id    *
- *                                                                  *
- * Args: odb      - the option database to register with            *
- *       callback - the callback function to register               *
- * Returns: nothing                                                 *
-\********************************************************************/
-void
-gnc_option_db_unregister_change_callback_id (GNCOptionDB *odb, SCM callback_id)
-{
-    SCM proc;
-
-    if (callback_id == SCM_UNDEFINED)
-        return;
-
-    proc = scm_c_eval_string ("gnc:options-unregister-callback-id");
-    if (!scm_is_procedure (proc))
-    {
-        PERR("not a procedure\n");
-        return;
-    }
-
-    scm_call_2 (proc, callback_id, odb->guile_options);
-}
-
-void
-gncp_option_invoke_callback (GNCOptionChangeCallback callback, void *data)
-{
-    callback (data);
-}
-
-static void
-gnc_call_option_change_callbacks (GNCOptionDB *odb)
-{
-    SCM proc;
-
-    proc = scm_c_eval_string ("gnc:options-run-callbacks");
-    if (!scm_is_procedure (proc))
-    {
-        PERR("not a procedure\n");
-        return;
-    }
-
-    scm_call_1 (proc, odb->guile_options);
-}
-
-static void
-initialize_getters(void)
-{
-    static gboolean getters_initialized = FALSE;
-
-    if (getters_initialized)
-        return;
-
-    getters.section = scm_c_eval_string ("gnc:option-section");
-    getters.name = scm_c_eval_string ("gnc:option-name");
-    getters.type = scm_c_eval_string ("gnc:option-type");
-    getters.sort_tag = scm_c_eval_string ("gnc:option-sort-tag");
-    getters.documentation =
-        scm_c_eval_string ("gnc:option-documentation");
-    getters.getter = scm_c_eval_string ("gnc:option-getter");
-    getters.setter = scm_c_eval_string ("gnc:option-setter");
-    getters.default_getter =
-        scm_c_eval_string ("gnc:option-default-getter");
-    getters.value_validator =
-        scm_c_eval_string ("gnc:option-value-validator");
-    getters.option_data = scm_c_eval_string ("gnc:option-data");
-    getters.index_to_name = scm_c_eval_string ("gnc:option-index-get-name");
-    getters.number_of_indices = scm_c_eval_string ("gnc:option-number-of-indices");
-    getters.index_to_value = scm_c_eval_string ("gnc:option-index-get-value");
-    getters.value_to_index = scm_c_eval_string ("gnc:option-value-get-index");
-    getters.option_widget_changed_cb =
-        scm_c_eval_string ("gnc:option-widget-changed-proc");
-    getters.date_option_subtype = scm_c_eval_string ("gnc:date-option-get-subtype");
-    getters.date_option_show_time = scm_c_eval_string ("gnc:date-option-show-time?");
-    getters.date_option_value_type = scm_c_eval_string ("gnc:date-option-value-type");
-    getters.date_option_value_absolute =
-        scm_c_eval_string ("gnc:date-option-absolute-time");
-    getters.date_option_value_relative =
-        scm_c_eval_string ("gnc:date-option-relative-time");
-    getters.plot_size_option_value_type = scm_c_eval_string ("gnc:plot-size-option-value-type");
-    getters.plot_size_option_value = scm_c_eval_string ("gnc:plot-size-option-value");
-
-    getters_initialized = TRUE;
-}
-
-/********************************************************************\
- * gnc_option_section                                               *
- *   returns the malloc'ed section name of the option, or NULL      *
- *   if it can't be retrieved.                                      *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: malloc'ed char * or NULL                                *
-\********************************************************************/
-char *
-gnc_option_section (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_string (getters.section, option->guile_option);
-}
-
-/********************************************************************\
- * gnc_option_name                                                  *
- *   returns the malloc'ed name of the option, or NULL              *
- *   if it can't be retrieved.                                      *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: malloc'ed char * or NULL                                *
-\********************************************************************/
-char *
-gnc_option_name (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_string (getters.name, option->guile_option);
-}
-
-/********************************************************************\
- * gnc_option_type                                                  *
- *   returns the malloc'ed type of the option, or NULL              *
- *   if it can't be retrieved.                                      *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: malloc'ed char * or NULL                                *
-\********************************************************************/
-char *
-gnc_option_type (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_symbol_to_string (getters.type,
-                                            option->guile_option);
-}
-
-/********************************************************************\
- * gnc_option_sort_tag                                              *
- *   returns the malloc'ed sort tag of the option, or NULL          *
- *   if it can't be retrieved.                                      *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: malloc'ed char * or NULL                                *
-\********************************************************************/
-char *
-gnc_option_sort_tag (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_string (getters.sort_tag, option->guile_option);
-}
-
-/********************************************************************\
- * gnc_option_documentation                                         *
- *   returns the malloc'ed documentation string of the option, or   *
- *   NULL if it can't be retrieved.                                 *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: malloc'ed char * or NULL                                *
-\********************************************************************/
-char *
-gnc_option_documentation (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_string (getters.documentation,
-                                     option->guile_option);
-}
-
-/********************************************************************\
- * gnc_option_getter                                                *
- *   returns the SCM handle for the option getter function.         *
- *   This value should be tested with scm_procedure_p before use.   *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: SCM handle to function                                  *
-\********************************************************************/
-SCM
-gnc_option_getter (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_procedure (getters.getter,
-                                        option->guile_option);
-}
-
-/********************************************************************\
- * gnc_option_setter                                                *
- *   returns the SCM handle for the option setter function.         *
- *   This value should be tested with scm_procedure_p before use.   *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: SCM handle to function                                  *
-\********************************************************************/
-SCM
-gnc_option_setter (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_procedure (getters.setter,
-                                        option->guile_option);
-}
-
-/********************************************************************\
- * gnc_option_default_getter                                        *
- *   returns the SCM handle for the option default_getter function. *
- *   This value should be tested with scm_procedure_p before use.   *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: SCM handle to function                                  *
-\********************************************************************/
-SCM
-gnc_option_default_getter (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_procedure (getters.default_getter,
-                                        option->guile_option);
-}
-
-/* ******************************************************************\
- * gnc_option_value_validator                                       *
- *   returns the SCM handle for the option value validator function.*
- *   This value should be tested with scm_procedure_p before use.   *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: SCM handle to function                                  *
-\********************************************************************/
-static SCM
-gnc_option_value_validator(GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_procedure (getters.value_validator,
-                                        option->guile_option);
-}
-
-
-/* ******************************************************************\
- * gnc_option_widget_changed_proc_getter                            *
- *   returns the SCM handle for the function to be called if the    *
- *   GUI widget representing the option is changed.                 *
- *   This value should be tested with scm_procedure_p before use.   *
- *   If no such function exists, returns SCM_UNDEFINED.             *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: SCM handle to function                                  *
- *          If no such function exists, returns SCM_UNDEFINED.      *
-\********************************************************************/
-static SCM
-gnc_option_widget_changed_proc_getter(GNCOption *option)
-{
-    SCM cb;
-
-    initialize_getters ();
-
-    if (scm_is_procedure (getters.option_widget_changed_cb))
-    {
-        /* call the callback function getter to get the actual callback function */
-        cb = scm_call_1 (getters.option_widget_changed_cb, option->guile_option);
-
-        if (scm_is_procedure (cb))  /* a callback exists */
-        {
-            return (cb);
-        }
-        /* else no callback exists -  this is a legal situation */
-    }
-    else  /* getters not set up correctly? */
-    {
-        PERR("getters.option_widget_changed_cb is not a valid procedure\n");
-    }
-
-    return( SCM_UNDEFINED );
-}
-
-/**********************************************************************\
- * gnc_option_call_option_widget_changed_proc                         *
- *   If there is an option_widget_changed_cb for this option, call    *
- *   it with the SCM value of the option that is passed in.  If       *
- *   there is no such callback function or value, do nothing.         *
- *                                                                    *
- * Args: option        - the GNCOption                                *
- *       reset_changed - whether to reset the changed flag afterwards *
- * Returns: void                                                      *
-\**********************************************************************/
-void
-gnc_option_call_option_widget_changed_proc (GNCOption *option,
-                                            gboolean reset_changed)
-{
-    SCM cb, value;
-
-    cb = gnc_option_widget_changed_proc_getter (option);
-
-    if (cb != SCM_UNDEFINED)
-    {
-        value = gnc_option_get_ui_value (option);
-
-        if (value != SCM_UNDEFINED)
-        {
-            scm_call_1 (cb, value);
-        }
-    }
-    if (reset_changed)
-        option->changed = FALSE;
-}
-
-/********************************************************************\
- * gnc_option_num_permissible_values                                *
- *   returns the number of permissible values in the option, or     *
- *   -1 if there are no values available.                           *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: number of permissible options or -1                     *
-\********************************************************************/
-int
-gnc_option_num_permissible_values (GNCOption *option)
-{
-    SCM value;
-
-    initialize_getters ();
-
-    value = scm_call_1 (getters.number_of_indices, option->guile_option);
-
-    if (scm_is_exact (value))
-    {
-        return scm_to_int (value);
-    }
-    else
-    {
-        return -1;
-    }
-}
-
-/********************************************************************\
- * gnc_option_permissible_value_index                               *
- *   returns the index of the permissible value matching the        *
- *   provided value, or -1 if it couldn't be found                  *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- *       value  - the SCM handle of the value                       *
- * Returns: index of permissible value, or -1                       *
-\********************************************************************/
-int
-gnc_option_permissible_value_index (GNCOption *option, SCM search_value)
-{
-    SCM value;
-    value = scm_call_2 (getters.value_to_index, option->guile_option, search_value);
-    if (value == SCM_BOOL_F)
-    {
-        return -1;
-    }
-    else
-    {
-        return scm_to_int (value);
-    }
-}
-
-/********************************************************************\
- * gnc_option_permissible_value                                     *
- *   returns the SCM handle to the indexth permissible value in the *
- *   option, or SCM_UNDEFINED if the index was out of range or      *
- *   there was some other problem.                                  *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- *       index  - the index of the permissible value                *
- * Returns: SCM handle to option value or SCM_UNDEFINED             *
-\********************************************************************/
-SCM
-gnc_option_permissible_value (GNCOption *option, int index)
-{
-    SCM value;
-
-    if (index < 0)
-        return SCM_UNDEFINED;
-
-    initialize_getters ();
-
-    value = scm_call_2 (getters.index_to_value, option->guile_option,
-                        scm_from_int (index));
-
-    return value;
-}
-
-/********************************************************************\
- * gnc_option_permissible_value_name                                *
- *   returns the malloc'd name of the indexth permissible value in  *
- *   the option, or NULL if the index was out of range or there are *
- *   no values available.                                           *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- *       index  - the index of the permissible value                *
- * Returns: malloc'd name of permissible value or NULL              *
-\********************************************************************/
-char *
-gnc_option_permissible_value_name (GNCOption *option, int index)
-{
-    SCM name;
-
-    if (index < 0)
-        return NULL;
-
-    initialize_getters ();
-
-    name = scm_call_2 (getters.index_to_name, option->guile_option,
-                       scm_from_int (index));
-    if (name == SCM_UNDEFINED)
-        return NULL;
-    if (!scm_is_string (name))
-        return NULL;
-
-    return gnc_scm_to_utf8_string (name);
-}
-
-/********************************************************************\
- * gnc_option_show_time                                             *
- *   returns true if the gui should display the time as well as     *
- *   the date for this option. Only use this for date options.      *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: true if time should be shown                            *
-\********************************************************************/
-gboolean
-gnc_option_show_time (GNCOption *option)
-{
-    SCM value;
-
-    initialize_getters ();
-
-    value = scm_call_1 (getters.date_option_show_time, option->guile_option);
-
-    return scm_is_true (value);
-}
-
-/********************************************************************\
- * gnc_option_get_option_data                                       *
- *   returns the option data of this option                         *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: the option data                                         *
-\********************************************************************/
-SCM
-gnc_option_get_option_data (GNCOption *option)
-{
-    initialize_getters ();
-
-    return scm_call_1 (getters.option_data, option->guile_option);
-}
-
-/********************************************************************\
- * gnc_option_multiple_selection                                    *
- *   returns true if the gui should allow multiple selection of     *
- *   accounts. Only use this for account options.                   *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: true if multiple selection allowed                      *
-\********************************************************************/
-gboolean
-gnc_option_multiple_selection (GNCOption *option)
-{
-    SCM pair;
-
-    initialize_getters ();
-
-    pair = scm_call_1 (getters.option_data, option->guile_option);
-
-    return !scm_is_true (scm_not (SCM_CAR(pair)));
-}
-
-/********************************************************************\
- * gnc_option_get_account_type_list                                 *
- *   returns the list of account_types in the option (or NULL if    *
- *   no special list is provided).  Only use this for account       *
- *   options.                                                       *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: GList of account types (must be freed by caller)        *
-\********************************************************************/
-GList *
-gnc_option_get_account_type_list (GNCOption *option)
-{
-    SCM pair;
-    SCM lst;
-    GList *type_list = NULL;
-
-    initialize_getters ();
-
-    pair = scm_call_1 (getters.option_data, option->guile_option);
-    lst = SCM_CDR(pair);
-
-    while (!scm_is_null (lst))
-    {
-        GNCAccountType type;
-        SCM item;
-
-        /* Compute this item and the rest of the list */
-        item = SCM_CAR(lst);
-        lst = SCM_CDR(lst);
-
-        if (scm_is_false (scm_integer_p (item)))
-        {
-            PERR("Invalid type");
-        }
-        else
-        {
-            type = scm_to_long (item);
-            type_list = g_list_prepend (type_list, GINT_TO_POINTER (type));
-        }
-    }
-
-    return g_list_reverse (type_list);
-}
-
-/********************************************************************\
- * gnc_option_get_range_info                                        *
- *   returns the range info for a number range option in the pointer*
- *   arguments. NULL arguments are ignored. Use only for number     *
- *   range options.                                                 *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: true if everything went ok :)                           *
-\********************************************************************/
-gboolean gnc_option_get_range_info (GNCOption *option,
-                                    double *lower_bound,
-                                    double *upper_bound,
-                                    int    *num_decimals,
-                                    double *step_size)
-{
-    SCM list;
-    SCM value;
-
-    initialize_getters ();
-
-    list = scm_call_1 (getters.option_data, option->guile_option);
-
-    if (!scm_is_list (list) || scm_is_null (list))
-        return FALSE;
-
-    /* lower bound */
-    value = SCM_CAR(list);
-    list = SCM_CDR(list);
-
-    if (!scm_is_number (value))
-        return FALSE;
-
-    if (lower_bound != NULL)
-        *lower_bound = scm_to_double (value);
-
-    if (!scm_is_list (list) || scm_is_null (list))
-        return FALSE;
-
-    /* upper bound */
-    value = SCM_CAR(list);
-    list = SCM_CDR(list);
-
-    if (!scm_is_number (value))
-        return FALSE;
-
-    if (upper_bound != NULL)
-        *upper_bound = scm_to_double (value);
-
-    if (!scm_is_list (list) || scm_is_null (list))
-        return FALSE;
-
-    /* number of decimals */
-    value = SCM_CAR(list);
-    list = SCM_CDR(list);
-
-    if (!scm_is_number (value))
-        return FALSE;
-
-    /* Guile-1.6 returns this as a double, so let's use that in all cases.
-     * This is still safe for earlier guiles, too -- tested with 1.3.4.
-     */
-    if (num_decimals != NULL)
-    {
-        double decimals = scm_to_double (value);
-        *num_decimals = (int)decimals;
-    }
-
-    if (!scm_is_list (list) || scm_is_null (list))
-        return FALSE;
-
-    /* step size */
-    value = SCM_CAR(list);
-
-    if (!scm_is_number (value))
-        return FALSE;
-
-    if (step_size != NULL)
-        *step_size = scm_to_double (value);
-
-    return TRUE;
-}
-
-/********************************************************************\
- * gnc_option_color_range                                           *
- *   returns the color range for rgba values.                       *
- *   Only use this for color options.                               *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: color range for the option                              *
-\********************************************************************/
-gdouble
-gnc_option_color_range (GNCOption *option)
-{
-    SCM list;
-    SCM value;
-
-    initialize_getters ();
-
-    list = scm_call_1 (getters.option_data, option->guile_option);
-    if (!scm_is_list (list) || scm_is_null (list))
-        return 0.0;
-
-    value = SCM_CAR(list);
-    if (!scm_is_number (value))
-        return 0.0;
-
-    return scm_to_double (value);
-}
-
-/********************************************************************\
- * gnc_option_use_alpha                                             *
- *   returns true if the color option should use alpha transparency *
- *   Only use this for color options.                               *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: true if alpha transparency should be used               *
-\********************************************************************/
-gdouble
-gnc_option_use_alpha (GNCOption *option)
-{
-    SCM list;
-    SCM value;
-
-    initialize_getters ();
-
-    list = scm_call_1 (getters.option_data, option->guile_option);
-    if (!scm_is_list (list) || scm_is_null (list))
-        return FALSE;
-
-    list = SCM_CDR(list);
-    if (!scm_is_list (list) || scm_is_null (list))
-        return FALSE;
-
-    value = SCM_CAR(list);
-    if (!scm_is_bool (value))
-        return FALSE;
-
-    return scm_is_true (value);
-}
-
-/********************************************************************\
- * gnc_option_get_color_info                                        *
- *   gets the color information from a color option. rgba values    *
- *   returned are between 0.0 and 1.0.                              *
- *                                                                  *
- * Args: option      - option to get info from                      *
- *       use_default - use the default or current value             *
- *       red         - where to store the red value                 *
- *       blue        - where to store the blue value                *
- *       green       - where to store the green value               *
- *       alpha       - where to store the alpha value               *
- * Return: true if everything went ok                               *
-\********************************************************************/
-gboolean
-gnc_option_get_color_info (GNCOption *option,
-                           gboolean use_default,
-                           gdouble *red,
-                           gdouble *green,
-                           gdouble *blue,
-                           gdouble *alpha)
-{
-    gdouble scale;
-    gdouble rgba;
-    SCM getter;
-    SCM value;
-
-    if (option == NULL)
-        return FALSE;
-
-    if (use_default)
-        getter = gnc_option_default_getter (option);
-    else
-        getter = gnc_option_getter (option);
-    if (getter == SCM_UNDEFINED)
-        return FALSE;
-
-    value = scm_call_0 (getter);
-    if (!scm_is_list (value) || scm_is_null (value) || !scm_is_number (SCM_CAR(value)))
-        return FALSE;
-
-    scale = gnc_option_color_range (option);
-    if (scale <= 0.0)
-        return FALSE;
-
-    scale = 1.0 / scale;
-
-    rgba = scm_to_double (SCM_CAR(value));
-    if (red != NULL)
-        *red = MIN(1.0, rgba * scale);
-
-    value = SCM_CDR(value);
-    if (!scm_is_list (value) || scm_is_null (value) || !scm_is_number (SCM_CAR(value)))
-        return FALSE;
-
-    rgba = scm_to_double (SCM_CAR(value));
-    if (green != NULL)
-        *green = MIN(1.0, rgba * scale);
-
-    value = SCM_CDR(value);
-    if (!scm_is_list (value) || scm_is_null (value) || !scm_is_number (SCM_CAR(value)))
-        return FALSE;
-
-    rgba = scm_to_double (SCM_CAR(value));
-    if (blue != NULL)
-        *blue = MIN(1.0, rgba * scale);
-
-    value = SCM_CDR(value);
-    if (!scm_is_list (value) || scm_is_null (value) || !scm_is_number (SCM_CAR(value)))
-        return FALSE;
-
-    rgba = scm_to_double (SCM_CAR(value));
-    if (alpha != NULL)
-        *alpha = MIN(1.0, rgba * scale);
-
-    return TRUE;
-}
-
-/********************************************************************\
- * gnc_option_set_default                                           *
- *   set the option to its default value                            *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: nothing                                                 *
-\********************************************************************/
-void
-gnc_option_set_default (GNCOption *option)
-{
-    SCM default_getter;
-    SCM setter;
-    SCM value;
-
-    if (option == NULL)
-        return;
-
-    default_getter = gnc_option_default_getter (option);
-    if (default_getter == SCM_UNDEFINED)
-        return;
-
-    value = scm_call_0 (default_getter);
-
-    setter = gnc_option_setter (option);
-    if (setter == SCM_UNDEFINED)
-        return;
-
-    scm_call_1 (setter, value);
-}
-
-static gint
-compare_sections (gconstpointer a, gconstpointer b)
-{
-    const GNCOptionSection *sa = a;
-    const GNCOptionSection *sb = b;
-
-    return g_strcmp0 (sa->section_name, sb->section_name);
-}
-
-static gint
-compare_option_tags (gconstpointer a, gconstpointer b)
-{
-    GNCOption *oa = (GNCOption *) a;
-    GNCOption *ob = (GNCOption *) b;
-    char *tag_a = gnc_option_sort_tag (oa);
-    char *tag_b = gnc_option_sort_tag (ob);
-    gint result;
-
-    result = g_strcmp0 (tag_a, tag_b);
-
-    if (tag_a != NULL)
-        free (tag_a);
-
-    if (tag_b != NULL)
-        free (tag_b);
-
-    return result;
-}
-#if 0
-/********************************************************************\
- * gnc_option_db_clean                                              *
- *   resets the dirty flag of the option database                   *
- *                                                                  *
-\********************************************************************/
-void
-gnc_option_db_clean (GNCOptionDB *odb)
-{
-    g_return_if_fail (odb);
-
-    odb->options_dirty = FALSE;
-}
-#endif
-
-/********************************************************************\
- * _gnc_option_db_register_option                                   *
- *   registers an option with an option database. Intended to be    *
- *   called from guile.                                             *
- *                                                                  *
- * Args: odb    - the option database                               *
- *       option - the guile option                                  *
- * Returns: nothing                                                 *
-\********************************************************************/
-void
-gnc_option_db_register_option (GNCOptionDBHandle handle, SCM guile_option)
-{
-    GNCOptionDB *odb;
-    GNCOption *option;
-    GNCOptionSection *section;
-
-    odb = g_hash_table_lookup (option_dbs, &handle);
-
-    g_return_if_fail (odb != NULL);
-
-    odb->options_dirty = TRUE;
-
-    /* Make the option structure */
-    option = g_new0 (GNCOption, 1);
-    option->guile_option = guile_option;
-    option->changed = FALSE;
-    option->widget = NULL;
-    option->odb = odb;
-
-    /* Prevent guile from garbage collecting the option */
-    scm_gc_protect_object (guile_option);
-
-    /* Make the section structure */
-    section = g_new0 (GNCOptionSection, 1);
-    section->section_name = gnc_option_section (option);
-    section->options = NULL;
-
-    /* See if the section is already there */
-    {
-        GSList *old;
-
-        old = g_slist_find_custom (odb->option_sections, section, compare_sections);
-
-        if (old != NULL)
-        {
-            if (section->section_name != NULL)
-                free (section->section_name);
-            g_free (section);
-            section = old->data;
-        }
-        else
-            odb->option_sections = g_slist_insert_sorted (odb->option_sections,
-                                                          section,
-                                                          compare_sections);
-    }
-
-    section->options = g_slist_insert_sorted (section->options, option,
-                                              compare_option_tags);
-}
-
-/********************************************************************\
- * gnc_option_db_num_sections                                       *
- *   returns the number of option sections registered so far in the *
- *   database                                                       *
- *                                                                  *
- * Args: odb - the database to count sections for                   *
- * Returns: number of option sections                               *
-\********************************************************************/
-guint
-gnc_option_db_num_sections (GNCOptionDB *odb)
-{
-    return g_slist_length (odb->option_sections);
-}
-
-/********************************************************************\
- * gnc_option_db_get_section                                        *
- *   returns the ith option section in the database, or NULL        *
- *                                                                  *
- * Args: odb - the option database                                  *
- *       i   - index of section                                     *
- * Returns: ith option sectioin                                     *
-\********************************************************************/
-GNCOptionSection *
-gnc_option_db_get_section (GNCOptionDB *odb, gint i)
-{
-    return g_slist_nth_data (odb->option_sections, i);
-}
-
-/********************************************************************\
- * gnc_option_section_name                                          *
- *   returns the name of the options section                        *
- *                                                                  *
- * Args: section - section to get name of                           *
- * Returns: name of option section                                  *
-\********************************************************************/
-const char *
-gnc_option_section_name (GNCOptionSection *section)
-{
-    return section->section_name;
-}
-
-/********************************************************************\
- * gnc_option_section_num_options                                   *
- *   returns the number of options in a given section               *
- *                                                                  *
- * Args: section - section to count options for                     *
- * Returns: number of options in section                            *
-\********************************************************************/
-guint
-gnc_option_section_num_options (GNCOptionSection *section)
-{
-    return g_slist_length (section->options);
-}
-
-/********************************************************************\
- * gnc_get_option_section_option                                    *
- *   returns the ith option in a given section                      *
- *                                                                  *
- * Args: section - section to retrieve option for                   *
- *       i       - index of option                                  *
- * Returns: ith option in section                                   *
-\********************************************************************/
-GNCOption *
-gnc_get_option_section_option (GNCOptionSection *section, int i)
-{
-    return g_slist_nth_data (section->options, i);
-}
-
-/********************************************************************\
- * gnc_option_db_get_option_by_name                                 *
- *   returns an option given section name and name                  *
- *                                                                  *
- * Args: odb          - option database to search in                *
- *       section_name - name of section to search for               *
- *       name         - name to search for                          *
- * Returns: given option, or NULL if none                           *
-\********************************************************************/
-GNCOption *
-gnc_option_db_get_option_by_name (GNCOptionDB *odb,
-                                  const char *section_name,
-                                  const char *name)
-{
-    GSList *section_node;
-    GSList *option_node;
-    GNCOptionSection section_key;
-    GNCOptionSection *section;
-    GNCOption *option;
-    gint result;
-    char *node_name;
-
-    if (odb == NULL)
-        return NULL;
-
-    section_key.section_name = (char *) section_name;
-
-    section_node = g_slist_find_custom (odb->option_sections, &section_key,
-                                        compare_sections);
-
-    if (section_node == NULL)
-        return NULL;
-
-    section = section_node->data;
-    option_node = section->options;
-
-    while (option_node != NULL)
-    {
-        option = option_node->data;
-
-        node_name = gnc_option_name (option);
-        result = g_strcmp0 (name, node_name);
-        free (node_name);
-
-        if (result == 0)
-            return option;
-
-        option_node = option_node->next;
-    }
-    return NULL;
-}
-
-static SCM
-gnc_option_valid_value (GNCOption *option, SCM value)
-{
-    SCM validator;
-    SCM result, ok;
-
-    validator = gnc_option_value_validator (option);
-
-    result = scm_call_1 (validator, value);
-    if (!scm_is_list (result) || scm_is_null (result))
-        return SCM_UNDEFINED;
-
-    ok = SCM_CAR(result);
-    if (!scm_is_bool (ok))
-        return SCM_UNDEFINED;
-
-    if (!scm_is_true (ok))
-        return SCM_UNDEFINED;
-
-    result = SCM_CDR(result);
-    if (!scm_is_list (result) || scm_is_null (result))
-        return SCM_UNDEFINED;
-
-    return SCM_CAR(result);
-}
-
-static char*
-gnc_commit_option (GNCOption *option)
-{
-    SCM validator, setter, value;
-    SCM result, ok;
-    char* retval = NULL;
-
-    /* Validate the ui's value */
-    value = gnc_option_get_ui_value (option);
-    if (value == SCM_UNDEFINED)
-        return NULL;
-
-    validator = gnc_option_value_validator (option);
-
-    result = scm_call_1(validator, value);
-    if (!scm_is_list (result) || scm_is_null (result))
-    {
-        PERR("bad validation result\n");
-        return NULL;
-    }
-
-    /* First element determines validity */
-    ok = SCM_CAR(result);
-    if (!scm_is_bool (ok))
-    {
-        PERR("bad validation result\n");
-        return NULL;
-    }
-
-    if (scm_is_true (ok))
-    {
-        /* Second element is value to use */
-        value = SCM_CADR(result);
-        setter = gnc_option_setter (option);
-
-        scm_call_1 (setter, value);
-
-        gnc_option_set_ui_value (option, FALSE);
-    }
-    else
-    {
-        SCM oops;
-        char *section, *name;
-        const char *message = NULL;
-        const char *format = _("There is a problem with option %s:%s.\n%s");
-        const char *bad_value = _("Invalid option value");
-
-        name = gnc_option_name (option);
-        section = gnc_option_section (option);
-
-        /* Second element is error message */
-        oops = SCM_CADR(result);
-        if (!scm_is_string (oops))
-        {
-            PERR("bad validation result\n");
-            retval = g_strdup_printf (format,
-                                      section ? section : "(null)",
-                                      name ? name : "(null)",
-                                      bad_value);
-        }
-        else
-        {
-            message = gnc_scm_to_utf8_string (oops);
-            retval = g_strdup_printf (format,
-                                      section ? section : "(null)",
-                                      name ? name : "(null)",
-                                      message ? message : "(null)");
-        }
-        if (name != NULL)
-            free (name);
-        if (section != NULL)
-            free (section);
-        g_free ((gpointer *) message);
-    }
-    return retval;
-}
-
-/********************************************************************\
- * gnc_option_db_get_changed                                        *
- *   returns a boolean value, TRUE if any option has changed,       *
- *   FALSE is none of the options have changed                      *
- *                                                                  *
- * Args: odb - option database to check                             *
- * Return: boolean                                                  *
-\********************************************************************/
-gboolean
-gnc_option_db_get_changed (GNCOptionDB *odb)
-{
-    GSList *section_node;
-    GSList *option_node;
-    GNCOptionSection *section;
-    GNCOption *option;
-
-    g_return_val_if_fail (odb, FALSE);
-
-    for (section_node = odb->option_sections; section_node;
-            section_node = section_node->next)
-    {
-        section = section_node->data;
-
-        for (option_node = section->options; option_node;
-                option_node = option_node->next)
-        {
-            option = option_node->data;
-
-            if (option->changed)
-                return TRUE;
-        }
-    }
-    return FALSE;
-}
-
-#if 0
-/********************************************************************\
- * gnc_option_db_commit                                             *
- *   commits the options which have changed, and which are valid    *
- *   for those which are not valid, error dialogs are shown.        *
- *                                                                  *
- * Args: odb - option database to commit                            *
- * Return: nothing                                                  *
-\********************************************************************/
-GList*
-gnc_option_db_commit (GNCOptionDB *odb)
-{
-    GSList *section_node;
-    GSList *option_node;
-    GNCOptionSection *section;
-    GNCOption *option;
-    gboolean changed_something = FALSE;
-    GList *commit_errors = NULL;
-
-    g_return_val_if_fail (odb, NULL);
-
-    section_node = odb->option_sections;
-    while (section_node != NULL)
-    {
-        section = section_node->data;
-
-        option_node = section->options;
-        while (option_node != NULL)
-        {
-            option = option_node->data;
-
-            if (option->changed)
-            {
-                char *result = NULL;
-                result = gnc_commit_option (option_node->data);
-                if (result)
-                    commit_errors = g_list_append (commit_errors, result);
-                changed_something = TRUE;
-                option->changed = FALSE;
-            }
-            option_node = option_node->next;
-        }
-        section_node = section_node->next;
-    }
-    if (changed_something)
-        gnc_call_option_change_callbacks (odb);
-
-    return commit_errors;
-}
-#endif
-
-/********************************************************************\
- * gnc_option_db_section_reset_widgets                              *
- *   reset all option widgets in one section to their default.      *
- *   values                                                         *
- *                                                                  *
- * Args: odb - option database to reset                             *
- * Return: nothing                                                  *
-\********************************************************************/
-void
-gnc_option_db_section_reset_widgets (GNCOptionSection *section)
-{
-    GSList *option_node;
-    GNCOption *option;
-
-    g_return_if_fail (section);
-
-    /* Don't reset "invisible" options.
-     * If the section name begins "__" we should not reset
-     */
-    if (section->section_name == NULL ||
-            strncmp (section->section_name, "__", 2) == 0)
-        return;
-
-    for (option_node = section->options;
-            option_node != NULL;
-            option_node = option_node->next)
-    {
-        option = option_node->data;
-        gnc_option_set_ui_value (option, TRUE);
-    }
-}
-
-/********************************************************************\
- * gnc_option_db_get_default_section                                *
- *   returns the malloc'd section name of the default section,      *
- *   or NULL if there is none.                                      *
- *                                                                  *
- * Args: odb - option database to get default page for              *
- * Return: g_malloc'd default section name                          *
-\********************************************************************/
-char *
-gnc_option_db_get_default_section (GNCOptionDB *odb)
-{
-    SCM getter;
-    SCM value;
-
-    if (odb == NULL)
-        return NULL;
-
-    getter = scm_c_eval_string ("gnc:options-get-default-section");
-    if (!scm_is_procedure (getter))
-        return NULL;
-
-    value = scm_call_1 (getter, odb->guile_options);
-    if (!scm_is_string (value))
-        return NULL;
-
-    return gnc_scm_to_utf8_string (value);
-}
-
-/********************************************************************\
- * gnc_option_db_lookup_option                                      *
- *   looks up an option. If present, returns its SCM value,         *
- *   otherwise returns the default.                                 *
- *                                                                  *
- * Args: odb     - option database to search in                     *
- *       section - section name of option                           *
- *       name    - name of option                                   *
- *       default - default value if not found                       *
- * Return: option value                                             *
-\********************************************************************/
-SCM
-gnc_option_db_lookup_option (GNCOptionDB *odb,
-                             const char *section,
-                             const char *name,
-                             SCM default_value)
-{
-    GNCOption *option;
-    SCM getter;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-
-    if (option == NULL)
-        return default_value;
-
-    getter = gnc_option_getter (option);
-    if (getter == SCM_UNDEFINED)
-        return default_value;
-
-    return scm_call_0 (getter);
-}
-
-/********************************************************************\
- * gnc_option_db_lookup_boolean_option                              *
- *   looks up a boolean option. If present, returns its value,      *
- *   otherwise returns the default.                                 *
- *                                                                  *
- * Args: odb     - option database to search in                     *
- *       section - section name of option                           *
- *       name    - name of option                                   *
- *       default - default value if not found                       *
- * Return: gboolean option value                                    *
-\********************************************************************/
-gboolean
-gnc_option_db_lookup_boolean_option (GNCOptionDB *odb,
-                                     const char *section,
-                                     const char *name,
-                                     gboolean default_value)
-{
-    GNCOption *option;
-    SCM getter;
-    SCM value;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-
-    if (option == NULL)
-        return default_value;
-
-    getter = gnc_option_getter (option);
-    if (getter == SCM_UNDEFINED)
-        return default_value;
-
-    value = scm_call_0 (getter);
-
-    if (scm_is_bool (value))
-        return scm_is_true (value);
-    else
-        return default_value;
-}
-
-/********************************************************************\
- * gnc_option_db_lookup_string_option                               *
- *   looks up a string option. If present, returns its malloc'ed    *
- *   value, otherwise returns the strdup'ed default, or NULL if     *
- *   default was NULL.                                              *
- *                                                                  *
- * Args: odb     - option database to search in                     *
- *       section - section name of option                           *
- *       name    - name of option                                   *
- *       default - default value if not found                       *
- * Return: char * option value                                      *
-\********************************************************************/
-char *
-gnc_option_db_lookup_string_option (GNCOptionDB *odb,
-                                    const char *section,
-                                    const char *name,
-                                    const char *default_value)
-{
-    GNCOption *option;
-    SCM getter;
-    SCM value;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-
-    if (option != NULL)
-    {
-        getter = gnc_option_getter (option);
-        if (getter != SCM_UNDEFINED)
-        {
-            value = scm_call_0 (getter);
-            if (scm_is_string (value))
-                return gnc_scm_to_utf8_string (value);
-        }
-    }
-
-    if (default_value == NULL)
-        return NULL;
-
-    return strdup (default_value);
-}
-
-/********************************************************************\
- * gnc_option_db_lookup_number_option                               *
- *   looks up a number option. If present, returns its value        *
- *   as a gdouble, otherwise returns the default_value.             *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       default   - default value if not found                     *
- * Return: gdouble representation of value                          *
-\********************************************************************/
-gdouble
-gnc_option_db_lookup_number_option (GNCOptionDB *odb,
-                                    const char *section,
-                                    const char *name,
-                                    gdouble default_value)
-{
-    GNCOption *option;
-    SCM getter;
-    SCM value;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-
-    if (option != NULL)
-    {
-        getter = gnc_option_getter (option);
-        if (getter != SCM_UNDEFINED)
-        {
-            value = scm_call_0 (getter);
-            if (scm_is_number (value))
-                return scm_to_double (value);
-        }
-    }
-    return default_value;
-}
-
-/********************************************************************\
- * gnc_option_db_set_option                                         *
- *   sets the option to the given value. If successful              *
- *   returns TRUE, otherwise FALSE.                                 *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       value     - value to set to                                *
- * Return: success indicator                                        *
-\********************************************************************/
-gboolean
-gnc_option_db_set_option (GNCOptionDB *odb,
-                          const char *section,
-                          const char *name,
-                          SCM value)
-{
-    GNCOption *option;
-    SCM setter;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-    if (option == NULL)
-        return FALSE;
-
-    value = gnc_option_valid_value (option, value);
-    if (value == SCM_UNDEFINED)
-        return FALSE;
-
-    setter = gnc_option_setter (option);
-    if (setter == SCM_UNDEFINED)
-        return FALSE;
-
-    scm_call_1 (setter, value);
-
-    return TRUE;
-}
-
-/********************************************************************\
- * gnc_option_db_set_number_option                                  *
- *   sets the number option to the given value. If successful       *
- *   returns TRUE, otherwise FALSE.                                 *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       value     - value to set to                                *
- * Return: success indicator                                        *
-\********************************************************************/
-gboolean
-gnc_option_db_set_number_option (GNCOptionDB *odb,
-                                 const char *section,
-                                 const char *name,
-                                 gdouble value)
-{
-    GNCOption *option;
-    SCM scm_value;
-    SCM setter;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-    if (option == NULL)
-        return FALSE;
-
-    scm_value = scm_from_double (value);
-
-    scm_value = gnc_option_valid_value (option, scm_value);
-    if (scm_value == SCM_UNDEFINED)
-        return FALSE;
-
-    setter = gnc_option_setter (option);
-    if (setter == SCM_UNDEFINED)
-        return FALSE;
-
-    scm_call_1 (setter, scm_value);
-
-    return TRUE;
-}
-
-/********************************************************************\
- * gnc_option_db_set_boolean_option                                 *
- *   sets the boolean option to the given value. If successful      *
- *   returns TRUE, otherwise FALSE.                                 *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       value     - value to set to                                *
- * Return: success indicator                                        *
-\********************************************************************/
-gboolean
-gnc_option_db_set_boolean_option (GNCOptionDB *odb,
-                                  const char *section,
-                                  const char *name,
-                                  gboolean value)
-{
-    GNCOption *option;
-    SCM scm_value;
-    SCM setter;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-    if (option == NULL)
-        return FALSE;
-
-    scm_value = SCM_BOOL(value);
-
-    scm_value = gnc_option_valid_value (option, scm_value);
-    if (scm_value == SCM_UNDEFINED)
-        return FALSE;
-
-    setter = gnc_option_setter (option);
-    if (setter == SCM_UNDEFINED)
-        return FALSE;
-
-    scm_call_1 (setter, scm_value);
-
-    return TRUE;
-}
-
-/********************************************************************\
- * gnc_option_db_set_string_option                                  *
- *   sets the string option to the given value. If successful       *
- *   returns TRUE, otherwise FALSE.                                 *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       value     - value to set to                                *
- * Return: success indicator                                        *
-\********************************************************************/
-gboolean
-gnc_option_db_set_string_option (GNCOptionDB *odb,
-                                 const char *section,
-                                 const char *name,
-                                 const char *value)
-{
-    GNCOption *option;
-    SCM scm_value;
-    SCM setter;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-    if (option == NULL)
-        return FALSE;
-
-    if (value)
-        scm_value = scm_from_utf8_string (value);
-    else
-        scm_value = SCM_BOOL_F;
-
-    scm_value = gnc_option_valid_value (option, scm_value);
-    if (scm_value == SCM_UNDEFINED)
-        return FALSE;
-
-    setter = gnc_option_setter (option);
-    if (setter == SCM_UNDEFINED)
-        return FALSE;
-
-    scm_call_1 (setter, scm_value);
-
-    return TRUE;
-}
-
-/*******************************************************************\
- * gnc_option_date_option_get_subtype                              *
- *   find out whether a date option is a relative or absolute date *
- *                                                                 *
- * Args: option - option to get date subtype for                   *
- * Return: newly allocated subtype string or NULL                  *
-\*******************************************************************/
-char *
-gnc_option_date_option_get_subtype (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_symbol_to_string (getters.date_option_subtype, option->guile_option);
-}
-
-/*******************************************************************\
- * gnc_date_option_value_get_type                                  *
- *   get the type of a date option value                           *
- *                                                                 *
- * Args: option_value - option value to get type of                *
- * Return: newly allocated type string or NULL                     *
-\*******************************************************************/
-char *
-gnc_date_option_value_get_type (SCM option_value)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_symbol_to_string (getters.date_option_value_type, option_value);
-}
-
-/*******************************************************************\
- * gnc_date_option_value_get_absolute                              *
- *   get the absolute time of a date option value                  *
- *                                                                 *
- * Args: option_value - option value to get absolute time of       *
- * Return: time64 value                                            *
-\*******************************************************************/
-time64
-gnc_date_option_value_get_absolute (SCM option_value)
-{
-    SCM value;
-    initialize_getters ();
-    value = scm_call_1 (getters.date_option_value_absolute, option_value);
-    return scm_to_int64 (value);
-}
-
-/*******************************************************************\
- * gnc_date_option_value_get_relative                              *
- *   get the relative time of a date option value                  *
- *                                                                 *
- * Args: option_value - option value to get relative time of       *
- * Return: SCM value                                               *
-\*******************************************************************/
-SCM
-gnc_date_option_value_get_relative (SCM option_value)
-{
-    initialize_getters ();
-
-    return scm_call_1 (getters.date_option_value_relative, option_value);
-}
-
-/*******************************************************************\
- * gnc_plot_size_option_value_get_type                             *
- *   get the type of a plot size option value                      *
- *                                                                 *
- * Args: option_value - option value to get type of                *
- * Return: newly allocated type string or NULL                     *
-\*******************************************************************/
-char *
-gnc_plot_size_option_value_get_type (SCM option_value)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_symbol_to_string (getters.plot_size_option_value_type, option_value);
-}
-
-/*******************************************************************\
- * gnc_plot_size_option_value_get_value                            *
- *   get the plot size option value                                *
- *                                                                 *
- * Args: option_value - option value to get the plot size of       *
- * Return: double value                                            *
-\*******************************************************************/
-gdouble
-gnc_plot_size_option_value_get_value (SCM option_value)
-{
-    SCM value;
-
-    initialize_getters ();
-
-    value = scm_call_1 (getters.plot_size_option_value, option_value);
-
-    if (scm_is_number (value))
-        return scm_to_double (value);
-    else
-        return 1.0;
-}
-
-static int
-find_option_db_with_selectable_pred (gpointer key, gpointer value, gpointer data)
-{
-    SCM guile_options = data;
-    GNCOptionDB *odb = value;
-
-    if (odb && (odb->guile_options == guile_options) && odb->set_selectable)
-        return TRUE;
-
-    return FALSE;
-}
-
-static GNCOptionDB *
-find_option_db_with_selectable (SCM guile_options)
-{
-    return g_hash_table_find (option_dbs, find_option_db_with_selectable_pred,
-                              guile_options);
-}
-
-/*******************************************************************\
- * gnc_option_db_set_option_selectable_by_name                     *
- *   set the sensitivity of the option widget                      *
- *                                                                 *
- * Args: guile_options - guile side option db                      *
- *       section       - section of option                         *
- *       name          - name of option                            *
- *       selectable    - selectable status                         *
- * Return: SCM value                                               *
-\*******************************************************************/
-void
-gnc_option_db_set_option_selectable_by_name (SCM guile_options,
-                                             const char *section,
-                                             const char *name,
-                                             gboolean selectable)
-{
-    GNCOptionDB *odb;
-    GNCOption *option;
-
-    odb = find_option_db_with_selectable (guile_options);
-    if (!odb)
-        return;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-    if (!option)
-        return;
-
-    gnc_option_set_selectable (option, selectable);
-}
-
-/* the value is a list of:
- * format(symbol), month(symbol), include-years(bool), custom-string(string)
- */
-
-gboolean gnc_dateformat_option_value_parse (SCM value,
-                                            QofDateFormat *format,
-                                            GNCDateMonthFormat *months,
-                                            gboolean *years,
-                                            char **custom)
-{
-    SCM val;
-    gchar *str;
-
-    if (!scm_is_list (value) || scm_is_null (value))
-        return TRUE;
-
-    do
-    {
-        /* Parse the format */
-        val = SCM_CAR(value);
-        value = SCM_CDR(value);
-        if (!scm_is_symbol (val))
-            break;
-        str = gnc_scm_symbol_to_locale_string (val);
-        if (!str)
-            break;
-
-        if (format)
-        {
-            if (gnc_date_string_to_dateformat (str, format))
-            {
-                g_free (str);
-                break;
-            }
-        }
-        g_free (str);
-
-        /* parse the months */
-        val = SCM_CAR(value);
-        value = SCM_CDR(value);
-        if (!scm_is_symbol (val))
-            break;
-        str = gnc_scm_symbol_to_locale_string (val);
-        if (!str)
-            break;
-
-        if (months)
-        {
-            if (gnc_date_string_to_monthformat (str, months))
-            {
-                g_free (str);
-                break;
-            }
-        }
-        g_free (str);
-
-        /* parse the years */
-        val = SCM_CAR(value);
-        value = SCM_CDR(value);
-        if (!scm_is_bool (val))
-            break;
-
-        if (years)
-            *years = scm_is_true (val);
-
-        /* parse the custom */
-        val = SCM_CAR(value);
-        value = SCM_CDR(value);
-        if (!scm_is_string (val))
-            break;
-        if (!scm_is_null (value))
-            break;
-
-        if (custom)
-            *custom = gnc_scm_to_utf8_string (val);
-
-        return FALSE;
-    }
-    while (FALSE);
-
-    return TRUE;
-}
-
-SCM gnc_dateformat_option_set_value (QofDateFormat format,
-                                     GNCDateMonthFormat months,
-                                     gboolean years,
-                                     const char *custom)
-{
-    SCM value = SCM_EOL;
-    SCM val;
-    const char *str;
-
-    /* build the list in reverse order */
-    if (custom)
-        val = scm_from_utf8_string (custom);
-    else
-        val = SCM_BOOL_F;
-    value = scm_cons (val, value);
-
-    val = SCM_BOOL(years);
-    value = scm_cons (val, value);
-
-    str = gnc_date_monthformat_to_string (months);
-    if (str)
-        val = scm_from_locale_symbol (str);
-    else
-        val = SCM_BOOL_F;
-    value = scm_cons (val, value);
-
-    str = gnc_date_dateformat_to_string (format);
-    if (str)
-        val = scm_from_locale_symbol (str);
-    else
-        val = SCM_BOOL_F;
-    value = scm_cons (val, value);
-
-    return value;
-}
-
-/*
- * the generator should be a procedure that takes one argument,
- * an options object.  The procedure should fill in the options with
- * its defined kvp options.
- */
-void
-gnc_register_kvp_option_generator (QofIdType id_type, SCM generator)
-{
-    GList *list;
-    init_table();
-    list = g_hash_table_lookup (kvp_registry, id_type);
-    list = g_list_prepend (list, generator);
-    g_hash_table_insert (kvp_registry, (gpointer) id_type, list);
-    scm_gc_protect_object (generator);
-}
diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h
deleted file mode 100644
index 953eb651d..000000000
--- a/libgnucash/app-utils/option-util.h
+++ /dev/null
@@ -1,254 +0,0 @@
-/********************************************************************\
- * option-util.h -- GNOME<->guile option interface                  *
- * Copyright (C) 1998,1999 Linas Vepstas                            *
- * Copyright (C) 2000 Dave Peticolas                                *
- * Copyright (C) 2017 Aaron Laws                                    *
- *                                                                  *
- * 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 OPTION_UTIL_H
-#define OPTION_UTIL_H
-
-#include <glib.h>
-#include <libguile.h>
-#include "guile-mappings.h"
-
-#include "gnc-commodity.h"
-#include "gnc-engine-guile.h"
-#include "qof.h"
-
-typedef struct gnc_option GNCOption;
-typedef struct gnc_option_section GNCOptionSection;
-typedef struct gnc_option_db GNCOptionDB;
-typedef int GNCOptionDBHandle;
-
-typedef SCM (*GNCOptionGetUIValue) (GNCOption *option);
-typedef void (*GNCOptionSetUIValue) (GNCOption *option,
-                                     gboolean use_default);
-typedef void (*GNCOptionSetSelectable) (GNCOption *option,
-                                        gboolean selectable);
-typedef void (*GNCOptionChangeCallback) (gpointer user_data);
-
-/***** Prototypes ********************************************************/
-
-void gnc_option_set_changed (GNCOption *option, gboolean changed);
-
-/** Returns an opaque pointer to the widget of this option. The actual
- * GUI implementation in dialog-options.c will store a GtkWidget* in
- * here. */
-gpointer gnc_option_get_widget (GNCOption *option);
-
-/** Store an opaque pointer to the widget of this option. The actual
- * GUI implementation in dialog-options.c will store a GtkWidget* in
- * here. */
-void gnc_option_set_widget (GNCOption *option, gpointer widget);
-
-SCM  gnc_option_get_ui_value (GNCOption *option);
-void gnc_option_set_ui_value (GNCOption *option, gboolean use_default);
-void gnc_option_set_selectable (GNCOption *option, gboolean selectable);
-
-GNCOptionDB * gnc_option_db_new(SCM guile_options);
-void          gnc_option_db_destroy(GNCOptionDB *odb);
-
-/* Create an option DB for a particular type, and save/load from a kvp.
- * This assumes the gnc:*kvp-option-path* location for the options
- * in the kvp.
- */
-GNCOptionDB * gnc_option_db_new_for_type(QofIdType id_type);
-void gnc_option_db_load(GNCOptionDB* odb, QofBook *book);
-void gnc_option_db_save(GNCOptionDB* odb, QofBook *book, gboolean clear_all);
-void gnc_register_kvp_option_generator(QofIdType id_type, SCM generator);
-
-void gnc_option_db_set_ui_callbacks (GNCOptionDB *odb,
-                                     GNCOptionGetUIValue get_ui_value,
-                                     GNCOptionSetUIValue set_ui_value,
-                                     GNCOptionSetSelectable set_selectable);
-
-SCM gnc_option_db_register_change_callback (GNCOptionDB *odb,
-                                            GNCOptionChangeCallback callback,
-                                            gpointer data,
-                                            const char *section,
-                                            const char *name);
-
-void gnc_option_db_unregister_change_callback_id (GNCOptionDB *odb,
-                                                  SCM callback_id);
-
-char * gnc_option_section (GNCOption *option);
-char * gnc_option_name (GNCOption *option);
-char * gnc_option_type (GNCOption *option);
-char * gnc_option_sort_tag (GNCOption *option);
-char * gnc_option_documentation (GNCOption *option);
-SCM    gnc_option_getter (GNCOption *option);
-SCM    gnc_option_setter (GNCOption *option);
-SCM    gnc_option_default_getter (GNCOption *option);
-SCM    gnc_option_get_option_data (GNCOption *option);
-
-int    gnc_option_num_permissible_values (GNCOption *option);
-int    gnc_option_permissible_value_index (GNCOption *option, SCM value);
-SCM    gnc_option_permissible_value (GNCOption *option, int index);
-char * gnc_option_permissible_value_name (GNCOption *option, int index);
-
-gboolean gnc_option_show_time (GNCOption *option);
-
-gboolean gnc_option_multiple_selection (GNCOption *option);
-GList * gnc_option_get_account_type_list (GNCOption *option);
-
-gboolean gnc_option_get_range_info (GNCOption *option,
-                                    double *lower_bound,
-                                    double *upper_bound,
-                                    int    *num_decimals,
-                                    double *step_size);
-
-gdouble  gnc_option_color_range (GNCOption *option);
-gdouble  gnc_option_use_alpha (GNCOption *option);
-guint32  gnc_option_get_color_argb (GNCOption *option);
-gboolean gnc_option_get_color_info (GNCOption *option,
-                                    gboolean use_default,
-                                    gdouble *red,
-                                    gdouble *green,
-                                    gdouble *blue,
-                                    gdouble *alpha);
-
-void gnc_option_call_option_widget_changed_proc (GNCOption *option,
-                                                 gboolean reset_changed);
-
-void gnc_option_set_default (GNCOption *option);
-
-guint gnc_option_db_num_sections (GNCOptionDB *odb);
-
-const char * gnc_option_section_name (GNCOptionSection *section);
-guint  gnc_option_section_num_options (GNCOptionSection *section);
-
-GNCOptionSection * gnc_option_db_get_section (GNCOptionDB *odb, gint i);
-
-GNCOption * gnc_get_option_section_option (GNCOptionSection *section, int i);
-
-GNCOption * gnc_option_db_get_option_by_name (GNCOptionDB *odb,
-                                              const char *section_name,
-                                              const char *name);
-
-GNCOption * gnc_option_db_get_option_by_SCM (GNCOptionDB *odb,
-                                             SCM guile_option);
-
-gboolean gnc_option_db_dirty (GNCOptionDB *odb);
-void     gnc_option_db_clean (GNCOptionDB *odb);
-
-gboolean gnc_option_db_get_changed (GNCOptionDB *odb);
-GList* gnc_option_db_commit (GNCOptionDB *odb);
-
-char * gnc_option_db_get_default_section (GNCOptionDB *odb);
-
-SCM gnc_option_db_lookup_option (GNCOptionDB *odb,
-                                 const char *section,
-                                 const char *name,
-                                 SCM default_value);
-
-gboolean gnc_option_db_lookup_boolean_option (GNCOptionDB *odb,
-                                              const char *section,
-                                              const char *name,
-                                              gboolean default_value);
-
-char * gnc_option_db_lookup_string_option (GNCOptionDB *odb,
-                                           const char *section,
-                                           const char *name,
-                                           const char *default_value);
-
-char * gnc_option_db_lookup_font_option (GNCOptionDB *odb,
-                                         const char *section,
-                                         const char *name,
-                                         const char *default_value);
-
-char * gnc_option_db_lookup_multichoice_option (GNCOptionDB *odb,
-                                                const char *section,
-                                                const char *name,
-                                                const char *default_value);
-
-gdouble gnc_option_db_lookup_number_option (GNCOptionDB *odb,
-                                           const char *section,
-                                           const char *name,
-                                           gdouble default_value);
-
-gboolean gnc_option_db_lookup_color_option (GNCOptionDB *odb,
-                                            const char *section,
-                                            const char *name,
-                                            gdouble *red,
-                                            gdouble *green,
-                                            gdouble *blue,
-                                            gdouble *alpha);
-
-guint32 gnc_option_db_lookup_color_option_argb (GNCOptionDB *odb,
-                                                const char *section,
-                                                const char *name,
-                                                guint32 default_value);
-
-gboolean gnc_option_db_set_option(GNCOptionDB *odb,
-                                  const char *section,
-                                  const char *name,
-                                  SCM value);
-
-gboolean gnc_option_db_set_number_option (GNCOptionDB *odb,
-                                          const char *section,
-                                          const char *name,
-                                          gdouble value);
-
-gboolean gnc_option_db_set_boolean_option (GNCOptionDB *odb,
-                                           const char *section,
-                                           const char *name,
-                                           gboolean value);
-
-gboolean gnc_option_db_set_string_option (GNCOptionDB *odb,
-                                          const char *section,
-                                          const char *name,
-                                          const char *value);
-
-char * gnc_option_date_option_get_subtype (GNCOption *option);
-
-char * gnc_date_option_value_get_type (SCM option_value);
-time64 gnc_date_option_value_get_absolute (SCM option_value);
-SCM gnc_date_option_value_get_relative (SCM option_value);
-
-char * gnc_plot_size_option_value_get_type (SCM option_value);
-gdouble gnc_plot_size_option_value_get_value (SCM option_value);
-
-void gnc_option_db_set_option_selectable_by_name (SCM guile_options,
-                                                  const char *section,
-                                                  const char *name,
-                                                  gboolean selectable);
-
-gboolean gnc_dateformat_option_value_parse (SCM value,
-                                            QofDateFormat *format,
-                                            GNCDateMonthFormat *months,
-                                            gboolean *years, char **custom);
-
-SCM gnc_dateformat_option_set_value (QofDateFormat format,
-                                     GNCDateMonthFormat months,
-                                     gboolean years,
-                                     const char *custom);
-
-void gnc_option_db_register_option (GNCOptionDBHandle handle,
-                                    SCM guile_option);
-
-/* private */
-void gncp_option_invoke_callback (GNCOptionChangeCallback callback,
-                                  gpointer data);
-
-/* Reset all the widgets in one section to their default values */
-void gnc_option_db_section_reset_widgets (GNCOptionSection *section);
-
-#endif /* OPTION_UTIL_H */
diff --git a/libgnucash/engine/qofbookslots.h b/libgnucash/engine/qofbookslots.h
index 4ec7b0f30..4ed5fd8c2 100644
--- a/libgnucash/engine/qofbookslots.h
+++ b/libgnucash/engine/qofbookslots.h
@@ -48,7 +48,6 @@
  * It is tied from this C #define in
  *   src/app-utils/app-utils.scm
  * and is extensively used in
- *   src/app-utils/option-util.c
  *   src/gnome-utils/gnome-utils.scm
  *   various reports
  */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b0e1da8fa..75b34e8e6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -537,7 +537,6 @@ libgnucash/app-utils/gnc-sx-instance-model.c
 libgnucash/app-utils/gnc-ui-balances.c
 libgnucash/app-utils/gnc-ui-util.c
 libgnucash/app-utils/options.scm
-libgnucash/app-utils/option-util.c
 libgnucash/app-utils/QuickFill.c
 libgnucash/backend/dbi/gnc-backend-dbi.cpp
 libgnucash/backend/dbi/gnc-dbisqlconnection.cpp

commit e2c87f23c3c8d0760f886fb6c89fa621820ff4aa
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 8 16:45:15 2020 -0700

    Convert optiondb users in gnome and gnome-utils to C++.
    
    To enable using std::unique_ptr<GncObjectDB> and avoid memory management
    problems.

diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt
index 960651b52..3d2e354ce 100644
--- a/gnucash/gnome-utils/CMakeLists.txt
+++ b/gnucash/gnome-utils/CMakeLists.txt
@@ -74,7 +74,7 @@ set (gnome_utils_SOURCES
   gnc-gui-query.c
   gnc-icons.c
   gnc-keyring.c
-  gnc-main-window.c
+  gnc-main-window.cpp
   gnc-menu-extensions.c
   gnc-plugin-file-history.c
   gnc-plugin-manager.c
diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h
index 82d0fdd68..8c3890a78 100644
--- a/gnucash/gnome-utils/dialog-options.h
+++ b/gnucash/gnome-utils/dialog-options.h
@@ -32,9 +32,9 @@ using GNCOptionDB = GncOptionDB;
 extern "C"
 {
 #else
-#include "option-util.h"
-typedef GNCOption GncOption;
-typedef GNCOptionDB GncOptionDB;
+typedef struct GncOption GncOption;
+typedef struct GncOptionDB GncOptionDB;
+typedef GncOptionDB GNCOptionDB;
 #endif
 #include <guile-mappings.h>
 #include <gtk/gtk.h>
diff --git a/gnucash/gnome-utils/gnc-icons.c b/gnucash/gnome-utils/gnc-icons.c
index 1cbe2483f..b4e155fa0 100644
--- a/gnucash/gnome-utils/gnc-icons.c
+++ b/gnucash/gnome-utils/gnc-icons.c
@@ -33,6 +33,7 @@
 #include "gnc-filepath-utils.h"
 #include "gnc-gnome-utils.h"
 #include "gnc-path.h"
+#include <gnc-engine.h> // For define GNC_MOD_GUI
 
 static QofLogModule log_module = GNC_MOD_GUI;
 
diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.cpp
similarity index 91%
rename from gnucash/gnome-utils/gnc-main-window.c
rename to gnucash/gnome-utils/gnc-main-window.cpp
index a8bda02f0..5c4aba19e 100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.cpp
@@ -32,6 +32,9 @@
     @author Copyright (C) 2003 Jan Arne Petersen <jpetersen at uni-bonn.de>
     @author Copyright (C) 2003,2005,2006 David Hampton <hampton at employees.org>
 */
+#include <libguile.h>
+extern "C"
+{
 #include <config.h>
 
 #include <glib/gi18n.h>
@@ -43,7 +46,6 @@
 #include "gnc-plugin-manager.h"
 #include "gnc-main-window.h"
 
-#include "dialog-options.h"
 #include "dialog-preferences.h"
 #include "dialog-reset-warnings.h"
 #include "dialog-transfer.h"
@@ -70,8 +72,7 @@
 #include "gnc-warnings.h"
 #include "gnc-window.h"
 #include "gnc-prefs.h"
-// +JSLED
-//#include "gnc-html.h"
+#include <gnc-optiondb.h>
 #include "gnc-autosave.h"
 #include "print-session.h"
 #ifdef MAC_INTEGRATION
@@ -82,6 +83,8 @@
 # include <sys/types.h>
 # include <sys/stat.h> // for stat(2)
 #endif
+}
+#include "dialog-options.h"
 
 /** Names of signals generated by the main window. */
 enum
@@ -132,12 +135,12 @@ extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options);
 /** The debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_GUI;
 /** A pointer to the parent class of an embedded window. */
-static GObjectClass *parent_class = NULL;
+static GObjectClass *parent_class = nullptr;
 /** An identifier that indicates a "main" window. */
 static GQuark window_type = 0;
 /** A list of all extant main windows. This is for convenience as the
  *  same information can be obtained from the object tracking code. */
-static GList *active_windows = NULL;
+static GList *active_windows = nullptr;
 /** Count down timer for the save changes dialog. If the timer reaches zero
  *  any changes will be saved and the save dialog closed automatically */
 static guint secs_to_save = 0;
@@ -282,27 +285,27 @@ static GtkActionEntry gnc_menu_actions [] =
 {
     /* Toplevel */
 
-    { "FileAction", NULL, N_("_File"), NULL, NULL, NULL, },
-    { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL },
-    { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL },
-    { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL },
-    { "TransactionAction", NULL, N_("Tra_nsaction"), NULL, NULL, NULL },
-    { "ReportsAction", NULL, N_("_Reports"), NULL, NULL, NULL },
-    { "ToolsAction", NULL, N_("_Tools"), NULL, NULL, NULL },
-    { "ExtensionsAction", NULL, N_("E_xtensions"), NULL, NULL, NULL },
-    { "WindowsAction", NULL, N_("_Windows"), NULL, NULL, NULL },
-    { "HelpAction", NULL, N_("_Help"), NULL, NULL, NULL },
+    { "FileAction", nullptr, N_("_File"), nullptr, nullptr, nullptr, },
+    { "EditAction", nullptr, N_("_Edit"), nullptr, nullptr, nullptr },
+    { "ViewAction", nullptr, N_("_View"), nullptr, nullptr, nullptr },
+    { "ActionsAction", nullptr, N_("_Actions"), nullptr, nullptr, nullptr },
+    { "TransactionAction", nullptr, N_("Tra_nsaction"), nullptr, nullptr, nullptr },
+    { "ReportsAction", nullptr, N_("_Reports"), nullptr, nullptr, nullptr },
+    { "ToolsAction", nullptr, N_("_Tools"), nullptr, nullptr, nullptr },
+    { "ExtensionsAction", nullptr, N_("E_xtensions"), nullptr, nullptr, nullptr },
+    { "WindowsAction", nullptr, N_("_Windows"), nullptr, nullptr, nullptr },
+    { "HelpAction", nullptr, N_("_Help"), nullptr, nullptr, nullptr },
 
     /* File menu */
 
-    { "FileImportAction", NULL, N_("_Import"), NULL, NULL, NULL },
-    { "FileExportAction", NULL, N_("_Export"), NULL, NULL, NULL },
+    { "FileImportAction", nullptr, N_("_Import"), nullptr, nullptr, nullptr },
+    { "FileExportAction", nullptr, N_("_Export"), nullptr, nullptr, nullptr },
     {
         "FilePrintAction", "document-print", N_("_Print..."), "<primary>p",
-        N_("Print the currently active page"), NULL
+        N_("Print the currently active page"), nullptr
     },
 #ifndef GTK_STOCK_PAGE_SETUP
-#    define GTK_STOCK_PAGE_SETUP NULL
+#    define GTK_STOCK_PAGE_SETUP nullptr
 #endif
     {
         "FilePageSetupAction", "document-page-setup", N_("Pa_ge Setup..."), "<primary><shift>p",
@@ -343,7 +346,7 @@ static GtkActionEntry gnc_menu_actions [] =
         G_CALLBACK (gnc_main_window_cmd_edit_paste)
     },
     {
-        "EditPreferencesAction", "preferences-system", N_("Pr_eferences"), NULL,
+        "EditPreferencesAction", "preferences-system", N_("Pr_eferences"), nullptr,
         N_("Edit the global preferences of GnuCash"),
         G_CALLBACK (gnc_main_window_cmd_edit_preferences)
     },
@@ -351,12 +354,12 @@ static GtkActionEntry gnc_menu_actions [] =
     /* View menu */
 
     {
-        "ViewSortByAction", NULL, N_("_Sort By..."), NULL,
-        N_("Select sorting criteria for this page view"), NULL
+        "ViewSortByAction", nullptr, N_("_Sort By..."), nullptr,
+        N_("Select sorting criteria for this page view"), nullptr
     },
     {
-        "ViewFilterByAction", NULL, N_("_Filter By..."), NULL,
-        N_("Select the account types that should be displayed."), NULL
+        "ViewFilterByAction", nullptr, N_("_Filter By..."), nullptr,
+        N_("Select the account types that should be displayed."), nullptr
     },
     {
         "ViewRefreshAction", "view-refresh", N_("_Refresh"), "<primary>r",
@@ -366,14 +369,14 @@ static GtkActionEntry gnc_menu_actions [] =
 
     /* Actions menu */
 
-    { "ScrubMenuAction", NULL, N_("_Check & Repair"), NULL, NULL, NULL },
+    { "ScrubMenuAction", nullptr, N_("_Check & Repair"), nullptr, nullptr, nullptr },
     {
-        "ActionsForgetWarningsAction", NULL, N_("Reset _Warnings..."), NULL,
+        "ActionsForgetWarningsAction", nullptr, N_("Reset _Warnings..."), nullptr,
         N_("Reset the state of all warning messages so they will be shown again."),
         G_CALLBACK (gnc_main_window_cmd_actions_reset_warnings)
     },
     {
-        "ActionsRenamePageAction", NULL, N_("Re_name Page"), NULL,
+        "ActionsRenamePageAction", nullptr, N_("Re_name Page"), nullptr,
         N_("Rename this page."),
         G_CALLBACK (gnc_main_window_cmd_actions_rename_page)
     },
@@ -381,12 +384,12 @@ static GtkActionEntry gnc_menu_actions [] =
     /* Windows menu */
 
     {
-        "WindowNewAction", NULL, N_("_New Window"), NULL,
+        "WindowNewAction", nullptr, N_("_New Window"), nullptr,
         N_("Open a new top-level GnuCash window."),
         G_CALLBACK (gnc_main_window_cmd_window_new)
     },
     {
-        "WindowMovePageAction", NULL, N_("New Window with _Page"), NULL,
+        "WindowMovePageAction", nullptr, N_("New Window with _Page"), nullptr,
         N_("Move the current page to a new top-level GnuCash window."),
         G_CALLBACK (gnc_main_window_cmd_window_move_page)
     },
@@ -404,7 +407,7 @@ static GtkActionEntry gnc_menu_actions [] =
         G_CALLBACK (gnc_main_window_cmd_help_contents)
     },
     {
-        "HelpAboutAction", "help-about", N_("_About"), NULL,
+        "HelpAboutAction", "help-about", N_("_About"), nullptr,
         N_("About GnuCash"),
         G_CALLBACK (gnc_main_window_cmd_help_about)
     },
@@ -417,17 +420,17 @@ static guint gnc_menu_n_actions = G_N_ELEMENTS (gnc_menu_actions);
 static GtkToggleActionEntry toggle_actions [] =
 {
     {
-        "ViewToolbarAction", NULL, N_("_Toolbar"), NULL,
+        "ViewToolbarAction", nullptr, N_("_Toolbar"), nullptr,
         N_("Show/hide the toolbar on this window"),
         G_CALLBACK (gnc_main_window_cmd_view_toolbar), TRUE
     },
     {
-        "ViewSummaryAction", NULL, N_("Su_mmary Bar"), NULL,
+        "ViewSummaryAction", nullptr, N_("Su_mmary Bar"), nullptr,
         N_("Show/hide the summary bar on this window"),
         G_CALLBACK (gnc_main_window_cmd_view_summary), TRUE
     },
     {
-        "ViewStatusbarAction", NULL, N_("Stat_us Bar"), NULL,
+        "ViewStatusbarAction", nullptr, N_("Stat_us Bar"), nullptr,
         N_("Show/hide the status bar on this window"),
         G_CALLBACK (gnc_main_window_cmd_view_statusbar), TRUE
     },
@@ -440,16 +443,16 @@ static guint n_toggle_actions = G_N_ELEMENTS (toggle_actions);
  *  code. */
 static GtkRadioActionEntry radio_entries [] =
 {
-    { "Window0Action", NULL, N_("Window _1"), NULL, NULL, 0 },
-    { "Window1Action", NULL, N_("Window _2"), NULL, NULL, 1 },
-    { "Window2Action", NULL, N_("Window _3"), NULL, NULL, 2 },
-    { "Window3Action", NULL, N_("Window _4"), NULL, NULL, 3 },
-    { "Window4Action", NULL, N_("Window _5"), NULL, NULL, 4 },
-    { "Window5Action", NULL, N_("Window _6"), NULL, NULL, 5 },
-    { "Window6Action", NULL, N_("Window _7"), NULL, NULL, 6 },
-    { "Window7Action", NULL, N_("Window _8"), NULL, NULL, 7 },
-    { "Window8Action", NULL, N_("Window _9"), NULL, NULL, 8 },
-    { "Window9Action", NULL, N_("Window _0"), NULL, NULL, 9 },
+    { "Window0Action", nullptr, N_("Window _1"), nullptr, nullptr, 0 },
+    { "Window1Action", nullptr, N_("Window _2"), nullptr, nullptr, 1 },
+    { "Window2Action", nullptr, N_("Window _3"), nullptr, nullptr, 2 },
+    { "Window3Action", nullptr, N_("Window _4"), nullptr, nullptr, 3 },
+    { "Window4Action", nullptr, N_("Window _5"), nullptr, nullptr, 4 },
+    { "Window5Action", nullptr, N_("Window _6"), nullptr, nullptr, 5 },
+    { "Window6Action", nullptr, N_("Window _7"), nullptr, nullptr, 6 },
+    { "Window7Action", nullptr, N_("Window _8"), nullptr, nullptr, 7 },
+    { "Window8Action", nullptr, N_("Window _9"), nullptr, nullptr, 8 },
+    { "Window9Action", nullptr, N_("Window _0"), nullptr, nullptr, 9 },
 };
 
 /** The number of radio actions provided by the main window. */
@@ -462,7 +465,7 @@ static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
 static const gchar *gnc_menu_important_actions[] =
 {
     "FileCloseAction",
-    NULL,
+    nullptr,
 };
 
 
@@ -473,7 +476,7 @@ static const gchar *gnc_menu_important_actions[] =
 static const gchar *always_insensitive_actions[] =
 {
     "FilePrintAction",
-    NULL
+    nullptr
 };
 
 
@@ -483,7 +486,7 @@ static const gchar *always_insensitive_actions[] =
 static const gchar *initially_insensitive_actions[] =
 {
     "FileCloseAction",
-    NULL
+    nullptr
 };
 
 
@@ -495,7 +498,7 @@ static const gchar *always_hidden_actions[] =
 {
     "ViewSortByAction",
     "ViewFilterByAction",
-    NULL
+    nullptr
 };
 
 
@@ -504,7 +507,7 @@ static const gchar *always_hidden_actions[] =
 static const gchar *immutable_page_actions[] =
 {
     "FileCloseAction",
-    NULL
+    nullptr
 };
 
 
@@ -513,7 +516,7 @@ static const gchar *immutable_page_actions[] =
 static const gchar *multiple_page_actions[] =
 {
     "WindowMovePageAction",
-    NULL
+    nullptr
 };
 
 
@@ -558,19 +561,14 @@ gnc_main_window_is_restoring_pages (GncMainWindow *window)
 void
 gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data)
 {
-    GncMainWindowPrivate *priv;
-    GncMainWindow *window;
-    GncPluginPage *page;
-    GList *w, *p;
-
     ENTER(" ");
-    for (w = active_windows; w; w = g_list_next(w))
+    for (auto w = active_windows; w; w = g_list_next(w))
     {
-        window = w->data;
-        priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-        for (p = priv->installed_pages; p; p = g_list_next(p))
+        auto window{static_cast<GncMainWindow*>(w->data)};
+        auto priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
+        for (auto p = priv->installed_pages; p; p = g_list_next(p))
         {
-            page = p->data;
+            auto page{static_cast<GncPluginPage*>(p->data)};
             fn(page, user_data);
         }
     }
@@ -595,9 +593,9 @@ gnc_main_window_restore_page (GncMainWindow *window,
 {
     GncMainWindowPrivate *priv;
     GncPluginPage *page;
-    gchar *page_group, *page_type = NULL, *name = NULL;
+    gchar *page_group, *page_type = nullptr, *name = nullptr;
     const gchar *class_type;
-    GError *error = NULL;
+    GError *error = nullptr;
 
     ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)",
           window, data, data->key_file, data->window_num, data->page_offset,
@@ -616,7 +614,8 @@ gnc_main_window_restore_page (GncMainWindow *window,
     }
 
     /* See if the page already exists. */
-    page = g_list_nth_data(priv->installed_pages, data->page_num);
+    page = static_cast<GncPluginPage*>(g_list_nth_data(priv->installed_pages,
+                                                      data->page_num));
     if (page)
     {
         class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name;
@@ -635,7 +634,7 @@ gnc_main_window_restore_page (GncMainWindow *window,
         if (page)
         {
             /* Does the page still need to be installed into the window? */
-            if (page->window == NULL)
+            if (page->window == nullptr)
             {
                 gnc_plugin_page_set_use_new_window(page, FALSE);
                 gnc_main_window_open_page(window, page);
@@ -687,7 +686,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
     gboolean max, visible, desired_visibility;
     gchar *window_group;
     gint page_start, page_count, i;
-    GError *error = NULL;
+    GError *error = nullptr;
 
     /* Setup */
     ENTER("window %p, data %p (key file %p, window %d)",
@@ -742,7 +741,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
     }
 
     /* Build a window if we don't already have one */
-    if (window == NULL)
+    if (window == nullptr)
     {
         DEBUG("Window %d doesn't exist. Creating new window.", data->window_num);
         DEBUG("active_windows %p.", active_windows);
@@ -761,7 +760,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
         g_warning("error reading group %s key %s: %s",
                   window_group, WINDOW_GEOMETRY, error->message);
         g_error_free(error);
-        error = NULL;
+        error = nullptr;
     }
     else if (length != 2)
     {
@@ -783,7 +782,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
         g_warning("error reading group %s key %s: %s",
                   window_group, WINDOW_POSITION, error->message);
         g_error_free(error);
-        error = NULL;
+        error = nullptr;
     }
     else if (length != 2)
     {
@@ -822,7 +821,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
         g_warning("error reading group %s key %s: %s",
                   window_group, WINDOW_MAXIMIZED, error->message);
         g_error_free(error);
-        error = NULL;
+        error = nullptr;
     }
     else if (max)
     {
@@ -839,7 +838,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
         g_warning("error reading group %s key %s: %s",
                   window_group, TOOLBAR_VISIBLE, error->message);
         g_error_free(error);
-        error = NULL;
+        error = nullptr;
     }
     else if (visible != desired_visibility)
     {
@@ -855,7 +854,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
         g_warning("error reading group %s key %s: %s",
                   window_group, TOOLBAR_VISIBLE, error->message);
         g_error_free(error);
-        error = NULL;
+        error = nullptr;
     }
     else if (visible != desired_visibility)
     {
@@ -871,7 +870,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
         g_warning("error reading group %s key %s: %s",
                   window_group, TOOLBAR_VISIBLE, error->message);
         g_error_free(error);
-        error = NULL;
+        error = nullptr;
     }
     else if (visible != desired_visibility)
     {
@@ -900,7 +899,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
         g_warning("error reading group %s key %s: %s",
                   window_group, WINDOW_PAGEORDER, error->message);
         g_error_free(error);
-        error = NULL;
+        error = nullptr;
     }
     else if (length != page_count)
     {
@@ -911,7 +910,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da
     {
         /* Dump any list that might exist */
         g_list_free(priv->usage_order);
-        priv->usage_order = NULL;
+        priv->usage_order = nullptr;
         /* Now rebuild the list from the key file. */
         for (i = 0; i < length; i++)
         {
@@ -945,9 +944,8 @@ void
 gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
 {
     gint i, window_count;
-    GError *error = NULL;
+    GError *error = nullptr;
     GncMainWindowSaveData data;
-    GncMainWindow *window;
 
     /* We use the same struct for reading and for writing, so we cast
        away the const. */
@@ -965,14 +963,15 @@ gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
 
     /* Restore all state information on the open windows.  Window
        numbers in state file are 1-based. GList indices are 0-based. */
-    gnc_set_busy_cursor (NULL, TRUE);
+    gnc_set_busy_cursor (nullptr, TRUE);
     for (i = 0; i < window_count; i++)
     {
         data.window_num = i;
-        window = g_list_nth_data(active_windows, i);
+        auto window{static_cast<GncMainWindow*>(g_list_nth_data(active_windows,
+                                                                i))};
         gnc_main_window_restore_window(window, &data);
     }
-    gnc_unset_busy_cursor (NULL);
+    gnc_unset_busy_cursor (nullptr);
 
     statusbar_notification_lastmodified();
 }
@@ -986,7 +985,7 @@ gnc_main_window_restore_default_state(GncMainWindow *window)
      * in the window. */
     DEBUG("no saved state file");
     if (!window)
-        window = g_list_nth_data(active_windows, 0);
+        window = static_cast<GncMainWindow*>(g_list_nth_data(active_windows, 0));
     gtk_widget_show (GTK_WIDGET(window));
     action = gnc_main_window_find_action(window, "ViewAccountTreeAction");
     gtk_action_activate(action);
@@ -1066,7 +1065,7 @@ gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data)
     /* Save page ordering within the notebook. Use +1 notation so the
      * numbers in the page order match the page sections, at least for
      * the one window case. */
-    order = g_malloc(sizeof(gint) * num_pages);
+    order = static_cast<int*>(g_malloc(sizeof(gint) * num_pages));
     for (i = 0; i < num_pages; i++)
     {
         gpointer page = g_list_nth_data(priv->usage_order, i);
@@ -1152,7 +1151,7 @@ gnc_main_window_finish_pending (GncMainWindow *window)
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
     for (item = priv->installed_pages; item; item = g_list_next(item))
     {
-        if (!gnc_plugin_page_finish_pending(item->data))
+        if (!gnc_plugin_page_finish_pending(static_cast<GncPluginPage*>(item->data)))
         {
             return FALSE;
         }
@@ -1169,14 +1168,14 @@ gnc_main_window_all_finish_pending (void)
     windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME);
     for (item = windows; item; item = g_list_next(item))
     {
-        if (!gnc_main_window_finish_pending(item->data))
+        if (!gnc_main_window_finish_pending(static_cast<GncMainWindow*>(item->data)))
         {
             return FALSE;
         }
     }
     if (gnc_gui_refresh_suspended ())
     {
-        gnc_warning_dialog (NULL, "%s", "An operation is still running, wait for it to complete before quitting.");
+        gnc_warning_dialog (nullptr, "%s", "An operation is still running, wait for it to complete before quitting.");
         return FALSE;
     }
     return TRUE;
@@ -1202,7 +1201,7 @@ gnc_main_window_page_exists (GncPluginPage *page)
 
     for (walker = active_windows; walker; walker = g_list_next(walker))
     {
-        window = walker->data;
+        auto window{static_cast<GncMainWindow*>(walker->data)};
         priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
         if (g_list_find(priv->installed_pages, page))
         {
@@ -1215,7 +1214,7 @@ gnc_main_window_page_exists (GncPluginPage *page)
 static gboolean auto_save_countdown (GtkWidget *dialog)
 {
     GtkWidget *label;
-    gchar *timeoutstr = NULL;
+    gchar *timeoutstr = nullptr;
 
     /* Stop count down if user closed the dialog since the last time we were called */
     if (!GTK_IS_DIALOG (dialog))
@@ -1281,7 +1280,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window)
     filename = qof_session_get_url(session);
     if (!strlen (filename))
         filename = _("<unknown>");
-    if ((tmp = strrchr(filename, '/')) != NULL)
+    if ((tmp = strrchr(filename, '/')) != nullptr)
         filename = tmp + 1;
 
     /* Remove any pending auto-save timeouts */
@@ -1294,7 +1293,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window)
                                     title,
                                     filename);
     oldest_change = qof_book_get_session_dirty_time(book);
-    minutes = (gnc_time (NULL) - oldest_change) / 60 + 1;
+    minutes = (gnc_time (nullptr) - oldest_change) / 60 + 1;
     hours = minutes / 60;
     minutes = minutes % 60;
     days = hours / 24;
@@ -1320,7 +1319,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window)
                            _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
                            _("_Cancel"), GTK_RESPONSE_CANCEL,
                            _("_Save"), GTK_RESPONSE_APPLY,
-                           NULL);
+                           nullptr);
     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
 
     /* If requested by the user, add a timeout to the question to save automatically
@@ -1328,7 +1327,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window)
      */
     if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_EXPIRES))
     {
-        gchar *timeoutstr = NULL;
+        gchar *timeoutstr = nullptr;
 
         secs_to_save = gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_WAIT_TIME);
         timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
@@ -1338,7 +1337,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window)
 
         msg_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG(dialog));
         gtk_box_pack_end (GTK_BOX(msg_area), label, TRUE, TRUE, 0);
-        g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
+        g_object_set (G_OBJECT (label), "xalign", 0.0, nullptr);
 
         g_object_set_data (G_OBJECT (dialog), "count-down-label", label);
         g_timeout_add_seconds (1, (GSourceFunc)auto_save_countdown, dialog);
@@ -1428,21 +1427,21 @@ gnc_main_window_quit(GncMainWindow *window)
         for (w = active_windows; w; w = next)
         {
             GncMainWindowPrivate *priv;
-            GncMainWindow *wind = w->data;
+            GncMainWindow *window = static_cast<GncMainWindow*>(w->data);
 
             next = g_list_next (w);
 
-            wind->window_quitting = TRUE; // set window_quitting on all windows
+            window->window_quitting = TRUE; //set window_quitting on all windows
 
-            priv = GNC_MAIN_WINDOW_GET_PRIVATE(wind);
+            priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 
             // if there are no pages destroy window
             if (priv->installed_pages == NULL)
-                gtk_widget_destroy (GTK_WIDGET(wind));
+                gtk_widget_destroy (GTK_WIDGET(window));
         }
         /* remove the preference callbacks from the main window */
         gnc_main_window_remove_prefs (window);
-        g_timeout_add(250, gnc_main_window_timed_quit, NULL);
+        g_timeout_add(250, gnc_main_window_timed_quit, nullptr);
         return TRUE;
     }
     return FALSE;
@@ -1581,10 +1580,10 @@ gnc_main_window_generate_title (GncMainWindow *window)
     GncPluginPage *page;
     QofBook *book;
     gboolean immutable;
-    gchar *filename = NULL;
-    const gchar *uri = NULL;
+    gchar *filename = nullptr;
+    const gchar *uri = nullptr;
     const gchar *dirty = "";
-    const gchar *readonly_text = NULL;
+    const gchar *readonly_text = nullptr;
     gchar *readonly;
     gchar *title;
 
@@ -1601,7 +1600,7 @@ gnc_main_window_generate_title (GncMainWindow *window)
             readonly_text = _("(read-only)");
         }
     }
-    readonly = (readonly_text != NULL)
+    readonly = (readonly_text != nullptr)
                ? g_strdup_printf(" %s", readonly_text)
                : g_strdup("");
 
@@ -1677,7 +1676,7 @@ gnc_main_window_update_all_titles (void)
 {
     g_list_foreach(active_windows,
                    (GFunc)gnc_main_window_update_title,
-                   NULL);
+                   nullptr);
 }
 
 static void
@@ -1699,7 +1698,7 @@ gnc_main_window_attach_to_book (QofSession *session)
     g_return_if_fail(session);
 
     book = qof_session_get_book(session);
-    qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, NULL);
+    qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, nullptr);
     gnc_main_window_update_all_titles();
 #ifndef MAC_INTEGRATION
     gnc_main_window_update_all_menu_items();
@@ -1713,7 +1712,7 @@ static guint gnc_statusbar_notification_messageid = 0;
  * statusbar by generate_statusbar_lastmodified_message. */
 static gboolean statusbar_notification_off(gpointer user_data_unused)
 {
-    GncMainWindow *mainwindow = GNC_MAIN_WINDOW (gnc_ui_get_main_window (NULL));
+    GncMainWindow *mainwindow = GNC_MAIN_WINDOW (gnc_ui_get_main_window (nullptr));
     //g_warning("statusbar_notification_off\n");
     if (gnc_statusbar_notification_messageid == 0)
         return FALSE;
@@ -1736,8 +1735,8 @@ static gboolean statusbar_notification_off(gpointer user_data_unused)
  * data file. */
 static gchar *generate_statusbar_lastmodified_message()
 {
-    gchar *message = NULL;
-    const gchar *uri = NULL;
+    gchar *message = nullptr;
+    const gchar *uri = nullptr;
 
     if (gnc_current_session_exist())
     {
@@ -1745,7 +1744,7 @@ static gchar *generate_statusbar_lastmodified_message()
     }
 
     if (!(uri && strlen (uri)))
-        return NULL;
+        return nullptr;
     else
     {
         if (gnc_uri_targets_local_fs (uri))
@@ -1761,30 +1760,37 @@ static gchar *generate_statusbar_lastmodified_message()
 
             if (info && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
             {
-                guint64 modtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
-
-                /* Translators: This is the date and time that is shown in
-                the status bar after opening a file: The date and time of
-                last modification. The string is a format string using
-                boost::date_time's format flags, see the boost docs for an
-                explanation of the modifiers. */
-                char *time_string = gnc_print_time64 (modtime,
-                 _("Last modified on %a, %b %d, %Y at %I:%M %p"));
-                //g_warning("got time %ld, str=%s\n", mtime, time_string);
-                /* Translators: This message appears in the status bar after opening the file. */
-                message = g_strdup_printf(_("File %s opened. %s"),
-                                          filename, time_string);
-                free(time_string);
-            }
-            else
-            {
-                g_warning("Unable to read mtime for file %s\n", filepath);
-                // message is still NULL
+                // Access the mtime information through stat(2)
+                struct stat statbuf;
+                int r = stat(filepath, &statbuf);
+                if (r == 0)
+                {
+                    /* Translators: This is the date and time that is shown in
+                    the status bar after opening a file: The date and time of
+                    last modification. The string is a format string using
+                    boost::date_time's format flags, see the boost docs for an
+                    explanation of the modifiers. */
+                    char *time_string = gnc_print_time64(statbuf.st_mtime,
+                     _("Last modified on %a, %b %d, %Y at %I:%M %p"));
+                    //g_warning("got time %ld, str=%s\n", mtime, time_string);
+                    /* Translators: This message appears in the status bar after opening the file. */
+                    message = g_strdup_printf(_("File %s opened. %s"),
+                                              filename, time_string);
+                    free(time_string);
+                }
+                else
+                {
+                    g_warning("Unable to read mtime for file %s\n", filepath);
+                    // message is still nullptr
+                }
             }
             g_free(filename);
             g_free(filepath);
             g_object_unref (info);
             g_object_unref (file);
+#else
+            return nullptr;
+#endif
         }
         // If the URI is not a file but a database, we can maybe also show
         // something useful, but I have no idea how to obtain this information.
@@ -1797,11 +1803,11 @@ statusbar_notification_lastmodified()
 {
     // First look up the first GncMainWindow to set the statusbar there
     GList *iter;
-    GtkWidget *widget = NULL;
+    GtkWidget *widget = nullptr;
     for (iter = active_windows; iter && !(widget && GNC_IS_MAIN_WINDOW(widget));
             iter = g_list_next(iter))
     {
-        widget = iter->data;
+        widget = static_cast<GtkWidget*>(iter->data);
     }
     if (widget && GNC_IS_MAIN_WINDOW(widget))
     {
@@ -1819,7 +1825,7 @@ statusbar_notification_lastmodified()
 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
         // Also register a timeout callback to remove that statusbar
         // notification again after 10 seconds
-        g_timeout_add(10 * 1000, statusbar_notification_off, NULL); // maybe not needed anyway?
+        g_timeout_add(10 * 1000, statusbar_notification_off, nullptr); // maybe not needed anyway?
 #endif
     }
     else
@@ -1873,7 +1879,7 @@ gnc_main_window_update_one_menu_action (GncMainWindow *window,
         g_object_set(G_OBJECT(action),
                      "label", data->label,
                      "visible", data->visible,
-                     (char *)NULL);
+                     (char *)nullptr);
     LEAVE(" ");
 }
 
@@ -2008,10 +2014,10 @@ gnc_main_window_update_all_menu_items (void)
     /* First update the entries for all existing windows */
     g_list_foreach(active_windows,
                    (GFunc)gnc_main_window_update_menu_item,
-                   NULL);
+                   nullptr);
     g_list_foreach(active_windows,
                    (GFunc)gnc_main_window_update_radio_button,
-                   NULL);
+                   nullptr);
 
     /* Now hide any entries that aren't being used. */
     data.visible = FALSE;
@@ -2047,11 +2053,9 @@ static void
 gnc_main_window_update_tab_close_one_page (GncPluginPage *page,
         gpointer user_data)
 {
-    gboolean *new_value = user_data;
-    GtkWidget * close_button;
-
+    auto new_value{static_cast<gboolean*>(user_data)};
     ENTER("page %p, visible %d", page, *new_value);
-    close_button = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON);
+    auto close_button{static_cast<GtkWidget*>(g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON))};
     if (!close_button)
     {
         LEAVE("no close button");
@@ -2126,13 +2130,10 @@ gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
 static void
 gnc_main_window_update_tab_color (gpointer gsettings, gchar *pref, gpointer user_data)
 {
-    GncMainWindowPrivate *priv;
-    GncMainWindow        *window;
-
     ENTER(" ");
     g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
-    window = user_data;
-    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
+    auto window{static_cast<GncMainWindow*>(user_data)};
+    auto priv{GNC_MAIN_WINDOW_GET_PRIVATE(window)};
     if (g_strcmp0 (GNC_PREF_TAB_COLOR, pref) == 0)
         priv->show_color_tabs = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
     gnc_main_window_foreach_page (gnc_main_window_update_tab_color_one_page, window);
@@ -2192,11 +2193,10 @@ static void
 gnc_main_window_update_tab_width_one_page (GncPluginPage *page,
         gpointer user_data)
 {
-    gint *new_value = user_data;
-    GtkWidget *label;
+    auto new_value{static_cast<int*>(user_data)};
 
     ENTER("page %p, visible %d", page, *new_value);
-    label = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL);
+    auto label{static_cast<GtkWidget*>(g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL))};
     if (!label)
     {
         LEAVE("no label");
@@ -2249,7 +2249,7 @@ main_window_find_tab_items (GncMainWindow *window,
     ENTER("window %p, page %p, label_p %p, entry_p %p",
           window, page, label_p, entry_p);
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    *label_p = *entry_p = NULL;
+    *label_p = *entry_p = nullptr;
 
     if (!page->notebook_page)
     {
@@ -2272,7 +2272,7 @@ main_window_find_tab_items (GncMainWindow *window,
     children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
     for (tmp = children; tmp; tmp = g_list_next(tmp))
     {
-        widget = tmp->data;
+        widget = static_cast<GtkWidget*>(tmp->data);
         if (GTK_IS_LABEL(widget))
         {
             *label_p = widget;
@@ -2297,7 +2297,7 @@ main_window_find_tab_widget (GncMainWindow *window,
 
     ENTER("window %p, page %p, widget %p",
           window, page, widget_p);
-    *widget_p = NULL;
+    *widget_p = nullptr;
 
     if (!page->notebook_page)
     {
@@ -2325,7 +2325,7 @@ main_window_update_page_name (GncPluginPage *page,
 
     ENTER(" ");
 
-    if ((name_in == NULL) || (*name_in == '\0'))
+    if ((name_in == nullptr) || (*name_in == '\0'))
     {
         LEAVE("no string");
         return;
@@ -2366,14 +2366,14 @@ main_window_update_page_name (GncPluginPage *page,
 
     /* Update Tooltip on notebook Tab */
     if (old_page_long_name && old_page_name
-            && g_strrstr(old_page_long_name, old_page_name) != NULL)
+            && g_strrstr(old_page_long_name, old_page_name) != nullptr)
     {
         gchar *new_page_long_name;
         gint string_position;
         GtkWidget *tab_widget;
 
         string_position = strlen(old_page_long_name) - strlen(old_page_name);
-        new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
+        new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, nullptr);
 
         gnc_plugin_page_set_page_long_name(page, new_page_long_name);
 
@@ -2409,7 +2409,7 @@ main_window_update_page_color (GncPluginPage *page,
     GncMainWindowPrivate *priv;
     GtkWidget *tab_widget;
     GdkRGBA tab_color;
-    gchar *color_string = NULL;
+    gchar *color_string = nullptr;
     gboolean want_color = FALSE;
 
     ENTER(" ");
@@ -2424,7 +2424,7 @@ main_window_update_page_color (GncPluginPage *page,
     if (want_color)
         gnc_plugin_page_set_page_color(page, color_string);
     else
-        gnc_plugin_page_set_page_color(page, NULL);
+        gnc_plugin_page_set_page_color(page, nullptr);
 
     /* Update the notebook tab */
     main_window_find_tab_widget (window, page, &tab_widget);
@@ -2449,9 +2449,9 @@ main_window_update_page_color (GncPluginPage *page,
 
         stylectxt = gtk_widget_get_style_context (GTK_WIDGET (tab_widget));
         col_str = gdk_rgba_to_string (&tab_color);
-        widget_css = g_strconcat ("*{\n  background-color:", col_str, ";\n}\n", NULL);
+        widget_css = g_strconcat ("*{\n  background-color:", col_str, ";\n}\n", nullptr);
 
-        gtk_css_provider_load_from_data (provider, widget_css, -1, NULL);
+        gtk_css_provider_load_from_data (provider, widget_css, -1, nullptr);
         gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER (provider),
                                         GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
         g_object_unref (provider);
@@ -2509,7 +2509,7 @@ main_window_update_page_set_read_only_icon (GncPluginPage *page,
     for (children = gtk_container_get_children (GTK_CONTAINER(tab_widget));
             children; children = children->next)
     {
-        GtkWidget *widget = children->data;
+        GtkWidget *widget = static_cast<GtkWidget*>(children->data);
         if (GTK_IS_IMAGE(widget))
             image = widget;
     }
@@ -2639,7 +2639,7 @@ gnc_main_window_class_init (GncMainWindowClass *klass)
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
     GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS(klass);
 
-    parent_class = g_type_class_peek_parent (klass);
+    parent_class = static_cast<GObjectClass*>(g_type_class_peek_parent (klass));
 
     window_type = g_quark_from_static_string ("gnc-main-window");
 
@@ -2664,7 +2664,7 @@ gnc_main_window_class_init (GncMainWindowClass *klass)
                       G_OBJECT_CLASS_TYPE (object_class),
                       G_SIGNAL_RUN_FIRST,
                       G_STRUCT_OFFSET (GncMainWindowClass, page_added),
-                      NULL, NULL,
+                      nullptr, nullptr,
                       g_cclosure_marshal_VOID__OBJECT,
                       G_TYPE_NONE, 1,
                       G_TYPE_OBJECT);
@@ -2684,24 +2684,24 @@ gnc_main_window_class_init (GncMainWindowClass *klass)
                       G_OBJECT_CLASS_TYPE (object_class),
                       G_SIGNAL_RUN_FIRST,
                       G_STRUCT_OFFSET (GncMainWindowClass, page_changed),
-                      NULL, NULL,
+                      nullptr, nullptr,
                       g_cclosure_marshal_VOID__OBJECT,
                       G_TYPE_NONE, 1,
                       G_TYPE_OBJECT);
 
     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
                            GNC_PREF_SHOW_CLOSE_BUTTON,
-                           gnc_main_window_update_tab_close,
-                           NULL);
+                           (gpointer)gnc_main_window_update_tab_close,
+                           nullptr);
     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
                            GNC_PREF_TAB_WIDTH,
-                           gnc_main_window_update_tab_width,
-                           NULL);
+                           (gpointer)gnc_main_window_update_tab_width,
+                           nullptr);
 
     gnc_hook_add_dangler(HOOK_BOOK_SAVED,
-                         (GFunc)gnc_main_window_update_all_titles, NULL, NULL);
+                         (GFunc)gnc_main_window_update_all_titles, nullptr, nullptr);
     gnc_hook_add_dangler(HOOK_BOOK_OPENED,
-                         (GFunc)gnc_main_window_attach_to_book, NULL, NULL);
+                         (GFunc)gnc_main_window_attach_to_book, nullptr, nullptr);
 
 }
 
@@ -2738,7 +2738,7 @@ gnc_main_window_init (GncMainWindow *window, void *data)
 
     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
                            GNC_PREF_TAB_COLOR,
-                           gnc_main_window_update_tab_color,
+                           (gpointer)gnc_main_window_update_tab_color,
                            window);
 
     gnc_main_window_setup_window (window);
@@ -2760,10 +2760,10 @@ gnc_main_window_init (GncMainWindow *window, void *data)
 static void
 gnc_main_window_finalize (GObject *object)
 {
-    g_return_if_fail (object != NULL);
+    g_return_if_fail (object != nullptr);
     g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
 
-    if (active_windows == NULL)
+    if (active_windows == nullptr)
     {
         /* Oops. User killed last window and we didn't catch it. */
         g_idle_add((GSourceFunc)gnc_shutdown, 0);
@@ -2780,37 +2780,37 @@ gnc_main_window_remove_prefs (GncMainWindow *window)
     // remove the registered preference callbacks setup in this file.
     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
                                  GNC_PREF_TAB_COLOR,
-                                 gnc_main_window_update_tab_color,
+                                 (gpointer)gnc_main_window_update_tab_color,
                                  window);
 
     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
                                  GNC_PREF_SHOW_CLOSE_BUTTON,
-                                 gnc_main_window_update_tab_close,
-                                 NULL);
+                                 (gpointer)gnc_main_window_update_tab_close,
+                                 nullptr);
 
     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
                                  GNC_PREF_TAB_WIDTH,
-                                 gnc_main_window_update_tab_width,
-                                 NULL);
+                                 (gpointer)gnc_main_window_update_tab_width,
+                                 nullptr);
 
     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
                                  GNC_PREF_TAB_POSITION_TOP,
-                                 gnc_main_window_update_tab_position,
+                                 (gpointer)gnc_main_window_update_tab_position,
                                  window);
 
     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
                                  GNC_PREF_TAB_POSITION_BOTTOM,
-                                 gnc_main_window_update_tab_position,
+                                 (gpointer)gnc_main_window_update_tab_position,
                                  window);
 
     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
                                  GNC_PREF_TAB_POSITION_LEFT,
-                                 gnc_main_window_update_tab_position,
+                                 (gpointer)gnc_main_window_update_tab_position,
                                  window);
 
     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
                                  GNC_PREF_TAB_POSITION_RIGHT,
-                                 gnc_main_window_update_tab_position,
+                                 (gpointer)gnc_main_window_update_tab_position,
                                  window);
 
     // remove the registered negative color preference callback.
@@ -2839,7 +2839,7 @@ gnc_main_window_destroy (GtkWidget *widget)
     GncPluginManager *manager;
     GList *plugins;
 
-    g_return_if_fail (widget != NULL);
+    g_return_if_fail (widget != nullptr);
     g_return_if_fail (GNC_IS_MAIN_WINDOW (widget));
 
     window = GNC_MAIN_WINDOW (widget);
@@ -2856,7 +2856,7 @@ gnc_main_window_destroy (GtkWidget *widget)
             gnc_main_window_close_page(priv->current_page);
 
         if (gnc_window_get_progressbar_window() == GNC_WINDOW(window))
-            gnc_window_set_progressbar_window(NULL);
+            gnc_window_set_progressbar_window(nullptr);
 #ifndef MAC_INTEGRATION
         /* Update the "Windows" menu in all other windows */
         gnc_main_window_update_all_menu_items();
@@ -2868,7 +2868,7 @@ gnc_main_window_destroy (GtkWidget *widget)
         priv->event_handler_id = 0;
 
         g_hash_table_destroy (priv->merged_actions_table);
-        priv->merged_actions_table = NULL;
+        priv->merged_actions_table = nullptr;
 
         /* GncPluginManager stuff */
         manager = gnc_plugin_manager_get ();
@@ -2937,13 +2937,10 @@ gnc_main_window_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer
 GncMainWindow *
 gnc_main_window_new (void)
 {
-    GncMainWindow *window;
-    GtkWindow *old_window;
-
-    window = g_object_new (GNC_TYPE_MAIN_WINDOW, NULL);
+    auto window{static_cast<GncMainWindow*>(g_object_new (GNC_TYPE_MAIN_WINDOW, nullptr))};
     gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
 
-    old_window = gnc_ui_get_main_window (NULL);
+    auto old_window = gnc_ui_get_main_window (nullptr);
     if (old_window)
     {
         gint width, height;
@@ -3075,9 +3072,9 @@ gnc_main_window_disconnect (GncMainWindow *window,
 
     /* Disconnect the callbacks */
     g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
-                                         G_CALLBACK(gnc_main_window_popup_menu_cb), page);
+                                         (gpointer)gnc_main_window_popup_menu_cb, page);
     g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
-                                         G_CALLBACK(gnc_main_window_button_press_cb), page);
+                                         (gpointer)gnc_main_window_button_press_cb, page);
 
     // Remove the page_changed signal callback
     gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
@@ -3088,7 +3085,7 @@ gnc_main_window_disconnect (GncMainWindow *window,
     {
         gnc_plugin_page_unmerge_actions (page, window->ui_merge);
         gnc_plugin_page_unselected (page);
-        priv->current_page = NULL;
+        priv->current_page = nullptr;
     }
 
     /* Remove it from the list of pages in the window */
@@ -3099,10 +3096,10 @@ gnc_main_window_disconnect (GncMainWindow *window,
     notebook = GTK_NOTEBOOK (priv->notebook);
     if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_NEXT_RECENT))
     {
-        new_page = g_list_nth_data (priv->usage_order, 0);
+        new_page = static_cast<GncPluginPage*>(g_list_nth_data (priv->usage_order, 0));
         if (new_page)
         {
-            page_num =  gtk_notebook_page_num(notebook, new_page->notebook_page);
+            page_num = gtk_notebook_page_num(notebook, new_page->notebook_page);
             gtk_notebook_set_current_page(notebook, page_num);
             /* This may have caused WebKit to schedule  a timer interrupt which it
                sometimes  forgets to cancel before deleting the object.  See
@@ -3123,14 +3120,14 @@ gnc_main_window_disconnect (GncMainWindow *window,
          * page is removed.  The notebook doesn't generate a signal
          * for this, therefore the switch_page code in this file
          * never gets called to generate this signal. */
-        gnc_main_window_switch_page(notebook, NULL, -1, window);
-        //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, NULL);
+        gnc_main_window_switch_page(notebook, nullptr, -1, window);
+        //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, nullptr);
     }
 
     gnc_plugin_page_removed (page);
 
     gtk_ui_manager_ensure_update (window->ui_merge);
-    gnc_window_set_status (GNC_WINDOW(window), page, NULL);
+    gnc_window_set_status (GNC_WINDOW(window), page, nullptr);
 }
 
 
@@ -3160,7 +3157,7 @@ gnc_main_window_display_page (GncPluginPage *page)
  *  exists in any window, then that window will be brought to the
  *  front and the notebook switch to display the specified page.  If
  *  the page is new then it will be added to the specified window.  If
- *  the window is NULL, the new page will be added to the first
+ *  the window is nullptr, the new page will be added to the first
  *  window.
  */
 void
@@ -3195,18 +3192,18 @@ gnc_main_window_open_page (GncMainWindow *window,
         {
             window = GNC_MAIN_WINDOW(tmp->data);
             priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-            if (priv->installed_pages == NULL)
+            if (priv->installed_pages == nullptr)
             {
                 break;
             }
         }
-        if (tmp == NULL)
+        if (tmp == nullptr)
             window = gnc_main_window_new ();
         gtk_widget_show(GTK_WIDGET(window));
     }
-    else if ((window == NULL) && active_windows)
+    else if ((window == nullptr) && active_windows)
     {
-        window = active_windows->data;
+        window = static_cast<GncMainWindow*>(active_windows->data);
     }
 
     page->window = GTK_WIDGET(window);
@@ -3235,7 +3232,7 @@ gnc_main_window_open_page (GncMainWindow *window,
     gtk_box_set_homogeneous (GTK_BOX (tab_hbox), FALSE);
     gtk_widget_show (tab_hbox);
 
-    if (icon != NULL)
+    if (icon != nullptr)
     {
         image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
         gtk_widget_show (image);
@@ -3277,7 +3274,7 @@ gnc_main_window_open_page (GncMainWindow *window,
         gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
         close_image = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU);
         gtk_widget_show(close_image);
-        gtk_widget_get_preferred_size (close_image, &requisition, NULL);
+        gtk_widget_get_preferred_size (close_image, &requisition, nullptr);
         gtk_widget_set_size_request(close_button, requisition.width + 4,
                                     requisition.height + 2);
         gtk_container_add(GTK_CONTAINER(close_button), close_image);
@@ -3343,7 +3340,7 @@ gnc_main_window_close_page (GncPluginPage *page)
 
     /* If this isn't the last window, go ahead and destroy the window. */
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    if (priv->installed_pages == NULL)
+    if (priv->installed_pages == nullptr)
     {
         if (window->window_quitting)
         {
@@ -3397,7 +3394,7 @@ gnc_main_window_manual_merge_actions (GncMainWindow *window,
     MergedActionEntry *entry;
 
     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
-    g_return_if_fail (group_name != NULL);
+    g_return_if_fail (group_name != nullptr);
     g_return_if_fail (GTK_IS_ACTION_GROUP(group));
     g_return_if_fail (merge_id > 0);
 
@@ -3428,18 +3425,17 @@ gnc_main_window_merge_actions (GncMainWindow *window,
 {
     GncMainWindowPrivate *priv;
     GncMainWindowActionData *data;
-    MergedActionEntry *entry;
-    GError *error = NULL;
+    GError *error = nullptr;
     gchar *pathname;
 
     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
-    g_return_if_fail (group_name != NULL);
-    g_return_if_fail (actions != NULL);
+    g_return_if_fail (group_name != nullptr);
+    g_return_if_fail (actions != nullptr);
     g_return_if_fail (n_actions > 0);
-    g_return_if_fail (filename != NULL);
+    g_return_if_fail (filename != nullptr);
 
     pathname = gnc_filepath_locate_ui_file (filename);
-    if (pathname == NULL)
+    if (pathname == nullptr)
         return;
 
     data = g_new0 (GncMainWindowActionData, 1);
@@ -3447,11 +3443,11 @@ gnc_main_window_merge_actions (GncMainWindow *window,
     data->data = user_data;
 
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    entry = g_new0 (MergedActionEntry, 1);
+    auto entry{static_cast<MergedActionEntry*>(g_new0 (MergedActionEntry, 1))};
     entry->action_group = gtk_action_group_new (group_name);
     gtk_action_group_set_translation_domain (entry->action_group, PROJECT_NAME);
     gtk_action_group_add_actions (entry->action_group, actions, n_actions, data);
-    if (toggle_actions != NULL && n_toggle_actions > 0)
+    if (toggle_actions != nullptr && n_toggle_actions > 0)
     {
         gtk_action_group_add_toggle_actions (entry->action_group,
                                              toggle_actions, n_toggle_actions,
@@ -3486,17 +3482,16 @@ gnc_main_window_unmerge_actions (GncMainWindow *window,
                                  const gchar *group_name)
 {
     GncMainWindowPrivate *priv;
-    MergedActionEntry *entry;
 
     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
-    g_return_if_fail (group_name != NULL);
+    g_return_if_fail (group_name != nullptr);
 
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    if (priv->merged_actions_table == NULL)
+    if (priv->merged_actions_table == nullptr)
         return;
-    entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
+    auto entry{static_cast<MergedActionEntry *>(g_hash_table_lookup (priv->merged_actions_table, group_name))};
 
-    if (entry == NULL)
+    if (entry == nullptr)
         return;
 
     gtk_ui_manager_remove_action_group (window->ui_merge, entry->action_group);
@@ -3534,7 +3529,7 @@ gnc_main_window_actions_updated (GncMainWindow *window)
 GtkAction *
 gnc_main_window_find_action (GncMainWindow *window, const gchar *name)
 {
-    GtkAction *action = NULL;
+    GtkAction *action = nullptr;
     const GList *groups, *tmp;
 
     groups = gtk_ui_manager_get_action_groups(window->ui_merge);
@@ -3557,18 +3552,17 @@ gnc_main_window_get_action_group (GncMainWindow *window,
                                   const gchar *group_name)
 {
     GncMainWindowPrivate *priv;
-    MergedActionEntry *entry;
 
-    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
-    g_return_val_if_fail (group_name != NULL, NULL);
+    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), nullptr);
+    g_return_val_if_fail (group_name != nullptr, nullptr);
 
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    if (priv->merged_actions_table == NULL)
-        return NULL;
-    entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
+    if (priv->merged_actions_table == nullptr)
+        return nullptr;
+    auto entry{static_cast<MergedActionEntry *>(g_hash_table_lookup (priv->merged_actions_table, group_name))};
 
-    if (entry == NULL)
-        return NULL;
+    if (entry == nullptr)
+        return nullptr;
 
     return entry->action_group;
 }
@@ -3623,7 +3617,7 @@ gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean
         gboolean has_selection;
 
         has_selection = gtk_editable_get_selection_bounds
-                        (GTK_EDITABLE (widget), NULL, NULL);
+                        (GTK_EDITABLE (widget), nullptr, nullptr);
 
         can_copy = has_selection;
         can_cut = has_selection;
@@ -3636,7 +3630,7 @@ gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean
 
         text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
         has_selection = gtk_text_buffer_get_selection_bounds
-                        (text_buffer, NULL, NULL);
+                        (text_buffer, nullptr, nullptr);
 
         can_copy = has_selection;
         can_cut = has_selection;
@@ -3720,7 +3714,7 @@ gnc_main_window_window_menu (GncMainWindow *window)
     gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui.xml");
     GncMainWindowPrivate *priv;
 #endif
-    GError *error = NULL;
+    GError *error = nullptr;
     g_assert(filename);
     merge_id = gtk_ui_manager_add_ui_from_file(window->ui_merge, filename,
                &error);
@@ -3741,7 +3735,7 @@ static gboolean
 gnc_main_window_page_focus_in (GtkWidget *widget, GdkEvent  *event,
                                gpointer user_data)
 {
-    GncMainWindow *window = user_data;
+    auto window{static_cast<GncMainWindow *>(user_data)};
     GncPluginPage *page = gnc_main_window_get_current_page (window);
 
     g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
@@ -3756,7 +3750,7 @@ gnc_main_window_setup_window (GncMainWindow *window)
     guint merge_id;
     GncPluginManager *manager;
     GList *plugins;
-    GError *error = NULL;
+    GError *error = nullptr;
     gchar *filename;
 
     ENTER(" ");
@@ -3782,7 +3776,7 @@ gnc_main_window_setup_window (GncMainWindow *window)
     g_object_set(G_OBJECT(priv->notebook),
                  "scrollable", TRUE,
                  "enable-popup", TRUE,
-                 (char *)NULL);
+                 (char *)nullptr);
     gtk_widget_show (priv->notebook);
     g_signal_connect (G_OBJECT (priv->notebook), "switch-page",
                       G_CALLBACK (gnc_main_window_switch_page), window);
@@ -3862,21 +3856,21 @@ gnc_main_window_setup_window (GncMainWindow *window)
     gnc_main_window_window_menu(window);
     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
                            GNC_PREF_TAB_POSITION_TOP,
-                           gnc_main_window_update_tab_position,
+                           (gpointer)gnc_main_window_update_tab_position,
                            window);
     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
                            GNC_PREF_TAB_POSITION_BOTTOM,
-                           gnc_main_window_update_tab_position,
+                           (gpointer)gnc_main_window_update_tab_position,
                            window);
     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
                            GNC_PREF_TAB_POSITION_LEFT,
-                           gnc_main_window_update_tab_position,
+                           (gpointer)gnc_main_window_update_tab_position,
                            window);
     gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
                            GNC_PREF_TAB_POSITION_RIGHT,
-                           gnc_main_window_update_tab_position,
+                           (gpointer)gnc_main_window_update_tab_position,
                            window);
-    gnc_main_window_update_tab_position(NULL, NULL, window);
+    gnc_main_window_update_tab_position(nullptr, nullptr, window);
 
     gnc_main_window_init_menu_updaters(window);
 
@@ -3935,7 +3929,7 @@ gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window)
 static void
 gnc_quartz_set_menu(GncMainWindow* window)
 {
-    GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
+    auto theApp{static_cast<GtkosxApplication *>(g_object_new(GTKOSX_TYPE_APPLICATION, nullptr))};
     GtkWidget       *menu;
     GtkWidget       *item;
 
@@ -4010,7 +4004,7 @@ gnc_main_window_add_widget (GtkUIManager *merge,
  *  @param window A pointer to the window in question.
  *
  *  @param action If known, a pointer to the "ViewSummaryBar"
- *  GtkToggleAction.  If NULL, the function will look up this action.
+ *  GtkToggleAction.  If nullptr, the function will look up this action.
  *
  *  @return TRUE if the summarybar should be visible.
  */
@@ -4020,10 +4014,10 @@ gnc_main_window_show_summarybar (GncMainWindow *window, GtkAction *action)
     GncMainWindowPrivate *priv;
 
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    if (action == NULL)
+    if (action == nullptr)
         action = gtk_action_group_get_action(priv->action_group,
                                              "ViewSummaryAction");
-    if (action == NULL)
+    if (action == nullptr)
         return TRUE;
     return gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
 }
@@ -4053,7 +4047,7 @@ gnc_main_window_switch_page (GtkNotebook *notebook,
     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    if (priv->current_page != NULL)
+    if (priv->current_page != nullptr)
     {
         page = priv->current_page;
         gnc_plugin_page_unmerge_actions (page, window->ui_merge);
@@ -4063,20 +4057,20 @@ gnc_main_window_switch_page (GtkNotebook *notebook,
     child = gtk_notebook_get_nth_page (notebook, pos);
     if (child)
     {
-        page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
+        page = static_cast<GncPluginPage*>(g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL));
     }
     else
     {
-        page = NULL;
+        page = nullptr;
     }
 
     priv->current_page = page;
 
-    if (page != NULL)
+    if (page != nullptr)
     {
         /* Update the user interface (e.g. menus and toolbars */
         gnc_plugin_page_merge_actions (page, window->ui_merge);
-        visible = gnc_main_window_show_summarybar(window, NULL);
+        visible = gnc_main_window_show_summarybar(window, nullptr);
         gnc_plugin_page_show_summarybar (page, visible);
 
         /* Allow page specific actions */
@@ -4125,7 +4119,7 @@ gnc_main_window_page_reordered (GtkNotebook *notebook,
 
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 
-    page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
+    page = static_cast<GncPluginPage*>(g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL));
     if (!page) return;
 
     old_link = g_list_find (priv->installed_pages, page);
@@ -4186,15 +4180,15 @@ gnc_book_options_dialog_apply_helper(GncOptionDB * options)
     gboolean use_split_action_for_num_after;
     gint use_read_only_threshold_after;
     gboolean return_val = FALSE;
-    GList *results = NULL, *iter;
+    GList *results = nullptr, *iter;
 
     if (!options) return return_val;
 
     results = gnc_option_db_commit (options);
     for (iter = results; iter; iter = iter->next)
     {
-        GtkWidget *dialog = gtk_message_dialog_new(gnc_ui_get_main_window (NULL),
-                                                   0,
+        GtkWidget *dialog = gtk_message_dialog_new(gnc_ui_get_main_window (nullptr),
+                                                   (GtkDialogFlags)0,
                                                    GTK_MESSAGE_ERROR,
                                                    GTK_BUTTONS_OK,
                                                    "%s",
@@ -4230,7 +4224,7 @@ static void
 gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin,
                                  gpointer user_data)
 {
-    GncOptionDB * options = user_data;
+    auto options{static_cast<GncOptionDB *>(user_data)};
 
     if (!options) return;
 
@@ -4242,7 +4236,7 @@ static void
 gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin,
                                  gpointer user_data)
 {
-    GncOptionDB * options = user_data;
+    auto options{static_cast<GncOptionDB *>(user_data)};
 
     gnc_options_dialog_destroy(optionwin);
     gnc_option_db_destroy(options);
@@ -4271,7 +4265,7 @@ static gboolean
 show_handler (const char *class_name, gint component_id,
               gpointer user_data, gpointer iter_data)
 {
-    GNCOptionWin *optwin = user_data;
+    auto optwin{static_cast<GNCOptionWin*>(user_data)};
     GtkWidget *widget;
 
     if (!optwin)
@@ -4297,9 +4291,9 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
     /* Only allow one Book Options dialog if called from file->properties
        menu */
     if (gnc_forall_gui_components(DIALOG_BOOK_OPTIONS_CM_CLASS,
-                                  show_handler, NULL))
+                                  show_handler, nullptr))
     {
-        return NULL;
+        return nullptr;
     }
     optionwin = gnc_options_dialog_new_modal (
         modal,
@@ -4323,7 +4317,7 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
 static void
 gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window)
 {
-    gnc_book_options_dialog_cb (FALSE, NULL, GTK_WINDOW (window));
+    gnc_book_options_dialog_cb (FALSE, nullptr, GTK_WINDOW (window));
 }
 
 static void
@@ -4399,13 +4393,15 @@ gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window)
     }
     else if (GTK_IS_TEXT_VIEW(widget))
     {
-        GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
-        GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
-                                                            GDK_SELECTION_CLIPBOARD);
-        gboolean editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget));
-
+        auto text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
+        auto clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
+                                                   GDK_SELECTION_CLIPBOARD);
         if (clipboard)
-            gtk_text_buffer_paste_clipboard (text_buffer, clipboard, NULL, editable);
+        {
+            auto editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget));
+            gtk_text_buffer_paste_clipboard (text_buffer, clipboard, nullptr,
+                                             editable);
+        }
     }
 }
 
@@ -4483,7 +4479,8 @@ gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window)
     visible = gnc_main_window_show_summarybar(window, action);
     for (item = priv->installed_pages; item; item = g_list_next(item))
     {
-        gnc_plugin_page_show_summarybar(item->data, visible);
+        gnc_plugin_page_show_summarybar(static_cast<GncPluginPage*>(item->data),
+                                        visible);
     }
 }
 
@@ -4618,17 +4615,17 @@ gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window)
  *  @param partial The name of the file relative to the gnucash
  *  specific shared data directory.
  *
- *  @return The text of the file or NULL. The caller is responsible
+ *  @return The text of the file or nullptr. The caller is responsible
  *  for freeing this string.
  */
 static gchar *
 get_file (const gchar *partial)
 {
-    gchar *filename, *text = NULL;
+    gchar *filename, *text = nullptr;
     gsize length;
 
     filename = gnc_filepath_locate_doc_file(partial);
-    if (filename && g_file_get_contents(filename, &text, &length, NULL))
+    if (filename && g_file_get_contents(filename, &text, &length, nullptr))
     {
         if (length)
         {
@@ -4638,7 +4635,7 @@ get_file (const gchar *partial)
         g_free(text);
     }
     g_free (filename);
-    return NULL;
+    return nullptr;
 }
 
 
@@ -4648,7 +4645,7 @@ get_file (const gchar *partial)
  *  @param partial The name of the file relative to the gnucash
  *  specific shared data directory.
  *
- *  @return The text of the file as an array of strings, or NULL. The
+ *  @return The text of the file as an array of strings, or nullptr. The
  *  caller is responsible for freeing all the strings and the array.
  */
 static gchar **
@@ -4658,7 +4655,7 @@ get_file_strsplit (const gchar *partial)
 
     text = get_file(partial);
     if (!text)
-        return NULL;
+        return nullptr;
 
     lines = g_strsplit_set(text, "\r\n", -1);
     g_free(text);
@@ -4697,7 +4694,7 @@ gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
                                                 GNC_ICON_APP,
                                                 128,
                                                 GTK_ICON_LOOKUP_USE_BUILTIN,
-                                                NULL);
+                                                nullptr);
     gchar *version = g_strdup_printf ("%s: %s\n%s: %s\nFinance::Quote: %s",
                                       _("Version"), gnc_version(),
                                       _("Build ID"), gnc_build_id(),
@@ -4721,7 +4718,7 @@ gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
                   "version", version,
                   "website", PACKAGE_URL,
                   "website-label", _("Visit the GnuCash website."),
-                  NULL);
+                  nullptr);
 
     g_free(version);
     g_free(copyright);
@@ -4733,7 +4730,7 @@ gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
         g_strfreev(authors);
     g_object_unref (logo);
     g_signal_connect (dialog, "activate-link",
-                      G_CALLBACK (url_signal_cb), NULL);
+                      G_CALLBACK (url_signal_cb), nullptr);
     /* Set dialog to resize. */
     gtk_window_set_resizable(GTK_WINDOW (dialog), TRUE);
 
@@ -4753,15 +4750,15 @@ gnc_main_window_show_all_windows(void)
 {
     GList *window_iter;
 #ifdef MAC_INTEGRATION
-    GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
+    auto theApp{static_cast<GtkosxApplication *>(g_object_new(GTKOSX_TYPE_APPLICATION, nullptr))};
 #endif
-    for (window_iter = active_windows; window_iter != NULL; window_iter = window_iter->next)
+    for (window_iter = active_windows; window_iter != nullptr; window_iter = window_iter->next)
     {
         gtk_widget_show(GTK_WIDGET(window_iter->data));
     }
 #ifdef MAC_INTEGRATION
     g_signal_connect(theApp, "NSApplicationWillTerminate",
-                     G_CALLBACK(gnc_quartz_shutdown), NULL);
+                     G_CALLBACK(gnc_quartz_shutdown), nullptr);
     gtkosx_application_ready(theApp);
     g_object_unref (theApp);
 #endif
@@ -4773,13 +4770,13 @@ gnc_ui_get_gtk_window (GtkWidget *widget)
     GtkWidget *toplevel;
 
     if (!widget)
-        return NULL;
+        return nullptr;
 
     toplevel = gtk_widget_get_toplevel (widget);
     if (toplevel && GTK_IS_WINDOW (toplevel))
         return GTK_WINDOW (toplevel);
     else
-        return NULL;
+        return nullptr;
 }
 
 GtkWindow *
@@ -4796,13 +4793,13 @@ gnc_ui_get_main_window (GtkWidget *widget)
 
     for (window = active_windows; window; window = window->next)
         if (gtk_window_is_active (GTK_WINDOW (window->data)))
-            return window->data;
+            return static_cast<GtkWindow*>(window->data);
 
     for (window = active_windows; window; window = window->next)
         if (gtk_widget_get_mapped (GTK_WIDGET(window->data)))
-            return window->data;
+            return static_cast<GtkWindow*>(window->data);
 
-    return NULL;
+    return nullptr;
 }
 
 
@@ -4814,7 +4811,7 @@ gnc_ui_get_main_window (GtkWidget *widget)
 static GtkWindow *
 gnc_main_window_get_gtk_window (GncWindow *window)
 {
-    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
+    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), nullptr);
     return GTK_WINDOW(window);
 }
 
@@ -4830,7 +4827,7 @@ gnc_main_window_get_statusbar (GncWindow *window_in)
     GncMainWindowPrivate *priv;
     GncMainWindow *window;
 
-    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
+    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), nullptr);
 
     window = GNC_MAIN_WINDOW(window_in);
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
@@ -4849,7 +4846,7 @@ gnc_main_window_get_progressbar (GncWindow *window_in)
     GncMainWindowPrivate *priv;
     GncMainWindow *window;
 
-    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
+    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), nullptr);
 
     window = GNC_MAIN_WINDOW(window_in);
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
@@ -4860,25 +4857,21 @@ gnc_main_window_get_progressbar (GncWindow *window_in)
 static void
 gnc_main_window_all_ui_set_sensitive (GncWindow *unused, gboolean sensitive)
 {
-    GncMainWindow *window;
-    GncMainWindowPrivate *priv;
-    GList *groupp, *groups, *winp, *tmp;
-    GtkWidget *close_button;
 
-    for (winp = active_windows; winp; winp = g_list_next(winp))
+    for (auto winp = active_windows; winp; winp = g_list_next(winp))
     {
-        window = winp->data;
-        priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
+        auto window{static_cast<GncMainWindow*>(winp->data)};
+        auto priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 
-        groups = gtk_ui_manager_get_action_groups(window->ui_merge);
-        for (groupp = groups; groupp; groupp = g_list_next(groupp))
+        auto groups = gtk_ui_manager_get_action_groups(window->ui_merge);
+        for (auto groupp = groups; groupp; groupp = g_list_next(groupp))
         {
             gtk_action_group_set_sensitive(GTK_ACTION_GROUP(groupp->data), sensitive);
         }
 
-        for (tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp))
+        for (auto tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp))
         {
-            close_button = g_object_get_data(tmp->data, PLUGIN_PAGE_CLOSE_BUTTON);
+            auto close_button{static_cast<GtkWidget*>(g_object_get_data(static_cast<GObject*>(tmp->data), PLUGIN_PAGE_CLOSE_BUTTON))};
             if (!close_button)
                 continue;
             gtk_widget_set_sensitive (close_button, sensitive);
@@ -4936,7 +4929,7 @@ do_popup_menu(GncPluginPage *page, GdkEventButton *event)
 
     ENTER("page %p, event %p", page, event);
     ui_merge = gnc_plugin_page_get_ui_merge(page);
-    if (ui_merge == NULL)
+    if (ui_merge == nullptr)
     {
         LEAVE("no ui merge");
         return;
@@ -4972,7 +4965,7 @@ gnc_main_window_popup_menu_cb (GtkWidget *widget,
                                GncPluginPage *page)
 {
     ENTER("widget %p, page %p", widget, page);
-    do_popup_menu(page, NULL);
+    do_popup_menu(page, nullptr);
     LEAVE(" ");
     return TRUE;
 }
@@ -5006,12 +4999,9 @@ void
 gnc_main_window_all_action_set_sensitive (const gchar *action_name,
         gboolean sensitive)
 {
-    GList *tmp;
-    GtkAction *action;
-
-    for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
+    for (auto tmp = active_windows; tmp; tmp = g_list_next(tmp))
     {
-        action = gnc_main_window_find_action (tmp->data, action_name);
+        auto action{gnc_main_window_find_action (static_cast<GncMainWindow*>(tmp->data), action_name)};
         gtk_action_set_sensitive (action, sensitive);
     }
 }
diff --git a/gnucash/gnome-utils/gnc-main-window.h b/gnucash/gnome-utils/gnc-main-window.h
index fe11c42ec..106fef53f 100644
--- a/gnucash/gnome-utils/gnc-main-window.h
+++ b/gnucash/gnome-utils/gnc-main-window.h
@@ -35,6 +35,10 @@
 
 #ifndef __GNC_MAIN_WINDOW_H
 #define __GNC_MAIN_WINDOW_H
+#ifdef __cplusplus
+extern "C"
+{
+#endif
 
 #include <gtk/gtk.h>
 #include "gnc-plugin-page.h"
@@ -443,6 +447,22 @@ void gnc_main_window_show_all_windows(void);
 GtkWidget *gnc_book_options_dialog_cb (gboolean modal, gchar *title,
                                        GtkWindow *parent);
 
+/**
+ * Processes selected options in the Book Options dialog: checks book_currency
+ * and use_split_action_for_num to see if features kvp should be set. To be used
+ * where ever a new book situation requires book option selection (e.g., not
+ * just in Book Options dialog opened from main window but also in new-file
+ * assistant).
+ *
+ *  @param GncOptionDB * options.
+ *
+ *  @return TRUE if gnc_gui_refresh_all should be called; otherwise FALSE.
+ **/
+gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options);
+
+#ifdef __cplusplus
+}
+#endif
 #endif /* __GNC_MAIN_WINDOW_H */
 
 /** @} */
diff --git a/gnucash/gnome/CMakeLists.txt b/gnucash/gnome/CMakeLists.txt
index 00e28d5c0..d48762487 100644
--- a/gnucash/gnome/CMakeLists.txt
+++ b/gnucash/gnome/CMakeLists.txt
@@ -27,7 +27,7 @@ set (gnc_gnome_noinst_HEADERS
   dialog-payment.h
   dialog-print-check.h
   dialog-progress.h
-#  dialog-report-column-view.h
+  dialog-report-column-view.hpp
   dialog-report-style-sheet.h
   dialog-sx-editor.h
   dialog-sx-editor2.h
@@ -69,7 +69,7 @@ gnc_add_swig_guile_command (swig-gnome-c
 
 set (gnc_gnome_SOURCES
   assistant-acct-period.c
-  assistant-hierarchy.c
+  assistant-hierarchy.cpp
   assistant-loan.cpp
   assistant-stock-split.c
   business-options-gnome.cpp
@@ -98,8 +98,8 @@ set (gnc_gnome_SOURCES
   dialog-price-edit-db.c
   dialog-print-check.c
   dialog-progress.c
-#  dialog-report-column-view.c
-  dialog-report-style-sheet.c
+  dialog-report-column-view.cpp
+  dialog-report-style-sheet.cpp
   dialog-sx-editor.c
   dialog-sx-editor2.c
   dialog-sx-from-trans.c
@@ -120,7 +120,7 @@ set (gnc_gnome_SOURCES
   gnc-plugin-page-owner-tree.c
   gnc-plugin-page-register.c
   gnc-plugin-page-register2.c
-  gnc-plugin-page-report.c
+  gnc-plugin-page-report.cpp
   gnc-plugin-page-sx-list.c
   gnc-split-reg.c
   gnc-split-reg2.c
@@ -129,7 +129,7 @@ set (gnc_gnome_SOURCES
   top-level.c
   window-reconcile.c
   window-reconcile2.c
-  window-report.c
+  window-report.cpp
   window-autoclear.c
 )
 
diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.cpp
similarity index 93%
rename from gnucash/gnome/assistant-hierarchy.c
rename to gnucash/gnome/assistant-hierarchy.cpp
index 4682477ec..3dd7c5529 100644
--- a/gnucash/gnome/assistant-hierarchy.c
+++ b/gnucash/gnome/assistant-hierarchy.cpp
@@ -22,10 +22,12 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
 \********************************************************************/
 
+#include <libguile.h>
+extern "C"
+{
 #include <config.h>
 
 #include <platform.h>
-#include <libguile.h>
 #if PLATFORM(WINDOWS)
 #include <windows.h>
 #endif
@@ -36,11 +38,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
-
 #ifdef MAC_INTEGRATION
 #include <Foundation/Foundation.h>
 #endif
-
 #include "gnc-account-merge.h"
 #include "dialog-new-user.h"
 #include "dialog-options.h"
@@ -66,6 +66,10 @@
 #include "gnc-plugin-page-account-tree.h"
 
 #include "gnc-engine.h"
+}
+
+#include <gnc-optiondb.h>
+
 static QofLogModule log_module = GNC_MOD_IMPORT;
 
 #define GNC_PREFS_GROUP           "dialogs.new-hierarchy"
@@ -156,7 +160,7 @@ delete_hierarchy_dialog (hierarchy_data *data)
 static void
 destroy_hash_helper (gpointer key, gpointer value, gpointer user_data)
 {
-    gnc_numeric *balance = value;
+    auto balance{static_cast<gnc_numeric*>(value)};
     g_free (balance);
 }
 
@@ -168,21 +172,19 @@ gnc_hierarchy_destroy_cb (GtkWidget *obj,   hierarchy_data *data)
     hash = data->balance_hash;
     if (hash)
     {
-        g_hash_table_foreach (hash, destroy_hash_helper, NULL);
+        g_hash_table_foreach (hash, destroy_hash_helper, nullptr);
         g_hash_table_destroy (hash);
-        data->balance_hash = NULL;
+        data->balance_hash = nullptr;
     }
 }
 
 static gnc_numeric
 get_final_balance (GHashTable *hash, Account *account)
 {
-    gnc_numeric *balance;
-
     if (!hash || !account)
         return gnc_numeric_zero ();
 
-    balance = g_hash_table_lookup(hash, account);
+    auto balance{static_cast<gnc_numeric*>(g_hash_table_lookup(hash, account))};
     if (balance)
         return *balance;
     return gnc_numeric_zero ();
@@ -191,12 +193,10 @@ get_final_balance (GHashTable *hash, Account *account)
 static void
 set_final_balance (GHashTable *hash, Account *account, gnc_numeric in_balance)
 {
-    gnc_numeric *balance;
-
     if (!hash || !account)
         return;
 
-    balance = g_hash_table_lookup (hash, account);
+    auto balance{static_cast<gnc_numeric*>(g_hash_table_lookup(hash, account))};
     if (balance)
     {
         *balance = in_balance;
@@ -218,7 +218,7 @@ mac_locale()
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSLocale* locale = [NSLocale currentLocale];
     NSString* locale_str;
-    char *retval = NULL;
+    char *retval = nullptr;
     @try
     {
         locale_str =[[[locale objectForKey: NSLocaleLanguageCode]
@@ -241,7 +241,7 @@ mac_locale()
 static gchar*
 gnc_get_ea_locale_dir(const char *top_dir)
 {
-    static gchar *default_locale = "C";
+    static const char* default_locale = "C";
     gchar *ret;
     gchar *locale;
     GStatBuf buf;
@@ -274,11 +274,11 @@ gnc_get_ea_locale_dir(const char *top_dir)
 #elif defined MAC_INTEGRATION
     locale = mac_locale();
 # else
-    locale = g_strdup(setlocale(LC_MESSAGES, NULL));
+    locale = g_strdup(setlocale(LC_MESSAGES, nullptr));
 #endif
 
     i = strlen(locale);
-    ret = g_build_filename(top_dir, locale, (char *)NULL);
+    ret = g_build_filename(top_dir, locale, (char *)nullptr);
 
     while (g_stat(ret, &buf) != 0)
     {
@@ -286,12 +286,12 @@ gnc_get_ea_locale_dir(const char *top_dir)
         if (i < 1)
         {
             g_free(ret);
-            ret = g_build_filename(top_dir, default_locale, (char *)NULL);
+            ret = g_build_filename(top_dir, default_locale, (char *)nullptr);
             break;
         }
         locale[i] = '\0';
         g_free(ret);
-        ret = g_build_filename(top_dir, locale, (char *)NULL);
+        ret = g_build_filename(top_dir, locale, (char *)nullptr);
     }
 
     g_free(locale);
@@ -313,8 +313,8 @@ region_combo_changed_cb (GtkComboBox *widget, hierarchy_data  *data)
     GtkTreeModel *filter_model = gtk_combo_box_get_model (GTK_COMBO_BOX(data->region_combo));
     GtkTreeModel *region_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
     GtkTreeIter filter_iter, region_iter;
-    gchar *lang_reg = NULL;
-    gchar *account_path = NULL;
+    gchar *lang_reg = nullptr;
+    gchar *account_path = nullptr;
 
     if (gtk_combo_box_get_active_iter (widget, &filter_iter))
     {
@@ -335,13 +335,13 @@ region_combo_changed_cb (GtkComboBox *widget, hierarchy_data  *data)
         /* Remove the old account tree */
         if (data->category_accounts_tree)
             gtk_widget_destroy(GTK_WIDGET(data->category_accounts_tree));
-        data->category_accounts_tree = NULL;
+        data->category_accounts_tree = nullptr;
 
         // clear the categories list store in prep for new load
         if (cat_list)
             gtk_list_store_clear (cat_list);
 
-        account_path = g_build_filename (data->gnc_accounts_dir, lang_reg, NULL);
+        account_path = g_build_filename (data->gnc_accounts_dir, lang_reg, nullptr);
 
         qof_event_suspend ();
         list = gnc_load_example_account_list (account_path);
@@ -350,7 +350,7 @@ region_combo_changed_cb (GtkComboBox *widget, hierarchy_data  *data)
         if (data->initial_category)
         {
             gtk_tree_row_reference_free (data->initial_category);
-            data->initial_category = NULL;
+            data->initial_category = nullptr;
         }
 
         // repopulate the category list
@@ -360,7 +360,7 @@ region_combo_changed_cb (GtkComboBox *widget, hierarchy_data  *data)
         {
             path = gtk_tree_row_reference_get_path (data->initial_category);
             gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(data->categories_tree),
-                                          path, NULL, TRUE, 0.5, 0.5);
+                                          path, nullptr, TRUE, 0.5, 0.5);
         }
         else
             path = gtk_tree_path_new_first ();
@@ -393,8 +393,8 @@ region_combo_change_filter_cb (GtkComboBox *widget, hierarchy_data  *data)
         GtkTreeModel *sort_model = gtk_combo_box_get_model (GTK_COMBO_BOX(data->language_combo));
         GtkTreeModel *language_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model));
         GtkListStore *cat_list = GTK_LIST_STORE(gtk_tree_view_get_model (data->categories_tree));
-        GtkTreeIter *iter = NULL;
-        gchar *language = NULL;
+        GtkTreeIter *iter = nullptr;
+        gchar *language = nullptr;
         gint count = 0;
         gboolean valid;
 
@@ -408,7 +408,7 @@ region_combo_change_filter_cb (GtkComboBox *widget, hierarchy_data  *data)
         // loop through the regions and filter any out that are not linked to language setting
         while (valid)
         {
-            gchar *region_test = NULL;
+            gchar *region_test = nullptr;
 
             gtk_tree_model_get (region_model, &region_iter,
                                 LANGUAGE_STRING, &region_test, -1);
@@ -433,7 +433,7 @@ region_combo_change_filter_cb (GtkComboBox *widget, hierarchy_data  *data)
         // if we only have a language or just one region activate it
         if (count == 1)
         {
-            gchar *region_label = NULL;
+            gchar *region_label = nullptr;
             GtkTreeIter filter_iter;
             gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER(filter_model),
                                                               &filter_iter,
@@ -476,10 +476,10 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir)
 {
     GtkListStore *language_store = gtk_list_store_new (1, G_TYPE_STRING);
     GtkListStore *region_store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
-    GtkTreeModel *filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL(region_store), NULL);
+    GtkTreeModel *filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL(region_store), nullptr);
     GtkTreeModel *sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(language_store));
     GtkTreeIter language_iter, region_iter;
-    gchar *start_region = NULL;
+    gchar *start_region = nullptr;
     gboolean valid;
 
     // set sort order
@@ -495,11 +495,11 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir)
 
     if (g_file_test (data->gnc_accounts_dir, G_FILE_TEST_IS_DIR))
     {
-        GHashTable *testhash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-        GDir *acct_dir = g_dir_open (data->gnc_accounts_dir, 0, NULL);
+        GHashTable *testhash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nullptr);
+        GDir *acct_dir = g_dir_open (data->gnc_accounts_dir, 0, nullptr);
         const gchar *name = "a";
 
-        while (name != NULL)
+        while (name != nullptr)
         {
             name = g_dir_read_name (acct_dir);
 
@@ -524,7 +524,7 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir)
                     start_region = g_strdup (parts[0]);
                 }
                 // add the region part to the region model store
-                if (parts[1] != NULL)
+                if (parts[1] != nullptr)
                     gtk_list_store_set (region_store, &region_iter, REGION_STRING, parts[1], -1);
                 else
                     gtk_list_store_set (region_store, &region_iter, REGION_STRING, "--", -1);
@@ -545,12 +545,13 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir)
                     lang_name = g_strdup (parts[0]);
 
                 // see if language is in hash table so we only add it once.
-                if (g_hash_table_lookup (testhash, lang_name) == NULL)
+                if (g_hash_table_lookup (testhash, lang_name) == nullptr)
                 {
+                    static const char* t_str{"test"};
                     gtk_list_store_append (language_store, &language_iter);
                     gtk_list_store_set (language_store, &language_iter, LANGUAGE_STRING, lang_name, -1);
 
-                    g_hash_table_insert (testhash, g_strdup (lang_name), "test");
+                    g_hash_table_insert (testhash, g_strdup (lang_name), &t_str);
                 }
                 g_strfreev (parts);
                 g_free (lang_name);
@@ -564,7 +565,7 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir)
     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(language_store), &language_iter);
     while (valid)
     {
-        gchar *language_test = NULL;
+        gchar *language_test = nullptr;
 
         gtk_tree_model_get (GTK_TREE_MODEL(language_store), &language_iter, LANGUAGE_STRING, &language_test, -1);
 
@@ -674,8 +675,8 @@ add_one_category (GncExampleAccount *acc,
     GtkTreePath* path;
     gboolean use_defaults;
 
-    g_return_if_fail(acc != NULL);
-    g_return_if_fail(data != NULL);
+    g_return_if_fail(acc != nullptr);
+    g_return_if_fail(data != nullptr);
 
     view = data->categories_tree;
     store = GTK_LIST_STORE(gtk_tree_view_get_model(view));
@@ -753,11 +754,11 @@ account_categories_tree_view_prepare (hierarchy_data  *data)
                       data);
 
     renderer = gtk_cell_renderer_toggle_new ();
-    g_object_set (G_OBJECT (renderer), "activatable", TRUE, NULL);
+    g_object_set (G_OBJECT (renderer), "activatable", TRUE, nullptr);
     column = gtk_tree_view_column_new_with_attributes (_("Selected"),
              renderer,
              "active", COL_CHECKED,
-             NULL);
+             nullptr);
     gtk_tree_view_append_column (tree_view, column);
     gtk_tree_view_column_set_sort_column_id (column, COL_CHECKED);
     g_signal_connect (G_OBJECT (renderer), "toggled",
@@ -769,7 +770,7 @@ account_categories_tree_view_prepare (hierarchy_data  *data)
     column = gtk_tree_view_column_new_with_attributes (_("Account Types"),
              renderer,
              "text", COL_TITLE,
-             NULL);
+             nullptr);
     gtk_tree_view_append_column (tree_view, column);
     gtk_tree_view_column_set_sort_column_id (column, COL_TITLE);
 
@@ -777,7 +778,7 @@ account_categories_tree_view_prepare (hierarchy_data  *data)
 //	column = gtk_tree_view_column_new_with_attributes (_("Description"),
 //							   renderer,
 //							   "text", COL_SHORT_DESCRIPTION,
-//							   NULL);
+//							   nullptr);
 //	gtk_tree_view_append_column (tree_view, column);
 //	gtk_tree_view_column_set_sort_column_id (column, COL_SHORT_DESCRIPTION);
 
@@ -791,7 +792,7 @@ account_categories_tree_view_prepare (hierarchy_data  *data)
     if (data->initial_category)
     {
         path = gtk_tree_row_reference_get_path (data->initial_category);
-        gtk_tree_view_scroll_to_cell (tree_view, path, NULL, TRUE, 0.5, 0.5);
+        gtk_tree_view_scroll_to_cell (tree_view, path, nullptr, TRUE, 0.5, 0.5);
     }
     else
         path = gtk_tree_path_new_first ();
@@ -830,7 +831,7 @@ on_choose_account_categories_prepare (hierarchy_data  *data)
         /* clear out the description/tree */
         if (data->category_accounts_tree)
             gtk_widget_destroy(GTK_WIDGET(data->category_accounts_tree));
-        data->category_accounts_tree = NULL;
+        data->category_accounts_tree = nullptr;
         buffer = gtk_text_view_get_buffer(data->category_description);
         gtk_text_buffer_set_text(buffer, "", -1);
 
@@ -859,7 +860,7 @@ categories_tree_selection_changed (GtkTreeSelection *selection,
     /* Remove the old account tree */
     if (data->category_accounts_tree)
         gtk_widget_destroy(GTK_WIDGET(data->category_accounts_tree));
-    data->category_accounts_tree = NULL;
+    data->category_accounts_tree = nullptr;
 
     /* Add a new one if something selected */
     if (gtk_tree_selection_get_selected (selection, &model, &iter))
@@ -909,7 +910,7 @@ select_helper (GtkListStore *store,
     g_return_val_if_fail(GTK_IS_LIST_STORE(store), FALSE);
 
     gtk_tree_model_get (GTK_TREE_MODEL(store), iter, COL_ACCOUNT, &gea, -1);
-    if ((gea != NULL) && !gea->exclude_from_select_all)
+    if ((gea != nullptr) && !gea->exclude_from_select_all)
     {
         gtk_list_store_set(store, iter,
                            COL_CHECKED, GPOINTER_TO_INT(data),
@@ -944,11 +945,11 @@ clear_all_clicked (GtkButton       *button,
 static void
 delete_our_account_tree (hierarchy_data *data)
 {
-    if (data->our_account_tree != NULL)
+    if (data->our_account_tree != nullptr)
     {
         xaccAccountBeginEdit (data->our_account_tree);
         xaccAccountDestroy (data->our_account_tree);
-        data->our_account_tree = NULL;
+        data->our_account_tree = nullptr;
     }
 }
 
@@ -974,7 +975,7 @@ struct add_group_data_struct
 static void
 add_groups_for_each (Account *toadd, gpointer data)
 {
-    struct add_group_data_struct *dadata = data;
+    auto dadata{static_cast<struct add_group_data_struct*>(data)};
     Account *foundact;
 
     foundact = gnc_account_lookup_by_name(dadata->to, xaccAccountGetName(toadd));
@@ -1013,7 +1014,7 @@ add_new_accounts_with_random_guids (Account *into, Account *from,
 {
     struct add_group_data_struct data;
     data.to = into;
-    data.parent = NULL;
+    data.parent = nullptr;
     data.com = com;
 
     gnc_account_foreach_child (from, add_groups_for_each, &data);
@@ -1027,7 +1028,7 @@ hierarchy_merge_accounts (GSList *dalist, gnc_commodity *com)
 
     for (mark = dalist; mark; mark = mark->next)
     {
-        GncExampleAccount *xea = mark->data;
+        auto xea{static_cast<GncExampleAccount*>(mark->data)};
 
         add_new_accounts_with_random_guids (ret, xea->root, com);
     }
@@ -1060,7 +1061,7 @@ accumulate_accounts (GtkListStore *store,
 static GSList *
 get_selected_account_list (GtkTreeView *tree_view)
 {
-    GSList *actlist = NULL;
+    GSList *actlist = nullptr;
     GtkTreeModel *model;
 
     model = gtk_tree_view_get_model (tree_view);
@@ -1122,7 +1123,7 @@ balance_cell_data_func (GtkTreeViewColumn *tree_column,
                   "text", string,
                   "editable", allow_value,
                   "sensitive", allow_value,
-                  NULL);
+                  nullptr);
 }
 
 static void
@@ -1136,20 +1137,20 @@ balance_cell_edited (GtkCellRendererText *cell,
     gnc_numeric amount;
     hierarchy_data *data = (hierarchy_data *)user_data;
 
-    g_return_if_fail(data != NULL);
+    g_return_if_fail(data != nullptr);
 
     account = gnc_tree_view_account_get_selected_account(data->final_account_tree);
-    if (account == NULL)
+    if (account == nullptr)
     {
         g_critical("account is null");
         return;
     }
 
-    error_loc = NULL;
+    error_loc = nullptr;
     if (!gnc_exp_parser_parse (new_text, &amount, &error_loc))
     {
         amount = gnc_numeric_zero();
-        g_object_set (G_OBJECT(cell), "text", "", NULL);
+        g_object_set (G_OBJECT(cell), "text", "", nullptr);
     }
     /* Bug#348364: Emulating price-cell, we need to ensure the denominator of
      * the amount is in the SCU of the account's commodity (so
@@ -1161,7 +1162,7 @@ balance_cell_edited (GtkCellRendererText *cell,
         amount = gnc_numeric_convert(amount, account_cmdty_fraction, GNC_HOW_RND_ROUND_HALF_UP);
     }
     set_final_balance (data->balance_hash, account, amount);
-    qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, NULL);
+    qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, nullptr);
 }
 
 static void
@@ -1209,7 +1210,7 @@ placeholder_cell_toggled (GtkCellRendererToggle *cell_renderer,
     GtkTreePath  *treepath;
     hierarchy_data *data = (hierarchy_data *)user_data;
 
-    g_return_if_fail(data != NULL);
+    g_return_if_fail(data != nullptr);
 
     treepath = gtk_tree_path_new_from_string (path);
 
@@ -1224,7 +1225,7 @@ placeholder_cell_toggled (GtkCellRendererToggle *cell_renderer,
     if (!state)
     {
         set_final_balance (data->balance_hash, account, gnc_numeric_zero());
-        qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, NULL);
+        qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, nullptr);
     }
     gtk_tree_path_free (treepath);
 }
@@ -1236,16 +1237,15 @@ use_existing_account_data_func(GtkTreeViewColumn *tree_column,
                                GtkTreeIter *iter,
                                gpointer user_data)
 {
-    Account *new_acct;
     Account *real_root;
     GncAccountMergeDisposition disposition;
-    char *to_user = "(error; unknown condition)";
+    auto to_user{"(error; unknown condition)"};
 
     g_return_if_fail (GTK_TREE_MODEL (tree_model));
-    new_acct = gnc_tree_view_account_get_account_from_iter(tree_model, iter);
-    if (new_acct == NULL)
+    auto new_acct{static_cast<Account*>(gnc_tree_view_account_get_account_from_iter(tree_model, iter))};
+    if (!new_acct)
     {
-        g_object_set (G_OBJECT(cell), "text", "(null account)", NULL);
+        g_object_set (G_OBJECT(cell), "text", "(null account)", nullptr);
         return;
     }
 
@@ -1261,7 +1261,7 @@ use_existing_account_data_func(GtkTreeViewColumn *tree_column,
         break;
     }
 
-    g_object_set(G_OBJECT(cell), "text", to_user, NULL);
+    g_object_set(G_OBJECT(cell), "text", to_user, nullptr);
 }
 
 void
@@ -1285,7 +1285,7 @@ on_final_account_prepare (hierarchy_data  *data)
     if (data->final_account_tree)
     {
         gtk_widget_destroy(GTK_WIDGET(data->final_account_tree));
-        data->final_account_tree = NULL;
+        data->final_account_tree = nullptr;
     }
     delete_our_account_tree (data);
 
@@ -1328,17 +1328,17 @@ on_final_account_prepare (hierarchy_data  *data)
         g_object_set(G_OBJECT (renderer),
                      "activatable", TRUE,
                      "sensitive", TRUE,
-                     NULL);
+                     nullptr);
 
         g_signal_connect (G_OBJECT (renderer), "toggled",
                           G_CALLBACK (placeholder_cell_toggled),
                           data);
 
         column = gtk_tree_view_column_new_with_attributes(_("Placeholder"),
-                 renderer, NULL);
+                 renderer, nullptr);
         gtk_tree_view_column_set_cell_data_func (column, renderer,
                 placeholder_cell_data_func,
-                (gpointer)data, NULL);
+                (gpointer)data, nullptr);
         gnc_tree_view_append_column (GNC_TREE_VIEW(tree_view), column);
     }
 
@@ -1347,16 +1347,16 @@ on_final_account_prepare (hierarchy_data  *data)
         renderer = gtk_cell_renderer_text_new ();
         g_object_set (G_OBJECT (renderer),
                       "xalign", 1.0,
-                      (char *)NULL);
+                      (char *)nullptr);
         g_signal_connect (G_OBJECT (renderer), "edited",
                           G_CALLBACK (balance_cell_edited),
                           data);
         column = gtk_tree_view_column_new_with_attributes (_("Opening Balance"),
                  renderer,
-                 NULL);
+                 nullptr);
         gtk_tree_view_column_set_cell_data_func (column, renderer,
                 balance_cell_data_func,
-                (gpointer)data, NULL);
+                (gpointer)data, nullptr);
         gnc_tree_view_append_column (GNC_TREE_VIEW(tree_view), column);
     }
 
@@ -1366,16 +1366,16 @@ on_final_account_prepare (hierarchy_data  *data)
         GList *renderers;
         column = gnc_tree_view_add_text_column(GNC_TREE_VIEW(tree_view),
                                                _("Use Existing"),
-                                               NULL,
-                                               NULL,
+                                               nullptr,
+                                               nullptr,
                                                "yes",
                                                GNC_TREE_VIEW_COLUMN_DATA_NONE,
                                                GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
-                                               NULL);
+                                               nullptr);
         renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
-        g_object_set(G_OBJECT(renderer), "xalign", 1.0, (char*)NULL);
+        g_object_set(G_OBJECT(renderer), "xalign", 1.0, (char*)nullptr);
         gtk_tree_view_column_set_cell_data_func(column, GTK_CELL_RENDERER(renderers->data),
-                                                use_existing_account_data_func, (gpointer)data, NULL);
+                                                use_existing_account_data_func, (gpointer)data, nullptr);
         g_list_free(renderers);
     }
 
@@ -1412,7 +1412,8 @@ starting_balance_helper (Account *account, hierarchy_data *data)
         balance = gnc_numeric_neg(balance);
     if (!gnc_numeric_zero_p (balance) &&
         gnc_commodity_is_currency (xaccAccountGetCommodity (account)))
-        gnc_account_create_opening_balance (account, balance, gnc_time (NULL),
+        gnc_account_create_opening_balance (account, balance,
+                                            gnc_time (nullptr),
                                             gnc_get_current_book ());
 }
 
@@ -1503,7 +1504,7 @@ static void
 book_options_dialog_close_cb(GNCOptionWin * optionwin,
                                gpointer user_data)
 {
-    GncOptionDB * options = user_data;
+    auto options{static_cast<GncOptionDB*>(user_data)};
 
     gnc_options_dialog_destroy(optionwin);
     gnc_option_db_destroy(options);
@@ -1524,7 +1525,7 @@ assistant_insert_book_options_page (hierarchy_data *data)
 
     /* The options dialog gets added to the notebook so it doesn't need a parent.*/
     data->optionwin = gnc_options_dialog_new_modal (TRUE, _("New Book Options"),
-                                                    DIALOG_BOOK_OPTIONS_CM_CLASS, NULL);
+                                                    DIALOG_BOOK_OPTIONS_CM_CLASS, nullptr);
     gnc_options_dialog_build_contents_full (data->optionwin, data->options, FALSE);
 
     gnc_options_dialog_set_close_cb (data->optionwin,
@@ -1620,12 +1621,12 @@ gnc_create_hierarchy_assistant (gboolean use_defaults, GncHierarchyAssistantFini
 
     /* Final Accounts Page */
     data->final_account_tree_container = GTK_WIDGET(gtk_builder_get_object (builder, "final_account_tree_box"));
-    data->final_account_tree = NULL;
+    data->final_account_tree = nullptr;
 
-    data->balance_hash = g_hash_table_new(NULL, NULL);
+    data->balance_hash = g_hash_table_new(nullptr, nullptr);
 
     gnc_restore_window_size (GNC_PREFS_GROUP,
-                             GTK_WINDOW(data->dialog), gnc_ui_get_main_window(NULL));
+                             GTK_WINDOW(data->dialog), gnc_ui_get_main_window(nullptr));
 
     g_signal_connect (G_OBJECT(dialog), "destroy",
                       G_CALLBACK (gnc_hierarchy_destroy_cb), data);
@@ -1642,7 +1643,7 @@ gnc_create_hierarchy_assistant (gboolean use_defaults, GncHierarchyAssistantFini
 GtkWidget*
 gnc_ui_hierarchy_assistant(gboolean use_defaults)
 {
-    return gnc_create_hierarchy_assistant(use_defaults, NULL);
+    return gnc_create_hierarchy_assistant(use_defaults, nullptr);
 }
 
 GtkWidget*
@@ -1656,7 +1657,7 @@ static void
 after_assistant(void)
 {
     qof_book_mark_session_dirty(gnc_get_current_book());
-    gnc_ui_file_access_for_save_as (gnc_ui_get_main_window (NULL));
+    gnc_ui_file_access_for_save_as (gnc_ui_get_main_window (nullptr));
 }
 
 static void
@@ -1672,5 +1673,6 @@ void
 gnc_ui_hierarchy_assistant_initialize (void)
 {
     gnc_hook_add_dangler(HOOK_NEW_BOOK,
-                         (GFunc)gnc_ui_hierarchy_assistant_hook, NULL, NULL);
+                         (GFunc)gnc_ui_hierarchy_assistant_hook,
+                         nullptr, nullptr);
 }
diff --git a/gnucash/gnome/assistant-hierarchy.h b/gnucash/gnome/assistant-hierarchy.h
index 8f48bbb9e..fc0d41475 100644
--- a/gnucash/gnome/assistant-hierarchy.h
+++ b/gnucash/gnome/assistant-hierarchy.h
@@ -29,6 +29,10 @@
  * completes successfully.  I.e., the new-user assistant can finish the GnuCash
  * New-User Experience, create an account plugin-page, &c.
  **/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
 
 typedef void (*GncHierarchyAssistantFinishedCallback)(void);
 
@@ -37,4 +41,7 @@ GtkWidget* gnc_ui_hierarchy_assistant_with_callback(gboolean use_defaults, GncHi
 
 void gnc_ui_hierarchy_assistant_initialize (void);
 
+#ifdef __cplusplus
+}
+#endif
 #endif
diff --git a/gnucash/gnome/dialog-custom-report.c b/gnucash/gnome/dialog-custom-report.c
index f2af3b0ef..248eb6ef0 100644
--- a/gnucash/gnome/dialog-custom-report.c
+++ b/gnucash/gnome/dialog-custom-report.c
@@ -32,7 +32,6 @@
 #include "dialog-options.h"
 #include "dialog-utils.h"
 #include "gnc-main-window.h"
-#include "option-util.h"
 #include "window-report.h"
 #include "guile-mappings.h"
 #include "gnc-guile-utils.h"
diff --git a/gnucash/gnome/dialog-report-column-view.c b/gnucash/gnome/dialog-report-column-view.cpp
similarity index 88%
rename from gnucash/gnome/dialog-report-column-view.c
rename to gnucash/gnome/dialog-report-column-view.cpp
index 7d8fbec85..cf1426859 100644
--- a/gnucash/gnome/dialog-report-column-view.c
+++ b/gnucash/gnome/dialog-report-column-view.cpp
@@ -21,22 +21,26 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  ********************************************************************/
 
+#include <libguile.h>
+extern "C"
+{
 #include <config.h>
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
-#include <libguile.h>
 #include "swig-runtime.h"
 
-#include "dialog-report-column-view.h"
-#include "dialog-options.h"
 #include "dialog-utils.h"
-#include "option-util.h"
 #include "window-report.h"
 #include "guile-mappings.h"
 #include "gnc-guile-utils.h"
 #include "gnc-report.h"
 #include "gnc-ui.h"
+}
+
+#include "dialog-report-column-view.hpp"
+#include "dialog-options.h"
+#include <gnc-optiondb.h>
 
 enum available_cols
 {
@@ -64,7 +68,7 @@ struct gncp_column_view_edit
     GncOptionDB  * odb;
 
     SCM       available_list;
-    GList*       contents_list;
+    SCM       contents_list;
     int       contents_selected;
 
     GtkWidget *add_button;
@@ -81,10 +85,10 @@ void gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data);
 void gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data);
 
 static void
-gnc_column_view_set_option(GncOptionDB * odb, char * section, char * name,
-                           GList* new_value)
+gnc_column_view_set_option(GncOptionDB* odb, const char* section,
+                           const char* name, SCM new_value)
 {
-    gnc_option_db_set_glist_value(section, name, new_value);
+    gnc_option_db_set_scm_value(odb, section, name, new_value);
 }
 
 static void
@@ -164,11 +168,10 @@ static void
 update_contents_lists(gnc_column_view_edit * view)
 {
     SCM   report_menu_name = scm_c_eval_string("gnc:report-menu-name");
-    GList* contents = gnc_option_db_lookup_glist_option(view->odb,
-                                                        "__general",
-                                                        "report-list");
-    SCM   this_report;
-    gchar*   selection;
+    SCM contents = gnc_option_db_lookup_scm_value(view->odb, "__general",
+                                                  "report-list");
+    SCM this_report;
+    SCM selection = SCM_UNDEFINED;
 
     GtkListStore *store;
     GtkTreeIter iter;
@@ -177,44 +180,46 @@ update_contents_lists(gnc_column_view_edit * view)
     /* Update the list of selected reports (right selection box). */
     tree_selection = gtk_tree_view_get_selection(view->contents);
 
-    if (g_list_length(contents))
+    if (scm_is_list(view->contents_list) && !scm_is_null (view->contents_list))
     {
         int row = view->contents_selected;
-        row = MIN (row,  g_list_length(view->contents_list) - 1);
-        selection = g_list_nth_value(view->contents_list, row);
+        row = MIN (row, scm_ilength (view->contents_list) - 1);
+        selection = scm_list_ref (view->contents_list, scm_from_int (row));
     }
-    else
-        selection = NULL;
 
+    scm_gc_unprotect_object(view->contents_list);
     view->contents_list = contents;
+    scm_gc_protect_object(view->contents_list);
 
     store = GTK_LIST_STORE(gtk_tree_view_get_model(view->contents));
     gtk_list_store_clear(store);
 
-    for (GList* node = contents; node; g_list_next(node))
+    if (!scm_is_list(contents))
+        return;
+
+    for (int i = 0; !scm_is_null(contents);
+         contents = SCM_CDR(contents), ++i)
     {
-        gchar *name;
-        SCM contents_temp = SCM_CAR(node);
-        int id = scm_to_int(SCM_CAAR(node));
+        SCM contents_temp = SCM_CAR(contents);
+
+        int id = scm_to_int(SCM_CAAR(contents));
 
         this_report = gnc_report_find(id);
-        name = gnc_scm_to_utf8_string (scm_call_1(report_menu_name, this_report));
+        auto name = gnc_scm_to_utf8_string (scm_call_1(report_menu_name, this_report));
 
         gtk_list_store_append(store, &iter);
-        gtk_list_store_set (store, &iter,
-                            CONTENTS_COL_NAME, _(name),
-                            CONTENTS_COL_ROW, i,
-                            CONTENTS_COL_REPORT_COLS,
-                            scm_to_int(SCM_CADR(contents_temp)),
-                            CONTENTS_COL_REPORT_ROWS,
-                            scm_to_int(SCM_CADDR(contents_temp)),
-                            -1);
-
-            if (scm_is_equal (contents_temp, selection))
-                gtk_tree_selection_select_iter (tree_selection, &iter);
-
-            g_free (name);
-        }
+        gtk_list_store_set
+            (store, &iter,
+             CONTENTS_COL_NAME, _(name),
+             CONTENTS_COL_ROW, i,
+             CONTENTS_COL_REPORT_COLS, scm_to_int(SCM_CADR(contents_temp)),
+             CONTENTS_COL_REPORT_ROWS, scm_to_int(SCM_CADDR(contents_temp)),
+             -1);
+
+        if (scm_is_equal (contents_temp, selection))
+            gtk_tree_selection_select_iter (tree_selection, &iter);
+
+        g_free (name);
     }
 }
 
@@ -242,7 +247,7 @@ gnc_column_view_update_buttons_cb (GtkTreeSelection *selection,
 
     if (is_selected)
     {
-        int len = scm_ilength (r->contents_list);
+        int len = scm_ilength (reinterpret_cast<SCM>(r->contents_list));
 
         gtk_tree_model_get (model, &iter,
                            CONTENTS_COL_ROW, &r->contents_selected, -1);
@@ -270,19 +275,20 @@ static void
 gnc_column_view_edit_apply_cb(GNCOptionWin * w, gpointer user_data)
 {
     SCM  dirty_report = scm_c_eval_string("gnc:report-set-dirty?!");
-    gnc_column_view_edit * win = user_data;
-    GList *results = NULL, *iter;
+    auto win{static_cast<gnc_column_view_edit*>(user_data)};
+    GList *results = nullptr, *iter;
 
     if (!win) return;
     results = gnc_option_db_commit (win->odb);
     for (iter = results; iter; iter = iter->next)
     {
-        GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(gnc_options_dialog_widget(w)),
-                                                   0,
-                                                   GTK_MESSAGE_ERROR,
-                                                   GTK_BUTTONS_OK,
-                                                   "%s",
-                                                   (char*)iter->data);
+        GtkWidget *dialog =
+            gtk_message_dialog_new(GTK_WINDOW(gnc_options_dialog_widget(w)),
+                                   GTK_DIALOG_MODAL,
+                                   GTK_MESSAGE_ERROR,
+                                   GTK_BUTTONS_OK,
+                                   "%s",
+                                   (char*)iter->data);
         gtk_dialog_run(GTK_DIALOG(dialog));
         gtk_widget_destroy(dialog);
         g_free (iter->data);
@@ -295,7 +301,7 @@ gnc_column_view_edit_apply_cb(GNCOptionWin * w, gpointer user_data)
 static void
 gnc_column_view_edit_close_cb(GNCOptionWin * win, gpointer user_data)
 {
-    gnc_column_view_edit * r = user_data;
+    auto r{static_cast<gnc_column_view_edit*>(user_data)};
     SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!");
 
     scm_call_2(set_editor, r->view, SCM_BOOL_F);
@@ -323,17 +329,17 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
     if (ptr != SCM_BOOL_F)
     {
 #define FUNC_NAME "gtk_window_present"
-        GtkWindow * w = SWIG_MustGetPtr(ptr, SWIG_TypeQuery("_p_GtkWidget"), 1, 0);
+        auto w{static_cast<GtkWindow*>(SWIG_MustGetPtr(ptr, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))};
         gtk_window_present(w);
 #undef FUNC_NAME
-        return NULL;
+        return nullptr;
     }
     else
     {
         gnc_column_view_edit * r = g_new0(gnc_column_view_edit, 1);
         GtkBuilder *builder;
 
-        r->optwin = gnc_options_dialog_new (NULL, GTK_WINDOW(gnc_ui_get_main_window (NULL)));
+        r->optwin = gnc_options_dialog_new (nullptr, GTK_WINDOW(gnc_ui_get_main_window (nullptr)));
 
         /* Hide the generic dialog page list. */
         gtk_widget_hide(gnc_options_page_list(r->optwin));
@@ -377,7 +383,7 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
         renderer = gtk_cell_renderer_text_new();
         column = gtk_tree_view_column_new_with_attributes("", renderer,
                  "text", AVAILABLE_COL_NAME,
-                 NULL);
+                 nullptr);
         gtk_tree_view_append_column(r->available, column);
 
         /* use the selection cb to update buttons */
@@ -394,19 +400,19 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
         renderer = gtk_cell_renderer_text_new();
         column = gtk_tree_view_column_new_with_attributes(_("Report"), renderer,
                  "text", CONTENTS_COL_NAME,
-                 NULL);
+                 nullptr);
         gtk_tree_view_append_column(r->contents, column);
 
         renderer = gtk_cell_renderer_text_new();
         column = gtk_tree_view_column_new_with_attributes(_("Rows"), renderer,
                  "text", CONTENTS_COL_REPORT_ROWS,
-                 NULL);
+                 nullptr);
         gtk_tree_view_append_column(r->contents, column);
 
         renderer = gtk_cell_renderer_text_new();
         column = gtk_tree_view_column_new_with_attributes(_("Cols"), renderer,
                  "text", CONTENTS_COL_REPORT_COLS,
-                 NULL);
+                 nullptr);
         gtk_tree_view_append_column(r->contents, column);
 
         /* use the selection cb to update buttons */
@@ -435,7 +441,7 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
 void
 gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data)
 {
-    gnc_column_view_edit * r = user_data;
+    auto r = static_cast<gnc_column_view_edit *>(user_data);
     SCM make_report = scm_c_eval_string("gnc:make-report");
     SCM mark_report = scm_c_eval_string("gnc:report-set-needs-save?!");
     SCM template_name;
@@ -511,7 +517,7 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data)
 void
 gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data)
 {
-    gnc_column_view_edit * r = user_data;
+    auto r = static_cast<gnc_column_view_edit *>(user_data);
     SCM newlist = SCM_EOL;
     SCM oldlist = r->contents_list;
     int count;
@@ -553,7 +559,7 @@ gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data)
 void
 gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data)
 {
-    gnc_column_view_edit * r = user_data;
+    auto r = static_cast<gnc_column_view_edit *>(user_data);
     SCM oldlist = r->contents_list;
     SCM newlist = SCM_EOL;
     SCM temp;
@@ -591,7 +597,7 @@ gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data)
 void
 gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data)
 {
-    gnc_column_view_edit * r = user_data;
+    auto r = static_cast<gnc_column_view_edit *>(user_data);
     SCM oldlist = r->contents_list;
     SCM newlist = SCM_EOL;
     SCM temp;
@@ -629,7 +635,7 @@ gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data)
 void
 gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data)
 {
-    gnc_column_view_edit * r = user_data;
+    auto r = static_cast<gnc_column_view_edit *>(user_data);
     GtkWidget * rowspin;
     GtkWidget * colspin;
     GtkWidget * dlg;
diff --git a/gnucash/gnome/dialog-report-column-view.h b/gnucash/gnome/dialog-report-column-view.hpp
similarity index 99%
rename from gnucash/gnome/dialog-report-column-view.h
rename to gnucash/gnome/dialog-report-column-view.hpp
index 4153690e3..9d3843b83 100644
--- a/gnucash/gnome/dialog-report-column-view.h
+++ b/gnucash/gnome/dialog-report-column-view.hpp
@@ -24,10 +24,12 @@
 #define GNC_DIALOG_COLUMN_VIEW_H
 
 #include <libguile.h>
+extern "C"
+{
 #include <gtk/gtk.h>
 
 typedef struct gncp_column_view_edit gnc_column_view_edit;
 
 GtkWidget * gnc_column_view_edit_options(GncOptionDB* odb, SCM view);
-
+}
 #endif
diff --git a/gnucash/gnome/dialog-report-style-sheet.c b/gnucash/gnome/dialog-report-style-sheet.cpp
similarity index 95%
rename from gnucash/gnome/dialog-report-style-sheet.c
rename to gnucash/gnome/dialog-report-style-sheet.cpp
index a9c5d8415..60d4287d7 100644
--- a/gnucash/gnome/dialog-report-style-sheet.c
+++ b/gnucash/gnome/dialog-report-style-sheet.cpp
@@ -22,13 +22,15 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  ********************************************************************/
 
+#include <libguile.h>
+extern "C"
+{
 #include <config.h>
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 
 #include "dialog-report-style-sheet.h"
-#include "dialog-options.h"
 #include "dialog-utils.h"
 #include "gnc-component-manager.h"
 #include "gnc-session.h"
@@ -37,6 +39,9 @@
 #include "gnc-guile-utils.h"
 #include "gnc-report.h"
 #include "gnc-ui.h"
+}
+#include "dialog-options.h"
+#include <gnc-optiondb.h>
 
 #define DIALOG_STYLE_SHEETS_CM_CLASS "style-sheets-dialog"
 #define GNC_PREFS_GROUP              "dialogs.style-sheet"
@@ -82,9 +87,9 @@ void gnc_style_sheet_select_dialog_destroy_cb (GtkWidget *widget, gpointer user_
 static void
 dirty_same_stylesheet (gpointer key, gpointer val, gpointer data)
 {
-    SCM dirty_ss = data;
+    auto dirty_ss{static_cast<SCM>(data)};
     SCM rep_ss = NULL;
-    SCM report = val;
+    auto report{static_cast<SCM>(val)};
     SCM func = NULL;
 
     func = scm_c_eval_string ("gnc:report-stylesheet");
@@ -118,14 +123,14 @@ gnc_style_sheet_options_apply_cb (GNCOptionWin * propertybox,
     results = gnc_option_db_commit (ssi->odb);
     for (iter = results; iter; iter = iter->next)
     {
-        GtkWidget *dialog = gtk_message_dialog_new (NULL,
-                                                    0,
-                                                    GTK_MESSAGE_ERROR,
-                                                    GTK_BUTTONS_OK,
-                                                    "%s",
-                                                    (char*)iter->data);
-        gtk_dialog_run (GTK_DIALOG(dialog));
-        gtk_widget_destroy (dialog);
+        GtkWidget *dialog = gtk_message_dialog_new(nullptr,
+                                                   GTK_DIALOG_MODAL,
+                                                   GTK_MESSAGE_ERROR,
+                                                   GTK_BUTTONS_OK,
+                                                   "%s",
+                                                   (char*)iter->data);
+        gtk_dialog_run(GTK_DIALOG(dialog));
+        gtk_widget_destroy(dialog);
         g_free (iter->data);
     }
     g_list_free (results);
@@ -135,7 +140,7 @@ static void
 gnc_style_sheet_options_close_cb (GNCOptionWin * propertybox,
                                   gpointer user_data)
 {
-    ss_info * ssi = user_data;
+    auto ssi{static_cast<ss_info*>(user_data)};
     GtkTreeIter iter;
 
     if (gtk_tree_row_reference_valid (ssi->row_ref))
@@ -182,7 +187,7 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss,
     gnc_options_dialog_build_contents (ssinfo->odialog,
                                        ssinfo->odb);
 
-    gnc_options_dialog_set_style_sheet_options_help_cb (ssinfo->odialog);
+//    gnc_options_dialog_set_style_sheet_options_help_cb (ssinfo->odialog);
 
     gnc_options_dialog_set_apply_cb (ssinfo->odialog,
                                      gnc_style_sheet_options_apply_cb,
@@ -260,9 +265,9 @@ gnc_style_sheet_new (StyleSheetDialog * ssd)
     if (dialog_retval == GTK_RESPONSE_OK)
     {
         gint choice = gtk_combo_box_get_active (GTK_COMBO_BOX(template_combo));
-        const char *template_str = g_list_nth_data (template_names, choice);
-        const char *name_str     = gtk_entry_get_text (GTK_ENTRY(name_entry));
-        if (name_str && strlen (name_str) == 0)
+        auto template_str{static_cast<const char *>(g_list_nth_data (template_names, choice))};
+        const char *name_str     = gtk_entry_get_text(GTK_ENTRY(name_entry));
+        if (name_str && strlen(name_str) == 0)
         {
             /* If the name is empty, we display an error dialog but
              * refuse to create the new style sheet. */
@@ -442,8 +447,7 @@ gnc_style_sheet_select_dialog_delete_event_cb (GtkWidget *widget,
                                                GdkEvent  *event,
                                                gpointer   user_data)
 {
-    StyleSheetDialog  *ss = (StyleSheetDialog *)user_data;
-    // this cb allows the window size to be saved on closing with the X
+    auto ss{static_cast<StyleSheetDialog*>(user_data)};
     gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(ss->toplevel));
     return FALSE;
 }
diff --git a/gnucash/gnome/dialog-report-style-sheet.h b/gnucash/gnome/dialog-report-style-sheet.h
index fc1905c57..b0a0cfa20 100644
--- a/gnucash/gnome/dialog-report-style-sheet.h
+++ b/gnucash/gnome/dialog-report-style-sheet.h
@@ -23,9 +23,14 @@
 
 #ifndef GNC_DIALOG_STYLE_SHEET_H
 #define GNC_DIALOG_STYLE_SHEET_H
-
+#ifdef __cplusplus
+extern "C"
+{
+#endif
 typedef struct _stylesheetdialog StyleSheetDialog;
 
-void gnc_style_sheet_dialog_open (GtkWindow *parent);
-
+void gnc_style_sheet_dialog_open(GtkWindow *parent);
+#ifdef __cplusplus
+}
+#endif
 #endif
diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index 0b288da2f..89a96d70b 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -61,7 +61,6 @@
 #include "gnc-tree-view-account.h"
 #include "gnc-ui.h"
 #include "gnc-ui-util.h"
-#include "option-util.h"
 #include "gnc-main-window.h"
 #include "gnc-component-manager.h"
 #include "gnc-state.h"
diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c
index 8a968bdf0..35496027e 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.c
+++ b/gnucash/gnome/gnc-plugin-page-budget.c
@@ -65,7 +65,6 @@
 #include "gnc-ui.h"
 #include "gnc-ui-util.h"
 #include "gnc-window.h"
-#include "option-util.h"
 #include "gnc-main-window.h"
 #include "gnc-component-manager.h"
 
diff --git a/gnucash/gnome/gnc-plugin-page-report.c b/gnucash/gnome/gnc-plugin-page-report.cpp
similarity index 94%
rename from gnucash/gnome/gnc-plugin-page-report.c
rename to gnucash/gnome/gnc-plugin-page-report.cpp
index bd1e5f40b..8b6137854 100644
--- a/gnucash/gnome/gnc-plugin-page-report.c
+++ b/gnucash/gnome/gnc-plugin-page-report.cpp
@@ -36,13 +36,14 @@
     @author Copyright (C) 2004 Joshua Sled <jsled at asynchronous.org>
     @author Copyright (C) 2005 David Hampton <hampton at employees.org>
 */
-
+#include <libguile.h>
+extern "C"
+{
 #include <config.h>
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
-#include <libguile.h>
 #include <sys/stat.h>
 #include <errno.h>
 
@@ -68,12 +69,12 @@
 #include "gnc-ui-util.h"
 #include "gnc-ui.h"
 #include "gnc-window.h"
-#include "option-util.h"
 #include "window-report.h"
 #include "swig-runtime.h"
 #include "guile-mappings.h"
 #include "gnc-icons.h"
 #include "print-session.h"
+}
 
 /* NW: you can add GNC_MOD_REPORT to gnc-engine.h
 or simply define it locally. Any unique string with
@@ -81,13 +82,13 @@ a gnucash- prefix will do. Then just set a log level
 with qof_log_set_level().*/
 static QofLogModule log_module = GNC_MOD_GUI;
 
-static GObjectClass *parent_class = NULL;
+static GObjectClass *parent_class = nullptr;
 
 // A static GHashTable to record the usage count for each printer
 // output name. FIXME: Currently this isn't cleaned up at program
 // shutdown because there isn't a place to easily insert a finalize()
 // function for this. Oh well.
-static GHashTable *static_report_printnames = NULL;
+static GHashTable *static_report_printnames = nullptr;
 
 // Property-id values.
 enum
@@ -272,7 +273,7 @@ gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass)
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
     GncPluginPageClass *gnc_plugin_page_class = GNC_PLUGIN_PAGE_CLASS(klass);
 
-    parent_class = g_type_class_peek_parent (klass);
+    parent_class = static_cast<GObjectClass*>(g_type_class_peek_parent (klass));
 
     object_class->constructor = gnc_plugin_page_report_constructor;
     object_class->finalize = gnc_plugin_page_report_finalize;
@@ -293,29 +294,19 @@ gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass)
     gnc_plugin_page_class->focus_page_function = gnc_plugin_page_report_focus_widget;
 
     // create the "reportId" property
+    auto paramspec{static_cast<GParamFlags>(G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)};
     g_object_class_install_property( object_class,
                                      PROP_REPORT_ID,
                                      g_param_spec_int( "report-id",
                                              _("The numeric ID of the report."),
                                              _("The numeric ID of the report."),
-                                             -1, G_MAXINT, -1, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE ) );
-
-    /* JSLED: report-selected?
-    	plugin_page_signals[ACCOUNT_SELECTED] =
-    	  g_signal_new ("account_selected",
-    			G_OBJECT_CLASS_TYPE (object_class),
-    			G_SIGNAL_RUN_FIRST,
-    			G_STRUCT_OFFSET (GncPluginPageReportClass, account_selected),
-    			NULL, NULL,
-    			g_cclosure_marshal_VOID__POINTER,
-    			G_TYPE_NONE, 1,
-    			G_TYPE_POINTER);
-    */
+                                                       -1, G_MAXINT, -1,
+                                                       paramspec));
 
     // Also initialize the report name usage count table
     if (!static_report_printnames)
         static_report_printnames = g_hash_table_new_full(g_str_hash,
-                                   g_str_equal, g_free, NULL);
+                                   g_str_equal, g_free, nullptr);
 }
 
 static void
@@ -352,8 +343,8 @@ gnc_plugin_page_report_load_uri (GncPluginPage *page)
     URLType type;
     char * id_name;
     char * child_name;
-    char * url_location = NULL;
-    char * url_label = NULL;
+    char * url_location = nullptr;
+    char * url_label = nullptr;
 
     report = GNC_PLUGIN_PAGE_REPORT(page);
     priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
@@ -362,7 +353,7 @@ gnc_plugin_page_report_load_uri (GncPluginPage *page)
 
     DEBUG( "Load uri id=%d", priv->reportId );
     id_name = g_strdup_printf("id=%d", priv->reportId );
-    child_name = gnc_build_url( URL_TYPE_REPORT, id_name, NULL );
+    child_name = gnc_build_url( URL_TYPE_REPORT, id_name, nullptr );
     type = gnc_html_parse_url( priv->html, child_name, &url_location, &url_label);
     DEBUG( "passing id_name=[%s] child_name=[%s] type=[%s], location=[%s], label=[%s]",
            id_name, child_name ? child_name : "(null)",
@@ -387,8 +378,8 @@ gnc_plugin_page_report_load_uri (GncPluginPage *page)
 
     gnc_plugin_page_report_set_progressbar( page, FALSE );
 
-    // this resets the window for the progressbar to NULL
-    gnc_window_set_progressbar_window( NULL );
+    // this resets the window for the progressbar to nullptr
+    gnc_window_set_progressbar_window( nullptr );
 }
 
 /* used to capture Ctrl+Alt+PgUp/Down for tab selection */
@@ -442,8 +433,8 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
     URLType type;
     char * id_name;
     char * child_name;
-    char * url_location = NULL;
-    char * url_label = NULL;
+    char * url_location = nullptr;
+    char * url_label = nullptr;
 
     ENTER("page %p", page);
 
@@ -457,7 +448,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
     report = GNC_PLUGIN_PAGE_REPORT(page);
     priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
 
-    topLvl = gnc_ui_get_main_window (NULL);
+    topLvl = gnc_ui_get_main_window (nullptr);
 //        priv->html = gnc_html_new( topLvl );
     priv->html = gnc_html_factory_create_html();
     gnc_html_set_parent( priv->html, topLvl );
@@ -467,7 +458,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
                                          gnc_plugin_page_report_history_destroy_cb,
                                          (gpointer)priv);
 
-    priv->container = GTK_CONTAINER(gtk_frame_new(NULL));
+    priv->container = GTK_CONTAINER(gtk_frame_new(nullptr));
     gtk_frame_set_shadow_type(GTK_FRAME(priv->container), GTK_SHADOW_NONE);
 
     // Set the name for this widget so it can be easily manipulated with css
@@ -477,7 +468,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
                       gnc_html_get_widget(priv->html));
 
     priv->component_manager_id =
-        gnc_register_gui_component(WINDOW_REPORT_CM_CLASS, NULL,
+        gnc_register_gui_component(WINDOW_REPORT_CM_CLASS, nullptr,
                                    close_handler, page);
     gnc_gui_component_set_session(priv->component_manager_id,
                                   gnc_get_current_session());
@@ -488,7 +479,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
     /* We need to call the load call back so the report appears to have been run
      so it will get saved properly if the report is not realized in session */
     id_name = g_strdup_printf("id=%d", priv->reportId );
-    child_name = gnc_build_url( URL_TYPE_REPORT, id_name, NULL );
+    child_name = gnc_build_url( URL_TYPE_REPORT, id_name, nullptr );
     type = gnc_html_parse_url( priv->html, child_name, &url_location, &url_label);
 
     gnc_plugin_page_report_load_cb (priv->html, type, id_name, url_label, report);
@@ -502,7 +493,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
 
     g_signal_connect (G_OBJECT(page), "inserted",
                       G_CALLBACK(gnc_plugin_page_inserted_cb),
-                      NULL);
+                      nullptr);
 
     // used to capture Ctrl+Alt+PgUp/Down for tab selection
     webview = gnc_html_get_webview (priv->html);
@@ -557,7 +548,7 @@ gnc_plugin_page_report_setup( GncPluginPage *ppage )
     priv->edited_reports    = SCM_EOL;
     priv->name_change_cb_id = SCM_BOOL_F;
 
-    g_object_get( ppage, "report-id", &report_id, NULL );
+    g_object_get( ppage, "report-id", &report_id, nullptr );
 
     PINFO("report-id: %d\n", report_id);
 
@@ -659,14 +650,14 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
 */
     }
 
-    if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != NULL))
+    if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != nullptr))
     {
 /*
         gnc_option_db_unregister_change_callback_id(priv->cur_odb,
                 priv->option_change_cb_id);
 */
         gnc_option_db_destroy(priv->cur_odb);
-        priv->cur_odb = NULL;
+        priv->cur_odb = nullptr;
     }
 
     if (priv->cur_report != SCM_BOOL_F)
@@ -680,7 +671,7 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
     priv->option_change_cb_id =
         gnc_option_db_register_change_callback(priv->cur_odb,
                 gnc_plugin_page_report_option_change_cb,
-                report, NULL, NULL);
+                report, nullptr, nullptr);
 */
     if (gnc_html_history_forward_p(gnc_html_get_history(priv->html)))
     {
@@ -767,8 +758,8 @@ gnc_plugin_page_report_option_change_cb(gpointer data)
 
     gnc_plugin_page_report_set_progressbar( page, FALSE );
 
-    // this resets the window for the progressbar to NULL
-    gnc_window_set_progressbar_window( NULL );
+    // this resets the window for the progressbar to nullptr
+    gnc_window_set_progressbar_window( nullptr );
 
     priv->reloading = FALSE;
 }
@@ -808,7 +799,7 @@ gnc_plugin_page_report_refresh(gpointer data)
 {
     // FIXME?
     DEBUG( "report-refresh called" );
-    // something like ... gnc_plugin_page_report_redraw( NULL, (GncPluginPageReportPrivate*)data );
+    // something like ... gnc_plugin_page_report_redraw( nullptr, (GncPluginPageReportPrivate*)data );
     return;
 }
 
@@ -868,8 +859,8 @@ gnc_plugin_page_report_save_page (GncPluginPage *plugin_page,
     gchar *text, *key_name;
 
     g_return_if_fail (GNC_IS_PLUGIN_PAGE_REPORT(plugin_page));
-    g_return_if_fail (key_file != NULL);
-    g_return_if_fail (group_name != NULL);
+    g_return_if_fail (key_file != nullptr);
+    g_return_if_fail (group_name != nullptr);
 
     ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
           group_name);
@@ -942,14 +933,14 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window,
     GncPluginPage *page;
     gchar **keys;
     gsize i, num_keys;
-    GError *error = NULL;
+    GError *error = nullptr;
     gchar *option_string;
     gint report_id;
     SCM scm_id, final_id = SCM_BOOL_F;
     SCM report;
 
-    g_return_val_if_fail(key_file, NULL);
-    g_return_val_if_fail(group_name, NULL);
+    g_return_val_if_fail(key_file, nullptr);
+    g_return_val_if_fail(group_name, nullptr);
     ENTER("key_file %p, group_name %s", key_file, group_name);
 
     keys = g_key_file_get_keys(key_file, group_name, &num_keys, &error);
@@ -959,7 +950,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window,
                   group_name, error->message);
         g_error_free(error);
         LEAVE("no keys");
-        return NULL;
+        return nullptr;
     }
 
     for (i = 0; i < num_keys; i++)
@@ -975,7 +966,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window,
             g_error_free(error);
             g_strfreev (keys);
             LEAVE("bad value");
-            return NULL;
+            return nullptr;
         }
         scm_id = scm_eval_string(scm_from_utf8_string(option_string));
         g_free(option_string);
@@ -984,7 +975,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window,
         {
             DEBUG("report id not an integer for key %s", keys[i]);
             g_strfreev (keys);
-            return NULL;
+            return nullptr;
         }
 
         if (final_id == SCM_BOOL_F)
@@ -1000,7 +991,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window,
     if (final_id == SCM_BOOL_F)
     {
         LEAVE("report not specified");
-        return NULL;
+        return nullptr;
     }
 
     report_id = scm_to_int(final_id);
@@ -1008,7 +999,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window,
     if (!report)
     {
         LEAVE("report doesn't exist");
-        return NULL;
+        return nullptr;
     }
 
     page = gnc_plugin_page_report_new( report_id );
@@ -1035,7 +1026,7 @@ gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name)
     const gchar *old_name;
 
     g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(page));
-    g_return_if_fail(name != NULL);
+    g_return_if_fail(name != nullptr);
 
     ENTER("page %p, name %s", page, name);
     priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page);
@@ -1107,10 +1098,8 @@ gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv)
         scm_call_2(set_editor, SCM_CAR(edited), SCM_BOOL_F);
         if (editor != SCM_BOOL_F)
         {
-            GtkWidget *w = NULL;
 #define FUNC_NAME "gtk_widget_destroy"
-            w = SWIG_MustGetPtr(editor,
-                                SWIG_TypeQuery("_p_GtkWidget"), 1, 0);
+            auto w{static_cast<GtkWidget*>(SWIG_MustGetPtr(editor, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))};
 #undef FUNC_NAME
             gtk_widget_destroy(GTK_WIDGET(w));
         }
@@ -1120,13 +1109,13 @@ gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv)
     {
 //Remove this if there's a double-free
         gnc_option_db_destroy(priv->initial_odb);
-        priv->initial_odb = NULL;
+        priv->initial_odb = nullptr;
     }
 
     gnc_html_destroy(priv->html);
 
-    priv->container     = NULL;
-    priv->html          = NULL;
+    priv->container     = nullptr;
+    priv->html          = nullptr;
 
     if (priv->cur_report != SCM_BOOL_F)
         scm_gc_unprotect_object(priv->cur_report);
@@ -1147,12 +1136,12 @@ static action_toolbar_labels toolbar_labels[] =
        to be used as toolbar button label. */
     { "ReportSaveAsAction", N_("Save Config As...") },
     { "FilePrintPDFAction", N_("Make Pdf") },
-    { NULL, NULL },
+    { nullptr, nullptr },
 };
 
 static const gchar *initially_insensitive_actions[] =
 {
-    NULL
+    nullptr
 };
 
 static void
@@ -1212,7 +1201,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report
             G_CALLBACK(gnc_plugin_page_report_print_cb)
         },
         {
-            "FilePrintPDFAction", GNC_ICON_PDF_EXPORT, N_("Export as P_DF..."), NULL,
+            "FilePrintPDFAction", GNC_ICON_PDF_EXPORT, N_("Export as P_DF..."), nullptr,
             N_("Export the current report as a PDF document"),
             G_CALLBACK(gnc_plugin_page_report_exportpdf_cb)
         },
@@ -1220,7 +1209,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report
         {
             "EditCutAction", "edit-cut", N_("Cu_t"), "<primary>X",
             N_("Cut the current selection and copy it to clipboard"),
-            NULL
+            nullptr
         },
         {
             "EditCopyAction", "edit-copy", N_("_Copy"), "<primary>C",
@@ -1230,7 +1219,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report
         {
             "EditPasteAction", "edit-paste", N_("_Paste"), "<primary>V",
             N_("Paste the clipboard content at the cursor position"),
-            NULL
+            nullptr
         },
         {
             "ViewRefreshAction", "view-refresh", N_("_Refresh"), "<primary>r",
@@ -1246,33 +1235,33 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report
             report_saveas_str, G_CALLBACK(gnc_plugin_page_report_save_as_cb)
         },
         {
-            "ReportExportAction", "go-next", N_("Export _Report"), NULL,
+            "ReportExportAction", "go-next", N_("Export _Report"), nullptr,
             N_("Export HTML-formatted report to file"),
             G_CALLBACK(gnc_plugin_page_report_export_cb)
         },
         {
-            "ReportOptionsAction", "document-properties", N_("_Report Options"), NULL,
+            "ReportOptionsAction", "document-properties", N_("_Report Options"), nullptr,
             N_("Edit report options"),
             G_CALLBACK(gnc_plugin_page_report_options_cb)
         },
 
         {
-            "ReportBackAction", "go-previous", N_("Back"), NULL,
+            "ReportBackAction", "go-previous", N_("Back"), nullptr,
             N_("Move back one step in the history"),
             G_CALLBACK(gnc_plugin_page_report_back_cb)
         },
         {
-            "ReportForwAction", "go-next", N_("Forward"), NULL,
+            "ReportForwAction", "go-next", N_("Forward"), nullptr,
             N_("Move forward one step in the history"),
             G_CALLBACK(gnc_plugin_page_report_forw_cb)
         },
         {
-            "ReportReloadAction", "view-refresh", N_("Reload"), NULL,
+            "ReportReloadAction", "view-refresh", N_("Reload"), nullptr,
             N_("Reload the current page"),
             G_CALLBACK(gnc_plugin_page_report_reload_cb)
         },
         {
-            "ReportStopAction", "process-stop", N_("Stop"), NULL,
+            "ReportStopAction", "process-stop", N_("Stop"), nullptr,
             N_("Cancel outstanding HTML requests"),
             G_CALLBACK(gnc_plugin_page_report_stop_cb)
         },
@@ -1294,7 +1283,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report
                  "page-uri",       "default:",
                  "ui-description", "gnc-plugin-page-report-ui.xml",
                  "use-new-window", use_new,
-                 NULL);
+                 nullptr);
     g_free(name);
 
     /* change me when the system supports multiple books */
@@ -1321,11 +1310,9 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report
 GncPluginPage*
 gnc_plugin_page_report_new( int reportId )
 {
-    GncPluginPageReport *plugin_page;
-
     DEBUG( "report id = %d", reportId );
-    plugin_page = g_object_new( GNC_TYPE_PLUGIN_PAGE_REPORT,
-                                "report-id", reportId, NULL );
+    auto plugin_page{g_object_new(GNC_TYPE_PLUGIN_PAGE_REPORT, "report-id",
+                                  reportId, nullptr)};
     DEBUG( "plugin_page: %p", plugin_page );
     DEBUG( "set %d on page %p", reportId, plugin_page );
     return GNC_PLUGIN_PAGE( plugin_page );
@@ -1361,8 +1348,7 @@ gnc_plugin_page_report_raise_editor(SCM report)
     SCM get_editor = scm_c_eval_string("gnc:report-editor-widget");
     SCM editor = scm_call_1(get_editor, report);
 #define FUNC_NAME "gtk_window_present"
-    GtkWidget *w = SWIG_MustGetPtr(editor,
-                                   SWIG_TypeQuery("_p_GtkWidget"), 1, 0);
+    auto w{static_cast<GtkWidget *>(SWIG_MustGetPtr(editor, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))};
 #undef FUNC_NAME
     gtk_window_present(GTK_WINDOW(w));
 }
@@ -1402,7 +1388,7 @@ static void
 gnc_plugin_page_report_forw_cb( GtkAction *action, GncPluginPageReport *report )
 {
     GncPluginPageReportPrivate *priv;
-    gnc_html_history_node * node = NULL;
+    gnc_html_history_node * node = nullptr;
 
     DEBUG( "forw" );
     priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
@@ -1469,8 +1455,8 @@ gnc_plugin_page_report_reload_cb( GtkAction *action, GncPluginPageReport *report
 
     gnc_plugin_page_report_set_progressbar( page, FALSE );
 
-    // this resets the window for the progressbar to NULL
-    gnc_window_set_progressbar_window( NULL );
+    // this resets the window for the progressbar to nullptr
+    gnc_window_set_progressbar_window( nullptr );
     priv->reloading = FALSE;
 }
 
@@ -1488,7 +1474,7 @@ gnc_plugin_page_report_stop_cb( GtkAction *action, GncPluginPageReport *report )
 static SCM
 gnc_get_export_type_choice (SCM export_types, GtkWindow *parent)
 {
-    GList * choices = NULL;
+    GList * choices = nullptr;
     gboolean bad = FALSE;
     GList * node;
     int choice;
@@ -1531,7 +1517,7 @@ gnc_get_export_type_choice (SCM export_types, GtkWindow *parent)
         choice = gnc_choose_radio_option_dialog
             (GTK_WIDGET (parent), _("Choose export format"),
              _("Choose the export format for this report:"),
-             NULL, 0, choices);
+             nullptr, 0, choices);
     }
     else
         choice = -1;
@@ -1573,21 +1559,21 @@ gnc_get_export_filename (SCM choice, GtkWindow *parent)
     title = g_strdup_printf (_("Save %s To File"), type);
     default_dir = gnc_get_default_directory(GNC_PREFS_GROUP_REPORT);
 
-    filepath = gnc_file_dialog (parent, title, NULL, default_dir,
+    filepath = gnc_file_dialog (parent, title, nullptr, default_dir,
                                 GNC_FILE_DIALOG_EXPORT);
 
-    if (filepath != NULL) // test for cancel pressed
+    if (filepath != nullptr) // 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);
+        if (g_strrstr(filepath, ".") == nullptr)
+            filepath = g_strconcat(filepath, ".", g_ascii_strdown(type, strlen(type)), nullptr);
     }
     g_free (type);
     g_free (title);
     g_free (default_dir);
 
     if (!filepath)
-        return NULL;
+        return nullptr;
 
     default_dir = g_path_get_dirname(filepath);
     gnc_set_default_directory (GNC_PREFS_GROUP_REPORT, default_dir);
@@ -1603,7 +1589,7 @@ gnc_get_export_filename (SCM choice, GtkWindow *parent)
 
         gnc_error_dialog (parent, format, strerror(errno));
         g_free(filepath);
-        return NULL;
+        return nullptr;
     }
 
     /* Check for a file that isn't a regular file. */
@@ -1613,7 +1599,7 @@ gnc_get_export_filename (SCM choice, GtkWindow *parent)
 
         gnc_error_dialog (parent, "%s", message);
         g_free(filepath);
-        return NULL;
+        return nullptr;
     }
 
     if (rc == 0)
@@ -1624,7 +1610,7 @@ gnc_get_export_filename (SCM choice, GtkWindow *parent)
         if (!gnc_verify_dialog (parent, FALSE, format, filepath))
         {
             g_free(filepath);
-            return NULL;
+            return nullptr;
         }
     }
 
@@ -1742,7 +1728,7 @@ return a document object with export-string or export-error."));
 
             if (scm_is_string (export_string))
             {
-                GError *err = NULL;
+                GError *err = nullptr;
                 gchar *exported = scm_to_utf8_string (export_string);
                 if (!g_file_set_contents (filepath, exported, -1, &err))
                     gnc_error_dialog (parent, "Error during export: %s", err->message);
@@ -1805,8 +1791,8 @@ lookup_invoice(GncPluginPageReportPrivate *priv)
 
 static gchar *report_create_jobname(GncPluginPageReportPrivate *priv)
 {
-    gchar *job_name = NULL;
-    gchar *report_name = NULL;
+    gchar *job_name = nullptr;
+    gchar *report_name = nullptr;
     const gchar *report_number = "";
     gchar *job_date;
     const gchar *default_jobname = N_("GnuCash-Report");
@@ -1830,7 +1816,7 @@ static gchar *report_create_jobname(GncPluginPageReportPrivate *priv)
 
         date_format_string = qof_date_format_get_string (date_format_here);
 
-        job_date = gnc_print_time64 (gnc_time (NULL), date_format_string);
+        job_date = gnc_print_time64 (gnc_time (nullptr), date_format_string);
         g_free (format_code);
     }
 
@@ -1909,7 +1895,7 @@ static gchar *report_create_jobname(GncPluginPageReportPrivate *priv)
 
         // Lookup the existing usage count
         value = g_hash_table_lookup(static_report_printnames, job_name);
-        already_found = (value != NULL);
+        already_found = (value != nullptr);
         if (!value)
         {
             value = GINT_TO_POINTER(0);
@@ -1957,7 +1943,7 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep
     GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report);
     gchar *job_name = report_create_jobname(priv);
     GncInvoice *invoice;
-    GncOwner *owner = NULL;
+    GncOwner *owner = nullptr;
 
     // Do we have an invoice report?
     invoice = lookup_invoice(priv);
@@ -1968,15 +1954,15 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep
         if (owner)
         {
 	    QofInstance *inst = qofOwnerGetOwner (owner);
-	    gchar *dirname = NULL;
-	    qof_instance_get (inst, "export-pdf-dir", &dirname, NULL);
+	    gchar *dirname = nullptr;
+	    qof_instance_get (inst, "export-pdf-dir", &dirname, nullptr);
             // Yes. In the kvp, look up the key for the Export-PDF output
             // directory. If it exists, prepend this to the job name so that
             // we can export to PDF.
 	    if (dirname && g_file_test(dirname,
-				       G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
+				       (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
 	    {
-		gchar *tmp = g_build_filename(dirname, job_name, NULL);
+		gchar *tmp = g_build_filename(dirname, job_name, nullptr);
 		g_free(job_name);
 		job_name = tmp;
 	    }
@@ -2005,7 +1991,8 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep
             const char* dirname = gtk_print_settings_get(print_settings,
                                   GNC_GTK_PRINT_SETTINGS_EXPORT_DIR);
             // Only store the directory if it exists.
-            if (g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
+            if (g_file_test(dirname,
+                            (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
             {
                 QofInstance *inst = qofOwnerGetOwner(owner);
                 gncOwnerBeginEdit(owner);
diff --git a/gnucash/gnome/gnc-plugin-page-report.h b/gnucash/gnome/gnc-plugin-page-report.h
index 55da7a652..46ba3ca50 100644
--- a/gnucash/gnome/gnc-plugin-page-report.h
+++ b/gnucash/gnome/gnc-plugin-page-report.h
@@ -34,6 +34,11 @@
 #ifndef __GNC_PLUGIN_PAGE_REPORT_H
 #define __GNC_PLUGIN_PAGE_REPORT_H
 
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
 #include <gtk/gtk.h>
 #include "gnc-plugin.h"
 
@@ -85,7 +90,9 @@ void       gnc_main_window_open_report (int report_id, GncMainWindow *window);
 void       gnc_main_window_open_report_url (const char * url, GncMainWindow *window);
 
 G_END_DECLS
-
+#ifdef __cplusplus
+}
+#endif
 #endif /* __GNC_PLUGIN_PAGE_REPORT_H */
 /** @} */
 /** @} */
diff --git a/gnucash/gnome/window-report.c b/gnucash/gnome/window-report.cpp
similarity index 91%
rename from gnucash/gnome/window-report.c
rename to gnucash/gnome/window-report.cpp
index 25fb3f6aa..10a3d23f1 100644
--- a/gnucash/gnome/window-report.c
+++ b/gnucash/gnome/window-report.cpp
@@ -24,25 +24,26 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
  *                                                                  *
  ********************************************************************/
-
+#include <libguile.h>
+extern "C"
+{
 #include <config.h>
 
 #include <glib/gi18n.h>
 #include <errno.h>
-#include <libguile.h>
 #include <sys/stat.h>
 
 #include "swig-runtime.h"
 #include "dialog-options.h"
-#include "dialog-report-column-view.h"
 #include "gnc-guile-utils.h"
 #include "gnc-report.h"
 #include "gnc-ui.h"
-#include "option-util.h"
 #include "window-report.h"
 #include "guile-mappings.h"
 
 #include "gnc-plugin-page-report.h"
+}
+#include "dialog-report-column-view.hpp"
 
 /********************************************************************
  *
@@ -51,9 +52,9 @@
 void
 reportWindow(int report_id, GtkWindow *parent)
 {
-    gnc_set_busy_cursor (NULL, TRUE);
+    gnc_set_busy_cursor (nullptr, TRUE);
     gnc_main_window_open_report(report_id, GNC_MAIN_WINDOW(parent));
-    gnc_unset_busy_cursor (NULL);
+    gnc_unset_busy_cursor (nullptr);
 }
 
 /********************************************************************
@@ -73,15 +74,15 @@ gnc_options_dialog_apply_cb(GNCOptionWin * propertybox,
                             gpointer user_data)
 {
     SCM  dirty_report = scm_c_eval_string("gnc:report-set-dirty?!");
-    struct report_default_params_data * win = user_data;
-    GList *results = NULL, *iter;
+    auto win{static_cast<struct report_default_params_data*>(user_data)};
+    GList *results = nullptr, *iter;
 
     if (!win) return;
     results = gnc_option_db_commit (win->odb);
     for (iter = results; iter; iter = iter->next)
     {
         GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW (win->win),
-                                                   0,
+                                                 static_cast<GtkDialogFlags>(0),
                                                    GTK_MESSAGE_ERROR,
                                                    GTK_BUTTONS_OK,
                                                    "%s",
@@ -100,7 +101,7 @@ gnc_options_dialog_help_cb(GNCOptionWin * propertybox,
                            gpointer user_data)
 {
     GtkWidget *dialog, *parent;
-    struct report_default_params_data * prm = user_data;
+    auto prm{static_cast<struct report_default_params_data*>(user_data)};
 
     parent = gnc_options_dialog_widget(prm->win);
     dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
@@ -110,7 +111,7 @@ gnc_options_dialog_help_cb(GNCOptionWin * propertybox,
                                     "%s",
                                     _("Set the report options you want using this dialog."));
     g_signal_connect(G_OBJECT(dialog), "response",
-                     (GCallback)gtk_widget_destroy, NULL);
+                     (GCallback)gtk_widget_destroy, nullptr);
     gtk_widget_show(dialog);
 }
 
@@ -118,7 +119,7 @@ static void
 gnc_options_dialog_close_cb(GNCOptionWin * propertybox,
                             gpointer user_data)
 {
-    struct report_default_params_data * win = user_data;
+    auto win{static_cast<struct report_default_params_data*>(user_data)};
     SCM    set_editor = scm_c_eval_string("gnc:report-set-editor-widget!");
 
     scm_call_2(set_editor, win->cur_report, SCM_BOOL_F);
@@ -135,8 +136,7 @@ gnc_report_raise_editor(SCM report)
     if (editor != SCM_BOOL_F)
     {
 #define FUNC_NAME "gnc-report-raise-editor"
-        GtkWidget *w = SWIG_MustGetPtr(editor,
-                                   SWIG_TypeQuery("_p_GtkWidget"), 1, 0);
+        auto w{static_cast<GtkWidget *>(SWIG_MustGetPtr(editor, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))};
 #undef FUNC_NAME
         gtk_window_present(GTK_WINDOW(w));
         return TRUE;
@@ -155,10 +155,10 @@ gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report,
     SCM get_template_name = scm_c_eval_string("gnc:report-template-name");
     SCM ptr;
 
-    const gchar *title = NULL;
+    const gchar *title = nullptr;
 
     if (gnc_report_raise_editor (report))
-        return NULL;
+        return nullptr;
     else
     {
         struct report_default_params_data * prm =
@@ -212,7 +212,7 @@ gnc_report_edit_options(SCM report, GtkWindow *parent)
     SCM ptr;
     SCM options;
     GncOptionDB* odb;
-    GtkWidget *options_widget = NULL;
+    GtkWidget *options_widget = nullptr;
 
     /* If the options editor widget already exists we simply raise it */
     if (gnc_report_raise_editor (report))
@@ -233,11 +233,9 @@ gnc_report_edit_options(SCM report, GtkWindow *parent)
     if (scm_is_string(ptr))
     {
         gchar *rpt_type = gnc_scm_to_utf8_string (ptr);
-#if 0
         if (g_strcmp0 (rpt_type, "d8ba4a2e89e8479ca9f6eccdeb164588") == 0)
             options_widget = gnc_column_view_edit_options (odb, report);
         else
-#endif
             options_widget = gnc_report_window_default_params_editor (odb, report, parent);
         g_free (rpt_type);
     }
diff --git a/gnucash/gnome/window-report.h b/gnucash/gnome/window-report.h
index 17ff0195c..1ecaf626e 100644
--- a/gnucash/gnome/window-report.h
+++ b/gnucash/gnome/window-report.h
@@ -24,7 +24,10 @@
 #define GNC_REPORT_WINDOW_H
 
 #include <libguile.h>
-
+#ifdef __cplusplus
+extern "C"
+{
+#endif
 //#include "gnc-html.h"
 #include "qof.h"
 #include <gnc-optiondb.h>
@@ -42,4 +45,7 @@ GtkWidget * gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report
 void       reportWindow(int id, GtkWindow *parent);
 gboolean   gnc_report_edit_options(SCM report, GtkWindow *parent);
 
+#ifdef __cplusplus
+}
+#endif
 #endif
diff --git a/gnucash/html/gnc-html-webkit2.c b/gnucash/html/gnc-html-webkit2.c
index a892a4cb9..3ab0f0ad5 100644
--- a/gnucash/html/gnc-html-webkit2.c
+++ b/gnucash/html/gnc-html-webkit2.c
@@ -587,8 +587,8 @@ perform_navigation_policy (WebKitWebView *web_view,
                GncHtml *self)
 {
      WebKitURIRequest *req = NULL;
-     const gchar* uri; // Can't init it here.
-     gchar *scheme = NULL, *location = NULL, *label = NULL;
+     const gchar* uri, *scheme; // Can't init it here.
+     gchar *location = NULL, *label = NULL;
      gboolean ignore = FALSE;
      WebKitNavigationAction *action =
       webkit_navigation_policy_decision_get_navigation_action (decision);
diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c
index 45228b697..ee0e1d64a 100644
--- a/libgnucash/app-utils/option-util.c
+++ b/libgnucash/app-utils/option-util.c
@@ -702,7 +702,7 @@ gnc_option_default_getter (GNCOption *option)
  * Args: option - the GNCOption                                     *
  * Returns: SCM handle to function                                  *
 \********************************************************************/
-SCM
+static SCM
 gnc_option_value_validator(GNCOption *option)
 {
     initialize_getters ();
@@ -723,7 +723,7 @@ gnc_option_value_validator(GNCOption *option)
  * Returns: SCM handle to function                                  *
  *          If no such function exists, returns SCM_UNDEFINED.      *
 \********************************************************************/
-SCM
+static SCM
 gnc_option_widget_changed_proc_getter(GNCOption *option)
 {
     SCM cb;
diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h
index 4c1676e35..953eb651d 100644
--- a/libgnucash/app-utils/option-util.h
+++ b/libgnucash/app-utils/option-util.h
@@ -97,8 +97,6 @@ char * gnc_option_documentation (GNCOption *option);
 SCM    gnc_option_getter (GNCOption *option);
 SCM    gnc_option_setter (GNCOption *option);
 SCM    gnc_option_default_getter (GNCOption *option);
-SCM    gnc_option_value_validator (GNCOption *option);
-SCM    gnc_option_widget_changed_proc_getter (GNCOption *option);
 SCM    gnc_option_get_option_data (GNCOption *option);
 
 int    gnc_option_num_permissible_values (GNCOption *option);
diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h
index 5932ecab1..0a7f90e86 100644
--- a/libgnucash/engine/qofbook.h
+++ b/libgnucash/engine/qofbook.h
@@ -44,8 +44,7 @@
 #include <glib.h> //To preempt it being included extern "C" in a later header.
 class GncOptionDB;
 #else
-#include <option-util.h>
-typedef GNCOptionDB GncOptionDB;
+typedef struct GncOptionDB GncOptionDB;
 #endif
 #ifdef __cplusplus
 extern "C"
diff --git a/po/POTFILES.in b/po/POTFILES.in
index fb268e6b3..b0e1da8fa 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -48,7 +48,7 @@ borrowed/libc/setenv.c
 borrowed/libc/strptime.c
 doc/tip_of_the_day.list.c
 gnucash/gnome/assistant-acct-period.c
-gnucash/gnome/assistant-hierarchy.c
+gnucash/gnome/assistant-hierarchy.cpp
 gnucash/gnome/assistant-loan.cpp
 gnucash/gnome/assistant-stock-split.c
 gnucash/gnome/business-gnome-utils.c
@@ -77,8 +77,8 @@ gnucash/gnome/dialog-price-edit-db.c
 gnucash/gnome/dialog-price-editor.c
 gnucash/gnome/dialog-print-check.c
 gnucash/gnome/dialog-progress.c
-gnucash/gnome/dialog-report-column-view.c
-gnucash/gnome/dialog-report-style-sheet.c
+gnucash/gnome/dialog-report-column-view.cpp
+gnucash/gnome/dialog-report-style-sheet.cpp
 gnucash/gnome/dialog-sx-editor2.c
 gnucash/gnome/dialog-sx-editor.c
 gnucash/gnome/dialog-sx-from-trans.c
@@ -96,7 +96,7 @@ gnucash/gnome/gnc-plugin-page-invoice.c
 gnucash/gnome/gnc-plugin-page-owner-tree.c
 gnucash/gnome/gnc-plugin-page-register2.c
 gnucash/gnome/gnc-plugin-page-register.c
-gnucash/gnome/gnc-plugin-page-report.c
+gnucash/gnome/gnc-plugin-page-report.cpp
 gnucash/gnome/gnc-plugin-page-sx-list.c
 gnucash/gnome/gnc-plugin-register2.c
 gnucash/gnome/gnc-plugin-register.c
@@ -112,7 +112,7 @@ gnucash/gnome/top-level.c
 gnucash/gnome/window-autoclear.c
 gnucash/gnome/window-reconcile2.c
 gnucash/gnome/window-reconcile.c
-gnucash/gnome/window-report.c
+gnucash/gnome/window-report.cpp
 gnucash/gnome-search/dialog-search.c
 gnucash/gnome-search/gnc-general-search.c
 gnucash/gnome-search/search-account.c
@@ -172,7 +172,7 @@ gnucash/gnome-utils/gnc-gtk-utils.c
 gnucash/gnome-utils/gnc-gui-query.c
 gnucash/gnome-utils/gnc-icons.c
 gnucash/gnome-utils/gnc-keyring.c
-gnucash/gnome-utils/gnc-main-window.c
+gnucash/gnome-utils/gnc-main-window.cpp
 gnucash/gnome-utils/gnc-menu-extensions.c
 gnucash/gnome-utils/gnc-menu-extensions.scm
 gnucash/gnome-utils/gnc-period-select.c

commit ea835b31b791c1348a20f8bc255f6faceda511e8
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Oct 6 10:14:10 2020 -0700

    Provide GncOptionPtr& overloads for register-option functions.
    
    Simplifies and makes safer calls from Scheme and C++ by not requiring
    a raw pointer.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index cc469d7a1..8ad69ddb8 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -215,7 +215,7 @@ GncOptionDB::find_option(const std::string& section, const char* name) const
      */
     if (alias && alias->first)
         return find_option(alias->first, alias->second);
-        return nullptr;
+    return nullptr;
 }
 
 std::string
@@ -1497,11 +1497,11 @@ gnc_option_db_set_scm_value(GncOptionDB*, const char*, const char*, SCM)
 }
 
 // Force creation of templates
-template void gnc_register_number_range_option(GncOptionDB* db,
+template void gnc_register_number_range_option(GncOptionDBPtr& db,
                                       const char* section, const char* name,
                                       const char* key, const char* doc_string,
                                       int value, int min, int max, int step);
-template void gnc_register_number_range_option(GncOptionDB* db,
+template void gnc_register_number_range_option(GncOptionDBPtr& db,
                                       const char* section, const char* name,
                                       const char* key, const char* doc_string,
                                       double value, double min,
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 75c0ac696..de0f13aff 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -45,6 +45,7 @@ extern "C"
 
 
 class GncOptionDB;
+using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
 using GncOptionAccountList = std::vector<const Account*>;
 
 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
@@ -72,43 +73,221 @@ GncOptionAccountList
 gnc_account_list_from_types(QofBook *book,
                             const GncOptionAccountTypeList& types);
 
-
+/**
+ * Create a new string option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_string_option(GncOptionDB* db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, std::string value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_string_option(const GncOptionDBPtr& db,
+                                       const char* section, const char* name,
+                                       const char* key, const char* doc_string,
+                                       std::string value)
+{
+    gnc_register_string_option(db.get(), section, name, key,
+                               doc_string, value);
+}
+
+/**
+ * Create a new text option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_text_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, std::string value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_text_option(const GncOptionDBPtr& db,
+                                     const char* section,
+                                     const char* name, const char* key,
+                                     const char* doc_string, std::string value)
+{
+    gnc_register_text_option(db.get(), section, name, key, doc_string, value);
+}
+
+/**
+ * Create a new font option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_font_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, std::string value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_font_option(const GncOptionDBPtr& db,
+                                     const char* section, const char* name,
+                                     const char* key, const char* doc_string,
+                                     std::string value)
+{
+    gnc_register_font_option(db.get(), section, name, key, doc_string, value);
+}
+
+/**
+ * Create a new budget option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_budget_option(GncOptionDB* db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, GncBudget* value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_budget_option(const GncOptionDBPtr& db,
+                                       const char* section, const char* name,
+                                       const char* key, const char* doc_string,
+                                       GncBudget* value)
+{
+    gnc_register_budget_option(db.get(), section, name, key, doc_string, value);
+}
+
+/**
+ * Create a new commodity option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_commodity_option(GncOptionDB* db,
                                    const char* section, const char* name,
                                    const char* key, const char* doc_string,
                                    gnc_commodity* value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_commodity_option(const GncOptionDBPtr& db,
+                                          const char* section,
+                                          const char* name, const char* key,
+                                          const char* doc_string,
+                                          gnc_commodity* value)
+{
+    gnc_register_commodity_option(db.get(), section, name, key,
+                                  doc_string, value);
+}
+
+/**
+ * Create a new simple boolean option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_simple_boolean_option(GncOptionDB* db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
                                         bool value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_simple_boolean_option(const GncOptionDBPtr& db,
+                                               const char* section,
+                                               const char* name,
+                                               const char* key,
+                                               const char* doc_string,
+                                               bool value)
+{
+    gnc_register_simple_boolean_option(db.get(), section, name, key,
+                                       doc_string, value);
+}
+
+/**
+ * Create a new complex boolean option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_complex_boolean_option(GncOptionDB* db,
                                          const char* section, const char* name,
                                          const char* key,
                                          const char* doc_string,
                                          bool value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_complex_boolean_option(const GncOptionDBPtr& db,
+                                                const char* section,
+                                                const char* name,
+                                                const char* key,
+                                                const char* doc_string,
+                                                bool value)
+{
+    gnc_register_complex_boolean_option(db.get(), section, name, key,
+                                        doc_string, value);
+}
+
+/**
+ * Create a new pixmap option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_pixmap_option(GncOptionDB* db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, std::string value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_pixmap_option(GncOptionDBPtr& db, const char* section,
+                                       const char* name, const char* key,
+                                       const char* doc_string,
+                                       std::string value)
+{
+    gnc_register_pixmap_option(db.get(), section, name, key, doc_string, value);
+}
+
+/**
+ * Create a new limited account list option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default values for the option.
+ * @param allowed The accounts which are available for selection.
+*/
 void gnc_register_account_list_limited_option(GncOptionDB* db,
                                              const char* section,
                                              const char* name, const char* key,
@@ -116,12 +295,59 @@ void gnc_register_account_list_limited_option(GncOptionDB* db,
                                              const GncOptionAccountList& value,
                                              GncOptionAccountTypeList&& allowed);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_account_list_limited_option(GncOptionDBPtr& db,
+                                             const char* section,
+                                             const char* name, const char* key,
+                                             const char* doc_string,
+                                             const GncOptionAccountList& value,
+                                             GncOptionAccountTypeList&& allowed)
+{
+    gnc_register_account_list_limited_option(db.get(), section, name, key,
+                                             doc_string, value,
+                                             std::move(allowed));
+}
+
+/**
+ * Create a new account list option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default values for the option.
+ */
 void gnc_register_account_list_option(GncOptionDB* db,
                                       const char* section,
                                       const char* name, const char* key,
                                       const char* doc_string,
                                       const GncOptionAccountList& value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_account_list_option(GncOptionDBPtr& db,
+                                      const char* section,
+                                      const char* name, const char* key,
+                                      const char* doc_string,
+                                      const GncOptionAccountList& value)
+{
+    gnc_register_account_list_option(db.get(), section, name, key,
+                                     doc_string, value);
+}
+
+/**
+ * Create a limited account selection option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ * @param allowed The accounts which are available for selection.
+ */
 void gnc_register_account_sel_limited_option(GncOptionDB* db,
                                              const char* section,
                                              const char* name, const char* key,
@@ -129,16 +355,88 @@ void gnc_register_account_sel_limited_option(GncOptionDB* db,
                                              const GncOptionAccountList& value,
                                              GncOptionAccountTypeList&& allowed);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_account_sel_limited_option(GncOptionDBPtr& db,
+                                             const char* section,
+                                             const char* name, const char* key,
+                                             const char* doc_string,
+                                             const GncOptionAccountList& value,
+                                             GncOptionAccountTypeList&& allowed)
+{
+    gnc_register_account_sel_limited_option(db.get(), section, name, key,
+                                            doc_string, value,
+                                            std::move(allowed));
+}
+
+/**
+ * Create a new multichoice option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The set of possible values for the option. Only one can be selected. Note that the value will be moved from the parameter and using the parameter after this call will result in undefined behavior.
+ */
 void gnc_register_multichoice_option(GncOptionDB* db,
                                      const char* section, const char* name,
                                      const char* key, const char* doc_string,
                                      GncMultichoiceOptionChoices&& value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_multichoice_option(GncOptionDBPtr& db,
+                                        const char* section, const char* name,
+                                        const char* key, const char* doc_string,
+                                        GncMultichoiceOptionChoices&& value)
+{
+    gnc_register_multichoice_option(db.get(), section, name,
+                                    key, doc_string, std::move(value));
+}
+
+/**
+ * Create a new list option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ * @param list The values available for selection. Note that this parameter will be moved from so using it after this call will result in undefined behavior.
+ */
 void gnc_register_list_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, const char* value,
                               GncMultichoiceOptionChoices&& list);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_list_option(GncOptionDBPtr& db, const char* section,
+                                     const char* name, const char* key,
+                                     const char* doc_string, const char* value,
+                                     GncMultichoiceOptionChoices&& list)
+{
+    gnc_register_list_option(db.get(), section, name, key, doc_string,
+                             value, std::move(list));
+}
+
+/**
+ * Create a new number range option and register it in the options database.
+ *
+ * These are normally associated with spin controls and ValueType is normally
+ * double, but it's templated to permit other numeric types if needed.
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ * @param min The minimum value for the spin control.
+ * @param max The maximum value for the spin control.
+ * @param step The step size (increment) of the spin control.
+ */
 template <typename ValueType>
 void gnc_register_number_range_option(GncOptionDB* db,
                                       const char* section, const char* name,
@@ -146,56 +444,311 @@ void gnc_register_number_range_option(GncOptionDB* db,
                                       ValueType value, ValueType min,
                                       ValueType max, ValueType step);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+template <typename ValueType>
+void gnc_register_number_range_option(GncOptionDBPtr& db,
+                                      const char* section, const char* name,
+                                      const char* key, const char* doc_string,
+                                      ValueType value, ValueType min,
+                                      ValueType max, ValueType step)
+{
+    gnc_register_number_range_option<ValueType>(db.get(), section, name,
+                                                key, doc_string, value,
+                                                min, max, step);
+}
+
+/**
+ * Create a new plot-size option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_number_plot_size_option(GncOptionDB* db,
                                           const char* section, const char* name,
                                           const char* key,
                                           const char* doc_string,
                                           int value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_number_plot_size_option(const GncOptionDBPtr& db,
+                                                 const char* section,
+                                                 const char* name,
+                                                 const char* key,
+                                                 const char* doc_string,
+                                                 int value)
+{
+    gnc_register_number_plot_size_option(db.get(), section, name, key,
+                                         doc_string, value);
+}
+
+/**
+ * Create a new QofQuery option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_query_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, QofQuery* value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_query_option(GncOptionDBPtr& db, const char* section,
+                                      const char* name, const char* key,
+                                      const char* doc_string, QofQuery* value)
+{
+    gnc_register_query_option(db.get(), section, name, key, doc_string, value);
+}
+
+/**
+ * Create a new color option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_color_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, std::string value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_color_option(GncOptionDBPtr& db, const char* section,
+                                      const char* name, const char* key,
+                                      const char* doc_string,
+                                      std::string value)
+{
+    gnc_register_color_option(db.get(), section, name, key, doc_string, value);
+}
+
+/**
+ * Create a new internal string option and register it in the options database.
+ *
+ * Internal options are used for passing state data in some reports. As the name
+ * suggests they do not create UI elements in options dialogs.
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_internal_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, SCM value);
 
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_internal_option(GncOptionDBPtr& db,
+                                         const char* section, const char* name,
+                                         const char* key,
+                                         const char* doc_string,
+                                         SCM value)
+{
+    gnc_register_internal_option(db.get(), section, name, key,
+                                 doc_string, value);
+}
+
+/**
+ * Create a new currency option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option. It is checked with gnc_commodity_is_currency.
+ */
 void gnc_register_currency_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, gnc_commodity* value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_currency_option(const GncOptionDBPtr& db,
+                                          const char* section,
+                                          const char* name, const char* key,
+                                          const char* doc_string,
+                                          gnc_commodity* value)
+{
+    gnc_register_currency_option(db.get(), section, name, key,
+                                 doc_string, value);
+}
+
+/**
+ * Create a new invoice option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_invoice_option(GncOptionDB* db, const char* section,
                                  const char* name, const char* key,
                                  const char* doc_string, GncInvoice* value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_invoice_option(const GncOptionDBPtr& db,
+                                        const char* section,
+                                        const char* name, const char* key,
+                                        const char* doc_string,
+                                        GncInvoice* value)
+{
+    gnc_register_invoice_option(db.get(), section, name, key,
+                                doc_string, value);
+}
+
+/**
+ * Create a new owner-type option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ * @param type The Owner-type (Customer, Employee, or Vendor)
+ */
 void gnc_register_owner_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, GncOwner* value,
                                GncOwnerType type);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_owner_option(const GncOptionDBPtr& db,
+                                      const char* section, const char* name,
+                                      const char* key, const char* doc_string,
+                                      GncOwner* value, GncOwnerType type)
+{
+    gnc_register_owner_option(db.get(), section, name, key,
+                              doc_string, value, type);
+}
+
+/**
+ * Create a new taxtable option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_taxtable_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, GncTaxTable* value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_taxtable_option(const GncOptionDBPtr& db,
+                                         const char* section, const char* name,
+                                         const char* key,
+                                         const char* doc_string,
+                                         GncTaxTable* value)
+{
+    gnc_register_taxtable_option(db.get(), section, name, key,
+                                 doc_string, value);
+}
+
+/**
+ * Create a new counter option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_counter_option(GncOptionDB* db, const char* section,
                                  const char* name, const char* key,
                                  const char* doc_string, double value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_counter_option(const GncOptionDBPtr& db,
+                                        const char* section, const char* name,
+                                        const char* key, const char* doc_string,
+                                        double value)
+{
+    gnc_register_counter_option(db.get(), section, name, key,
+                                  doc_string, value);
+}
+
+/**
+ * Create a new counter format option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_counter_format_option(GncOptionDB* db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
                                         std::string value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_counter_format_option(GncOptionDBPtr& db,
+                                               const char* section,
+                                               const char* name,
+                                               const char* key,
+                                               const char* doc_string,
+                                               std::string value)
+{
+    gnc_register_counter_format_option(db.get(), section, name, key,
+                                       doc_string, value);
+}
+
+/**
+ * Create a new date format option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param value The initial and default value for the option.
+ */
 void gnc_register_dateformat_option(GncOptionDB* db,
                                     const char* section, const char* name,
                                     const char* key, const char* doc_string,
                                     std::string value);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_dateformat_option(GncOptionDBPtr& db,
+                                           const char* section,
+                                           const char* name, const char* key,
+                                           const char* doc_string,
+                                           std::string value)
+{
+    gnc_register_dateformat_option(db.get(), section, name, key,
+                                   doc_string, value);
+}
+
 enum RelativeDateUI : uint8_t
 {
     ABSOLUTE,
@@ -203,6 +756,16 @@ enum RelativeDateUI : uint8_t
     BOTH
 };
 
+/**
+ * Create a new date option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param period The default/starting relative date value for the option.
+ * @param ui What UI to display, relative, absolute, or both.
+*/
 void gnc_register_date_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string,
@@ -210,25 +773,126 @@ void gnc_register_date_option(GncOptionDB* db, const char* section,
                               RelativeDatePeriod::TODAY,
                               RelativeDateUI ui = RelativeDateUI::BOTH);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_date_option(GncOptionDBPtr& db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string,
+                              RelativeDatePeriod period =
+                              RelativeDatePeriod::TODAY,
+                              RelativeDateUI ui = RelativeDateUI::BOTH)
+{
+    gnc_register_date_option(db.get(), section, name, key, doc_string,
+                             period, ui);
+}
+
+/**
+ * Create a new date option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param time The initial time to set in the option.
+ * @param ui What UI to display, relative, absolute, or both.
+ */
 void gnc_register_date_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, time64 time,
                               RelativeDateUI ui = RelativeDateUI::BOTH);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_date_option(GncOptionDBPtr& db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string, time64 time,
+                              RelativeDateUI ui = RelativeDateUI::BOTH)
+{
+    gnc_register_date_option(db.get(), section, name, key, doc_string,
+                             time, ui);
+}
+
+/**
+ * Create a new date option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param period_set A vector of relative date periods to display in the relative control.
+ * @param both Whether to display both a relative and absolute control or a onla a relative control.
+*/
 void gnc_register_date_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string,
                               RelativeDatePeriodVec& period_set,
                               bool both = true);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_date_option(GncOptionDBPtr& db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string,
+                              RelativeDatePeriodVec& period_set,
+                              bool both = true)
+{
+    gnc_register_date_option(db.get(), section, name, key, doc_string,
+                             period_set, both);
+}
+
+/**
+ * Create a new start-date option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param both Whether to display both a relative and absolute control or a onla a relative control.
+ */
 void gnc_register_start_date_option(GncOptionDB* db,
                                     const char* section,
                                     const char* name, const char* key,
                                     const char* doc_string, bool both = true);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_start_date_option(GncOptionDBPtr& db,
+                                    const char* section,
+                                    const char* name, const char* key,
+                                    const char* doc_string, bool both = true)
+{
+    gnc_register_start_date_option(db.get(), section, name, key, doc_string,
+                                   both);
+}
+
+/**
+ * Create a new end-date option and register it in the options database.
+ *
+ * @param db A GncOptionDB* for calling from C. Caller retains ownership.
+ * @param section The database section for the option.
+ * @param name The option name.
+ * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation.
+ * @param both Whether to display both a relative and absolute control or a onla a relative control.
+ */
 void gnc_register_end_date_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, bool both = true);
 
+/**
+ * As above but takes a const GncOptionDBPtr& (const std::unique_ptr<GncOptionDB>&) for calling from C++.
+ */
+inline void gnc_register_end_date_option(GncOptionDBPtr& db, const char* section,
+                                  const char* name, const char* key,
+                                  const char* doc_string, bool both = true)
+{
+    gnc_register_end_date_option(db.get(), section, name, key, doc_string,
+                                 both);
+}
+
+
 
 #endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index f620aabe1..cd752e7ce 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -234,7 +234,7 @@ gnc_option_test_book_destroy(QofBook* book)
 %rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD;
 
 %rename(gnc_register_date_option_set)
-    gnc_register_date_option(GncOptionDB*, const char*, const char*,
+    gnc_register_date_option(GncOptionDBPtr&, const char*, const char*,
                              const char*, const char*, RelativeDatePeriodVec&,
                              bool);
 
@@ -364,6 +364,39 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
 %ignore gnc_option_to_scheme;
 %ignore gnc_option_from_scheme;
+/* The following functions are overloaded in gnc-optiondb.hpp to provide both
+ * GncOptionDB* and GncOptionDBPtr& versions. That confuses SWIG so ignore the
+ * raw-ptr version.
+ */
+%ignore gnc_register_string_option(GncOptionDB*, const char* section, const char* name, const char* key, const char* doc_string, std::string value);
+%ignore gnc_register_text_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
+%ignore gnc_register_font_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
+%ignore gnc_register_budget_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncBudget*);
+%ignore gnc_register_commodity_option(GncOptionDB*, const char*, const char*, const char*, const char*, gnc_commodity*);
+%ignore gnc_register_simple_boolean_option(GncOptionDB*, const char* section, const char* name, const char* key, const char* doc_string, bool value);
+%ignore gnc_register_complex_boolean_option(GncOptionDB*, const char* section, const char* name, const char* key, const char* doc_string, bool value);
+%ignore gnc_register_pixmap_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
+%ignore gnc_register_account_list_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&, GncOptionAccountTypeList&&);
+%ignore gnc_register_account_list_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&);
+%ignore gnc_register_account_sel_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&, GncOptionAccountTypeList&&);
+%ignore gnc_register_multichoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&);
+%ignore gnc_register_list_option(GncOptionDB*, const char*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&);
+%ignore gnc_register_number_Plot_size_option(GncOptionDB*, const char*, const char*, const char*, const char*, int);
+%ignore gnc_register_query_option(GncOptionDB*, const char*, const char*, const char*, const char*, QofQuery*);
+%ignore gnc_register_color_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
+%ignore gnc_register_internal_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
+%ignore gnc_register_currency_option(GncOptionDB*, const char*, const char*, const char*, const char*, gnc_commodity*);
+%ignore gnc_register_invoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncInvoice*);
+%ignore gnc_register_owner_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncOwner*, GncOwnerType);
+%ignore gnc_register_taxtable_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncTaxTable*);
+%ignore gnc_register_counter_option(GncOptionDB*, const char*, const char*, const char*, const char*, double);
+%ignore gnc_register_counter_format_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
+%ignore gnc_register_dateformat_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string);
+%ignore gnc_register_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, RelativeDatePeriod, RelativeDateUI);
+%ignore gnc_register_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, time64, RelativeDateUI);
+%ignore gnc_register_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, RelativeDatePeriodVec, bool);
+%ignore gnc_register_start_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
+%ignore gnc_register_end_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool);
 
 %include "gnc-option-date.hpp"
 %include "gnc-option.hpp"
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index eaab1fa35..0acbb633b 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -75,7 +75,7 @@ TEST_F(GncOptionDBTest, test_unregister_option)
 
 TEST_F(GncOptionDBTest, test_register_string_option)
 {
-    gnc_register_string_option(m_db.get(), "foo", "bar", "baz", "Phony Option",
+    gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
                                std::string{"waldo"});
     EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
 }
@@ -131,7 +131,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_option)
 {
     AccountTestBook book;
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
-    gnc_register_account_list_option(m_db.get(), "foo", "bar", "baz",
+    gnc_register_account_list_option(m_db, "foo", "bar", "baz",
                                      "Phony Option", acclist);
     EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
     EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(3));
@@ -141,7 +141,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
 {
     AccountTestBook book;
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
-    gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz",
+    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_STOCK});
     EXPECT_EQ(4, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
@@ -153,7 +153,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
     AccountTestBook book;
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
     GncOptionAccountList accsel{acclist[2]};
-    gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz",
+    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", accsel,
                                              {ACCT_TYPE_STOCK});
     EXPECT_EQ(1, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
@@ -165,10 +165,10 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct)
     AccountTestBook book;
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
     GncOptionAccountList accsel{acclist[2]};
-    gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz", "Phony Option",
+    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option",
                                      accsel, {ACCT_TYPE_BANK});
     EXPECT_FALSE(m_db->find_option("foo", "bar"));
-    gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz",
+    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_BANK});
     EXPECT_FALSE(m_db->find_option("foo", "bar"));
@@ -181,7 +181,7 @@ TEST_F(GncOptionDBTest, test_register_multichoice_option)
         { "waldo", "pepper", "salt"},
         { "pork", "sausage", "links"},
         { "corge", "grault", "garply"}};
-    gnc_register_multichoice_option(m_db.get(), "foo", "bar", "baz",
+    gnc_register_multichoice_option(m_db, "foo", "bar", "baz",
                                     "Phony Option", std::move(choices));
     ASSERT_TRUE(m_db->set_option("foo", "bar", std::string{"corge"}));
     EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str());
@@ -199,7 +199,7 @@ time64_from_gdate(const GDate* g_date, DayPart when)
 
 TEST_F(GncOptionDBTest, test_register_relative_date_option)
 {
-    gnc_register_date_option(m_db.get(), "foo", "bar", "baz", "Phony Option",
+    gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option",
                              RelativeDatePeriod::START_ACCOUNTING_PERIOD);
     GDate prev_year_start;
     g_date_set_time_t(&prev_year_start, time(nullptr));
@@ -213,7 +213,7 @@ TEST_F(GncOptionDBTest, test_register_absolute_date_option)
 {
     time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
 
-    gnc_register_date_option(m_db.get(), "foo", "bar", "baz", "Phony Option",
+    gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option",
                              time1);
     GDate prev_year_start;
     g_date_set_time_t(&prev_year_start, time(nullptr));
@@ -240,7 +240,7 @@ static const RelativeDatePeriodVec begin_dates
 
 TEST_F(GncOptionDBTest, test_register_start_date_option)
 {
-    gnc_register_start_date_option(m_db.get(), "foo", "bar", "baz",
+    gnc_register_start_date_option(m_db, "foo", "bar", "baz",
                                    "Phony Option");
     GDate prev_year_start;
     g_date_set_time_t(&prev_year_start, time(nullptr));
@@ -305,18 +305,18 @@ protected:
         create_account(expenses, ACCT_TYPE_EXPENSE, "Gas");
         create_account(expenses, ACCT_TYPE_EXPENSE, "Rent");
 
-        gnc_register_string_option(m_db.get(), "foo", "bar", "baz",
+        gnc_register_string_option(m_db, "foo", "bar", "baz",
                                    "Phony Option", std::string{"waldo"});
-        gnc_register_text_option(m_db.get(), "foo", "sausage", "links",
+        gnc_register_text_option(m_db, "foo", "sausage", "links",
                                  "Phony Option", std::string{"waldo"});
-        gnc_register_string_option(m_db.get(), "qux", "grault", "baz",
+        gnc_register_string_option(m_db, "qux", "grault", "baz",
                                    "Phony Option", std::string{""});
-        gnc_register_text_option(m_db.get(), "qux", "garply", "fred",
+        gnc_register_text_option(m_db, "qux", "garply", "fred",
                                    "Phony Option", std::string{"waldo"});
-        gnc_register_date_option(m_db.get(), "pork", "garply", "first",
+        gnc_register_date_option(m_db, "pork", "garply", "first",
                                  "Phony Date Option",
                                  RelativeDatePeriod::START_CURRENT_QUARTER);
-        gnc_register_account_list_option(m_db.get(), "quux", "xyzzy", "second",
+        gnc_register_account_list_option(m_db, "quux", "xyzzy", "second",
                                          "Phony AccountList Option",
                                          {aapl, hpe});
     }
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 56f3e9b16..2da34c98b 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -23,7 +23,7 @@
 
 (use-modules (srfi srfi-64))
 (use-modules (tests srfi64-extras))
-(use-modules (gnucash gnc-module))
+;; Load the C++ option implementation, avoiding the options.scm ones.
 (eval-when
  (compile load eval expand)
  (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module"))
@@ -46,7 +46,7 @@
 (define (test-gnc-make-text-option)
   (test-begin "test-gnc-test-string-option")
   (let* ((option-db (new-gnc-optiondb))
-         (string-opt (gnc-register-string-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
+         (string-opt (gnc-register-string-option option-db "foo" "bar" "baz"
                                                  "Phony Option" "waldo")))
     (test-equal "waldo" (gnc-option-value option-db "foo" "bar"))
 
@@ -92,7 +92,7 @@
     (let ((option-db (new-gnc-optiondb))
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
-      (gnc-register-account-list-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
+      (gnc-register-account-list-option option-db "foo" "bar" "baz"
                                         "Phony Option" acctlist)
       (let ((acct-list (gnc-option-value option-db "foo" "bar")))
         (test-equal (length acctlist) (length acct-list))
@@ -104,13 +104,13 @@
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
       (gnc-register-account-list-limited-option
-       (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
+       option-db "foo" "bar" "baz"
        "Phony Option" acctlist (list ACCT-TYPE-STOCK))
       (let ((acct-list (gnc-option-value option-db "foo" "bar")))
         (test-equal (length acctlist) (length acct-list))
         (test-equal (cadr acctlist) (cadr acct-list)))
       (gnc-register-account-list-limited-option
-       (GncOptionDBPtr-get option-db) "waldo" "pepper" "baz"
+       option-db "waldo" "pepper" "baz"
        "Phony Option" acctlist (list ACCT-TYPE-BANK))
       (let ((acct-list (gnc-option-value option-db "waldo" "pepper")))
         (test-equal #f (length acct-list))))))
@@ -121,7 +121,7 @@
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
       (gnc-register-account-sel-limited-option
-       (GncOptionDBPtr-get option-db) "salt" "pork" "baz"
+       option-db "salt" "pork" "baz"
        "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK))
       (let ((acct (gnc-option-value option-db "salt" "pork")))
         (test-equal (list (cadr acctlist)) acct)))))
@@ -158,7 +158,7 @@
                        (list "pork" (cons 'text "sausage") (cons 'tip "links"))
                        (list "corge" (cons 'text "grault") (cons 'tip "garply"))))
          (multichoice (keylist->vectorlist multilist))
-         (multi-opt (gnc-register-multichoice-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
+         (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz"
                                                      "Phony Option" multichoice)))
 
     (test-equal "plugh" (gnc-option-value option-db "foo" "bar"))
@@ -173,7 +173,7 @@
          (value-list (list (vector "AvgBalPlot" "Average" "Average Balance")
                            (vector "GainPlot" "Profit" "Profit (Gain minus Loss)")
                            (vector "GLPlot" "Gain/Loss" "Gain and Loss")))
-         (list-op (gnc-register-list-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
+         (list-op (gnc-register-list-option option-db "foo" "bar" "baz"
                                             "Phony Option" "AvgBalPlot"
                                             value-list)))
     (test-equal "AvgBalPlot" (gnc-option-value option-db "foo" "bar"))
@@ -185,7 +185,7 @@
 (define (test-gnc-make-date-option)
   (test-begin "test-gnc-test-date-option")
   (let* ((option-db (new-gnc-optiondb))
-         (date-opt (gnc-register-date-option (GncOptionDBPtr-get option-db) "foo" "bar"
+         (date-opt (gnc-register-date-option option-db "foo" "bar"
                                              "baz" "Phony Option"
                                              (RelativeDatePeriod-today)))
          (a-time (gnc-dmy2time64 11 07 2019)))
@@ -198,7 +198,7 @@
   (test-begin "test-gnc-test-date-set-option")
   (let* ((option-db (new-gnc-optiondb))
          (date-opt (gnc-register-date-option-set
-                    (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option"
+                    option-db "foo" "bar" "baz" "Phony Option"
                     (list (RelativeDatePeriod-today)
                           (RelativeDatePeriod-start-this-month)
                           (RelativeDatePeriod-start-prev-month)
@@ -214,7 +214,7 @@
 (define (test-gnc-make-number-range-option)
   (test-begin "test-gnc-number-range-option")
   (let* ((option-db (new-gnc-optiondb))
-         (number-opt (gnc-register-number-range-option-double (GncOptionDBPtr-get option-db) "foo" "bar"
+         (number-opt (gnc-register-number-range-option-double option-db "foo" "bar"
                                                        "baz" "Phony Option"
                                                        15 5 30 1)))
     (test-equal 15.0 (gnc-option-value option-db "foo" "bar"))
diff --git a/libgnucash/app-utils/test/test-options.scm b/libgnucash/app-utils/test/test-options.scm
index f7e55eeb3..8e28da919 100644
--- a/libgnucash/app-utils/test/test-options.scm
+++ b/libgnucash/app-utils/test/test-options.scm
@@ -15,13 +15,12 @@
 
 (define (test-lookup-option)
   (let* ((options (new-gnc-optiondb))
-         (string-opt (gnc-register-string-option (GncOptionDBPtr-get options)
-                                                 "Section" "Start Date"
+         (string-opt (gnc-register-string-option options "Section" "Start Date"
                                                  "sort-tag" "docstring" "waldo")
                      ))
 
     (gnc-register-simple-boolean-option
-     (GncOptionDBPtr-get options)
+     options
       "Filter" "Void Transactions" "sort-tag" "docstring" 'default-val)
     ;; Testing that the old option name aliases work.
     (let ((option (gnc-lookup-option options "Section" "From")))

commit 472814d315cb424f488a9c7da208272501294fa3
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Oct 9 14:08:18 2020 -0700

    Change the normal internal option ValueType to SCM.
    
    Internal options are used as a Scheme hack and don't need to be interpreted
    on the C side so there's no point in converting them to C types.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 4dddb407a..0abd8314f 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -274,9 +274,14 @@ template<class OptType,
 std::istream& operator>>(std::istream& iss, OptType& opt)
 {
     std::decay_t<decltype(opt.get_value())> value;
-    iss >> value;
-    opt.set_value(value);
-    return iss;
+    if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, SCM>)
+        return iss;
+    else
+    {
+        iss >> value;
+        opt.set_value(value);
+        return iss;
+    }
 }
 
 template<class OptType,
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 30e874e08..5ba5b5731 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -528,6 +528,8 @@ template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, std::string, GncOptionUIType);
 template GncOption::GncOption(const char*, const char*, const char*,
                               const char*, const QofInstance*, GncOptionUIType);
+template GncOption::GncOption(const char*, const char*, const char*,
+                              const char*, SCM, GncOptionUIType);
 
 template bool GncOption::get_value<bool>() const;
 template int GncOption::get_value<int>() const;
@@ -540,6 +542,7 @@ template const QofInstance* GncOption::get_value<const QofInstance*>() const;
 template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_value<GncMultichoiceOptionIndexVec>() const;
+template SCM GncOption::get_value<SCM>() const;
 
 template bool GncOption::get_default_value<bool>() const;
 template int GncOption::get_default_value<int>() const;
@@ -551,6 +554,7 @@ template const QofInstance* GncOption::get_default_value<const QofInstance*>() c
 template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_default_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_default_value<GncMultichoiceOptionIndexVec>() const;
+template SCM GncOption::get_default_value<SCM>() const;
 
 template void GncOption::set_value(bool);
 template void GncOption::set_value(int);
@@ -564,6 +568,7 @@ template void GncOption::set_value(RelativeDatePeriod);
 template void GncOption::set_value(size_t);
 template void GncOption::set_value(GncOptionAccountList);
 template void GncOption::set_value(GncMultichoiceOptionIndexVec);
+template void GncOption::set_value(SCM);
 
 template void GncOption::get_limits(double&, double&, double&) const noexcept;
 template void GncOption::get_limits(int&, int&, int&) const noexcept;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 7b8e3d4c5..5a9af1093 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -29,6 +29,7 @@ extern "C"
 #include <glib.h>
 }
 
+#include <libguile.h>
 #include <string>
 #include <iostream>
 #include <variant>
@@ -51,6 +52,7 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<bool>,
                                       GncOptionValue<int64_t>,
                                       GncOptionValue<const QofInstance*>,
+                                      GncOptionValue<SCM>,
                                       GncOptionAccountValue,
                                       GncOptionMultichoiceValue,
                                       GncOptionRangeValue<int>,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 1bc1f2fa3..cc469d7a1 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1048,7 +1048,7 @@ gnc_register_query_option(GncOptionDB* db, const char* section,
 void
 gnc_register_internal_option(GncOptionDB* db, const char* section,
                              const char* name, const char* key,
-                             const char* doc_string, std::string value)
+                             const char* doc_string, SCM value)
 {
     GncOption option{section, name, key, doc_string, value,
             GncOptionUIType::INTERNAL};
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 34b812268..75c0ac696 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -29,6 +29,7 @@
 #include <exception>
 #include <optional>
 #include <iostream>
+#include <libguile.h>
 extern "C"
 {
 #include <config.h>
@@ -161,7 +162,7 @@ void gnc_register_color_option(GncOptionDB* db, const char* section,
 
 void gnc_register_internal_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
-                                  const char* doc_string, std::string value);
+                                  const char* doc_string, SCM value);
 
 
 void gnc_register_currency_option(GncOptionDB* db, const char* section,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 7ccc7214e..f620aabe1 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -64,6 +64,12 @@ template <typename ValueType> inline SCM
 /*{
     return SCM_BOOL_F;
     }*/
+template <> inline SCM
+scm_from_value<SCM>(SCM value)
+{
+    return value;
+}
+
 template <> inline SCM
 scm_from_value<QofQuery*>(QofQuery* value)
 {
@@ -118,6 +124,8 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
 template <typename ValueType> inline ValueType
 scm_to_value(SCM new_value)
 {
+    if constexpr (std::is_same_v<std::decay_t<ValueType>, SCM>)
+        return new_value;
     return ValueType{};
 }
 
@@ -369,6 +377,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_value()};
+                if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
+                              SCM>)
+                    return value;
                 return scm_from_value(static_cast<decltype(value)>(value));
             }, swig_get_option($self));
     }
@@ -376,6 +387,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_default_value()};
+                if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
+                              SCM>)
+                    return value;
                 return scm_from_value(static_cast<decltype(value)>(value));
             }, swig_get_option($self));
     }

commit 023db2335349a8d5f15843f199d6cdfd9a769f03
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 8 17:43:07 2020 -0700

    Make URLType an alias for const char* instead of char*.
    
    Because it uses macros declared as string constants as values and C++
    objects if one tries to initialize a non-const char* with a string constant.
    On the other hand one doesn't want to strdup into a const char* because
    then you can't free it, so all of the instances where it's strduped for
    inserting into a container must be changed from URLType to char*.

diff --git a/gnucash/html/gnc-html-extras.h b/gnucash/html/gnc-html-extras.h
index 0bcb4c60e..8bcda0700 100644
--- a/gnucash/html/gnc-html-extras.h
+++ b/gnucash/html/gnc-html-extras.h
@@ -27,7 +27,7 @@
 // gnc-html.i file.  The full gnc-html.h file can't be parsed because of the new
 // use of GObject
 
-typedef gchar* URLType;
+typedef const char* URLType;
 
 #define URL_TYPE_FILE       "file"
 #define URL_TYPE_JUMP       "jump"
diff --git a/gnucash/html/gnc-html-history.h b/gnucash/html/gnc-html-history.h
index 95429e3f2..e4fdfd31d 100644
--- a/gnucash/html/gnc-html-history.h
+++ b/gnucash/html/gnc-html-history.h
@@ -30,7 +30,7 @@ typedef struct _gnc_html_history gnc_html_history;
 
 struct _gnc_html_history_node
 {
-    URLType type;
+    char* type;
     gchar   * location;
     gchar   * label;
 };
diff --git a/gnucash/html/gnc-html.c b/gnucash/html/gnc-html.c
index 4565bdaad..edeb5df43 100644
--- a/gnucash/html/gnc-html.c
+++ b/gnucash/html/gnc-html.c
@@ -359,7 +359,7 @@ gnc_html_show_url( GncHtml* self, URLType type,
                    const gchar* location, const gchar* label,
                    gboolean new_window_hint )
 {
-    URLType lc_type = NULL;
+    char* lc_type = NULL;
 
     g_return_if_fail( self != NULL );
     g_return_if_fail( GNC_IS_HTML(self) );
@@ -629,7 +629,7 @@ gnc_html_set_parent( GncHtml* self, GtkWindow* parent )
 gboolean
 gnc_html_register_urltype( URLType type, const char *protocol )
 {
-    URLType  lc_type  = NULL;
+    char*  lc_type  = NULL;
     char    *lc_proto = NULL;
 
     if (!gnc_html_type_to_proto_hash)
@@ -697,7 +697,7 @@ gnc_html_initialize( void )
 gchar*
 gnc_build_url( URLType type, const gchar* location, const gchar* label )
 {
-    URLType  lc_type  = NULL;
+    char*  lc_type  = NULL;
     char * type_name;
 
     DEBUG(" ");
@@ -931,7 +931,7 @@ gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand )
     gnc_html_unregister_stream_handler( url_type );
     if ( hand != NULL )
     {
-        URLType  lc_type  = g_ascii_strdown (url_type, -1);
+        char*  lc_type  = g_ascii_strdown (url_type, -1);
         g_hash_table_insert( gnc_html_stream_handlers, lc_type, hand );
     }
 }
@@ -939,7 +939,7 @@ gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand )
 void
 gnc_html_unregister_stream_handler( URLType url_type )
 {
-    URLType  lc_type = g_ascii_strdown (url_type, -1);
+    char*  lc_type = g_ascii_strdown (url_type, -1);
     g_hash_table_remove( gnc_html_stream_handlers, lc_type );
     g_free(lc_type);
 }
@@ -957,7 +957,7 @@ gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand )
     gnc_html_unregister_url_handler( url_type );
     if ( hand != NULL )
     {
-        URLType lc_type = g_ascii_strdown (url_type, -1);
+        char* lc_type = g_ascii_strdown (url_type, -1);
         g_hash_table_insert( gnc_html_url_handlers, lc_type, hand );
     }
 }
@@ -965,7 +965,7 @@ gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand )
 void
 gnc_html_unregister_url_handler( URLType url_type )
 {
-    URLType lc_type = g_ascii_strdown (url_type, -1);
+    char* lc_type = g_ascii_strdown (url_type, -1);
     g_hash_table_remove( gnc_html_url_handlers, lc_type );
     g_free(lc_type);
 }
diff --git a/gnucash/html/gnc-html.h b/gnucash/html/gnc-html.h
index 70b4352c9..45a4ce671 100644
--- a/gnucash/html/gnc-html.h
+++ b/gnucash/html/gnc-html.h
@@ -294,4 +294,5 @@ void gnc_html_unregister_url_handler( URLType url_type );
 
 const gchar* gnc_html_get_embedded_param( gpointer eb, const gchar* param_name );
 
+G_END_DECLS
 #endif

commit ae73b3855d27c769fd8055194452e75f81adf545
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 8 17:37:56 2020 -0700

    Cast const char* args from guile to char*.
    
    Swig's guile typemaps lack a const char* freearg typemap. When a function
    argument is explicitly const char* Swig automatically discards the const
    in the temporary's decl so freeing it isn't a problem, but if the function
    arg is declared as something typedeffed to const char* the alias is used
    in the temporary's decl causing an error about discarding the const
    qualifier when it's time to free the temporary. Providing a const char*
    freearg typemap works around the shortcoming.

diff --git a/gnucash/html/gnc-html.i b/gnucash/html/gnc-html.i
index 573cf81cf..4b76ff050 100644
--- a/gnucash/html/gnc-html.i
+++ b/gnucash/html/gnc-html.i
@@ -34,6 +34,9 @@
 #include <gnc-html.h>
 %}
 #if defined(SWIGGUILE)
+
+%typemap (freearg) const char* "if (must_free$argnum && $1) SWIG_free((char*)$1);";
+
 %{
 #include "guile-mappings.h"
 

commit e48416010dfd4fb47d9142e09f1f1f61f5fb1c68
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 8 16:51:49 2020 -0700

    Remove redundant GncOptionDB decl.

diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index 6511594ae..6c5dfa9b5 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -36,11 +36,8 @@ typedef struct GncOptionDB GncOptionDB;
 #include <libguile.h>
 
 #ifdef __cplusplus
-class GncOptionDB;
 extern "C"
 {
-#else
-typedef struct GncOptionDB GncOptionDB;
 #endif
 #include <config.h>
 #include <Account.h>

commit 7022f5222d6250b54ac7ef7e682a02c1de7b175f
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 8 16:50:41 2020 -0700

    Revert gnc_option_db_lookup|set_glist_value to ...scm_value.
    
    GLists and SCM lists are not interchangeable.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index f6750b1a3..1bc1f2fa3 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -21,6 +21,7 @@
  *                                                                  *
 \********************************************************************/
 
+#include <libguile.h>
 #include <string>
 #include <limits>
 #include <sstream>
@@ -1484,14 +1485,14 @@ gnc_option_db_lookup_qofinstance_value(GncOptionDB*, const char*, const char*)
     return nullptr;
 }
 
-GList*
-gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*)
+SCM
+gnc_option_db_lookup_scm_value(GncOptionDB*, const char*, const char*)
 {
     return nullptr;
 }
 
 void
-gnc_option_db_set_glist_value(GncOptionDB*, const char*, const char*, GList*)
+gnc_option_db_set_scm_value(GncOptionDB*, const char*, const char*, SCM)
 {
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index f4b3d4246..6511594ae 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -32,6 +32,9 @@ class GncOptionDB;
 typedef struct GncOption GncOption;
 typedef struct GncOptionDB GncOptionDB;
 #endif
+
+#include <libguile.h>
+
 #ifdef __cplusplus
 class GncOptionDB;
 extern "C"
@@ -153,7 +156,7 @@ const QofInstance* gnc_option_db_lookup_qofinstance_value(GncOptionDB*,
  * @return the GList* of the value or nullptr if the option isn't found
  * or if its value isn't a string.
  */
-GList* gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*);
+SCM gnc_option_db_lookup_scm_value(GncOptionDB*, const char*, const char*);
 
 /**
  * Set the GList* value of an option in the GncOptionDB.
@@ -166,8 +169,8 @@ GList* gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*);
  * @param name the option name
  * @param value the value to be stored in the option.
  */
-void gnc_option_db_set_glist_value(GncOptionDB*, const char*,
-                                    const char*, GList*);
+void gnc_option_db_set_scm_value(GncOptionDB*, const char*,
+                                    const char*, SCM);
 
 #ifdef __cplusplus
 }

commit 776d1aaa0ed8b711490edf18590a1ed59ea4d256
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 8 16:48:00 2020 -0700

    GNC_DEFINE_TYPE_WITH_CODE: Don't use class as a symbol name.
    
    It conflicts with the C++ keyword. Even GObject code knows better.

diff --git a/gnucash/gnome-utils/gnc-gobject-utils.h b/gnucash/gnome-utils/gnc-gobject-utils.h
index e84e06d57..c0fd9cff9 100644
--- a/gnucash/gnome-utils/gnc-gobject-utils.h
+++ b/gnucash/gnome-utils/gnc-gobject-utils.h
@@ -128,7 +128,7 @@ void gnc_gobject_tracking_dump (void);
 
 #define _GNC_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) \
 \
-static void     type_name##_init         (TypeName        *self, void *class); \
+static void     type_name##_init         (TypeName        *self, void *klass); \
 static void     type_name##_class_init   (TypeName##Class *klass); \
 static gpointer type_name##_parent_class = NULL; \
 static gint     TypeName##_private_offset; \

commit 8c77ce967b500aeaaa02db7209b0c513c1cbc6d0
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Oct 6 10:09:27 2020 -0700

    Implement and test obsolete option name aliasing.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 0cdaf0db4..f6750b1a3 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -34,6 +34,69 @@
 constexpr const char* log_module{G_LOG_DOMAIN};
 
 constexpr auto stream_max = std::numeric_limits<std::streamsize>::max();
+using AliasedOption = std::pair<const char*, const char*>;
+using OptionAlias = std::pair<const char*, AliasedOption>;
+using OptionAliases = std::vector<OptionAlias>;
+class Aliases
+{
+    static const OptionAliases c_option_aliases;
+public:
+    static const AliasedOption* find_alias (const char* old_name)
+    {
+        if (!old_name) return nullptr;
+        const auto alias =
+            std::find_if(c_option_aliases.begin(), c_option_aliases.end(),
+                         [old_name](auto alias){
+                             return std::strcmp(old_name, alias.first) == 0;
+                         });
+        if (alias == c_option_aliases.end())
+        {
+            std::cerr << "No alias for " << old_name << " found.\n";
+            return nullptr;
+        }
+        std::cerr << "Found " << alias->second.second << " as alias for " << old_name << ".\n";
+        return &alias->second;
+    }
+};
+
+const OptionAliases Aliases::c_option_aliases
+{
+    {"Accounts to include", {nullptr, "Accounts"}},
+    {"Exclude transactions between selected accounts?",
+        {nullptr, "Exclude transactions between selected accounts"}},
+    {"Filter Accounts", {nullptr, "Filter By..."}},
+    {"Flatten list to depth limit?",
+        {nullptr, "Flatten list to depth limit"}},
+    {"From", {nullptr, "Start Date"}},
+    {"Report Accounts", {nullptr, "Accounts"}},
+    {"Report Currency", {nullptr, "Report's currency"}},
+    {"Show Account Code?", {nullptr, "Show Account Code"}},
+    {"Show Full Account Name?", {nullptr, "Show Full Account Name"}},
+    {"Show Multi-currency Totals?",
+        {nullptr, "Show Multi-currency Totals"}},
+    {"Show zero balance items?", {nullptr, "Show zero balance items"}},
+    {"Sign Reverses?", {nullptr, "Sign Reverses"}},
+    {"To", {nullptr, "End Date"}},
+    {"Charge Type", {nullptr, "Action"}}, // easy-invoice.scm, renamed June 2018
+        // the following 4 options in income-gst-statement.scm renamed Dec 2018
+    {"Individual income columns", {nullptr, "Individual sales columns"}},
+    {"Individual expense columns",
+        {nullptr, "Individual purchases columns"}},
+    {"Remittance amount", {nullptr, "Gross Balance"}},
+    {"Net Income", {nullptr, "Net Balance"}},
+        // transaction.scm:
+    {"Use Full Account Name?", {nullptr, "Use Full Account Name"}},
+    {"Use Full Other Account Name?",
+        {nullptr, "Use Full Other Account Name"}},
+    {"Void Transactions?", {"Filter", "Void Transactions"}},
+    {"Void Transactions", {"Filter", "Void Transactions"}},
+    {"Account Substring", {"Filter", "Account Name Filter"}},
+    {"Enable links", {nullptr, "Enable Links"}},
+        // invoice.scm, renamed November 2018
+    {"Individual Taxes", {nullptr, "Use Detailed Tax Summary"}},
+        // income-gst-statement.scm
+    {"default format", {nullptr, "Default Format"}}
+};
 
 static bool
 operator==(const std::string& str, const char* cstr)
@@ -76,7 +139,13 @@ GncOptionSection::find_option(const char* name) const
                                [name](auto& option) -> bool {
                                    return option.get_name() == name;
                                });
-    return (option == m_options.end() ? nullptr : &*option);
+    if (option != m_options.end())
+        return &*option;
+
+    auto alias = Aliases::find_alias(name);
+    if (!alias || alias->first) // No alias or the alias
+        return nullptr;         // is in a different section.
+    return find_option(alias->second);
 }
 
 GncOptionDB::GncOptionDB() : m_default_section{} {}
@@ -133,9 +202,19 @@ const GncOption*
 GncOptionDB::find_option(const std::string& section, const char* name) const
 {
     auto db_section = const_cast<GncOptionDB*>(this)->find_section(section);
-    if (!db_section)
+    const GncOption* option = nullptr;
+    if (db_section)
+        option = db_section->find_option(name);
+    if (option)
+        return option;
+    auto alias = Aliases::find_alias(name);
+     /* Only try again if alias.first isn't
+     * nullptr. GncOptionSection::find_option already checked if the alias
+     * should have been in the same section.
+     */
+    if (alias && alias->first)
+        return find_option(alias->first, alias->second);
         return nullptr;
-    return db_section->find_option(name);
 }
 
 std::string
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index a698fb574..7ccc7214e 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -446,6 +446,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         auto db_ptr{std::make_unique<GncOptionDB>()};
         return db_ptr;
     }
+
+    GncOption*
+    gnc_lookup_option(const GncOptionDBPtr& optiondb, const char* section,
+                      const char* name)
+    {
+        return optiondb->find_option(section, name);
+    }
 %}
 
 #endif //SWIGGUILE
diff --git a/libgnucash/app-utils/test/test-options.scm b/libgnucash/app-utils/test/test-options.scm
index 27b970692..f7e55eeb3 100644
--- a/libgnucash/app-utils/test/test-options.scm
+++ b/libgnucash/app-utils/test/test-options.scm
@@ -1,4 +1,9 @@
-(use-modules (gnucash app-utils))
+;;(use-modules (gnucash app-utils))
+;; Load the C++ option implementation, avoiding the options.scm ones.
+(eval-when
+ (compile load eval expand)
+ (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module"))
+(use-modules (sw_app_utils))
 (use-modules (srfi srfi-64))
 (use-modules (tests srfi64-extras))
 
@@ -9,19 +14,17 @@
   (test-end "test-options"))
 
 (define (test-lookup-option)
-  (let ((options (gnc:new-options)))
-    (gnc:register-option
-     options
-     (gnc:make-simple-boolean-option
-      "Section" "Start Date" "sort-tag" "docstring" 'default-val))
+  (let* ((options (new-gnc-optiondb))
+         (string-opt (gnc-register-string-option (GncOptionDBPtr-get options)
+                                                 "Section" "Start Date"
+                                                 "sort-tag" "docstring" "waldo")
+                     ))
 
-    (gnc:register-option
-     options
-     (gnc:make-simple-boolean-option
-      "Filter" "Void Transactions" "sort-tag" "docstring" 'default-val))
-
-    (test-assert "lookup-option changed name"
-      (gnc:lookup-option options "Section" "From"))
-
-    (test-assert "lookup-option changed section and name"
-      (gnc:lookup-option options "Section" "Void Transactions?"))))
+    (gnc-register-simple-boolean-option
+     (GncOptionDBPtr-get options)
+      "Filter" "Void Transactions" "sort-tag" "docstring" 'default-val)
+    ;; Testing that the old option name aliases work.
+    (let ((option (gnc-lookup-option options "Section" "From")))
+      (test-assert "lookup-option changed name" option))
+    (let ((option (gnc-lookup-option options "Section" "Void Transactions?")))
+    (test-assert "lookup-option changed section and name" option))))

commit 90b8fce59fe02a0e0ccac05a2baaadf7317e07a0
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 6 13:15:51 2020 -0700

    Fix build after merge.

diff --git a/bindings/guile/gnc-guile-bindings.c b/bindings/guile/gnc-guile-bindings.c
index 4d40df5cd..77dbf384b 100644
--- a/bindings/guile/gnc-guile-bindings.c
+++ b/bindings/guile/gnc-guile-bindings.c
@@ -44,6 +44,5 @@ gnc_guile_bindings_init(void)
         scm_init_sw_core_utils_module();
         scm_init_sw_engine_module();
 
-        is_initialized = 1;
     }
 }
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 7b2568e5b..f5358e733 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -80,18 +80,14 @@ gnc_add_scheme_tests("${test_app_utils_scheme_SOURCES}")
 
 if (HAVE_SRFI64)
   gnc_add_scheme_test_targets(scm-test-app-utils-srfi64
-    "${test_app_utils_scheme_SRFI64_SOURCES}"
-    "tests"
-    "${GUILE_DEPENDS};scm-srfi64-extras"
-    FALSE
-    )
+    SOURCES "${test_app_utils_scheme_SRFI64_SOURCES}"
+    OUTPUT_DIR "tests"
+    DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras")
 
   gnc_add_scheme_test_targets(scm-test-gnc-optiondb
-    "test-gnc-optiondb.scm"
-    "tests"
-    "swig-apputils-guile-cpp;scm-srfi64-extras"
-    FALSE
-    )
+    SOURCES "test-gnc-optiondb.scm"
+    OUTPUT_DIR "tests"
+    DEPENDS "swig-apputils-guile-cpp;scm-srfi64-extras")
   gnc_add_scheme_tests("test-gnc-optiondb.scm")
   gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}")
 endif()
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 7df17cac0..56f3e9b16 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -26,7 +26,7 @@
 (use-modules (gnucash gnc-module))
 (eval-when
  (compile load eval expand)
- (load-extension "_sw_app_utils" "scm_init_sw_app_utils_module"))
+ (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module"))
 
 (use-modules (gnucash engine))
 (use-modules (sw_app_utils))

commit 60c1d16e64d2ea327234dc91ab3bd6d232f2aa33
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 6 13:15:36 2020 -0700

    Add/rename files in POTFILES.

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5e1439b19..fb268e6b3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -135,7 +135,6 @@ gnucash/gnome-utils/dialog-doclink-utils.c
 gnucash/gnome-utils/dialog-dup-trans.c
 gnucash/gnome-utils/dialog-file-access.c
 gnucash/gnome-utils/dialog-object-references.c
-gnucash/gnome-utils/dialog-options.c
 gnucash/gnome-utils/dialog-options.cpp
 gnucash/gnome-utils/dialog-preferences.c
 gnucash/gnome-utils/dialog-query-view.c

commit 4f3fd665cfc1f7552f3b06b9de7bec5e86b3e4e8
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Apr 8 12:20:12 2020 -0700

    Fix counter and counter_format storage in KVP.
    
    So that they go to their own slots instead of to the options slot.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 70a8dc061..0cdaf0db4 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -568,7 +568,37 @@ GncOptionDB::load_from_key_value(std::istream& iss)
     return iss;
 }
 
-bool
+static inline void
+counter_option_path(const GncOption& option, GSList* list, std::string& name)
+{
+    constexpr const char* counters{"counters"};
+    constexpr const char* formats{"counter_formats/"};
+    auto key = option.get_key();
+    name = key.substr(0, key.size() - 1);
+    list->next->data = (void*)name.c_str();
+    if (option.get_name().rfind("format")
+        != std::string::npos)
+        list->data = (void*)formats;
+    else
+        list->data = (void*)counters;
+}
+
+static inline void
+option_path(const GncOption& option, GSList* list)
+{
+    list->next->data = (void*)option.get_name().c_str();
+    list->data = (void*)option.get_section().c_str();
+}
+
+static inline KvpValue*
+kvp_value_from_bool_option(const GncOption& option)
+{
+    auto val{option.template get_value<bool>()};
+    // ~KvpValue will g_free the value.
+    return new KvpValue(val ? g_strdup("t") : g_strdup("f"));
+}
+
+static bool
 is_qofinstance_ui_type(GncOptionUIType type)
 {
     switch (type)
@@ -590,55 +620,73 @@ is_qofinstance_ui_type(GncOptionUIType type)
     }
 }
 
+static inline KvpValue*
+kvp_value_from_qof_instance_option(const GncOption& option)
+{
+    const QofInstance* inst{QOF_INSTANCE(option.template get_value<const QofInstance*>())};
+    auto guid = guid_copy(qof_instance_get_guid(inst));
+    return new KvpValue(guid);
+}
+
 void
 GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
 {
     if (clear_options)
         qof_book_options_delete(book, nullptr);
     const_cast<GncOptionDB*>(this)->foreach_section(
-        [clear_options, book](GncOptionSectionPtr& section)
+        [book](GncOptionSectionPtr& section)
         {
             section->foreach_option(
-                [clear_options, book, &section](auto& option) {
-                    if (clear_options || option.is_changed())
+                [book, &section](auto& option) {
+                    if (option.is_changed())
                     {
-                        // qof_book_set_option wants a GSList path. Let's avoid
-                        // allocating and make one here.
-                        GSList list_tail{(void*)option.get_name().c_str(), nullptr};
-                        GSList list_head{(void*)section->get_name().c_str(), &list_tail};
+                        /* We need the string name out here so that it stays in
+                         * scope long enough to pass its c_str to
+                         * gnc_book_set_option. */
+                        std::string name;
+                        /* qof_book_set_option wants a GSList path. Let's avoid
+                         * allocating and make one here. */
+                        GSList list_tail{}, list_head{nullptr, &list_tail};
+                        if (strcmp(section->get_name().c_str(), "Counters") == 0)
+                            counter_option_path(option, &list_head, name);
+                        else
+                            option_path(option, &list_head);
                         auto type{option.get_ui_type()};
+                        KvpValue* kvp{};
                         if (type == GncOptionUIType::BOOLEAN)
-                        {
-                            auto val{option.template get_value<bool>()};
-                            // ~KvpValue will g_free the value.
-                            auto kvp{new KvpValue(val ? g_strdup("t") :
-                                                  g_strdup("f"))};
-                            qof_book_set_option(book, kvp, &list_head);
-                        }
+                            kvp = kvp_value_from_bool_option(option);
                         else if (is_qofinstance_ui_type(type))
-                        {
-                            const QofInstance* inst{QOF_INSTANCE(option.template get_value<const QofInstance*>())};
-                            auto guid = guid_copy(qof_instance_get_guid(inst));
-                            auto kvp{new KvpValue(guid)};
-                            qof_book_set_option(book, kvp, &list_head);
-                        }
+                            kvp = kvp_value_from_qof_instance_option(option);
                         else if (type == GncOptionUIType::NUMBER_RANGE)
-                        {
                             /* The Gtk control uses a double so that's what we
                              * have to store. */
-                            auto kvp{new KvpValue(option.template get_value<double>())};
-                            qof_book_set_option(book, kvp, &list_head);
-                        }
+                            kvp = new KvpValue(option.template get_value<double>());
                         else
-                        {
-                            auto kvp{new KvpValue{g_strdup(option.template get_value<std::string>().c_str())}};
-                            qof_book_set_option(book, kvp, &list_head);
-                        }
+                            kvp = new KvpValue{g_strdup(option.template get_value<std::string>().c_str())};
+                        qof_book_set_option(book, kvp, &list_head);
                     }
                 });
         });
 }
 
+static inline void
+fill_option_from_string_kvp(GncOption& option, KvpValue* kvp)
+{
+    auto str{kvp->get<const char*>()};
+    if (option.get_ui_type() == GncOptionUIType::BOOLEAN)
+        option.set_value(*str == 't' ? true : false);
+    else
+        option.set_value(std::string{str});
+}
+
+static inline void
+fill_option_from_guid_kvp(GncOption& option, KvpValue* kvp)
+{
+    auto guid{kvp->get<GncGUID*>()};
+    option.set_value(
+        (const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type()));
+}
+
 void
 GncOptionDB::load_from_kvp(QofBook* book) noexcept
 {
@@ -648,11 +696,15 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
             section->foreach_option(
                 [book, &section](GncOption& option)
                 {
-                    /* qof_book_set_option wants a GSList path. Let's avoid allocating
-                     * and make one here.
-                     */
-                    GSList list_tail{(void*)option.get_name().c_str(), nullptr};
-                    GSList list_head{(void*)section->get_name().c_str(), &list_tail};
+                    // Make path list as above.
+                    std::string name;
+                    /* qof_book_set_option wants a GSList path. Let's avoid
+                     * allocating and make one here. */
+                    GSList list_tail{}, list_head{nullptr, &list_tail};
+                    if (strcmp(section->get_name().c_str(), "Counters") == 0)
+                        counter_option_path(option, &list_head, name);
+                    else
+                        option_path(option, &list_head);
                     auto kvp = qof_book_get_option(book, &list_head);
                     if (!kvp)
                         return;
@@ -665,20 +717,11 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
                             option.set_value(kvp->get<int64_t>());
                             break;
                         case KvpValue::Type::STRING:
-                        {
-                            auto str{kvp->get<const char*>()};
-                            if (option.get_ui_type() == GncOptionUIType::BOOLEAN)
-                                option.set_value(*str == 't' ? true : false);
-                            else
-                                option.set_value(std::string{str});
+                            fill_option_from_string_kvp(option, kvp);
                             break;
-                        }
                         case KvpValue::Type::GUID:
-                        {
-                            auto guid{kvp->get<GncGUID*>()};
-                            option.set_value((const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type()));
-                            break;
-                        }
+                            fill_option_from_guid_kvp(option, kvp);
+                          break;
                         default:
                             return;
                             break;
@@ -1224,67 +1267,71 @@ gnc_option_db_book_options(GncOptionDB* odb)
 //Counters Tab
 
     gnc_register_counter_option(odb, counter_section,
-                                N_("Customer number"), "a",
+                                N_("Customer number"), "gncCustomera",
                                 N_("The previous customer number generated. This number will be incremented to generate the next customer number."),
                                 0.0);
     gnc_register_counter_format_option(odb, counter_section,
-                                       N_("Customer number format"), "b",
+                                       N_("Customer number format"),
+                                       "gncCustomerb",
                                        N_("The format string to use for generating customer numbers. This is a printf-style format string."),
                                        empty_string);
     gnc_register_counter_option(odb, counter_section,
-                                N_("Employee number"), "a",
+                                N_("Employee number"), "gncEmployeea",
                                 N_("The previous employee number generated. This number will be incremented to generate the next employee number."),
                                 0.0);
     gnc_register_counter_format_option(odb, counter_section,
-                                       N_("Employee number format"), "b",
+                                       N_("Employee number format"),
+                                       "gncEmployeeb",
                                        N_("The format string to use for generating employee numbers. This is a printf-style format string."),
                                        empty_string);
     gnc_register_counter_option(odb, counter_section,
-                                N_("Invoice number"), "a",
+                                N_("Invoice number"), "gncInvoicea",
                                 N_("The previous invoice number generated. This number will be incremented to generate the next invoice number."),
                                 0.0);
     gnc_register_counter_format_option(odb, counter_section,
-                                       N_("Invoice number format"), "b",
+                                       N_("Invoice number format"),
+                                       "gncInvoiceb",
                                        N_("The format string to use for generating invoice numbers. This is a printf-style format string."),
                                        empty_string);
     gnc_register_counter_option(odb, counter_section,
-                                N_("Bill number"), "a",
+                                N_("Bill number"), "gncBilla",
                                 N_("The previous bill number generated. This number will be incremented to generate the next bill number."),
                                 0.0);
     gnc_register_counter_format_option(odb, counter_section,
-                                       N_("Bill number format"), "b",
+                                       N_("Bill number format"), "gncBillb",
                                        N_("The format string to use for generating bill numbers. This is a printf-style format string."),
                                        empty_string);
     gnc_register_counter_option(odb, counter_section,
-                                N_("Expense voucher number"), "a",
+                                N_("Expense voucher number"), "gncExpVouchera",
                                 N_("The previous expense voucher number generated. This number will be incremented to generate the next voucher number."),
                                 0.0);
     gnc_register_counter_format_option(odb, counter_section,
-                                       N_("Expense voucher number format"), "b",
+                                       N_("Expense voucher number format"),
+                                       "gncExpVoucherb",
                                        N_("The format string to use for generating expense voucher numbers. This is a printf-style format string."),
                                        empty_string);
     gnc_register_counter_option(odb, counter_section,
-                                N_("Job number"), "a",
+                                N_("Job number"), "gncJoba",
                                 N_("The previous job number generated. This number will be incremented to generate the next job number."),
                                 0.0);
     gnc_register_counter_format_option(odb, counter_section,
-                                       N_("Job number format"), "b",
+                                       N_("Job number format"), "gncJobb",
                                        N_("The format string to use for generating job numbers. This is a printf-style format string."),
                                        empty_string);
     gnc_register_counter_option(odb, counter_section,
-                                N_("Order number"), "a",
+                                N_("Order number"), "gncOrdera",
                                 N_("The previous order number generated. This number will be incremented to generate the next order number."),
                                 0.0);
     gnc_register_counter_format_option(odb, counter_section,
-                                       N_("Order number format"), "b",
+                                       N_("Order number format"), "gncOrderb",
                                        N_("The format string to use for generating order numbers. This is a printf-style format string."),
                                        empty_string);
     gnc_register_counter_option(odb, counter_section,
-                                N_("Vendor number"), "a",
+                                N_("Vendor number"), "gncVendora",
                                 N_("The previous vendor number generated. This number will be incremented to generate the next vendor number."),
                                 0.0);
     gnc_register_counter_format_option(odb, counter_section,
-                                       N_("Vendor number format"), "b",
+                                       N_("Vendor number format"), "gncVendorb",
                                        N_("The format string to use for generating vendor numbers. This is a printf-style format string."),
                                        empty_string);
 

commit b19f3d383c9ad869f9ea6a76fb93f3880beffbde
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Apr 8 12:20:06 2020 -0700

    Protect some exceptions from being passed to Swig.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 452b7bca7..70a8dc061 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -888,10 +888,17 @@ gnc_register_number_range_option(GncOptionDB* db, const char* section,
                                  const char* doc_string, ValueType value,
                                  ValueType min, ValueType max, ValueType step)
 {
-    GncOption option{GncOptionRangeValue<ValueType>{section, name, key,
-                                                    doc_string, value, min,
-                                                    max, step}};
-    db->register_option(section, std::move(option));
+    try
+    {
+        GncOption option{GncOptionRangeValue<ValueType>{section, name, key,
+                                                        doc_string, value, min,
+                                                        max, step}};
+        db->register_option(section, std::move(option));
+    }
+    catch(const std::invalid_argument& err)
+    {
+        std::cerr <<"Number Range Option " << err.what() << ", option not registerd.\n";
+    }
 }
 
 void
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 06bac06d4..a698fb574 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -425,10 +425,19 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         auto db_opt = optiondb->find_option(section, name);
         if (!db_opt)
         {
-//          PWARN("Attempt to write non-existent option %s/%s", section, name);
+            std::cerr <<"Attempt to write non-existent option " << section
+                << "/" << name;
             return;
         }
-        GncOption_set_value_from_scm(db_opt, new_value);
+        try
+        {
+            GncOption_set_value_from_scm(db_opt, new_value);
+        }
+        catch(const std::invalid_argument& err)
+        {
+            std::cerr << "Failed to set option " << section << "/" << name
+                      << ": " << err.what() << "\n";
+        }
     }
 
     GncOptionDBPtr

commit 52600bbd1f9a8a2c0bc04edca605572c8ccbd50b
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Apr 8 12:11:16 2020 -0700

    Instantiate gnc_register_number_range_option templates in SWIG.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index b9472eb96..06bac06d4 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -142,6 +142,12 @@ scm_to_value<int64_t>(SCM new_value)
     return scm_to_int64(new_value);
 }
 
+template <> inline double
+scm_to_value<double>(SCM new_value)
+{
+    return scm_to_double(new_value);
+}
+
 template <>inline SCM
 scm_from_value<GncOptionAccountList>(GncOptionAccountList value)
 {
@@ -384,7 +390,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     %template(set_option_string) set_option<std::string>;
     %template(set_option_int) set_option<int>;
     %template(set_option_time64) set_option<time64>;
- };
+};
+
+%template(gnc_register_number_range_option_double) gnc_register_number_range_option<double>;
+%template(gnc_register_number_range_option_int) gnc_register_number_range_option<int>;
 
 %inline %{
     using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;

commit 31a0153fec66fcacc4348ba0f75efd488a37369b
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Apr 8 12:08:54 2020 -0700

    Another GncOptionDBPtr fix.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 48f449233..b9472eb96 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -220,7 +220,7 @@ gnc_option_test_book_destroy(QofBook* book)
 %rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD;
 
 %rename(gnc_register_date_option_set)
-    gnc_register_date_option(const GncOptionDBPtr&, const char*, const char*,
+    gnc_register_date_option(GncOptionDB*, const char*, const char*,
                              const char*, const char*, RelativeDatePeriodVec&,
                              bool);
 

commit 85db341afe0211b9be71a2ba678a106048cbd18f
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Apr 8 12:05:19 2020 -0700

    Remove test-option-utils
    
    It no longer works. Since that's all that test-app-utils called,
    delete it too.

diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index eb6b27c0e..7b2568e5b 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -12,8 +12,6 @@ set(APP_UTILS_TEST_INCLUDE_DIRS
 
 set(APP_UTILS_TEST_LIBS gnc-app-utils gnc-test-engine test-core ${GIO_LDFLAGS} ${GUILE_LDFLAGS})
 
-set(test_app_utils_SOURCES test-app-utils.c test-option-util.cpp)
-
 macro(add_app_utils_test _TARGET _SOURCE_FILES)
   gnc_add_test(${_TARGET} "${_SOURCE_FILES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS)
 endmacro()
diff --git a/libgnucash/app-utils/test/test-app-utils.c b/libgnucash/app-utils/test/test-app-utils.c
deleted file mode 100644
index ae63a01cf..000000000
--- a/libgnucash/app-utils/test/test-app-utils.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/********************************************************************
- * test-app-utils.c: GLib g_test test execution file.		    *
- * Copyright 2013 John Ralls <jralls at ceridwen.us>		    *
- *                                                                  *
- * This program is free software; you can redistribute it and/or    *
- * modify it under the terms of the GNU General Public License as   *
- * published by the Free Software Foundation; either version 2 of   *
- * the License, or (at your option) any later version.              *
- *                                                                  *
- * This program is distributed in the hope that it will be useful,  *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
- * GNU General Public License for more details.                     *
- *                                                                  *
- * You should have received a copy of the GNU General Public License*
- * along with this program; if not, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
-\********************************************************************/
-
-#include <config.h>
-#include <glib.h>
-#include <qof.h>
-#include <libguile.h>
-
-extern void test_suite_option_util (void);
-extern void test_suite_gnc_ui_util (void);
-
-static void
-guile_main (void *closure, int argc, char **argv)
-{
-    int retval;
-    scm_c_use_module("gnucash app-utils");
-
-    test_suite_option_util ();
-    retval = g_test_run ();
-
-    exit (retval);
-}
-
-int
-main (int argc, char *argv[])
-{
-    qof_init (); 			/* Initialize the GObject system */
-    qof_log_init_filename_special ("stderr"); /* Init the log system */
-    g_test_init (&argc, &argv, NULL); 	/* initialize test program */
-    //qof_log_set_level("gnc", G_LOG_LEVEL_DEBUG);
-    g_test_bug_base("https://bugs.gnucash.org/show_bug.cgi?id="); /* init the bugzilla URL */
-    g_setenv ("GNC_UNINSTALLED", "1", TRUE);
-    scm_boot_guile (argc, argv, guile_main, NULL);
-    return 0;
-}
diff --git a/libgnucash/app-utils/test/test-option-util.cpp b/libgnucash/app-utils/test/test-option-util.cpp
deleted file mode 100644
index c4b1c0c40..000000000
--- a/libgnucash/app-utils/test/test-option-util.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/********************************************************************
- * test-option-util.cpp: GLib test suite for option-util.c.	    *
- * Copyright 2013 John Ralls <jralls at ceridwen.us>		    *
- *                                                                  *
- * This program is free software; you can redistribute it and/or    *
- * modify it under the terms of the GNU General Public License as   *
- * published by the Free Software Foundation; either version 2 of   *
- * the License, or (at your option) any later version.              *
- *                                                                  *
- * This program is distributed in the hope that it will be useful,  *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
- * GNU General Public License for more details.                     *
- *                                                                  *
- * You should have received a copy of the GNU General Public License*
- * along with this program; if not, you can retrieve it from        *
- * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html            *
- * or contact:                                                      *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
- ********************************************************************/
-#include <kvp-frame.hpp>
-#include <gmp.h>
-#include <config.h>
-#include <glib.h>
-
-extern "C"
-{
-#include <unittest-support.h>
-#include <qofbookslots.h>
-#include "test-engine-stuff.h"
-#include "../option-util.h"
-}
-
-static const gchar *suitename = "/app-utils/option-util";
-extern "C" void test_suite_option_util (void);
-
-typedef struct
-{
-    QofBook *book;
-    GSList *hdlrs;
-} Fixture;
-
-/* Expose a mostly-private QofInstance function to load options into
- * the Book.
- */
-extern "C" KvpFrame *qof_instance_get_slots (const QofInstance*);
-
-static void
-setup (Fixture *fixture, gconstpointer pData)
-{
-    fixture->book = qof_book_new ();
-    fixture->hdlrs = NULL;
-}
-
-static void
-setup_kvp (Fixture *fixture, gconstpointer pData)
-{
-    QofBook *book;
-    KvpFrame *slots;
-    setup (fixture, pData);
-    book = fixture->book;
-    slots = qof_instance_get_slots (QOF_INSTANCE (book));
-    qof_begin_edit (QOF_INSTANCE (book));
-    qof_instance_set (QOF_INSTANCE (book),
-                     "trading-accts", "t",
-                     "split-action-num-field", "t",
-                     "autoreadonly-days", (double)21,
-                     NULL);
-
-    slots->set_path({"options", "Business", "Company Name"},
-               new KvpValue(g_strdup ("Bogus Company")));
-    qof_commit_edit (QOF_INSTANCE (book));
-}
-
-static void
-teardown (Fixture *fixture, gconstpointer pData)
-{
-    qof_book_destroy (fixture->book);
-    g_slist_free_full (fixture->hdlrs, test_free_log_handler);
-    test_clear_error_list();
-}
-
-static void
-test_option_load (Fixture *fixture, gconstpointer pData)
-{
-    gchar *str = NULL;
-    SCM symbol_value;
-    QofBook *book = fixture->book;
-    GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK);
-
-    qof_book_load_options (book, gnc_option_db_load, odb);
-    g_assert (gnc_option_db_lookup_boolean_option (odb, OPTION_SECTION_ACCOUNTS, OPTION_NAME_TRADING_ACCOUNTS, FALSE));
-    g_assert (gnc_option_db_lookup_boolean_option (odb,
-                        OPTION_SECTION_ACCOUNTS,
-                        OPTION_NAME_NUM_FIELD_SOURCE, FALSE));
-    g_assert_cmpstr (gnc_option_db_lookup_string_option (odb, "Business", "Company Name", FALSE), ==, "Bogus Company");
-    g_assert_cmpfloat (gnc_option_db_lookup_number_option (odb, OPTION_SECTION_ACCOUNTS, OPTION_NAME_AUTO_READONLY_DAYS, FALSE), ==, 21);
-
-    gnc_option_db_destroy (odb);
-}
-
-static void
-test_option_save (Fixture *fixture, gconstpointer pData)
-{
-    QofBook *book = fixture->book;
-    GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK);
-    KvpFrame *slots = qof_instance_get_slots (QOF_INSTANCE (book));
-
-    g_assert (gnc_option_db_set_boolean_option (odb, OPTION_SECTION_ACCOUNTS,
-						                       OPTION_NAME_TRADING_ACCOUNTS,
-                                               TRUE));
-    g_assert (gnc_option_db_set_boolean_option (odb, OPTION_SECTION_ACCOUNTS,
-                                               OPTION_NAME_NUM_FIELD_SOURCE,
-                                               TRUE));
-    g_assert (gnc_option_db_set_string_option (odb, "Business", "Company Name",
-					       "Bogus Company"));
-    g_assert (gnc_option_db_set_number_option (odb, OPTION_SECTION_ACCOUNTS,
-					       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);
-
-    gnc_option_db_destroy (odb);
-}
-
-extern "C" void
-test_suite_option_util (void)
-{
-    GNC_TEST_ADD (suitename, "Option DB Load", Fixture, NULL, setup_kvp, test_option_load, teardown);
-    GNC_TEST_ADD (suitename, "Option DB Save", Fixture, NULL, setup, test_option_save, teardown);
-}

commit 21398dfda1462591522be96b387a581db492bbd0
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Apr 8 11:44:11 2020 -0700

    Testing fixups for using GncOptionDB* in register_option functions.

diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 963626b46..eaab1fa35 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -33,6 +33,8 @@ extern "C"
 #include <gnc-session.h>
 }
 
+using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
+
 class GncOptionDBTest : public ::testing::Test
 {
 protected:
@@ -73,7 +75,7 @@ TEST_F(GncOptionDBTest, test_unregister_option)
 
 TEST_F(GncOptionDBTest, test_register_string_option)
 {
-    gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
+    gnc_register_string_option(m_db.get(), "foo", "bar", "baz", "Phony Option",
                                std::string{"waldo"});
     EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
 }
@@ -129,8 +131,8 @@ TEST_F(GncOptionDBTest, test_register_account_list_option)
 {
     AccountTestBook book;
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
-    gnc_register_account_list_option(m_db, "foo", "bar", "baz", "Phony Option",
-                                    acclist);
+    gnc_register_account_list_option(m_db.get(), "foo", "bar", "baz",
+                                     "Phony Option", acclist);
     EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
     EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(3));
 }
@@ -139,7 +141,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
 {
     AccountTestBook book;
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
-    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
+    gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_STOCK});
     EXPECT_EQ(4, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
@@ -151,7 +153,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
     AccountTestBook book;
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
     GncOptionAccountList accsel{acclist[2]};
-    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
+    gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz",
                                              "Phony Option", accsel,
                                              {ACCT_TYPE_STOCK});
     EXPECT_EQ(1, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
@@ -163,10 +165,10 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct)
     AccountTestBook book;
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
     GncOptionAccountList accsel{acclist[2]};
-    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option",
+    gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz", "Phony Option",
                                      accsel, {ACCT_TYPE_BANK});
     EXPECT_FALSE(m_db->find_option("foo", "bar"));
-    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
+    gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_BANK});
     EXPECT_FALSE(m_db->find_option("foo", "bar"));
@@ -179,8 +181,8 @@ TEST_F(GncOptionDBTest, test_register_multichoice_option)
         { "waldo", "pepper", "salt"},
         { "pork", "sausage", "links"},
         { "corge", "grault", "garply"}};
-    gnc_register_multichoice_option(m_db, "foo", "bar", "baz", "Phony Option",
-                                    std::move(choices));
+    gnc_register_multichoice_option(m_db.get(), "foo", "bar", "baz",
+                                    "Phony Option", std::move(choices));
     ASSERT_TRUE(m_db->set_option("foo", "bar", std::string{"corge"}));
     EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str());
 }
@@ -197,7 +199,7 @@ time64_from_gdate(const GDate* g_date, DayPart when)
 
 TEST_F(GncOptionDBTest, test_register_relative_date_option)
 {
-    gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option",
+    gnc_register_date_option(m_db.get(), "foo", "bar", "baz", "Phony Option",
                              RelativeDatePeriod::START_ACCOUNTING_PERIOD);
     GDate prev_year_start;
     g_date_set_time_t(&prev_year_start, time(nullptr));
@@ -211,7 +213,8 @@ TEST_F(GncOptionDBTest, test_register_absolute_date_option)
 {
     time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
 
-    gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option", time1);
+    gnc_register_date_option(m_db.get(), "foo", "bar", "baz", "Phony Option",
+                             time1);
     GDate prev_year_start;
     g_date_set_time_t(&prev_year_start, time(nullptr));
     gnc_gdate_set_prev_year_start(&prev_year_start);
@@ -237,7 +240,8 @@ static const RelativeDatePeriodVec begin_dates
 
 TEST_F(GncOptionDBTest, test_register_start_date_option)
 {
-    gnc_register_start_date_option(m_db, "foo", "bar", "baz", "Phony Option");
+    gnc_register_start_date_option(m_db.get(), "foo", "bar", "baz",
+                                   "Phony Option");
     GDate prev_year_start;
     g_date_set_time_t(&prev_year_start, time(nullptr));
     gnc_gdate_set_prev_year_start(&prev_year_start);
@@ -301,18 +305,18 @@ protected:
         create_account(expenses, ACCT_TYPE_EXPENSE, "Gas");
         create_account(expenses, ACCT_TYPE_EXPENSE, "Rent");
 
-        gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
-                                   std::string{"waldo"});
-        gnc_register_text_option(m_db, "foo", "sausage", "links",
+        gnc_register_string_option(m_db.get(), "foo", "bar", "baz",
+                                   "Phony Option", std::string{"waldo"});
+        gnc_register_text_option(m_db.get(), "foo", "sausage", "links",
                                  "Phony Option", std::string{"waldo"});
-        gnc_register_string_option(m_db, "qux", "grault", "baz", "Phony Option",
-                                   std::string{""});
-        gnc_register_text_option(m_db, "qux", "garply", "fred",
+        gnc_register_string_option(m_db.get(), "qux", "grault", "baz",
+                                   "Phony Option", std::string{""});
+        gnc_register_text_option(m_db.get(), "qux", "garply", "fred",
                                    "Phony Option", std::string{"waldo"});
-        gnc_register_date_option(m_db, "pork", "garply", "first",
+        gnc_register_date_option(m_db.get(), "pork", "garply", "first",
                                  "Phony Date Option",
                                  RelativeDatePeriod::START_CURRENT_QUARTER);
-        gnc_register_account_list_option(m_db, "quux", "xyzzy", "second",
+        gnc_register_account_list_option(m_db.get(), "quux", "xyzzy", "second",
                                          "Phony AccountList Option",
                                          {aapl, hpe});
     }
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 5a407f31b..7df17cac0 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -46,7 +46,7 @@
 (define (test-gnc-make-text-option)
   (test-begin "test-gnc-test-string-option")
   (let* ((option-db (new-gnc-optiondb))
-         (string-opt (gnc-register-string-option option-db "foo" "bar" "baz"
+         (string-opt (gnc-register-string-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
                                                  "Phony Option" "waldo")))
     (test-equal "waldo" (gnc-option-value option-db "foo" "bar"))
 
@@ -89,38 +89,41 @@
 
   (define (test-make-account-list-option book)
     (test-group "test-make-account-list-option"
-    (let ((optiondb (new-gnc-optiondb))
+    (let ((option-db (new-gnc-optiondb))
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
-      (gnc-register-account-list-option optiondb "foo" "bar" "baz"
+      (gnc-register-account-list-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
                                         "Phony Option" acctlist)
-      (let ((acct-list (gnc-option-value optiondb "foo" "bar")))
+      (let ((acct-list (gnc-option-value option-db "foo" "bar")))
         (test-equal (length acctlist) (length acct-list))
         (test-equal (car acctlist) (car acct-list))) )))
 
   (define (test-make-account-list-limited-option book)
     (test-group "test-make-account-list-option"
-    (let ((optiondb (new-gnc-optiondb))
+    (let ((option-db (new-gnc-optiondb))
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
-      (gnc-register-account-list-limited-option optiondb "foo" "bar" "baz"
-                                        "Phony Option" acctlist (list ACCT-TYPE-STOCK))
-      (let ((acct-list (gnc-option-value optiondb "foo" "bar")))
+      (gnc-register-account-list-limited-option
+       (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
+       "Phony Option" acctlist (list ACCT-TYPE-STOCK))
+      (let ((acct-list (gnc-option-value option-db "foo" "bar")))
         (test-equal (length acctlist) (length acct-list))
         (test-equal (cadr acctlist) (cadr acct-list)))
-      (gnc-register-account-list-limited-option optiondb "waldo" "pepper" "baz"
-                                        "Phony Option" acctlist (list ACCT-TYPE-BANK))
-      (let ((acct-list (gnc-option-value optiondb "waldo" "pepper")))
+      (gnc-register-account-list-limited-option
+       (GncOptionDBPtr-get option-db) "waldo" "pepper" "baz"
+       "Phony Option" acctlist (list ACCT-TYPE-BANK))
+      (let ((acct-list (gnc-option-value option-db "waldo" "pepper")))
         (test-equal #f (length acct-list))))))
 
   (define (test-make-account-sel-limited-option book)
     (test-group "test-make-account-list-option"
-    (let ((optiondb (new-gnc-optiondb))
+    (let ((option-db (new-gnc-optiondb))
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
-      (gnc-register-account-sel-limited-option optiondb "salt" "pork" "baz"
-                                        "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK))
-      (let ((acct (gnc-option-value optiondb "salt" "pork")))
+      (gnc-register-account-sel-limited-option
+       (GncOptionDBPtr-get option-db) "salt" "pork" "baz"
+       "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK))
+      (let ((acct (gnc-option-value option-db "salt" "pork")))
         (test-equal (list (cadr acctlist)) acct)))))
 
   (let* ((book (gnc-option-test-book-new))
@@ -155,7 +158,7 @@
                        (list "pork" (cons 'text "sausage") (cons 'tip "links"))
                        (list "corge" (cons 'text "grault") (cons 'tip "garply"))))
          (multichoice (keylist->vectorlist multilist))
-         (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz"
+         (multi-opt (gnc-register-multichoice-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
                                                      "Phony Option" multichoice)))
 
     (test-equal "plugh" (gnc-option-value option-db "foo" "bar"))
@@ -170,7 +173,7 @@
          (value-list (list (vector "AvgBalPlot" "Average" "Average Balance")
                            (vector "GainPlot" "Profit" "Profit (Gain minus Loss)")
                            (vector "GLPlot" "Gain/Loss" "Gain and Loss")))
-         (list-op (gnc-register-list-option option-db "foo" "bar" "baz"
+         (list-op (gnc-register-list-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz"
                                             "Phony Option" "AvgBalPlot"
                                             value-list)))
     (test-equal "AvgBalPlot" (gnc-option-value option-db "foo" "bar"))
@@ -182,7 +185,7 @@
 (define (test-gnc-make-date-option)
   (test-begin "test-gnc-test-date-option")
   (let* ((option-db (new-gnc-optiondb))
-         (date-opt (gnc-register-date-option option-db "foo" "bar"
+         (date-opt (gnc-register-date-option (GncOptionDBPtr-get option-db) "foo" "bar"
                                              "baz" "Phony Option"
                                              (RelativeDatePeriod-today)))
          (a-time (gnc-dmy2time64 11 07 2019)))
@@ -195,7 +198,7 @@
   (test-begin "test-gnc-test-date-set-option")
   (let* ((option-db (new-gnc-optiondb))
          (date-opt (gnc-register-date-option-set
-                    option-db "foo" "bar" "baz" "Phony Option"
+                    (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option"
                     (list (RelativeDatePeriod-today)
                           (RelativeDatePeriod-start-this-month)
                           (RelativeDatePeriod-start-prev-month)
@@ -211,10 +214,10 @@
 (define (test-gnc-make-number-range-option)
   (test-begin "test-gnc-number-range-option")
   (let* ((option-db (new-gnc-optiondb))
-         (number-opt (gnc-register-number-range-option option-db "foo" "bar"
+         (number-opt (gnc-register-number-range-option-double (GncOptionDBPtr-get option-db) "foo" "bar"
                                                        "baz" "Phony Option"
                                                        15 5 30 1)))
-    (test-equal 15 (gnc-option-value option-db "foo" "bar"))
+    (test-equal 15.0 (gnc-option-value option-db "foo" "bar"))
     (gnc-set-option option-db "foo" "bar" 20)
-    (test-equal 20 (gnc-option-value option-db "foo" "bar")))
+    (test-equal 20.0 (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-number-range-option"))

commit 7dab089d49b3b6e5aa43b866d0733a7e4de576b5
Author: John Ralls <jralls at ceridwen.us>
Date:   Wed Apr 8 09:53:12 2020 -0700

    Fix miscalculation of prev_quarter_end for the first quarter.
    
    If the current quarter ends June 30 backing up 3 months from there
    gets March 30, one day off. Back up first and calculate the end
    of the actual quarter of interest.
    
    Changed pre_quarter_start for consistency, it doesn't matter.

diff --git a/libgnucash/engine/gnc-date.cpp b/libgnucash/engine/gnc-date.cpp
index 3a969a46d..969ef7a2c 100644
--- a/libgnucash/engine/gnc-date.cpp
+++ b/libgnucash/engine/gnc-date.cpp
@@ -1548,15 +1548,15 @@ gnc_gdate_set_quarter_end (GDate *date)
 void
 gnc_gdate_set_prev_quarter_start (GDate *date)
 {
-    gnc_gdate_set_quarter_start(date);
     g_date_subtract_months(date, 3);
+    gnc_gdate_set_quarter_start(date);
 }
 
 void
 gnc_gdate_set_prev_quarter_end (GDate *date)
 {
-    gnc_gdate_set_quarter_end(date);
     g_date_subtract_months(date, 3);
+    gnc_gdate_set_quarter_end(date);
 }
 
 /* ================================================= */

commit d1cfd62f3177abf681724ff6010042d2791fe28c
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Apr 5 10:44:37 2020 -0700

    Fix loading text fields from KVP into options.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index ff8148200..452b7bca7 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -670,7 +670,7 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
                             if (option.get_ui_type() == GncOptionUIType::BOOLEAN)
                                 option.set_value(*str == 't' ? true : false);
                             else
-                                option.set_value(str);
+                                option.set_value(std::string{str});
                             break;
                         }
                         case KvpValue::Type::GUID:

commit 1f0bfe0cfa22cc7572ccac3e4b858ff3424b6c1b
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Apr 5 10:39:48 2020 -0700

    Log an error if we try to create an option for an unregistered UI type.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index a3785ae9d..c117eb63d 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -151,6 +151,7 @@ GncOptionUIFactory::create(GncOption& option, GtkGrid* page, GtkLabel* name,
     auto func{s_registry[static_cast<size_t>(type)]};
     if (func)
         return func(option, page, name, description, enclosing, packed);
+    PERR("No function registered for type %d", type);
     return nullptr;
 }
 

commit c6fce31795344a42a63967e68b4e2f7c980f09c1
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Apr 5 10:38:24 2020 -0700

    Set the component_class on the GNCOptionWin, silencing component-manager error.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 2f32c3d38..a3785ae9d 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -780,6 +780,7 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title,
     gnc_builder_add_from_file (builder, "dialog-options.glade", "gnucash_options_window");
     retval->window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window"));
     retval->page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll"));
+    retval->component_class = component_class ? component_class : DIALOG_OPTIONS_CM_CLASS;
 
     // Set the name for this dialog so it can be easily manipulated with css
     gtk_widget_set_name (GTK_WIDGET(retval->window), "gnc-id-options");

commit 507f35cc303e0bc6453d50d5a61d8e7e038c30de
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Apr 5 10:37:33 2020 -0700

    Fix wrong callback for TEXT control.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 10ac44dde..2f32c3d38 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1069,7 +1069,7 @@ create_option_widget<GncOptionUIType::TEXT> (GncOption& option, GtkGrid *page_bo
     auto ui_item{std::make_unique<GncGtkTextUIItem>(widget)};
     auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
     g_signal_connect(G_OBJECT(text_buffer), "changed",
-                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
+                     G_CALLBACK(gnc_option_changed_option_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 

commit 8440b9c99c9ced5e5e8cdd993cd996b723c1799a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Apr 5 10:02:20 2020 -0700

    Test QofInstance from option before trying to use it.
    
    Avoids GObject type error noise.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 838fefae4..10ac44dde 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1086,9 +1086,9 @@ public:
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         auto widget{GNC_CURRENCY_EDIT(get_widget())};
-        auto currency =
-            GNC_COMMODITY(option.get_value<const QofInstance*>());
-        gnc_currency_edit_set_currency(widget, currency);
+        auto instance{option.get_value<const QofInstance*>()};
+        if (instance)
+            gnc_currency_edit_set_currency(widget, GNC_COMMODITY(instance));
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
@@ -1126,9 +1126,9 @@ public:
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         auto widget{GNC_GENERAL_SELECT(get_widget())};
-        auto commodity =
-            GNC_COMMODITY(option.get_value<const QofInstance*>());
-        gnc_general_select_set_selected(widget, commodity);
+        auto instance{option.get_value<const QofInstance*>()};
+        if (instance)
+            gnc_general_select_set_selected(widget, GNC_COMMODITY(instance));
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
@@ -1872,9 +1872,9 @@ public:
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         auto widget{GNC_ACCOUNT_SEL(get_widget())};
-        gnc_account_sel_set_account(widget,
-                                    GNC_ACCOUNT(option.get_value<const QofInstance*>()),
-                                    FALSE);
+        auto instance{option.get_value<const QofInstance*>()};
+        if (instance)
+            gnc_account_sel_set_account(widget, GNC_ACCOUNT(instance), FALSE);
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
@@ -2725,12 +2725,14 @@ public:
     {
         GtkTreeIter iter;
         auto widget{GTK_COMBO_BOX(get_widget())};
-        auto budget{GNC_BUDGET(option.get_value<const QofInstance*>())};
-        auto tree_model{gtk_combo_box_get_model(widget)};
-        if (gnc_tree_model_budget_get_iter_for_budget(tree_model, &iter,
-                                                      budget))
-            gtk_combo_box_set_active_iter(widget, &iter);
-
+        auto instance{option.get_value<const QofInstance*>()};
+        if (instance)
+        {
+            auto tree_model{gtk_combo_box_get_model(widget)};
+            if (gnc_tree_model_budget_get_iter_for_budget(tree_model, &iter,
+                                                          GNC_BUDGET(instance)))
+                gtk_combo_box_set_active_iter(widget, &iter);
+        }
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp
index e7ac983ab..fc0204a71 100644
--- a/gnucash/gnome/business-options-gnome.cpp
+++ b/gnucash/gnome/business-options-gnome.cpp
@@ -123,8 +123,10 @@ public:
         GncOptionGtkUIItem(widget, GncOptionUIType::INVOICE) {}
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
-        gnc_general_search_set_selected(GNC_GENERAL_SEARCH(get_widget()),
-                                        GNC_INVOICE(option.get_value<const QofInstance*>()));
+        auto instance{option.get_value<const QofInstance*>()};
+        if (instance)
+            gnc_general_search_set_selected(GNC_GENERAL_SEARCH(get_widget()),
+                                            GNC_INVOICE(instance));
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
@@ -162,8 +164,9 @@ public:
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         auto taxtable{option.get_value<const QofInstance*>()};
-        gnc_simple_combo_set_value(GTK_COMBO_BOX(get_widget()),
-                                                 GNC_TAXTABLE(taxtable));
+        if (taxtable)
+            gnc_simple_combo_set_value(GTK_COMBO_BOX(get_widget()),
+                                       GNC_TAXTABLE(taxtable));
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {

commit 2ccfe0c1910befd269c1001afd7ea38551b29eec
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Apr 5 09:09:08 2020 -0700

    Finish implementing the TaxTable option GUI.

diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp
index 2e26b992a..e7ac983ab 100644
--- a/gnucash/gnome/business-options-gnome.cpp
+++ b/gnucash/gnome/business-options-gnome.cpp
@@ -161,9 +161,14 @@ public:
         GncOptionGtkUIItem(widget, GncOptionUIType::TAX_TABLE) {}
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
+        auto taxtable{option.get_value<const QofInstance*>()};
+        gnc_simple_combo_set_value(GTK_COMBO_BOX(get_widget()),
+                                                 GNC_TAXTABLE(taxtable));
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
+        auto taxtable{gnc_simple_combo_get_value(GTK_COMBO_BOX(get_widget()))};
+        option.set_value<const QofInstance*>(qof_instance_cast(taxtable));
     }
 };
 
@@ -179,18 +184,18 @@ create_option_widget<GncOptionUIType::TAX_TABLE>(GncOption& option,
     *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
     gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
     constexpr const char* glade_file{"business-options-gnome.glade"};
-    constexpr const char* glade_store{"taxtable-store"};
-    constexpr const char* glade_menu{"taxtable-menu"};
+    constexpr const char* glade_store{"taxtable_store"};
+    constexpr const char* glade_menu{"taxtable_menu"};
     auto builder{gtk_builder_new()};
     gnc_builder_add_from_file(builder, glade_file, glade_store);
     gnc_builder_add_from_file(builder, glade_file, glade_menu);
     auto widget{GTK_WIDGET(gtk_builder_get_object(builder, glade_menu))};
-    g_object_unref(builder);
     gnc_taxtables_combo(GTK_COMBO_BOX(widget), gnc_get_current_book(), TRUE,
                         nullptr);
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     option.set_ui_item(std::make_unique<GncGtkTaxTableUIItem>(widget));
     option.set_ui_item_from_option();
+    g_object_unref(builder); // Needs to wait until after widget has been reffed.
     g_signal_connect (G_OBJECT (widget), "changed",
                       G_CALLBACK (gnc_option_changed_widget_cb), &option);
 

commit 276d33975abed7c76066e36c6f48cb387314d4df
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Apr 5 09:07:34 2020 -0700

    Remove dialog-options.c, replaced by dialog-options.cpp.
    
    Adds new function reset_default_value to GncOption; doing it outside
    of the class requires knowing the Type of m_value.

diff --git a/gnucash/gnome-utils/dialog-options.c b/gnucash/gnome-utils/dialog-options.c
deleted file mode 100644
index ec0a9fda2..000000000
--- a/gnucash/gnome-utils/dialog-options.c
+++ /dev/null
@@ -1,4163 +0,0 @@
-/********************************************************************\
- * dialog-options.c -- GNOME option handling                        *
- * Copyright (C) 1998-2000 Linas Vepstas                            *
- * Copyright (c) 2006 David Hampton <hampton at employees.org>         *
- * Copyright (c) 2011 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                   *
-\********************************************************************/
-
-#include <config.h>
-
-#include <gtk/gtk.h>
-#include <gdk/gdk.h>
-#include <glib/gi18n.h>
-#include "swig-runtime.h"
-
-#include "gnc-tree-model-budget.h" //FIXME?
-#include "gnc-budget.h"
-
-#include "dialog-options.h"
-#include "dialog-utils.h"
-#include "gnc-engine-guile.h"
-#include "glib-guile.h"
-#include "gnc-account-sel.h"
-#include "gnc-tree-view-account.h"
-#include "gnc-tree-model-account.h"
-#include "gnc-commodity-edit.h"
-#include "gnc-component-manager.h"
-#include "gnc-general-select.h"
-#include "gnc-currency-edit.h"
-#include "gnc-date-edit.h"
-#include "gnc-engine.h"
-#include "gnc-prefs.h"
-#include "gnc-gui-query.h"
-#include "gnc-session.h"
-#include "gnc-ui.h"
-#include "gnc-guile-utils.h"
-#include "option-util.h"
-#include "guile-mappings.h"
-#include "gnc-date-format.h"
-#include "misc-gnome-utils.h"
-
-#define GNC_PREF_CLOCK_24H "clock-24h"
-
-#define FUNC_NAME G_STRFUNC
-/* TODO: clean up "register-stocks" junk
- */
-
-
-/* This static indicates the debugging module that this .o belongs to.  */
-static QofLogModule log_module = GNC_MOD_GUI;
-
-#define DIALOG_OPTIONS_CM_CLASS      "dialog-options"
-
-#define GNC_PREFS_GROUP              "dialogs.options"
-
-/*
- * Point where preferences switch control method from a set of
- * notebook tabs to a list.
- */
-#define MAX_TAB_COUNT 6
-
-/* A pointer to the last selected filename */
-#define LAST_SELECTION "last-selection"
-
-/* A Hash-table of GNCOptionDef_t keyed with option names. */
-static GHashTable *optionTable = NULL;
-
-struct gnc_option_win
-{
-    GtkWidget  * window;
-    GtkWidget  * notebook;
-    GtkWidget  * page_list_view;
-    GtkWidget  * page_list;
-
-    gboolean toplevel;
-
-    GNCOptionWinCallback apply_cb;
-    gpointer             apply_cb_data;
-
-    GNCOptionWinCallback help_cb;
-    gpointer             help_cb_data;
-
-    GNCOptionWinCallback close_cb;
-    gpointer             close_cb_data;
-
-    /* Hold onto this for a complete reset */
-    GNCOptionDB *option_db;
-
-    /* Hold on to this to unregister the right class */
-    const char *component_class;
-
-    /* widget being destroyed */
-    gboolean destroyed;
-};
-
-typedef enum
-{
-    GNC_RD_WID_AB_BUTTON_POS = 0,
-    GNC_RD_WID_AB_WIDGET_POS,
-    GNC_RD_WID_REL_BUTTON_POS,
-    GNC_RD_WID_REL_WIDGET_POS
-} GNCRdPositions;
-
-enum page_tree
-{
-    PAGE_INDEX = 0,
-    PAGE_NAME,
-    NUM_COLUMNS
-};
-
-static GNCOptionWinCallback global_help_cb = NULL;
-gpointer global_help_cb_data = NULL;
-
-static void gnc_options_dialog_reset_cb (GtkWidget * w, gpointer data);
-void gnc_options_dialog_list_select_cb (GtkTreeSelection *selection,
-                                        gpointer data);
-static void component_close_handler (gpointer data);
-
-GtkWidget *
-gnc_option_get_gtk_widget (GNCOption *option)
-{
-    return (GtkWidget *)gnc_option_get_widget (option);
-}
-
-static void
-gnc_options_dialog_changed_internal (GtkWidget *widget, gboolean sensitive)
-{
-    GtkButton *button = NULL;
-
-    while (widget && !GTK_IS_WINDOW(widget))
-        widget = gtk_widget_get_parent (widget);
-    if (widget == NULL)
-        return;
-
-    /* find the ok and cancel buttons, we know where they will be so do it
-       this way as opposed to using gtk_container_foreach, much less iteration */
-    if (GTK_IS_CONTAINER(widget))
-    {
-        GList *children = gtk_container_get_children (GTK_CONTAINER(widget));
-        for (GList *it = children; it; it = it->next)
-        {
-            if (GTK_IS_BOX(GTK_WIDGET(it->data)))
-            {
-                GList *children = gtk_container_get_children (GTK_CONTAINER(it->data));
-                for (GList *it = children; it; it = it->next)
-                {
-                    if (GTK_IS_BUTTON_BOX(GTK_WIDGET(it->data)))
-                    {
-                        GList *children = gtk_container_get_children (GTK_CONTAINER(it->data));
-                        for (GList *it = children; it; it = it->next)
-                        {
-                            if (g_strcmp0 (gtk_widget_get_name (GTK_WIDGET(it->data)), "ok_button") == 0)
-                                gtk_widget_set_sensitive (GTK_WIDGET(it->data), sensitive);
-
-                            if (g_strcmp0 (gtk_widget_get_name (GTK_WIDGET(it->data)), "apply_button") == 0)
-                                gtk_widget_set_sensitive (GTK_WIDGET(it->data), sensitive);
-
-                            if (g_strcmp0 (gtk_widget_get_name (GTK_WIDGET(it->data)), "cancel_button") == 0)
-                                button = GTK_BUTTON(it->data);
-                        }
-                        g_list_free (children);
-                    }
-                }
-                g_list_free (children);
-            }
-        }
-        g_list_free (children);
-    }
-
-    if (button)
-    {
-        if (sensitive)
-           gtk_button_set_label (button, _("_Cancel"));
-        else
-           gtk_button_set_label (button, _("_Close"));
-    }
-}
-
-void
-gnc_options_dialog_changed (GNCOptionWin *win)
-{
-    if (!win) return;
-
-    gnc_options_dialog_changed_internal (win->window, TRUE);
-}
-
-void
-gnc_option_changed_widget_cb (GtkWidget *widget, GNCOption *option)
-{
-    gnc_option_set_changed (option, TRUE);
-    gnc_option_call_option_widget_changed_proc (option, FALSE);
-    gnc_options_dialog_changed_internal (widget, TRUE);
-}
-
-void
-gnc_option_changed_option_cb (GtkWidget *dummy, GNCOption *option)
-{
-    GtkWidget *widget = gnc_option_get_gtk_widget (option);
-    gnc_option_changed_widget_cb (widget, option);
-}
-
-static void
-gnc_date_option_set_select_method (GNCOption *option,
-                                   gboolean use_absolute,
-                                   gboolean set_buttons)
-{
-    GList* widget_list;
-    GtkWidget *ab_button, *rel_button, *rel_widget, *ab_widget;
-    GtkWidget *widget;
-
-    widget = gnc_option_get_gtk_widget (option);
-
-    widget_list = gtk_container_get_children (GTK_CONTAINER(widget));
-    ab_button = g_list_nth_data (widget_list, GNC_RD_WID_AB_BUTTON_POS);
-    ab_widget = g_list_nth_data (widget_list, GNC_RD_WID_AB_WIDGET_POS);
-    rel_button = g_list_nth_data (widget_list, GNC_RD_WID_REL_BUTTON_POS);
-    rel_widget = g_list_nth_data (widget_list, GNC_RD_WID_REL_WIDGET_POS);
-    g_list_free (widget_list);
-
-    if (use_absolute)
-    {
-        gtk_widget_set_sensitive (ab_widget, TRUE);
-        gtk_widget_set_sensitive (rel_widget, FALSE);
-        if (set_buttons)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(ab_button), TRUE);
-    }
-    else
-    {
-        gtk_widget_set_sensitive (rel_widget, TRUE);
-        gtk_widget_set_sensitive (ab_widget, FALSE);
-        if (set_buttons)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(rel_button), TRUE);
-    }
-}
-
-static void
-gnc_rd_option_ab_set_cb (GtkWidget *widget, gpointer *raw_option)
-{
-    GNCOption *option = (GNCOption *) raw_option;
-    gnc_date_option_set_select_method (option, TRUE, FALSE);
-    gnc_option_changed_option_cb (widget, option);
-}
-
-static void
-gnc_rd_option_rel_set_cb (GtkWidget *widget, gpointer *raw_option)
-{
-    GNCOption *option = (GNCOption *) raw_option;
-    gnc_date_option_set_select_method (option, FALSE, FALSE);
-    gnc_option_changed_option_cb (widget, option);
-    return;
-}
-
-static void
-gnc_image_option_update_preview_cb (GtkFileChooser *chooser,
-                                    GNCOption *option)
-{
-    gchar *filename;
-    GtkImage *image;
-    GdkPixbuf *pixbuf;
-    gboolean have_preview;
-
-    g_return_if_fail (chooser != NULL);
-
-    ENTER("chooser %p, option %p", chooser, option);
-    filename = gtk_file_chooser_get_preview_filename (chooser);
-    DEBUG("chooser preview name is %s.", filename ? filename : "(null)");
-    if (filename == NULL)
-    {
-        filename = g_strdup (g_object_get_data (G_OBJECT(chooser), LAST_SELECTION));
-        DEBUG("using last selection of %s", filename ? filename : "(null)");
-        if (filename == NULL)
-        {
-            LEAVE("no usable name");
-            return;
-        }
-    }
-
-    image = GTK_IMAGE(gtk_file_chooser_get_preview_widget (chooser));
-    pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 128, 128, NULL);
-    g_free (filename);
-    have_preview = (pixbuf != NULL);
-
-    gtk_image_set_from_pixbuf (image, pixbuf);
-    if (pixbuf)
-        g_object_unref (pixbuf);
-
-    gtk_file_chooser_set_preview_widget_active (chooser, have_preview);
-    LEAVE("preview visible is %d", have_preview);
-}
-
-static void
-gnc_image_option_selection_changed_cb (GtkFileChooser *chooser,
-                                       GNCOption *option)
-{
-    gchar *filename = gtk_file_chooser_get_preview_filename (chooser);
-    if (!filename)
-        return;
-    g_object_set_data_full (G_OBJECT(chooser), LAST_SELECTION, filename, g_free);
-}
-
-/********************************************************************\
- * gnc_option_set_ui_value_internal                                 *
- *   sets the GUI representation of an option with either its       *
- *   current guile value, or its default value                      *
- *                                                                  *
- * Args: option      - option structure containing option           *
- *       use_default - if true, use the default value, otherwise    *
- *                     use the current value                        *
- * Return: nothing                                                  *
-\********************************************************************/
-static void
-gnc_option_set_ui_value_internal (GNCOption *option, gboolean use_default)
-{
-    gboolean bad_value = FALSE;
-    GtkWidget *widget;
-    char *type;
-    SCM getter;
-    SCM value;
-    GNCOptionDef_t *option_def;
-
-    widget = gnc_option_get_gtk_widget (option);
-    if (!widget)
-        return;
-
-    type = gnc_option_type (option);
-
-    if (use_default)
-    {
-        SCM opt_getter = gnc_option_getter (option);
-        SCM opt_value = scm_call_0 (opt_getter);
-        SCM def_value;
-
-        getter = gnc_option_default_getter (option);
-        def_value = scm_call_0 (getter);
-
-        // only set changed if the values have changed
-        if (!scm_is_true (scm_equal_p (opt_value, def_value)))
-            gnc_option_set_changed (option, TRUE);
-    }
-    else
-        getter = gnc_option_getter (option);
-
-    value = scm_call_0 (getter);
-
-    option_def = gnc_options_ui_get_option (type);
-    if (option_def && option_def->set_value)
-    {
-        bad_value = option_def->set_value (option, use_default, widget, value);
-        if (bad_value)
-        {
-            gchar *name = gnc_option_name (option);
-            gchar *val = scm_to_locale_string (scm_object_to_string
-                                               (value, scm_c_eval_string ("write")));
-            PERR ("option '%s' bad value '%s'\n", name, val);
-            g_free (name);
-            g_free (val);
-        }
-    }
-    else
-        PERR("Unknown type. Ignoring.\n");
-
-    free (type);
-}
-
-/********************************************************************\
- * gnc_option_get_ui_value_internal                                 *
- *   returns the SCM representation of the GUI option value         *
- *                                                                  *
- * Args: option - option structure containing option                *
- * Return: SCM handle to GUI option value                           *
-\********************************************************************/
-static SCM
-gnc_option_get_ui_value_internal (GNCOption *option)
-{
-    SCM result = SCM_UNDEFINED;
-    GtkWidget *widget;
-    char *type;
-    GNCOptionDef_t *option_def;
-
-    widget = gnc_option_get_gtk_widget (option);
-    if (!widget)
-        return result;
-
-    type = gnc_option_type (option);
-
-    option_def = gnc_options_ui_get_option (type);
-
-    if (option_def && option_def->get_value)
-        result = option_def->get_value (option, widget);
-    else
-        PERR("Unknown type for refresh. Ignoring.\n");
-
-    free (type);
-
-    return result;
-}
-
-/********************************************************************\
- * gnc_option_set_selectable_internal                               *
- *   Change the selectable state of the widget that represents a    *
- *   GUI option.                                                    *
- *                                                                  *
- * Args: option      - option to change widget state for            *
- *       selectable  - if false, update the widget so that it       *
- *                     cannot be selected by the user.  If true,    *
- *                     update the widget so that it can be selected.*
- * Return: nothing                                                  *
-\********************************************************************/
-static void
-gnc_option_set_selectable_internal (GNCOption *option, gboolean selectable)
-{
-    GtkWidget *widget;
-
-    widget = gnc_option_get_gtk_widget (option);
-    if (!widget)
-        return;
-
-    gtk_widget_set_sensitive (widget, selectable);
-}
-
-static void
-gnc_option_default_cb (GtkWidget *widget, GNCOption *option)
-{
-    gnc_option_set_ui_value (option, TRUE);
-    gnc_option_set_changed (option, TRUE);
-    gnc_options_dialog_changed_internal (widget, TRUE);
-}
-
-static void
-gnc_option_show_hidden_toggled_cb (GtkWidget *widget, GNCOption* option)
-{
-    AccountViewInfo avi;
-    GncTreeViewAccount *tree_view;
-
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
-    gnc_tree_view_account_get_view_info (tree_view, &avi);
-    avi.show_hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
-    gnc_tree_view_account_set_view_info (tree_view, &avi);
-    gnc_option_changed_widget_cb (widget, option);
-}
-
-static void
-gnc_option_multichoice_cb (GtkWidget *widget, gpointer data)
-{
-    GNCOption *option = data;
-    gnc_option_changed_widget_cb (widget, option);
-}
-
-static void
-gnc_option_radiobutton_cb (GtkWidget *w, gpointer data)
-{
-    GNCOption *option = data;
-    GtkWidget *widget;
-    gpointer _current, _new_value;
-    gint current, new_value;
-
-    widget = gnc_option_get_gtk_widget (option);
-
-    _current = g_object_get_data (G_OBJECT(widget), "gnc_radiobutton_index");
-    current = GPOINTER_TO_INT(_current);
-
-    _new_value = g_object_get_data (G_OBJECT(w), "gnc_radiobutton_index");
-    new_value = GPOINTER_TO_INT(_new_value);
-
-    if (current == new_value)
-        return;
-
-    g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index",
-                       GINT_TO_POINTER(new_value));
-    gnc_option_changed_widget_cb (widget, option);
-}
-
-<<<<<<< HEAD
-static gboolean
-gnc_gain_loss_account_view_filter (Account  *account, gpointer  data)
-{
-    GNCAccountType type = xaccAccountGetType (account);
-
-    /* gain/loss accts must be an Income or Expense accts and not hidden;
-       placeholder accounts must be included, irrespective of their currency,
-       so their children are available to be considered */
-    if (((type == ACCT_TYPE_INCOME) || (type == ACCT_TYPE_EXPENSE)) &&
-        (!xaccAccountIsHidden(account)))
-    {
-        if (xaccAccountGetPlaceholder (account))
-        {
-            GList *placeholder_children = gnc_account_get_children (account);
-
-            if (placeholder_children)
-            { /* determine if any children qualify; just need one but don't
-                 double count in gain_loss_accounts_in_filter */
-                int saved_gain_loss_accounts_in_filter =
-                                                gain_loss_accounts_in_filter;
-                gboolean child_pass_filter = FALSE;
-                GList *l = NULL;
-                for (l = placeholder_children; l != NULL; l = l->next)
-                {
-                    Account  *child_account = l->data;
-                    child_pass_filter =
-                        gnc_gain_loss_account_view_filter (child_account, NULL);
-                    if (child_pass_filter)
-                        break;
-                }
-                g_list_free (placeholder_children);
-                gain_loss_accounts_in_filter =
-                                           saved_gain_loss_accounts_in_filter;
-                return child_pass_filter;
-            }
-            else return FALSE; // no children, not interested
-        }
-        else
-        {
-            gnc_commodity *commodity = NULL;
-
-            /* gain/loss accts must be in book-currency; if a book currency has been
-               specified in the widget, use it to filter */
-            if (gtk_combo_box_get_active (GTK_COMBO_BOX(
-                                          book_currency_data->book_currency_widget)) != -1)
-                commodity = gnc_currency_edit_get_currency (
-                                GNC_CURRENCY_EDIT(
-                                    book_currency_data->book_currency_widget));
-            if (commodity)
-            {
-                if (gnc_commodity_equal (xaccAccountGetCommodity (account),
-                                         commodity))
-                {
-                    gain_loss_accounts_in_filter++;
-                    return TRUE;
-                }
-                else return FALSE;
-            }
-            /* else use the default currency */
-            else if (gnc_commodity_equal (xaccAccountGetCommodity (account),
-                                          gnc_default_currency ()))
-            {
-                gain_loss_accounts_in_filter++;
-                return TRUE;
-            }
-            else return FALSE;
-        }
-    }
-    else return FALSE;
-}
-
-static gboolean
-gnc_gain_loss_account_all_fail_filter (Account  *account, gpointer  data)
-{
-    return FALSE;
-}
-
-void
-gnc_set_default_cost_policy_widget (SCM list_symbol)
-{
-    GList *list_of_policies = gnc_get_valid_policy_list ();
-
-    if (list_of_policies)
-    {
-        GList *l = NULL;
-        gint i = 0;
-        for (l = list_of_policies; l != NULL; l = l->next)
-        {
-            GNCPolicy *pcy = l->data;
-            if (g_strcmp0 (PolicyGetName (pcy),
-                           gnc_scm_symbol_to_locale_string (list_symbol))
-                           == 0)
-            {
-                gtk_combo_box_set_active (
-                    GTK_COMBO_BOX(
-                        book_currency_data->default_cost_policy_widget), i);
-            }
-            i++;
-        }
-        g_list_free (list_of_policies);
-    }
-    else
-    {
-        gtk_combo_box_set_active (
-            GTK_COMBO_BOX(book_currency_data->default_cost_policy_widget), -1);
-    }
-}
-
-void
-gnc_set_default_gain_loss_account_widget (gnc_commodity *commodity)
-{
-    if (book_currency_data->default_gain_loss_account_widget)
-    {
-        gtk_widget_destroy (
-                    book_currency_data->default_gain_loss_account_widget);
-        book_currency_data->default_gain_loss_account_widget = NULL;
-        book_currency_data->prior_gain_loss_account = NULL;
-        gain_loss_accounts_in_filter = 0;
-    }
-    if (book_currency_data->gain_loss_account_del_button)
-    {
-        gtk_widget_destroy (
-                    book_currency_data->gain_loss_account_del_button);
-        book_currency_data->gain_loss_account_del_button = NULL;
-    }
-    if (book_currency_data->default_gain_loss_account_text)
-    {
-        gtk_widget_destroy (
-                    book_currency_data->default_gain_loss_account_text);
-        book_currency_data->default_gain_loss_account_text = NULL;
-    }
-    if (gnc_is_new_book ())
-    {
-        book_currency_data->default_gain_loss_account_text =
-                    gtk_label_new ( _("Because no accounts have " \
-                        "been set up yet, you will need to return to this " \
-                        "dialog (via File->Properties), after account setup, " \
-                        "if you want to set a default gain/loss account.") );
-
-        gtk_label_set_line_wrap (GTK_LABEL(book_currency_data->default_gain_loss_account_text), TRUE);
-
-        gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table),
-                                  book_currency_data->default_gain_loss_account_text, 0, 1, 2, 1);
-    }
-    else
-    {
-        GtkTreeSelection *selection = NULL;
-        book_currency_data->default_gain_loss_account_widget =
-                            GTK_WIDGET(gnc_tree_view_account_new (FALSE));
-        gain_loss_accounts_in_filter = 0;
-        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(
-                        book_currency_data->default_gain_loss_account_widget));
-        if (!commodity) // that means not book currency
-        {
-            /* set the default_gain_loss_account_widget to be blank with a
-               no-acct filter */
-            gnc_tree_view_account_set_filter (GNC_TREE_VIEW_ACCOUNT(
-                        book_currency_data->default_gain_loss_account_widget),
-                        gnc_gain_loss_account_all_fail_filter,
-                        NULL,  /* user data */
-                        NULL  /* destroy callback */ );
-            gtk_tree_selection_unselect_all (selection);
-        }
-        else // that means book currency
-        {
-            /* see if there are any accounts after filter */
-            gnc_tree_view_account_set_filter (GNC_TREE_VIEW_ACCOUNT(
-                        book_currency_data->default_gain_loss_account_widget),
-                        gnc_gain_loss_account_view_filter,
-                        NULL, /* user data */
-                        NULL  /* destroy callback */);
-            if (gain_loss_accounts_in_filter > 0)
-            {   /* there are accounts; find out if one is selected */
-                Account *gain_loss_account = NULL;
-                Account *selected_account = NULL;
-                GtkTreeViewColumn *col;
-
-                book_currency_data->gain_loss_account_del_button =
-                        gtk_button_new_with_label ( _("Select no account") );
-
-                g_signal_connect (GTK_BUTTON(
-                        book_currency_data->gain_loss_account_del_button),
-                        "clicked",
-                        G_CALLBACK(
-                            gnc_option_changed_gain_loss_account_del_button_widget_cb),
-                        NULL);
-                gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table),
-                                          book_currency_data->gain_loss_account_del_button, 1, 0, 1, 1);
-
-                gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(
-                        book_currency_data->default_gain_loss_account_widget),
-                        TRUE);
-                col = gnc_tree_view_add_text_column (GNC_TREE_VIEW(
-                        book_currency_data->default_gain_loss_account_widget),
-                         _("Currency"), /* title */
-                        "commodity", /* pref name */
-                        NULL,
-                        "Currency--", /* sizing text */
-                        GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY,
-                        GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
-                        NULL);
-                g_object_set_data (G_OBJECT(col), DEFAULT_VISIBLE,
-                                   GINT_TO_POINTER(1));
-
-                // add the color background data function to the column
-                gnc_tree_view_account_column_add_color (GNC_TREE_VIEW_ACCOUNT(
-                         book_currency_data->default_gain_loss_account_widget), col);
-
-                col = gnc_tree_view_add_toggle_column (GNC_TREE_VIEW(
-                        book_currency_data->default_gain_loss_account_widget),
-                        _("Placeholder"),
-                        C_("Column header for 'Placeholder'", "P"),
-                        "placeholder",
-                        GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER,
-                        GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
-                        NULL,
-                        NULL);
-                g_object_set_data (G_OBJECT(col), DEFAULT_VISIBLE,
-                    GINT_TO_POINTER(1));
-
-                // add the color background data function to the column
-                gnc_tree_view_account_column_add_color (GNC_TREE_VIEW_ACCOUNT(
-                         book_currency_data->default_gain_loss_account_widget), col);
-
-                gnc_tree_view_configure_columns (GNC_TREE_VIEW(
-                        book_currency_data->default_gain_loss_account_widget));
-                gnc_tree_view_set_show_column_menu (GNC_TREE_VIEW(
-                        book_currency_data->default_gain_loss_account_widget),
-                        FALSE);
-                if (book_currency_data->retrieved_gain_loss_acct_guid_scm &&
-                    (scm_is_string (
-                        book_currency_data->retrieved_gain_loss_acct_guid_scm)))
-                {
-                    GncGUID *guid= g_new (GncGUID, 1);
-
-                    if (string_to_guid (
-                        gnc_scm_to_utf8_string (
-                        book_currency_data->retrieved_gain_loss_acct_guid_scm),
-                        guid))
-                    gain_loss_account =
-                                xaccAccountLookup (guid, gnc_get_current_book ());
-                    g_free (guid);
-                }
-                if (gain_loss_account)
-                {
-                    (gnc_tree_view_account_set_selected_account
-                        (GNC_TREE_VIEW_ACCOUNT(
-                          book_currency_data->default_gain_loss_account_widget),
-                        gain_loss_account));
-                    selected_account =
-                        gnc_tree_view_account_get_selected_account (
-                            GNC_TREE_VIEW_ACCOUNT(
-                            book_currency_data->default_gain_loss_account_widget));
-                }
-                if (selected_account)
-                {
-                    book_currency_data->prior_gain_loss_account =
-                        selected_account;
-                    gtk_widget_set_sensitive (
-                        book_currency_data->gain_loss_account_del_button,
-                        TRUE);
-                }
-                else /* none selected */
-                {
-                    gtk_tree_selection_unselect_all (selection);
-                    gtk_widget_set_sensitive (
-                        book_currency_data->gain_loss_account_del_button,
-                        FALSE);
-                }
-            }
-            else /* no accts in widget?; replace widget with text */
-            {
-                gtk_widget_destroy (
-                    book_currency_data->default_gain_loss_account_widget);
-                book_currency_data->default_gain_loss_account_widget = NULL;
-                book_currency_data->prior_gain_loss_account = NULL;
-                gain_loss_accounts_in_filter = 0;
-                book_currency_data->default_gain_loss_account_text =
-                    gtk_label_new ( _("There are no income " \
-                        "or expense accounts of the specified\n" \
-                        "book currency; you will have to return to this " \
-                        "dialog\n(via File->Properties), after account setup, " \
-                        "to select a\ndefault gain/loss account.") );
-                gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table),
-                                          book_currency_data->default_gain_loss_account_text, 0, 1, 2, 1);
-            }
-        }
-        if (book_currency_data->default_gain_loss_account_widget)
-        {
-            gtk_widget_set_hexpand (GTK_WIDGET(book_currency_data->default_gain_loss_account_widget), TRUE);
-            g_signal_connect (G_OBJECT(selection),
-                              "changed",
-                              G_CALLBACK(gnc_option_changed_gain_loss_account_widget_cb),
-                              NULL);
-            gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table),
-                                      book_currency_data->default_gain_loss_account_widget, 0, 1, 2, 1);
-        }
-    }
-}
-
-void
-gnc_option_changed_book_currency_widget_cb (GtkWidget *widget)
-{
-    /* Once the book currency widget is set, need to set the
-       default_gain_loss_account_widget and/or del-button or text*/
-    if (gtk_combo_box_get_active (GTK_COMBO_BOX(book_currency_data->book_currency_widget)) != -1)
-    {
-        gnc_commodity *commodity = gnc_currency_edit_get_currency (
-                                       GNC_CURRENCY_EDIT(
-                                       book_currency_data->book_currency_widget));
-
-        gnc_set_default_gain_loss_account_widget (commodity);
-    }
-    gtk_widget_show_all (book_currency_data->book_currency_vbox);
-    gnc_option_changed_widget_cb (widget, book_currency_data->option);
-}
-
-void
-gnc_option_changed_gain_loss_account_widget_cb (GtkTreeSelection *selection,
-                                                gpointer data)
-{
-    Account *account = NULL;
-    gboolean new_eq_prior_acct = FALSE;
-
-    g_return_if_fail (book_currency_data->default_gain_loss_account_widget);
-    account = gnc_tree_view_account_get_selected_account (
-                    GNC_TREE_VIEW_ACCOUNT(
-                        book_currency_data->default_gain_loss_account_widget));
-    if (account && book_currency_data->prior_gain_loss_account)
-        new_eq_prior_acct = xaccAccountEqual (account,
-                                book_currency_data->prior_gain_loss_account,
-                                TRUE);
-    if (account && (!new_eq_prior_acct))
-    { /* a new account has been selected */
-        if (!xaccAccountGetPlaceholder (account))
-        {
-            GtkWidget *option_widget =
-                        gnc_option_get_gtk_widget (book_currency_data->option);
-            book_currency_data->prior_gain_loss_account = account;
-            gtk_widget_set_sensitive (
-                    book_currency_data->gain_loss_account_del_button, TRUE);
-            gtk_widget_show_all (book_currency_data->book_currency_vbox);
-            gnc_option_changed_option_cb (option_widget, book_currency_data->option);
-        }
-        else /*  new account, but placeholder */
-        {
-            const char *message = _("The account %s is a placeholder account " \
-                "and does not allow transactions. " \
-            "Please choose a different account.");
-
-            gnc_error_dialog (gnc_ui_get_gtk_window (book_currency_data->default_gain_loss_account_widget),
-                  message, xaccAccountGetName (account));
-            if (book_currency_data->prior_gain_loss_account)
-            {
-                (gnc_tree_view_account_set_selected_account
-                    (GNC_TREE_VIEW_ACCOUNT(
-                          book_currency_data->default_gain_loss_account_widget),
-                        book_currency_data->prior_gain_loss_account));
-            }
-            else
-            {
-                gtk_tree_selection_unselect_all (selection);
-            }
-        }
-    }
-    else /* a new account has not been selected */
-    {
-        if (book_currency_data->prior_gain_loss_account == NULL)
-        {
-            gtk_tree_selection_unselect_all (selection);
-            if (book_currency_data->gain_loss_account_del_button)
-            {
-                gtk_widget_set_sensitive (
-                    book_currency_data->gain_loss_account_del_button, FALSE);
-            }
-        }
-    }
-}
-
-void
-gnc_option_changed_gain_loss_account_del_button_widget_cb (GtkButton *button,
-                                                           gpointer data)
-{
-    GtkTreeSelection *selection = NULL;
-    GtkWidget *option_widget =
-                        gnc_option_get_gtk_widget (book_currency_data->option);
-
-    g_return_if_fail (book_currency_data->default_gain_loss_account_widget);
-    g_return_if_fail (book_currency_data->gain_loss_account_del_button);
-
-    selection = gtk_tree_view_get_selection (
-                    GTK_TREE_VIEW(
-                        book_currency_data->default_gain_loss_account_widget));
-    gtk_tree_selection_unselect_all (selection);
-    book_currency_data->prior_gain_loss_account = NULL;
-    gtk_widget_set_sensitive (
-                    book_currency_data->gain_loss_account_del_button, FALSE);
-    gnc_option_changed_option_cb (option_widget, book_currency_data->option);
-}
-
-static void
-gnc_option_currency_accounting_non_book_cb (GtkWidget *widget, gpointer data)
-{
-    gnc_currency_edit_clear_display (GNC_CURRENCY_EDIT(
-                                     book_currency_data->book_currency_widget));
-    gtk_combo_box_set_active (GTK_COMBO_BOX(
-                              book_currency_data->default_cost_policy_widget),
-                              -1);
-    gnc_set_default_gain_loss_account_widget (NULL);
-    gtk_widget_show_all (book_currency_data->book_currency_vbox);
-    gtk_widget_set_sensitive (book_currency_data->book_currency_vbox, FALSE);
-    gnc_option_radiobutton_cb (widget, (gpointer) book_currency_data->option);
-}
-
-static void
-gnc_option_currency_accounting_book_cb (GtkWidget *widget, gpointer data)
-{
-    SCM list_symbol =
-            gnc_currency_accounting_option_get_default_policy (
-                                                    book_currency_data->option);
-    SCM curr_scm = gnc_currency_accounting_option_get_default_currency (
-                                                    book_currency_data->option);
-    gnc_commodity *commodity = gnc_scm_to_commodity (curr_scm);
-
-    if (book_currency_data->retrieved_book_currency)
-    {
-        gnc_currency_edit_set_currency
-                (GNC_CURRENCY_EDIT(book_currency_data->book_currency_widget),
-                 book_currency_data->retrieved_book_currency);
-    }
-    else if (commodity)
-    {
-        gnc_currency_edit_set_currency
-                (GNC_CURRENCY_EDIT(book_currency_data->book_currency_widget),
-                 commodity);
-    }
-    else
-    {
-        gnc_currency_edit_set_currency
-                (GNC_CURRENCY_EDIT(book_currency_data->book_currency_widget),
-                 gnc_default_currency());
-    }
-    if (book_currency_data->retrieved_policy_scm)
-    {
-        gnc_set_default_cost_policy_widget (
-                                      book_currency_data->retrieved_policy_scm);
-    }
-    else
-    {
-        gnc_set_default_cost_policy_widget (list_symbol);
-    }
-    gtk_widget_show_all (book_currency_data->book_currency_vbox);
-    gtk_widget_set_sensitive (book_currency_data->book_currency_vbox, TRUE);
-    gnc_option_radiobutton_cb (widget, (gpointer) book_currency_data->option);
-}
-=======
->>>>>>> f87d57145 (Remove the incomplete book-currency code.)
-
-static GtkWidget *
-gnc_option_create_date_widget (GNCOption *option)
-{
-    GtkWidget * box = NULL;
-    GtkWidget *rel_button = NULL, *ab_button = NULL;
-    GtkWidget *rel_widget = NULL, *ab_widget = NULL;
-    GtkWidget *entry;
-    gboolean show_time, use24;
-    char *type;
-    int num_values;
-
-    type = gnc_option_date_option_get_subtype (option);
-    show_time = gnc_option_show_time (option);
-    use24 = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_CLOCK_24H);
-
-    if (g_strcmp0 (type, "relative") != 0)
-    {
-        ab_widget = gnc_date_edit_new (time (NULL), show_time, use24);
-        entry = GNC_DATE_EDIT(ab_widget)->date_entry;
-        g_signal_connect (G_OBJECT(entry), "changed",
-                          G_CALLBACK(gnc_option_changed_option_cb), option);
-        if (show_time)
-        {
-            entry = GNC_DATE_EDIT(ab_widget)->time_entry;
-            g_signal_connect (G_OBJECT(entry), "changed",
-                              G_CALLBACK(gnc_option_changed_option_cb), option);
-        }
-    }
-
-    if (g_strcmp0 (type, "absolute") != 0)
-    {
-        int i;
-        num_values = gnc_option_num_permissible_values (option);
-
-        g_return_val_if_fail (num_values >= 0, NULL);
-
-        {
-            GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
-            GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);
-            GtkTreeIter  iter;
-            char *itemstring;
-
-            /* Add values to the list store */
-            for (i = 0; i < num_values; i++)
-            {
-                itemstring = gnc_option_permissible_value_name (option, i);
-                gtk_list_store_append (store, &iter);
-                gtk_list_store_set (store, &iter, 0, itemstring, -1);
-                if (itemstring)
-                    g_free (itemstring);
-            }
-            /* Create the new Combo and add the store */
-            rel_widget = GTK_WIDGET(gtk_combo_box_new_with_model (GTK_TREE_MODEL(store)));
-
-            gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(rel_widget), renderer, TRUE);
-            gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(rel_widget),
-                                           renderer, "text", 0);
-            g_object_unref (store);
-
-            g_signal_connect (G_OBJECT(rel_widget), "changed",
-                              G_CALLBACK(gnc_option_multichoice_cb), option);
-        }
-    }
-
-    if (g_strcmp0 (type, "absolute") == 0)
-    {
-        free (type);
-        gnc_option_set_widget (option, ab_widget);
-        return ab_widget;
-    }
-    else if (g_strcmp0 (type, "relative") == 0)
-    {
-        gnc_option_set_widget (option, rel_widget);
-        free (type);
-
-        return rel_widget;
-    }
-    else if (g_strcmp0 (type, "both") == 0)
-    {
-        box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-        gtk_box_set_homogeneous (GTK_BOX(box), FALSE);
-
-        ab_button = gtk_radio_button_new (NULL);
-        g_signal_connect (G_OBJECT(ab_button), "toggled",
-                          G_CALLBACK(gnc_rd_option_ab_set_cb), option);
-
-        rel_button = gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON(ab_button));
-        g_signal_connect (G_OBJECT(rel_button), "toggled",
-                          G_CALLBACK(gnc_rd_option_rel_set_cb), option);
-
-        gtk_box_pack_start (GTK_BOX(box), ab_button, FALSE, FALSE, 0);
-        gtk_box_pack_start (GTK_BOX(box), ab_widget, FALSE, FALSE, 0);
-        gtk_box_pack_start (GTK_BOX(box), rel_button, FALSE, FALSE, 0);
-        gtk_box_pack_start (GTK_BOX(box), rel_widget, FALSE, FALSE, 0);
-
-        free (type);
-
-        gnc_option_set_widget (option, box);
-
-        return box;
-    }
-    else /* can't happen */
-        return NULL;
-}
-
-static GtkWidget *
-gnc_option_create_budget_widget (GNCOption *option)
-{
-    GtkTreeModel *tm;
-    GtkComboBox *cb;
-    GtkCellRenderer *cr;
-
-    tm = gnc_tree_model_budget_new (gnc_get_current_book());
-    cb = GTK_COMBO_BOX(gtk_combo_box_new_with_model (tm));
-    g_object_unref (tm);
-    cr = gtk_cell_renderer_text_new ();
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(cb), cr, TRUE);
-
-    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT(cb), cr, "text",
-                                    BUDGET_NAME_COLUMN, NULL);
-    return GTK_WIDGET(cb);
-}
-
-static GtkWidget *
-gnc_option_create_multichoice_widget (GNCOption *option)
-{
-    GtkWidget *widget;
-    int num_values;
-    int i;
-
-    num_values = gnc_option_num_permissible_values (option);
-
-    g_return_val_if_fail (num_values >= 0, NULL);
-
-    {
-        GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
-        GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);
-        GtkTreeIter  iter;
-        char *itemstring;
-
-        /* Add values to the list store */
-        for (i = 0; i < num_values; i++)
-        {
-            itemstring = gnc_option_permissible_value_name (option, i);
-
-            gtk_list_store_append (store, &iter);
-            gtk_list_store_set (store, &iter, 0,
-                                (itemstring && *itemstring) ? _(itemstring) : "", -1);
-
-            if (itemstring)
-                g_free (itemstring);
-        }
-        /* Create the new Combo and add the store */
-        widget = GTK_WIDGET(gtk_combo_box_new_with_model (GTK_TREE_MODEL(store)));
-
-        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(widget), renderer, TRUE);
-        gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(widget),
-                                       renderer, "text", 0);
-        g_object_unref (store);
-
-        g_signal_connect (G_OBJECT(widget), "changed",
-                          G_CALLBACK(gnc_option_multichoice_cb), option);
-    }
-    return widget;
-}
-
-static GtkWidget *
-gnc_option_create_radiobutton_widget (char *name, GNCOption *option)
-{
-    GtkWidget *frame, *box;
-    GtkWidget *widget = NULL;
-    int num_values;
-    char *label;
-    int i;
-
-    num_values = gnc_option_num_permissible_values (option);
-
-    g_return_val_if_fail (num_values >= 0, NULL);
-
-    /* Create our button frame */
-    frame = gtk_frame_new (name);
-
-    /* Create the button box */
-    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(box), FALSE);
-    gtk_container_add (GTK_CONTAINER(frame), box);
-
-    /* Iterate over the options and create a radio button for each one */
-    for (i = 0; i < num_values; i++)
-    {
-        label = gnc_option_permissible_value_name (option, i);
-
-        widget =
-            gtk_radio_button_new_with_label_from_widget (widget ?
-                    GTK_RADIO_BUTTON(widget) :
-                    NULL,
-                    label && *label ? _(label) : "");
-        g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index",
-                           GINT_TO_POINTER (i));
-
-        g_signal_connect (G_OBJECT(widget), "toggled",
-                          G_CALLBACK(gnc_option_radiobutton_cb), option);
-        gtk_box_pack_start (GTK_BOX(box), widget, FALSE, FALSE, 0);
-
-        if (label)
-            free (label);
-    }
-    return frame;
-}
-
-<<<<<<< HEAD
-static GtkWidget *
-gnc_option_create_currency_accounting_widget (char *name, GNCOption *option)
-{
-    GtkWidget *frame = NULL,
-              *widget = NULL,
-              *vbox = NULL;
-    int i;
-    int num_values = gnc_option_num_permissible_values (option);
-
-    g_return_val_if_fail (num_values == 3, NULL);
-    book_currency_data = g_new0 (currency_accounting_data, 1);
-    book_currency_data->option = option;
-
-    /* Create the button frame */
-    frame = gtk_frame_new (name);
-    gtk_widget_set_halign (GTK_WIDGET(frame), GTK_ALIGN_FILL);
-    gtk_widget_set_hexpand (GTK_WIDGET(frame), TRUE);
-
-    /* Create the vertical button box */
-    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
-    gtk_container_add (GTK_CONTAINER(frame), vbox);
-    gtk_widget_set_halign (GTK_WIDGET(vbox), GTK_ALIGN_FILL);
-    gtk_widget_set_hexpand (GTK_WIDGET(vbox), TRUE);
-
-    gtk_widget_set_margin_end (GTK_WIDGET(vbox), 12);
-    gtk_widget_set_margin_bottom (GTK_WIDGET(vbox), 12);
-
-    /* Iterate over the three options and create a radio button for each one */
-    for (i = 0; i < num_values; i++)
-    {
-        char *label;
-        char *tip = NULL;
-        GtkWidget *table = NULL;
-
-        label = gnc_option_permissible_value_name (option, i);
-
-        widget =
-            gtk_radio_button_new_with_label_from_widget (widget ?
-                    GTK_RADIO_BUTTON(widget) :
-                    NULL,
-                    label && *label ? _(label) : "");
-        g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index",
-                           GINT_TO_POINTER(i));
-        switch (i)
-        {
-        case 0:
-            book_currency_data->gnc_currency_radiobutton_0 = widget;
-            break;
-
-        case 1:
-            book_currency_data->gnc_currency_radiobutton_1 = widget;
-            break;
-
-        case 2:
-            book_currency_data->gnc_currency_radiobutton_2 = widget;
-            break;
-
-        default:
-            break;
-        }
-        if (g_strcmp0 (gnc_option_permissible_value_name (option, i),
-                                                    "Use a Book Currency") == 0)
-        {
-            GtkWidget *widget_label,
-                      *policy_table = gtk_grid_new ();
-
-            book_currency_data->book_currency_widget = gnc_currency_edit_new ();
-            book_currency_data->default_cost_policy_widget =
-                                    gnc_cost_policy_select_new ();
-            book_currency_data->default_gain_loss_account_widget = NULL;
-            book_currency_data->gain_loss_account_del_button = NULL;
-            book_currency_data->default_gain_loss_account_text = NULL;
-            book_currency_data->prior_gain_loss_account = NULL;
-
-            book_currency_data->book_currency_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-            gtk_box_set_homogeneous (GTK_BOX(book_currency_data->book_currency_vbox), FALSE);
-
-            table = gtk_grid_new ();
-            gtk_grid_attach (GTK_GRID(table), widget, 0, 0, 2, 1);
-            g_signal_connect (G_OBJECT(widget), "toggled",
-                              G_CALLBACK(gnc_option_currency_accounting_book_cb),
-                              book_currency_data);
-
-            book_currency_data->book_currency_table = gtk_grid_new ();
-            gtk_grid_set_row_spacing (GTK_GRID(book_currency_data->book_currency_table), 6);
-            gtk_grid_set_column_spacing (GTK_GRID(book_currency_data->book_currency_table), 6);
-
-            tip = gnc_currency_accounting_option_currency_documentation (option);
-            widget_label = gtk_label_new ( _("Book currency") );
-            gtk_widget_set_tooltip_text (book_currency_data->book_currency_table,
-                                         tip && *tip ? _(tip) : "");
-
-            gtk_widget_set_halign (GTK_WIDGET(widget_label), GTK_ALIGN_START);
-            gtk_widget_set_hexpand (GTK_WIDGET(widget_label), TRUE);
-
-            gtk_grid_attach (GTK_GRID(book_currency_data->book_currency_table), widget_label, 0, 0, 1, 1);
-
-            g_signal_connect (G_OBJECT(book_currency_data->book_currency_widget),
-                              "changed",
-                              G_CALLBACK(gnc_option_changed_book_currency_widget_cb),
-                              NULL);
-
-            gtk_grid_attach (GTK_GRID(book_currency_data->book_currency_table),
-                                      book_currency_data->book_currency_widget, 1, 0, 1, 1);
-
-            gtk_box_pack_start (GTK_BOX(book_currency_data->book_currency_vbox),
-                                book_currency_data->book_currency_table,
-                                TRUE, TRUE, 0);
-            gtk_widget_set_margin_start (GTK_WIDGET(book_currency_data->book_currency_table), 12);
-            gtk_grid_set_row_spacing (GTK_GRID(policy_table), 6);
-            gtk_grid_set_column_spacing (GTK_GRID(policy_table), 6);
-
-            tip = gnc_currency_accounting_option_policy_documentation (option);
-            widget_label = gtk_label_new ( _("Default lot tracking policy") );
-            gtk_widget_set_tooltip_text (policy_table, tip && *tip ? _(tip) : "");
-
-            gtk_widget_set_halign (GTK_WIDGET(widget_label), GTK_ALIGN_START);
-            gtk_widget_set_hexpand (GTK_WIDGET(widget_label), TRUE);
-
-            gtk_grid_attach (GTK_GRID(policy_table), widget_label, 0, 1, 1, 1);
-
-            g_signal_connect (G_OBJECT(book_currency_data->default_cost_policy_widget),
-                              "changed",
-                              G_CALLBACK(gnc_option_multichoice_cb), option);
-
-            gtk_grid_attach (GTK_GRID(policy_table),
-                             book_currency_data->default_cost_policy_widget, 1, 1, 1, 1);
-
-            gtk_box_pack_start (GTK_BOX(book_currency_data->book_currency_vbox),
-                                policy_table, TRUE, TRUE, 0);
-            gtk_widget_set_margin_start (GTK_WIDGET(policy_table), 12);
-            book_currency_data->gain_loss_account_table = gtk_grid_new ();
-            gtk_grid_set_row_spacing (GTK_GRID(book_currency_data->gain_loss_account_table), 6);
-            gtk_grid_set_column_spacing (GTK_GRID(book_currency_data->gain_loss_account_table), 6);
-
-            tip = gnc_currency_accounting_option_gain_loss_account_documentation (option);
-            widget_label = gtk_label_new ( _("Default gain/loss account") );
-            gnc_label_set_alignment (GTK_WIDGET(widget_label), 0.0, 0.5);
-
-            gtk_widget_set_tooltip_text (book_currency_data->gain_loss_account_table,
-                                         tip && *tip ? _(tip) : "");
-
-            gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table),
-                             widget_label, 0, 0, 1, 1);
-
-            widget_label = NULL;
-            gtk_box_pack_start (GTK_BOX (book_currency_data->book_currency_vbox),
-                                book_currency_data->gain_loss_account_table,
-                                TRUE, TRUE, 0);
-            gtk_widget_set_margin_start (GTK_WIDGET(book_currency_data->gain_loss_account_table), 12);
-            gtk_grid_attach (GTK_GRID(table), book_currency_data->book_currency_vbox, 1, 2, 1, 1);
-        }
-        else /* trading or neither */
-        {
-            table = gtk_grid_new ();
-            gtk_grid_attach (GTK_GRID(table), widget, 0, 1, 1, 1);
-
-            g_signal_connect (G_OBJECT(widget), "toggled",
-                              G_CALLBACK(gnc_option_currency_accounting_non_book_cb),
-                              book_currency_data);
-        }
-        gtk_box_pack_start (GTK_BOX(vbox), table, TRUE, TRUE, 0);
-
-        if (label)
-            free (label);
-        if (tip)
-            free (tip);
-    }
-    return frame;
-}
-
-=======
->>>>>>> f87d57145 (Remove the incomplete book-currency code.)
-static void
-gnc_option_account_cb (GtkTreeSelection *selection, gpointer data)
-{
-    GNCOption *option = data;
-    GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection);
-    gnc_option_changed_widget_cb (GTK_WIDGET(tree_view), option);
-}
-
-static void
-gnc_option_account_select_all_cb (GtkWidget *widget, gpointer data)
-{
-    GNCOption *option = data;
-    GncTreeViewAccount *tree_view;
-    GtkTreeSelection *selection;
-
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
-    gtk_tree_view_expand_all (GTK_TREE_VIEW(tree_view));
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree_view));
-    gtk_tree_selection_select_all (selection);
-    gnc_option_changed_widget_cb (widget, option);
-}
-
-static void
-gnc_option_account_clear_all_cb (GtkWidget *widget, gpointer data)
-{
-    GNCOption *option = data;
-    GncTreeViewAccount *tree_view;
-    GtkTreeSelection *selection;
-
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree_view));
-    gtk_tree_selection_unselect_all (selection);
-    gnc_option_changed_widget_cb (widget, option);
-}
-
-static void
-gnc_option_account_select_children_cb (GtkWidget *widget, gpointer data)
-{
-    GNCOption *option = data;
-    GncTreeViewAccount *tree_view;
-    GList *acct_list = NULL, *acct_iter = NULL;
-
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
-    acct_list = gnc_tree_view_account_get_selected_accounts (tree_view);
-
-    for (acct_iter = acct_list; acct_iter; acct_iter = acct_iter->next)
-        gnc_tree_view_account_select_subaccounts (tree_view, acct_iter->data);
-
-    g_list_free (acct_list);
-}
-
-static GtkWidget *
-gnc_option_create_account_widget (GNCOption *option, char *name)
-{
-    gboolean multiple_selection;
-    GtkWidget *scroll_win;
-    GtkWidget *button;
-    GtkWidget *frame;
-    GtkWidget *tree;
-    GtkWidget *vbox;
-    GtkWidget *bbox;
-    GList *acct_type_list;
-    GtkTreeSelection *selection;
-
-    multiple_selection = gnc_option_multiple_selection (option);
-    acct_type_list = gnc_option_get_account_type_list (option);
-
-    frame = gtk_frame_new (name);
-
-    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-    gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE);
-
-    gtk_container_add (GTK_CONTAINER(frame), vbox);
-
-    tree = GTK_WIDGET(gnc_tree_view_account_new (FALSE));
-    gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(tree), FALSE);
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree));
-    if (multiple_selection)
-        gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
-    else
-        gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
-
-    if (acct_type_list)
-    {
-        GList *node;
-        AccountViewInfo avi;
-        int i;
-
-        gnc_tree_view_account_get_view_info (GNC_TREE_VIEW_ACCOUNT(tree), &avi);
-
-        for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
-            avi.include_type[i] = FALSE;
-        avi.show_hidden = FALSE;
-
-        for (node = acct_type_list; node; node = node->next)
-        {
-            GNCAccountType type = GPOINTER_TO_INT(node->data);
-            avi.include_type[type] = TRUE;
-        }
-
-        gnc_tree_view_account_set_view_info (GNC_TREE_VIEW_ACCOUNT(tree), &avi);
-        g_list_free (acct_type_list);
-    }
-    else
-    {
-        AccountViewInfo avi;
-        int i;
-
-        gnc_tree_view_account_get_view_info (GNC_TREE_VIEW_ACCOUNT(tree), &avi);
-
-        for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
-            avi.include_type[i] = TRUE;
-        avi.show_hidden = FALSE;
-        gnc_tree_view_account_set_view_info (GNC_TREE_VIEW_ACCOUNT(tree), &avi);
-    }
-
-    scroll_win = gtk_scrolled_window_new (NULL, NULL);
-    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll_win),
-                                    GTK_POLICY_AUTOMATIC,
-                                    GTK_POLICY_AUTOMATIC);
-
-    gtk_box_pack_start (GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
-    gtk_container_set_border_width (GTK_CONTAINER(scroll_win), 5);
-    gtk_container_add (GTK_CONTAINER(scroll_win), tree);
-
-    bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
-    gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
-    gtk_box_pack_start (GTK_BOX(vbox), bbox, FALSE, FALSE, 10);
-
-    if (multiple_selection)
-    {
-        button = gtk_button_new_with_label (_("Select All"));
-        gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
-        gtk_widget_set_tooltip_text (button, _("Select all accounts."));
-
-        g_signal_connect (G_OBJECT(button), "clicked",
-                          G_CALLBACK(gnc_option_account_select_all_cb), option);
-
-        button = gtk_button_new_with_label (_("Clear All"));
-        gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
-        gtk_widget_set_tooltip_text (button, _("Clear the selection and unselect all accounts."));
-
-        g_signal_connect (G_OBJECT(button), "clicked",
-                          G_CALLBACK(gnc_option_account_clear_all_cb), option);
-
-        button = gtk_button_new_with_label (_("Select Children"));
-        gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
-        gtk_widget_set_tooltip_text (button, _("Select all descendents of selected account."));
-
-        g_signal_connect (G_OBJECT(button), "clicked",
-                          G_CALLBACK(gnc_option_account_select_children_cb), option);
-    }
-
-    button = gtk_button_new_with_label (_("Select Default"));
-    gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
-    gtk_widget_set_tooltip_text (button, _("Select the default account selection."));
-
-    g_signal_connect (G_OBJECT(button), "clicked",
-                      G_CALLBACK(gnc_option_default_cb), option);
-
-    gtk_widget_set_margin_start (GTK_WIDGET(bbox), 6);
-    gtk_widget_set_margin_end (GTK_WIDGET(bbox), 6);
-
-    if (multiple_selection)
-    {
-        /* Put the "Show hidden" checkbox on a separate line since the 4 buttons make
-           the dialog too wide. */
-        bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
-        gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);
-        gtk_box_pack_start (GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
-    }
-
-    button = gtk_check_button_new_with_label (_("Show Hidden Accounts"));
-    gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
-    gtk_widget_set_tooltip_text (button, _("Show accounts that have been marked hidden."));
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), FALSE);
-    g_signal_connect (G_OBJECT(button), "toggled",
-                      G_CALLBACK(gnc_option_show_hidden_toggled_cb), option);
-
-    gnc_option_set_widget (option, tree);
-
-    return frame;
-}
-
-static void
-gnc_option_list_changed_cb (GtkTreeSelection *selection,
-                            GNCOption *option)
-{
-    GtkTreeView *view = gtk_tree_selection_get_tree_view (selection);
-    gnc_option_changed_widget_cb (GTK_WIDGET(view), option);
-}
-
-static void
-gnc_option_list_select_all_cb (GtkWidget *widget, gpointer data)
-{
-    GNCOption *option = data;
-    GtkTreeView *view;
-    GtkTreeSelection *selection;
-
-    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option));
-    selection = gtk_tree_view_get_selection (view);
-    gtk_tree_selection_select_all (selection);
-    gnc_option_changed_widget_cb (GTK_WIDGET(view), option);
-}
-
-static void
-gnc_option_list_clear_all_cb (GtkWidget *widget, gpointer data)
-{
-    GNCOption *option = data;
-    GtkTreeView *view;
-    GtkTreeSelection *selection;
-
-    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option));
-    selection = gtk_tree_view_get_selection (view);
-    gtk_tree_selection_unselect_all (selection);
-    gnc_option_changed_widget_cb (GTK_WIDGET(view), option);
-}
-
-static GtkWidget *
-gnc_option_create_list_widget (GNCOption *option, char *name)
-{
-    GtkListStore *store;
-    GtkTreeView *view;
-    GtkTreeIter iter;
-    GtkTreeViewColumn *column;
-    GtkCellRenderer *renderer;
-    GtkTreeSelection *selection;
-
-    GtkWidget *button;
-    GtkWidget *frame;
-    GtkWidget *hbox;
-    GtkWidget *bbox;
-    gint num_values;
-    gint i;
-
-    frame = gtk_frame_new (name);
-    hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-    gtk_box_set_homogeneous (GTK_BOX(hbox), FALSE);
-    gtk_container_add (GTK_CONTAINER(frame), hbox);
-
-    store = gtk_list_store_new (1, G_TYPE_STRING);
-    view = GTK_TREE_VIEW(gtk_tree_view_new_with_model (GTK_TREE_MODEL(store)));
-    g_object_unref (store);
-    renderer = gtk_cell_renderer_text_new ();
-    column = gtk_tree_view_column_new_with_attributes ("", renderer,
-                                                       "text", 0,
-                                                       NULL);
-    gtk_tree_view_append_column (view, column);
-    gtk_tree_view_set_headers_visible (view, FALSE);
-
-    num_values = gnc_option_num_permissible_values (option);
-    for (i = 0; i < num_values; i++)
-    {
-        gchar *raw_string, *string;
-
-        raw_string = gnc_option_permissible_value_name (option, i);
-        string = (raw_string && *raw_string) ? _(raw_string) : "";
-        gtk_list_store_append (store, &iter);
-        gtk_list_store_set (store, &iter,
-                            0, string ? string : "",
-                            -1);
-        g_free (raw_string);
-    }
-
-    gtk_box_pack_start (GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0);
-
-    selection = gtk_tree_view_get_selection (view);
-    gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
-    g_signal_connect (selection, "changed",
-                      G_CALLBACK(gnc_option_list_changed_cb), option);
-
-    bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
-    gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
-    gtk_box_pack_end (GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
-
-    button = gtk_button_new_with_label (_("Select All"));
-    gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
-    gtk_widget_set_tooltip_text (button, _("Select all entries."));
-
-    g_signal_connect (G_OBJECT(button), "clicked",
-                      G_CALLBACK(gnc_option_list_select_all_cb), option);
-
-    button = gtk_button_new_with_label (_("Clear All"));
-    gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
-    gtk_widget_set_tooltip_text (button, _("Clear the selection and unselect all entries."));
-
-    g_signal_connect (G_OBJECT(button), "clicked",
-                      G_CALLBACK(gnc_option_list_clear_all_cb), option);
-
-    button = gtk_button_new_with_label (_("Select Default"));
-    gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0);
-    gtk_widget_set_tooltip_text (button, _("Select the default selection."));
-
-    g_signal_connect (G_OBJECT(button), "clicked",
-                      G_CALLBACK(gnc_option_default_cb), option);
-
-    g_object_set (G_OBJECT(hbox), "margin", 3, NULL);
-
-    gnc_option_set_widget (option, GTK_WIDGET(view));
-
-    return frame;
-}
-
-static void
-gnc_option_color_changed_cb (GtkColorButton *color_button, GNCOption *option)
-{
-    gnc_option_changed_widget_cb (GTK_WIDGET(color_button), option);
-}
-
-static void
-gnc_option_font_changed_cb (GtkFontButton *font_button, GNCOption *option)
-{
-    gnc_option_changed_widget_cb (GTK_WIDGET(font_button), option);
-}
-
-static void
-gnc_option_set_ui_widget (GNCOption *option, GtkGrid *page_box, gint grid_row)
-{
-    GtkWidget *enclosing = NULL;
-    GtkWidget *value = NULL;
-    gboolean packed = FALSE;
-    char *raw_name, *raw_documentation;
-    char *name, *documentation;
-    char *type;
-    GNCOptionDef_t *option_def;
-    GtkLabel *name_label;
-
-    ENTER("option %p(%s), box %p",
-          option, gnc_option_name (option), page_box);
-    type = gnc_option_type (option);
-    if (type == NULL)
-    {
-        LEAVE("bad type");
-        return;
-    }
-    else if (g_strcmp0 (type, "internal") == 0)
-    {
-        LEAVE("internal type");
-        return;
-    }
-
-    raw_name = gnc_option_name (option);
-    if (raw_name && *raw_name)
-        name = _(raw_name);
-    else
-        name = NULL;
-
-    raw_documentation = gnc_option_documentation (option);
-    if (raw_documentation && *raw_documentation)
-        documentation = _(raw_documentation);
-    else
-        documentation = NULL;
-
-    name_label = GTK_LABEL(gtk_label_new (name));
-
-    option_def = gnc_options_ui_get_option (type);
-    if (option_def && option_def->set_widget)
-    {
-        value = option_def->set_widget (option, page_box,
-                                        name_label, documentation,
-                                        /* Return values */
-                                        &enclosing, &packed);
-    }
-    else
-    {
-        PERR("Unknown option type. Ignoring option \"%s\".\n", name);
-    }
-
-    /* attach the name label to the first column of the grid and align to the end
-     * if option is a check button, do not add a label as we are using the built
-     * in one */
-    if (!GTK_IS_CHECK_BUTTON(value))
-        gtk_grid_attach (GTK_GRID(page_box), GTK_WIDGET(name_label),
-                         0, grid_row, // left, top
-                         1, 1);  // width, height
-
-    gtk_widget_set_halign (GTK_WIDGET(name_label), GTK_ALIGN_END);
-
-    if (!packed && (enclosing != NULL))
-    {
-        /* Pack option widget into an extra eventbox because otherwise the
-           "documentation" tooltip is not displayed. */
-        GtkWidget *eventbox = gtk_event_box_new ();
-
-        gtk_container_add (GTK_CONTAINER(eventbox), enclosing);
-
-        /* attach the option widget to the second column of the grid */
-        gtk_grid_attach (GTK_GRID(page_box), eventbox,
-                         1, grid_row, // left, top
-                         1, 1);  // width, height
-
-        gtk_widget_set_tooltip_text (eventbox, documentation);
-    }
-
-    if (value != NULL)
-        gtk_widget_set_tooltip_text (value, documentation);
-
-    if (raw_name != NULL)
-        free (raw_name);
-    if (raw_documentation != NULL)
-        free (raw_documentation);
-    free (type);
-    LEAVE(" ");
-}
-
-static void
-gnc_options_dialog_add_option (GtkWidget *page,
-                               GNCOption *option, gint row)
-{
-    g_object_set_data (G_OBJECT(page), "options-grid-row", GINT_TO_POINTER(row));
-    gnc_option_set_ui_widget (option, GTK_GRID(page), row);
-}
-
-static gint
-gnc_options_dialog_append_page (GNCOptionWin * propertybox,
-                                GNCOptionSection *section)
-{
-    GNCOption *option;
-    GtkWidget *page_label;
-    GtkWidget *options_box;
-    GtkWidget *page_content_box;
-    GtkWidget* notebook_page;
-    GtkWidget *reset_button;
-    GtkWidget *listitem = NULL;
-    GtkWidget *buttonbox;
-    GtkWidget *options_scrolled_win;
-    GtkTreeView *view;
-    GtkListStore *list;
-    GtkTreeIter iter;
-    gint num_options;
-    const char *name;
-    gint i, page_count, name_offset;
-    gboolean advanced;
-
-    name = gnc_option_section_name (section);
-    if (!name)
-        return -1;
-
-    if (strncmp (name, "__", 2) == 0)
-        return -1;
-    advanced = (strncmp (name, "_+", 2) == 0);
-    name_offset = (advanced) ? 2 : 0;
-    page_label = gtk_label_new (_(name + name_offset));
-    PINFO("Page_label is %s", _(name + name_offset));
-    gtk_widget_show (page_label);
-
-    /* Build this options page */
-    page_content_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
-    gtk_widget_set_name (page_content_box, "page-content-box");
-    gtk_box_set_homogeneous (GTK_BOX (page_content_box), FALSE);
-
-    gtk_container_set_border_width (GTK_CONTAINER(page_content_box), 12);
-
-    options_scrolled_win = gtk_scrolled_window_new (NULL, NULL);
-    gtk_box_pack_start (GTK_BOX(page_content_box), options_scrolled_win, TRUE, TRUE, 0);
-
-    /* Build space for the content - the options box */
-    options_box = gtk_grid_new (); // this will have two columns
-    gtk_widget_set_name (options_box, "options-box");
-    gtk_grid_set_row_homogeneous (GTK_GRID(options_box), FALSE);
-    gtk_grid_set_column_homogeneous (GTK_GRID(options_box), FALSE);
-    gtk_grid_set_row_spacing (GTK_GRID(options_box), 6);
-    gtk_grid_set_column_spacing (GTK_GRID(options_box), 6);
-
-    gtk_container_set_border_width (GTK_CONTAINER(options_box), 0);
-    gtk_container_add (GTK_CONTAINER(options_scrolled_win), GTK_WIDGET(options_box));
-    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(options_scrolled_win),
-                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-
-    /* Create all the options */
-    num_options = gnc_option_section_num_options (section);
-    for (i = 0; i < num_options; i++)
-    {
-        option = gnc_get_option_section_option (section, i);
-        gnc_options_dialog_add_option (options_box, option, i);
-    }
-
-    /* Add a button box at the bottom of the page */
-    buttonbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
-    gtk_button_box_set_layout (GTK_BUTTON_BOX (buttonbox),
-                               GTK_BUTTONBOX_EDGE);
-    gtk_container_set_border_width (GTK_CONTAINER(buttonbox), 5);
-    gtk_box_pack_end (GTK_BOX(page_content_box), buttonbox, FALSE, FALSE, 0);
-
-    /* The reset button on each option page */
-    reset_button = gtk_button_new_with_label (_("Reset defaults"));
-    gtk_widget_set_tooltip_text (reset_button,
-                                 _("Reset all values to their defaults."));
-
-    g_signal_connect (G_OBJECT(reset_button), "clicked",
-                      G_CALLBACK(gnc_options_dialog_reset_cb), propertybox);
-    g_object_set_data (G_OBJECT(reset_button), "section", section);
-    gtk_box_pack_end (GTK_BOX(buttonbox), reset_button, FALSE, FALSE, 0);
-    gtk_widget_show_all (page_content_box);
-    gtk_notebook_append_page (GTK_NOTEBOOK(propertybox->notebook),
-                              page_content_box, page_label);
-
-    /* Switch to selection from a list if the page count threshold is reached */
-    page_count = gtk_notebook_page_num (GTK_NOTEBOOK(propertybox->notebook),
-                                        page_content_box);
-
-    if (propertybox->page_list_view)
-    {
-        /* Build the matching list item for selecting from large page sets */
-        view = GTK_TREE_VIEW(propertybox->page_list_view);
-        list = GTK_LIST_STORE(gtk_tree_view_get_model (view));
-
-        PINFO("Page name is %s and page_count is %d", name, page_count);
-        gtk_list_store_append (list, &iter);
-        gtk_list_store_set (list, &iter,
-                            PAGE_NAME, _(name),
-                            PAGE_INDEX, page_count,
-                            -1);
-
-        if (page_count > MAX_TAB_COUNT - 1)   /* Convert 1-based -> 0-based */
-        {
-            gtk_widget_show (propertybox->page_list);
-            gtk_notebook_set_show_tabs (GTK_NOTEBOOK(propertybox->notebook), FALSE);
-            gtk_notebook_set_show_border (GTK_NOTEBOOK(propertybox->notebook), FALSE);
-        }
-        else
-            gtk_widget_hide (propertybox->page_list);
-
-        /* Tweak "advanced" pages for later handling. */
-        if (advanced)
-        {
-            notebook_page = gtk_notebook_get_nth_page (GTK_NOTEBOOK(propertybox->notebook),
-                                                       page_count);
-
-            g_object_set_data (G_OBJECT(notebook_page), "listitem", listitem);
-            g_object_set_data (G_OBJECT(notebook_page), "advanced",
-                               GINT_TO_POINTER(advanced));
-        }
-    }
-    return (page_count);
-}
-
-/********************************************************************\
- * gnc_options_dialog_build_contents                                *
- *   builds an options dialog given a property box and an options   *
- *   database and make the dialog visible                           *
- *                                                                  *
- * Args: propertybox - gnome property box to use                    *
- *       odb         - option database to use                       *
- * Return: nothing                                                  *
-\********************************************************************/
-void
-gnc_options_dialog_build_contents (GNCOptionWin *propertybox,
-                                   GNCOptionDB  *odb)
-{
-    gnc_options_dialog_build_contents_full (propertybox, odb, TRUE);
-}
-
-/********************************************************************\
- * gnc_options_dialog_build_contents_full                           *
- *   builds an options dialog given a property box and an options   *
- *   database and make the dialog visible depending on the          *
- *   show_dialog flag                                               *
- *                                                                  *
- * Args: propertybox - gnome property box to use                    *
- *       odb         - option database to use                       *
- *       show_dialog - should dialog be made visible or not         *
- * Return: nothing                                                  *
-\********************************************************************/
-void
-gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox,
-                                        GNCOptionDB  *odb, gboolean show_dialog)
-{
-    GNCOptionSection *section;
-    gchar *default_section_name;
-    gint default_page = -1;
-    gint num_sections;
-    gint page;
-    gint i;
-    guint j;
-
-    g_return_if_fail (propertybox != NULL);
-    g_return_if_fail (odb != NULL);
-
-    gnc_option_db_set_ui_callbacks (odb,
-                                    gnc_option_get_ui_value_internal,
-                                    gnc_option_set_ui_value_internal,
-                                    gnc_option_set_selectable_internal);
-
-    propertybox->option_db = odb;
-
-    num_sections = gnc_option_db_num_sections (odb);
-    default_section_name = gnc_option_db_get_default_section (odb);
-
-    PINFO("Default Section name is %s", default_section_name);
-
-    for (i = 0; i < num_sections; i++)
-    {
-        const char *section_name;
-
-        section = gnc_option_db_get_section (odb, i);
-        page = gnc_options_dialog_append_page (propertybox, section);
-
-        section_name = gnc_option_section_name (section);
-        if (g_strcmp0 (section_name, default_section_name) == 0)
-            default_page = page;
-    }
-
-    if (default_section_name != NULL)
-        free (default_section_name);
-
-    /* call each option widget changed callbacks once at this point,
-     * now that all options widgets exist.
-     */
-    for (i = 0; i < num_sections; i++)
-    {
-        section = gnc_option_db_get_section (odb, i);
-
-        for (j = 0; j < gnc_option_section_num_options (section); j++)
-        {
-            // setting TRUE will clear the changed flag after proc
-            gnc_option_call_option_widget_changed_proc (
-                gnc_get_option_section_option(section, j), TRUE);
-        }
-    }
-
-    gtk_notebook_popup_enable (GTK_NOTEBOOK(propertybox->notebook));
-    if (default_page >= 0)
-    {
-        /* Find the page list and set the selection to the default page */
-        GtkTreeSelection* selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(propertybox->page_list_view));
-        GtkTreeIter iter;
-        GtkTreeModel *model;
-
-        model = gtk_tree_view_get_model (GTK_TREE_VIEW(propertybox->page_list_view));
-        gtk_tree_model_iter_nth_child (model, &iter, NULL, default_page);
-        gtk_tree_selection_select_iter (selection, &iter);
-        gtk_notebook_set_current_page (GTK_NOTEBOOK(propertybox->notebook), default_page);
-    }
-    gnc_options_dialog_changed_internal (propertybox->window, FALSE);
-    if (show_dialog)
-        gtk_widget_show (propertybox->window);
-}
-
-GtkWidget *
-gnc_options_dialog_widget (GNCOptionWin * win)
-{
-    return win->window;
-}
-
-GtkWidget *
-gnc_options_page_list (GNCOptionWin * win)
-{
-    return win->page_list;
-}
-
-GtkWidget *
-gnc_options_dialog_notebook (GNCOptionWin * win)
-{
-    return win->notebook;
-}
-
-static void
-gnc_options_dialog_help_button_cb (GtkWidget * widget, GNCOptionWin *win)
-{
-    if (win->help_cb)
-        (win->help_cb)(win, win->help_cb_data);
-}
-
-static void
-gnc_options_dialog_cancel_button_cb (GtkWidget * widget, GNCOptionWin *win)
-{
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
-
-    if (win->close_cb)
-        (win->close_cb)(win, win->close_cb_data);
-    else
-        gtk_widget_hide (win->window);
-}
-
-static void
-gnc_options_dialog_apply_button_cb (GtkWidget * widget, GNCOptionWin *win)
-{
-    GNCOptionWinCallback close_cb = win->close_cb;
-
-    win->close_cb = NULL;
-    if (win->apply_cb)
-        win->apply_cb (win, win->apply_cb_data);
-    win->close_cb = close_cb;
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
-    gnc_options_dialog_changed_internal (win->window, FALSE);
-}
-
-static void
-gnc_options_dialog_ok_button_cb (GtkWidget * widget, GNCOptionWin *win)
-{
-    GNCOptionWinCallback close_cb = win->close_cb;
-
-    win->close_cb = NULL;
-    if (win->apply_cb)
-        win->apply_cb (win, win->apply_cb_data);
-    win->close_cb = close_cb;
-
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
-
-    if (win->close_cb)
-        (win->close_cb)(win, win->close_cb_data);
-    else
-        gtk_widget_hide (win->window);
-}
-
-static void
-gnc_options_dialog_destroy_cb (GtkWidget *object, GNCOptionWin *win)
-{
-    if (!win) return;
-
-    if (win->destroyed == FALSE)
-    {
-        if (win->close_cb)
-            (win->close_cb)(win, win->close_cb_data);
-    }
-}
-
-static gboolean
-gnc_options_dialog_window_key_press_cb (GtkWidget *widget,
-                                        GdkEventKey *event,
-                                        gpointer data)
-{
-    GNCOptionWin *win = data;
-
-    if (event->keyval == GDK_KEY_Escape)
-    {
-        component_close_handler (win);
-        return TRUE;
-    }
-    else
-        return FALSE;
-}
-
-static void
-gnc_options_dialog_reset_cb (GtkWidget * w, gpointer data)
-{
-    GNCOptionWin *win = data;
-    GNCOptionSection *section;
-    gpointer val;
-
-    val = g_object_get_data (G_OBJECT(w), "section");
-    g_return_if_fail (val);
-    g_return_if_fail (win);
-
-    section = (GNCOptionSection*)val;
-
-    gnc_option_db_section_reset_widgets (section);
-
-    if (gnc_option_db_get_changed (win->option_db))
-        gnc_options_dialog_changed_internal (win->window, TRUE);
-}
-
-void
-gnc_options_dialog_list_select_cb (GtkTreeSelection *selection,
-                                   gpointer data)
-{
-    GNCOptionWin * win = data;
-    GtkTreeModel *list;
-    GtkTreeIter iter;
-    gint index = 0;
-
-    if (!gtk_tree_selection_get_selected (selection, &list, &iter))
-        return;
-    gtk_tree_model_get (list, &iter,
-                        PAGE_INDEX, &index,
-                        -1);
-    PINFO("Index is %d", index);
-    gtk_notebook_set_current_page (GTK_NOTEBOOK(win->notebook), index);
-}
-
-void
-gnc_options_register_stocks (void)
-{
-#if 0
-    static gboolean done = FALSE;
-
-    GtkStockItem items[] =
-    {
-        { GTK_STOCK_APPLY       , "gnc_option_apply_button",    0, 0, NULL },
-        { GTK_STOCK_HELP        , "gnc_options_dialog_help",    0, 0, NULL },
-        { GTK_STOCK_OK          , "gnc_options_dialog_ok",      0, 0, NULL },
-        { GTK_STOCK_CANCEL      , "gnc_options_dialog_cancel",  0, 0, NULL },
-    };
-
-    if (done)
-    {
-        return;
-    }
-    done = TRUE;
-
-    gtk_stock_add (items, G_N_ELEMENTS (items));
-#endif
-}
-
-static void
-component_close_handler (gpointer data)
-{
-    GNCOptionWin *win = data;
-    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
-    gnc_options_dialog_cancel_button_cb (NULL, win);
-}
-
-/* gnc_options_dialog_new:
- *
- *   - Opens the dialog-options glade file
- *   - Connects signals specified in the builder file
- *   - Sets the window's title
- *   - Initializes a new GtkNotebook, and adds it to the window
- *
- */
-GNCOptionWin *
-gnc_options_dialog_new (gchar *title, GtkWindow *parent)
-{
-    return gnc_options_dialog_new_modal (FALSE, title, NULL, parent);
-}
-
-/* gnc_options_dialog_new_modal:
- *
- *   - Opens the dialog-options glade file
- *   - Connects signals specified in the builder file
- *   - Sets the window's title
- *   - Initializes a new GtkNotebook, and adds it to the window
- *   - If modal TRUE, hides 'apply' button
- *   - If component_class is provided, it is used, otherwise,
- *     DIALOG_OPTIONS_CM_CLASS is used; this is used to distinguish the
- *     book-option dialog from report dialogs. The book-option dialog is a
- *     singleton, so if a dialog already exists it will be raised to the top of
- *     the window stack instead of creating a new dialog.
- */
-GNCOptionWin *
-gnc_options_dialog_new_modal (gboolean modal, gchar *title,
-                              const char *component_class,
-                              GtkWindow *parent)
-{
-    GNCOptionWin *retval;
-    GtkBuilder   *builder;
-    GtkWidget    *hbox;
-    gint component_id;
-    GtkWidget    *button;
-
-    retval = g_new0 (GNCOptionWin, 1);
-    builder = gtk_builder_new ();
-    gnc_builder_add_from_file (builder, "dialog-options.glade", "gnucash_options_window");
-    retval->window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window"));
-    retval->page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll"));
-
-    // Set the name for this dialog so it can be easily manipulated with css
-    gtk_widget_set_name (GTK_WIDGET(retval->window), "gnc-id-options");
-
-    /* Page List */
-    {
-        GtkTreeView *view;
-        GtkListStore *store;
-        GtkTreeSelection *selection;
-        GtkCellRenderer *renderer;
-        GtkTreeViewColumn *column;
-
-        retval->page_list_view = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_treeview"));
-
-        view = GTK_TREE_VIEW(retval->page_list_view);
-
-        store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING);
-        gtk_tree_view_set_model (view, GTK_TREE_MODEL(store));
-        g_object_unref (store);
-
-        renderer = gtk_cell_renderer_text_new ();
-        column = gtk_tree_view_column_new_with_attributes (_("Page"), renderer,
-                                                           "text", PAGE_NAME,
-                                                            NULL);
-        gtk_tree_view_append_column (view, column);
-
-        gtk_tree_view_column_set_alignment (column, 0.5);
-
-        selection = gtk_tree_view_get_selection (view);
-        gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
-        g_signal_connect (selection, "changed",
-                          G_CALLBACK (gnc_options_dialog_list_select_cb), retval);
-    }
-
-    button = GTK_WIDGET(gtk_builder_get_object (builder, "helpbutton"));
-        g_signal_connect (button, "clicked", G_CALLBACK(gnc_options_dialog_help_button_cb), retval);
-    button = GTK_WIDGET(gtk_builder_get_object (builder, "cancelbutton"));
-        g_signal_connect (button, "clicked", G_CALLBACK(gnc_options_dialog_cancel_button_cb), retval);
-    button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton"));
-        g_signal_connect (button, "clicked", G_CALLBACK(gnc_options_dialog_apply_button_cb), retval);
-    button = GTK_WIDGET(gtk_builder_get_object (builder, "okbutton"));
-        g_signal_connect (button, "clicked", G_CALLBACK(gnc_options_dialog_ok_button_cb), retval);
-
-    gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, retval);
-
-    if (parent) // when added to a page of the hierarchy assistant there will be no parent
-        gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(retval->window), parent);
-
-    if (title)
-        gtk_window_set_title (GTK_WINDOW(retval->window), title);
-
-    /* modal */
-    if (modal == TRUE)
-    {
-        GtkWidget *apply_button;
-
-        apply_button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton"));
-        gtk_widget_hide (apply_button);
-    }
-
-    /* glade doesn't support a notebook with zero pages */
-    hbox = GTK_WIDGET(gtk_builder_get_object (builder, "notebook_placeholder"));
-    retval->notebook = gtk_notebook_new ();
-
-    gtk_widget_set_vexpand (retval->notebook, TRUE);
-
-    gtk_widget_show (retval->notebook);
-    gtk_box_pack_start (GTK_BOX(hbox), retval->notebook, TRUE, TRUE, 5);
-
-    component_id = gnc_register_gui_component (retval->component_class,
-                                               NULL, component_close_handler,
-                                               retval);
-    gnc_gui_component_set_session (component_id, gnc_get_current_session());
-
-    g_signal_connect (retval->window, "destroy",
-                      G_CALLBACK(gnc_options_dialog_destroy_cb), retval);
-
-    g_signal_connect (retval->window, "key_press_event",
-                      G_CALLBACK(gnc_options_dialog_window_key_press_cb), retval);
-
-    g_object_unref (G_OBJECT(builder));
-
-    retval->destroyed = FALSE;
-    return retval;
-}
-
-/* Creates a new GNCOptionWin structure, but assumes you have your own
-   dialog widget you want to plugin */
-GNCOptionWin *
-gnc_options_dialog_new_w_dialog (gchar *title, GtkWidget *window)
-{
-    GNCOptionWin * retval;
-
-    retval = g_new0 (GNCOptionWin, 1);
-    retval->window = window;
-    return retval;
-}
-
-void
-gnc_options_dialog_set_apply_cb (GNCOptionWin * win, GNCOptionWinCallback cb,
-                                 gpointer data)
-{
-    win->apply_cb = cb;
-    win->apply_cb_data = data;
-}
-
-void
-gnc_options_dialog_set_help_cb (GNCOptionWin * win, GNCOptionWinCallback cb,
-                                gpointer data)
-{
-    win->help_cb = cb;
-    win->help_cb_data = data;
-}
-
-void
-gnc_options_dialog_set_close_cb (GNCOptionWin * win, GNCOptionWinCallback cb,
-                                 gpointer data)
-{
-    win->close_cb = cb;
-    win->close_cb_data = data;
-}
-
-void
-gnc_options_dialog_set_global_help_cb (GNCOptionWinCallback thunk,
-                                       gpointer cb_data)
-{
-    global_help_cb = thunk;
-    global_help_cb_data = cb_data;
-}
-
-/* This is for global program preferences. */
-void
-gnc_options_dialog_destroy (GNCOptionWin * win)
-{
-    if (!win) return;
-
-    gnc_unregister_gui_component_by_data (win->component_class, win);
-
-    win->destroyed = TRUE;
-    gtk_widget_destroy (win->window);
-
-    win->window = NULL;
-    win->notebook = NULL;
-    win->apply_cb = NULL;
-    win->help_cb = NULL;
-    win->component_class = NULL;
-
-    g_free (win);
-}
-
-/*****************************************************************/
-/* Option Registration                                           */
-
-/*************************
- *       SET WIDGET      *
- *************************
- *
- * gnc_option_set_ui_widget_<type>():
- *
- * You should create the widget representation for the option type,
- * and set the top-level container widget for your control in
- * *enclosing.  If you want to pack the widget into the page yourself,
- * then you may -- just set *packed to TRUE.  Otherwise, the widget
- * you return in *enclosing will be packed for you.  (*packed is
- * initialized to FALSE, so if you're not setting it to TRUE, you
- * don't have to touch it at all.)
- *
- * If you need to initialize the state of your control or to connect
- * any signals to you widgets, then you should do so in this function.
- * If you want to create a label for the widget you should use 'name'
- * for the label text.
- *
- * Somewhere in this function, you should also call
- * gnc_option_set_widget(option, value); where 'value' is the
- * GtkWidget you will actually store the value in.
- *
- * Also call gnc_option_set_ui_value(option, FALSE);
- *
- * You probably want to end with something like:
- *   gtk_widget_show_all(*enclosing);
- *
- * If you can detect state changes for your widget's value, you should also
- * gnc_option_changed_widget_cb() upon changes.
- *
- * The widget you return from this function should be the widget in
- * which you're storing the option value.
- */
-
-static void
-gnc_option_set_ui_label_alignment (GtkLabel *name_label)
-{
-    /* some option widgets have a large vertical foot print so align
-       the label name to the top and add a small top margin */
-    gtk_widget_set_valign (GTK_WIDGET(name_label), GTK_ALIGN_START);
-    gtk_widget_set_margin_top (GTK_WIDGET(name_label), 6);
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_boolean (GNCOption *option, GtkGrid *page_box,
-                                  GtkLabel *name_label, char *documentation,
-                                  /* Return values */
-                                  GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-    value = gtk_check_button_new_with_label (gtk_label_get_text (name_label));
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    g_signal_connect (G_OBJECT(value), "toggled",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_string (GNCOption *option, GtkGrid *page_box,
-                                 GtkLabel *name_label, char *documentation,
-                                 /* Return values */
-                                 GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-    value = gtk_entry_new ();
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    if (gtk_widget_get_direction (GTK_WIDGET(value)) == GTK_TEXT_DIR_RTL)
-        gtk_entry_set_alignment (GTK_ENTRY(value), 1.0);
-
-    g_signal_connect (G_OBJECT(value), "changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, TRUE, TRUE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_text (GNCOption *option, GtkGrid *page_box,
-                               GtkLabel *name_label, char *documentation,
-                               /* Return values */
-                               GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-    GtkWidget *frame;
-    GtkWidget *scroll;
-    GtkTextBuffer* text_buffer;
-
-    frame = gtk_frame_new(NULL);
-
-    gnc_option_set_ui_label_alignment (name_label);
-
-    scroll = gtk_scrolled_window_new (NULL, NULL);
-    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll),
-                                    GTK_POLICY_NEVER,
-                                    GTK_POLICY_AUTOMATIC);
-    gtk_container_set_border_width (GTK_CONTAINER(scroll), 2);
-
-    gtk_container_add (GTK_CONTAINER(frame), scroll);
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
-    gtk_widget_set_vexpand (GTK_WIDGET(*enclosing), TRUE);
-    gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE);
-    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
-    value = gtk_text_view_new ();
-    gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(value), GTK_WRAP_WORD);
-    gtk_text_view_set_editable (GTK_TEXT_VIEW(value), TRUE);
-    gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW(value), FALSE);
-    gtk_container_add (GTK_CONTAINER(scroll), value);
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(value));
-    g_signal_connect (G_OBJECT(text_buffer), "changed",
-                      G_CALLBACK(gnc_option_changed_option_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), frame, TRUE, TRUE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_currency (GNCOption *option, GtkGrid *page_box,
-                                   GtkLabel *name_label, char *documentation,
-                                   /* Return values */
-                                   GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-    value = gnc_currency_edit_new ();
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    g_signal_connect (G_OBJECT(value), "changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_commodity (GNCOption *option, GtkGrid *page_box,
-                                    GtkLabel *name_label, char *documentation,
-                                    /* Return values */
-                                    GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-    value = gnc_general_select_new (GNC_GENERAL_SELECT_TYPE_SELECT,
-                                    gnc_commodity_edit_get_string,
-                                    gnc_commodity_edit_new_select,
-                                    NULL);
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    if (documentation != NULL)
-        gtk_widget_set_tooltip_text (GNC_GENERAL_SELECT(value)->entry,
-                                     documentation);
-
-    g_signal_connect (G_OBJECT(GNC_GENERAL_SELECT(value)->entry), "changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_multichoice (GNCOption *option, GtkGrid *page_box,
-                                      GtkLabel *name_label, char *documentation,
-                                      /* Return values */
-                                      GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-
-    value = gnc_option_create_multichoice_widget (option);
-    gnc_option_set_widget (option, value);
-
-    gnc_option_set_ui_value (option, FALSE);
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_date (GNCOption *option, GtkGrid *page_box,
-                               GtkLabel *name_label, char *documentation,
-                               /* Return values */
-                               GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-    gchar *colon_name;
-    GtkWidget *eventbox;
-    gchar *type = gnc_option_date_option_get_subtype (option);
-
-
-    gint  grid_row = GPOINTER_TO_INT(g_object_get_data
-                                    (G_OBJECT(page_box), "options-grid-row"));
-
-    value = gnc_option_create_date_widget (option);
-
-    gnc_option_set_widget (option, value);
-
-    if (g_strcmp0 (type, "relative") == 0)
-    {
-        *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-        gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-
-        gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    }
-    else
-    {
-        *enclosing = gtk_frame_new (NULL);
-        g_object_set (G_OBJECT(value), "margin", 3, NULL);
-
-        gtk_container_add (GTK_CONTAINER(*enclosing), value);
-    }
-    g_free (type);
-
-    gtk_widget_set_halign (GTK_WIDGET(*enclosing), GTK_ALIGN_START);
-
-    /* Pack option widget into an extra eventbox because otherwise the
-       "documentation" tooltip is not displayed. */
-    eventbox = gtk_event_box_new ();
-    gtk_container_add (GTK_CONTAINER(eventbox), *enclosing);
-
-    gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1);
-    *packed = TRUE;
-
-    gtk_widget_set_tooltip_text (eventbox, documentation);
-
-    gnc_option_set_ui_value (option, FALSE);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_account_list (GNCOption *option, GtkGrid *page_box,
-                                       GtkLabel *name_label, char *documentation,
-                                       /* Return values */
-                                       GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-    GtkTreeSelection *selection;
-    gint  grid_row = GPOINTER_TO_INT(g_object_get_data
-                                    (G_OBJECT(page_box), "options-grid-row"));
-
-    gnc_option_set_ui_label_alignment (name_label);
-
-    *enclosing = gnc_option_create_account_widget (option, NULL);
-    gtk_widget_set_vexpand (GTK_WIDGET(*enclosing), TRUE);
-    gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE);
-    value = gnc_option_get_gtk_widget (option);
-
-    gtk_widget_set_tooltip_text (*enclosing, documentation);
-
-    gtk_grid_attach (GTK_GRID(page_box), *enclosing, 1, grid_row, 1, 1);
-    *packed = TRUE;
-
-    //gtk_widget_realize(value);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(value));
-    g_signal_connect (G_OBJECT(selection), "changed",
-                      G_CALLBACK(gnc_option_account_cb), option);
-
-    //  gtk_clist_set_row_height(GTK_CLIST(value), 0);
-    //  gtk_widget_set_size_request(value, -1, GTK_CLIST(value)->row_height * 10);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_account_sel (GNCOption *option, GtkGrid *page_box,
-                                      GtkLabel *name_label, char *documentation,
-                                      /* Return values */
-                                      GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value = gnc_account_sel_new ();
-    GList *acct_type_list = gnc_option_get_account_type_list (option);
-
-    gnc_account_sel_set_acct_filters (GNC_ACCOUNT_SEL(value), acct_type_list, NULL);
-
-    g_signal_connect (value, "account_sel_changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    gnc_option_set_widget (option, value);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_list (GNCOption *option, GtkGrid *page_box,
-                               GtkLabel *name_label, char *documentation,
-                               /* Return values */
-                               GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-    GtkWidget *eventbox;
-    gint       grid_row = GPOINTER_TO_INT(g_object_get_data
-                                         (G_OBJECT(page_box), "options-grid-row"));
-
-    *enclosing = gnc_option_create_list_widget (option, NULL);
-    value = gnc_option_get_gtk_widget (option);
-
-    gnc_option_set_ui_label_alignment (name_label);
-
-    /* Pack option widget into an extra eventbox because otherwise the
-       "documentation" tooltip is not displayed. */
-    eventbox = gtk_event_box_new ();
-    gtk_container_add (GTK_CONTAINER(eventbox), *enclosing);
-
-    gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1);
-    *packed = TRUE;
-
-    gtk_widget_set_tooltip_text (eventbox, documentation);
-
-    gnc_option_set_ui_value (option, FALSE);
-    gtk_widget_show (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_number_range (GNCOption *option, GtkGrid *page_box,
-                                       GtkLabel *name_label, char *documentation,
-                                       /* Return values */
-                                       GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-    GtkAdjustment *adj;
-    gdouble lower_bound = G_MINDOUBLE;
-    gdouble upper_bound = G_MAXDOUBLE;
-    gdouble step_size = 1.0;
-    int num_decimals = 0;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-
-    gnc_option_get_range_info (option, &lower_bound, &upper_bound,
-                               &num_decimals, &step_size);
-    adj = GTK_ADJUSTMENT(gtk_adjustment_new (lower_bound, lower_bound,
-                                             upper_bound, step_size,
-                                             step_size * 5.0,
-                                             0));
-    value = gtk_spin_button_new (adj, step_size, num_decimals);
-    gtk_spin_button_set_numeric (GTK_SPIN_BUTTON(value), TRUE);
-
-    {
-        gdouble biggest;
-        gint num_digits;
-
-        biggest = ABS(lower_bound);
-        biggest = MAX(biggest, ABS(upper_bound));
-
-        num_digits = 0;
-        while (biggest >= 1)
-        {
-            num_digits++;
-            biggest = biggest / 10;
-        }
-
-        if (num_digits == 0)
-            num_digits = 1;
-
-        num_digits += num_decimals;
-
-        gtk_entry_set_width_chars (GTK_ENTRY(value), num_digits);
-    }
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    g_signal_connect (G_OBJECT(value), "changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_color (GNCOption *option, GtkGrid *page_box,
-                                GtkLabel *name_label, char *documentation,
-                                /* Return values */
-                                GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-    gboolean use_alpha;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-
-    use_alpha = gnc_option_use_alpha (option);
-
-    value = gtk_color_button_new ();
-    gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER(value), use_alpha);
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    g_signal_connect (G_OBJECT(value), "color-set",
-                      G_CALLBACK(gnc_option_color_changed_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_font (GNCOption *option, GtkGrid *page_box,
-                               GtkLabel *name_label, char *documentation,
-                               /* Return values */
-                               GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-    value = gtk_font_button_new ();
-    g_object_set (G_OBJECT(value),
-                  "use-font", TRUE,
-                  "show-style", TRUE,
-                  "show-size", TRUE,
-                  (char *)NULL);
-
-    gnc_option_set_widget (option, value);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    g_signal_connect (G_OBJECT(value), "font-set",
-                      G_CALLBACK(gnc_option_font_changed_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_pixmap (GNCOption *option, GtkGrid *page_box,
-                                 GtkLabel *name_label, char *documentation,
-                                 /* Return values */
-                                 GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-    GtkWidget *button;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-
-    button = gtk_button_new_with_label (_("Clear"));
-    gtk_widget_set_tooltip_text (button, _("Clear any selected image file."));
-
-    value = gtk_file_chooser_button_new (_("Select image"),
-                                         GTK_FILE_CHOOSER_ACTION_OPEN);
-    gtk_widget_set_tooltip_text (value, _("Select an image file."));
-    g_object_set (G_OBJECT(value),
-                  "width-chars", 30,
-                  "preview-widget", gtk_image_new(),
-                  (char *)NULL);
-    g_signal_connect (G_OBJECT(value), "selection-changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-    g_signal_connect (G_OBJECT(value), "selection-changed",
-                      G_CALLBACK(gnc_image_option_selection_changed_cb), option);
-    g_signal_connect (G_OBJECT(value), "update-preview",
-                      G_CALLBACK(gnc_image_option_update_preview_cb), option);
-    g_signal_connect_swapped (G_OBJECT(button), "clicked",
-                              G_CALLBACK(gtk_file_chooser_unselect_all), value);
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_box_pack_start (GTK_BOX(*enclosing), button, FALSE, FALSE, 0);
-
-    gtk_widget_show (value);
-    gtk_widget_show (*enclosing);
-
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_radiobutton (GNCOption *option, GtkGrid *page_box,
-                                      GtkLabel *name_label, char *documentation,
-                                      /* Return values */
-                                      GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-
-    gnc_option_set_ui_label_alignment (name_label);
-
-    value = gnc_option_create_radiobutton_widget (NULL, option);
-    gnc_option_set_widget (option, value);
-
-    gnc_option_set_ui_value (option, FALSE);
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_dateformat (GNCOption *option, GtkGrid *page_box,
-                                     GtkLabel *name_label, char *documentation,
-                                     /* Return values */
-                                     GtkWidget **enclosing, gboolean *packed)
-{
-    *enclosing = gnc_date_format_new_without_label ();
-    gnc_option_set_widget (option, *enclosing);
-
-    gnc_option_set_ui_label_alignment (name_label);
-
-    gnc_option_set_ui_value (option, FALSE);
-    g_signal_connect (G_OBJECT(*enclosing), "format_changed",
-                      G_CALLBACK(gnc_option_changed_option_cb), option);
-    gtk_widget_show_all (*enclosing);
-    return *enclosing;
-}
-
-static void
-gnc_plot_size_option_set_select_method (GNCOption *option, gboolean set_buttons)
-{
-    GList* widget_list;
-    GtkWidget *px_widget, *p_widget;
-    GtkWidget *widget;
-
-    widget = gnc_option_get_gtk_widget (option);
-
-    widget_list = gtk_container_get_children (GTK_CONTAINER(widget));
-    // px_button item 0
-    px_widget = g_list_nth_data (widget_list, 1);
-    // p_button item 2
-    p_widget = g_list_nth_data (widget_list, 3);
-    g_list_free (widget_list);
-
-    if (set_buttons)
-    {
-        gtk_widget_set_sensitive (px_widget, TRUE);
-        gtk_widget_set_sensitive (p_widget, FALSE);
-    }
-    else
-    {
-        gtk_widget_set_sensitive (p_widget, TRUE);
-        gtk_widget_set_sensitive (px_widget, FALSE);
-    }
-}
-
-static void
-gnc_rd_option_px_set_cb (GtkWidget *widget, gpointer *raw_option)
-{
-    GNCOption *option = (GNCOption *) raw_option;
-    gnc_plot_size_option_set_select_method (option, TRUE);
-    gnc_option_changed_option_cb (widget, option);
-}
-
-static void
-gnc_rd_option_p_set_cb (GtkWidget *widget, gpointer *raw_option)
-{
-    GNCOption *option = (GNCOption *) raw_option;
-    gnc_plot_size_option_set_select_method (option, FALSE);
-    gnc_option_changed_option_cb (widget, option);
-    return;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_plot_size (GNCOption *option, GtkGrid *page_box,
-                                     GtkLabel *name_label, char *documentation,
-                                     /* Return values */
-                                     GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value_px, *value_percent;
-    GtkWidget *px_butt, *p_butt;
-    GtkWidget *hbox;
-    GtkAdjustment *adj_px, *adj_percent;
-    gdouble lower_bound = G_MINDOUBLE;
-    gdouble upper_bound = G_MAXDOUBLE;
-    gdouble step_size = 1.0;
-    int num_decimals = 0;
-
-    *enclosing = gtk_frame_new (NULL);
-    gtk_widget_set_halign (GTK_WIDGET(*enclosing), GTK_ALIGN_START);
-
-    hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(hbox), FALSE);
-    g_object_set (G_OBJECT(hbox), "margin", 3, NULL);
-
-    gtk_container_add (GTK_CONTAINER(*enclosing), hbox);
-
-    gnc_option_get_range_info (option, &lower_bound, &upper_bound,
-                               &num_decimals, &step_size);
-    adj_px = GTK_ADJUSTMENT(gtk_adjustment_new (lower_bound, lower_bound,
-                                                upper_bound, step_size,
-                                                step_size * 5.0,
-                                                0));
-
-    value_px = gtk_spin_button_new (adj_px, step_size, num_decimals);
-    gtk_spin_button_set_numeric (GTK_SPIN_BUTTON(value_px), TRUE);
-
-    {
-        gdouble biggest;
-        gint num_digits;
-
-        biggest = ABS(lower_bound);
-        biggest = MAX(biggest, ABS(upper_bound));
-
-        num_digits = 0;
-        while (biggest >= 1)
-        {
-            num_digits++;
-            biggest = biggest / 10;
-        }
-
-        if (num_digits == 0)
-            num_digits = 1;
-
-        num_digits += num_decimals;
-
-        gtk_entry_set_width_chars (GTK_ENTRY(value_px), num_digits);
-    }
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(value_px), (upper_bound / 2)); //default
-    g_signal_connect (G_OBJECT(value_px), "changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    adj_percent = GTK_ADJUSTMENT(gtk_adjustment_new (1, 10, 100, 1, 5.0, 0));
-    value_percent = gtk_spin_button_new (adj_percent, 1, 0);
-    gtk_spin_button_set_numeric (GTK_SPIN_BUTTON(value_percent), TRUE);
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON(value_percent), 100); //default
-    gtk_entry_set_width_chars (GTK_ENTRY(value_percent), 3);
-    gtk_widget_set_sensitive (value_percent, FALSE);
-
-    g_signal_connect (G_OBJECT(value_percent), "changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    px_butt = gtk_radio_button_new_with_label (NULL, _("Pixels"));
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(px_butt), TRUE);
-
-    g_signal_connect (G_OBJECT(px_butt), "toggled",
-                      G_CALLBACK(gnc_rd_option_px_set_cb), option);
-
-    p_butt = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(px_butt), _("Percent"));
-    g_signal_connect (G_OBJECT(p_butt), "toggled",
-                      G_CALLBACK(gnc_rd_option_p_set_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(hbox), px_butt, FALSE, FALSE, 0);
-    gtk_box_pack_start (GTK_BOX(hbox), value_px, FALSE, FALSE, 0);
-    gtk_box_pack_start (GTK_BOX(hbox), p_butt, FALSE, FALSE, 0);
-    gtk_box_pack_start (GTK_BOX(hbox), value_percent, FALSE, FALSE, 0);
-
-    gnc_option_set_widget (option, hbox);
-    gnc_option_set_ui_value (option, FALSE);
-
-    gtk_widget_show_all (*enclosing);
-    return hbox;
-}
-
-static GtkWidget *
-gnc_option_set_ui_widget_budget (GNCOption *option, GtkGrid *page_box,
-                                 GtkLabel *name_label, char *documentation,
-                                 /* Return values */
-                                 GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-
-    value = gnc_option_create_budget_widget (option);
-
-    gnc_option_set_widget (option, value);
-    gnc_option_set_ui_value (option, FALSE);
-
-    /* Maybe connect destroy handler for tree model here? */
-    g_signal_connect (G_OBJECT(value), "changed",
-                      G_CALLBACK(gnc_option_changed_widget_cb), option);
-
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-/*************************
- *       SET VALUE       *
- *************************
- *
- * gnc_option_set_ui_value_<type>():
- *
- *   In this function you should set the state of the gui widget to
- * correspond to the value provided in 'value'.  You should return
- * TRUE if there was an error, FALSE otherwise.
- *
- *
- */
-static gboolean
-gnc_option_set_ui_value_boolean (GNCOption *option, gboolean use_default,
-                                 GtkWidget *widget, SCM value)
-{
-    if (scm_is_bool (value))
-    {
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget),
-                                      scm_is_true(value));
-        return FALSE;
-    }
-    else
-        return TRUE;
-}
-
-static gboolean
-gnc_option_set_ui_value_string (GNCOption *option, gboolean use_default,
-                                GtkWidget *widget, SCM value)
-{
-    if (scm_is_string (value))
-    {
-        const gchar *string;
-
-        string = gnc_scm_to_utf8_string (value);
-        gtk_entry_set_text (GTK_ENTRY(widget), string);
-        g_free ((gpointer *) string);
-        return FALSE;
-    }
-    else
-        return TRUE;
-}
-
-static gboolean
-gnc_option_set_ui_value_text (GNCOption *option, gboolean use_default,
-                              GObject *object, SCM value)
-{
-    GtkTextBuffer *buffer;
-
-    if (GTK_IS_TEXT_BUFFER(object))
-        buffer = GTK_TEXT_BUFFER(object);
-    else
-        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(object));
-
-    if (scm_is_string (value))
-    {
-        const gchar *string;
-
-        string = gnc_scm_to_utf8_string (value);
-        gtk_text_buffer_set_text (buffer, string, -1);
-        free ((void*) string);
-        return FALSE;
-    }
-    else
-        return TRUE;
-}
-
-static gboolean
-gnc_option_set_ui_value_currency (GNCOption *option, gboolean use_default,
-                                  GtkWidget *widget, SCM value)
-{
-    gnc_commodity *commodity;
-
-    commodity = gnc_scm_to_commodity (value);
-    if (commodity)
-    {
-        gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(widget), commodity);
-        return FALSE;
-    }
-    else
-        return TRUE;
-}
-
-static gboolean
-gnc_option_set_ui_value_commodity (GNCOption *option, gboolean use_default,
-                                   GtkWidget *widget, SCM value)
-{
-    gnc_commodity *commodity;
-
-    commodity = gnc_scm_to_commodity (value);
-    if (commodity)
-    {
-        gnc_general_select_set_selected (GNC_GENERAL_SELECT(widget), commodity);
-        return FALSE;
-    }
-    else
-        return TRUE;
-}
-
-static gboolean
-gnc_option_set_ui_value_multichoice (GNCOption *option, gboolean use_default,
-                                     GtkWidget *widget, SCM value)
-{
-    int index;
-
-    index = gnc_option_permissible_value_index (option, value);
-    if (index < 0)
-        return TRUE;
-    else
-    {
-        gtk_combo_box_set_active (GTK_COMBO_BOX(widget), index);
-        return FALSE;
-    }
-}
-
-static gboolean
-gnc_option_set_ui_value_date (GNCOption *option, gboolean use_default,
-                              GtkWidget *widget, SCM value)
-{
-    int index;
-    char *date_option_type;
-    char *symbol_str;
-    gboolean bad_value = FALSE;
-
-    date_option_type = gnc_option_date_option_get_subtype (option);
-
-    if (scm_is_pair (value))
-    {
-        symbol_str = gnc_date_option_value_get_type (value);
-        if (symbol_str)
-        {
-            if (g_strcmp0 (symbol_str, "relative") == 0)
-            {
-                SCM relative = gnc_date_option_value_get_relative (value);
-
-                index = gnc_option_permissible_value_index (option, relative);
-                if (g_strcmp0 (date_option_type, "relative") == 0)
-                {
-                    gtk_combo_box_set_active (GTK_COMBO_BOX(widget), index);
-                }
-                else if (g_strcmp0 (date_option_type, "both") == 0)
-                {
-                    GList *widget_list;
-                    GtkWidget *rel_date_widget;
-
-                    widget_list = gtk_container_get_children (GTK_CONTAINER(widget));
-                    rel_date_widget = g_list_nth_data (widget_list,
-                                                       GNC_RD_WID_REL_WIDGET_POS);
-                    g_list_free (widget_list);
-                    gnc_date_option_set_select_method (option, FALSE, TRUE);
-                    gtk_combo_box_set_active (GTK_COMBO_BOX(rel_date_widget), index);
-                }
-                else
-                    bad_value = TRUE;
-            }
-            else if (g_strcmp0 (symbol_str, "absolute") == 0)
-            {
-                time64 time;
-
-                time = gnc_date_option_value_get_absolute (value);
-
-                if (g_strcmp0 (date_option_type, "absolute") == 0)
-                {
-                    gnc_date_edit_set_time (GNC_DATE_EDIT(widget), time);
-                }
-                else if (g_strcmp0 (date_option_type, "both") == 0)
-                {
-                    GList *widget_list;
-                    GtkWidget *ab_widget;
-
-                    widget_list = gtk_container_get_children (GTK_CONTAINER(widget));
-                    ab_widget = g_list_nth_data (widget_list,
-                                                 GNC_RD_WID_AB_WIDGET_POS);
-                    g_list_free (widget_list);
-                    gnc_date_option_set_select_method (option, TRUE, TRUE);
-                    gnc_date_edit_set_time (GNC_DATE_EDIT(ab_widget), time);
-                }
-                else
-                    bad_value = TRUE;
-            }
-            else
-                bad_value = TRUE;
-
-            if (symbol_str)
-                free (symbol_str);
-        }
-    }
-    else
-        bad_value = TRUE;
-
-    if (date_option_type)
-        free (date_option_type);
-
-    return bad_value;
-}
-
-static gboolean
-gnc_option_set_ui_value_account_list (GNCOption *option, gboolean use_default,
-                                      GtkWidget *widget, SCM value)
-{
-    GList *list;
-
-    list = gnc_scm_list_to_glist (value);
-
-    gnc_tree_view_account_set_selected_accounts (GNC_TREE_VIEW_ACCOUNT(widget),
-                                                 list, TRUE);
-    g_list_free (list);
-    return FALSE;
-}
-
-static gboolean
-gnc_option_set_ui_value_account_sel (GNCOption *option, gboolean use_default,
-                                     GtkWidget *widget, SCM value)
-{
-    Account *acc = NULL;
-
-    if (value != SCM_BOOL_F)
-    {
-        if (!SWIG_IsPointer (value))
-            scm_misc_error ("gnc_option_set_ui_value_account_sel",
-                            "Option Value not a wcp.", value);
-
-        acc = SWIG_MustGetPtr (value, SWIG_TypeQuery ("_p_Account"), 4, 0);
-    }
-
-    //doesn't default because this function is called to set a specific account
-    gnc_account_sel_set_account (GNC_ACCOUNT_SEL(widget), acc, FALSE);
-
-    return FALSE;
-}
-
-static gboolean
-gnc_option_set_ui_value_list (GNCOption *option, gboolean use_default,
-                              GtkWidget *widget, SCM value)
-{
-    GtkTreeSelection *selection;
-    GtkTreePath *path;
-    gint row;
-
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(widget));
-    gtk_tree_selection_unselect_all (selection);
-
-    while (scm_is_list (value) && !scm_is_null (value))
-    {
-        SCM item;
-
-        item = SCM_CAR(value);
-        value = SCM_CDR(value);
-
-        row = gnc_option_permissible_value_index (option, item);
-        if (row < 0)
-        {
-            return TRUE;
-        }
-
-        path = gtk_tree_path_new_from_indices (row, -1);
-        gtk_tree_selection_select_path (selection, path);
-        gtk_tree_path_free (path);
-    }
-
-    if (!scm_is_list (value) || !scm_is_null (value))
-        return TRUE;
-
-    return FALSE;
-}
-
-static gboolean
-gnc_option_set_ui_value_number_range (GNCOption *option, gboolean use_default,
-                                      GtkWidget *widget, SCM value)
-{
-    GtkSpinButton *spinner;
-    gdouble d_value;
-
-    spinner = GTK_SPIN_BUTTON(widget);
-
-    if (scm_is_number (value))
-    {
-        d_value = scm_to_double (value);
-        gtk_spin_button_set_value (spinner, d_value);
-        return FALSE;
-    }
-    else
-        return TRUE;
-}
-
-static gboolean
-gnc_option_set_ui_value_color (GNCOption *option, gboolean use_default,
-                               GtkWidget *widget, SCM value)
-{
-
-    GdkRGBA color;
-    if (gnc_option_get_color_info (option, use_default,
-                                   &color.red, &color.green,
-                                   &color.blue, &color.alpha))
-    {
-        GtkColorChooser *color_button;
-
-        DEBUG("red %f, green %f, blue %f, alpha %f",
-              color.red, color.green, color.blue, color.alpha);
-        color_button = GTK_COLOR_CHOOSER(widget);
-
-        gtk_color_chooser_set_rgba (color_button, &color);
-        return FALSE;
-    }
-
-    LEAVE("TRUE");
-    return TRUE;
-}
-
-static gboolean
-gnc_option_set_ui_value_font (GNCOption *option, gboolean use_default,
-                              GtkWidget *widget, SCM value)
-{
-    if (scm_is_string (value))
-    {
-        const gchar *string;
-
-        string = gnc_scm_to_utf8_string (value);
-        if ((string != NULL) && (*string != '\0'))
-        {
-            GtkFontButton *font_button = GTK_FONT_BUTTON(widget);
-            gtk_font_button_set_font_name (font_button, string);
-        }
-        g_free ((gpointer *) string);
-        return FALSE;
-    }
-    else
-        return TRUE;
-}
-
-static gboolean
-gnc_option_set_ui_value_pixmap (GNCOption *option, gboolean use_default,
-                                GtkWidget *widget, SCM value)
-{
-    ENTER("option %p(%s)", option, gnc_option_name (option));
-    if (scm_is_string (value))
-    {
-        const gchar *string;
-
-        string = gnc_scm_to_locale_string (value);
-        if (string && *string)
-        {
-            gchar *test;
-            DEBUG("string = %s", string);
-            gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(widget), string);
-            test = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(widget));
-            g_object_set_data_full (G_OBJECT(widget), LAST_SELECTION,
-                                    g_strdup (string), g_free);
-            DEBUG("Set %s, retrieved %s", string, test ? test : "(null)");
-            gnc_image_option_update_preview_cb (GTK_FILE_CHOOSER(widget), option);
-        }
-        LEAVE("FALSE");
-        g_free ((gpointer *) string);
-        return FALSE;
-    }
-
-    LEAVE("TRUE");
-    return TRUE;
-}
-
-static gboolean gnc_option_set_ui_value_budget (GNCOption *option,
-                                                gboolean use_default,
-                                                GtkWidget *widget,
-                                                SCM value)
-{
-    GncBudget *bgt;
-
-//    if (!scm_is_null(value)) {
-    if (value != SCM_BOOL_F)
-    {
-        if (!SWIG_IsPointer (value))
-            scm_misc_error ("gnc_option_set_ui_value_budget",
-                            "Option Value not a wcp.", value);
-
-        bgt = SWIG_MustGetPtr (value, SWIG_TypeQuery ("GncBudget *"), 4, 0);
-        if (bgt)
-        {
-            GtkComboBox *cb = GTK_COMBO_BOX(widget);
-            GtkTreeModel *tm = gtk_combo_box_get_model (cb);
-            GtkTreeIter iter;
-            if (gnc_tree_model_budget_get_iter_for_budget (tm, &iter, bgt))
-                gtk_combo_box_set_active_iter (cb, &iter);
-        }
-    }
-    //FIXME: Unimplemented.
-    return FALSE;
-}
-
-static gboolean
-gnc_option_set_ui_value_radiobutton (GNCOption *option, gboolean use_default,
-                                     GtkWidget *widget, SCM value)
-{
-    int index;
-
-    index = gnc_option_permissible_value_index (option, value);
-    if (index < 0)
-        return TRUE;
-    else
-    {
-        GtkWidget *box, *button;
-        GList *list;
-        int i;
-        gpointer val;
-
-        list = gtk_container_get_children (GTK_CONTAINER(widget));
-        box = list->data;
-        g_list_free(list);
-
-        list = gtk_container_get_children (GTK_CONTAINER(box));
-        for (i = 0; i < index && list; i++)
-            list = list->next;
-        g_return_val_if_fail (list, TRUE);
-
-        button = list->data;
-        g_list_free (list);
-        val = g_object_get_data (G_OBJECT(button), "gnc_radiobutton_index");
-        g_return_val_if_fail (GPOINTER_TO_INT(val) == index, TRUE);
-
-        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
-        //    g_object_set_data(G_OBJECT(widget), "gnc_radiobutton_index",
-        //          GINT_TO_POINTER(index));
-        return FALSE;
-    }
-}
-
-static gboolean
-gnc_option_set_ui_value_dateformat (GNCOption *option, gboolean use_default,
-                                    GtkWidget *widget, SCM value)
-{
-    GNCDateFormat * gdf = GNC_DATE_FORMAT(widget);
-    QofDateFormat format;
-    GNCDateMonthFormat months;
-    gboolean years;
-    char *custom;
-
-    if (gnc_dateformat_option_value_parse (value, &format, &months, &years, &custom))
-        return TRUE;
-
-    gnc_date_format_set_format (gdf, format);
-    gnc_date_format_set_months (gdf, months);
-    gnc_date_format_set_years (gdf, years);
-    gnc_date_format_set_custom (gdf, custom);
-    gnc_date_format_refresh (gdf);
-
-    if (custom)
-        free (custom);
-
-    return FALSE;
-}
-
-static gboolean
-gnc_option_set_ui_value_plot_size (GNCOption *option, gboolean use_default,
-                                   GtkWidget *widget, SCM value)
-{
-    GList* widget_list;
-    GtkWidget *px_button, *p_button, *px_widget, *p_widget;
-    char *symbol_str;
-    gdouble d_value;
-
-    widget_list = gtk_container_get_children (GTK_CONTAINER(widget));
-    px_button = g_list_nth_data (widget_list, 0);
-    px_widget = g_list_nth_data (widget_list, 1);
-    p_button = g_list_nth_data (widget_list, 2);
-    p_widget = g_list_nth_data (widget_list, 3);
-    g_list_free (widget_list);
-
-    if (scm_is_pair (value))
-    {
-        symbol_str = gnc_plot_size_option_value_get_type (value);
-        d_value = gnc_plot_size_option_value_get_value (value);
-
-        if (symbol_str)
-        {
-            if (g_strcmp0 (symbol_str, "pixels") == 0) // pixel values
-            {
-                gtk_spin_button_set_value (GTK_SPIN_BUTTON(px_widget), d_value);
-                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(px_button), TRUE);
-            }
-            else // percent values
-            {
-                gtk_spin_button_set_value (GTK_SPIN_BUTTON(p_widget), (d_value));
-                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(p_button), TRUE);
-            }
-            return FALSE;
-        }
-    }
-    return TRUE;
-}
-
-/*************************
- *       GET VALUE       *
- *************************
- *
- * gnc_option_get_ui_value_<type>():
- *
- * 'widget' will be the widget returned from the
- * gnc_option_set_ui_widget_<type>() function.
- *
- * You should return a SCM value corresponding to the current state of the
- * gui widget.
- *
- */
-static SCM
-gnc_option_get_ui_value_boolean (GNCOption *option, GtkWidget *widget)
-{
-    gboolean active;
-
-    active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget));
-    return SCM_BOOL(active);
-}
-
-static SCM
-gnc_option_get_ui_value_string (GNCOption *option, GtkWidget *widget)
-{
-    char * string;
-    SCM result;
-
-    string = gtk_editable_get_chars (GTK_EDITABLE(widget), 0, -1);
-    result = scm_from_utf8_string (string ? string : "");
-    g_free (string);
-    return result;
-}
-
-static SCM
-gnc_option_get_ui_value_text (GNCOption *option, GtkWidget *widget)
-{
-    char * string;
-    SCM result;
-
-    string = xxxgtk_textview_get_text (GTK_TEXT_VIEW(widget));
-    result = scm_from_utf8_string (string ? string : "");
-    g_free (string);
-    return result;
-}
-
-static SCM
-gnc_option_get_ui_value_currency (GNCOption *option, GtkWidget *widget)
-{
-    gnc_commodity *commodity;
-
-    commodity =
-        gnc_currency_edit_get_currency (GNC_CURRENCY_EDIT(widget));
-
-    return (gnc_commodity_to_scm (commodity));
-}
-
-static SCM
-gnc_option_get_ui_value_commodity (GNCOption *option, GtkWidget *widget)
-{
-    gnc_commodity *commodity;
-
-    commodity =
-        gnc_general_select_get_selected (GNC_GENERAL_SELECT(widget));
-
-    return (gnc_commodity_to_scm (commodity));
-}
-
-static SCM
-gnc_option_get_ui_value_multichoice (GNCOption *option, GtkWidget *widget)
-{
-    int index = gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-    return (gnc_option_permissible_value (option, index));
-}
-
-static SCM
-gnc_option_get_ui_value_date (GNCOption *option, GtkWidget *widget)
-{
-    int index;
-    SCM type, val, result = SCM_UNDEFINED;
-    char *subtype = gnc_option_date_option_get_subtype (option);
-
-    if (g_strcmp0 (subtype, "relative") == 0)
-    {
-        index = gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
-
-        type = scm_from_locale_symbol ("relative");
-        val = gnc_option_permissible_value (option, index);
-        result = scm_cons (type, val);
-    }
-    else if (g_strcmp0 (subtype, "absolute") == 0)
-    {
-        time64 time;
-        time = gnc_date_edit_get_date (GNC_DATE_EDIT(widget));
-        result = scm_cons (scm_from_locale_symbol ("absolute"), scm_from_int64 (time));
-    }
-    else if (g_strcmp0 (subtype, "both") == 0)
-    {
-        time64 time;
-        int index;
-        SCM val;
-        GList *widget_list;
-        GtkWidget *ab_button, *rel_widget, *ab_widget;
-
-        widget_list = gtk_container_get_children (GTK_CONTAINER(widget));
-        ab_button = g_list_nth_data (widget_list,  GNC_RD_WID_AB_BUTTON_POS);
-        ab_widget = g_list_nth_data (widget_list,  GNC_RD_WID_AB_WIDGET_POS);
-        rel_widget = g_list_nth_data (widget_list, GNC_RD_WID_REL_WIDGET_POS);
-        g_list_free (widget_list);
-
-        /* if it's an absolute date */
-        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(ab_button)))
-        {
-            time = gnc_date_edit_get_date (GNC_DATE_EDIT(ab_widget));
-            result = scm_cons (scm_from_locale_symbol ("absolute"), scm_from_int64 (time));
-        }
-        else
-        {
-            index = gtk_combo_box_get_active (GTK_COMBO_BOX(rel_widget));
-
-            val = gnc_option_permissible_value (option, index);
-            result = scm_cons (scm_from_locale_symbol ("relative"), val);
-        }
-    }
-    g_free (subtype);
-    return result;
-}
-
-static SCM
-gnc_option_get_ui_value_account_list (GNCOption *option, GtkWidget *widget)
-{
-    GncTreeViewAccount *tree;
-    GList *list;
-    SCM result;
-
-    tree = GNC_TREE_VIEW_ACCOUNT(widget);
-    list = gnc_tree_view_account_get_selected_accounts (tree);
-
-    /* handover list */
-    result = gnc_glist_to_scm_list (list, "_p_Account");
-    g_list_free (list);
-    return result;
-}
-
-static SCM
-gnc_option_get_ui_value_account_sel (GNCOption *option, GtkWidget *widget)
-{
-    GNCAccountSel *gas;
-    Account* acc;
-
-    gas = GNC_ACCOUNT_SEL(widget);
-    acc = gnc_account_sel_get_account (gas);
-
-    if (!acc)
-        return SCM_BOOL_F;
-
-    return SWIG_NewPointerObj (acc, SWIG_TypeQuery ("_p_Account"), 0);
-}
-
-static SCM
-gnc_option_get_ui_value_budget (GNCOption *option, GtkWidget *widget)
-{
-    GncBudget *bgt;
-    GtkComboBox *cb;
-    GtkTreeModel *tm;
-    GtkTreeIter iter;
-
-    cb = GTK_COMBO_BOX(widget);
-    gtk_combo_box_get_active_iter (cb, &iter);
-    tm = gtk_combo_box_get_model (cb);
-    bgt = gnc_tree_model_budget_get_budget (tm, &iter);
-
-    if (!bgt)
-        return SCM_BOOL_F;
-
-    return SWIG_NewPointerObj (bgt, SWIG_TypeQuery ("_p_budget_s"), 0);
-}
-
-static SCM
-gnc_option_get_ui_value_list (GNCOption *option, GtkWidget *widget)
-{
-    GtkTreeSelection *selection;
-    GtkTreePath *path;
-    SCM result;
-    gboolean selected;
-    gint num_rows;
-    gint row;
-
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(widget));
-    num_rows = gnc_option_num_permissible_values (option);
-    result = scm_c_eval_string ("'()");
-
-    for (row = 0; row < num_rows; row++)
-    {
-        path = gtk_tree_path_new_from_indices (row, -1);
-        selected = gtk_tree_selection_path_is_selected (selection, path);
-        gtk_tree_path_free (path);
-        if (selected)
-            result = scm_cons (gnc_option_permissible_value (option, row), result);
-    }
-    return (scm_reverse (result));
-}
-
-static SCM
-gnc_option_get_ui_value_number_range (GNCOption *option, GtkWidget *widget)
-{
-    GtkSpinButton *spinner;
-    gdouble value;
-
-    spinner = GTK_SPIN_BUTTON(widget);
-
-    value = gtk_spin_button_get_value (spinner);
-
-    return (scm_from_double (value));
-}
-
-static SCM
-gnc_option_get_ui_value_color (GNCOption *option, GtkWidget *widget)
-{
-    SCM result;
-    GtkColorChooser *color_button;
-    GdkRGBA color;
-    gdouble scale;
-
-    ENTER("option %p(%s), widget %p",
-          option, gnc_option_name (option), widget);
-
-    color_button = GTK_COLOR_CHOOSER(widget);
-    gtk_color_chooser_get_rgba (color_button, &color);
-
-    scale = gnc_option_color_range (option);
-
-    result = SCM_EOL;
-    result = scm_cons (scm_from_double (color.alpha * scale), result);
-    result = scm_cons (scm_from_double (color.blue * scale), result);
-    result = scm_cons (scm_from_double (color.green * scale), result);
-    result = scm_cons (scm_from_double (color.red * scale), result);
-    return result;
-}
-
-static SCM
-gnc_option_get_ui_value_font (GNCOption *option, GtkWidget *widget)
-{
-    GtkFontButton *font_button = GTK_FONT_BUTTON(widget);
-    const gchar * string;
-
-    string = gtk_font_button_get_font_name (font_button);
-    return (string ? scm_from_utf8_string (string) : SCM_BOOL_F);
-}
-
-static SCM
-gnc_option_get_ui_value_pixmap (GNCOption *option, GtkWidget *widget)
-{
-    gchar *string;
-    SCM result;
-
-    string = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(widget));
-    DEBUG("filename %s", string ? string : "(null)");
-    result = scm_from_utf8_string (string ? string : "");
-    g_free (string);
-    return result;
-}
-
-static SCM
-gnc_option_get_ui_value_radiobutton (GNCOption *option, GtkWidget *widget)
-{
-    gpointer _index;
-    int index;
-
-    _index = g_object_get_data (G_OBJECT(widget), "gnc_radiobutton_index");
-    index = GPOINTER_TO_INT(_index);
-
-    return (gnc_option_permissible_value (option, index));
-}
-
-static SCM
-gnc_option_get_ui_value_dateformat (GNCOption *option, GtkWidget *widget)
-{
-    GNCDateFormat *gdf = GNC_DATE_FORMAT(widget);
-    QofDateFormat format;
-    GNCDateMonthFormat months;
-    gboolean years;
-    const char* custom;
-
-    format = gnc_date_format_get_format (gdf);
-    months = gnc_date_format_get_months (gdf);
-    years = gnc_date_format_get_years (gdf);
-    custom = gnc_date_format_get_custom (gdf);
-
-    return (gnc_dateformat_option_set_value (format, months, years, custom));
-}
-
-static SCM
-gnc_option_get_ui_value_plot_size (GNCOption *option, GtkWidget *widget)
-{
-    GList* widget_list;
-    GtkWidget *px_button, *px_widget, *p_widget;
-    gdouble d_value;
-    SCM type, val;
-
-    widget_list = gtk_container_get_children (GTK_CONTAINER(widget));
-    px_button = g_list_nth_data (widget_list, 0);
-    px_widget = g_list_nth_data (widget_list, 1);
-    // p_button item 2
-    p_widget = g_list_nth_data (widget_list, 3);
-    g_list_free (widget_list);
-
-    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(px_button)))
-    {
-        type = scm_from_locale_symbol ("pixels");
-        d_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON(px_widget));
-    }
-    else
-    {
-        type = scm_from_locale_symbol ("percent");
-        d_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON(p_widget));
-    }
-    val = scm_from_double (d_value);
-    return scm_cons (type, val);
-}
-
-<<<<<<< HEAD
-static SCM
-gnc_option_get_ui_value_currency_accounting (GNCOption *option,
-                                             GtkWidget *widget)
-{
-    gpointer _index;
-    int index;
-    SCM value = SCM_EOL;
-
-    _index = g_object_get_data (G_OBJECT(widget), "gnc_radiobutton_index");
-    index = GPOINTER_TO_INT(_index);
-
-    /* build the return list in reverse order */
-    if (g_strcmp0 (gnc_option_permissible_value_name (option, index),
-                   "Use a Book Currency") == 0)
-    {
-        gnc_commodity *commodity = NULL;
-        int policy_index;
-        SCM val;
-        GList *list_of_policies = NULL;
-        const char *str = NULL;
-
-        if (book_currency_data->default_gain_loss_account_widget)
-        {
-            /* get account from widget, if one selected */
-            Account *gain_loss_account = NULL;
-
-            gain_loss_account =
-                gnc_tree_view_account_get_selected_account
-                    (GNC_TREE_VIEW_ACCOUNT(
-                        book_currency_data->default_gain_loss_account_widget));
-
-            if (gain_loss_account == NULL)
-            {
-                val = SCM_BOOL_F;
-            }
-            else
-            {
-                gchar *gain_loss_account_guid = guid_to_string (
-                                        xaccAccountGetGUID (gain_loss_account));
-
-                val = scm_from_utf8_string (gain_loss_account_guid);
-                g_free (gain_loss_account_guid);
-            }
-        }
-        else
-        {
-            val = SCM_BOOL_F;
-        }
-        value = scm_cons (val, value);
-
-        list_of_policies = gnc_get_valid_policy_list ();
-        if (list_of_policies && book_currency_data->default_cost_policy_widget)
-        {
-            GList *l = NULL;
-            gint i = 0;
-
-            policy_index =
-                gtk_combo_box_get_active (GTK_COMBO_BOX(
-                             book_currency_data->default_cost_policy_widget));
-            for (l = list_of_policies; l != NULL; l = l->next)
-            {
-                GNCPolicy *pcy = l->data;
-                if(i == policy_index)
-                    str = PolicyGetName (pcy);
-                i++;
-            }
-            g_list_free (list_of_policies);
-        }
-        if (str)
-        {
-            val = scm_from_locale_symbol (str);
-        }
-        else
-        {
-            val = SCM_BOOL_F;
-        }
-        value = scm_cons (val, value);
-
-        if (gtk_combo_box_get_active (GTK_COMBO_BOX(book_currency_data->book_currency_widget)) != -1)
-        {
-            commodity =
-                gnc_currency_edit_get_currency (
-                    GNC_CURRENCY_EDIT(
-                        book_currency_data->book_currency_widget));
-            if (commodity)
-            {
-                val = gnc_commodity_to_scm (commodity);
-            }
-            else
-            {
-                val = SCM_BOOL_F;
-            }
-        }
-        else
-        {
-            val = SCM_BOOL_F;
-        }
-        value = scm_cons (val, value);
-    }
-
-    return (scm_cons (gnc_option_permissible_value (option, index), value));
-}
-
-=======
->>>>>>> f87d57145 (Remove the incomplete book-currency code.)
-/************************************/
-/*          INITIALIZATION          */
-/************************************/
-static void gnc_options_initialize_options (void)
-{
-    static GNCOptionDef_t options[] =
-    {
-        {
-            "boolean", gnc_option_set_ui_widget_boolean,
-            gnc_option_set_ui_value_boolean, gnc_option_get_ui_value_boolean
-        },
-        {
-            "string", gnc_option_set_ui_widget_string,
-            gnc_option_set_ui_value_string, gnc_option_get_ui_value_string
-        },
-        {
-            "text", gnc_option_set_ui_widget_text,
-            (GNCOptionUISetValue)gnc_option_set_ui_value_text,
-            gnc_option_get_ui_value_text
-        },
-        {
-            "currency", gnc_option_set_ui_widget_currency,
-            gnc_option_set_ui_value_currency, gnc_option_get_ui_value_currency
-        },
-        {
-            "commodity", gnc_option_set_ui_widget_commodity,
-            gnc_option_set_ui_value_commodity, gnc_option_get_ui_value_commodity
-        },
-        {
-            "multichoice", gnc_option_set_ui_widget_multichoice,
-            gnc_option_set_ui_value_multichoice, gnc_option_get_ui_value_multichoice
-        },
-        {
-            "date", gnc_option_set_ui_widget_date,
-            gnc_option_set_ui_value_date, gnc_option_get_ui_value_date
-        },
-        {
-            "account-list", gnc_option_set_ui_widget_account_list,
-            gnc_option_set_ui_value_account_list, gnc_option_get_ui_value_account_list
-        },
-        {
-            "account-sel", gnc_option_set_ui_widget_account_sel,
-            gnc_option_set_ui_value_account_sel, gnc_option_get_ui_value_account_sel
-        },
-        {
-            "list", gnc_option_set_ui_widget_list,
-            gnc_option_set_ui_value_list, gnc_option_get_ui_value_list
-        },
-        {
-            "number-range", gnc_option_set_ui_widget_number_range,
-            gnc_option_set_ui_value_number_range, gnc_option_get_ui_value_number_range
-        },
-        {
-            "color", gnc_option_set_ui_widget_color,
-            gnc_option_set_ui_value_color, gnc_option_get_ui_value_color
-        },
-        {
-            "font", gnc_option_set_ui_widget_font,
-            gnc_option_set_ui_value_font, gnc_option_get_ui_value_font
-        },
-        {
-            "pixmap", gnc_option_set_ui_widget_pixmap,
-            gnc_option_set_ui_value_pixmap, gnc_option_get_ui_value_pixmap
-        },
-        {
-            "radiobutton", gnc_option_set_ui_widget_radiobutton,
-            gnc_option_set_ui_value_radiobutton, gnc_option_get_ui_value_radiobutton
-        },
-        {
-            "dateformat", gnc_option_set_ui_widget_dateformat,
-            gnc_option_set_ui_value_dateformat, gnc_option_get_ui_value_dateformat
-        },
-        {
-            "plot-size", gnc_option_set_ui_widget_plot_size,
-            gnc_option_set_ui_value_plot_size, gnc_option_get_ui_value_plot_size
-        },
-        {
-            "budget", gnc_option_set_ui_widget_budget,
-            gnc_option_set_ui_value_budget, gnc_option_get_ui_value_budget
-        },
-        { NULL, NULL, NULL, NULL }
-    };
-    int i;
-
-    for (i = 0; options[i].option_name; i++)
-        gnc_options_ui_register_option (&(options[i]));
-}
-
-/* Register a new option type in the UI */
-void gnc_options_ui_register_option (GNCOptionDef_t *option)
-{
-    g_return_if_fail (optionTable);
-    g_return_if_fail (option);
-
-    /* FIXME: should protect against repeat insertion. */
-    g_hash_table_insert (optionTable, (gpointer)(option->option_name), option);
-}
-
-GNCOptionDef_t * gnc_options_ui_get_option (const char *option_name)
-{
-    GNCOptionDef_t *retval;
-    g_return_val_if_fail (optionTable, NULL);
-    g_return_val_if_fail (option_name, NULL);
-
-    retval = g_hash_table_lookup (optionTable, option_name);
-    if (!retval)
-    {
-        PERR("Option lookup for type '%s' failed!", option_name);
-    }
-    return retval;
-}
-
-void gnc_options_ui_initialize (void)
-{
-    SWIG_GetModule (NULL); /* Work-around for SWIG bug. */
-    //  gnc_options_register_stocks ();
-    g_return_if_fail (optionTable == NULL);
-    optionTable = g_hash_table_new (g_str_hash, g_str_equal);
-
-    /* add known types */
-    gnc_options_initialize_options ();
-}
-
-struct scm_cb
-{
-    SCM apply_cb;
-    SCM close_cb;
-};
-
-static void
-scm_apply_cb (GNCOptionWin *win, gpointer data)
-{
-    struct scm_cb *cbdata = data;
-
-    if (gnc_option_db_get_changed (win->option_db))
-    {
-        GList *results = NULL, *iter;
-        results = gnc_option_db_commit (win->option_db);
-        for (iter = results; iter; iter = iter->next)
-        {
-            GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(gnc_options_dialog_widget (win)),
-                                                        0,
-                                                        GTK_MESSAGE_ERROR,
-                                                        GTK_BUTTONS_OK,
-                                                        "%s",
-                                                        (char*)iter->data);
-            gtk_dialog_run (GTK_DIALOG(dialog));
-            gtk_widget_destroy (dialog);
-            g_free (iter->data);
-        }
-        g_list_free (results);
-
-        if (cbdata->apply_cb != SCM_BOOL_F)
-        {
-            scm_call_0 (cbdata->apply_cb);
-        }
-    }
-}
-
-static void
-scm_close_cb (GNCOptionWin *win, gpointer data)
-{
-    struct scm_cb *cbdata = data;
-
-    if (cbdata->close_cb != SCM_BOOL_F)
-    {
-        scm_call_0 (cbdata->close_cb);
-        scm_gc_unprotect_object (cbdata->close_cb);
-    }
-
-    if (cbdata->apply_cb != SCM_BOOL_F)
-        scm_gc_unprotect_object (cbdata->apply_cb);
-
-    g_free (cbdata);
-}
-
-/* Both apply_cb and close_cb should be scheme functions with 0 arguments.
- * References to these functions will be held until the close_cb is called
- */
-void
-gnc_options_dialog_set_scm_callbacks (GNCOptionWin *win, SCM apply_cb,
-                                      SCM close_cb)
-{
-    struct scm_cb *cbdata;
-
-    cbdata = g_new0 (struct scm_cb, 1);
-    cbdata->apply_cb = apply_cb;
-    cbdata->close_cb = close_cb;
-
-    if (apply_cb != SCM_BOOL_F)
-        scm_gc_protect_object (cbdata->apply_cb);
-
-    if (close_cb != SCM_BOOL_F)
-        scm_gc_protect_object (cbdata->close_cb);
-
-    gnc_options_dialog_set_apply_cb (win, scm_apply_cb, cbdata);
-    gnc_options_dialog_set_close_cb (win, scm_close_cb, cbdata);
-}
diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 57cec6218..838fefae4 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -241,17 +241,24 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
                         GList *children = gtk_container_get_children(GTK_CONTAINER(it->data));
                         for (GList *it = children; it; it = it->next)
                         {
-                            if (g_strcmp0 (gtk_widget_get_name(GTK_WIDGET(it->data)), "ok_button") == 0)
-                                gtk_widget_set_sensitive (GTK_WIDGET(it->data), sensitive);
-
-                            if (g_strcmp0 (gtk_widget_get_name(GTK_WIDGET(it->data)), "apply_button") == 0)
-                                gtk_widget_set_sensitive (GTK_WIDGET(it->data), sensitive);
+                            GtkWidget* widget = GTK_WIDGET(it->data);
+                            const gchar* name = gtk_widget_get_name(widget);
+
+                            if (g_strcmp0 (name, "ok_button") == 0 ||
+                                g_strcmp0 (name, "apply_button") == 0)
+                                gtk_widget_set_sensitive (widget, sensitive);
+                            else if (g_strcmp0 (name, "cancel_button") == 0)
+                                gtk_button_set_label (GTK_BUTTON (widget),
+                                                      sensitive ? _("_Cancel") :
+                                                      _("_Close"));
                         }
                         g_list_free (children);
                     }
+                    break; // Found the button-box, no need to continue.
                 }
                 g_list_free (children);
             }
+            break; // Found the box, no need to continue.
         }
         g_list_free (children);
     }
@@ -684,6 +691,7 @@ dialog_reset_cb(GtkWidget * w, gpointer data)
 {
     GNCOptionWin *win = static_cast<decltype(win)>(data);
     gpointer val;
+    bool dialog_changed = false;
 
     val = g_object_get_data(G_OBJECT(w), "section");
     g_return_if_fail (val);
@@ -691,12 +699,17 @@ dialog_reset_cb(GtkWidget * w, gpointer data)
 
     auto section = static_cast<GncOptionSection*>(val);
     section->foreach_option(
-        [](GncOption& option) {
+        [&dialog_changed](GncOption& option) {
+            if (option.is_changed())
+            {
+                option.reset_default_value();
+                option.get_ui_item()->set_dirty(true);
+                dialog_changed = true;
+            }
             option.set_ui_item_from_option();
-            const_cast<GncOptionUIItem*>(option.get_ui_item())->set_dirty(true);
         });
 
-    dialog_changed_internal (win->window, TRUE);
+    dialog_changed_internal (win->window, dialog_changed);
 }
 
 void
@@ -768,6 +781,9 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title,
     retval->window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window"));
     retval->page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll"));
 
+    // Set the name for this dialog so it can be easily manipulated with css
+    gtk_widget_set_name (GTK_WIDGET(retval->window), "gnc-id-options");
+
     /* Page List */
     {
         GtkTreeView *view;
@@ -808,7 +824,10 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title,
 
     gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, retval);
 
-    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(retval->window), parent);
+    // when added to a page of the hierarchy assistant there will be no parent
+    if (parent)
+        gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(retval->window),
+                                 parent);
 
     if (title)
         gtk_window_set_title(GTK_WINDOW(retval->window), title);
@@ -942,7 +961,8 @@ create_option_widget<GncOptionUIType::BOOLEAN> (GncOption& option,
 
     *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
     gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
-    auto widget = gtk_check_button_new ();
+    auto widget =
+        gtk_check_button_new_with_label (gtk_label_get_text(name_label));
 
     auto ui_item{std::make_unique<GncGtkBooleanUIItem>(widget)};
 
@@ -1791,8 +1811,8 @@ create_account_widget(GncOption& option, char *name)
 
     if (multiple_selection)
     {
-        /* Put the "Show hidden" checkbox on a separate line since the 4 buttons make
-           the dialog too wide. */
+        /* Put the "Show hidden" checkbox on a separate line since
+           the 4 buttons make the dialog too wide. */
         bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
         gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);
         gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
@@ -2824,7 +2844,7 @@ gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win)
 {
     gnc_options_dialog_set_help_cb(win,
                                 (GNCOptionWinCallback)gnc_book_options_help_cb,
-                                NULL);
+                                nullptr);
 }
 
 /* Dummy function impls. The following functions are declared in
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index de985b437..4dddb407a 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -129,6 +129,7 @@ public:
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
     void set_value(ValueType new_value) { m_value = new_value; }
+    void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
@@ -182,6 +183,7 @@ public:
         else
             throw std::invalid_argument("Validation failed, value not set.");
     }
+    void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     std::ostream& to_scheme(std::ostream&) const;
     std::istream& from_scheme(std::istream&);
@@ -425,6 +427,7 @@ public:
         lower = m_min;
         step = m_step;
     }
+    void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
@@ -659,6 +662,7 @@ public:
     {
         return std::get<2>(m_choices.at(index)).c_str();
     }
+    void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
@@ -855,6 +859,7 @@ public:
             m_value = values;
     }
     GList* account_type_list() const noexcept;
+    void reset_default_value() { m_value = m_default_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
@@ -1043,6 +1048,10 @@ public:
     {
         return gnc_relative_date_description(m_period_set.at(index));
     }
+    void reset_default_value() {
+        m_period = m_default_period;
+        m_date = m_default_date;
+    }
     bool is_changed() const noexcept { return m_period != m_default_period &&
             m_date != m_default_date; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index c50f0a04f..30e874e08 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -123,6 +123,12 @@ GncOption::set_value(ValueType value)
                }, *m_option);
 }
 
+void
+GncOption::reset_default_value()
+{
+    std::visit([](auto& option) { option.reset_default_value(); }, *m_option);
+}
+
 template <typename ValueType> void
 GncOption::get_limits(ValueType& max, ValueType& min, ValueType& step) const noexcept
 {
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 72ba3fedd..7b8e3d4c5 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -74,6 +74,7 @@ public:
     template <typename ValueType> void set_value(ValueType value);
     template <typename ValueType> ValueType get_default_value() const;
     template <typename ValueType> ValueType get_value() const;
+    void reset_default_value();
 
     const std::string& get_section() const;
     const std::string& get_name() const;

commit 5f9c66aa616a33cb0f6e6d31df3d952522672a8b
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 17:57:07 2020 -0700

    Save all options to KVP if clear_options is true.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 74fdc6cd5..ff8148200 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -596,11 +596,11 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
     if (clear_options)
         qof_book_options_delete(book, nullptr);
     const_cast<GncOptionDB*>(this)->foreach_section(
-        [book](GncOptionSectionPtr& section)
+        [clear_options, book](GncOptionSectionPtr& section)
         {
             section->foreach_option(
-                [book, &section](auto& option) {
-                    if (option.is_changed())
+                [clear_options, book, &section](auto& option) {
+                    if (clear_options || option.is_changed())
                     {
                         // qof_book_set_option wants a GSList path. Let's avoid
                         // allocating and make one here.

commit 6491c98563152043228891e24dc6d70cafc36695
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 17:49:34 2020 -0700

    Clean up some omitted type-forcing in set_option_from_ui_type() instances.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 0e9a42b7f..57cec6218 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -971,7 +971,7 @@ public:
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
         auto widget{GTK_ENTRY(get_widget())};
-        option.set_value(gtk_entry_get_text(widget));
+        option.set_value(std::string{gtk_entry_get_text(widget)});
     }
 };
 
@@ -2091,7 +2091,7 @@ public:
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
-        option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget())));
+        option.set_value<double>(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget())));
     }
 };
 
@@ -2217,13 +2217,14 @@ public:
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         GtkFontButton *font_button = GTK_FONT_BUTTON(get_widget());
-        gtk_font_button_set_font_name(font_button, option.get_value<std::string>().c_str());
+        gtk_font_button_set_font_name(font_button,
+                                      option.get_value<std::string>().c_str());
 
     }
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
         GtkFontButton *font_button = GTK_FONT_BUTTON(get_widget());
-        option.set_value(gtk_font_button_get_font_name(font_button));
+        option.set_value(std::string{gtk_font_button_get_font_name(font_button)});
     }
 };
 
@@ -2727,7 +2728,7 @@ create_option_widget<GncOptionUIType::BUDGET> (GncOption& option,
                                                GtkGrid *page_box,
                                                GtkLabel *name_label,
                                                char *documentation,
-                                 /* Return values */
+                                               /* Return values */
                                                GtkWidget **enclosing,
                                                bool *packed)
 {
diff --git a/gnucash/gnome/dialog-report-column-view.c b/gnucash/gnome/dialog-report-column-view.c
index 88c5925fc..7d8fbec85 100644
--- a/gnucash/gnome/dialog-report-column-view.c
+++ b/gnucash/gnome/dialog-report-column-view.c
@@ -60,12 +60,11 @@ struct gncp_column_view_edit
     GtkTreeView  * available;
     GtkTreeView  * contents;
 
-    SCM          options;
     SCM          view;
-    GNCOptionDB  * odb;
+    GncOptionDB  * odb;
 
     SCM       available_list;
-    SCM       contents_list;
+    GList*       contents_list;
     int       contents_selected;
 
     GtkWidget *add_button;
@@ -82,26 +81,16 @@ void gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data);
 void gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data);
 
 static void
-gnc_column_view_set_option(GNCOptionDB * odb, char * section, char * name,
-                           SCM new_value)
+gnc_column_view_set_option(GncOptionDB * odb, char * section, char * name,
+                           GList* new_value)
 {
-    GNCOption *  option =
-        gnc_option_db_get_option_by_name(odb, section, name);
-
-    if (option)
-    {
-        gnc_option_db_set_option(odb, section, name, new_value);
-
-        /* set_option doesn't do this */
-        gnc_option_set_changed (option, TRUE);
-    }
+    gnc_option_db_set_glist_value(section, name, new_value);
 }
 
 static void
 gnc_column_view_edit_destroy(gnc_column_view_edit * view)
 {
     gnc_options_dialog_destroy(view->optwin);
-    scm_gc_unprotect_object(view->options);
     scm_gc_unprotect_object(view->view);
     gnc_option_db_destroy(view->odb);
     g_free(view);
@@ -175,12 +164,11 @@ static void
 update_contents_lists(gnc_column_view_edit * view)
 {
     SCM   report_menu_name = scm_c_eval_string("gnc:report-menu-name");
-    SCM   contents =
-        gnc_option_db_lookup_option(view->odb, "__general", "report-list",
-                                    SCM_BOOL_F);
+    GList* contents = gnc_option_db_lookup_glist_option(view->odb,
+                                                        "__general",
+                                                        "report-list");
     SCM   this_report;
-    SCM   selection;
-    gchar *name;
+    gchar*   selection;
 
     GtkListStore *store;
     GtkTreeIter iter;
@@ -189,41 +177,38 @@ update_contents_lists(gnc_column_view_edit * view)
     /* Update the list of selected reports (right selection box). */
     tree_selection = gtk_tree_view_get_selection(view->contents);
 
-    if (scm_is_list(view->contents_list) && !scm_is_null (view->contents_list))
+    if (g_list_length(contents))
     {
         int row = view->contents_selected;
-        row = MIN (row, scm_ilength (view->contents_list) - 1);
-        selection = scm_list_ref (view->contents_list, scm_from_int (row));
+        row = MIN (row,  g_list_length(view->contents_list) - 1);
+        selection = g_list_nth_value(view->contents_list, row);
     }
     else
-        selection = SCM_UNDEFINED;
+        selection = NULL;
 
-    scm_gc_unprotect_object(view->contents_list);
     view->contents_list = contents;
-    scm_gc_protect_object(view->contents_list);
 
     store = GTK_LIST_STORE(gtk_tree_view_get_model(view->contents));
     gtk_list_store_clear(store);
 
-    if (scm_is_list(contents))
+    for (GList* node = contents; node; g_list_next(node))
     {
-        for (int i = 0; !scm_is_null(contents); contents = SCM_CDR(contents), i++)
-        {
-            SCM contents_temp = SCM_CAR(contents);
-
-            int id = scm_to_int(SCM_CAAR(contents));
-
-            this_report = gnc_report_find(id);
-            name = gnc_scm_to_utf8_string (scm_call_1(report_menu_name, this_report));
-
-            gtk_list_store_append(store, &iter);
-            gtk_list_store_set
-            (store, &iter,
-             CONTENTS_COL_NAME, _(name),
-             CONTENTS_COL_ROW, i,
-             CONTENTS_COL_REPORT_COLS, scm_to_int(SCM_CADR(contents_temp)),
-             CONTENTS_COL_REPORT_ROWS, scm_to_int(SCM_CADDR(contents_temp)),
-             -1);
+        gchar *name;
+        SCM contents_temp = SCM_CAR(node);
+        int id = scm_to_int(SCM_CAAR(node));
+
+        this_report = gnc_report_find(id);
+        name = gnc_scm_to_utf8_string (scm_call_1(report_menu_name, this_report));
+
+        gtk_list_store_append(store, &iter);
+        gtk_list_store_set (store, &iter,
+                            CONTENTS_COL_NAME, _(name),
+                            CONTENTS_COL_ROW, i,
+                            CONTENTS_COL_REPORT_COLS,
+                            scm_to_int(SCM_CADR(contents_temp)),
+                            CONTENTS_COL_REPORT_ROWS,
+                            scm_to_int(SCM_CADDR(contents_temp)),
+                            -1);
 
             if (scm_is_equal (contents_temp, selection))
                 gtk_tree_selection_select_iter (tree_selection, &iter);
@@ -324,7 +309,7 @@ gnc_column_view_edit_close_cb(GNCOptionWin * win, gpointer user_data)
  ********************************************************************/
 
 GtkWidget *
-gnc_column_view_edit_options(SCM options, SCM view)
+gnc_column_view_edit_options(GncOptionDB* odb, SCM view)
 {
     SCM get_editor = scm_c_eval_string("gnc:report-editor-widget");
     SCM ptr;
@@ -366,12 +351,11 @@ gnc_column_view_edit_options(SCM options, SCM view)
         r->down_button = GTK_WIDGET(gtk_builder_get_object (builder, "down_button1"));
         r->size_button = GTK_WIDGET(gtk_builder_get_object (builder, "size_button1"));
 
-        r->options   = options;
         r->view      = view;
         r->available_list = SCM_EOL;
         r->contents_selected = 0;
         r->contents_list = SCM_EOL;
-        r->odb       = gnc_option_db_new(r->options);
+        r->odb       = odb;
 
         gnc_options_dialog_build_contents(r->optwin, r->odb);
 
@@ -380,7 +364,6 @@ gnc_column_view_edit_options(SCM options, SCM view)
                                  editor,
                                  gtk_label_new(_("Contents")));
 
-        scm_gc_protect_object(r->options);
         scm_gc_protect_object(r->view);
         scm_gc_protect_object(r->available_list);
         scm_gc_protect_object(r->contents_list);
diff --git a/gnucash/gnome/dialog-report-column-view.h b/gnucash/gnome/dialog-report-column-view.h
index 1b37b499f..4153690e3 100644
--- a/gnucash/gnome/dialog-report-column-view.h
+++ b/gnucash/gnome/dialog-report-column-view.h
@@ -28,6 +28,6 @@
 
 typedef struct gncp_column_view_edit gnc_column_view_edit;
 
-GtkWidget * gnc_column_view_edit_options(SCM options, SCM view);
+GtkWidget * gnc_column_view_edit_options(GncOptionDB* odb, SCM view);
 
 #endif
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index b05350311..5cc07c058 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -53,6 +53,7 @@ enum GncOptionUIType
     INVOICE,
     TAX_TABLE,
     QUERY,
+    REPORT_LIST,
     MAX_VALUE,  //Nake sure this one is always last
 };
 

commit 245a8fccc7886550b22cb6bfb95b9e63681b53a1
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 17:47:26 2020 -0700

    Correctly select UI types for GUID output.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index a0f3fc235..74fdc6cd5 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -568,6 +568,28 @@ GncOptionDB::load_from_key_value(std::istream& iss)
     return iss;
 }
 
+bool
+is_qofinstance_ui_type(GncOptionUIType type)
+{
+    switch (type)
+    {
+        case CURRENCY:
+        case COMMODITY:
+        case ACCOUNT_SEL:
+        case BUDGET:
+        case OWNER:
+        case CUSTOMER:
+        case VENDOR:
+        case EMPLOYEE:
+        case INVOICE:
+        case TAX_TABLE:
+        case QUERY:
+            return true;
+        default:
+            return false;
+    }
+}
+
 void
 GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
 {
@@ -593,7 +615,7 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
                                                   g_strdup("f"))};
                             qof_book_set_option(book, kvp, &list_head);
                         }
-                        else if (type > GncOptionUIType::DATE_FORMAT)
+                        else if (is_qofinstance_ui_type(type))
                         {
                             const QofInstance* inst{QOF_INSTANCE(option.template get_value<const QofInstance*>())};
                             auto guid = guid_copy(qof_instance_get_guid(inst));

commit c751e561847409444b25f819edd8b6292fb2e68c
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 17:27:15 2020 -0700

    Fix fail to load or save number-range values in KVP.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 909b5c294..a0f3fc235 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -602,7 +602,9 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
                         }
                         else if (type == GncOptionUIType::NUMBER_RANGE)
                         {
-                            auto kvp{new KvpValue(option.template get_value<int64_t>())};
+                            /* The Gtk control uses a double so that's what we
+                             * have to store. */
+                            auto kvp{new KvpValue(option.template get_value<double>())};
                             qof_book_set_option(book, kvp, &list_head);
                         }
                         else
@@ -634,6 +636,9 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
                         return;
                     switch (kvp->get_type())
                     {
+                        case KvpValue::Type::DOUBLE:
+                            option.set_value(kvp->get<double>());
+                            break;
                         case KvpValue::Type::INT64:
                             option.set_value(kvp->get<int64_t>());
                             break;
@@ -1166,7 +1171,7 @@ gnc_option_db_book_options(GncOptionDB* odb)
 
 //Accounts Tab
 
-    gnc_register_number_range_option(odb, OPTION_SECTION_ACCOUNTS,
+    gnc_register_number_range_option<double>(odb, OPTION_SECTION_ACCOUNTS,
                                      OPTION_NAME_AUTO_READONLY_DAYS, "a",
                                      N_("Choose the number of days after which transactions will be read-only and cannot be edited anymore. This threshold is marked by a red line in the account register windows. If zero, all transactions can be edited and none are read-only."),
                                      0.0, 0.0, 3650.0, 1.0);

commit 010ab1a965a87c25550f7b32207558d533ca9db7
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 17:25:46 2020 -0700

    Fix free of unallocated ptr crash.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index c91574e91..909b5c294 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -588,7 +588,9 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
                         if (type == GncOptionUIType::BOOLEAN)
                         {
                             auto val{option.template get_value<bool>()};
-                            auto kvp{new KvpValue(val ? "t" : "f")};
+                            // ~KvpValue will g_free the value.
+                            auto kvp{new KvpValue(val ? g_strdup("t") :
+                                                  g_strdup("f"))};
                             qof_book_set_option(book, kvp, &list_head);
                         }
                         else if (type > GncOptionUIType::DATE_FORMAT)

commit 79fdb4124e6810cc4cba90f48acf376991b0f91c
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:50:17 2020 -0700

    Fix some errant UI Types and a mis-formatted function.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 8e736832d..0e9a42b7f 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1938,7 +1938,7 @@ class GncGtkListUIItem : public GncOptionGtkUIItem
 {
 public:
     GncGtkListUIItem(GtkWidget* widget) :
-        GncOptionGtkUIItem{widget, GncOptionUIType::MULTICHOICE} {}
+        GncOptionGtkUIItem{widget, GncOptionUIType::LIST} {}
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         auto widget{GTK_TREE_VIEW(get_widget())};
@@ -2496,7 +2496,7 @@ class GncGtkDateFormatUIItem : public GncOptionGtkUIItem
 {
 public:
     GncGtkDateFormatUIItem(GtkWidget* widget) :
-        GncOptionGtkUIItem{widget, GncOptionUIType::STRING} {}
+        GncOptionGtkUIItem{widget, GncOptionUIType::DATE_FORMAT} {}
     void set_ui_item_from_option(GncOption& option) noexcept override
     {
         auto widget{GNC_DATE_FORMAT(get_widget())};
@@ -2723,10 +2723,13 @@ public:
 };
 
 template<> GtkWidget *
-create_option_widget<GncOptionUIType::BUDGET> (GncOption& option, GtkGrid *page_box,
-                                 GtkLabel *name_label, char *documentation,
+create_option_widget<GncOptionUIType::BUDGET> (GncOption& option,
+                                               GtkGrid *page_box,
+                                               GtkLabel *name_label,
+                                               char *documentation,
                                  /* Return values */
-                                 GtkWidget **enclosing, bool *packed)
+                                               GtkWidget **enclosing,
+                                               bool *packed)
 {
     *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
     gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);

commit e3b5a7d833f2a470e47225b9c8201336e0c71f62
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:48:41 2020 -0700

    Fix boolean controls so that the set visitor recognizes the value.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index f3f329ad4..8e736832d 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -926,7 +926,7 @@ public:
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
         auto widget{GTK_TOGGLE_BUTTON(get_widget())};
-        option.set_value(gtk_toggle_button_get_active(widget));
+        option.set_value(static_cast<bool>(gtk_toggle_button_get_active(widget)));
     }
 };
 
@@ -944,7 +944,7 @@ create_option_widget<GncOptionUIType::BOOLEAN> (GncOption& option,
     gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
     auto widget = gtk_check_button_new ();
 
-    auto ui_item{std::make_unique<GncGtkBooleanUIItem>(GncGtkBooleanUIItem{widget})};
+    auto ui_item{std::make_unique<GncGtkBooleanUIItem>(widget)};
 
     g_signal_connect(G_OBJECT(widget), "toggled",
                      G_CALLBACK(gnc_option_changed_widget_cb), &option);

commit 2dd8d782e5f692e8096bfc29022d8fb888197b85
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:46:47 2020 -0700

    Don't crash if the option set doesn't set a default section.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 328ca26ef..f3f329ad4 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -542,13 +542,14 @@ gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox,
     auto num_sections = odb->num_sections();
     auto default_section = odb->get_default_section();
 
-    PINFO("Default Section name is %s", default_section->get_name().c_str());
+    PINFO("Default Section name is %s",
+          default_section ? default_section->get_name().c_str() : "NULL");
 
     odb->foreach_section(
         [propertybox, default_section, &default_page]
         (GncOptionSectionPtr& section) {
             auto page = gnc_options_dialog_append_page(propertybox, section);
-            if (section.get() == default_section)
+            if (default_section && section.get() == default_section)
                 default_page = page;
         });
 

commit 7cb27b26362aebfcdd842ec4deeb41fc75a2ad6a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:45:34 2020 -0700

    Make changes to controls enable the Apply & OK buttons.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 78a67fae4..328ca26ef 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -270,6 +270,7 @@ gnc_option_changed_widget_cb(GtkWidget *widget, GncOption* option)
 {
     if (!option) return;
     const_cast<GncOptionUIItem*>(option->get_ui_item())->set_dirty(true);
+    dialog_changed_internal(widget, true);
 }
 
 void

commit 4a4e5d36f8f837bdf3e44e5586fec81aaead100a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:44:38 2020 -0700

    Enable gnc_register_owner_option to handle the three types that GncOwner aliases.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 90f44e6e6..c91574e91 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -896,23 +896,43 @@ gnc_register_internal_option(GncOptionDB* db, const char* section,
     db->register_option(section, std::move(option));
 }
 
+static inline GncOptionUIType
+owner_type_to_ui_type(GncOwnerType type)
+{
+    switch (type)
+    {
+        case GNC_OWNER_NONE:
+        case GNC_OWNER_UNDEFINED:
+        case GNC_OWNER_JOB:
+            return GncOptionUIType::INTERNAL;
+        case GNC_OWNER_CUSTOMER:
+            return GncOptionUIType::CUSTOMER;
+        case GNC_OWNER_VENDOR:
+            return GncOptionUIType::VENDOR;
+        case GNC_OWNER_EMPLOYEE:
+            return GncOptionUIType::EMPLOYEE;
+    }
+}
+
 void
 gnc_register_owner_option(GncOptionDB* db, const char* section,
-                            const char* name, const char* key,
-                            const char* doc_string, GncOwner* value)
+                          const char* name, const char* key,
+                          const char* doc_string, GncOwner* value,
+                          GncOwnerType type)
 {
-    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
-            GncOptionUIType::INVOICE};
+    GncOption option{section, name, key, doc_string,
+                     (const QofInstance*)value->owner.undefined,
+                     owner_type_to_ui_type(type)};
     db->register_option(section, std::move(option));
 }
 
 void
 gnc_register_invoice_option(GncOptionDB* db, const char* section,
-                          const char* name, const char* key,
-                          const char* doc_string, GncInvoice* value)
+                            const char* name, const char* key,
+                            const char* doc_string, GncInvoice* value)
 {
     GncOption option{section, name, key, doc_string, (const QofInstance*)value,
-            GncOptionUIType::OWNER};
+            GncOptionUIType::INVOICE};
     db->register_option(section, std::move(option));
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index eeb1c565c..34b812268 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -174,7 +174,8 @@ void gnc_register_invoice_option(GncOptionDB* db, const char* section,
 
 void gnc_register_owner_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
-                               const char* doc_string, GncOwner* value);
+                               const char* doc_string, GncOwner* value,
+                               GncOwnerType type);
 
 void gnc_register_taxtable_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,

commit d8f83d6ee6634bd5e843a1e9e6cc066976f30732
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:42:59 2020 -0700

    Boolean simple won't work if its UI type is INTERNAL.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 2ee330443..90f44e6e6 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -726,7 +726,7 @@ gnc_register_simple_boolean_option(GncOptionDB* db,
                                    bool value)
 {
     GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::INTERNAL};
+            GncOptionUIType::BOOLEAN};
     db->register_option(section, std::move(option));
 }
 

commit 3514725a97f0737b63a84701d40d8f9c5e9e7135
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:34:45 2020 -0700

    Make gnc_register_number_range_option a template on ValueType.
    
    GtkSpinButton works with doubles, but we want to preserve the
    ability to use other types.
    It really should have enable_if<is_arithmetic(ValueType)>.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index f854ea618..2ee330443 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -853,14 +853,15 @@ gnc_register_list_option(GncOptionDB* db, const char* section,
 /* Only balance-forecast.scm, hello-world.scm, and net-charts.scm
  * use decimals and fractional steps and they can be worked around.
  */
-void
+template <typename ValueType> void
 gnc_register_number_range_option(GncOptionDB* db, const char* section,
                                  const char* name, const char* key,
-                                 const char* doc_string, int value, int min,
-                                 int max, int step)
+                                 const char* doc_string, ValueType value,
+                                 ValueType min, ValueType max, ValueType step)
 {
-    GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
-                value, min, max, step}};
+    GncOption option{GncOptionRangeValue<ValueType>{section, name, key,
+                                                    doc_string, value, min,
+                                                    max, step}};
     db->register_option(section, std::move(option));
 }
 
@@ -928,10 +929,10 @@ gnc_register_taxtable_option(GncOptionDB* db, const char* section,
 void
 gnc_register_counter_option(GncOptionDB* db, const char* section,
                             const char* name, const char* key,
-                            const char* doc_string, int value)
+                            const char* doc_string, double value)
 {
-    GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
-                value, 0, 999999999, 1}};
+    GncOption option{GncOptionRangeValue<double>{section, name, key, doc_string,
+                value, 0.0, 999999999.0, 1.0}};
     db->register_option(section, std::move(option));
 }
 
@@ -1312,3 +1313,13 @@ gnc_option_db_set_glist_value(GncOptionDB*, const char*, const char*, GList*)
 {
 }
 
+// Force creation of templates
+template void gnc_register_number_range_option(GncOptionDB* db,
+                                      const char* section, const char* name,
+                                      const char* key, const char* doc_string,
+                                      int value, int min, int max, int step);
+template void gnc_register_number_range_option(GncOptionDB* db,
+                                      const char* section, const char* name,
+                                      const char* key, const char* doc_string,
+                                      double value, double min,
+                                      double max, double step);
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index b373aa763..eeb1c565c 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -138,10 +138,12 @@ void gnc_register_list_option(GncOptionDB* db, const char* section,
                               const char* doc_string, const char* value,
                               GncMultichoiceOptionChoices&& list);
 
+template <typename ValueType>
 void gnc_register_number_range_option(GncOptionDB* db,
                                       const char* section, const char* name,
                                       const char* key, const char* doc_string,
-                                      int value, int min, int max, int step);
+                                      ValueType value, ValueType min,
+                                      ValueType max, ValueType step);
 
 void gnc_register_number_plot_size_option(GncOptionDB* db,
                                           const char* section, const char* name,
@@ -180,7 +182,7 @@ void gnc_register_taxtable_option(GncOptionDB* db, const char* section,
 
 void gnc_register_counter_option(GncOptionDB* db, const char* section,
                                  const char* name, const char* key,
-                                 const char* doc_string, int value);
+                                 const char* doc_string, double value);
 
 void gnc_register_counter_format_option(GncOptionDB* db,
                                         const char* section, const char* name,

commit e78c01269949feeefea911663a3fa98cf29cfb5d
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:33:06 2020 -0700

    Fill in the book options.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 44326a27c..f854ea618 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -25,6 +25,7 @@
 #include <limits>
 #include <sstream>
 #include <kvp-value.hpp>
+#include <qofbookslots.h>
 #include "gnc-optiondb.h"
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
@@ -1132,6 +1133,156 @@ gnc_option_db_save(GncOptionDB* odb, QofBook* book,
 {
     odb->save_to_kvp(book, static_cast<bool>(clear_options));
 }
+
+void
+gnc_option_db_book_options(GncOptionDB* odb)
+{
+    constexpr const char* business_section{N_("Business")};
+    constexpr const char* counter_section{N_("Counters")};
+    static const std::string empty_string{""};
+
+//Accounts Tab
+
+    gnc_register_number_range_option(odb, OPTION_SECTION_ACCOUNTS,
+                                     OPTION_NAME_AUTO_READONLY_DAYS, "a",
+                                     N_("Choose the number of days after which transactions will be read-only and cannot be edited anymore. This threshold is marked by a red line in the account register windows. If zero, all transactions can be edited and none are read-only."),
+                                     0.0, 0.0, 3650.0, 1.0);
+
+    gnc_register_simple_boolean_option(odb, OPTION_SECTION_ACCOUNTS,
+                                       OPTION_NAME_NUM_FIELD_SOURCE, "b",
+                                       N_("Check to have split action field used in registers for 'Num' field in place of transaction number; transaction number shown as 'T-Num' on second line of register. Has corresponding effect on business features, reporting and imports/exports."),
+                                       false);
+    gnc_register_simple_boolean_option(odb, OPTION_SECTION_ACCOUNTS,
+                                       OPTION_NAME_TRADING_ACCOUNTS, "a",
+                                       N_("Check to have trading accounts used for transactions involving more than one currency or commodity."),
+                                       false);
+
+//Budgeting Tab
+
+    gnc_register_budget_option(odb, OPTION_SECTION_BUDGETING,
+                               OPTION_NAME_DEFAULT_BUDGET, "a",
+                               N_("Budget to be used when none has been otherwise specified."),
+                               nullptr);
+
+//Counters Tab
+
+    gnc_register_counter_option(odb, counter_section,
+                                N_("Customer number"), "a",
+                                N_("The previous customer number generated. This number will be incremented to generate the next customer number."),
+                                0.0);
+    gnc_register_counter_format_option(odb, counter_section,
+                                       N_("Customer number format"), "b",
+                                       N_("The format string to use for generating customer numbers. This is a printf-style format string."),
+                                       empty_string);
+    gnc_register_counter_option(odb, counter_section,
+                                N_("Employee number"), "a",
+                                N_("The previous employee number generated. This number will be incremented to generate the next employee number."),
+                                0.0);
+    gnc_register_counter_format_option(odb, counter_section,
+                                       N_("Employee number format"), "b",
+                                       N_("The format string to use for generating employee numbers. This is a printf-style format string."),
+                                       empty_string);
+    gnc_register_counter_option(odb, counter_section,
+                                N_("Invoice number"), "a",
+                                N_("The previous invoice number generated. This number will be incremented to generate the next invoice number."),
+                                0.0);
+    gnc_register_counter_format_option(odb, counter_section,
+                                       N_("Invoice number format"), "b",
+                                       N_("The format string to use for generating invoice numbers. This is a printf-style format string."),
+                                       empty_string);
+    gnc_register_counter_option(odb, counter_section,
+                                N_("Bill number"), "a",
+                                N_("The previous bill number generated. This number will be incremented to generate the next bill number."),
+                                0.0);
+    gnc_register_counter_format_option(odb, counter_section,
+                                       N_("Bill number format"), "b",
+                                       N_("The format string to use for generating bill numbers. This is a printf-style format string."),
+                                       empty_string);
+    gnc_register_counter_option(odb, counter_section,
+                                N_("Expense voucher number"), "a",
+                                N_("The previous expense voucher number generated. This number will be incremented to generate the next voucher number."),
+                                0.0);
+    gnc_register_counter_format_option(odb, counter_section,
+                                       N_("Expense voucher number format"), "b",
+                                       N_("The format string to use for generating expense voucher numbers. This is a printf-style format string."),
+                                       empty_string);
+    gnc_register_counter_option(odb, counter_section,
+                                N_("Job number"), "a",
+                                N_("The previous job number generated. This number will be incremented to generate the next job number."),
+                                0.0);
+    gnc_register_counter_format_option(odb, counter_section,
+                                       N_("Job number format"), "b",
+                                       N_("The format string to use for generating job numbers. This is a printf-style format string."),
+                                       empty_string);
+    gnc_register_counter_option(odb, counter_section,
+                                N_("Order number"), "a",
+                                N_("The previous order number generated. This number will be incremented to generate the next order number."),
+                                0.0);
+    gnc_register_counter_format_option(odb, counter_section,
+                                       N_("Order number format"), "b",
+                                       N_("The format string to use for generating order numbers. This is a printf-style format string."),
+                                       empty_string);
+    gnc_register_counter_option(odb, counter_section,
+                                N_("Vendor number"), "a",
+                                N_("The previous vendor number generated. This number will be incremented to generate the next vendor number."),
+                                0.0);
+    gnc_register_counter_format_option(odb, counter_section,
+                                       N_("Vendor number format"), "b",
+                                       N_("The format string to use for generating vendor numbers. This is a printf-style format string."),
+                                       empty_string);
+
+//Business Tab
+
+    gnc_register_string_option(odb, business_section, N_("Company Name"), "a",
+                               N_("The name of your business."),
+                               empty_string);
+    gnc_register_text_option(odb, business_section, N_("Company Address"), "b1",
+                             N_("The address of your business."),
+                             empty_string);
+    gnc_register_string_option(odb, business_section,
+                               N_("Company Contact Person"), "b2",
+                               N_("The contact person to print on invoices."),
+                               empty_string);
+    gnc_register_string_option(odb, business_section,
+                               N_("Company Phone Number"), "c1",
+                               N_("The contact person to print on invoices."),
+                               empty_string);
+    gnc_register_string_option(odb, business_section,
+                               N_("Company Fax Number"), "c2",
+                               N_("The fax number of your business."),
+                               empty_string);
+    gnc_register_string_option(odb, business_section,
+                               N_("Company Email Address"), "c3",
+                               N_ ("The email address of your business."),
+                               empty_string);
+    gnc_register_string_option(odb, business_section,
+                               N_("Company Website URL"), "c4",
+                               N_("The URL address of your website."),
+                               empty_string);
+    gnc_register_string_option(odb, business_section, N_("Company ID"), "c5",
+                               N_("The ID for your company (eg 'Tax-ID: 00-000000)."),
+                               empty_string);
+
+    gnc_register_taxtable_option(odb, business_section,
+                                 N_("Default Customer TaxTable"), "e",
+                                 N_("The default tax table to apply to customers."),
+                                 nullptr);
+    gnc_register_taxtable_option(odb, business_section,
+                                 N_("Default Vendor TaxTable"), "f",
+                                 N_("The default tax table to apply to vendors."),
+                                 nullptr);
+    gnc_register_dateformat_option(odb, business_section,
+                                   N_("Fancy Date Format"), "g",
+                                   N_("The default date format used for fancy printed dates."),
+                                   empty_string);
+
+//Tax Tab
+
+    gnc_register_string_option(odb, N_("Tax"), N_("Tax Number"), "a",
+                               N_("The electronic tax number of your business"),
+                               empty_string);
+}
+
 const char*
 gnc_option_db_lookup_string_value(GncOptionDB*, const char*, const char*)
 {

commit ae79fd016df7ee1902c6b8015a382e31b7ce802a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:32:16 2020 -0700

    More skeletons

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index f0018994e..44326a27c 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1132,9 +1132,15 @@ gnc_option_db_save(GncOptionDB* odb, QofBook* book,
 {
     odb->save_to_kvp(book, static_cast<bool>(clear_options));
 }
+const char*
+gnc_option_db_lookup_string_value(GncOptionDB*, const char*, const char*)
+{
+    return nullptr;
+}
 
 void
-gnc_option_db_book_options(GncOptionDB*)
+gnc_option_db_set_string_value(GncOptionDB*, const char*,
+                               const char*, const char*)
 {
 }
 

commit 28438e312632aa7b490b33d59da1ddbb14718ac6
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:26:49 2020 -0700

    Rewire gnome and gnome-utils to use GncOptionDB.

diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.c
index 5d07d85de..4682477ec 100644
--- a/gnucash/gnome/assistant-hierarchy.c
+++ b/gnucash/gnome/assistant-hierarchy.c
@@ -119,7 +119,7 @@ typedef struct
     gboolean use_defaults;
     gboolean new_book;  /* presumably only used for new book creation but we check*/
 
-    GNCOptionDB *options;
+    GncOptionDB *options;
     GNCOptionWin *optionwin;
 
     GncHierarchyAssistantFinishedCallback when_completed;
@@ -1503,7 +1503,7 @@ static void
 book_options_dialog_close_cb(GNCOptionWin * optionwin,
                                gpointer user_data)
 {
-    GNCOptionDB * options = user_data;
+    GncOptionDB * options = user_data;
 
     gnc_options_dialog_destroy(optionwin);
     gnc_option_db_destroy(options);
@@ -1516,7 +1516,8 @@ assistant_insert_book_options_page (hierarchy_data *data)
     GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
     gtk_box_set_homogeneous (GTK_BOX (vbox), FALSE);
 
-    data->options = gnc_option_db_new_for_type (QOF_ID_BOOK);
+    data->options = gnc_option_db_new();
+    gnc_option_db_book_options(data->options);
     qof_book_load_options (gnc_get_current_book (),
 			   gnc_option_db_load, data->options);
     gnc_option_db_clean (data->options);
diff --git a/gnucash/gnome/dialog-report-style-sheet.c b/gnucash/gnome/dialog-report-style-sheet.c
index 1df072a85..a9c5d8415 100644
--- a/gnucash/gnome/dialog-report-style-sheet.c
+++ b/gnucash/gnome/dialog-report-style-sheet.c
@@ -55,10 +55,10 @@ struct _stylesheetdialog
 
 typedef struct ss_info
 {
-    GNCOptionWin        * odialog;
-    GNCOptionDB         * odb;
-    SCM                   stylesheet;
-    GtkTreeRowReference * row_ref;
+    GNCOptionWin  * odialog;
+    GncOptionDB   * odb;
+    SCM           stylesheet;
+    GtkTreeRowReference *row_ref;
 } ss_info;
 
 enum
@@ -169,9 +169,9 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss,
     gchar          * title;
     GtkWindow      * parent = GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(ss->list_view)));
 
-    title = g_strdup_printf (_("HTML Style Sheet Properties: %s"), name);
-    ssinfo->odialog = gnc_options_dialog_new (title, parent);
-    ssinfo->odb     = gnc_option_db_new (scm_options);
+    title = g_strdup_printf(_("HTML Style Sheet Properties: %s"), name);
+    ssinfo->odialog = gnc_options_dialog_new(title, parent);
+    ssinfo->odb     = (GncOptionDB *)scm_to_pointer(scm_options);
     ssinfo->stylesheet = sheet_info;
     ssinfo->row_ref    = row_ref;
     g_free (title);
diff --git a/gnucash/gnome/gnc-plugin-page-report.c b/gnucash/gnome/gnc-plugin-page-report.c
index 3c5f4104c..bd1e5f40b 100644
--- a/gnucash/gnome/gnc-plugin-page-report.c
+++ b/gnucash/gnome/gnc-plugin-page-report.c
@@ -105,14 +105,14 @@ typedef struct GncPluginPageReportPrivate
     /// The report which this Page is satisfying
     SCM cur_report;
     /// The Option DB for this report.
-    GNCOptionDB *cur_odb;
+    GncOptionDB *cur_odb;
     SCM option_change_cb_id;
 
     /* initial_report is special; it's the one that's saved and
      * restored.  The name_change_callback only gets called when
      * the initial_report name is changed. */
     SCM          initial_report;
-    GNCOptionDB  * initial_odb;
+    GncOptionDB  * initial_odb;
     SCM          name_change_cb_id;
 
     /* keep a list of edited reports so that we can destroy them when
@@ -648,18 +648,23 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
         DEBUG("calling set_needs_save for report with id=%d", report_id);
         scm_call_2(set_needs_save, inst_report, SCM_BOOL_T);
 
-        priv->initial_odb = gnc_option_db_new(scm_call_1(get_options, inst_report));
+        priv->initial_odb =
+            (GncOptionDB *)scm_to_pointer(scm_call_1(get_options, inst_report));
+/*
         priv->name_change_cb_id =
             gnc_option_db_register_change_callback(priv->initial_odb,
                     gnc_plugin_page_report_refresh,
                     priv,
                     "General", "Report name");
+*/
     }
 
     if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != NULL))
     {
+/*
         gnc_option_db_unregister_change_callback_id(priv->cur_odb,
                 priv->option_change_cb_id);
+*/
         gnc_option_db_destroy(priv->cur_odb);
         priv->cur_odb = NULL;
     }
@@ -669,12 +674,14 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type,
     priv->cur_report = inst_report;
     scm_gc_protect_object(priv->cur_report);
 
-    priv->cur_odb = gnc_option_db_new(scm_call_1(get_options, inst_report));
+    priv->cur_odb = (GncOptionDB *)scm_to_pointer(scm_call_1(get_options,
+                                                             inst_report));
+/*
     priv->option_change_cb_id =
         gnc_option_db_register_change_callback(priv->cur_odb,
                 gnc_plugin_page_report_option_change_cb,
                 report, NULL, NULL);
-
+*/
     if (gnc_html_history_forward_p(gnc_html_get_history(priv->html)))
     {
         gnc_plugin_page_report_set_fwd_button(report, TRUE);
@@ -730,8 +737,9 @@ gnc_plugin_page_report_option_change_cb(gpointer data)
 
     /* Update the page (i.e. the notebook tab and window title) */
     old_name = gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(report));
-    new_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General",
-               "Report name", NULL);
+    new_name = g_strdup(gnc_option_db_lookup_string_value(priv->cur_odb,
+                                                          "General",
+                                                          "Report name"));
     if (strcmp(old_name, new_name) != 0)
     {
         /* Bug 727130, 760711 - remove only the non-printable
@@ -1033,8 +1041,8 @@ gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name)
     priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page);
 
     /* Is this a redundant call? */
-    old_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General",
-               "Report name", NULL);
+    old_name = gnc_option_db_lookup_string_value(priv->cur_odb, "General",
+                                                 "Report name");
     DEBUG("Comparing old name '%s' to new name '%s'",
           old_name ? old_name : "(null)", name);
     if (old_name && (strcmp(old_name, name) == 0))
@@ -1044,7 +1052,7 @@ gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name)
     }
 
     /* Store the new name for the report. */
-    gnc_option_db_set_string_option(priv->cur_odb, "General",
+    gnc_option_db_set_string_value(priv->cur_odb, "General",
                                     "Report name", name);
 
     /* Have to manually call the option change hook. */
@@ -1110,9 +1118,7 @@ gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv)
 
     if (priv->initial_odb)
     {
-        gnc_option_db_unregister_change_callback_id(priv->initial_odb,
-                priv->name_change_cb_id);
-
+//Remove this if there's a double-free
         gnc_option_db_destroy(priv->initial_odb);
         priv->initial_odb = NULL;
     }
@@ -1787,14 +1793,10 @@ gnc_plugin_page_report_options_cb( GtkAction *action, GncPluginPageReport *repor
 static GncInvoice*
 lookup_invoice(GncPluginPageReportPrivate *priv)
 {
-    SCM opt_val = gnc_option_db_lookup_option(priv->cur_odb, "General",
-                                              "Invoice Number", NULL);
-    if (opt_val == SCM_UNDEFINED)
-        return NULL;
-
-#define FUNC_NAME G_STRFUNC
-    return SWIG_MustGetPtr(opt_val, SWIG_TypeQuery("_p__gncInvoice"), 1, 0);
-#undef FUNC_NAME
+    const QofInstance* opt_val =
+        gnc_option_db_lookup_qofinstance_value(priv->cur_odb, "General",
+                                               "Invoice Number");
+    return GNC_INVOICE(opt_val);
 }
 
 #define GNC_PREFS_GROUP_REPORT_PDFEXPORT GNC_PREFS_GROUP_GENERAL_REPORT ".pdf-export"
@@ -1849,8 +1851,9 @@ static gchar *report_create_jobname(GncPluginPageReportPrivate *priv)
          *        so I added yet another hack below for this. cstim.
          */
         GncInvoice *invoice;
-        report_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General",
-                      "Report name", NULL);
+        report_name = g_strdup(gnc_option_db_lookup_string_value(priv->cur_odb,
+                                                                 "General",
+                                                                 "Report name"));
         if (!report_name)
             report_name = g_strdup (_(default_jobname));
         if (g_strcmp0(report_name, _("Printable Invoice")) == 0
diff --git a/gnucash/gnome/window-report.c b/gnucash/gnome/window-report.c
index 31df07d5b..25fb3f6aa 100644
--- a/gnucash/gnome/window-report.c
+++ b/gnucash/gnome/window-report.c
@@ -63,8 +63,7 @@ reportWindow(int report_id, GtkWindow *parent)
 struct report_default_params_data
 {
     GNCOptionWin * win;
-    GNCOptionDB  * db;
-    SCM          scm_options;
+    GncOptionDB  * odb;
     SCM          cur_report;
 };
 
@@ -78,7 +77,7 @@ gnc_options_dialog_apply_cb(GNCOptionWin * propertybox,
     GList *results = NULL, *iter;
 
     if (!win) return;
-    results = gnc_option_db_commit (win->db);
+    results = gnc_option_db_commit (win->odb);
     for (iter = results; iter; iter = iter->next)
     {
         GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW (win->win),
@@ -124,8 +123,7 @@ gnc_options_dialog_close_cb(GNCOptionWin * propertybox,
 
     scm_call_2(set_editor, win->cur_report, SCM_BOOL_F);
     gnc_options_dialog_destroy(win->win);
-    gnc_option_db_destroy(win->db);
-    scm_gc_unprotect_object(win->scm_options);
+    gnc_option_db_destroy(win->odb);
     g_free(win);
 }
 
@@ -149,7 +147,7 @@ gnc_report_raise_editor(SCM report)
 
 
 GtkWidget *
-gnc_report_window_default_params_editor(SCM options, SCM report,
+gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report,
                                         GtkWindow *parent)
 {
     SCM get_report_type   = scm_c_eval_string("gnc:report-type");
@@ -166,9 +164,8 @@ gnc_report_window_default_params_editor(SCM options, SCM report,
         struct report_default_params_data * prm =
             g_new0(struct report_default_params_data, 1);
 
-        prm->scm_options = options;
+        prm->odb         = odb;
         prm->cur_report  = report;
-        prm->db          = gnc_option_db_new(prm->scm_options);
 
         /* Get the title of the report's template. */
         ptr = scm_call_1(get_report_type, report);
@@ -188,11 +185,10 @@ gnc_report_window_default_params_editor(SCM options, SCM report,
 
         g_free ((gpointer *) title);
 
-        scm_gc_protect_object(prm->scm_options);
         scm_gc_protect_object(prm->cur_report);
 
-        gnc_options_dialog_build_contents(prm->win, prm->db);
-        gnc_option_db_clean(prm->db);
+        gnc_options_dialog_build_contents(prm->win, prm->odb);
+        gnc_option_db_clean(prm->odb);
 
         gnc_options_dialog_set_apply_cb(prm->win,
                                         gnc_options_dialog_apply_cb,
@@ -215,6 +211,7 @@ gnc_report_edit_options(SCM report, GtkWindow *parent)
     SCM get_report_type   = scm_c_eval_string("gnc:report-type");
     SCM ptr;
     SCM options;
+    GncOptionDB* odb;
     GtkWidget *options_widget = NULL;
 
     /* If the options editor widget already exists we simply raise it */
@@ -229,16 +226,19 @@ gnc_report_edit_options(SCM report, GtkWindow *parent)
                             _("There are no options for this report."));
         return FALSE;
     }
+    odb = (GncOptionDB*)scm_to_pointer(options);
 
     /* Multi-column type reports need a special options dialog */
     ptr = scm_call_1(get_report_type, report);
     if (scm_is_string(ptr))
     {
         gchar *rpt_type = gnc_scm_to_utf8_string (ptr);
+#if 0
         if (g_strcmp0 (rpt_type, "d8ba4a2e89e8479ca9f6eccdeb164588") == 0)
-            options_widget = gnc_column_view_edit_options (options, report);
+            options_widget = gnc_column_view_edit_options (odb, report);
         else
-            options_widget = gnc_report_window_default_params_editor (options, report, parent);
+#endif
+            options_widget = gnc_report_window_default_params_editor (odb, report, parent);
         g_free (rpt_type);
     }
 
diff --git a/gnucash/gnome/window-report.h b/gnucash/gnome/window-report.h
index e51bbc0a1..17ff0195c 100644
--- a/gnucash/gnome/window-report.h
+++ b/gnucash/gnome/window-report.h
@@ -27,13 +27,14 @@
 
 //#include "gnc-html.h"
 #include "qof.h"
+#include <gnc-optiondb.h>
 
 typedef struct gnc_report_window_s gnc_report_window;
 
 /** PROTOTYPES ******************************************************/
 
 // scm-exposed
-GtkWidget * gnc_report_window_default_params_editor(SCM options, SCM report, GtkWindow *parent);
+GtkWidget * gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report, GtkWindow *parent);
 
 // called from multiple places
 // [gnome-business/dialog-invoice.c;gnome/window-register.c]; and
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 0aea402c7..f0018994e 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -897,7 +897,7 @@ gnc_register_internal_option(GncOptionDB* db, const char* section,
 void
 gnc_register_owner_option(GncOptionDB* db, const char* section,
                             const char* name, const char* key,
-                            const char* doc_string, GncInvoice* value)
+                            const char* doc_string, GncOwner* value)
 {
     GncOption option{section, name, key, doc_string, (const QofInstance*)value,
             GncOptionUIType::INVOICE};
@@ -907,7 +907,7 @@ gnc_register_owner_option(GncOptionDB* db, const char* section,
 void
 gnc_register_invoice_option(GncOptionDB* db, const char* section,
                           const char* name, const char* key,
-                          const char* doc_string, GncOwner* value)
+                          const char* doc_string, GncInvoice* value)
 {
     GncOption option{section, name, key, doc_string, (const QofInstance*)value,
             GncOptionUIType::OWNER};
@@ -1132,7 +1132,6 @@ gnc_option_db_save(GncOptionDB* odb, QofBook* book,
 {
     odb->save_to_kvp(book, static_cast<bool>(clear_options));
 }
-}
 
 void
 gnc_option_db_book_options(GncOptionDB*)
diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index 53873e8e9..01e32ea6c 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -1137,14 +1137,14 @@ qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr)
 }
 
 void
-qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb)
+qof_book_load_options (QofBook *book, GncOptionLoad load_cb, GncOptionDB *odb)
 {
     load_cb (odb, book);
 }
 
 void
-qof_book_save_options (QofBook *book, GNCOptionSave save_cb,
-               GNCOptionDB* odb, gboolean clear)
+qof_book_save_options (QofBook *book, GncOptionSave save_cb,
+                       GncOptionDB* odb, gboolean clear)
 {
     /* Wrap this in begin/commit so that it commits only once instead of doing
      * so for every option. Qof_book_set_option will take care of dirtying the
diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h
index 7c0d33035..5932ecab1 100644
--- a/libgnucash/engine/qofbook.h
+++ b/libgnucash/engine/qofbook.h
@@ -42,6 +42,12 @@
 
 #ifdef __cplusplus
 #include <glib.h> //To preempt it being included extern "C" in a later header.
+class GncOptionDB;
+#else
+#include <option-util.h>
+typedef GNCOptionDB GncOptionDB;
+#endif
+#ifdef __cplusplus
 extern "C"
 {
 #endif
@@ -74,15 +80,8 @@ typedef struct KvpValueImpl KvpValue;
 
 typedef void (*QofBookDirtyCB) (QofBook *, gboolean dirty, gpointer user_data);
 
-#ifdef __cplusplus
-class GncOptionDB;
-using GNCOptionDB = GncOptionDB;
-#else
-typedef struct gnc_option_db GNCOptionDB;
-#endif
-
-typedef void (*GNCOptionSave) (GNCOptionDB*, QofBook*, gboolean);
-typedef void (*GNCOptionLoad) (GNCOptionDB*, QofBook*);
+typedef void (*GncOptionSave) (GncOptionDB*, QofBook*, gboolean);
+typedef void (*GncOptionLoad) (GncOptionDB*, QofBook*);
 
 /* Book structure */
 struct _QofBook
@@ -375,21 +374,21 @@ void qof_book_commit_edit(QofBook *book);
 /** @ingroup KVP
  @{
  */
-/** Load a GNCOptionsDB from KVP data.
+/** Load a GncOptionsDB from KVP data.
  * @param book: The book.
  * @param load_cb: A callback function that does the loading.
- * @param odb: The GNCOptionDB to load.
+ * @param odb: The GncOptionDB to load.
  */
-void qof_book_load_options (QofBook *book, GNCOptionLoad load_cb,
-                GNCOptionDB *odb);
-/** Save a GNCOptionsDB back to the book's KVP.
+void qof_book_load_options (QofBook *book, GncOptionLoad load_cb,
+                            GncOptionDB *odb);
+/** Save a GncOptionsDB back to the book's KVP.
  * @param book: The book.
  * @param save_cb: A callback function that does the saving.
- * @param odb: The GNCOptionsDB to save from.
- * @param clear: Should the GNCOptionsDB be emptied after the save?
+ * @param odb: The GncOptionsDB to save from.
+ * @param clear: Should the GncOptionsDB be emptied after the save?
  */
-void qof_book_save_options (QofBook *book, GNCOptionSave save_cb,
-                            GNCOptionDB* odb, gboolean clear);
+void qof_book_save_options (QofBook *book, GncOptionSave save_cb,
+                            GncOptionDB* odb, gboolean clear);
 /** Save a single option value.
  * Used from Scheme, the KvpValue<-->SCM translation is handled by the functions
  * in kvp-scm.c and automated by SWIG. The starting element is set as

commit 41e59df71fb74ec157b7cc3af767f17cf1a4e7bb
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:24:43 2020 -0700

    Remove unused and un-needed function.

diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 0a87756bb..058f308be 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -82,7 +82,6 @@ public:
             func(section);
     }
     size_t num_sections() const noexcept { return m_sections.size(); }
-    void save_to_book(QofBook* book, bool do_clear) const;
     bool get_changed() const noexcept { return m_dirty; }
     void register_option(const char* section, GncOption&& option);
     void unregister_option(const char* section, const char* name);
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index def04f72a..0aea402c7 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -82,11 +82,6 @@ GncOptionDB::GncOptionDB() : m_default_section{} {}
 
 GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
 
-void
-GncOptionDB::save_to_book(QofBook* book, bool do_clear) const
-{
-}
-
 void
 GncOptionDB::register_option(const char* sectname, GncOption&& option)
 {
@@ -1135,7 +1130,8 @@ void
 gnc_option_db_save(GncOptionDB* odb, QofBook* book,
                         gboolean clear_options)
 {
-    odb->save_to_book(book, static_cast<bool>(clear_options));
+    odb->save_to_kvp(book, static_cast<bool>(clear_options));
+}
 }
 
 void

commit 1eef796f098bd20c143040e55ee5b0332181d192
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:23:55 2020 -0700

    Add some skeleton functions to make the option-using code in gnucash/gnome-utils and gnucash/gnome happy.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 0dcaa917b..def04f72a 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -1142,3 +1142,21 @@ void
 gnc_option_db_book_options(GncOptionDB*)
 {
 }
+
+const QofInstance*
+gnc_option_db_lookup_qofinstance_value(GncOptionDB*, const char*, const char*)
+{
+    return nullptr;
+}
+
+GList*
+gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*)
+{
+    return nullptr;
+}
+
+void
+gnc_option_db_set_glist_value(GncOptionDB*, const char*, const char*, GList*)
+{
+}
+
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
index 3b86dbfea..f4b3d4246 100644
--- a/libgnucash/app-utils/gnc-optiondb.h
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -104,7 +104,70 @@ void gnc_option_db_save(GncOptionDB* odb, QofBook* book,
  */
 void gnc_option_db_book_options(GncOptionDB*);
 
+/**
+ * Retrieve the string value of an option in the GncOptionDB
+ *
+ * @param odb the GncOptionDB
+ * @param section the section in which the option is stored
+ * @param name the option name
+ * @return the static char* of the value or nullptr if the option isn't found
+ * or if its value isn't a string.
+ */
+const char* gnc_option_db_lookup_string_value(GncOptionDB*, const char*,
+                                              const char*);
 
+/**
+ * Set the string value of an option in the GncOptionDB.
+ *
+ * The value will not be saved if the option is not in the GncOptionDB or if the
+ * type of the option isn't string or text.
+ *
+ * @param odb the GncOptionDB
+ * @param section the section in which the option is stored
+ * @param name the option name
+ * @param value the value to be stored in the option.
+ */
+void gnc_option_db_set_string_value(GncOptionDB*, const char*,
+                                    const char*, const char*);
+
+/**
+ * Retrieve the string value of an option in the GncOptionDB
+ *
+ * @param odb the GncOptionDB
+ * @param section the section in which the option is stored
+ * @param name the option name
+ * @return the const QofInstance* of the value or nullptr if the option isn't
+ * found or if its value isn't a QofInstance*.
+ */
+
+const QofInstance* gnc_option_db_lookup_qofinstance_value(GncOptionDB*,
+                                                          const char*,
+                                                          const char*);
+
+/**
+ * Retrieve the GList* value of an option in the GncOptionDB
+ *
+ * @param odb the GncOptionDB
+ * @param section the section in which the option is stored
+ * @param name the option name
+ * @return the GList* of the value or nullptr if the option isn't found
+ * or if its value isn't a string.
+ */
+GList* gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*);
+
+/**
+ * Set the GList* value of an option in the GncOptionDB.
+ *
+ * The value will not be saved if the option is not in the GncOptionDB or if the
+ * type of the option isn't string or text.
+ *
+ * @param odb the GncOptionDB
+ * @param section the section in which the option is stored
+ * @param name the option name
+ * @param value the value to be stored in the option.
+ */
+void gnc_option_db_set_glist_value(GncOptionDB*, const char*,
+                                    const char*, GList*);
 
 #ifdef __cplusplus
 }

commit 99103ffd104618b40018a7725d7b7c22d66325ef
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 14:16:22 2020 -0700

    Change gnc-register-option functions to take GncOptionDB*.
    
    Instead of std::unique_ptr<GncOptionDB> because there's no way
    to get a unique_ptr through a C call.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 11a47f879..0dcaa917b 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -663,7 +663,7 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
 }
 
 void
-gnc_register_string_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_string_option(GncOptionDB* db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, std::string value)
 {
@@ -673,7 +673,7 @@ gnc_register_string_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const char* name,
+gnc_register_text_option(GncOptionDB* db, const char* section, const char* name,
                          const char* key, const char* doc_string,
                          std::string value)
 {
@@ -684,7 +684,7 @@ gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const ch
 }
 
 void
-gnc_register_font_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_font_option(GncOptionDB* db, const char* section,
                          const char* name, const char* key,
                          const char* doc_string, std::string value)
 {
@@ -694,7 +694,7 @@ gnc_register_font_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_budget_option(GncOptionDB* db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, GncBudget *value)
 {
@@ -704,7 +704,7 @@ gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_color_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_color_option(GncOptionDB* db, const char* section,
                          const char* name, const char* key,
                          const char* doc_string, std::string value)
 {
@@ -714,7 +714,7 @@ gnc_register_color_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_commodity_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, gnc_commodity *value)
 {
@@ -724,7 +724,7 @@ gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_simple_boolean_option(const GncOptionDBPtr& db,
+gnc_register_simple_boolean_option(GncOptionDB* db,
                                    const char* section, const char* name,
                                    const char* key, const char* doc_string,
                                    bool value)
@@ -735,7 +735,7 @@ gnc_register_simple_boolean_option(const GncOptionDBPtr& db,
 }
 
 void
-gnc_register_complex_boolean_option(const GncOptionDBPtr& db,
+gnc_register_complex_boolean_option(GncOptionDB* db,
                                     const char* section, const char* name,
                                     const char* key, const char* doc_string,
                                     bool value)
@@ -746,7 +746,7 @@ gnc_register_complex_boolean_option(const GncOptionDBPtr& db,
 }
 
 void
-gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_pixmap_option(GncOptionDB* db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, std::string value)
 {
@@ -756,7 +756,7 @@ gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_account_list_option(GncOptionDB* db, const char* section,
                                  const char* name, const char* key,
                                  const char* doc_string,
                                  const GncOptionAccountList& value)
@@ -767,7 +767,7 @@ gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
+gnc_register_account_list_limited_option(GncOptionDB* db,
                                          const char* section, const char* name,
                                          const char* key,
                                          const char* doc_string,
@@ -814,7 +814,7 @@ gnc_account_list_from_types(QofBook *book,
 
 
 void
-gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
+gnc_register_account_sel_limited_option(GncOptionDB* db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
                                         const GncOptionAccountList& value,
@@ -833,7 +833,7 @@ gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
 }
 
 void
-gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_multichoice_option(GncOptionDB* db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string,
                                 GncMultichoiceOptionChoices&& choices)
@@ -844,7 +844,7 @@ gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_list_option(GncOptionDB* db, const char* section,
                          const char* name, const char* key,
                          const char* doc_string, const char* value,
                          GncMultichoiceOptionChoices&& list)
@@ -858,7 +858,7 @@ gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
  * use decimals and fractional steps and they can be worked around.
  */
 void
-gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_number_range_option(GncOptionDB* db, const char* section,
                                  const char* name, const char* key,
                                  const char* doc_string, int value, int min,
                                  int max, int step)
@@ -869,7 +869,7 @@ gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_number_plot_size_option(const GncOptionDBPtr& db,
+gnc_register_number_plot_size_option(GncOptionDB* db,
                                      const char* section, const char* name,
                                      const char* key, const char* doc_string,
                                      int value)
@@ -880,7 +880,7 @@ gnc_register_number_plot_size_option(const GncOptionDBPtr& db,
 }
 
 void
-gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_query_option(GncOptionDB* db, const char* section,
                           const char* name, const char* key,
                           const char* doc_string, QofQuery* value)
 {
@@ -890,7 +890,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_internal_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_internal_option(GncOptionDB* db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, std::string value)
 {
@@ -900,7 +900,7 @@ gnc_register_internal_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_owner_option(GncOptionDB* db, const char* section,
                             const char* name, const char* key,
                             const char* doc_string, GncInvoice* value)
 {
@@ -910,7 +910,7 @@ gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_owner_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_invoice_option(GncOptionDB* db, const char* section,
                           const char* name, const char* key,
                           const char* doc_string, GncOwner* value)
 {
@@ -920,7 +920,7 @@ gnc_register_owner_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_taxtable_option(GncOptionDB* db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, GncTaxTable* value)
 {
@@ -930,7 +930,7 @@ gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_counter_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_counter_option(GncOptionDB* db, const char* section,
                             const char* name, const char* key,
                             const char* doc_string, int value)
 {
@@ -940,7 +940,7 @@ gnc_register_counter_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_counter_format_option(const GncOptionDBPtr& db,
+gnc_register_counter_format_option(GncOptionDB* db,
                                    const char* section, const char* name,
                                    const char* key, const char* doc_string,
                                    std::string value)
@@ -951,7 +951,7 @@ gnc_register_counter_format_option(const GncOptionDBPtr& db,
 }
 
 void
-gnc_register_dateformat_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_dateformat_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, std::string value)
 {
@@ -961,7 +961,7 @@ gnc_register_dateformat_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_currency_option(GncOptionDB* db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, gnc_commodity *value)
 {
@@ -978,7 +978,7 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_date_option(GncOptionDB* db, const char* section,
                          const char* name, const char* key,
                          const char* doc_string, time64 time,
                          RelativeDateUI ui)
@@ -992,7 +992,7 @@ gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_date_option(GncOptionDB* db, const char* section,
                          const char* name, const char* key,
                          const char* doc_string, RelativeDatePeriod period,
                          RelativeDateUI ui)
@@ -1006,7 +1006,7 @@ gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_date_option(const GncOptionDBPtr& db,
+gnc_register_date_option(GncOptionDB* db,
                                   const char* section, const char* name,
                                   const char* key, const char* doc_string,
                                   RelativeDatePeriodVec& period_set,
@@ -1033,7 +1033,7 @@ static const RelativeDatePeriodVec begin_dates
 };
 
 void
-gnc_register_start_date_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_start_date_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, bool both)
 {
@@ -1057,7 +1057,7 @@ static const RelativeDatePeriodVec end_dates
 };
 
 void
-gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_end_date_option(GncOptionDB* db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, bool both)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 926815315..b373aa763 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -72,123 +72,122 @@ gnc_account_list_from_types(QofBook *book,
                             const GncOptionAccountTypeList& types);
 
 
-using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
-void gnc_register_string_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_string_option(GncOptionDB* db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, std::string value);
 
-void gnc_register_text_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_text_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, std::string value);
 
-void gnc_register_font_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_font_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, std::string value);
 
-void gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_budget_option(GncOptionDB* db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, GncBudget* value);
 
-void gnc_register_commodity_option(const GncOptionDBPtr& db,
+void gnc_register_commodity_option(GncOptionDB* db,
                                    const char* section, const char* name,
                                    const char* key, const char* doc_string,
                                    gnc_commodity* value);
 
-void gnc_register_simple_boolean_option(const GncOptionDBPtr& db,
+void gnc_register_simple_boolean_option(GncOptionDB* db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
                                         bool value);
 
-void gnc_register_complex_boolean_option(const GncOptionDBPtr& db,
+void gnc_register_complex_boolean_option(GncOptionDB* db,
                                          const char* section, const char* name,
                                          const char* key,
                                          const char* doc_string,
                                          bool value);
 
-void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_pixmap_option(GncOptionDB* db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, std::string value);
 
-void gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
+void gnc_register_account_list_limited_option(GncOptionDB* db,
                                              const char* section,
                                              const char* name, const char* key,
                                              const char* doc_string,
                                              const GncOptionAccountList& value,
                                              GncOptionAccountTypeList&& allowed);
 
-void gnc_register_account_list_option(const GncOptionDBPtr& db,
+void gnc_register_account_list_option(GncOptionDB* db,
                                       const char* section,
                                       const char* name, const char* key,
                                       const char* doc_string,
                                       const GncOptionAccountList& value);
 
-void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
+void gnc_register_account_sel_limited_option(GncOptionDB* db,
                                              const char* section,
                                              const char* name, const char* key,
                                              const char* doc_string,
                                              const GncOptionAccountList& value,
                                              GncOptionAccountTypeList&& allowed);
 
-void gnc_register_multichoice_option(const GncOptionDBPtr& db,
+void gnc_register_multichoice_option(GncOptionDB* db,
                                      const char* section, const char* name,
                                      const char* key, const char* doc_string,
                                      GncMultichoiceOptionChoices&& value);
 
-void gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_list_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, const char* value,
                               GncMultichoiceOptionChoices&& list);
 
-void gnc_register_number_range_option(const GncOptionDBPtr& db,
+void gnc_register_number_range_option(GncOptionDB* db,
                                       const char* section, const char* name,
                                       const char* key, const char* doc_string,
                                       int value, int min, int max, int step);
 
-void gnc_register_number_plot_size_option(const GncOptionDBPtr& db,
+void gnc_register_number_plot_size_option(GncOptionDB* db,
                                           const char* section, const char* name,
                                           const char* key,
                                           const char* doc_string,
                                           int value);
 
-void gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_query_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, QofQuery* value);
 
-void gnc_register_color_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_color_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, std::string value);
 
-void gnc_register_internal_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_internal_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, std::string value);
 
 
-void gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_currency_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, gnc_commodity* value);
 
-void gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_invoice_option(GncOptionDB* db, const char* section,
                                  const char* name, const char* key,
                                  const char* doc_string, GncInvoice* value);
 
-void gnc_register_owner_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_owner_option(GncOptionDB* db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, GncOwner* value);
 
-void gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_taxtable_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, GncTaxTable* value);
 
-void gnc_register_counter_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_counter_option(GncOptionDB* db, const char* section,
                                  const char* name, const char* key,
                                  const char* doc_string, int value);
 
-void gnc_register_counter_format_option(const GncOptionDBPtr& db,
+void gnc_register_counter_format_option(GncOptionDB* db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
                                         std::string value);
 
-void gnc_register_dateformat_option(const GncOptionDBPtr& db,
+void gnc_register_dateformat_option(GncOptionDB* db,
                                     const char* section, const char* name,
                                     const char* key, const char* doc_string,
                                     std::string value);
@@ -200,30 +199,30 @@ enum RelativeDateUI : uint8_t
     BOTH
 };
 
-void gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_date_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string,
                               RelativeDatePeriod period =
                               RelativeDatePeriod::TODAY,
                               RelativeDateUI ui = RelativeDateUI::BOTH);
 
-void gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_date_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, time64 time,
                               RelativeDateUI ui = RelativeDateUI::BOTH);
 
-void gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_date_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string,
                               RelativeDatePeriodVec& period_set,
                               bool both = true);
 
-void gnc_register_start_date_option(const GncOptionDBPtr& db,
+void gnc_register_start_date_option(GncOptionDB* db,
                                     const char* section,
                                     const char* name, const char* key,
                                     const char* doc_string, bool both = true);
 
-void gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section,
+void gnc_register_end_date_option(GncOptionDB* db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, bool both = true);
 

commit 8fe338f315253cf4c82ed5d1553e7628e97604d4
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 13:48:20 2020 -0700

    Temporarily disable building dialog-report-column-view.
    
    It will require some major surgery to get working, and disabling
    it allows building GnuCash so that the Book Properties dialog
    can be runtime-tested.

diff --git a/gnucash/gnome/CMakeLists.txt b/gnucash/gnome/CMakeLists.txt
index 6cea3d0c8..00e28d5c0 100644
--- a/gnucash/gnome/CMakeLists.txt
+++ b/gnucash/gnome/CMakeLists.txt
@@ -27,7 +27,7 @@ set (gnc_gnome_noinst_HEADERS
   dialog-payment.h
   dialog-print-check.h
   dialog-progress.h
-  dialog-report-column-view.h
+#  dialog-report-column-view.h
   dialog-report-style-sheet.h
   dialog-sx-editor.h
   dialog-sx-editor2.h
@@ -98,7 +98,7 @@ set (gnc_gnome_SOURCES
   dialog-price-edit-db.c
   dialog-print-check.c
   dialog-progress.c
-  dialog-report-column-view.c
+#  dialog-report-column-view.c
   dialog-report-style-sheet.c
   dialog-sx-editor.c
   dialog-sx-editor2.c

commit 9111f118e098211a59f1a379d230c557b8e0cc26
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 13:31:34 2020 -0700

    Business option create_option_widget specializations.
    
    Required creating a registration class for create_option_widget
    overload functions because without it the dispatch function
    complained of missing symbols at link time.

diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt
index 76fe198fc..960651b52 100644
--- a/gnucash/gnome-utils/CMakeLists.txt
+++ b/gnucash/gnome-utils/CMakeLists.txt
@@ -113,6 +113,7 @@ set (gnome_utils_SOURCES
 
 set(gnome_utils_noinst_HEADERS
   dialog-tax-table.h
+  dialog-options.hpp
   gnc-autosave.h
   gnc-gobject-utils.h
   gnc-gtk-utils.h
diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index f5efcaf8a..78a67fae4 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -1,8 +1,9 @@
 /********************************************************************\
- * dialog-options.cpp -- GNOME option handling                      *
+ * dialog-options.cpp -- option handling                      *
  * Copyright (C) 1998-2000 Linas Vepstas                            *
  * Copyright (c) 2006 David Hampton <hampton at employees.org>         *
  * Copyright (c) 2011 Robert Fewell                                 *
+ * Copyright 2020 John Ralls                                        *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -62,7 +63,10 @@ extern "C"
 #include "misc-gnome-utils.h"
 }
 
-#include "dialog-options.h"
+#include <iostream>
+#include <sstream>
+
+#include "dialog-options.hpp"
 #include <gnc-optiondb.hpp>
 #include <gnc-optiondb-impl.hpp>
 
@@ -83,13 +87,6 @@ extern "C"
 /* This static indicates the debugging module that this .o belongs to.  */
 static QofLogModule log_module = GNC_MOD_GUI;
 
-template <typename Instance> inline const QofInstance*
-qof_instance_cast(Instance inst)
-{
-    static_assert(std::is_pointer_v<Instance>, "Pointers Only!");
-    return reinterpret_cast<const QofInstance*>(inst);
-}
-
 static constexpr const char* DIALOG_OPTIONS_CM_CLASS{"dialog-options"};
 static constexpr const char* GNC_PREFS_GROUP{"dialogs.options"};
 
@@ -137,45 +134,64 @@ enum page_tree
     NUM_COLUMNS
 };
 
-class GncOptionGtkUIItem : public GncOptionUIItem
+//Init the class static.
+std::vector<WidgetCreateFunc> GncOptionUIFactory::s_registry{static_cast<size_t>(GncOptionUIType::MAX_VALUE)};
+
+void
+GncOptionUIFactory::set_func(GncOptionUIType type, WidgetCreateFunc func)
 {
-public:
-    GncOptionGtkUIItem(GtkWidget* widget, GncOptionUIType type) :
-        GncOptionUIItem(type),
-        m_widget{static_cast<GtkWidget*>(g_object_ref(widget))} {}
-    GncOptionGtkUIItem(const GncOptionGtkUIItem& item) :
+    s_registry[static_cast<size_t>(type)] = func;
+}
+
+GtkWidget*
+GncOptionUIFactory::create(GncOption& option, GtkGrid* page, GtkLabel* name,
+                     char* description, GtkWidget** enclosing, bool* packed)
+{
+    auto type{option.get_ui_type()};
+    auto func{s_registry[static_cast<size_t>(type)]};
+    if (func)
+        return func(option, page, name, description, enclosing, packed);
+    return nullptr;
+}
+
+GncOptionGtkUIItem::GncOptionGtkUIItem(GtkWidget* widget,
+                                       GncOptionUIType type) :
+    GncOptionUIItem{type},
+    m_widget{static_cast<GtkWidget*>(g_object_ref(widget))} {}
+
+GncOptionGtkUIItem::GncOptionGtkUIItem(const GncOptionGtkUIItem& item) :
         GncOptionUIItem{item.get_ui_type()},
         m_widget{static_cast<GtkWidget*>(g_object_ref(item.get_widget()))} {}
-    GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default;
-    virtual ~GncOptionGtkUIItem() override
-    {
-        if (m_widget)
-            g_object_unref(m_widget);
-    }
-    void clear_ui_item() override
+
+GncOptionGtkUIItem::~GncOptionGtkUIItem()
+{
+    if (m_widget)
+        g_object_unref(m_widget);
+}
+
+void
+GncOptionGtkUIItem::clear_ui_item()
+{
+    if (m_widget)
+        g_object_unref(m_widget);
+    m_widget = nullptr;
+}
+
+void
+GncOptionGtkUIItem::set_widget(GtkWidget* widget)
+{
+    if (get_ui_type() == GncOptionUIType::INTERNAL)
     {
-        if (m_widget)
-            g_object_unref(m_widget);
-        m_widget = nullptr;
+        std::string error{"INTERNAL option, setting the UI item forbidden."};
+        throw std::logic_error(std::move(error));
     }
-    void set_widget(GtkWidget* widget)
-    {
-        if (get_ui_type() == GncOptionUIType::INTERNAL)
-        {
-            std::string error{"INTERNAL option, setting the UI item forbidden."};
-            throw std::logic_error(std::move(error));
-        }
 
-        if (m_widget)
-            g_object_unref(m_widget);
+    if (m_widget)
+        g_object_unref(m_widget);
 
-        m_widget = static_cast<GtkWidget*>(g_object_ref(widget));
-    }
-    virtual GtkWidget* const get_widget() const { return m_widget; }
+    m_widget = static_cast<GtkWidget*>(g_object_ref(widget));
+}
 
-private:
-    GtkWidget* m_widget;
-};
 
 static GNCOptionWinCallback global_help_cb = NULL;
 gpointer global_help_cb_data = NULL;
@@ -249,18 +265,19 @@ gnc_options_dialog_changed (GNCOptionWin *win)
     dialog_changed_internal (win->window, TRUE);
 }
 
-static void
-widget_changed_cb(GtkWidget *widget, GncOption* option)
+void
+gnc_option_changed_widget_cb(GtkWidget *widget, GncOption* option)
 {
     if (!option) return;
     const_cast<GncOptionUIItem*>(option->get_ui_item())->set_dirty(true);
 }
 
 void
-option_changed_cb(GtkWidget *dummy, GncOption* option)
+gnc_option_changed_option_cb(GtkWidget *dummy, GncOption* option)
 {
     if (!option) return;
-    option->set_ui_item_from_option();
+    auto widget{gnc_option_get_gtk_widget(option)};
+    gnc_option_changed_widget_cb(widget, option);
 }
 
 
@@ -290,9 +307,6 @@ create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**,
     return nullptr;
 }
 
-static GtkWidget* option_widget_factory(GncOption& option, GtkGrid* page,
-                                        GtkLabel* name, char* description,
-                                        GtkWidget** enclosing, bool* packed);
 static void
 gnc_option_set_ui_widget(GncOption& option, GtkGrid *page_box, gint grid_row)
 {
@@ -326,8 +340,9 @@ gnc_option_set_ui_widget(GncOption& option, GtkGrid *page_box, gint grid_row)
         documentation = nullptr;
 
     name_label = GTK_LABEL(gtk_label_new (name));
-    auto widget = option_widget_factory(option, page_box, name_label,
-                                        documentation, &enclosing, &packed);
+    auto widget = GncOptionUIFactory::create(option, page_box, name_label,
+                                             documentation, &enclosing,
+                                             &packed);
     /* attach the name label to the first column of the grid and
        align to the end */
     gtk_grid_attach (GTK_GRID(page_box), GTK_WIDGET(name_label),
@@ -930,7 +945,7 @@ create_option_widget<GncOptionUIType::BOOLEAN> (GncOption& option,
     auto ui_item{std::make_unique<GncGtkBooleanUIItem>(GncGtkBooleanUIItem{widget})};
 
     g_signal_connect(G_OBJECT(widget), "toggled",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
@@ -976,7 +991,7 @@ create_option_widget<GncOptionUIType::STRING> (GncOption& option,
     auto ui_item{std::make_unique<GncGtkStringUIItem>(widget)};
 
     g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
@@ -1032,7 +1047,7 @@ create_option_widget<GncOptionUIType::TEXT> (GncOption& option, GtkGrid *page_bo
     auto ui_item{std::make_unique<GncGtkTextUIItem>(widget)};
     auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
     g_signal_connect(G_OBJECT(text_buffer), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
@@ -1072,7 +1087,7 @@ create_option_widget<GncOptionUIType::CURRENCY> (GncOption& option, GtkGrid *pag
     auto widget = gnc_currency_edit_new();
     auto ui_item{std::make_unique<GncGtkCurrencyUIItem>(widget)};
     g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
@@ -1117,7 +1132,7 @@ create_option_widget<GncOptionUIType::COMMODITY> (GncOption& option, GtkGrid *pa
     auto ui_item{std::make_unique<GncGtkCommodityUIItem>(widget)};
 
     g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     option.set_ui_item(std::move(ui_item));
     option.set_ui_item_from_option();
 
@@ -1126,7 +1141,7 @@ create_option_widget<GncOptionUIType::COMMODITY> (GncOption& option, GtkGrid *pa
                                     documentation);
 
     g_signal_connect(G_OBJECT(GNC_GENERAL_SELECT(widget)->entry), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_widget_show_all(*enclosing);
@@ -1164,7 +1179,7 @@ create_multichoice_widget(GncOption& option)
     g_object_unref(store);
 
     g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     return widget;
 }
@@ -1243,7 +1258,7 @@ AbsoluteDateEntry::AbsoluteDateEntry(GncOption& option) :
 {
     auto entry = GNC_DATE_EDIT(m_entry)->date_entry;
     g_signal_connect(G_OBJECT(entry), "changed",
-                     G_CALLBACK(option_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_option_cb), &option);
 }
 
 void
@@ -1300,7 +1315,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) :
     g_object_unref(store);
 
     g_signal_connect(G_OBJECT(m_entry), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 }
 
 void
@@ -1448,7 +1463,7 @@ date_set_absolute_cb(GtkWidget *widget, gpointer data1)
     if (auto date_ui = dynamic_cast<const GncOptionDateUIItem* const>(ui_item))
     {
         const_cast<GncOptionDateUIItem*>(date_ui)->get_entry()->toggle_relative(true);
-        option_changed_cb(widget, option);
+        gnc_option_changed_option_cb(widget, option);
     }
 }
 
@@ -1460,7 +1475,7 @@ date_set_relative_cb(GtkWidget *widget, gpointer data1)
     if (auto date_ui = dynamic_cast<const GncOptionDateUIItem* const>(ui_item))
     {
         const_cast<GncOptionDateUIItem*>(date_ui)->get_entry()->toggle_relative(false);
-        option_changed_cb(widget, option);
+        gnc_option_changed_option_cb(widget, option);
     }
 }
 
@@ -1575,7 +1590,7 @@ account_select_all_cb(GtkWidget *widget, gpointer data)
     gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
     gtk_tree_selection_select_all(selection);
-    widget_changed_cb(widget, option);
+    gnc_option_changed_widget_cb(widget, option);
 }
 
 static void
@@ -1588,7 +1603,7 @@ account_clear_all_cb(GtkWidget *widget, gpointer data)
     tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
     gtk_tree_selection_unselect_all(selection);
-    widget_changed_cb(widget, option);
+    gnc_option_changed_widget_cb(widget, option);
 }
 
 static void
@@ -1628,7 +1643,7 @@ show_hidden_toggled_cb(GtkWidget *widget, GncOption* option)
     gnc_tree_view_account_get_view_info (tree_view, &avi);
     avi.show_hidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
     gnc_tree_view_account_set_view_info (tree_view, &avi);
-    widget_changed_cb(widget, option);
+    gnc_option_changed_widget_cb(widget, option);
 }
 
 class GncGtkAccountListUIItem : public GncOptionGtkUIItem
@@ -1819,7 +1834,7 @@ create_option_widget<GncOptionUIType::ACCOUNT_LIST>(GncOption& option,
     auto widget = gnc_option_get_gtk_widget(&option);
     auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
     g_signal_connect(G_OBJECT(selection), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     //  gtk_clist_set_row_height(GTK_CLIST(value), 0);
     //  gtk_widget_set_size_request(value, -1, GTK_CLIST(value)->row_height * 10);
@@ -1861,7 +1876,7 @@ create_option_widget<GncOptionUIType::ACCOUNT_SEL> (GncOption& option,
                                      acct_type_list, NULL);
     g_list_free(acct_type_list);
     g_signal_connect(widget, "account_sel_changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
 
 // gnc_account_sel doesn't emit a changed signal
@@ -1879,7 +1894,7 @@ static void
 list_changed_cb(GtkTreeSelection *selection, GncOption* option)
 {
     GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option));
-    widget_changed_cb(GTK_WIDGET(view), option);
+    gnc_option_changed_widget_cb(GTK_WIDGET(view), option);
 }
 
 static void
@@ -1892,7 +1907,7 @@ list_select_all_cb(GtkWidget *widget, gpointer data)
     view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option));
     selection = gtk_tree_view_get_selection(view);
     gtk_tree_selection_select_all(selection);
-    widget_changed_cb(GTK_WIDGET(view), option);
+    gnc_option_changed_widget_cb(GTK_WIDGET(view), option);
 }
 
 static void
@@ -1905,7 +1920,7 @@ list_clear_all_cb(GtkWidget *widget, gpointer data)
     view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option));
     selection = gtk_tree_view_get_selection(view);
     gtk_tree_selection_unselect_all(selection);
-    widget_changed_cb(GTK_WIDGET(view), option);
+    gnc_option_changed_widget_cb(GTK_WIDGET(view), option);
 }
 
 static void
@@ -2133,7 +2148,7 @@ create_option_widget<GncOptionUIType::NUMBER_RANGE> (GncOption& option,
     option.set_ui_item_from_option();
 
     g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     gtk_box_pack_start(GTK_BOX(*enclosing), GTK_WIDGET(widget),
                        FALSE, FALSE, 0);
@@ -2185,7 +2200,7 @@ create_option_widget<GncOptionUIType::COLOR> (GncOption& option, GtkGrid *page_b
     option.set_ui_item_from_option();
 
     g_signal_connect(G_OBJECT(widget), "color-set",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_widget_show_all(*enclosing);
@@ -2229,7 +2244,7 @@ create_option_widget<GncOptionUIType::FONT> (GncOption& option, GtkGrid *page_bo
     option.set_ui_item_from_option();
 
     g_signal_connect(G_OBJECT(widget), "font-set",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_widget_show_all(*enclosing);
@@ -2331,7 +2346,7 @@ create_option_widget<GncOptionUIType::PIXMAP> (GncOption& option,
                  "preview-widget", gtk_image_new(),
                  (char *)NULL);
     g_signal_connect(G_OBJECT (widget), "selection-changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     g_signal_connect(G_OBJECT (widget), "selection-changed",
                      G_CALLBACK(change_image_cb), &option);
     g_signal_connect(G_OBJECT (widget), "update-preview",
@@ -2371,7 +2386,7 @@ radiobutton_set_cb(GtkWidget *w, gpointer data)
 
     g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index",
                        GINT_TO_POINTER(new_value));
-    widget_changed_cb(widget, option);
+    gnc_option_changed_widget_cb(widget, option);
 }
 
 class GncGtkRadioButtonUIItem : public GncOptionGtkUIItem
@@ -2510,7 +2525,7 @@ create_option_widget<GncOptionUIType::DATE_FORMAT> (GncOption& option,
     option.set_ui_item_from_option();
 
     g_signal_connect(G_OBJECT(*enclosing), "format_changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
     gtk_widget_show_all(*enclosing);
     return *enclosing;
 }
@@ -2547,14 +2562,14 @@ static void
 gnc_rd_option_px_set_cb(GtkWidget *widget, GncOption* option)
 {
     option->set_alternate(true);
-    option_changed_cb(widget, option);
+    gnc_option_changed_option_cb(widget, option);
 }
 
 static void
 gnc_rd_option_p_set_cb(GtkWidget *widget, GncOption* option)
 {
     option->set_alternate(false);
-    option_changed_cb(widget, option);
+    gnc_option_changed_option_cb(widget, option);
 }
 
 class GncGtkPlotSizeUIItem : public GncOptionGtkUIItem
@@ -2625,7 +2640,7 @@ create_option_widget<GncOptionUIType::PLOT_SIZE> (GncOption& option,
     auto value_px = create_range_spinner(option);
 
     g_signal_connect(G_OBJECT(value_px), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     adj_percent = GTK_ADJUSTMENT(gtk_adjustment_new(1, 10, 100, 1, 5.0, 0));
     value_percent = gtk_spin_button_new(adj_percent, 1, 0);
@@ -2635,7 +2650,7 @@ create_option_widget<GncOptionUIType::PLOT_SIZE> (GncOption& option,
     gtk_widget_set_sensitive(value_percent, FALSE);
 
     g_signal_connect(G_OBJECT(value_percent), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     px_butt = gtk_radio_button_new_with_label(NULL, _("Pixels"));
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(px_butt), TRUE);
@@ -2721,120 +2736,58 @@ create_option_widget<GncOptionUIType::BUDGET> (GncOption& option, GtkGrid *page_
 
     /* Maybe connect destroy handler for tree model here? */
     g_signal_connect(G_OBJECT(widget), "changed",
-                     G_CALLBACK(widget_changed_cb), &option);
+                     G_CALLBACK(gnc_option_changed_widget_cb), &option);
 
     gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
     gtk_widget_show_all(*enclosing);
     return widget;
 }
 
-static GtkWidget*
-option_widget_factory(GncOption& option, GtkGrid* page, GtkLabel* name,
-                      char* description, GtkWidget** enclosing, bool* packed)
-{
-    switch(option.get_ui_type())
-    {
-        case INTERNAL:
-            return nullptr;
-        case BOOLEAN:
-            return create_option_widget<BOOLEAN>(option, page, name,
-                                                 description, enclosing,
-                                                 packed);
-        case STRING:
-            return create_option_widget<STRING>(option, page, name, description,
-                                                enclosing, packed);
-        case TEXT:
-            return create_option_widget<TEXT>(option, page, name, description,
-                                              enclosing, packed);
-        case CURRENCY:
-            return create_option_widget<CURRENCY>(option, page, name,
-                                                  description, enclosing,
-                                                  packed);
-        case COMMODITY:
-            return create_option_widget<COMMODITY>(option, page, name,
-                                                   description, enclosing,
-                                                   packed);
-        case MULTICHOICE:
-            return create_option_widget<MULTICHOICE>(option, page, name,
-                                                     description, enclosing,
-                                                     packed);
-        case DATE_ABSOLUTE:
-            return create_option_widget<DATE_ABSOLUTE>(option, page, name,
-                                                       description, enclosing,
-                                                       packed);
-        case DATE_RELATIVE:
-            return create_option_widget<DATE_RELATIVE>(option, page, name,
-                                                       description,
-                                                       enclosing, packed);
-        case DATE_BOTH:
-            return create_option_widget<DATE_BOTH>(option, page, name,
-                                                   description, enclosing,
-                                                   packed);
-        case ACCOUNT_LIST:
-            return create_option_widget<ACCOUNT_LIST>(option, page, name,
-                                                      description, enclosing,
-                                                      packed);
-        case ACCOUNT_SEL:
-            return create_option_widget<ACCOUNT_SEL>(option, page, name,
-                                                     description, enclosing,
-                                                     packed);
-        case LIST:
-            return create_option_widget<LIST>(option, page, name, description,
-                                              enclosing, packed);
-        case NUMBER_RANGE:
-            return create_option_widget<NUMBER_RANGE>(option, page, name,
-                                                      description, enclosing,
-                                                      packed);
-        case COLOR:
-            return create_option_widget<COLOR>(option, page, name, description,
-                                               enclosing, packed);
-        case FONT:
-            return create_option_widget<FONT>(option, page, name, description,
-                                              enclosing, packed);
-        case PLOT_SIZE:
-            return create_option_widget<PLOT_SIZE>(option, page, name,
-                                                   description, enclosing,
-                                                   packed);
-        case BUDGET:
-            return create_option_widget<BUDGET>(option, page, name, description,
-                                                enclosing, packed);
-        case PIXMAP:
-            return create_option_widget<PIXMAP>(option, page, name, description,
-                                                enclosing, packed);
-        case RADIOBUTTON:
-            return create_option_widget<RADIOBUTTON>(option, page, name,
-                                                     description, enclosing,
-                                                     packed);
-        case DATE_FORMAT:
-            return create_option_widget<DATE_FORMAT>(option, page, name,
-                                                     description, enclosing,
-                                                     packed);
-        case OWNER:
-            return create_option_widget<OWNER>(option, page, name, description,
-                                               enclosing, packed);
-        case CUSTOMER:
-            return create_option_widget<CUSTOMER>(option, page, name,
-                                                  description, enclosing,
-                                                  packed);
-        case VENDOR:
-            return create_option_widget<VENDOR>(option, page, name, description,
-                                                enclosing, packed);
-        case EMPLOYEE:
-            return create_option_widget<EMPLOYEE>(option, page, name,
-                                                  description, enclosing,
-                                                  packed);
-        case INVOICE:
-            return create_option_widget<INVOICE>(option, page, name,
-                                                 description, enclosing,
-                                                 packed);
-        case TAX_TABLE:
-            return create_option_widget<TAX_TABLE>(option, page, name,
-                                                   description, enclosing,
-                                                   packed);
-        case QUERY:
-            return create_option_widget<QUERY>(option, page, name, description,
-                                               enclosing, packed);
-    }
+void
+gnc_options_ui_initialize(void)
+{
+    GncOptionUIFactory::set_func(GncOptionUIType::BOOLEAN,
+                                 create_option_widget<GncOptionUIType::BOOLEAN>);
+    GncOptionUIFactory::set_func(GncOptionUIType::STRING,
+                                 create_option_widget<GncOptionUIType::STRING>);
+    GncOptionUIFactory::set_func(GncOptionUIType::TEXT,
+                                 create_option_widget<GncOptionUIType::TEXT>);
+    GncOptionUIFactory::set_func(GncOptionUIType::CURRENCY,
+                                 create_option_widget<GncOptionUIType::CURRENCY>);
+    GncOptionUIFactory::set_func(GncOptionUIType::COMMODITY,
+                                 create_option_widget<GncOptionUIType::COMMODITY>);
+    GncOptionUIFactory::set_func(GncOptionUIType::MULTICHOICE,
+                                 create_option_widget<GncOptionUIType::MULTICHOICE>);
+    GncOptionUIFactory::set_func(GncOptionUIType::DATE_ABSOLUTE,
+                                 create_option_widget<GncOptionUIType::DATE_ABSOLUTE>);
+    GncOptionUIFactory::set_func(GncOptionUIType::DATE_RELATIVE,
+                                 create_option_widget<GncOptionUIType::DATE_RELATIVE>);
+    GncOptionUIFactory::set_func(GncOptionUIType::DATE_BOTH,
+                                 create_option_widget<GncOptionUIType::DATE_BOTH>);
+    GncOptionUIFactory::set_func(GncOptionUIType::ACCOUNT_LIST,
+                                 create_option_widget<GncOptionUIType::ACCOUNT_LIST>);
+    GncOptionUIFactory::set_func(GncOptionUIType::ACCOUNT_SEL,
+                                 create_option_widget<GncOptionUIType::ACCOUNT_SEL>);
+    GncOptionUIFactory::set_func(GncOptionUIType::LIST,
+                                 create_option_widget<GncOptionUIType::LIST>);
+    GncOptionUIFactory::set_func(GncOptionUIType::NUMBER_RANGE,
+                                 create_option_widget<GncOptionUIType::NUMBER_RANGE>);
+    GncOptionUIFactory::set_func(GncOptionUIType::COLOR,
+                                 create_option_widget<GncOptionUIType::COLOR>);
+    GncOptionUIFactory::set_func(GncOptionUIType::FONT,
+                                 create_option_widget<GncOptionUIType::FONT>);
+    GncOptionUIFactory::set_func(GncOptionUIType::PLOT_SIZE,
+                                 create_option_widget<GncOptionUIType::PLOT_SIZE>);
+    GncOptionUIFactory::set_func(GncOptionUIType::BUDGET,
+                                 create_option_widget<GncOptionUIType::BUDGET>);
+    GncOptionUIFactory::set_func(GncOptionUIType::PIXMAP,
+                                 create_option_widget<GncOptionUIType::PIXMAP>);
+    GncOptionUIFactory::set_func(GncOptionUIType::RADIOBUTTON,
+                                 create_option_widget<GncOptionUIType::RADIOBUTTON>);
+    GncOptionUIFactory::set_func(GncOptionUIType::DATE_FORMAT,
+                                 create_option_widget<GncOptionUIType::DATE_FORMAT>);
+
+
 }
 
 void
@@ -2877,9 +2830,3 @@ gnc_option_get_gtk_widget (GncOption *option)
 {
     return nullptr;
 }
-
-void
-gnc_options_ui_initialize (void)
-{
-    return;
-}
diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h
index 9bac92ac0..82d0fdd68 100644
--- a/gnucash/gnome-utils/dialog-options.h
+++ b/gnucash/gnome-utils/dialog-options.h
@@ -97,7 +97,13 @@ void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win);
 /** Set the initial values of new book options to values specified in user
  *  preferences.
  */
-void gnc_options_dialog_set_new_book_option_values (GNCOptionDB *odb);
+void gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb);
+
+/**
+ * Initialize the option UI elements.
+ */
+void gnc_options_ui_initialize(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp
new file mode 100644
index 000000000..7daef2351
--- /dev/null
+++ b/gnucash/gnome-utils/dialog-options.hpp
@@ -0,0 +1,77 @@
+/********************************************************************\
+ * dialog-options.hpp -- GNOME option handling                      *
+ * Copyright (C) 1998-2000 Linas Vepstas                            *
+ * Copyright (c) 2006 David Hampton <hampton at employees.org>         *
+ * Copyright (c) 2011 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                   *
+\********************************************************************/
+
+#ifndef GNC_DIALOG_OPTIONS_HPP_
+#define GNC_DIALOG_OPTIONS_HPP_
+
+#include <vector>
+
+#include <gnc-option-uitype.hpp>
+#include <gnc-option-ui.hpp>
+#include "dialog-options.h"
+
+typedef GtkWidget* (*WidgetCreateFunc)(GncOption&, GtkGrid*, GtkLabel*, char*,
+                                       GtkWidget**, bool*);
+class GncOptionUIFactory
+{
+public:
+    static void set_func(GncOptionUIType type, WidgetCreateFunc func);
+    static GtkWidget* create(GncOption&, GtkGrid*, GtkLabel*, char*,
+                             GtkWidget**, bool*);
+private:
+    static std::vector<WidgetCreateFunc> s_registry;
+};
+
+class GncOptionGtkUIItem : public GncOptionUIItem
+{
+public:
+    GncOptionGtkUIItem(GtkWidget* widget, GncOptionUIType type);
+    GncOptionGtkUIItem(const GncOptionGtkUIItem& item);
+    GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default;
+    virtual ~GncOptionGtkUIItem() override;
+    void clear_ui_item() override;
+    void set_widget(GtkWidget* widget);
+    virtual GtkWidget* const get_widget() const { return m_widget; }
+    static WidgetCreateFunc option_widget_factory(GncOption& option,
+                                                  GtkGrid* page,
+                                                  GtkLabel* name,
+                                                  char* description,
+                                                  GtkWidget** enclosing,
+                                                  bool* packed);
+private:
+    GtkWidget* m_widget;
+};
+
+template<GncOptionUIType type> GtkWidget*
+create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**,
+                     bool*);
+
+template <typename Instance> inline const QofInstance*
+qof_instance_cast(Instance inst)
+{
+    static_assert(std::is_pointer_v<Instance>, "Pointers Only!");
+    return reinterpret_cast<const QofInstance*>(inst);
+}
+
+#endif // GNC_DIALOG_OPTIONS_HPP_
diff --git a/gnucash/gnome/CMakeLists.txt b/gnucash/gnome/CMakeLists.txt
index 64096ff88..6cea3d0c8 100644
--- a/gnucash/gnome/CMakeLists.txt
+++ b/gnucash/gnome/CMakeLists.txt
@@ -72,7 +72,7 @@ set (gnc_gnome_SOURCES
   assistant-hierarchy.c
   assistant-loan.cpp
   assistant-stock-split.c
-  business-options-gnome.c
+  business-options-gnome.cpp
   business-urls.c
   business-gnome-utils.c
   dialog-doclink.c
diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp
index a9bc680dd..2e26b992a 100644
--- a/gnucash/gnome/business-options-gnome.cpp
+++ b/gnucash/gnome/business-options-gnome.cpp
@@ -22,464 +22,195 @@
  * Boston, MA  02110-1301,  USA       gnu at gnu.org
  */
 
+#include <libguile.h>
+
+extern "C"
+{
 #include <config.h>
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include "swig-runtime.h"
 #include "guile-mappings.h"
-#include <libguile.h>
 
 #include "gnc-ui-util.h"
 #include "dialog-utils.h"
 #include "qof.h"
-#include "option-util.h"
 #include "gnc-general-search.h"
 
-#include "dialog-options.h"
 #include "business-options-gnome.h"
 #include "business-gnome-utils.h"
 #include "dialog-invoice.h"
-
-#define FUNC_NAME G_STRFUNC
-/* To be consolidated into dialog-options.cpp later. */
-#if 0
-static GtkWidget *
-create_owner_widget (GncOption *option, GncOwnerType type, GtkWidget *hbox)
-{
-    GtkWidget *widget;
-    GncOwner owner;
-
-    switch (type)
-    {
-    case GNC_OWNER_CUSTOMER:
-        gncOwnerInitCustomer (&owner, NULL);
-        break;
-    case GNC_OWNER_VENDOR:
-        gncOwnerInitVendor (&owner, NULL);
-        break;
-    case GNC_OWNER_EMPLOYEE:
-        gncOwnerInitEmployee (&owner, NULL);
-        break;
-    case GNC_OWNER_JOB:
-        gncOwnerInitJob (&owner, NULL);
-        break;
-    default:
-        return NULL;
-    }
-
-    widget = gnc_owner_select_create (NULL, hbox,
-                                      gnc_get_current_book (), &owner);
-    gnc_option_set_widget (option, widget);
-
-    g_signal_connect (G_OBJECT (widget), "changed",
-                      G_CALLBACK (gnc_option_changed_option_cb), option);
-
-    return widget;
 }
 
-static GtkWidget *
-make_name_label (char *name)
-{
-    GtkWidget *label = gtk_label_new (name);
-    gnc_label_set_alignment (label, 1.0, 0.5);
-
-    return label;
-}
+#include <iostream>
+#include <sstream>
+#include <exception>
 
-/********************************************************************/
-/* "Owner" Option functions */
+#include <dialog-options.hpp>
+#include <gnc-option.hpp>
+#define FUNC_NAME G_STRFUNC
 
 
-static GncOwnerType
-get_owner_type_from_option (GncOption *option)
+static inline GncOwnerType
+ui_type_to_owner_type(GncOptionUIType ui_type)
 {
-    SCM odata = gnc_option_get_option_data (option);
+    if (ui_type == GncOptionUIType::CUSTOMER)
+        return GNC_OWNER_CUSTOMER;
+    if (ui_type == GncOptionUIType::VENDOR)
+        return GNC_OWNER_VENDOR;
+    if (ui_type == GncOptionUIType::EMPLOYEE)
+        return GNC_OWNER_EMPLOYEE;
 
-    /* The option data is enum-typed.  It's just the enum value. */
-    return (GncOwnerType) scm_to_int(odata);
+    std::ostringstream oss;
+    oss << "UI type " << ui_type << " could not be converted to owner type\n";
+    throw std::invalid_argument(oss.str());
 }
 
 
-/* Function to set the UI widget based upon the option */
-static GtkWidget *
-owner_set_widget (GncOption *option, GtkGrid *page_box,
-                  GtkLabel *name_label, char *documentation,
-                  /* Return values */
-                  GtkWidget **enclosing, gboolean *packed)
+class GncGtkOwnerUIItem : public GncOptionGtkUIItem
 {
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
-
-    value = create_owner_widget (option, get_owner_type_from_option (option),
-                                 *enclosing);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-/* Function to set the UI Value for a particular option */
-static gboolean
-owner_set_value (GncOption *option, gboolean use_default,
-                 GtkWidget *widget, SCM value)
-{
-    GncOwner owner_def;
-    GncOwner *owner;
-
-    if (!SWIG_IsPointer (value))
-        scm_misc_error("business_options:owner_set_value",
-                       "SCM is not a wrapped pointer.", value);
-
-    owner = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncOwner"), 1, 0);
-
-    /* XXX: should we verify that the owner type is correct? */
-    if (!owner)
+public:
+    GncGtkOwnerUIItem(GtkWidget* widget, GncOptionUIType type) :
+        GncOptionGtkUIItem(widget, type) {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
     {
-        owner_def.type = get_owner_type_from_option (option);
-        owner_def.owner.undefined = NULL;
-        owner = &owner_def;
+        GncOwner owner{};
+        owner.type = ui_type_to_owner_type(option.get_ui_type());
+        owner.owner.undefined = (void*)option.get_value<const QofInstance*>();
+        gnc_owner_set_owner(get_widget(), &owner);
     }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        GncOwner owner{};
+        gnc_owner_get_owner(get_widget(), &owner);
+        if (owner.type == ui_type_to_owner_type(option.get_ui_type()))
+            option.set_value(static_cast<const QofInstance*>(owner.owner.undefined));
+    }
+};
 
-    widget = gnc_option_get_gtk_widget (option);
-    gnc_owner_set_owner (widget, owner);
-    return FALSE;
-}
-
-/* Function to get the UI Value for a particular option */
-static SCM
-owner_get_value (GncOption *option, GtkWidget *widget)
-{
-    static GncOwner owner;	/* XXX: might cause trouble? */
-    GncOwnerType type;
-
-    type = get_owner_type_from_option (option);
-    owner.type = type;
-    gnc_owner_get_owner (widget, &owner);
-
-    return SWIG_NewPointerObj(&owner, SWIG_TypeQuery("_p__gncOwner"), 0);
-}
-
-
-/********************************************************************/
-/* "Customer" Option functions */
-
-
-/* Function to set the UI widget based upon the option */
-static GtkWidget *
-customer_set_widget (GncOption *option, GtkGrid *page_box,
-                     GtkLabel *name_label, char *documentation,
-                     /* Return values */
-                     GtkWidget **enclosing, gboolean *packed)
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::OWNER>(GncOption& option,
+                                                GtkGrid *page_box,
+                                                GtkLabel *name_label,
+                                                char *documentation,
+                                                /* Return values */
+                                                GtkWidget **enclosing,
+                                                bool *packed)
 {
-    GtkWidget *value;
-
     *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
     gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
 
-    value = create_owner_widget (option, GNC_OWNER_CUSTOMER, *enclosing);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-/* Function to set the UI Value for a particular option */
-static gboolean
-customer_set_value (GncOption *option, gboolean use_default,
-                    GtkWidget *widget, SCM value)
-{
-    GncOwner owner;
-    GncCustomer *customer;
-
-    if (!SWIG_IsPointer (value))
-        scm_misc_error("business_options:customer_set_value",
-                       "SCM is not a wrapped pointer.", value);
-
-    customer = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncCustomer"), 1, 0);
-    gncOwnerInitCustomer (&owner, customer);
+    GncOwner owner{};
+    auto ui_type{option.get_ui_type()};
+    owner.type = ui_type_to_owner_type(ui_type);
+    auto widget = gnc_owner_select_create(nullptr, *enclosing,
+                                          gnc_get_current_book(),
+                                          &owner);
 
-    widget = gnc_option_get_gtk_widget (option);
-    gnc_owner_set_owner (widget, &owner);
-    return FALSE;
+    option.set_ui_item(std::make_unique<GncGtkOwnerUIItem>(widget, ui_type));
+    option.set_ui_item_from_option();
+    g_signal_connect (G_OBJECT (widget), "changed",
+                      G_CALLBACK (gnc_option_changed_widget_cb), &option);
+    gtk_widget_show_all(*enclosing);
+    return widget;
 }
 
-/* Function to get the UI Value for a particular option */
-static SCM
-customer_get_value (GncOption *option, GtkWidget *widget)
+class GncGtkInvoiceUIItem : public GncOptionGtkUIItem
 {
-    GncOwner owner;
-
-    gnc_owner_get_owner (widget, &owner);
-    return SWIG_NewPointerObj(owner.owner.undefined,
-                              SWIG_TypeQuery("_p__gncCustomer"), 0);
-}
-
-
-/********************************************************************/
-/* "Vendor" Option functions */
-
+public:
+    GncGtkInvoiceUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem(widget, GncOptionUIType::INVOICE) {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        gnc_general_search_set_selected(GNC_GENERAL_SEARCH(get_widget()),
+                                        GNC_INVOICE(option.get_value<const QofInstance*>()));
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        option.set_value(qof_instance_cast(gnc_general_search_get_selected(GNC_GENERAL_SEARCH(get_widget()))));
+    }
+};
 
-/* Function to set the UI widget based upon the option */
-static GtkWidget *
-vendor_set_widget (GncOption *option, GtkGrid *page_box,
-                   GtkLabel *name_label, char *documentation,
-                   /* Return values */
-                   GtkWidget **enclosing, gboolean *packed)
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::INVOICE>(GncOption& option,
+                                               GtkGrid *page_box,
+                                               GtkLabel *name_label,
+                                               char *documentation,
+                                               /* Return values */
+                                               GtkWidget **enclosing,
+                                               bool *packed)
 {
-    GtkWidget *value;
-
     *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
     gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
-
-    value = create_owner_widget (option, GNC_OWNER_VENDOR, *enclosing);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-/* Function to set the UI Value for a particular option */
-static gboolean
-vendor_set_value (GncOption *option, gboolean use_default,
-                  GtkWidget *widget, SCM value)
-{
-    GncOwner owner;
-    GncVendor *vendor;
-
-    if (!SWIG_IsPointer (value))
-        scm_misc_error("business_options:vendor_set_value",
-                       "SCM is not a wrapped pointer.", value);
-
-    vendor = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncVendor"), 1, 0);
-    gncOwnerInitVendor (&owner, vendor);
-
-    widget = gnc_option_get_gtk_widget (option);
-    gnc_owner_set_owner (widget, &owner);
-    return FALSE;
+    auto widget{gnc_invoice_select_create(*enclosing, gnc_get_current_book(),
+                                          nullptr, nullptr, nullptr)};
+
+    option.set_ui_item(std::make_unique<GncGtkInvoiceUIItem>(widget));
+    option.set_ui_item_from_option();
+    g_signal_connect(G_OBJECT (widget), "changed",
+                     G_CALLBACK (gnc_option_changed_widget_cb), &option);
+    gtk_widget_show_all(*enclosing);
+    return widget;
 }
 
-/* Function to get the UI Value for a particular option */
-static SCM
-vendor_get_value (GncOption *option, GtkWidget *widget)
+class GncGtkTaxTableUIItem : public GncOptionGtkUIItem
 {
-    GncOwner owner;
-
-    gnc_owner_get_owner (widget, &owner);
-    return SWIG_NewPointerObj(owner.owner.undefined,
-                              SWIG_TypeQuery("_p__gncVendor"), 0);
-}
-
-/********************************************************************/
-/* "Employee" Option functions */
-
+public:
+    GncGtkTaxTableUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem(widget, GncOptionUIType::TAX_TABLE) {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+    }
+};
 
-/* Function to set the UI widget based upon the option */
-static GtkWidget *
-employee_set_widget (GncOption *option, GtkGrid *page_box,
-                     GtkLabel *name_label, char *documentation,
-                     /* Return values */
-                     GtkWidget **enclosing, gboolean *packed)
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::TAX_TABLE>(GncOption& option,
+                                                 GtkGrid *page_box,
+                                                 GtkLabel *name_label,
+                                                 char *documentation,
+                                                 /* Return values */
+                                                 GtkWidget **enclosing,
+                                                 bool *packed)
 {
-    GtkWidget *value;
-
     *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
     gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
-
-    value = create_owner_widget (option, GNC_OWNER_EMPLOYEE, *enclosing);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-/* Function to set the UI Value for a particular option */
-static gboolean
-employee_set_value (GncOption *option, gboolean use_default,
-                    GtkWidget *widget, SCM value)
-{
-    GncOwner owner;
-    GncEmployee *employee;
-
-    if (!SWIG_IsPointer (value))
-        scm_misc_error("business_options:employee_set_value",
-                       "SCM is not a wrapped pointer.", value);
-
-    employee = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncEmployee"), 1, 0);
-    gncOwnerInitEmployee (&owner, employee);
-
-    widget = gnc_option_get_gtk_widget (option);
-    gnc_owner_set_owner (widget, &owner);
-    return FALSE;
-}
-
-/* Function to get the UI Value for a particular option */
-static SCM
-employee_get_value (GncOption *option, GtkWidget *widget)
-{
-    GncOwner owner;
-
-    gnc_owner_get_owner (widget, &owner);
-
-    return SWIG_NewPointerObj(owner.owner.undefined,
-                              SWIG_TypeQuery("_p__gncEmployee"), 0);
-}
-
-/********************************************************************/
-/* "Invoice" Option functions */
-
-
-static GtkWidget *
-create_invoice_widget (GncOption *option, GtkWidget *hbox)
-{
-    GtkWidget *widget;
-
-    /* No owner or starting invoice here, but that's okay. */
-    widget = gnc_invoice_select_create (hbox, gnc_get_current_book(),
-                                        NULL, NULL, NULL);
-
-    gnc_option_set_widget (option, widget);
+    constexpr const char* glade_file{"business-options-gnome.glade"};
+    constexpr const char* glade_store{"taxtable-store"};
+    constexpr const char* glade_menu{"taxtable-menu"};
+    auto builder{gtk_builder_new()};
+    gnc_builder_add_from_file(builder, glade_file, glade_store);
+    gnc_builder_add_from_file(builder, glade_file, glade_menu);
+    auto widget{GTK_WIDGET(gtk_builder_get_object(builder, glade_menu))};
+    g_object_unref(builder);
+    gnc_taxtables_combo(GTK_COMBO_BOX(widget), gnc_get_current_book(), TRUE,
+                        nullptr);
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    option.set_ui_item(std::make_unique<GncGtkTaxTableUIItem>(widget));
+    option.set_ui_item_from_option();
     g_signal_connect (G_OBJECT (widget), "changed",
-                      G_CALLBACK (gnc_option_changed_option_cb), option);
-
-    return widget;
-}
-
-/* Function to set the UI widget based upon the option */
-static GtkWidget *
-invoice_set_widget (GncOption *option, GtkGrid *page_box,
-                    GtkLabel *name_label, char *documentation,
-                    /* Return values */
-                    GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
-
-    value = create_invoice_widget (option, *enclosing);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-/* Function to set the UI Value for a particular option */
-static gboolean
-invoice_set_value (GncOption *option, gboolean use_default,
-                   GtkWidget *widget, SCM value)
-{
-    GncInvoice *invoice;
+                      G_CALLBACK (gnc_option_changed_widget_cb), &option);
 
-    if (!SWIG_IsPointer (value))
-        scm_misc_error("business_options:invoice_set_value",
-                       "SCM is not a wrapped pointer.", value);
-
-    invoice = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncInvoice"), 1, 0);
-
-    widget = gnc_option_get_gtk_widget (option);
-    gnc_general_search_set_selected (GNC_GENERAL_SEARCH (widget), invoice);
-    return FALSE;
-}
-
-/* Function to get the UI Value for a particular option */
-static SCM
-invoice_get_value (GncOption *option, GtkWidget *widget)
-{
-    GncInvoice *invoice;
-
-    invoice = gnc_general_search_get_selected (GNC_GENERAL_SEARCH (widget));
-    return SWIG_NewPointerObj(invoice, SWIG_TypeQuery("_p__gncInvoice"), 0);
-}
-
-
-/********************************************************************/
-/* "Tax Table" Option functions */
-
-
-static GtkWidget *
-create_taxtable_widget (GncOption *option, GtkWidget *hbox)
-{
-    GtkWidget *widget;
-    GtkBuilder *builder;
-
-    builder = gtk_builder_new();
-    gnc_builder_add_from_file (builder, "business-options-gnome.glade", "taxtable_store");
-    gnc_builder_add_from_file (builder, "business-options-gnome.glade", "taxtable_menu");
-
-    widget = GTK_WIDGET (gtk_builder_get_object (builder, "taxtable_menu"));
-    gnc_taxtables_combo (GTK_COMBO_BOX(widget), gnc_get_current_book (), TRUE, NULL);
-    gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
-
-    gnc_option_set_widget (option, widget);
-
-    g_signal_connect (widget, "changed",
-                      G_CALLBACK (gnc_option_changed_option_cb), option);
-
-    g_object_unref(G_OBJECT(builder));
+    gtk_widget_show_all(*enclosing);
     return widget;
 }
 
-/* Function to set the UI widget based upon the option */
-static GtkWidget *
-taxtable_set_widget (GncOption *option, GtkGrid *page_box,
-                     GtkLabel *name_label, char *documentation,
-                     /* Return values */
-                     GtkWidget **enclosing, gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
-
-    value = create_taxtable_widget (option, *enclosing);
-
-    gnc_option_set_ui_value (option, FALSE);
-
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
-/* Function to set the UI Value for a particular option */
-static gboolean
-taxtable_set_value (GncOption *option, gboolean use_default,
-                    GtkWidget *widget, SCM value)
-{
-    GncTaxTable *taxtable;
-
-    if (!SWIG_IsPointer (value))
-        scm_misc_error("business_options:taxtable_set_value",
-                       "SCM is not a wrapped pointer.", value);
-
-    taxtable = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncTaxTable"), 1, 0);
-
-    widget = gnc_option_get_gtk_widget (option);
-    gnc_simple_combo_set_value (GTK_COMBO_BOX(widget), taxtable);
-    return FALSE;
-}
-
-/* Function to get the UI Value for a particular option */
-static SCM
-taxtable_get_value (GncOption *option, GtkWidget *widget)
-{
-    GncTaxTable *taxtable;
-
-    taxtable = gnc_simple_combo_get_value (GTK_COMBO_BOX(widget));
-    return SWIG_NewPointerObj(taxtable, SWIG_TypeQuery("_p__gncTaxTable"), 0);
-}
-
-#endif
-
-
 void
-gnc_business_options_gnome_initialize (void)
-{
-/* Create the above option types. */
+gnc_business_options_gnome_initialize(void)
+{
+    GncOptionUIFactory::set_func(GncOptionUIType::OWNER,
+                                 create_option_widget<GncOptionUIType::OWNER>);
+    GncOptionUIFactory::set_func(GncOptionUIType::CUSTOMER,
+                                 create_option_widget<GncOptionUIType::OWNER>);
+    GncOptionUIFactory::set_func(GncOptionUIType::VENDOR,
+                                 create_option_widget<GncOptionUIType::OWNER>);
+    GncOptionUIFactory::set_func(GncOptionUIType::EMPLOYEE,
+                                 create_option_widget<GncOptionUIType::OWNER>);
+    GncOptionUIFactory::set_func(GncOptionUIType::INVOICE,
+                                 create_option_widget<GncOptionUIType::INVOICE>);
+    GncOptionUIFactory::set_func(GncOptionUIType::TAX_TABLE,
+                                 create_option_widget<GncOptionUIType::TAX_TABLE>);
 }
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index ac3889f4a..b05350311 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -53,6 +53,7 @@ enum GncOptionUIType
     INVOICE,
     TAX_TABLE,
     QUERY,
+    MAX_VALUE,  //Nake sure this one is always last
 };
 
 #endif // GNC_OPTION_UITYPE_H__
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 84ae44d04..5e1439b19 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -52,7 +52,7 @@ gnucash/gnome/assistant-hierarchy.c
 gnucash/gnome/assistant-loan.cpp
 gnucash/gnome/assistant-stock-split.c
 gnucash/gnome/business-gnome-utils.c
-gnucash/gnome/business-options-gnome.c
+gnucash/gnome/business-options-gnome.cpp
 gnucash/gnome/business-urls.c
 gnucash/gnome/dialog-billterms.c
 gnucash/gnome/dialog-choose-owner.c

commit 01c0fe2364bd620268c25785deab29330930271f
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Apr 4 13:10:19 2020 -0700

    Include what we use.

diff --git a/gnucash/gnome/business-options-gnome.c b/gnucash/gnome/business-options-gnome.cpp
similarity index 100%
rename from gnucash/gnome/business-options-gnome.c
rename to gnucash/gnome/business-options-gnome.cpp
diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp
index 87ca289a8..dbd44e8b9 100644
--- a/libgnucash/app-utils/gnc-option-ui.hpp
+++ b/libgnucash/app-utils/gnc-option-ui.hpp
@@ -24,8 +24,10 @@
 #ifndef GNC_OPTION_UI_HPP_
 #define GNC_OPTION_UI_HPP_
 
+#include <memory>
 #include "gnc-option-uitype.hpp"
 
+class GncOption;
 class GncOptionUIItem;
 using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
 

commit 25b717d47a59e10e7930e36832426e75b0f4cbd8
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 26 17:43:52 2020 -0700

    Add a rudimentary C API for GncOptionDB.
    
    So that most of the gnome-util and gnome consumers don't need to
    be converted to C++.
    
    Hide the corresponding functions in options-utils to stop the
    compiler whining.
    
    Note that this commit breaks the build but is necessary at
    least temporarily to compartmentalize the changes.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index d76c59cfe..f5efcaf8a 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -24,11 +24,11 @@
 
 extern "C"
 {
-#include <config.h>
+#include <config.h> // Need this to include Account.h
 }
 
 #include <Account.h> // To include as C++ overriding later indirect includes
-#include "dialog-options.h"
+#include <libguile.h>
 extern "C"
 {
 #include <gtk/gtk.h>
@@ -62,6 +62,8 @@ extern "C"
 #include "misc-gnome-utils.h"
 }
 
+#include "dialog-options.h"
+#include <gnc-optiondb.hpp>
 #include <gnc-optiondb-impl.hpp>
 
 #define GNC_PREF_CLOCK_24H "clock-24h"
@@ -204,8 +206,7 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive)
 {
     while (widget && !GTK_IS_WINDOW(widget))
         widget = gtk_widget_get_parent(widget);
-    if (widget == NULL)
-        return;
+    if (!widget) return;
 
     /* find the ok and cancel buttons, we know where they will be so do it
        this way as opposed to using gtk_container_foreach, much less iteration */
@@ -263,17 +264,16 @@ option_changed_cb(GtkWidget *dummy, GncOption* option)
 }
 
 
-/********************************************************************\
+/*
  * set_selectable                               *
- *   Change the selectable state of the widget that represents a    *
- *   GUI option.                                                    *
- *                                                                  *
- * Args: option      - option to change widget state for            *
- *       selectable  - if false, update the widget so that it       *
- *                     cannot be selected by the user.  If true,    *
- *                     update the widget so that it can be selected.*
- * Return: nothing                                                  *
-\********************************************************************/
+ *   Change the selectable state of the widget that represents a
+ *   GUI option.
+ *
+ * option      - option to change widget state for
+ * selectable  - if false, update the widget so that it
+ *                     cannot be selected by the user.  If true,
+ *                     update the widget so that it can be selected.
+ */
 static void
 set_selectable (GncOption& option, bool selectable)
 {
@@ -282,6 +282,7 @@ set_selectable (GncOption& option, bool selectable)
         gtk_widget_set_sensitive (widget, selectable);
 }
 
+// This do-nothing template is specialized for each GncOptionUIType.
 template<GncOptionUIType type> GtkWidget*
 create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**,
                      bool*)
@@ -489,9 +490,8 @@ gnc_options_dialog_append_page(GNCOptionWin * propertybox,
  *   builds an options dialog given a property box and an options   *
  *   database and make the dialog visible                           *
  *                                                                  *
- * Args: propertybox - gnome property box to use                    *
- *       odb         - option database to use                       *
- * Return: nothing                                                  *
+ * @param propertybox - gnome property box to use                    *
+ * @param odb         - option database to use                       *
 \********************************************************************/
 void
 gnc_options_dialog_build_contents (GNCOptionWin *propertybox,
@@ -506,10 +506,9 @@ gnc_options_dialog_build_contents (GNCOptionWin *propertybox,
  *   database and make the dialog visible depending on the          *
  *   show_dialog flag                                               *
  *                                                                  *
- * Args: propertybox - gnome property box to use                    *
- *       odb         - option database to use                       *
- *       show_dialog - should dialog be made visible or not         *
- * Return: nothing                                                  *
+ * @param propertybox - gnome property box to use                    *
+ * @param odb         - option database to use                       *
+ * @param show_dialog - should dialog be made visible or not         *
 \********************************************************************/
 void
 gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox,
@@ -723,8 +722,7 @@ gnc_options_dialog_new(gchar *title, GtkWindow *parent)
     return gnc_options_dialog_new_modal(FALSE, title, NULL, parent);
 }
 
-/* gnc_options_dialog_new_modal:
- *
+/**
  *   - Opens the dialog-options glade file
  *   - Connects signals specified in the builder file
  *   - Sets the window's title
@@ -1000,7 +998,7 @@ public:
     void set_option_from_ui_item(GncOption& option) noexcept override
     {
         auto widget{GTK_TEXT_VIEW(get_widget())};
-        option.set_value(xxxgtk_textview_get_text(widget));
+        option.set_value(std::string{xxxgtk_textview_get_text(widget)});
     }
 };
 
@@ -1625,7 +1623,7 @@ show_hidden_toggled_cb(GtkWidget *widget, GncOption* option)
         option->get_ui_type() != GncOptionUIType::ACCOUNT_SEL)
         return;
 
-    auto tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
+    auto tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget(option));
     AccountViewInfo avi;
     gnc_tree_view_account_get_view_info (tree_view, &avi);
     avi.show_hidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
@@ -2042,10 +2040,8 @@ create_option_widget<GncOptionUIType::LIST> (GncOption& option,
                                              GtkWidget **enclosing,
                                              bool *packed)
 {
-    GtkWidget *value;
-    GtkWidget *eventbox;
-    gint       grid_row = GPOINTER_TO_INT(g_object_get_data
-                                         (G_OBJECT(page_box), "options-grid-row"));
+    auto grid_row = GPOINTER_TO_INT(g_object_get_data
+                                    (G_OBJECT(page_box), "options-grid-row"));
 
     *enclosing = create_list_widget(option, NULL);
     auto value = gnc_option_get_gtk_widget(&option);
@@ -2054,7 +2050,7 @@ create_option_widget<GncOptionUIType::LIST> (GncOption& option,
 
     /* Pack option widget into an extra eventbox because otherwise the
        "documentation" tooltip is not displayed. */
-    eventbox = gtk_event_box_new();
+    auto eventbox = gtk_event_box_new();
     gtk_container_add (GTK_CONTAINER (eventbox), *enclosing);
 
     gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1);
@@ -2166,7 +2162,7 @@ public:
         auto color_button = GTK_COLOR_CHOOSER(get_widget());
         gtk_color_chooser_set_rgba(color_button, &color);
         auto rgba_str = gdk_rgba_to_string(&color);
-        option.set_value(rgba_str);
+        option.set_value(std::string{rgba_str});
         g_free(rgba_str);
     }
 };
@@ -2287,13 +2283,6 @@ public:
     GncGtkPixmapUIItem(GtkWidget* widget) :
         GncOptionGtkUIItem{widget, GncOptionUIType::PIXMAP} {}
     void set_ui_item_from_option(GncOption& option) noexcept override
-    {
-        auto string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_widget()));
-        DEBUG("filename %s", string ? string : "(null)");
-        option.set_value(string);
-        g_free(string);
-    }
-    void set_option_from_ui_item(GncOption& option) noexcept override
     {
         auto string{option.get_value<std::string>()};
         if (!string.empty())
@@ -2309,6 +2298,13 @@ public:
             update_preview_cb(chooser, &option);
         }
     }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_widget()));
+        DEBUG("filename %s", string ? string : "(null)");
+        option.set_value(std::string{string});
+        g_free(string);
+    }
 };
 
 template<> GtkWidget *
@@ -2359,11 +2355,10 @@ static void
 radiobutton_set_cb(GtkWidget *w, gpointer data)
 {
     GncOption* option = static_cast<decltype(option)>(data);
-    GtkWidget *widget;
     gpointer _current, _new_value;
     gint current, new_value;
 
-    widget = gnc_option_get_gtk_widget (option);
+    auto widget = gnc_option_get_gtk_widget(option);
 
     _current = g_object_get_data(G_OBJECT(widget), "gnc_radiobutton_index");
     current = GPOINTER_TO_INT (_current);
diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h
index b3613de21..9bac92ac0 100644
--- a/gnucash/gnome-utils/dialog-options.h
+++ b/gnucash/gnome-utils/dialog-options.h
@@ -24,7 +24,6 @@
 #define OPTIONS_DIALOG_H
 
 #include <libguile.h>
-#include <gtk/gtk.h>
 #ifdef __cplusplus
 class GncOption;
 class GncOptionDB;
@@ -37,10 +36,18 @@ extern "C"
 typedef GNCOption GncOption;
 typedef GNCOptionDB GncOptionDB;
 #endif
+#include <guile-mappings.h>
+#include <gtk/gtk.h>
 
-/** A simple wrapper that casts the gpointer result of
- * gnc_option_get_widget() already into a GtkWidget*. */
-GtkWidget* const gnc_option_get_gtk_widget (GncOption *option);
+
+/**
+ * Retrieve the GtkWidget* used for packing the option control.
+ *
+ * This is not ncessarily the widget that has the input or handles signals.
+ * @param option The option
+ * @return a GtkWidget* const
+ */
+GtkWidget* const gnc_option_get_gtk_widget (const GncOption* option);
 
 typedef struct gnc_option_win GNCOptionWin;
 
diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.c
index ddea6b2f2..a8bda02f0 100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.c
@@ -70,7 +70,6 @@
 #include "gnc-warnings.h"
 #include "gnc-window.h"
 #include "gnc-prefs.h"
-#include "option-util.h"
 // +JSLED
 //#include "gnc-html.h"
 #include "gnc-autosave.h"
@@ -115,6 +114,19 @@ enum
 
 #define DIALOG_BOOK_OPTIONS_CM_CLASS "dialog-book-options"
 
+/**
+ * Processes selected options in the Book Options dialog: checks book_currency
+ * and use_split_action_for_num to see if features kvp should be set. To be used
+ * where ever a new book situation requires book option selection (e.g., not
+ * just in Book Options dialog opened from main window but also in new-file
+ * assistant).
+ *
+ *  @param GncOptionDB * options.
+ *
+ *  @return TRUE if gnc_gui_refresh_all should be called; otherwise FALSE.
+ **/
+extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options);
+
 /* Static Globals *******************************************************/
 
 /** The debugging module that this .o belongs to.  */
@@ -4164,7 +4176,7 @@ gnc_main_window_cmd_page_setup (GtkAction *action,
 }
 
 gboolean
-gnc_book_options_dialog_apply_helper(GNCOptionDB * options)
+gnc_book_options_dialog_apply_helper(GncOptionDB * options)
 {
     QofBook *book = gnc_get_current_book ();
     gboolean use_split_action_for_num_before =
@@ -4218,7 +4230,7 @@ static void
 gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin,
                                  gpointer user_data)
 {
-    GNCOptionDB * options = user_data;
+    GncOptionDB * options = user_data;
 
     if (!options) return;
 
@@ -4230,7 +4242,7 @@ static void
 gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin,
                                  gpointer user_data)
 {
-    GNCOptionDB * options = user_data;
+    GncOptionDB * options = user_data;
 
     gnc_options_dialog_destroy(optionwin);
     gnc_option_db_destroy(options);
@@ -4275,7 +4287,7 @@ GtkWidget *
 gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
 {
     QofBook *book = gnc_get_current_book ();
-    GNCOptionDB *options;
+    GncOptionDB *options;
     GNCOptionWin *optionwin;
 
     options = gnc_option_db_new_for_type (QOF_ID_BOOK);
diff --git a/gnucash/gnome-utils/gnc-main-window.h b/gnucash/gnome-utils/gnc-main-window.h
index 35b70d588..fe11c42ec 100644
--- a/gnucash/gnome-utils/gnc-main-window.h
+++ b/gnucash/gnome-utils/gnc-main-window.h
@@ -443,19 +443,6 @@ void gnc_main_window_show_all_windows(void);
 GtkWidget *gnc_book_options_dialog_cb (gboolean modal, gchar *title,
                                        GtkWindow *parent);
 
-/**
- * Processes selected options in the Book Options dialog: checks book_currency
- * and use_split_action_for_num to see if features kvp should be set. To be used
- * where ever a new book situation requires book option selection (e.g., not
- * just in Book Options dialog opened from main window but also in new-file
- * assistant).
- *
- *  @param GNCOptionDB * options.
- *
- *  @return TRUE if gnc_gui_refresh_all should be called; otherwise FALSE.
- **/
-gboolean gnc_book_options_dialog_apply_helper(GNCOptionDB * options);
-
 #endif /* __GNC_MAIN_WINDOW_H */
 
 /** @} */
diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.c
index aa8c59a0e..5d07d85de 100644
--- a/gnucash/gnome/assistant-hierarchy.c
+++ b/gnucash/gnome/assistant-hierarchy.c
@@ -126,6 +126,8 @@ typedef struct
 
 } hierarchy_data;
 
+extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options);
+
 void on_prepare (GtkAssistant  *assistant, GtkWidget *page,
                  hierarchy_data  *data);
 void on_choose_account_categories_prepare (hierarchy_data  *data);
diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt
index 20cd3c6d9..142976109 100644
--- a/libgnucash/app-utils/CMakeLists.txt
+++ b/libgnucash/app-utils/CMakeLists.txt
@@ -31,6 +31,7 @@ set (app_utils_HEADERS
   gnc-help-utils.h
   gnc-helpers.h
   gnc-option.hpp
+  gnc-optiondb.h
   gnc-optiondb.hpp
   gnc-prefs-utils.h
   gnc-state.h
diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i
index 274bc696f..2fc313df6 100644
--- a/libgnucash/app-utils/app-utils.i
+++ b/libgnucash/app-utils/app-utils.i
@@ -26,7 +26,6 @@ extern "C"
 {
 #endif
 #include <config.h>
-#include <option-util.h>
 #include <gnc-euro.h>
 #include <gnc-exp-parser.h>
 #include <gnc-ui-util.h>
@@ -65,9 +64,10 @@ PyObject* SWIG_init (void);
 
 %import "base-typemaps.i"
 
+ /* OBSOLETE
 typedef void (*GNCOptionChangeCallback) (gpointer user_data);
 typedef int GNCOptionDBHandle;
-
+ */
 void gnc_prefs_init();
 
 QofBook * gnc_get_current_book (void);
@@ -76,11 +76,6 @@ const gchar * gnc_get_current_book_tax_name (void);
 const gchar * gnc_get_current_book_tax_type (void);
 Account * gnc_get_current_root_account (void);
 
-GNCOptionDB * gnc_option_db_new(SCM guile_options);
-void gnc_option_db_destroy(GNCOptionDB *odb);
-
-void gnc_option_db_set_option_selectable_by_name(SCM guile_option,
-      const char *section, const char *name, gboolean selectable);
 
 #if defined(SWIGGUILE)
 %typemap(out) GncCommodityList * {
@@ -103,10 +98,9 @@ gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table * table);
 gnc_commodity * gnc_default_currency (void);
 gnc_commodity * gnc_default_report_currency (void);
 
+/* Obsolete: Options are C++ now, no need for convoluted callbacks.
 void gncp_option_invoke_callback(GNCOptionChangeCallback callback, void *data);
-void gnc_option_db_register_option(GNCOptionDBHandle handle,
-        SCM guile_option);
-
+*/
 GNCPrintAmountInfo gnc_default_print_info (gboolean use_symbol);
 GNCPrintAmountInfo gnc_account_print_info (const Account *account,
         gboolean use_symbol);
@@ -133,8 +127,9 @@ gnc_numeric gnc_convert_from_euro(const gnc_commodity * currency,
 time64 gnc_accounting_period_fiscal_start(void);
 time64 gnc_accounting_period_fiscal_end(void);
 
+/* OBSOLETE Options are C++, no SCM generators.
 void gnc_register_kvp_option_generator(QofIdType id_type, SCM generator);
-
+*/
 %typemap(out) GHashTable * {
   SCM table = scm_c_make_hash_table (g_hash_table_size($1) + 17);
   GHashTableIter iter;
diff --git a/libgnucash/app-utils/app-utils.scm b/libgnucash/app-utils/app-utils.scm
index 94bcfa55d..d0ed9635f 100644
--- a/libgnucash/app-utils/app-utils.scm
+++ b/libgnucash/app-utils/app-utils.scm
@@ -29,49 +29,8 @@
 (load-and-reexport (sw_app_utils)
                    (gnucash app-utils date-utilities)
                    (gnucash app-utils business-options)
-                   (gnucash app-utils business-prefs)
                    (gnucash app-utils options)
                    (gnucash app-utils c-interface))
-
-(export gnc:make-internal-option)
-(export gnc:make-query-option)
-(export gnc:make-color-option)
-(export gnc:make-dateformat-option)
-(export gnc:dateformat-get-format)
-
-(export gnc:color->html)
-(export gnc:color-option->html)
-(export gnc:color-option->hex-string)
-(export gnc:new-options)
-
-(export gnc:register-option)
-(export gnc:unregister-option)
-(export gnc:options-register-callback)
-(export gnc:options-register-c-callback)
-(export gnc:options-unregister-callback-id)
-(export gnc:options-for-each)
-(export gnc:options-for-each-general)
-(export gnc:lookup-option)
-(export gnc:generate-restore-forms)
-(export gnc:options-fancy-date)
-(export gnc:options-scm->kvp)
-(export gnc:options-kvp->scm)
-(export gnc:options-clear-changes)
-(export gnc:options-touch)
-(export gnc:options-run-callbacks)
-(export gnc:options-set-default-section)
-(export gnc:options-get-default-section)
-(export gnc:options-copy-values)
-(export gnc:send-options)
-
-(define (gnc:option-get-value book category key)
-  ;;Access an option directly
-  (qof-book-get-option book
-                       (if (list? key)
-                           (append (list category) key)
-                           (list category key))))
-(export gnc:option-get-value)
-
 ;; gw-engine-spec.scm
 (re-export HOOK-SAVE-OPTIONS)
 
diff --git a/libgnucash/app-utils/business-prefs.scm b/libgnucash/app-utils/business-prefs.scm
index 9e50ae3c1..84fc701fd 100644
--- a/libgnucash/app-utils/business-prefs.scm
+++ b/libgnucash/app-utils/business-prefs.scm
@@ -196,4 +196,4 @@
    counter-types))
 
 
-(gnc-register-kvp-option-generator QOF-ID-BOOK-SCM book-options-generator)
+;;(gnc-register-kvp-option-generator QOF-ID-BOOK-SCM book-options-generator)
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index c149e083d..11a47f879 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -25,11 +25,14 @@
 #include <limits>
 #include <sstream>
 #include <kvp-value.hpp>
+#include "gnc-optiondb.h"
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
 #include "gnc-option-ui.hpp"
 
-auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
+constexpr const char* log_module{G_LOG_DOMAIN};
+
+constexpr auto stream_max = std::numeric_limits<std::streamsize>::max();
 
 static bool
 operator==(const std::string& str, const char* cstr)
@@ -659,12 +662,6 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
         });
 }
 
-GncOptionDBPtr
-gnc_option_db_new(void)
-{
-    return GncOptionDBPtr{new GncOptionDB};
-}
-
 void
 gnc_register_string_option(const GncOptionDBPtr& db, const char* section,
                            const char* name, const char* key,
@@ -1070,3 +1067,78 @@ gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section,
                                         ui_type, end_dates)};
     db->register_option(section, std::move(option));
 }
+
+GncOptionDB*
+gnc_option_db_new(void)
+{
+    return new GncOptionDB;
+}
+
+GncOptionDB*
+gnc_option_db_new_for_type(QofIdType type)
+{
+    if (strcmp(type, QOF_ID_BOOK))
+        return nullptr;
+    auto db = new GncOptionDB;
+    gnc_option_db_book_options(db);
+    return db;
+}
+
+void
+gnc_option_db_destroy(GncOptionDB* odb)
+{
+    delete odb;
+}
+
+GList*
+gnc_option_db_commit(GncOptionDB* odb)
+{
+    GList* errors{};
+    odb->foreach_section(
+        [&errors](GncOptionSectionPtr& section){
+            section->foreach_option(
+                [&errors](GncOption& option) {
+                    try
+                    {
+                        option.set_option_from_ui_item();
+                    }
+                    catch (const std::invalid_argument& err)
+                    {
+                        PWARN("Option %s:%s failed to set its value %s",
+                              option.get_section().c_str(),
+                              option.get_name().c_str(), err.what());
+                        errors = g_list_prepend(errors,
+                                                (void*)option.get_name().c_str());
+                    } });
+        });
+    return errors;
+}
+
+void
+gnc_option_db_clean(GncOptionDB* odb)
+{
+        odb->foreach_section(
+        [](GncOptionSectionPtr& section){
+            section->foreach_option(
+                [](GncOption& option) {
+                    option.set_ui_item_from_option();
+                });
+        });
+}
+
+void gnc_option_db_load(GncOptionDB* odb, QofBook* book)
+{
+    odb->load_from_kvp(book);
+}
+
+void
+gnc_option_db_save(GncOptionDB* odb, QofBook* book,
+                        gboolean clear_options)
+{
+    odb->save_to_book(book, static_cast<bool>(clear_options));
+}
+
+void
+gnc_option_db_book_options(GncOptionDB*)
+{
+}
diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h
new file mode 100644
index 000000000..3b86dbfea
--- /dev/null
+++ b/libgnucash/app-utils/gnc-optiondb.h
@@ -0,0 +1,112 @@
+/********************************************************************\
+ * gnc-optiondb.h -- Collection of GncOption objects C interface    *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#ifndef GNC_OPTIONDB_H_
+#define GNC_OPTIONDB_H_
+
+#ifdef __cplusplus
+class GncOption;
+class GncOptionDB;
+#else
+// It's a class in C++ but the C compiler can't tell.
+typedef struct GncOption GncOption;
+typedef struct GncOptionDB GncOptionDB;
+#endif
+#ifdef __cplusplus
+class GncOptionDB;
+extern "C"
+{
+#else
+typedef struct GncOptionDB GncOptionDB;
+#endif
+#include <config.h>
+#include <Account.h>
+#include <gnc-budget.h>
+#include <gnc-commodity.h>
+#include <gncInvoice.h>
+#include <gncOwner.h>
+#include <gncTaxTable.h>
+
+/**
+ * Create an empty option database.
+ *
+ * @return A newly allocated GncOptionDB. Use delete to destroy it.
+ */
+GncOptionDB* gnc_option_db_new(void);
+
+/**
+ * Convenence function duplicating an option-util function. We need this temporarily to make gnc-main-window and assistant-hierarchy happy.
+ * @param type The QofType
+ * @return a new GncOptionDB*. Transfers ownership.
+ */
+GncOptionDB* gnc_option_db_new_for_type(QofIdType type);
+
+/**
+ * Destruct and release a GncOptionDB.
+ * @param odb The GncOptionDB.
+ */
+void gnc_option_db_destroy(GncOptionDB* odb);
+
+/**
+ * Write all changed ui_item values to their options.
+ * @param odb The GncOptionDB.
+ * @return A GList* conatining the names of options that raised exceptions when
+ * attempting to set their values. The names are const, free only the list.
+ */
+GList* gnc_option_db_commit(GncOptionDB* odb);
+
+/**
+ * Reset all ui_items to the option value.
+ * @param odb The GncOptionDB.
+ */
+void gnc_option_db_clean(GncOptionDB* odb);
+
+/**
+ * Load a book's options into the GncOptionDB.
+ * @param odb The GncOptionDB
+ * @param book The book in which the options are saved.
+ */
+void gnc_option_db_load(GncOptionDB* odb, QofBook* book);
+
+/**
+ * Save the GncOptionDB contents into a book's options store.
+ * @param odb The GncOptionDB
+ * @param book The book in which the options are saved.
+ * @param clear_options TRUE if the books existing options should be removed first.
+ */
+void gnc_option_db_save(GncOptionDB* odb, QofBook* book,
+                        gboolean clear_options);
+
+/**
+ * Register the standard option set for a QofBook.
+ *
+ * @param odb The GncOptionDB
+ */
+void gnc_option_db_book_options(GncOptionDB*);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif //GNC_OPTIONDB_H_
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 399316222..926815315 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -73,15 +73,6 @@ gnc_account_list_from_types(QofBook *book,
 
 
 using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
-/**
- * Create an empty option database.
- *
- * It would be nice to use a std::shared_ptr here but Swig doesn't implement
- * that for Guile.
- * @return A newly allocated GncOptionDB. Use delete to destroy it.
- */
-GncOptionDBPtr gnc_option_db_new(void);
-
 void gnc_register_string_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, std::string value);
diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c
index 04ff31ea0..45228b697 100644
--- a/libgnucash/app-utils/option-util.c
+++ b/libgnucash/app-utils/option-util.c
@@ -191,6 +191,7 @@ gnc_option_db_init (GNCOptionDB *odb)
     scm_call_2 (func, scm_from_int (odb->handle), odb->guile_options);
 }
 
+#if 0
 /********************************************************************\
  * gnc_option_db_new                                                *
  *   allocate a new option database and initialize its values       *
@@ -228,7 +229,7 @@ gnc_option_db_new (SCM guile_options)
 
     return odb;
 }
-
+#endif
 /* Create an option DB for a particular data type */
 /* For now, this is global, just like when it was in guile.
    But, it should be make per-book. */
@@ -262,7 +263,7 @@ gnc_make_kvp_options (QofIdType id_type)
     }
     return options;
 }
-
+#if 0
 GNCOptionDB *
 gnc_option_db_new_for_type (QofIdType id_type)
 {
@@ -382,7 +383,7 @@ gnc_option_db_destroy (GNCOptionDB *odb)
 
     g_free(odb);
 }
-
+#endif
 void
 gnc_option_db_set_ui_callbacks (GNCOptionDB *odb,
                                 GNCOptionGetUIValue get_ui_value,
@@ -1260,7 +1261,7 @@ compare_option_tags (gconstpointer a, gconstpointer b)
 
     return result;
 }
-
+#if 0
 /********************************************************************\
  * gnc_option_db_clean                                              *
  *   resets the dirty flag of the option database                   *
@@ -1273,6 +1274,7 @@ gnc_option_db_clean (GNCOptionDB *odb)
 
     odb->options_dirty = FALSE;
 }
+#endif
 
 /********************************************************************\
  * _gnc_option_db_register_option                                   *
@@ -1592,6 +1594,7 @@ gnc_option_db_get_changed (GNCOptionDB *odb)
     return FALSE;
 }
 
+#if 0
 /********************************************************************\
  * gnc_option_db_commit                                             *
  *   commits the options which have changed, and which are valid    *
@@ -1640,6 +1643,7 @@ gnc_option_db_commit (GNCOptionDB *odb)
 
     return commit_errors;
 }
+#endif
 
 /********************************************************************\
  * gnc_option_db_section_reset_widgets                              *
diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h
index 23742f011..4c1676e35 100644
--- a/libgnucash/app-utils/option-util.h
+++ b/libgnucash/app-utils/option-util.h
@@ -35,8 +35,7 @@
 
 typedef struct gnc_option GNCOption;
 typedef struct gnc_option_section GNCOptionSection;
-/* typedef struct gnc_option_db GNCOptionDB is in qof-book.h */
-
+typedef struct gnc_option_db GNCOptionDB;
 typedef int GNCOptionDBHandle;
 
 typedef SCM (*GNCOptionGetUIValue) (GNCOption *option);
@@ -64,18 +63,17 @@ SCM  gnc_option_get_ui_value (GNCOption *option);
 void gnc_option_set_ui_value (GNCOption *option, gboolean use_default);
 void gnc_option_set_selectable (GNCOption *option, gboolean selectable);
 
-GNCOptionDB * gnc_option_db_new (SCM guile_options);
-void          gnc_option_db_destroy (GNCOptionDB *odb);
+GNCOptionDB * gnc_option_db_new(SCM guile_options);
+void          gnc_option_db_destroy(GNCOptionDB *odb);
 
 /* Create an option DB for a particular type, and save/load from a kvp.
  * This assumes the gnc:*kvp-option-path* location for the options
  * in the kvp.
  */
-GNCOptionDB * gnc_option_db_new_for_type (QofIdType id_type);
-void gnc_option_db_load (GNCOptionDB* odb, QofBook *book);
-void gnc_option_db_save (GNCOptionDB* odb, QofBook *book, gboolean clear_all);
-
-void gnc_register_kvp_option_generator (QofIdType id_type, SCM generator);
+GNCOptionDB * gnc_option_db_new_for_type(QofIdType id_type);
+void gnc_option_db_load(GNCOptionDB* odb, QofBook *book);
+void gnc_option_db_save(GNCOptionDB* odb, QofBook *book, gboolean clear_all);
+void gnc_register_kvp_option_generator(QofIdType id_type, SCM generator);
 
 void gnc_option_db_set_ui_callbacks (GNCOptionDB *odb,
                                      GNCOptionGetUIValue get_ui_value,

commit 4451f58bd6e8c2f6fb7556164d004b1a4b999ecf
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 26 17:09:51 2020 -0700

    Rename RelativeDatePeriods missed earlier.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 61b235feb..48f449233 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -202,14 +202,20 @@ gnc_option_test_book_destroy(QofBook* book)
 %rename(end_this_month) RelativeDatePeriod::END_THIS_MONTH;
 %rename(start_prev_month) RelativeDatePeriod::START_PREV_MONTH;
 %rename(end_prev_month) RelativeDatePeriod::END_PREV_MONTH;
+%rename(start_next_month) RelativeDatePeriod::START_NEXT_MONTH;
+%rename(end_next_month) RelativeDatePeriod::END_NEXT_MONTH;
 %rename(start_current_quarter) RelativeDatePeriod::START_CURRENT_QUARTER;
 %rename(end_current_quarter) RelativeDatePeriod::END_CURRENT_QUARTER;
 %rename(start_prev_quarter) RelativeDatePeriod::START_PREV_QUARTER;
 %rename(end_prev_quarter) RelativeDatePeriod::END_PREV_QUARTER;
+%rename(start_next_quarter) RelativeDatePeriod::START_NEXT_QUARTER;
+%rename(end_next_quarter) RelativeDatePeriod::END_NEXT_QUARTER;
 %rename(start_cal_year) RelativeDatePeriod::START_CAL_YEAR;
-%rename(end_cal_yea) RelativeDatePeriod::END_CAL_YEAR;
+%rename(end_cal_year) RelativeDatePeriod::END_CAL_YEAR;
 %rename(start_prev_year) RelativeDatePeriod::START_PREV_YEAR;
 %rename(end_prev_year) RelativeDatePeriod::END_PREV_YEAR;
+%rename(start_next_year) RelativeDatePeriod::START_NEXT_YEAR;
+%rename(end_next_year) RelativeDatePeriod::END_NEXT_YEAR;
 %rename(start_accounting_period) RelativeDatePeriod::START_ACCOUNTING_PERIOD;
 %rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD;
 

commit 6feb92d4e8f88b03408787e4f5b2a43fd9003ed8
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 26 16:56:07 2020 -0700

    Provide scheme function new-gnc-optiondb.
    
    Wraps creating a GncOptionDBPtr, freeing up gnc_option_db_new() to
    return a GncOptionDB* for use in C code. Convert gnc_option_db_new()
    calls in gtest-gnc-optiondb to call std::make_unique() as well.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index db2a19b4d..61b235feb 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -415,6 +415,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         }
         GncOption_set_value_from_scm(db_opt, new_value);
     }
+
+    GncOptionDBPtr
+    new_gnc_optiondb()
+    {
+        auto db_ptr{std::make_unique<GncOptionDB>()};
+        return db_ptr;
+    }
 %}
 
 #endif //SWIGGUILE
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index a34612c2e..963626b46 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -36,7 +36,7 @@ extern "C"
 class GncOptionDBTest : public ::testing::Test
 {
 protected:
-    GncOptionDBTest() : m_db{gnc_option_db_new()} {}
+    GncOptionDBTest() : m_db{std::make_unique<GncOptionDB>()} {}
 
     GncOptionDBPtr m_db;
 };
@@ -271,7 +271,9 @@ TEST_F(GncOptionDBTest, test_register_start_date_option)
 class GncOptionDBIOTest : public ::testing::Test
 {
 protected:
-    GncOptionDBIOTest() : m_book{gnc_get_current_book()}, m_root{gnc_account_create_root(m_book)}, m_db{gnc_option_db_new()}
+    GncOptionDBIOTest() :
+        m_book{gnc_get_current_book()}, m_root{gnc_account_create_root(m_book)},
+        m_db{std::make_unique<GncOptionDB>()}
     {
         auto create_account = [this](Account* parent, GNCAccountType type,
                                        const char* name)->Account* {
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 89f683c81..5a407f31b 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -45,7 +45,7 @@
 
 (define (test-gnc-make-text-option)
   (test-begin "test-gnc-test-string-option")
-  (let* ((option-db (gnc-option-db-new))
+  (let* ((option-db (new-gnc-optiondb))
          (string-opt (gnc-register-string-option option-db "foo" "bar" "baz"
                                                  "Phony Option" "waldo")))
     (test-equal "waldo" (gnc-option-value option-db "foo" "bar"))
@@ -89,7 +89,7 @@
 
   (define (test-make-account-list-option book)
     (test-group "test-make-account-list-option"
-    (let ((optiondb (gnc-option-db-new))
+    (let ((optiondb (new-gnc-optiondb))
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
       (gnc-register-account-list-option optiondb "foo" "bar" "baz"
@@ -100,7 +100,7 @@
 
   (define (test-make-account-list-limited-option book)
     (test-group "test-make-account-list-option"
-    (let ((optiondb (gnc-option-db-new))
+    (let ((optiondb (new-gnc-optiondb))
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
       (gnc-register-account-list-limited-option optiondb "foo" "bar" "baz"
@@ -115,7 +115,7 @@
 
   (define (test-make-account-sel-limited-option book)
     (test-group "test-make-account-list-option"
-    (let ((optiondb (gnc-option-db-new))
+    (let ((optiondb (new-gnc-optiondb))
           (acctlist (gnc-account-list-from-types book
                                (list ACCT-TYPE-STOCK))))
       (gnc-register-account-sel-limited-option optiondb "salt" "pork" "baz"
@@ -148,7 +148,7 @@
   (assq-ref (assq-ref keylist key) info))
 
   (test-begin "test-gnc-test-multichoice-option")
-  (let* ((option-db (gnc-option-db-new))
+  (let* ((option-db (new-gnc-optiondb))
          (multilist (list
                        (list "plugh" (cons 'text "xyzzy") (cons 'tip "thud"))
                        (list "waldo" (cons 'text "pepper") (cons 'tip "salt"))
@@ -166,7 +166,7 @@
 
 (define (test-gnc-make-list-option)
   (test-begin "test-gnc-test-list-option")
-  (let* ((option-db (gnc-option-db-new))
+  (let* ((option-db (new-gnc-optiondb))
          (value-list (list (vector "AvgBalPlot" "Average" "Average Balance")
                            (vector "GainPlot" "Profit" "Profit (Gain minus Loss)")
                            (vector "GLPlot" "Gain/Loss" "Gain and Loss")))
@@ -181,7 +181,7 @@
 
 (define (test-gnc-make-date-option)
   (test-begin "test-gnc-test-date-option")
-  (let* ((option-db (gnc-option-db-new))
+  (let* ((option-db (new-gnc-optiondb))
          (date-opt (gnc-register-date-option option-db "foo" "bar"
                                              "baz" "Phony Option"
                                              (RelativeDatePeriod-today)))
@@ -193,7 +193,7 @@
 
 (define (test-gnc-make-date-set-option)
   (test-begin "test-gnc-test-date-set-option")
-  (let* ((option-db (gnc-option-db-new))
+  (let* ((option-db (new-gnc-optiondb))
          (date-opt (gnc-register-date-option-set
                     option-db "foo" "bar" "baz" "Phony Option"
                     (list (RelativeDatePeriod-today)
@@ -210,7 +210,7 @@
 
 (define (test-gnc-make-number-range-option)
   (test-begin "test-gnc-number-range-option")
-  (let* ((option-db (gnc-option-db-new))
+  (let* ((option-db (new-gnc-optiondb))
          (number-opt (gnc-register-number-range-option option-db "foo" "bar"
                                                        "baz" "Phony Option"
                                                        15 5 30 1)))

commit f9e136dbace3c44eeb444c9b574734008ac4295c
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 26 16:09:02 2020 -0700

    Get the spelling of GncMultichoiceOptionEntry consistent.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index fd2b95329..c149e083d 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -839,7 +839,7 @@ void
 gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string,
-                                GncMultiChoiceOptionChoices&& choices)
+                                GncMultichoiceOptionChoices&& choices)
 {
     GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
                 std::get<0>(choices.at(0)).c_str(), std::move(choices)}};
@@ -850,7 +850,7 @@ void
 gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
                          const char* name, const char* key,
                          const char* doc_string, const char* value,
-                         GncMultiChoiceOptionChoices&& list)
+                         GncMultichoiceOptionChoices&& list)
 {
     GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
                 value,  std::move(list), GncOptionUIType::LIST}};
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 26d5a3c89..399316222 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -47,10 +47,10 @@ class GncOptionDB;
 using GncOptionAccountList = std::vector<const Account*>;
 
 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
-using GncMultiChoiceOptionEntry = std::tuple<const std::string,
+using GncMultichoiceOptionEntry = std::tuple<const std::string,
                                              const std::string,
                                              const std::string>;
-using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
+using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
 
 /**
  * Extract a list of accounts in the book having one of the GNCAccountTypes in
@@ -141,12 +141,12 @@ void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
 void gnc_register_multichoice_option(const GncOptionDBPtr& db,
                                      const char* section, const char* name,
                                      const char* key, const char* doc_string,
-                                     GncMultiChoiceOptionChoices&& value);
+                                     GncMultichoiceOptionChoices&& value);
 
 void gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, const char* value,
-                              GncMultiChoiceOptionChoices&& list);
+                              GncMultichoiceOptionChoices&& list);
 
 void gnc_register_number_range_option(const GncOptionDBPtr& db,
                                       const char* section, const char* name,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index c850f5241..db2a19b4d 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -233,7 +233,7 @@ gnc_option_test_book_destroy(QofBook* book)
     $1 = &period_set;
 }
 
-%typemap(in) GncMultiChoiceOptionChoices&& (GncMultiChoiceOptionChoices choices)
+%typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices)
 {
     auto len = scm_to_size_t(scm_length($input));
     for (std::size_t i = 0; i < len; ++i)
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 70754bea7..a34612c2e 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -174,7 +174,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct)
 
 TEST_F(GncOptionDBTest, test_register_multichoice_option)
 {
-    GncMultiChoiceOptionChoices choices{
+    GncMultichoiceOptionChoices choices{
         { "plugh", "xyzzy", "thud"},
         { "waldo", "pepper", "salt"},
         { "pork", "sausage", "links"},

commit eb6e31f8036614e87e998e2483f0c90ac8c3d5b8
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 24 11:06:57 2020 -0700

    dialog-options: Change signature of gnc_option_get_gtk_widget()
    
    So that it can be called from C.

diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
index 9b04423b7..d76c59cfe 100644
--- a/gnucash/gnome-utils/dialog-options.cpp
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -189,9 +189,10 @@ section_reset_widgets(GncOptionSection* section)
 }
 
 GtkWidget* const
-gnc_option_get_gtk_widget (const GncOption& option)
+gnc_option_get_gtk_widget (const GncOption* option)
 {
-    auto ui_item{dynamic_cast<const GncOptionGtkUIItem*>(option.get_ui_item())};
+    if (!option) return nullptr;
+    auto ui_item{dynamic_cast<const GncOptionGtkUIItem*>(option->get_ui_item())};
     if (ui_item)
         return ui_item->get_widget();
 
@@ -250,12 +251,14 @@ gnc_options_dialog_changed (GNCOptionWin *win)
 static void
 widget_changed_cb(GtkWidget *widget, GncOption* option)
 {
+    if (!option) return;
     const_cast<GncOptionUIItem*>(option->get_ui_item())->set_dirty(true);
 }
 
 void
 option_changed_cb(GtkWidget *dummy, GncOption* option)
 {
+    if (!option) return;
     option->set_ui_item_from_option();
 }
 
@@ -274,7 +277,7 @@ option_changed_cb(GtkWidget *dummy, GncOption* option)
 static void
 set_selectable (GncOption& option, bool selectable)
 {
-    auto widget = gnc_option_get_gtk_widget(option);
+    auto widget = gnc_option_get_gtk_widget(&option);
     if (widget)
         gtk_widget_set_sensitive (widget, selectable);
 }
@@ -1490,7 +1493,7 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box,
     }
 
     option.set_ui_item_from_option();
-    auto widget{gnc_option_get_gtk_widget(option)};
+    auto widget{gnc_option_get_gtk_widget(&option)};
     if (type == GncOptionUIType::DATE_RELATIVE)
     {
         *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
@@ -1570,7 +1573,7 @@ account_select_all_cb(GtkWidget *widget, gpointer data)
     GncTreeViewAccount *tree_view;
     GtkTreeSelection *selection;
 
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option));
+    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
     gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
     gtk_tree_selection_select_all(selection);
@@ -1584,7 +1587,7 @@ account_clear_all_cb(GtkWidget *widget, gpointer data)
     GncTreeViewAccount *tree_view;
     GtkTreeSelection *selection;
 
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option));
+    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
     gtk_tree_selection_unselect_all(selection);
     widget_changed_cb(widget, option);
@@ -1597,7 +1600,7 @@ account_select_children_cb(GtkWidget *widget, gpointer data)
     GncTreeViewAccount *tree_view;
     GList *acct_list = NULL, *acct_iter = NULL;
 
-    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option));
+    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
     acct_list = gnc_tree_view_account_get_selected_accounts (tree_view);
 
     for (acct_iter = acct_list; acct_iter; acct_iter = acct_iter->next)
@@ -1815,7 +1818,7 @@ create_option_widget<GncOptionUIType::ACCOUNT_LIST>(GncOption& option,
     gtk_grid_attach (GTK_GRID(page_box), *enclosing, 1, grid_row, 1, 1);
     *packed = TRUE;
 
-    auto widget = gnc_option_get_gtk_widget(option);
+    auto widget = gnc_option_get_gtk_widget(&option);
     auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
     g_signal_connect(G_OBJECT(selection), "changed",
                      G_CALLBACK(widget_changed_cb), &option);
@@ -1877,7 +1880,7 @@ create_option_widget<GncOptionUIType::ACCOUNT_SEL> (GncOption& option,
 static void
 list_changed_cb(GtkTreeSelection *selection, GncOption* option)
 {
-    GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (*option));
+    GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option));
     widget_changed_cb(GTK_WIDGET(view), option);
 }
 
@@ -1888,7 +1891,7 @@ list_select_all_cb(GtkWidget *widget, gpointer data)
     GtkTreeView *view;
     GtkTreeSelection *selection;
 
-    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(*option));
+    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option));
     selection = gtk_tree_view_get_selection(view);
     gtk_tree_selection_select_all(selection);
     widget_changed_cb(GTK_WIDGET(view), option);
@@ -1901,7 +1904,7 @@ list_clear_all_cb(GtkWidget *widget, gpointer data)
     GtkTreeView *view;
     GtkTreeSelection *selection;
 
-    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (*option));
+    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option));
     selection = gtk_tree_view_get_selection(view);
     gtk_tree_selection_unselect_all(selection);
     widget_changed_cb(GTK_WIDGET(view), option);
@@ -2045,7 +2048,7 @@ create_option_widget<GncOptionUIType::LIST> (GncOption& option,
                                          (G_OBJECT(page_box), "options-grid-row"));
 
     *enclosing = create_list_widget(option, NULL);
-    value = gnc_option_get_gtk_widget (option);
+    auto value = gnc_option_get_gtk_widget(&option);
 
     align_label (name_label);
 
@@ -2524,7 +2527,7 @@ gnc_plot_size_option_set_select_method(GncOption& option, bool set_buttons)
     GtkWidget *px_widget, *p_widget;
     GtkWidget *widget;
 
-    widget = gnc_option_get_gtk_widget (option);
+    widget = gnc_option_get_gtk_widget(&option);
 
     widget_list = gtk_container_get_children(GTK_CONTAINER(widget));
     // px_button item 0
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 50b087a4a..c50f0a04f 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -192,7 +192,7 @@ GncOption::get_ui_type() const
                       }, *m_option);
 }
 
-const GncOptionUIItem*
+GncOptionUIItem* const
 GncOption::get_ui_item() const
 {
     return m_ui_item.get();
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 2d901d303..72ba3fedd 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -81,7 +81,7 @@ public:
     const std::string& get_docstring() const;
     void set_ui_item(GncOptionUIItemPtr&& ui_elem);
     const GncOptionUIType get_ui_type() const;
-    const GncOptionUIItem* get_ui_item() const;
+    GncOptionUIItem* const get_ui_item() const;
     void set_ui_item_from_option();
     void set_option_from_ui_item();
     void make_internal();
diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 372f558d9..0a87756bb 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -51,6 +51,7 @@ public:
     ~GncOptionSection() = default;
 
     void foreach_option(std::function<void(GncOption&)> func);
+    void foreach_option(std::function<void(const GncOption&)> func) const;
     const std::string& get_name() const noexcept { return m_name; }
     size_t get_num_options() const noexcept { return m_options.size(); }
     void add_option(GncOption&& option);
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 36367aace..fd2b95329 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -43,6 +43,12 @@ GncOptionSection::foreach_option(std::function<void(GncOption&)> func)
     std::for_each(m_options.begin(), m_options.end(), func);
 }
 
+void
+GncOptionSection::foreach_option(std::function<void(const GncOption&)> func) const
+{
+    std::for_each(m_options.begin(), m_options.end(), func);
+}
+
 void
 GncOptionSection::add_option(GncOption&& option)
 {
@@ -615,7 +621,7 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
         [book](GncOptionSectionPtr& section)
         {
             section->foreach_option(
-                [book, &section](auto& option)
+                [book, &section](GncOption& option)
                 {
                     /* qof_book_set_option wants a GSList path. Let's avoid allocating
                      * and make one here.

commit 67508ea039d3cffd4403c2c0857729bed229f44d
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 15 10:26:26 2020 -0800

    Reimplement dialog-options for C++.

diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt
index 35efced09..76fe198fc 100644
--- a/gnucash/gnome-utils/CMakeLists.txt
+++ b/gnucash/gnome-utils/CMakeLists.txt
@@ -36,7 +36,7 @@ set (gnome_utils_SOURCES
   dialog-dup-trans.c
   dialog-file-access.c
   dialog-object-references.c
-  dialog-options.c
+  dialog-options.cpp
   dialog-preferences.c
   dialog-query-view.c
   dialog-reset-warnings.c
@@ -59,7 +59,7 @@ set (gnome_utils_SOURCES
   gnc-currency-edit.c
   gnc-date-delta.c
   gnc-date-edit.c
-  gnc-date-format.c 
+  gnc-date-format.c
   gnc-dense-cal.c
   gnc-dense-cal-model.c
   gnc-dense-cal-store.c
diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp
new file mode 100644
index 000000000..9b04423b7
--- /dev/null
+++ b/gnucash/gnome-utils/dialog-options.cpp
@@ -0,0 +1,2887 @@
+/********************************************************************\
+ * dialog-options.cpp -- GNOME option handling                      *
+ * Copyright (C) 1998-2000 Linas Vepstas                            *
+ * Copyright (c) 2006 David Hampton <hampton at employees.org>         *
+ * Copyright (c) 2011 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                   *
+\********************************************************************/
+
+extern "C"
+{
+#include <config.h>
+}
+
+#include <Account.h> // To include as C++ overriding later indirect includes
+#include "dialog-options.h"
+extern "C"
+{
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+#include "swig-runtime.h"
+
+#include "gnc-tree-model-budget.h" //FIXME?
+#include "gnc-budget.h"
+#include <qofbookslots.h>
+
+#include "dialog-utils.h"
+#include "gnc-engine-guile.h"
+#include "glib-guile.h"
+#include "gnc-account-sel.h"
+#include "gnc-tree-view-account.h"
+#include "gnc-combott.h"
+#include "gnc-commodity-edit.h"
+#include "gnc-component-manager.h"
+#include "gnc-general-select.h"
+#include "gnc-currency-edit.h"
+#include "gnc-date-edit.h"
+#include "gnc-engine.h"
+#include "gnc-prefs.h"
+#include "gnc-gui-query.h"
+#include "gnc-session.h"
+#include "gnc-ui.h"
+#include "gnc-guile-utils.h"
+#include "guile-mappings.h"
+#include "gnc-date-format.h"
+#include "misc-gnome-utils.h"
+}
+
+#include <gnc-optiondb-impl.hpp>
+
+#define GNC_PREF_CLOCK_24H "clock-24h"
+
+
+#include <gnc-option.hpp>
+//#include <gnc-option-impl.hpp>
+#include <gnc-optiondb.hpp>
+#include <gnc-option-uitype.hpp>
+#include <gnc-option-ui.hpp>
+
+#define FUNC_NAME G_STRFUNC
+/* TODO: clean up "register-stocks" junk
+ */
+
+
+/* This static indicates the debugging module that this .o belongs to.  */
+static QofLogModule log_module = GNC_MOD_GUI;
+
+template <typename Instance> inline const QofInstance*
+qof_instance_cast(Instance inst)
+{
+    static_assert(std::is_pointer_v<Instance>, "Pointers Only!");
+    return reinterpret_cast<const QofInstance*>(inst);
+}
+
+static constexpr const char* DIALOG_OPTIONS_CM_CLASS{"dialog-options"};
+static constexpr const char* GNC_PREFS_GROUP{"dialogs.options"};
+
+/*
+ * Point where preferences switch control method from a set of
+ * notebook tabs to a list.
+ */
+#define MAX_TAB_COUNT 6
+
+/* A pointer to the last selected filename */
+#define LAST_SELECTION "last-selection"
+
+struct gnc_option_win
+{
+    GtkWidget  * window;
+    GtkWidget  * notebook;
+    GtkWidget  * page_list_view;
+    GtkWidget  * page_list;
+
+    bool toplevel;
+
+    GNCOptionWinCallback apply_cb;
+    gpointer             apply_cb_data;
+
+    GNCOptionWinCallback help_cb;
+    gpointer             help_cb_data;
+
+    GNCOptionWinCallback close_cb;
+    gpointer             close_cb_data;
+
+    /* Hold onto this for a complete reset */
+    GncOptionDB *option_db;
+
+    /* Hold on to this to unregister the right class */
+    const char *component_class;
+
+    /* widget being destroyed */
+    bool destroyed;
+};
+
+enum page_tree
+{
+    PAGE_INDEX = 0,
+    PAGE_NAME,
+    NUM_COLUMNS
+};
+
+class GncOptionGtkUIItem : public GncOptionUIItem
+{
+public:
+    GncOptionGtkUIItem(GtkWidget* widget, GncOptionUIType type) :
+        GncOptionUIItem(type),
+        m_widget{static_cast<GtkWidget*>(g_object_ref(widget))} {}
+    GncOptionGtkUIItem(const GncOptionGtkUIItem& item) :
+        GncOptionUIItem{item.get_ui_type()},
+        m_widget{static_cast<GtkWidget*>(g_object_ref(item.get_widget()))} {}
+    GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default;
+    virtual ~GncOptionGtkUIItem() override
+    {
+        if (m_widget)
+            g_object_unref(m_widget);
+    }
+    void clear_ui_item() override
+    {
+        if (m_widget)
+            g_object_unref(m_widget);
+        m_widget = nullptr;
+    }
+    void set_widget(GtkWidget* widget)
+    {
+        if (get_ui_type() == GncOptionUIType::INTERNAL)
+        {
+            std::string error{"INTERNAL option, setting the UI item forbidden."};
+            throw std::logic_error(std::move(error));
+        }
+
+        if (m_widget)
+            g_object_unref(m_widget);
+
+        m_widget = static_cast<GtkWidget*>(g_object_ref(widget));
+    }
+    virtual GtkWidget* const get_widget() const { return m_widget; }
+
+private:
+    GtkWidget* m_widget;
+};
+
+static GNCOptionWinCallback global_help_cb = NULL;
+gpointer global_help_cb_data = NULL;
+
+static void dialog_reset_cb(GtkWidget * w, gpointer data);
+void dialog_list_select_cb (GtkTreeSelection *selection,
+                                        gpointer data);
+static void component_close_handler (gpointer data);
+
+static void
+section_reset_widgets(GncOptionSection* section)
+{
+}
+
+GtkWidget* const
+gnc_option_get_gtk_widget (const GncOption& option)
+{
+    auto ui_item{dynamic_cast<const GncOptionGtkUIItem*>(option.get_ui_item())};
+    if (ui_item)
+        return ui_item->get_widget();
+
+    return nullptr;
+}
+
+static void
+dialog_changed_internal (GtkWidget *widget, bool sensitive)
+{
+    while (widget && !GTK_IS_WINDOW(widget))
+        widget = gtk_widget_get_parent(widget);
+    if (widget == NULL)
+        return;
+
+    /* find the ok and cancel buttons, we know where they will be so do it
+       this way as opposed to using gtk_container_foreach, much less iteration */
+    if (GTK_IS_CONTAINER(widget))
+    {
+        GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
+        for (GList *it = children; it; it = it->next)
+        {
+            if (GTK_IS_BOX (GTK_WIDGET(it->data)))
+            {
+                GList *children = gtk_container_get_children(GTK_CONTAINER(it->data));
+                for (GList *it = children; it; it = it->next)
+                {
+                    if (GTK_IS_BUTTON_BOX (GTK_WIDGET(it->data)))
+                    {
+                        GList *children = gtk_container_get_children(GTK_CONTAINER(it->data));
+                        for (GList *it = children; it; it = it->next)
+                        {
+                            if (g_strcmp0 (gtk_widget_get_name(GTK_WIDGET(it->data)), "ok_button") == 0)
+                                gtk_widget_set_sensitive (GTK_WIDGET(it->data), sensitive);
+
+                            if (g_strcmp0 (gtk_widget_get_name(GTK_WIDGET(it->data)), "apply_button") == 0)
+                                gtk_widget_set_sensitive (GTK_WIDGET(it->data), sensitive);
+                        }
+                        g_list_free (children);
+                    }
+                }
+                g_list_free (children);
+            }
+        }
+        g_list_free (children);
+    }
+}
+
+void
+gnc_options_dialog_changed (GNCOptionWin *win)
+{
+    if (!win) return;
+
+    dialog_changed_internal (win->window, TRUE);
+}
+
+static void
+widget_changed_cb(GtkWidget *widget, GncOption* option)
+{
+    const_cast<GncOptionUIItem*>(option->get_ui_item())->set_dirty(true);
+}
+
+void
+option_changed_cb(GtkWidget *dummy, GncOption* option)
+{
+    option->set_ui_item_from_option();
+}
+
+
+/********************************************************************\
+ * set_selectable                               *
+ *   Change the selectable state of the widget that represents a    *
+ *   GUI option.                                                    *
+ *                                                                  *
+ * Args: option      - option to change widget state for            *
+ *       selectable  - if false, update the widget so that it       *
+ *                     cannot be selected by the user.  If true,    *
+ *                     update the widget so that it can be selected.*
+ * Return: nothing                                                  *
+\********************************************************************/
+static void
+set_selectable (GncOption& option, bool selectable)
+{
+    auto widget = gnc_option_get_gtk_widget(option);
+    if (widget)
+        gtk_widget_set_sensitive (widget, selectable);
+}
+
+template<GncOptionUIType type> GtkWidget*
+create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**,
+                     bool*)
+{
+    return nullptr;
+}
+
+static GtkWidget* option_widget_factory(GncOption& option, GtkGrid* page,
+                                        GtkLabel* name, char* description,
+                                        GtkWidget** enclosing, bool* packed);
+static void
+gnc_option_set_ui_widget(GncOption& option, GtkGrid *page_box, gint grid_row)
+{
+    GtkWidget *enclosing = NULL;
+    GtkWidget *value = NULL;
+    bool packed = FALSE;
+    char *name, *documentation;
+    GtkLabel *name_label;
+
+    ENTER("option %p(%s), box %p",
+          &option, option.get_name().c_str(), page_box);
+    auto type = option.get_ui_type();
+    if (type == GncOptionUIType::INTERNAL)
+    {
+        LEAVE("internal type");
+        return;
+    }
+
+
+
+    const char* raw_name = option.get_name().c_str();
+    if (raw_name && *raw_name)
+        name = _(raw_name);
+    else
+        name = nullptr;
+
+    const char* raw_documentation = option.get_docstring().c_str();
+    if (raw_documentation && *raw_documentation)
+        documentation = _(raw_documentation);
+    else
+        documentation = nullptr;
+
+    name_label = GTK_LABEL(gtk_label_new (name));
+    auto widget = option_widget_factory(option, page_box, name_label,
+                                        documentation, &enclosing, &packed);
+    /* attach the name label to the first column of the grid and
+       align to the end */
+    gtk_grid_attach (GTK_GRID(page_box), GTK_WIDGET(name_label),
+                     0, grid_row, // left, top
+                     1, 1);  // width, height
+    gtk_widget_set_halign (GTK_WIDGET(name_label), GTK_ALIGN_END);
+
+    if (!packed && (enclosing != NULL))
+    {
+        /* Pack option widget into an extra eventbox because otherwise the
+           "documentation" tooltip is not displayed. */
+        GtkWidget *eventbox = gtk_event_box_new();
+
+        gtk_container_add (GTK_CONTAINER (eventbox), enclosing);
+
+        /* attach the option widget to the second column of the grid */
+        gtk_grid_attach (GTK_GRID(page_box), eventbox,
+                         1, grid_row, // left, top
+                         1, 1);  // width, height
+
+        gtk_widget_set_tooltip_text (eventbox, documentation);
+    }
+
+    if (value != NULL)
+        gtk_widget_set_tooltip_text(value, documentation);
+
+    LEAVE(" ");
+}
+
+static GtkBox*
+create_content_box()
+{
+    auto content_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+    gtk_widget_set_name (content_box, "page-content-box");
+    gtk_box_set_homogeneous (GTK_BOX (content_box), FALSE);
+
+    gtk_container_set_border_width(GTK_CONTAINER(content_box), 12);
+    return GTK_BOX(content_box);
+}
+
+static GtkGrid*
+create_options_box(GtkBox* content_box)
+{
+    auto options_scrolled_win = gtk_scrolled_window_new(NULL, NULL);
+    gtk_box_pack_start(GTK_BOX(content_box), options_scrolled_win,
+                       TRUE, TRUE, 0);
+
+    /* Build space for the content - the options box */
+    auto options_box = gtk_grid_new(); // this will have two columns
+    gtk_widget_set_name (options_box, "options-box");
+    gtk_grid_set_row_homogeneous (GTK_GRID(options_box), FALSE);
+    gtk_grid_set_column_homogeneous (GTK_GRID(options_box), FALSE);
+    gtk_grid_set_row_spacing (GTK_GRID(options_box), 6);
+    gtk_grid_set_column_spacing (GTK_GRID(options_box), 6);
+
+    gtk_container_set_border_width(GTK_CONTAINER(options_box), 0);
+    gtk_container_add (GTK_CONTAINER(options_scrolled_win),
+                       GTK_WIDGET(options_box));
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(options_scrolled_win),
+                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+    return GTK_GRID(options_box);
+}
+
+static GtkButtonBox*
+create_reset_button_box(GtkBox* page_content_box)
+{
+    auto buttonbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+    gtk_button_box_set_layout (GTK_BUTTON_BOX (buttonbox),
+                               GTK_BUTTONBOX_EDGE);
+    gtk_container_set_border_width(GTK_CONTAINER (buttonbox), 5);
+    gtk_box_pack_end(GTK_BOX(page_content_box), buttonbox, FALSE, FALSE, 0);
+    return GTK_BUTTON_BOX(buttonbox);
+}
+
+static int
+setup_notebook_pages(GNCOptionWin* propertybox, GtkBox* page_content_box,
+                     const char* name)
+{
+    auto page_count = gtk_notebook_page_num(GTK_NOTEBOOK(propertybox->notebook),
+                                            GTK_WIDGET(page_content_box));
+
+    if (propertybox->page_list_view)
+    {
+        /* Build the matching list item for selecting from large page sets */
+        auto view = GTK_TREE_VIEW(propertybox->page_list_view);
+        auto list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
+
+        PINFO("Page name is %s and page_count is %d", name, page_count);
+        GtkTreeIter iter;
+        gtk_list_store_append(list, &iter);
+        gtk_list_store_set(list, &iter,
+                           PAGE_NAME, _(name),
+                           PAGE_INDEX, page_count,
+                           -1);
+
+        if (page_count > MAX_TAB_COUNT - 1)   /* Convert 1-based -> 0-based */
+        {
+            gtk_widget_show(propertybox->page_list);
+            gtk_notebook_set_show_tabs(GTK_NOTEBOOK(propertybox->notebook), FALSE);
+            gtk_notebook_set_show_border(GTK_NOTEBOOK(propertybox->notebook), FALSE);
+        }
+        else
+            gtk_widget_hide(propertybox->page_list);
+
+    }
+    return page_count;
+}
+
+static int
+gnc_options_dialog_append_page(GNCOptionWin * propertybox,
+                               GncOptionSectionPtr& section)
+{
+    auto name = section->get_name().c_str();
+    if (!name)
+        return -1;
+
+    if (strncmp(name, "__", 2) == 0)
+        return -1;
+
+    auto page_label = gtk_label_new(_(name));
+    PINFO("Page_label is %s", _(name));
+    gtk_widget_show(page_label);
+
+    /* Build this options page */
+    auto page_content_box = create_content_box();
+    auto options_box = create_options_box(page_content_box);
+
+    /* Create all the options */
+    size_t row = 0;
+    section->foreach_option(
+        [options_box, &row](GncOption& option) {
+            g_object_set_data (G_OBJECT(options_box), "options-grid-row",
+                               GINT_TO_POINTER(row));
+            gnc_option_set_ui_widget(option, GTK_GRID(options_box), row);
+            ++row;
+        });
+
+    /* Add a button box at the bottom of the page */
+    auto buttonbox = create_reset_button_box(page_content_box);
+    /* The reset button on each option page */
+    auto reset_button = gtk_button_new_with_label (_("Reset defaults"));
+    gtk_widget_set_tooltip_text(reset_button,
+                                _("Reset all values to their defaults."));
+
+    g_signal_connect(G_OBJECT(reset_button), "clicked",
+                     G_CALLBACK(dialog_reset_cb), propertybox);
+    g_object_set_data(G_OBJECT(reset_button), "section",
+                      static_cast<void*>(section.get()));
+    gtk_box_pack_end(GTK_BOX(buttonbox), reset_button, FALSE, FALSE, 0);
+    gtk_widget_show_all(GTK_WIDGET(page_content_box));
+    gtk_notebook_append_page(GTK_NOTEBOOK(propertybox->notebook),
+                             GTK_WIDGET(page_content_box), page_label);
+
+    /* Switch to selection from a list if the page count threshold is reached */
+    return setup_notebook_pages(propertybox, page_content_box, name);
+}
+
+/********************************************************************\
+ * gnc_options_dialog_build_contents                                *
+ *   builds an options dialog given a property box and an options   *
+ *   database and make the dialog visible                           *
+ *                                                                  *
+ * Args: propertybox - gnome property box to use                    *
+ *       odb         - option database to use                       *
+ * Return: nothing                                                  *
+\********************************************************************/
+void
+gnc_options_dialog_build_contents (GNCOptionWin *propertybox,
+                                   GncOptionDB  *odb)
+{
+    gnc_options_dialog_build_contents_full (propertybox, odb, true);
+}
+
+/********************************************************************\
+ * gnc_options_dialog_build_contents_full                           *
+ *   builds an options dialog given a property box and an options   *
+ *   database and make the dialog visible depending on the          *
+ *   show_dialog flag                                               *
+ *                                                                  *
+ * Args: propertybox - gnome property box to use                    *
+ *       odb         - option database to use                       *
+ *       show_dialog - should dialog be made visible or not         *
+ * Return: nothing                                                  *
+\********************************************************************/
+void
+gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox,
+                                        GNCOptionDB  *odb, gboolean show_dialog)
+{
+    gint default_page = -1;
+
+    gint page;
+
+    g_return_if_fail (propertybox != NULL);
+    g_return_if_fail (odb != NULL);
+
+    propertybox->option_db = odb;
+
+    auto num_sections = odb->num_sections();
+    auto default_section = odb->get_default_section();
+
+    PINFO("Default Section name is %s", default_section->get_name().c_str());
+
+    odb->foreach_section(
+        [propertybox, default_section, &default_page]
+        (GncOptionSectionPtr& section) {
+            auto page = gnc_options_dialog_append_page(propertybox, section);
+            if (section.get() == default_section)
+                default_page = page;
+        });
+
+    /* call each option widget changed callbacks once at this point, now that
+     * all options widgets exist.
+     *
+     * Note that this may be superfluous as each create_option_widget
+     * specialization calls option.set_ui_item_from_option after creating the UI
+     * item.
+     */
+    odb->foreach_section(
+        [](GncOptionSectionPtr& section) {
+            section->foreach_option(
+                [](GncOption& option) {
+                    option.set_ui_item_from_option();
+                });
+        });
+
+    gtk_notebook_popup_enable(GTK_NOTEBOOK(propertybox->notebook));
+    if (default_page >= 0)
+    {
+        /* Find the page list and set the selection to the default page */
+        GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(propertybox->page_list_view));
+        GtkTreeIter iter;
+        GtkTreeModel *model;
+
+        model = gtk_tree_view_get_model(GTK_TREE_VIEW(propertybox->page_list_view));
+        gtk_tree_model_iter_nth_child(model, &iter, NULL, default_page);
+        gtk_tree_selection_select_iter (selection, &iter);
+        gtk_notebook_set_current_page(GTK_NOTEBOOK(propertybox->notebook), default_page);
+    }
+    dialog_changed_internal(propertybox->window, FALSE);
+    if (show_dialog)
+        gtk_widget_show(propertybox->window);
+}
+
+GtkWidget *
+gnc_options_dialog_widget(GNCOptionWin * win)
+{
+    return win->window;
+}
+
+GtkWidget *
+gnc_options_page_list(GNCOptionWin * win)
+{
+    return win->page_list;
+}
+
+GtkWidget *
+gnc_options_dialog_notebook(GNCOptionWin * win)
+{
+    return win->notebook;
+}
+
+static void
+gnc_options_dialog_help_button_cb(GtkWidget * widget, GNCOptionWin *win)
+{
+    if (win->help_cb)
+        (win->help_cb)(win, win->help_cb_data);
+}
+
+static void
+gnc_options_dialog_cancel_button_cb(GtkWidget * widget, GNCOptionWin *win)
+{
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
+
+    if (win->close_cb)
+        (win->close_cb)(win, win->close_cb_data);
+    else
+        gtk_widget_hide(win->window);
+}
+
+static void
+gnc_options_dialog_apply_button_cb(GtkWidget * widget, GNCOptionWin *win)
+{
+    GNCOptionWinCallback close_cb = win->close_cb;
+
+    win->close_cb = NULL;
+    if (win->apply_cb)
+        win->apply_cb (win, win->apply_cb_data);
+    win->close_cb = close_cb;
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
+    dialog_changed_internal (win->window, FALSE);
+}
+
+static void
+gnc_options_dialog_ok_button_cb(GtkWidget * widget, GNCOptionWin *win)
+{
+    GNCOptionWinCallback close_cb = win->close_cb;
+
+    win->close_cb = NULL;
+    if (win->apply_cb)
+        win->apply_cb (win, win->apply_cb_data);
+    win->close_cb = close_cb;
+
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
+
+    if (win->close_cb)
+        (win->close_cb)(win, win->close_cb_data);
+    else
+        gtk_widget_hide(win->window);
+}
+
+static void
+gnc_options_dialog_destroy_cb (GtkWidget *object, GNCOptionWin *win)
+{
+    if (!win) return;
+
+    if (win->destroyed == FALSE)
+    {
+        if (win->close_cb)
+            (win->close_cb)(win, win->close_cb_data);
+    }
+}
+
+static bool
+gnc_options_dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+    GNCOptionWin *win = static_cast<decltype(win)>(data);
+
+    if (event->keyval == GDK_KEY_Escape)
+    {
+        component_close_handler (win);
+        return TRUE;
+    }
+    else
+        return FALSE;
+}
+
+static void
+dialog_reset_cb(GtkWidget * w, gpointer data)
+{
+    GNCOptionWin *win = static_cast<decltype(win)>(data);
+    gpointer val;
+
+    val = g_object_get_data(G_OBJECT(w), "section");
+    g_return_if_fail (val);
+    g_return_if_fail (win);
+
+    auto section = static_cast<GncOptionSection*>(val);
+    section->foreach_option(
+        [](GncOption& option) {
+            option.set_ui_item_from_option();
+            const_cast<GncOptionUIItem*>(option.get_ui_item())->set_dirty(true);
+        });
+
+    dialog_changed_internal (win->window, TRUE);
+}
+
+void
+dialog_list_select_cb (GtkTreeSelection *selection,
+                                   gpointer data)
+{
+    GNCOptionWin * win = static_cast<decltype(win)>(data);
+    GtkTreeModel *list;
+    GtkTreeIter iter;
+    gint index = 0;
+
+    if (!gtk_tree_selection_get_selected(selection, &list, &iter))
+        return;
+    gtk_tree_model_get(list, &iter,
+                       PAGE_INDEX, &index,
+                       -1);
+    PINFO("Index is %d", index);
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), index);
+}
+
+static void
+component_close_handler (gpointer data)
+{
+    GNCOptionWin *win = static_cast<decltype(win)>(data);
+    gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window));
+    gnc_options_dialog_cancel_button_cb (NULL, win);
+}
+
+/* gnc_options_dialog_new:
+ *
+ *   - Opens the dialog-options glade file
+ *   - Connects signals specified in the builder file
+ *   - Sets the window's title
+ *   - Initializes a new GtkNotebook, and adds it to the window
+ *
+ */
+GNCOptionWin *
+gnc_options_dialog_new(gchar *title, GtkWindow *parent)
+{
+    return gnc_options_dialog_new_modal(FALSE, title, NULL, parent);
+}
+
+/* gnc_options_dialog_new_modal:
+ *
+ *   - Opens the dialog-options glade file
+ *   - Connects signals specified in the builder file
+ *   - Sets the window's title
+ *   - Initializes a new GtkNotebook, and adds it to the window
+ *   - If modal TRUE, hides 'apply' button
+ *   - If component_class is provided, it is used, otherwise,
+ *     DIALOG_OPTIONS_CM_CLASS is used; this is used to distinguish the
+ *     book-option dialog from report dialogs. The book-option dialog is a
+ *     singleton, so if a dialog already exists it will be raised to the top of
+ *     the window stack instead of creating a new dialog.
+ */
+GNCOptionWin *
+gnc_options_dialog_new_modal(gboolean modal, gchar *title,
+                             const char *component_class,
+                             GtkWindow *parent)
+{
+    GNCOptionWin *retval;
+    GtkBuilder   *builder;
+    GtkWidget    *hbox;
+    gint component_id;
+    GtkWidget    *button;
+
+    retval = g_new0(GNCOptionWin, 1);
+    builder = gtk_builder_new();
+    gnc_builder_add_from_file (builder, "dialog-options.glade", "gnucash_options_window");
+    retval->window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window"));
+    retval->page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll"));
+
+    /* Page List */
+    {
+        GtkTreeView *view;
+        GtkListStore *store;
+        GtkTreeSelection *selection;
+        GtkCellRenderer *renderer;
+        GtkTreeViewColumn *column;
+
+        retval->page_list_view = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_treeview"));
+
+        view = GTK_TREE_VIEW(retval->page_list_view);
+
+        store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING);
+        gtk_tree_view_set_model(view, GTK_TREE_MODEL(store));
+        g_object_unref(store);
+
+        renderer = gtk_cell_renderer_text_new();
+        column = gtk_tree_view_column_new_with_attributes(_("Page"), renderer,
+                 "text", PAGE_NAME, NULL);
+        gtk_tree_view_append_column(view, column);
+
+        gtk_tree_view_column_set_alignment(column, 0.5);
+
+        selection = gtk_tree_view_get_selection(view);
+        gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+        g_signal_connect (selection, "changed",
+                          G_CALLBACK (dialog_list_select_cb), retval);
+    }
+
+    button = GTK_WIDGET(gtk_builder_get_object (builder, "helpbutton"));
+        g_signal_connect(button, "clicked", G_CALLBACK(gnc_options_dialog_help_button_cb), retval);
+    button = GTK_WIDGET(gtk_builder_get_object (builder, "cancelbutton"));
+        g_signal_connect(button, "clicked", G_CALLBACK(gnc_options_dialog_cancel_button_cb), retval);
+    button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton"));
+        g_signal_connect(button, "clicked", G_CALLBACK(gnc_options_dialog_apply_button_cb), retval);
+    button = GTK_WIDGET(gtk_builder_get_object (builder, "okbutton"));
+        g_signal_connect(button, "clicked", G_CALLBACK(gnc_options_dialog_ok_button_cb), retval);
+
+    gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, retval);
+
+    gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(retval->window), parent);
+
+    if (title)
+        gtk_window_set_title(GTK_WINDOW(retval->window), title);
+
+    /* modal */
+    if (modal == TRUE)
+    {
+        GtkWidget *apply_button;
+
+        apply_button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton"));
+        gtk_widget_hide (apply_button);
+    }
+
+    /* glade doesn't support a notebook with zero pages */
+    hbox = GTK_WIDGET(gtk_builder_get_object (builder, "notebook_placeholder"));
+    retval->notebook = gtk_notebook_new();
+
+    gtk_widget_set_vexpand (retval->notebook, TRUE);
+
+    gtk_widget_show(retval->notebook);
+    gtk_box_pack_start(GTK_BOX(hbox), retval->notebook, TRUE, TRUE, 5);
+
+    component_id = gnc_register_gui_component (retval->component_class,
+                                               NULL, component_close_handler,
+                                               retval);
+    gnc_gui_component_set_session (component_id, gnc_get_current_session());
+
+    g_signal_connect (retval->window, "destroy",
+                      G_CALLBACK(gnc_options_dialog_destroy_cb), retval);
+
+    g_signal_connect (retval->window, "key_press_event",
+                      G_CALLBACK(gnc_options_dialog_window_key_press_cb), retval);
+
+    g_object_unref(G_OBJECT(builder));
+
+    retval->destroyed = FALSE;
+    return retval;
+}
+
+void
+gnc_options_dialog_set_apply_cb(GNCOptionWin * win, GNCOptionWinCallback cb,
+                                gpointer data)
+{
+    win->apply_cb = cb;
+    win->apply_cb_data = data;
+}
+
+void
+gnc_options_dialog_set_help_cb(GNCOptionWin * win, GNCOptionWinCallback cb,
+                               gpointer data)
+{
+    win->help_cb = cb;
+    win->help_cb_data = data;
+}
+
+void
+gnc_options_dialog_set_close_cb(GNCOptionWin * win, GNCOptionWinCallback cb,
+                                gpointer data)
+{
+    win->close_cb = cb;
+    win->close_cb_data = data;
+}
+
+void
+gnc_options_dialog_set_global_help_cb(GNCOptionWinCallback thunk,
+                                      gpointer cb_data)
+{
+    global_help_cb = thunk;
+    global_help_cb_data = cb_data;
+}
+
+/* This is for global program preferences. */
+void
+gnc_options_dialog_destroy(GNCOptionWin * win)
+{
+    if (!win) return;
+
+    gnc_unregister_gui_component_by_data(win->component_class, win);
+
+    win->destroyed = TRUE;
+    gtk_widget_destroy(win->window);
+
+    win->window = NULL;
+    win->notebook = NULL;
+    win->apply_cb = NULL;
+    win->help_cb = NULL;
+    win->component_class = NULL;
+
+    g_free(win);
+}
+
+/* ****************************************************************/
+/* Option Widgets                                      */
+/* ***************************************************************/
+
+static void
+align_label (GtkLabel *name_label)
+{
+    /* some option widgets have a large vertical foot print so align
+       the label name to the top and add a small top margin */
+    gtk_widget_set_valign (GTK_WIDGET(name_label), GTK_ALIGN_START);
+    gtk_widget_set_margin_top (GTK_WIDGET(name_label), 6);
+}
+
+class GncGtkBooleanUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkBooleanUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::BOOLEAN} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GTK_TOGGLE_BUTTON(get_widget())};
+        gtk_toggle_button_set_active(widget, option.get_value<bool>());
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GTK_TOGGLE_BUTTON(get_widget())};
+        option.set_value(gtk_toggle_button_get_active(widget));
+    }
+};
+
+template <> GtkWidget *
+create_option_widget<GncOptionUIType::BOOLEAN> (GncOption& option,
+                                                GtkGrid* page_box,
+                                                GtkLabel* name_label,
+                                                char* documentation,
+                                                /* Return values */
+                                                GtkWidget** enclosing,
+                                                bool* packed)
+{
+
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+    auto widget = gtk_check_button_new ();
+
+    auto ui_item{std::make_unique<GncGtkBooleanUIItem>(GncGtkBooleanUIItem{widget})};
+
+    g_signal_connect(G_OBJECT(widget), "toggled",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    option.set_ui_item(std::move(ui_item));
+    option.set_ui_item_from_option();
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+
+    return widget;
+}
+
+class GncGtkStringUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkStringUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::STRING} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GTK_ENTRY(get_widget())};
+        gtk_entry_set_text(widget, option.get_value<std::string>().c_str());
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GTK_ENTRY(get_widget())};
+        option.set_value(gtk_entry_get_text(widget));
+    }
+};
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::STRING> (GncOption& option,
+                                               GtkGrid *page_box,
+                                               GtkLabel *name_label,
+                                               char *documentation,
+                                               /* Return values */
+                                               GtkWidget **enclosing,
+                                               bool *packed)
+{
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+    auto widget = gtk_entry_new();
+    if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+        gtk_entry_set_alignment (GTK_ENTRY(widget), 1.0);
+    auto ui_item{std::make_unique<GncGtkStringUIItem>(widget)};
+
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+    option.set_ui_item(std::move(ui_item));
+    option.set_ui_item_from_option();
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, TRUE, TRUE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+class GncGtkTextUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkTextUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::TEXT} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GTK_TEXT_VIEW(get_widget())};
+        xxxgtk_textview_set_text(widget, option.get_value<std::string>().c_str());
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GTK_TEXT_VIEW(get_widget())};
+        option.set_value(xxxgtk_textview_get_text(widget));
+    }
+};
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::TEXT> (GncOption& option, GtkGrid *page_box,
+                               GtkLabel *name_label, char *documentation,
+                               /* Return values */
+                               GtkWidget **enclosing, bool *packed)
+{
+    align_label (name_label);
+
+    auto scroll = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+                                   GTK_POLICY_NEVER,
+                                   GTK_POLICY_AUTOMATIC);
+    gtk_container_set_border_width(GTK_CONTAINER(scroll), 2);
+
+    auto frame = gtk_frame_new(NULL);
+    gtk_container_add(GTK_CONTAINER(frame), scroll);
+
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+    gtk_widget_set_vexpand (GTK_WIDGET(*enclosing), TRUE);
+    gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+    auto widget = gtk_text_view_new();
+    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD);
+    gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), TRUE);
+    gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW(widget), FALSE);
+    gtk_container_add (GTK_CONTAINER (scroll), widget);
+
+    auto ui_item{std::make_unique<GncGtkTextUIItem>(widget)};
+    auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
+    g_signal_connect(G_OBJECT(text_buffer), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+    option.set_ui_item(std::move(ui_item));
+    option.set_ui_item_from_option();
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), frame, TRUE, TRUE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+class GncGtkCurrencyUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkCurrencyUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::CURRENCY} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GNC_CURRENCY_EDIT(get_widget())};
+        auto currency =
+            GNC_COMMODITY(option.get_value<const QofInstance*>());
+        gnc_currency_edit_set_currency(widget, currency);
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GNC_CURRENCY_EDIT(get_widget())};
+        auto currency = gnc_currency_edit_get_currency(widget);
+        option.set_value(qof_instance_cast(currency));
+    }
+};
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::CURRENCY> (GncOption& option, GtkGrid *page_box,
+                                   GtkLabel *name_label, char *documentation,
+                                   /* Return values */
+                                   GtkWidget **enclosing, bool *packed)
+{
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+    auto widget = gnc_currency_edit_new();
+    auto ui_item{std::make_unique<GncGtkCurrencyUIItem>(widget)};
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+    option.set_ui_item(std::move(ui_item));
+    option.set_ui_item_from_option();
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+class GncGtkCommodityUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkCommodityUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::COMMODITY} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GNC_GENERAL_SELECT(get_widget())};
+        auto commodity =
+            GNC_COMMODITY(option.get_value<const QofInstance*>());
+        gnc_general_select_set_selected(widget, commodity);
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GNC_GENERAL_SELECT(get_widget())};
+        auto commodity{gnc_general_select_get_selected(widget)};
+        option.set_value(qof_instance_cast(commodity));
+    }
+};
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::COMMODITY> (GncOption& option, GtkGrid *page_box,
+                                    GtkLabel *name_label, char *documentation,
+                                    /* Return values */
+                                    GtkWidget **enclosing, bool *packed)
+{
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+    auto widget = gnc_general_select_new(GNC_GENERAL_SELECT_TYPE_SELECT,
+                                   gnc_commodity_edit_get_string,
+                                   gnc_commodity_edit_new_select,
+                                   NULL);
+
+    auto ui_item{std::make_unique<GncGtkCommodityUIItem>(widget)};
+
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+    option.set_ui_item(std::move(ui_item));
+    option.set_ui_item_from_option();
+
+    if (documentation != NULL)
+        gtk_widget_set_tooltip_text(GNC_GENERAL_SELECT(widget)->entry,
+                                    documentation);
+
+    g_signal_connect(G_OBJECT(GNC_GENERAL_SELECT(widget)->entry), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+static GtkWidget*
+create_multichoice_widget(GncOption& option)
+{
+    auto num_values = option.num_permissible_values();
+
+    g_return_val_if_fail(num_values >= 0, NULL);
+
+    /* GtkComboBox still does not support per-item tooltips, so have
+       created a basic one called Combott implemented in gnc-combott.
+       Have highlighted changes in this file with comments for when
+       the feature of per-item tooltips is implemented in gtk,
+       see https://bugs.gnucash.org/show_bug.cgi?id=303717 */
+
+    auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+    /* Add values to the list store, entry and tooltip */
+    for (auto i = 0; i < num_values; i++)
+    {
+        GtkTreeIter iter;
+        auto itemstring = option.permissible_value_name(i);
+        auto description = option.permissible_value_description(i);
+        gtk_list_store_append (store, &iter);
+        gtk_list_store_set (store, &iter, 0,
+                            (itemstring && *itemstring) ? _(itemstring) : "", 1,
+                            (description && *description) ? _(description) : "", -1);
+    }
+    /* Create the new Combo with tooltip and add the store */
+    auto widget = GTK_WIDGET(gnc_combott_new());
+    g_object_set( G_OBJECT( widget ), "model", GTK_TREE_MODEL(store), NULL );
+    g_object_unref(store);
+
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    return widget;
+}
+
+class GncGtkMultichoiceUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkMultichoiceUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::MULTICHOICE} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GNC_COMBOTT(get_widget())};
+        gnc_combott_set_active(widget, option.get_value<size_t>());
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GNC_COMBOTT(get_widget())};
+        option.set_value<size_t>(gnc_combott_get_active(widget));
+    }
+};
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::MULTICHOICE> (GncOption& option, GtkGrid *page_box,
+                                      GtkLabel *name_label, char *documentation,
+                                      /* Return values */
+                                      GtkWidget **enclosing, bool *packed)
+{
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+
+    auto widget = create_multichoice_widget(option);
+    auto ui_item{std::make_unique<GncGtkMultichoiceUIItem>(widget)};
+
+//GncCombott doesn't emit a changed signal
+    option.set_ui_item(std::move(ui_item));
+    option.set_ui_item_from_option();
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+
+class GncDateEntry
+{
+public:
+    GncDateEntry() = default;
+    virtual ~GncDateEntry() = default;
+    virtual void set_entry_from_option(GncOption& option) = 0;
+    virtual void set_option_from_entry(GncOption& option) = 0;
+    // Get the widget that has data
+    virtual GtkWidget* get_entry() = 0;
+    // Get the widget that gets put on the page
+    virtual GtkWidget* get_widget() = 0;
+    virtual void toggle_relative(bool) {} //BothDateEntry only
+};
+
+
+using GncDateEntryPtr = std::unique_ptr<GncDateEntry>;
+
+class AbsoluteDateEntry : public GncDateEntry
+{
+public:
+    AbsoluteDateEntry(GncOption& option);
+    ~AbsoluteDateEntry() { g_object_unref(G_OBJECT(m_entry)); }
+    void set_entry_from_option(GncOption& option) override;
+    void set_option_from_entry(GncOption& option) override;
+    GtkWidget* get_entry() override { return GTK_WIDGET(m_entry); }
+    GtkWidget* get_widget() override { return GTK_WIDGET(m_entry); }
+private:
+    GNCDateEdit* m_entry;
+};
+
+AbsoluteDateEntry::AbsoluteDateEntry(GncOption& option) :
+    m_entry{GNC_DATE_EDIT(gnc_date_edit_new(time(NULL), FALSE, FALSE))}
+{
+    auto entry = GNC_DATE_EDIT(m_entry)->date_entry;
+    g_signal_connect(G_OBJECT(entry), "changed",
+                     G_CALLBACK(option_changed_cb), &option);
+}
+
+void
+AbsoluteDateEntry::set_entry_from_option(GncOption& option)
+{
+    gnc_date_edit_set_time(m_entry, option.get_value<time64>());
+}
+
+void
+AbsoluteDateEntry::set_option_from_entry(GncOption& option)
+{
+    option.set_value<time64>(gnc_date_edit_get_date(m_entry));
+}
+
+class RelativeDateEntry : public GncDateEntry
+{
+public:
+    RelativeDateEntry(GncOption& option);
+    ~RelativeDateEntry() { g_object_unref(G_OBJECT(m_entry)); };
+    void set_entry_from_option(GncOption& option) override;
+    void set_option_from_entry(GncOption& option) override;
+    GtkWidget* get_widget() override { return m_entry; }
+    GtkWidget* get_entry() override { return m_entry; }
+private:
+    GtkWidget* m_entry;
+};
+
+
+RelativeDateEntry::RelativeDateEntry(GncOption& option) :
+    m_entry{GTK_WIDGET(gnc_combott_new())}
+
+{
+
+    /* GtkComboBox still does not support per-item tooltips, so have
+       created a basic one called Combott implemented in gnc-combott.
+       Have highlighted changes in this file with comments for when
+       the feature of per-item tooltips is implemented in gtk,
+       see https://bugs.gnucash.org/show_bug.cgi?id=303717 */
+
+    auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+    /* Add values to the list store, entry and tooltip */
+    auto num = option.num_permissible_values();
+    for (auto index = 0; index < num; ++index)
+    {
+        GtkTreeIter  iter;
+        gtk_list_store_append (store, &iter);
+        gtk_list_store_set (store, &iter, 0,
+                            option.permissible_value_name(index), 1,
+                            option.permissible_value_description(index), -1);
+    }
+
+    /* Create the new Combo with tooltip and add the store */
+    g_object_set( G_OBJECT(m_entry), "model", GTK_TREE_MODEL(store), nullptr);
+    g_object_unref(store);
+
+    g_signal_connect(G_OBJECT(m_entry), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+}
+
+void
+RelativeDateEntry::set_entry_from_option(GncOption& option)
+{
+    gnc_combott_set_active(GNC_COMBOTT(m_entry), option.get_value<size_t>());
+}
+
+void
+RelativeDateEntry::set_option_from_entry(GncOption& option)
+{
+    option.set_value<size_t>(gnc_combott_get_active(GNC_COMBOTT(m_entry)));
+}
+
+using AbsoluteDateEntryPtr = std::unique_ptr<AbsoluteDateEntry>;
+using RelativeDateEntryPtr = std::unique_ptr<RelativeDateEntry>;
+
+class BothDateEntry : public GncDateEntry
+{
+public:
+    BothDateEntry(GncOption& option);
+    ~BothDateEntry(); //The GncOptionGtkUIItem owns the widget
+    void set_entry_from_option(GncOption& option) override;
+    void set_option_from_entry(GncOption& option) override;
+    GtkWidget* get_widget() override { return m_widget; }
+    GtkWidget* get_entry() override;
+    void toggle_relative(bool use_absolute) override;
+private:
+    GtkWidget* m_widget;
+    GtkWidget* m_abs_button;
+    AbsoluteDateEntryPtr m_abs_entry;
+    GtkWidget* m_rel_button;
+    RelativeDateEntryPtr m_rel_entry;
+    bool m_use_absolute = true;
+};
+
+static void date_set_absolute_cb(GtkWidget *widget, gpointer data1);
+static void date_set_relative_cb(GtkWidget *widget, gpointer data1);
+
+BothDateEntry::BothDateEntry(GncOption& option) :
+    m_widget{gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5)},
+    m_abs_button{gtk_radio_button_new(NULL)},
+    m_abs_entry{std::make_unique<AbsoluteDateEntry>(option)},
+    m_rel_button{
+        gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(m_abs_button))},
+    m_rel_entry{std::make_unique<RelativeDateEntry>(option)}
+{
+    gtk_box_set_homogeneous (GTK_BOX(m_widget), FALSE);
+    g_signal_connect(G_OBJECT(m_abs_button), "toggled",
+                     G_CALLBACK(date_set_absolute_cb), &option);
+    g_signal_connect(G_OBJECT(m_rel_button), "toggled",
+                     G_CALLBACK(date_set_relative_cb), &option);
+
+    gtk_box_pack_start(GTK_BOX(m_widget),
+                       m_abs_button, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(m_widget),
+                       m_abs_entry->get_entry(), FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(m_widget),
+                       m_rel_button, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(m_widget),
+                       m_rel_entry->get_entry(), FALSE, FALSE, 0);
+
+}
+
+BothDateEntry::~BothDateEntry()
+{
+    g_object_unref(G_OBJECT(m_abs_button));
+    g_object_unref(G_OBJECT(m_rel_button));
+}
+
+GtkWidget*
+BothDateEntry::get_entry()
+{
+    return m_use_absolute ? m_abs_entry->get_entry() : m_rel_entry->get_entry();
+}
+
+void
+BothDateEntry::toggle_relative(bool use_absolute)
+{
+    if (use_absolute)
+    {
+        gtk_widget_set_sensitive(m_abs_entry->get_entry(), TRUE);
+        gtk_widget_set_sensitive(m_rel_entry->get_entry(), FALSE);
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_abs_button), TRUE);
+    }
+    else
+    {
+        gtk_widget_set_sensitive(m_rel_entry->get_entry(), TRUE);
+        gtk_widget_set_sensitive(m_abs_entry->get_entry(), FALSE);
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_rel_button), TRUE);
+    }
+    m_use_absolute = use_absolute;
+}
+
+void
+BothDateEntry::set_entry_from_option(GncOption& option)
+{
+    if (m_use_absolute)
+        m_abs_entry->set_entry_from_option(option);
+    else
+        m_rel_entry->set_entry_from_option(option);
+}
+
+void
+BothDateEntry::set_option_from_entry(GncOption& option)
+{
+    if (m_use_absolute)
+        m_abs_entry->set_option_from_entry(option);
+    else
+        m_rel_entry->set_option_from_entry(option);
+}
+
+
+class GncOptionDateUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncOptionDateUIItem(GncDateEntryPtr entry, GncOptionUIType type) :
+        GncOptionGtkUIItem{nullptr, type}, m_entry{std::move(entry)} { }
+    ~GncOptionDateUIItem() = default;
+    void clear_ui_item() override { m_entry = nullptr; }
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        if (m_entry)
+            m_entry->set_entry_from_option(option);
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        if (m_entry)
+            m_entry->set_option_from_entry(option);
+    }
+    GtkWidget* const get_widget() const override
+    {
+        return m_entry->get_widget();
+    }
+    GncDateEntry* get_entry() { return m_entry.get(); }
+private:
+    GncDateEntryPtr m_entry;
+};
+
+static void
+date_set_absolute_cb(GtkWidget *widget, gpointer data1)
+{
+    GncOption* option = static_cast<decltype(option)>(data1);
+    auto ui_item = option->get_ui_item();
+    if (auto date_ui = dynamic_cast<const GncOptionDateUIItem* const>(ui_item))
+    {
+        const_cast<GncOptionDateUIItem*>(date_ui)->get_entry()->toggle_relative(true);
+        option_changed_cb(widget, option);
+    }
+}
+
+static void
+date_set_relative_cb(GtkWidget *widget, gpointer data1)
+{
+    GncOption* option = static_cast<decltype(option)>(data1);
+    auto ui_item = option->get_ui_item();
+    if (auto date_ui = dynamic_cast<const GncOptionDateUIItem* const>(ui_item))
+    {
+        const_cast<GncOptionDateUIItem*>(date_ui)->get_entry()->toggle_relative(false);
+        option_changed_cb(widget, option);
+    }
+}
+
+GtkWidget*
+create_date_option_widget(GncOption& option, GtkGrid *page_box,
+                          GtkLabel *name_label, char *documentation,
+                               /* Return values */
+                          GtkWidget **enclosing, bool *packed)
+{
+    auto grid_row = GPOINTER_TO_INT(g_object_get_data
+                                    (G_OBJECT(page_box), "options-grid-row"));
+    auto type = option.get_ui_type();
+    switch (type)
+    {
+        case GncOptionUIType::DATE_ABSOLUTE:
+            option.set_ui_item(std::make_unique<GncOptionDateUIItem>(std::make_unique<AbsoluteDateEntry>(option), type));
+            break;
+        case GncOptionUIType::DATE_RELATIVE:
+            option.set_ui_item(std::make_unique<GncOptionDateUIItem>(std::make_unique<RelativeDateEntry>(option), type));
+            break;
+        case GncOptionUIType::DATE_BOTH:
+            option.set_ui_item(std::make_unique<GncOptionDateUIItem>(std::make_unique<BothDateEntry>(option), type));
+            break;
+        default:
+            PERR("Attempted to create date option widget with wrong UI type %d", type);
+            return nullptr;
+            break;
+    }
+
+    option.set_ui_item_from_option();
+    auto widget{gnc_option_get_gtk_widget(option)};
+    if (type == GncOptionUIType::DATE_RELATIVE)
+    {
+        *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+        gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+
+        gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    }
+    else
+    {
+        *enclosing = gtk_frame_new (NULL);
+        g_object_set (G_OBJECT(widget), "margin", 3, NULL);
+
+        gtk_container_add (GTK_CONTAINER(*enclosing), widget);
+    }
+
+    gtk_widget_set_halign (GTK_WIDGET(*enclosing), GTK_ALIGN_START);
+
+    /* Pack option widget into an extra eventbox because otherwise the
+       "documentation" tooltip is not displayed. */
+    auto eventbox = gtk_event_box_new();
+    gtk_container_add (GTK_CONTAINER (eventbox), *enclosing);
+
+    gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1);
+    *packed = TRUE;
+
+    gtk_widget_set_tooltip_text (eventbox, documentation);
+
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::DATE_ABSOLUTE>(GncOption& option,
+                                                     GtkGrid *page_box,
+                                                     GtkLabel *name_label,
+                                                     char *documentation,
+                                                     /* Return values */
+                                                     GtkWidget **enclosing,
+                                                     bool *packed)
+{
+    return create_date_option_widget(option, page_box, name_label,
+                                     documentation, enclosing, packed);
+}
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::DATE_RELATIVE>(GncOption& option,
+                                                     GtkGrid *page_box,
+                                                     GtkLabel *name_label,
+                                                     char *documentation,
+                                                     /* Return values */
+                                                     GtkWidget **enclosing,
+                                                     bool *packed)
+{
+    return create_date_option_widget(option, page_box, name_label,
+                                     documentation, enclosing, packed);
+}
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::DATE_BOTH>(GncOption& option,
+                                                 GtkGrid *page_box,
+                                                 GtkLabel *name_label,
+                                                 char *documentation,
+                                                 /* Return values */
+                                                 GtkWidget **enclosing,
+                                                 bool *packed)
+{
+    return create_date_option_widget(option, page_box, name_label,
+                                     documentation, enclosing, packed);
+}
+
+using GncOptionAccountList = std::vector<const Account*>;
+
+static void
+account_select_all_cb(GtkWidget *widget, gpointer data)
+{
+    GncOption* option = static_cast<decltype(option)>(data);
+    GncTreeViewAccount *tree_view;
+    GtkTreeSelection *selection;
+
+    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option));
+    gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+    gtk_tree_selection_select_all(selection);
+    widget_changed_cb(widget, option);
+}
+
+static void
+account_clear_all_cb(GtkWidget *widget, gpointer data)
+{
+    GncOption* option = static_cast<decltype(option)>(data);
+    GncTreeViewAccount *tree_view;
+    GtkTreeSelection *selection;
+
+    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option));
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+    gtk_tree_selection_unselect_all(selection);
+    widget_changed_cb(widget, option);
+}
+
+static void
+account_select_children_cb(GtkWidget *widget, gpointer data)
+{
+    GncOption* option = static_cast<decltype(option)>(data);
+    GncTreeViewAccount *tree_view;
+    GList *acct_list = NULL, *acct_iter = NULL;
+
+    tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option));
+    acct_list = gnc_tree_view_account_get_selected_accounts (tree_view);
+
+    for (acct_iter = acct_list; acct_iter; acct_iter = acct_iter->next)
+        gnc_tree_view_account_select_subaccounts (tree_view, static_cast<Account*>(acct_iter->data));
+
+    g_list_free (acct_list);
+}
+
+static void
+account_set_default_cb(GtkWidget* widget, gpointer data)
+{
+    GncOption* option = static_cast<decltype(option)>(data);
+    account_clear_all_cb(widget, data);
+    option->set_value(option->get_default_value<GncOptionAccountList>());
+    option->set_ui_item_from_option();
+}
+
+static void
+show_hidden_toggled_cb(GtkWidget *widget, GncOption* option)
+{
+    if (option->get_ui_type() != GncOptionUIType::ACCOUNT_LIST &&
+        option->get_ui_type() != GncOptionUIType::ACCOUNT_SEL)
+        return;
+
+    auto tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option));
+    AccountViewInfo avi;
+    gnc_tree_view_account_get_view_info (tree_view, &avi);
+    avi.show_hidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+    gnc_tree_view_account_set_view_info (tree_view, &avi);
+    widget_changed_cb(widget, option);
+}
+
+class GncGtkAccountListUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkAccountListUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::ACCOUNT_LIST} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GNC_TREE_VIEW_ACCOUNT(get_widget())};
+        GList *acc_list = nullptr;
+        const GncOptionAccountList& accounts =
+            option.get_value<GncOptionAccountList>();
+        for (auto account : accounts)
+            g_list_prepend(acc_list, static_cast<void*>(const_cast<Account*>(account)));
+        g_list_reverse(acc_list);
+        gnc_tree_view_account_set_selected_accounts(widget, acc_list, TRUE);
+        g_list_free(acc_list);
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GNC_TREE_VIEW_ACCOUNT(get_widget())};
+        auto acc_list = gnc_tree_view_account_get_selected_accounts(widget);
+        GncOptionAccountList acc_vec(g_list_length(acc_list));
+        for (auto node = acc_list; node; node = g_list_next(node))
+            acc_vec.push_back(static_cast<const Account*>(node->data));
+        g_list_free(acc_list);
+        option.set_value(acc_vec);
+    }
+};
+
+GtkWidget*
+create_account_widget(GncOption& option, char *name)
+{
+    bool multiple_selection;
+    GtkWidget *scroll_win;
+    GtkWidget *button;
+    GtkWidget *frame;
+    GtkWidget *tree;
+    GtkWidget *vbox;
+    GtkWidget *bbox;
+    GList *acct_type_list;
+    GtkTreeSelection *selection;
+
+    multiple_selection = option.is_multiselect();
+    acct_type_list = option.account_type_list();
+
+    frame = gtk_frame_new(name);
+
+    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+    gtk_box_set_homogeneous (GTK_BOX (vbox), FALSE);
+
+    gtk_container_add(GTK_CONTAINER(frame), vbox);
+
+    tree = GTK_WIDGET(gnc_tree_view_account_new (FALSE));
+    gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(tree), FALSE);
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree));
+    if (multiple_selection)
+        gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+    else
+        gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+
+    if (acct_type_list)
+    {
+        GList *node;
+        AccountViewInfo avi;
+        int i;
+
+        gnc_tree_view_account_get_view_info (GNC_TREE_VIEW_ACCOUNT (tree), &avi);
+
+        for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
+            avi.include_type[i] = FALSE;
+        avi.show_hidden = FALSE;
+
+        for (node = acct_type_list; node; node = node->next)
+        {
+            GNCAccountType type = static_cast<decltype(type)>(GPOINTER_TO_INT (node->data));
+            avi.include_type[type] = TRUE;
+        }
+
+        gnc_tree_view_account_set_view_info (GNC_TREE_VIEW_ACCOUNT (tree), &avi);
+        g_list_free (acct_type_list);
+    }
+    else
+    {
+        AccountViewInfo avi;
+        int i;
+
+        gnc_tree_view_account_get_view_info (GNC_TREE_VIEW_ACCOUNT (tree), &avi);
+
+        for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
+            avi.include_type[i] = TRUE;
+        avi.show_hidden = FALSE;
+        gnc_tree_view_account_set_view_info (GNC_TREE_VIEW_ACCOUNT (tree), &avi);
+    }
+
+    scroll_win = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
+                                   GTK_POLICY_AUTOMATIC,
+                                   GTK_POLICY_AUTOMATIC);
+
+    gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(scroll_win), 5);
+    gtk_container_add(GTK_CONTAINER(scroll_win), tree);
+
+    bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 10);
+
+    if (multiple_selection)
+    {
+        button = gtk_button_new_with_label(_("Select All"));
+        gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+        gtk_widget_set_tooltip_text(button, _("Select all accounts."));
+
+        g_signal_connect(G_OBJECT(button), "clicked",
+                         G_CALLBACK(account_select_all_cb), &option);
+
+        button = gtk_button_new_with_label(_("Clear All"));
+        gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+        gtk_widget_set_tooltip_text(button, _("Clear the selection and unselect all accounts."));
+
+        g_signal_connect(G_OBJECT(button), "clicked",
+                         G_CALLBACK(account_clear_all_cb), &option);
+
+        button = gtk_button_new_with_label(_("Select Children"));
+        gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+        gtk_widget_set_tooltip_text(button, _("Select all descendents of selected account."));
+
+        g_signal_connect(G_OBJECT(button), "clicked",
+                         G_CALLBACK(account_select_children_cb), &option);
+    }
+
+    button = gtk_button_new_with_label(_("Select Default"));
+    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+    gtk_widget_set_tooltip_text(button, _("Select the default account selection."));
+
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(account_set_default_cb), &option);
+
+    gtk_widget_set_margin_start (GTK_WIDGET(bbox), 6);
+    gtk_widget_set_margin_end (GTK_WIDGET(bbox), 6);
+
+    if (multiple_selection)
+    {
+        /* Put the "Show hidden" checkbox on a separate line since the 4 buttons make
+           the dialog too wide. */
+        bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+        gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);
+        gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+    }
+
+    button = gtk_check_button_new_with_label(_("Show Hidden Accounts"));
+    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+    gtk_widget_set_tooltip_text(button, _("Show accounts that have been marked hidden."));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+    g_signal_connect(G_OBJECT(button), "toggled",
+                     G_CALLBACK(show_hidden_toggled_cb), &option);
+
+    option.set_ui_item(std::make_unique<GncGtkAccountListUIItem>(tree));
+    option.set_ui_item_from_option();
+
+    return frame;
+}
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::ACCOUNT_LIST>(GncOption& option,
+                                                    GtkGrid *page_box,
+                                                    GtkLabel *name_label,
+                                                    char *documentation,
+                                                    /* Return values */
+                                                    GtkWidget **enclosing,
+                                                    bool *packed)
+{
+    align_label (name_label);
+
+    *enclosing = create_account_widget(option, NULL);
+    gtk_widget_set_vexpand (GTK_WIDGET(*enclosing), TRUE);
+    gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE);
+
+    gtk_widget_set_tooltip_text(*enclosing, documentation);
+
+    auto  grid_row = GPOINTER_TO_INT(g_object_get_data
+                                    (G_OBJECT(page_box), "options-grid-row"));
+    gtk_grid_attach (GTK_GRID(page_box), *enclosing, 1, grid_row, 1, 1);
+    *packed = TRUE;
+
+    auto widget = gnc_option_get_gtk_widget(option);
+    auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
+    g_signal_connect(G_OBJECT(selection), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    //  gtk_clist_set_row_height(GTK_CLIST(value), 0);
+    //  gtk_widget_set_size_request(value, -1, GTK_CLIST(value)->row_height * 10);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+class GncGtkAccountSelUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkAccountSelUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::ACCOUNT_SEL} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GNC_ACCOUNT_SEL(get_widget())};
+        gnc_account_sel_set_account(widget,
+                                    GNC_ACCOUNT(option.get_value<const QofInstance*>()),
+                                    FALSE);
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GNC_ACCOUNT_SEL(get_widget())};
+        option.set_value(qof_instance_cast((gnc_account_sel_get_account(widget))));
+    }
+};
+
+template<> GtkWidget*
+create_option_widget<GncOptionUIType::ACCOUNT_SEL> (GncOption& option,
+                                                    GtkGrid *page_box,
+                                                    GtkLabel *name_label,
+                                                    char *documentation,
+                                                    /* Return values */
+                                                    GtkWidget **enclosing,
+                                                    bool *packed)
+{
+    auto acct_type_list = option.account_type_list();
+    auto widget = gnc_account_sel_new ();
+    gnc_account_sel_set_acct_filters(GNC_ACCOUNT_SEL(widget),
+                                     acct_type_list, NULL);
+    g_list_free(acct_type_list);
+    g_signal_connect(widget, "account_sel_changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+
+// gnc_account_sel doesn't emit a changed signal
+    option.set_ui_item(std::make_unique<GncGtkAccountSelUIItem>(widget));
+    option.set_ui_item_from_option();
+
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+static void
+list_changed_cb(GtkTreeSelection *selection, GncOption* option)
+{
+    GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (*option));
+    widget_changed_cb(GTK_WIDGET(view), option);
+}
+
+static void
+list_select_all_cb(GtkWidget *widget, gpointer data)
+{
+    GncOption* option = static_cast<decltype(option)>(data);
+    GtkTreeView *view;
+    GtkTreeSelection *selection;
+
+    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(*option));
+    selection = gtk_tree_view_get_selection(view);
+    gtk_tree_selection_select_all(selection);
+    widget_changed_cb(GTK_WIDGET(view), option);
+}
+
+static void
+list_clear_all_cb(GtkWidget *widget, gpointer data)
+{
+    GncOption* option = static_cast<decltype(option)>(data);
+    GtkTreeView *view;
+    GtkTreeSelection *selection;
+
+    view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (*option));
+    selection = gtk_tree_view_get_selection(view);
+    gtk_tree_selection_unselect_all(selection);
+    widget_changed_cb(GTK_WIDGET(view), option);
+}
+
+static void
+list_set_default_cb(GtkWidget *widget, gpointer data)
+{
+    GncOption* option = static_cast<decltype(option)>(data);
+    list_clear_all_cb(widget, data);
+    option->set_value(option->get_default_value<GncMultichoiceOptionIndexVec>());
+    option->set_ui_item_from_option();
+}
+
+class GncGtkListUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkListUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::MULTICHOICE} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GTK_TREE_VIEW(get_widget())};
+        auto selection{gtk_tree_view_get_selection(widget)};
+        gtk_tree_selection_unselect_all(selection);
+        for (auto index : option.get_value<GncMultichoiceOptionIndexVec>())
+        {
+            auto path{gtk_tree_path_new_from_indices(index, -1)};
+            gtk_tree_selection_select_path(selection, path);
+            gtk_tree_path_free(path);
+        }
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GTK_TREE_VIEW(get_widget())};
+        auto selection{gtk_tree_view_get_selection(widget)};
+        auto rows{option.num_permissible_values()};
+        GncMultichoiceOptionIndexVec vec;
+        for (size_t row = 0; row < rows; ++row)
+        {
+            auto path{gtk_tree_path_new_from_indices(row, -1)};
+            auto selected{gtk_tree_selection_path_is_selected(selection, path)};
+            gtk_tree_path_free(path);
+            if (selected)
+                vec.push_back(row);
+        }
+        option.set_value(vec);
+    }
+};
+
+static GtkWidget *
+create_list_widget(GncOption& option, char *name)
+{
+    GtkListStore *store;
+    GtkTreeView *view;
+    GtkTreeIter iter;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *renderer;
+    GtkTreeSelection *selection;
+
+    GtkWidget *button;
+    GtkWidget *frame;
+    GtkWidget *hbox;
+    GtkWidget *bbox;
+
+    frame = gtk_frame_new(name);
+    hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+    gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
+    gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+    store = gtk_list_store_new(1, G_TYPE_STRING);
+    view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
+    g_object_unref(store);
+    renderer = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes("", renderer,
+             "text", 0,
+             NULL);
+    gtk_tree_view_append_column(view, column);
+    gtk_tree_view_set_headers_visible(view, FALSE);
+
+    auto num_values = option.num_permissible_values();
+    for (auto i = 0; i < num_values; i++)
+    {
+        auto raw_string = option.permissible_value_name(i);
+        auto string = (raw_string && *raw_string) ? _(raw_string) : "";
+        gtk_list_store_append(store, &iter);
+        gtk_list_store_set(store, &iter, 0, string ? string : "", -1);
+    }
+
+    gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0);
+
+    selection = gtk_tree_view_get_selection(view);
+    gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+    g_signal_connect(selection, "changed",
+                     G_CALLBACK(list_changed_cb), &option);
+
+    bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
+    gtk_box_pack_end(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
+
+    button = gtk_button_new_with_label(_("Select All"));
+    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+    gtk_widget_set_tooltip_text(button, _("Select all entries."));
+
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(list_select_all_cb), &option);
+
+    button = gtk_button_new_with_label(_("Clear All"));
+    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+    gtk_widget_set_tooltip_text(button, _("Clear the selection and unselect all entries."));
+
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(list_clear_all_cb), &option);
+
+    button = gtk_button_new_with_label(_("Select Default"));
+    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+    gtk_widget_set_tooltip_text(button, _("Select the default selection."));
+
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(list_set_default_cb), &option);
+
+    g_object_set (G_OBJECT(hbox), "margin", 3, NULL);
+
+    option.set_ui_item(std::make_unique<GncGtkListUIItem>(GTK_WIDGET(view)));
+    option.set_ui_item_from_option();
+
+    return frame;
+}
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::LIST> (GncOption& option,
+                                             GtkGrid *page_box,
+                                             GtkLabel *name_label,
+                                             char *documentation,
+                                             /* Return values */
+                                             GtkWidget **enclosing,
+                                             bool *packed)
+{
+    GtkWidget *value;
+    GtkWidget *eventbox;
+    gint       grid_row = GPOINTER_TO_INT(g_object_get_data
+                                         (G_OBJECT(page_box), "options-grid-row"));
+
+    *enclosing = create_list_widget(option, NULL);
+    value = gnc_option_get_gtk_widget (option);
+
+    align_label (name_label);
+
+    /* Pack option widget into an extra eventbox because otherwise the
+       "documentation" tooltip is not displayed. */
+    eventbox = gtk_event_box_new();
+    gtk_container_add (GTK_CONTAINER (eventbox), *enclosing);
+
+    gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1);
+    *packed = TRUE;
+
+    gtk_widget_set_tooltip_text(eventbox, documentation);
+
+    gtk_widget_show(*enclosing);
+    return value;
+}
+
+class GncGtkNumberRangeUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkNumberRangeUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::NUMBER_RANGE} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        gtk_spin_button_set_value(GTK_SPIN_BUTTON(get_widget()),
+                                  option.get_value<double>());
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget())));
+    }
+};
+
+/* New spin button configured with the values provided by the passed-in
+ * GncOption, which had better contain a GncOptionRangeValue.
+ *
+ * Also used to set up the pixel spinner in the plot_size control.
+ */
+static GtkSpinButton*
+create_range_spinner(GncOption& option)
+{
+    gdouble lower_bound = G_MINDOUBLE;
+    gdouble upper_bound = G_MAXDOUBLE;
+    gdouble step_size = 1.0;
+
+    option.get_limits(upper_bound, lower_bound, step_size);
+    auto adj = GTK_ADJUSTMENT(gtk_adjustment_new(lower_bound, lower_bound,
+                                                 upper_bound, step_size,
+                                                 step_size * 5.0,
+                                                 0));
+
+    size_t num_decimals = 0;
+    for (auto steps = step_size; steps < 1; steps *= 10)
+        ++num_decimals;
+    auto widget = gtk_spin_button_new(adj, step_size, num_decimals);
+    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(widget), TRUE);
+
+    size_t num_digits = 0;
+    for (auto bigger = MAX(ABS(lower_bound), ABS(upper_bound));
+         bigger >= 1; bigger /= 10.0)
+        ++num_digits;
+    num_digits += num_decimals;
+    gtk_entry_set_width_chars(GTK_ENTRY(widget), num_digits);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),
+                              (upper_bound / 2)); //default
+    return GTK_SPIN_BUTTON(widget);
+}
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::NUMBER_RANGE> (GncOption& option,
+                                                     GtkGrid *page_box,
+                                                     GtkLabel *name_label,
+                                                     char *documentation,
+                                                     /* Return values */
+                                                     GtkWidget **enclosing,
+                                                     bool *packed)
+{
+    GtkAdjustment *adj;
+    size_t num_decimals = 0;
+
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+
+    auto widget = create_range_spinner(option);
+    option.set_ui_item(std::make_unique<GncGtkNumberRangeUIItem>(GTK_WIDGET(widget)));
+    option.set_ui_item_from_option();
+
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), GTK_WIDGET(widget),
+                       FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return GTK_WIDGET(widget);
+}
+
+class GncGtkColorUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkColorUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::COLOR} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        GdkRGBA color;
+        if (gdk_rgba_parse(&color,
+                           option.get_value<std::string>().c_str()))
+        {
+            auto color_button = GTK_COLOR_CHOOSER(get_widget());
+            gtk_color_chooser_set_rgba(color_button, &color);
+        }
+     }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        GdkRGBA color;
+        auto color_button = GTK_COLOR_CHOOSER(get_widget());
+        gtk_color_chooser_set_rgba(color_button, &color);
+        auto rgba_str = gdk_rgba_to_string(&color);
+        option.set_value(rgba_str);
+        g_free(rgba_str);
+    }
+};
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::COLOR> (GncOption& option, GtkGrid *page_box,
+                                GtkLabel *name_label, char *documentation,
+                                /* Return values */
+                                GtkWidget **enclosing, bool *packed)
+{
+    bool use_alpha;
+
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+
+    auto widget = gtk_color_button_new();
+    gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(widget), TRUE);
+
+    option.set_ui_item(std::make_unique<GncGtkColorUIItem>(widget));
+    option.set_ui_item_from_option();
+
+    g_signal_connect(G_OBJECT(widget), "color-set",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+class GncGtkFontUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkFontUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::FONT} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        GtkFontButton *font_button = GTK_FONT_BUTTON(get_widget());
+        gtk_font_button_set_font_name(font_button, option.get_value<std::string>().c_str());
+
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        GtkFontButton *font_button = GTK_FONT_BUTTON(get_widget());
+        option.set_value(gtk_font_button_get_font_name(font_button));
+    }
+};
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::FONT> (GncOption& option, GtkGrid *page_box,
+                               GtkLabel *name_label, char *documentation,
+                               /* Return values */
+                               GtkWidget **enclosing, bool *packed)
+{
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+    auto widget = gtk_font_button_new();
+    g_object_set(G_OBJECT(widget),
+                 "use-font", TRUE,
+                 "show-style", TRUE,
+                 "show-size", TRUE,
+                 (char *)NULL);
+
+    option.set_ui_item(std::make_unique<GncGtkFontUIItem>(widget));
+    option.set_ui_item_from_option();
+
+    g_signal_connect(G_OBJECT(widget), "font-set",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+static void
+update_preview_cb (GtkFileChooser *chooser, void* data)
+{
+    g_return_if_fail(chooser != NULL);
+
+    ENTER("chooser %p", chooser);
+    auto filename = gtk_file_chooser_get_preview_filename(chooser);
+    DEBUG("chooser preview name is %s.", filename ? filename : "(null)");
+    if (filename == NULL)
+    {
+        filename = g_strdup(static_cast<const char*>(g_object_get_data(G_OBJECT(chooser), LAST_SELECTION)));
+        DEBUG("using last selection of %s", filename ? filename : "(null)");
+        if (filename == NULL)
+        {
+            LEAVE("no usable name");
+            return;
+        }
+    }
+
+    auto image = GTK_IMAGE(gtk_file_chooser_get_preview_widget(chooser));
+    auto pixbuf = gdk_pixbuf_new_from_file_at_size(filename, 128, 128, NULL);
+    g_free(filename);
+    auto have_preview = (pixbuf != NULL);
+
+    gtk_image_set_from_pixbuf(image, pixbuf);
+    if (pixbuf)
+        g_object_unref(pixbuf);
+
+    gtk_file_chooser_set_preview_widget_active(chooser, have_preview);
+    LEAVE("preview visible is %d", have_preview);
+}
+
+static void
+change_image_cb (GtkFileChooser *chooser, void* data)
+{
+    auto filename{gtk_file_chooser_get_preview_filename(chooser)};
+    if (!filename)
+        return;
+    g_object_set_data_full(G_OBJECT(chooser), LAST_SELECTION, filename, g_free);
+}
+
+class GncGtkPixmapUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkPixmapUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::PIXMAP} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_widget()));
+        DEBUG("filename %s", string ? string : "(null)");
+        option.set_value(string);
+        g_free(string);
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto string{option.get_value<std::string>()};
+        if (!string.empty())
+        {
+            DEBUG("string = %s", string.c_str());
+            auto chooser{GTK_FILE_CHOOSER(get_widget())};
+            gtk_file_chooser_select_filename(chooser, string.c_str());
+            auto filename{gtk_file_chooser_get_filename(chooser)};
+            g_object_set_data_full(G_OBJECT(chooser), LAST_SELECTION,
+                                   g_strdup(string.c_str()), g_free);
+            DEBUG("Set %s, retrieved %s", string.c_str(),
+                  filename ? filename : "(null)");
+            update_preview_cb(chooser, &option);
+        }
+    }
+};
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::PIXMAP> (GncOption& option,
+                                               GtkGrid *page_box,
+                                               GtkLabel *name_label,
+                                               char *documentation,
+                                               /* Return values */
+                                               GtkWidget **enclosing,
+                                               bool *packed)
+{
+
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+
+    auto button = gtk_button_new_with_label(_("Clear"));
+    gtk_widget_set_tooltip_text(button, _("Clear any selected image file."));
+
+    auto widget = gtk_file_chooser_button_new(_("Select image"),
+                                        GTK_FILE_CHOOSER_ACTION_OPEN);
+    gtk_widget_set_tooltip_text(widget, _("Select an image file."));
+    g_object_set(G_OBJECT(widget),
+                 "width-chars", 30,
+                 "preview-widget", gtk_image_new(),
+                 (char *)NULL);
+    g_signal_connect(G_OBJECT (widget), "selection-changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+    g_signal_connect(G_OBJECT (widget), "selection-changed",
+                     G_CALLBACK(change_image_cb), &option);
+    g_signal_connect(G_OBJECT (widget), "update-preview",
+                     G_CALLBACK(update_preview_cb), &option);
+    g_signal_connect_swapped(G_OBJECT (button), "clicked",
+                             G_CALLBACK(gtk_file_chooser_unselect_all), widget);
+
+    option.set_ui_item(std::make_unique<GncGtkPixmapUIItem>(widget));
+    option.set_ui_item_from_option();
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(*enclosing), button, FALSE, FALSE, 0);
+
+    gtk_widget_show(widget);
+    gtk_widget_show(*enclosing);
+
+    return widget;
+}
+
+static void
+radiobutton_set_cb(GtkWidget *w, gpointer data)
+{
+    GncOption* option = static_cast<decltype(option)>(data);
+    GtkWidget *widget;
+    gpointer _current, _new_value;
+    gint current, new_value;
+
+    widget = gnc_option_get_gtk_widget (option);
+
+    _current = g_object_get_data(G_OBJECT(widget), "gnc_radiobutton_index");
+    current = GPOINTER_TO_INT (_current);
+
+    _new_value = g_object_get_data (G_OBJECT(w), "gnc_radiobutton_index");
+    new_value = GPOINTER_TO_INT (_new_value);
+
+    if (current == new_value)
+        return;
+
+    g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index",
+                       GINT_TO_POINTER(new_value));
+    widget_changed_cb(widget, option);
+}
+
+class GncGtkRadioButtonUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkRadioButtonUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::RADIOBUTTON} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto index{option.get_value<size_t>()};
+        auto list{gtk_container_get_children(GTK_CONTAINER(get_widget()))};
+        auto box{GTK_WIDGET(list->data)};
+        g_list_free(list);
+
+        list = gtk_container_get_children(GTK_CONTAINER(box));
+        auto node{g_list_nth(list, index)};
+        GtkButton* button{};
+        if (node)
+        {
+            button = GTK_BUTTON(node->data);
+        }
+        else
+        {
+            PERR("Invalid Radio Button Selection %lu", index);
+            g_list_free(list);
+            return;
+        }
+        g_list_free(list);
+        auto val{g_object_get_data (G_OBJECT (button),
+                                    "gnc_radiobutton_index")};
+        g_return_if_fail (GPOINTER_TO_INT (val) == index);
+
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto index{g_object_get_data(G_OBJECT(get_widget()),
+                                     "gnc_radiobutton_index")};
+        option.set_value<size_t>(GPOINTER_TO_INT(index));
+    }
+};
+
+static GtkWidget *
+create_radiobutton_widget(char *name, GncOption& option)
+{
+    GtkWidget *frame, *box;
+    GtkWidget *widget = NULL;
+
+    auto num_values{option.num_permissible_values()};
+
+    g_return_val_if_fail(num_values >= 0, NULL);
+
+    /* Create our button frame */
+    frame = gtk_frame_new (name);
+
+    /* Create the button box */
+    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (box), FALSE);
+    gtk_container_add (GTK_CONTAINER (frame), box);
+
+    /* Iterate over the options and create a radio button for each one */
+    for (auto i = 0; i < num_values; i++)
+    {
+        auto label = option.permissible_value_name(i);
+        auto tip = option.permissible_value_description(i);
+
+        widget =
+            gtk_radio_button_new_with_label_from_widget (widget ?
+                    GTK_RADIO_BUTTON (widget) :
+                    NULL,
+                    label && *label ? _(label) : "");
+        g_object_set_data (G_OBJECT (widget), "gnc_radiobutton_index",
+                           GINT_TO_POINTER (i));
+        gtk_widget_set_tooltip_text(widget, tip && *tip ? _(tip) : "");
+        g_signal_connect(G_OBJECT(widget), "toggled",
+                         G_CALLBACK(radiobutton_set_cb), &option);
+        gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0);
+    }
+
+    return frame;
+}
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::RADIOBUTTON> (GncOption& option, GtkGrid *page_box,
+                                      GtkLabel *name_label, char *documentation,
+                                      /* Return values */
+                                      GtkWidget **enclosing, bool *packed)
+{
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+
+    align_label (name_label);
+
+    auto widget = create_radiobutton_widget(NULL, option);
+
+    option.set_ui_item(std::make_unique<GncGtkPixmapUIItem>(widget));
+    option.set_ui_item_from_option();
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+class GncGtkDateFormatUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkDateFormatUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::STRING} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widget{GNC_DATE_FORMAT(get_widget())};
+        gnc_date_format_set_custom(widget,
+                                   option.get_value<std::string>().c_str());
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widget{GNC_DATE_FORMAT(get_widget())};
+        option.set_value(std::string{gnc_date_format_get_custom(widget)});
+    }
+};
+
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::DATE_FORMAT> (GncOption& option,
+                                                    GtkGrid *page_box,
+                                                    GtkLabel *name_label,
+                                                    char *documentation,
+                                                    /* Return values */
+                                                    GtkWidget **enclosing,
+                                                    bool *packed)
+{
+    *enclosing = gnc_date_format_new_without_label ();
+    align_label (name_label);
+
+    option.set_ui_item(std::make_unique<GncGtkDateFormatUIItem>(*enclosing));
+    option.set_ui_item_from_option();
+
+    g_signal_connect(G_OBJECT(*enclosing), "format_changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+    gtk_widget_show_all(*enclosing);
+    return *enclosing;
+}
+
+static void
+gnc_plot_size_option_set_select_method(GncOption& option, bool set_buttons)
+{
+    GList* widget_list;
+    GtkWidget *px_widget, *p_widget;
+    GtkWidget *widget;
+
+    widget = gnc_option_get_gtk_widget (option);
+
+    widget_list = gtk_container_get_children(GTK_CONTAINER(widget));
+    // px_button item 0
+    px_widget = static_cast<decltype(px_widget)>(g_list_nth_data(widget_list, 1));
+    // p_button item 2
+    p_widget = static_cast<decltype(p_widget)>(g_list_nth_data(widget_list, 3));
+    g_list_free(widget_list);
+
+    if (set_buttons)
+    {
+        gtk_widget_set_sensitive(px_widget, TRUE);
+        gtk_widget_set_sensitive(p_widget, FALSE);
+    }
+    else
+    {
+        gtk_widget_set_sensitive(p_widget, TRUE);
+        gtk_widget_set_sensitive(px_widget, FALSE);
+    }
+}
+
+static void
+gnc_rd_option_px_set_cb(GtkWidget *widget, GncOption* option)
+{
+    option->set_alternate(true);
+    option_changed_cb(widget, option);
+}
+
+static void
+gnc_rd_option_p_set_cb(GtkWidget *widget, GncOption* option)
+{
+    option->set_alternate(false);
+    option_changed_cb(widget, option);
+}
+
+class GncGtkPlotSizeUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkPlotSizeUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::PLOT_SIZE} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        auto widgets{gtk_container_get_children(GTK_CONTAINER(get_widget()))};
+        GtkWidget *button{}, *spin{};
+        if (option.is_alternate())
+        {
+            button = GTK_WIDGET(g_list_nth_data(widgets, 2));
+            spin = GTK_WIDGET(g_list_nth_data(widgets, 3));
+        }
+        else
+        {
+            button = GTK_WIDGET(g_list_nth_data(widgets, 2));
+            spin = GTK_WIDGET(g_list_nth_data(widgets, 3));
+        }
+        gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin),
+                                  option.get_value<double>());
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        auto widgets{gtk_container_get_children(GTK_CONTAINER(get_widget()))};
+        auto px_button{GTK_BUTTON(g_list_nth_data(widgets, 0))};
+        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(px_button)))
+        {
+            option.set_alternate(false);
+            auto spin{g_list_nth_data(widgets, 1)};
+            option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget())));
+        }
+        else
+        {
+            option.set_alternate(true);
+            auto spin{g_list_nth_data(widgets, 3)};
+            option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget())));
+        }
+    }
+};
+
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::PLOT_SIZE> (GncOption& option,
+                                                  GtkGrid *page_box,
+                                                  GtkLabel *name_label,
+                                                  char *documentation,
+                                                  /* Return values */
+                                                  GtkWidget **enclosing,
+                                                  bool *packed)
+{
+    GtkWidget *value_percent;
+    GtkWidget *px_butt, *p_butt;
+    GtkWidget *hbox;
+    GtkAdjustment *adj_percent;
+
+    *enclosing = gtk_frame_new(NULL);
+    gtk_widget_set_halign (GTK_WIDGET(*enclosing), GTK_ALIGN_START);
+
+    hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
+    g_object_set (G_OBJECT(hbox), "margin", 3, NULL);
+
+    gtk_container_add(GTK_CONTAINER(*enclosing), hbox);
+    auto value_px = create_range_spinner(option);
+
+    g_signal_connect(G_OBJECT(value_px), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    adj_percent = GTK_ADJUSTMENT(gtk_adjustment_new(1, 10, 100, 1, 5.0, 0));
+    value_percent = gtk_spin_button_new(adj_percent, 1, 0);
+    gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(value_percent), TRUE);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(value_percent), 100); //default
+    gtk_entry_set_width_chars(GTK_ENTRY(value_percent), 3);
+    gtk_widget_set_sensitive(value_percent, FALSE);
+
+    g_signal_connect(G_OBJECT(value_percent), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    px_butt = gtk_radio_button_new_with_label(NULL, _("Pixels"));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(px_butt), TRUE);
+
+    g_signal_connect(G_OBJECT(px_butt), "toggled",
+                         G_CALLBACK(gnc_rd_option_px_set_cb), &option);
+
+    p_butt = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(px_butt), _("Percent"));
+    g_signal_connect(G_OBJECT(p_butt), "toggled",
+                         G_CALLBACK(gnc_rd_option_p_set_cb), &option);
+
+    gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(px_butt), FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(value_px), FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(p_butt), FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(value_percent),
+                       FALSE, FALSE, 0);
+
+    option.set_ui_item(std::make_unique<GncGtkPlotSizeUIItem>(static_cast<GtkWidget*>(hbox)));
+    option.set_ui_item_from_option();
+
+    gtk_widget_show_all(*enclosing);
+    return hbox;
+}
+
+static GtkWidget *
+create_budget_widget(GncOption& option)
+{
+    GtkTreeModel *tm;
+    GtkComboBox *cb;
+    GtkCellRenderer *cr;
+
+    tm = gnc_tree_model_budget_new(gnc_get_current_book());
+    cb = GTK_COMBO_BOX(gtk_combo_box_new_with_model(tm));
+    g_object_unref(tm);
+    cr = gtk_cell_renderer_text_new();
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cb), cr, TRUE);
+
+    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(cb), cr, "text",
+                                   BUDGET_NAME_COLUMN, NULL);
+    return GTK_WIDGET(cb);
+}
+
+class GncGtkBudgetUIItem : public GncOptionGtkUIItem
+{
+public:
+    GncGtkBudgetUIItem(GtkWidget* widget) :
+        GncOptionGtkUIItem{widget, GncOptionUIType::BUDGET} {}
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        GtkTreeIter iter;
+        auto widget{GTK_COMBO_BOX(get_widget())};
+        auto budget{GNC_BUDGET(option.get_value<const QofInstance*>())};
+        auto tree_model{gtk_combo_box_get_model(widget)};
+        if (gnc_tree_model_budget_get_iter_for_budget(tree_model, &iter,
+                                                      budget))
+            gtk_combo_box_set_active_iter(widget, &iter);
+
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        GtkTreeIter iter;
+        auto widget{GTK_COMBO_BOX(get_widget())};
+        gtk_combo_box_get_active_iter(widget, &iter);
+        auto tree_model{gtk_combo_box_get_model(widget)};
+        auto budget{gnc_tree_model_budget_get_budget(tree_model, &iter)};
+        option.set_value(qof_instance_cast(budget));
+    }
+};
+
+template<> GtkWidget *
+create_option_widget<GncOptionUIType::BUDGET> (GncOption& option, GtkGrid *page_box,
+                                 GtkLabel *name_label, char *documentation,
+                                 /* Return values */
+                                 GtkWidget **enclosing, bool *packed)
+{
+    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+    gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE);
+
+    auto widget{create_budget_widget(option)};
+
+    option.set_ui_item(std::make_unique<GncGtkBudgetUIItem>(widget));
+    option.set_ui_item_from_option();
+
+    /* Maybe connect destroy handler for tree model here? */
+    g_signal_connect(G_OBJECT(widget), "changed",
+                     G_CALLBACK(widget_changed_cb), &option);
+
+    gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0);
+    gtk_widget_show_all(*enclosing);
+    return widget;
+}
+
+static GtkWidget*
+option_widget_factory(GncOption& option, GtkGrid* page, GtkLabel* name,
+                      char* description, GtkWidget** enclosing, bool* packed)
+{
+    switch(option.get_ui_type())
+    {
+        case INTERNAL:
+            return nullptr;
+        case BOOLEAN:
+            return create_option_widget<BOOLEAN>(option, page, name,
+                                                 description, enclosing,
+                                                 packed);
+        case STRING:
+            return create_option_widget<STRING>(option, page, name, description,
+                                                enclosing, packed);
+        case TEXT:
+            return create_option_widget<TEXT>(option, page, name, description,
+                                              enclosing, packed);
+        case CURRENCY:
+            return create_option_widget<CURRENCY>(option, page, name,
+                                                  description, enclosing,
+                                                  packed);
+        case COMMODITY:
+            return create_option_widget<COMMODITY>(option, page, name,
+                                                   description, enclosing,
+                                                   packed);
+        case MULTICHOICE:
+            return create_option_widget<MULTICHOICE>(option, page, name,
+                                                     description, enclosing,
+                                                     packed);
+        case DATE_ABSOLUTE:
+            return create_option_widget<DATE_ABSOLUTE>(option, page, name,
+                                                       description, enclosing,
+                                                       packed);
+        case DATE_RELATIVE:
+            return create_option_widget<DATE_RELATIVE>(option, page, name,
+                                                       description,
+                                                       enclosing, packed);
+        case DATE_BOTH:
+            return create_option_widget<DATE_BOTH>(option, page, name,
+                                                   description, enclosing,
+                                                   packed);
+        case ACCOUNT_LIST:
+            return create_option_widget<ACCOUNT_LIST>(option, page, name,
+                                                      description, enclosing,
+                                                      packed);
+        case ACCOUNT_SEL:
+            return create_option_widget<ACCOUNT_SEL>(option, page, name,
+                                                     description, enclosing,
+                                                     packed);
+        case LIST:
+            return create_option_widget<LIST>(option, page, name, description,
+                                              enclosing, packed);
+        case NUMBER_RANGE:
+            return create_option_widget<NUMBER_RANGE>(option, page, name,
+                                                      description, enclosing,
+                                                      packed);
+        case COLOR:
+            return create_option_widget<COLOR>(option, page, name, description,
+                                               enclosing, packed);
+        case FONT:
+            return create_option_widget<FONT>(option, page, name, description,
+                                              enclosing, packed);
+        case PLOT_SIZE:
+            return create_option_widget<PLOT_SIZE>(option, page, name,
+                                                   description, enclosing,
+                                                   packed);
+        case BUDGET:
+            return create_option_widget<BUDGET>(option, page, name, description,
+                                                enclosing, packed);
+        case PIXMAP:
+            return create_option_widget<PIXMAP>(option, page, name, description,
+                                                enclosing, packed);
+        case RADIOBUTTON:
+            return create_option_widget<RADIOBUTTON>(option, page, name,
+                                                     description, enclosing,
+                                                     packed);
+        case DATE_FORMAT:
+            return create_option_widget<DATE_FORMAT>(option, page, name,
+                                                     description, enclosing,
+                                                     packed);
+        case OWNER:
+            return create_option_widget<OWNER>(option, page, name, description,
+                                               enclosing, packed);
+        case CUSTOMER:
+            return create_option_widget<CUSTOMER>(option, page, name,
+                                                  description, enclosing,
+                                                  packed);
+        case VENDOR:
+            return create_option_widget<VENDOR>(option, page, name, description,
+                                                enclosing, packed);
+        case EMPLOYEE:
+            return create_option_widget<EMPLOYEE>(option, page, name,
+                                                  description, enclosing,
+                                                  packed);
+        case INVOICE:
+            return create_option_widget<INVOICE>(option, page, name,
+                                                 description, enclosing,
+                                                 packed);
+        case TAX_TABLE:
+            return create_option_widget<TAX_TABLE>(option, page, name,
+                                                   description, enclosing,
+                                                   packed);
+        case QUERY:
+            return create_option_widget<QUERY>(option, page, name, description,
+                                               enclosing, packed);
+    }
+}
+
+void
+gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb)
+{
+    if (!odb) return;
+    auto num_split_action = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL,
+                                               GNC_PREF_NUM_SOURCE);
+    if (num_split_action)
+    {
+        auto option{odb->find_option(OPTION_SECTION_ACCOUNTS,
+                                    OPTION_NAME_NUM_FIELD_SOURCE)};
+        auto num_source_button{gnc_option_get_gtk_widget(option)};
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (num_source_button),
+                                      num_split_action);
+    }
+}
+
+
+static void
+gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat)
+{
+    gnc_gnome_help (GTK_WINDOW (win), HF_HELP, HL_BOOK_OPTIONS);
+}
+
+void
+gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win)
+{
+    gnc_options_dialog_set_help_cb(win,
+                                (GNCOptionWinCallback)gnc_book_options_help_cb,
+                                NULL);
+}
+
+/* Dummy function impls. The following functions are declared in
+ * dialog_options.h and used by clients but they're made obsolete by the new
+ * options system. They're here to satisfy the linker.
+ */
+GtkWidget* const
+gnc_option_get_gtk_widget (GncOption *option)
+{
+    return nullptr;
+}
+
+void
+gnc_options_ui_initialize (void)
+{
+    return;
+}
diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h
index 300071734..b3613de21 100644
--- a/gnucash/gnome-utils/dialog-options.h
+++ b/gnucash/gnome-utils/dialog-options.h
@@ -24,12 +24,23 @@
 #define OPTIONS_DIALOG_H
 
 #include <libguile.h>
-#include "option-util.h"
 #include <gtk/gtk.h>
+#ifdef __cplusplus
+class GncOption;
+class GncOptionDB;
+using GNCOption = GncOption;
+using GNCOptionDB = GncOptionDB;
+extern "C"
+{
+#else
+#include "option-util.h"
+typedef GNCOption GncOption;
+typedef GNCOptionDB GncOptionDB;
+#endif
 
 /** A simple wrapper that casts the gpointer result of
  * gnc_option_get_widget() already into a GtkWidget*. */
-GtkWidget *gnc_option_get_gtk_widget (GNCOption *option);
+GtkWidget* const gnc_option_get_gtk_widget (GncOption *option);
 
 typedef struct gnc_option_win GNCOptionWin;
 
@@ -39,7 +50,6 @@ GNCOptionWin * gnc_options_dialog_new_modal (gboolean modal, gchar *title,
                                              const char *component_class,
                                              GtkWindow *parent);
 GNCOptionWin * gnc_options_dialog_new (gchar *title, GtkWindow *parent);
-GNCOptionWin * gnc_options_dialog_new_w_dialog (gchar *title, GtkWidget *dialog);
 void gnc_options_dialog_destroy (GNCOptionWin * win);
 void gnc_options_register_stocks (void);
 
@@ -49,8 +59,8 @@ GtkWidget * gnc_options_dialog_notebook (GNCOptionWin * win);
 
 void gnc_options_dialog_changed (GNCOptionWin *win);
 
-void gnc_option_changed_widget_cb (GtkWidget *widget, GNCOption *option);
-void gnc_option_changed_option_cb (GtkWidget *dummy, GNCOption *option);
+void gnc_option_changed_widget_cb (GtkWidget *widget, GncOption *option);
+void gnc_option_changed_option_cb (GtkWidget *dummy, GncOption *option);
 
 void gnc_options_dialog_set_apply_cb (GNCOptionWin * win,
                                       GNCOptionWinCallback thunk,
@@ -67,50 +77,21 @@ void gnc_options_dialog_set_global_help_cb (GNCOptionWinCallback thunk,
 
 void gnc_options_dialog_build_contents (GNCOptionWin *win,
                                         GNCOptionDB  *odb);
-
 void gnc_options_dialog_build_contents_full (GNCOptionWin *win,
                                              GNCOptionDB  *odb,
                                              gboolean show_dialog);
+void gnc_options_ui_initialize (void);
 
-/* Both apply_cb and close_cb should be scheme functions with 0 arguments.
- * References to these functions will be held until the close_cb is called
+/** Set the help callback to 'gnc_book_options_help_cb' to open a help browser
+ *  and point it to the Book Options link in the Help file.
  */
-void gnc_options_dialog_set_scm_callbacks (GNCOptionWin *win,
-                                           SCM apply_cb,
-                                           SCM close_cb);
-
-/*****************************************************************/
-/* Option Registration                                           */
-
-/* Function to set the UI widget based upon the option */
-typedef GtkWidget *
-(*GNCOptionUISetWidget) (GNCOption *option, GtkGrid *page_box,
-                         GtkLabel *name_label, char *documentation,
-                         /* Return values */
-                         GtkWidget **enclosing, gboolean *packed);
-
-/* Function to set the UI Value for a particular option */
-typedef gboolean
-(*GNCOptionUISetValue)  (GNCOption *option, gboolean use_default,
-                         GtkWidget *widget, SCM value);
-
-/* Function to get the UI Value for a particular option */
-typedef SCM
-(*GNCOptionUIGetValue)  (GNCOption *option, GtkWidget *widget);
-
-
-typedef struct gnc_option_def
-{
-    const char *         option_name;
-    GNCOptionUISetWidget set_widget;
-    GNCOptionUISetValue  set_value;
-    GNCOptionUIGetValue  get_value;
-} GNCOptionDef_t;
-
-
-/* Register a new option type in the UI */
-void gnc_options_ui_initialize (void);
-void gnc_options_ui_register_option (GNCOptionDef_t *option);
-GNCOptionDef_t * gnc_options_ui_get_option (const char *option_name);
+void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win);
 
+/** Set the initial values of new book options to values specified in user
+ *  preferences.
+ */
+void gnc_options_dialog_set_new_book_option_values (GNCOptionDB *odb);
+#ifdef __cplusplus
+}
+#endif
 #endif /* OPTIONS_DIALOG_H */
diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c
index a771f3525..58e4272a9 100644
--- a/gnucash/gnome-utils/gnc-gnome-utils.c
+++ b/gnucash/gnome-utils/gnc-gnome-utils.c
@@ -99,6 +99,7 @@ gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat)
     gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_BOOK_OPTIONS);
 }
 
+#if 0 // Reimplemented in dialog-options.cpp
 void
 gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win)
 {
@@ -129,6 +130,7 @@ gnc_options_dialog_set_new_book_option_values (GNCOptionDB *odb)
                         num_source_is_split_action);
     }
 }
+#endif
 
 static void
 gnc_style_sheet_options_help_cb (GNCOptionWin *win, gpointer dat)
diff --git a/gnucash/gnome/business-options-gnome.c b/gnucash/gnome/business-options-gnome.c
index afee518ca..a9bc680dd 100644
--- a/gnucash/gnome/business-options-gnome.c
+++ b/gnucash/gnome/business-options-gnome.c
@@ -42,9 +42,10 @@
 #include "dialog-invoice.h"
 
 #define FUNC_NAME G_STRFUNC
-
+/* To be consolidated into dialog-options.cpp later. */
+#if 0
 static GtkWidget *
-create_owner_widget (GNCOption *option, GncOwnerType type, GtkWidget *hbox)
+create_owner_widget (GncOption *option, GncOwnerType type, GtkWidget *hbox)
 {
     GtkWidget *widget;
     GncOwner owner;
@@ -91,7 +92,7 @@ make_name_label (char *name)
 
 
 static GncOwnerType
-get_owner_type_from_option (GNCOption *option)
+get_owner_type_from_option (GncOption *option)
 {
     SCM odata = gnc_option_get_option_data (option);
 
@@ -102,7 +103,7 @@ get_owner_type_from_option (GNCOption *option)
 
 /* Function to set the UI widget based upon the option */
 static GtkWidget *
-owner_set_widget (GNCOption *option, GtkGrid *page_box,
+owner_set_widget (GncOption *option, GtkGrid *page_box,
                   GtkLabel *name_label, char *documentation,
                   /* Return values */
                   GtkWidget **enclosing, gboolean *packed)
@@ -123,7 +124,7 @@ owner_set_widget (GNCOption *option, GtkGrid *page_box,
 
 /* Function to set the UI Value for a particular option */
 static gboolean
-owner_set_value (GNCOption *option, gboolean use_default,
+owner_set_value (GncOption *option, gboolean use_default,
                  GtkWidget *widget, SCM value)
 {
     GncOwner owner_def;
@@ -150,7 +151,7 @@ owner_set_value (GNCOption *option, gboolean use_default,
 
 /* Function to get the UI Value for a particular option */
 static SCM
-owner_get_value (GNCOption *option, GtkWidget *widget)
+owner_get_value (GncOption *option, GtkWidget *widget)
 {
     static GncOwner owner;	/* XXX: might cause trouble? */
     GncOwnerType type;
@@ -169,7 +170,7 @@ owner_get_value (GNCOption *option, GtkWidget *widget)
 
 /* Function to set the UI widget based upon the option */
 static GtkWidget *
-customer_set_widget (GNCOption *option, GtkGrid *page_box,
+customer_set_widget (GncOption *option, GtkGrid *page_box,
                      GtkLabel *name_label, char *documentation,
                      /* Return values */
                      GtkWidget **enclosing, gboolean *packed)
@@ -189,7 +190,7 @@ customer_set_widget (GNCOption *option, GtkGrid *page_box,
 
 /* Function to set the UI Value for a particular option */
 static gboolean
-customer_set_value (GNCOption *option, gboolean use_default,
+customer_set_value (GncOption *option, gboolean use_default,
                     GtkWidget *widget, SCM value)
 {
     GncOwner owner;
@@ -209,7 +210,7 @@ customer_set_value (GNCOption *option, gboolean use_default,
 
 /* Function to get the UI Value for a particular option */
 static SCM
-customer_get_value (GNCOption *option, GtkWidget *widget)
+customer_get_value (GncOption *option, GtkWidget *widget)
 {
     GncOwner owner;
 
@@ -225,7 +226,7 @@ customer_get_value (GNCOption *option, GtkWidget *widget)
 
 /* Function to set the UI widget based upon the option */
 static GtkWidget *
-vendor_set_widget (GNCOption *option, GtkGrid *page_box,
+vendor_set_widget (GncOption *option, GtkGrid *page_box,
                    GtkLabel *name_label, char *documentation,
                    /* Return values */
                    GtkWidget **enclosing, gboolean *packed)
@@ -245,7 +246,7 @@ vendor_set_widget (GNCOption *option, GtkGrid *page_box,
 
 /* Function to set the UI Value for a particular option */
 static gboolean
-vendor_set_value (GNCOption *option, gboolean use_default,
+vendor_set_value (GncOption *option, gboolean use_default,
                   GtkWidget *widget, SCM value)
 {
     GncOwner owner;
@@ -265,7 +266,7 @@ vendor_set_value (GNCOption *option, gboolean use_default,
 
 /* Function to get the UI Value for a particular option */
 static SCM
-vendor_get_value (GNCOption *option, GtkWidget *widget)
+vendor_get_value (GncOption *option, GtkWidget *widget)
 {
     GncOwner owner;
 
@@ -280,7 +281,7 @@ vendor_get_value (GNCOption *option, GtkWidget *widget)
 
 /* Function to set the UI widget based upon the option */
 static GtkWidget *
-employee_set_widget (GNCOption *option, GtkGrid *page_box,
+employee_set_widget (GncOption *option, GtkGrid *page_box,
                      GtkLabel *name_label, char *documentation,
                      /* Return values */
                      GtkWidget **enclosing, gboolean *packed)
@@ -300,7 +301,7 @@ employee_set_widget (GNCOption *option, GtkGrid *page_box,
 
 /* Function to set the UI Value for a particular option */
 static gboolean
-employee_set_value (GNCOption *option, gboolean use_default,
+employee_set_value (GncOption *option, gboolean use_default,
                     GtkWidget *widget, SCM value)
 {
     GncOwner owner;
@@ -320,7 +321,7 @@ employee_set_value (GNCOption *option, gboolean use_default,
 
 /* Function to get the UI Value for a particular option */
 static SCM
-employee_get_value (GNCOption *option, GtkWidget *widget)
+employee_get_value (GncOption *option, GtkWidget *widget)
 {
     GncOwner owner;
 
@@ -335,7 +336,7 @@ employee_get_value (GNCOption *option, GtkWidget *widget)
 
 
 static GtkWidget *
-create_invoice_widget (GNCOption *option, GtkWidget *hbox)
+create_invoice_widget (GncOption *option, GtkWidget *hbox)
 {
     GtkWidget *widget;
 
@@ -352,7 +353,7 @@ create_invoice_widget (GNCOption *option, GtkWidget *hbox)
 
 /* Function to set the UI widget based upon the option */
 static GtkWidget *
-invoice_set_widget (GNCOption *option, GtkGrid *page_box,
+invoice_set_widget (GncOption *option, GtkGrid *page_box,
                     GtkLabel *name_label, char *documentation,
                     /* Return values */
                     GtkWidget **enclosing, gboolean *packed)
@@ -372,7 +373,7 @@ invoice_set_widget (GNCOption *option, GtkGrid *page_box,
 
 /* Function to set the UI Value for a particular option */
 static gboolean
-invoice_set_value (GNCOption *option, gboolean use_default,
+invoice_set_value (GncOption *option, gboolean use_default,
                    GtkWidget *widget, SCM value)
 {
     GncInvoice *invoice;
@@ -390,7 +391,7 @@ invoice_set_value (GNCOption *option, gboolean use_default,
 
 /* Function to get the UI Value for a particular option */
 static SCM
-invoice_get_value (GNCOption *option, GtkWidget *widget)
+invoice_get_value (GncOption *option, GtkWidget *widget)
 {
     GncInvoice *invoice;
 
@@ -404,7 +405,7 @@ invoice_get_value (GNCOption *option, GtkWidget *widget)
 
 
 static GtkWidget *
-create_taxtable_widget (GNCOption *option, GtkWidget *hbox)
+create_taxtable_widget (GncOption *option, GtkWidget *hbox)
 {
     GtkWidget *widget;
     GtkBuilder *builder;
@@ -428,7 +429,7 @@ create_taxtable_widget (GNCOption *option, GtkWidget *hbox)
 
 /* Function to set the UI widget based upon the option */
 static GtkWidget *
-taxtable_set_widget (GNCOption *option, GtkGrid *page_box,
+taxtable_set_widget (GncOption *option, GtkGrid *page_box,
                      GtkLabel *name_label, char *documentation,
                      /* Return values */
                      GtkWidget **enclosing, gboolean *packed)
@@ -448,7 +449,7 @@ taxtable_set_widget (GNCOption *option, GtkGrid *page_box,
 
 /* Function to set the UI Value for a particular option */
 static gboolean
-taxtable_set_value (GNCOption *option, gboolean use_default,
+taxtable_set_value (GncOption *option, gboolean use_default,
                     GtkWidget *widget, SCM value)
 {
     GncTaxTable *taxtable;
@@ -466,7 +467,7 @@ taxtable_set_value (GNCOption *option, gboolean use_default,
 
 /* Function to get the UI Value for a particular option */
 static SCM
-taxtable_get_value (GNCOption *option, GtkWidget *widget)
+taxtable_get_value (GncOption *option, GtkWidget *widget)
 {
     GncTaxTable *taxtable;
 
@@ -474,28 +475,11 @@ taxtable_get_value (GNCOption *option, GtkWidget *widget)
     return SWIG_NewPointerObj(taxtable, SWIG_TypeQuery("_p__gncTaxTable"), 0);
 }
 
-
+#endif
 
 
 void
 gnc_business_options_gnome_initialize (void)
 {
-    int i;
-    static GNCOptionDef_t options[] =
-    {
-        { "owner", owner_set_widget, owner_set_value, owner_get_value },
-        {
-            "customer", customer_set_widget, customer_set_value,
-            customer_get_value
-        },
-        { "vendor", vendor_set_widget, vendor_set_value, vendor_get_value },
-        { "employee", employee_set_widget, employee_set_value, employee_get_value },
-        { "invoice", invoice_set_widget, invoice_set_value, invoice_get_value },
-        { "taxtable", taxtable_set_widget, taxtable_set_value, taxtable_get_value },
-        { NULL }
-    };
-
-    SWIG_GetModule(NULL); /* Work-around for SWIG bug. */
-    for (i = 0; options[i].option_name; i++)
-        gnc_options_ui_register_option (&(options[i]));
+/* Create the above option types. */
 }
diff --git a/gnucash/gnucash.cpp b/gnucash/gnucash.cpp
index d7a76466a..0592960ee 100644
--- a/gnucash/gnucash.cpp
+++ b/gnucash/gnucash.cpp
@@ -33,12 +33,15 @@
 #include "gnucash-core-app.hpp"
 
 extern "C" {
+#include <glib/gi18n.h>
 #include <dialog-new-user.h>
 #include <gfec.h>
+#include <gnc-engine.h> // For define GNC_MOD_GUI
 #include <gnc-file.h>
 #include <gnc-filepath-utils.h>
 #include <gnc-gnome-utils.h>
 #include <gnc-gsettings.h>
+#include <gnc-hooks.h>
 #include <gnc-module.h>
 #include <gnc-path.h>
 #include <gnc-plugin-bi-import.h>
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 42c069742..50b087a4a 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -505,7 +505,6 @@ GncOption::from_scheme(std::istream& iss)
  * the template implementation in the public header.
  */
 
-using GncOptionAccountList = std::vector<const Account*>;
 
 template class GncOptionValidatedValue<const QofInstance*>;
 
@@ -544,17 +543,20 @@ template const char* GncOption::get_default_value<const char*>() const;
 template std::string GncOption::get_default_value<std::string>() const;
 template const QofInstance* GncOption::get_default_value<const QofInstance*>() const;
 template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() const;
+template GncOptionAccountList GncOption::get_default_value<GncOptionAccountList>() const;
 template GncMultichoiceOptionIndexVec GncOption::get_default_value<GncMultichoiceOptionIndexVec>() const;
 
 template void GncOption::set_value(bool);
 template void GncOption::set_value(int);
 template void GncOption::set_value(int64_t);
 template void GncOption::set_value(double);
+template void GncOption::set_value(char*);
 template void GncOption::set_value(const char*);
 template void GncOption::set_value(std::string);
 template void GncOption::set_value(const QofInstance*);
 template void GncOption::set_value(RelativeDatePeriod);
 template void GncOption::set_value(size_t);
+template void GncOption::set_value(GncOptionAccountList);
 template void GncOption::set_value(GncMultichoiceOptionIndexVec);
 
 template void GncOption::get_limits(double&, double&, double&) const noexcept;
diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h
index b4401382b..7c0d33035 100644
--- a/libgnucash/engine/qofbook.h
+++ b/libgnucash/engine/qofbook.h
@@ -74,7 +74,12 @@ typedef struct KvpValueImpl KvpValue;
 
 typedef void (*QofBookDirtyCB) (QofBook *, gboolean dirty, gpointer user_data);
 
+#ifdef __cplusplus
+class GncOptionDB;
+using GNCOptionDB = GncOptionDB;
+#else
 typedef struct gnc_option_db GNCOptionDB;
+#endif
 
 typedef void (*GNCOptionSave) (GNCOptionDB*, QofBook*, gboolean);
 typedef void (*GNCOptionLoad) (GNCOptionDB*, QofBook*);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 39773851c..84ae44d04 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -136,6 +136,7 @@ gnucash/gnome-utils/dialog-dup-trans.c
 gnucash/gnome-utils/dialog-file-access.c
 gnucash/gnome-utils/dialog-object-references.c
 gnucash/gnome-utils/dialog-options.c
+gnucash/gnome-utils/dialog-options.cpp
 gnucash/gnome-utils/dialog-preferences.c
 gnucash/gnome-utils/dialog-query-view.c
 gnucash/gnome-utils/dialog-reset-warnings.c
@@ -528,6 +529,7 @@ libgnucash/app-utils/gnc-gsettings.c
 libgnucash/app-utils/gnc-helpers.c
 libgnucash/app-utils/gnc-help-utils.c
 libgnucash/app-utils/gnc-option.cpp
+libgnucash/app-utils/gnc-option-date.cpp
 libgnucash/app-utils/gnc-optiondb.cpp
 libgnucash/app-utils/gnc-option-impl.cpp
 libgnucash/app-utils/gnc-prefs-utils.c

commit 3b4785e744a88ab062c6b13b3accd0cc766d9488
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 17 12:24:20 2020 -0700

    Convert GncOptionSection from a std::pair to a class.
    
    Provides find_option(const char*) and foreach_option(func) for easy
    iteration. find_option and find_section now return plain const ptrs
    instead of std::optionals. Much less cumbersome though the compiler
    won't nag if you forget the nullptr check.

diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 074b36247..372f558d9 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -41,7 +41,25 @@ extern "C"
 }
 
 using GncOptionVec = std::vector<GncOption>;
-using GncOptionSection = std::pair<std::string, GncOptionVec>;
+
+class GncOptionSection
+{
+    std::string m_name;
+    GncOptionVec m_options;
+public:
+    GncOptionSection(const char* name) : m_name{name}, m_options{} {}
+    ~GncOptionSection() = default;
+
+    void foreach_option(std::function<void(GncOption&)> func);
+    const std::string& get_name() const noexcept { return m_name; }
+    size_t get_num_options() const noexcept { return m_options.size(); }
+    void add_option(GncOption&& option);
+    void remove_option(const char* name);
+    const GncOption* find_option(const char* name) const;
+};
+
+using GncOptionSectionPtr = std::shared_ptr<GncOptionSection>;
+
 class GncOptionDB
 {
 public:
@@ -49,15 +67,27 @@ public:
     GncOptionDB(QofBook* book);
     ~GncOptionDB() = default;
 
+/* The non-const version can't be redirected to the const one because the
+ * function parameters are incompatible.
+ */
+    void foreach_section(std::function<void(GncOptionSectionPtr&)> func)
+    {
+        for (auto& section : m_sections)
+            func(section);
+    }
+    void foreach_section(std::function<void(const GncOptionSectionPtr&)> func) const
+    {
+        for (auto& section : m_sections)
+            func(section);
+    }
+    size_t num_sections() const noexcept { return m_sections.size(); }
     void save_to_book(QofBook* book, bool do_clear) const;
-    int num_sections() const noexcept { return m_sections.size(); }
     bool get_changed() const noexcept { return m_dirty; }
     void register_option(const char* section, GncOption&& option);
     void unregister_option(const char* section, const char* name);
     void set_default_section(const char* section);
     const GncOptionSection* const get_default_section() const noexcept;
-    std::string lookup_string_option(const char* section,
-                                            const char* name);
+    std::string lookup_string_option(const char* section, const char* name);
     template <typename ValueType>
     bool set_option(const char* section, const char* name, ValueType value)
     {
@@ -66,7 +96,7 @@ public:
             auto option{find_option(section, name)};
             if (!option)
                 return false;
-            option->get().set_value(value);
+            option->set_value(value);
             return true;
         }
         catch(const std::invalid_argument& err)
@@ -78,11 +108,16 @@ public:
 //    void set_selectable(const char* section, const char* name);
     void make_internal(const char* section, const char* name);
     void commit() {};
-    std::optional<std::reference_wrapper<GncOptionSection>> find_section(const std::string& section);
-    std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
-        return static_cast<const GncOptionDB&>(*this).find_option(section, name);
+    GncOptionSection* find_section(const std::string& sectname)
+    {
+        return const_cast<GncOptionSection*>(static_cast<const GncOptionDB&>(*this).find_section(sectname));
+    }
+    const GncOptionSection* find_section(const std::string& sectname) const;
+    GncOption* find_option(const std::string& section, const char* name)
+    {
+        return const_cast<GncOption*>(static_cast<const GncOptionDB&>(*this).find_option(section, name));
     }
-    std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) const;
+    const GncOption* find_option(const std::string& section, const char* name) const;
     std::ostream& save_to_scheme(std::ostream& oss,
                                  const char* options_prolog) const noexcept;
     std::istream& load_from_scheme(std::istream& iss) noexcept;
@@ -100,8 +135,8 @@ public:
                                         const std::string& name) const noexcept;
     std::istream& load_option_key_value(std::istream& iss);
 private:
-    std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
-    std::vector<GncOptionSection> m_sections;
+    GncOptionSection* m_default_section;
+    std::vector<GncOptionSectionPtr> m_sections;
     bool m_dirty = false;
 
     std::function<GncOptionUIItem*()> m_get_ui_value;
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 1ab1509f1..36367aace 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -30,100 +30,107 @@
 #include "gnc-option-ui.hpp"
 
 auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
-GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}
 
-GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
 static bool
 operator==(const std::string& str, const char* cstr)
 {
     return strcmp(str.c_str(), cstr) == 0;
 }
 
+void
+GncOptionSection::foreach_option(std::function<void(GncOption&)> func)
+{
+    std::for_each(m_options.begin(), m_options.end(), func);
+}
+
+void
+GncOptionSection::add_option(GncOption&& option)
+{
+    m_options.emplace_back(std::move(option));
+}
+
+void
+GncOptionSection::remove_option(const char* name)
+{
+    m_options.erase(std::remove_if(m_options.begin(), m_options.end(),
+                                   [name](const auto& option) -> bool
+                                   {
+                                       return option.get_name() == name;
+                                   }));
+}
+
+const GncOption*
+GncOptionSection::find_option(const char* name) const
+{
+    auto option = std::find_if(m_options.begin(), m_options.end(),
+                               [name](auto& option) -> bool {
+                                   return option.get_name() == name;
+                               });
+    return (option == m_options.end() ? nullptr : &*option);
+}
+
+GncOptionDB::GncOptionDB() : m_default_section{} {}
+
+GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
+
 void
 GncOptionDB::save_to_book(QofBook* book, bool do_clear) const
 {
 }
 
 void
-GncOptionDB::register_option(const char* section, GncOption&& option)
+GncOptionDB::register_option(const char* sectname, GncOption&& option)
 {
-    auto db_section = find_section(section);
+    auto section = find_section(sectname);
 
-    if (db_section)
+    if (section)
     {
-        auto& sec_vec = db_section->get().second;
-        sec_vec.emplace_back(std::move(option));
+        section->add_option(std::move(option));
         return;
     }
 
-    m_sections.emplace_back(std::make_pair(std::string{section},
-                                               GncOptionVec{}));
-    auto new_section = std::prev(m_sections.end());
-    new_section->second.emplace_back(std::move(option));
+    m_sections.emplace_back(std::make_shared<GncOptionSection>(sectname));
+    m_sections.back()->add_option(std::move(option));
 }
 
 void
-GncOptionDB::unregister_option(const char* section, const char* name)
+GncOptionDB::unregister_option(const char* sectname, const char* name)
 {
-    auto db_section = find_section(section);
-    if (db_section)
-    {
-        auto& sec_vec = db_section->get().second;
-        sec_vec.erase(
-            std::remove_if(
-                sec_vec.begin(), sec_vec.end(),
-                [name](const auto& option) -> bool
-                {
-                    return option.get_name() == std::string{name};
-                }));
-    }
+    auto section = find_section(sectname);
+    if (section)
+        section->remove_option(name);
 }
 
 void
-GncOptionDB::set_default_section(const char* section)
+GncOptionDB::set_default_section(const char* sectname)
 {
-    m_default_section = find_section(section);
+    m_default_section = find_section(sectname);
 }
 
 const GncOptionSection* const
 GncOptionDB::get_default_section() const noexcept
 {
-    if (m_default_section)
-        return &(m_default_section.value().get());
-    return nullptr;
+        return m_default_section;
 }
 
-std::optional<std::reference_wrapper<GncOptionSection>>
-GncOptionDB::find_section(const std::string& section)
+const GncOptionSection*
+GncOptionDB::find_section(const std::string& section) const
 {
-    auto db_section = std::find_if(
-        m_sections.begin(), m_sections.end(),
-        [&section](auto& sect) -> bool
-        {
-            return section.compare(0, classifier_size_max, sect.first) == 0;
-        });
-    if (db_section == m_sections.end())
-        return std::nullopt;
-    return *db_section;
+    auto db_section = std::find_if(m_sections.begin(), m_sections.end(),
+                                   [&section](auto& sect) -> bool
+                                   {
+                                       return section == sect->get_name();
+                                   });
+    return db_section == m_sections.end() ? nullptr : db_section->get();
 }
 
-std::optional<std::reference_wrapper<GncOption>>
-GncOptionDB::find_option(const std::string& section, const std::string& name) const
+const GncOption*
+GncOptionDB::find_option(const std::string& section, const char* name) const
 {
     auto db_section = const_cast<GncOptionDB*>(this)->find_section(section);
     if (!db_section)
-        return std::nullopt;
-    auto& sec_vec =  db_section->get().second;
-    auto db_opt = std::find_if(
-       sec_vec.begin(), sec_vec.end(),
-        [&name](GncOption& option) -> bool
-        {
-            return name.compare(0, classifier_size_max - 1,
-                                  option.get_name()) == 0;
-        });
-    if (db_opt == sec_vec.end())
-        return std::nullopt;
-    return *db_opt;
+        return nullptr;
+    return db_section->find_option(name);
 }
 
 std::string
@@ -134,7 +141,7 @@ GncOptionDB::lookup_string_option(const char* section, const char* name)
     auto db_opt = find_option(section, name);
     if (!db_opt)
         return empty_string;
-    return db_opt->get().get_value<std::string>();
+    return db_opt->get_value<std::string>();
 }
 
 void
@@ -143,7 +150,7 @@ GncOptionDB::make_internal(const char* section, const char* name)
 
     auto db_opt = find_option(section, name);
     if (db_opt)
-        db_opt->get().make_internal();
+        db_opt->make_internal();
 }
 
 std::ostream&
@@ -152,15 +159,15 @@ GncOptionDB::save_option_scheme(std::ostream& oss,
                                 const std::string& section,
                                 const std::string& name) const noexcept
 {
-    auto db_opt = find_option(section, name);
+    auto db_opt = find_option(section, name.c_str());
 
-    if (!db_opt || !db_opt->get().is_changed())
+    if (!db_opt || !db_opt->is_changed())
         return oss;
     oss << scheme_tags[0] << option_prolog << "\n";
     oss << scheme_tags[1] << '"' << section.substr(0, classifier_size_max) << "\"\n";
     oss << scheme_tags[1] << '"' << name.substr(0, classifier_size_max) << '"';
     oss  <<  scheme_tags[2] << "\n" << scheme_tags[3];
-    db_opt->get().to_scheme(oss);
+    db_opt->to_scheme(oss);
     oss << scheme_tags[4] << "\n\n";
 
     return oss;
@@ -421,7 +428,7 @@ GncOptionDB::load_option_scheme(std::istream& iss)
         throw std::runtime_error("Malformed option classifier.");
     const auto& section = unquote_scheme_string(classifier[1].m_name);
     const auto& name = unquote_scheme_string(classifier[2].m_name);
-    auto option = find_option(section.c_str(), name.c_str());
+    auto option = find_option(section, name.c_str());
     std::string option_str{section};
     option_str += ':';
     option_str += name;
@@ -439,29 +446,30 @@ GncOptionDB::load_option_scheme(std::istream& iss)
         throw std::runtime_error(err + " malformed value lambda form.");
     }
     std::istringstream value_iss{value_id->get().m_ids[1].m_name};
-    option->get().from_scheme(value_iss);
+    option->from_scheme(value_iss);
     return iss;
 }
 
 std::ostream&
 GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept
 {
-    for (auto& section : m_sections)
-    {
-        const auto& [s_name, s_vec] = section;
-        oss << "\n; Section: " << s_name << "\n\n";
-        for (auto& option : s_vec)
+    foreach_section(
+        [&oss, options_prolog](const GncOptionSectionPtr& section)
         {
-            if (!option.is_changed())
-                continue;
-            oss << scheme_tags[0] << options_prolog << "\n";
-            oss << scheme_tags[1] << '"' << section.first.substr(0, classifier_size_max) << "\"\n";
-            oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"';
-            oss  <<  scheme_tags[2] << "\n" << scheme_tags[3];
-            option.to_scheme(oss);
-            oss << scheme_tags[4] << "\n\n";
-        }
-    }
+            oss << "\n; Section: " << section->get_name() << "\n\n";
+            section->foreach_option(
+                [&oss, options_prolog, &section](auto& option)
+                {
+                    if (!option.is_changed())
+                        return;
+                    oss << scheme_tags[0] << options_prolog << "\n";
+                    oss << scheme_tags[1] << '"' << section->get_name().substr(0, classifier_size_max) << "\"\n";
+                    oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"';
+                    oss  <<  scheme_tags[2] << "\n" << scheme_tags[3];
+                    option.to_scheme(oss);
+                    oss << scheme_tags[4] << "\n\n";
+                });
+        });
     return oss;
 }
 
@@ -487,11 +495,11 @@ GncOptionDB::save_option_key_value(std::ostream& oss,
                                    const std::string& name) const noexcept
 {
 
-    auto db_opt = find_option(section, name);
-    if (!db_opt || !db_opt->get().is_changed())
+    auto db_opt = find_option(section, name.c_str());
+    if (!db_opt || !db_opt->is_changed())
         return oss;
     oss << section.substr(0, classifier_size_max) << ":" <<
-        name.substr(0, classifier_size_max) << "=" << db_opt->get() << ";";
+        name.substr(0, classifier_size_max) << "=" << *db_opt << ";";
     return oss;
 }
 
@@ -512,7 +520,7 @@ GncOptionDB::load_option_key_value(std::istream& iss)
         std::string value;
         std::getline(iss, value, ';');
         std::istringstream item_iss{value};
-        item_iss >> option->get();
+        item_iss >> *option;
     }
     return iss;
 }
@@ -521,18 +529,19 @@ std::ostream&
 GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept
 {
 
-    for (auto& section : m_sections)
-    {
-        const auto& [s_name, s_vec] = section;
-        oss << "[Options]\n";
-        for (auto& option : s_vec)
+    foreach_section(
+        [&oss](const GncOptionSectionPtr& section)
         {
-            if (option.is_changed())
-                oss << section.first.substr(0, classifier_size_max) <<
-                    ':' << option.get_name().substr(0, classifier_size_max) <<
-                    '=' << option << '\n';
-       }
-    }
+            oss << "[Options]\n";
+            section->foreach_option(
+                [&oss, &section](auto& option)
+                {
+                    if (option.is_changed())
+                        oss << section->get_name().substr(0, classifier_size_max) <<
+                            ':' << option.get_name().substr(0, classifier_size_max) <<
+                            '=' << option << '\n';
+                });
+        });
     return oss;
 }
 
@@ -559,85 +568,89 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
 {
     if (clear_options)
         qof_book_options_delete(book, nullptr);
-    for (auto& section : m_sections)
-    {
-        const auto& [s_name, s_vec] = section;
-        for (auto& option : s_vec)
-            if (option.is_changed())
-            {
-                // qof_book_set_option wants a GSList path. Let's avoid allocating and make one here.
-                GSList list_tail{(void*)option.get_name().c_str(), nullptr};
-                GSList list_head{(void*)s_name.c_str(), &list_tail};
-                auto type{option.get_ui_type()};
-                if (type == GncOptionUIType::BOOLEAN)
-                {
-                    auto val{option.get_value<bool>()};
-                    auto kvp{new KvpValue(val ? "t" : "f")};
-                    qof_book_set_option(book, kvp, &list_head);
-                }
-                else if (type > GncOptionUIType::DATE_FORMAT)
-                {
-                    const QofInstance* inst{QOF_INSTANCE(option.get_value<const QofInstance*>())};
-                    auto guid = guid_copy(qof_instance_get_guid(inst));
-                    auto kvp{new KvpValue(guid)};
-                    qof_book_set_option(book, kvp, &list_head);
-                }
-                else if (type == GncOptionUIType::NUMBER_RANGE)
-                {
-                    auto kvp{new KvpValue(option.get_value<int64_t>())};
-                    qof_book_set_option(book, kvp, &list_head);
-                }
-                else
-                {
-                    auto kvp{new KvpValue{g_strdup(option.get_value<std::string>().c_str())}};
-                    qof_book_set_option(book, kvp, &list_head);
-                }
-            }
-    }
+    const_cast<GncOptionDB*>(this)->foreach_section(
+        [book](GncOptionSectionPtr& section)
+        {
+            section->foreach_option(
+                [book, &section](auto& option) {
+                    if (option.is_changed())
+                    {
+                        // qof_book_set_option wants a GSList path. Let's avoid
+                        // allocating and make one here.
+                        GSList list_tail{(void*)option.get_name().c_str(), nullptr};
+                        GSList list_head{(void*)section->get_name().c_str(), &list_tail};
+                        auto type{option.get_ui_type()};
+                        if (type == GncOptionUIType::BOOLEAN)
+                        {
+                            auto val{option.template get_value<bool>()};
+                            auto kvp{new KvpValue(val ? "t" : "f")};
+                            qof_book_set_option(book, kvp, &list_head);
+                        }
+                        else if (type > GncOptionUIType::DATE_FORMAT)
+                        {
+                            const QofInstance* inst{QOF_INSTANCE(option.template get_value<const QofInstance*>())};
+                            auto guid = guid_copy(qof_instance_get_guid(inst));
+                            auto kvp{new KvpValue(guid)};
+                            qof_book_set_option(book, kvp, &list_head);
+                        }
+                        else if (type == GncOptionUIType::NUMBER_RANGE)
+                        {
+                            auto kvp{new KvpValue(option.template get_value<int64_t>())};
+                            qof_book_set_option(book, kvp, &list_head);
+                        }
+                        else
+                        {
+                            auto kvp{new KvpValue{g_strdup(option.template get_value<std::string>().c_str())}};
+                            qof_book_set_option(book, kvp, &list_head);
+                        }
+                    }
+                });
+        });
 }
 
 void
 GncOptionDB::load_from_kvp(QofBook* book) noexcept
 {
-    for (auto& section : m_sections)
-    {
-        auto& [s_name, s_vec] = section;
-        for (auto& option : s_vec)
+    foreach_section(
+        [book](GncOptionSectionPtr& section)
         {
-            /* qof_book_set_option wants a GSList path. Let's avoid allocating
-             * and make one here.
-             */
-            GSList list_tail{(void*)option.get_name().c_str(), nullptr};
-            GSList list_head{(void*)s_name.c_str(), &list_tail};
-            auto kvp = qof_book_get_option(book, &list_head);
-            if (!kvp)
-                continue;
-            switch (kvp->get_type())
-            {
-                case KvpValue::Type::INT64:
-                    option.set_value(kvp->get<int64_t>());
-                    break;
-                case KvpValue::Type::STRING:
-                {
-                    auto str{kvp->get<const char*>()};
-                    if (option.get_ui_type() == GncOptionUIType::BOOLEAN)
-                        option.set_value(*str == 't' ? true : false);
-                    else
-                        option.set_value(str);
-                    break;
-                }
-                case KvpValue::Type::GUID:
+            section->foreach_option(
+                [book, &section](auto& option)
                 {
-                    auto guid{kvp->get<GncGUID*>()};
-                    option.set_value((const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type()));
-                    break;
-                }
-                default:
-                    continue;
-                    break;
-            }
-        }
-    }
+                    /* qof_book_set_option wants a GSList path. Let's avoid allocating
+                     * and make one here.
+                     */
+                    GSList list_tail{(void*)option.get_name().c_str(), nullptr};
+                    GSList list_head{(void*)section->get_name().c_str(), &list_tail};
+                    auto kvp = qof_book_get_option(book, &list_head);
+                    if (!kvp)
+                        return;
+                    switch (kvp->get_type())
+                    {
+                        case KvpValue::Type::INT64:
+                            option.set_value(kvp->get<int64_t>());
+                            break;
+                        case KvpValue::Type::STRING:
+                        {
+                            auto str{kvp->get<const char*>()};
+                            if (option.get_ui_type() == GncOptionUIType::BOOLEAN)
+                                option.set_value(*str == 't' ? true : false);
+                            else
+                                option.set_value(str);
+                            break;
+                        }
+                        case KvpValue::Type::GUID:
+                        {
+                            auto guid{kvp->get<GncGUID*>()};
+                            option.set_value((const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type()));
+                            break;
+                        }
+                        default:
+                            return;
+                            break;
+                    }
+                });
+        });
 }
 
 GncOptionDBPtr
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 823f51c31..c850f5241 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -342,6 +342,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     }
 %}
 
+%ignore gnc_option_to_scheme;
+%ignore gnc_option_from_scheme;
+
 %include "gnc-option-date.hpp"
 %include "gnc-option.hpp"
 %include "gnc-option-impl.hpp"
@@ -387,7 +390,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         auto db_opt = optiondb->find_option(section, name);
         if (!db_opt)
             return SCM_BOOL_F;
-        return GncOption_get_scm_value(&(db_opt->get()));
+        return GncOption_get_scm_value(db_opt);
     }
 
     static SCM
@@ -397,7 +400,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         auto db_opt = optiondb->find_option(section, name);
         if (!db_opt)
             return SCM_BOOL_F;
-        return GncOption_get_scm_default_value(&(db_opt->get()));
+        return GncOption_get_scm_default_value(db_opt);
     }
 
     static void
@@ -410,7 +413,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 //          PWARN("Attempt to write non-existent option %s/%s", section, name);
             return;
         }
-        GncOption_set_value_from_scm(&(db_opt->get()), new_value);
+        GncOption_set_value_from_scm(db_opt, new_value);
     }
 %}
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 993577342..70754bea7 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -131,8 +131,8 @@ TEST_F(GncOptionDBTest, test_register_account_list_option)
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
     gnc_register_account_list_option(m_db, "foo", "bar", "baz", "Phony Option",
                                     acclist);
-    EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
-    EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
+    EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(3));
 }
 
 TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
@@ -142,8 +142,8 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_STOCK});
-    EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
-    EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
+    EXPECT_EQ(4, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(3));
 }
 
 TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
@@ -154,8 +154,8 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", accsel,
                                              {ACCT_TYPE_STOCK});
-    EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
-    EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(0));
+    EXPECT_EQ(1, m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get_value<GncOptionAccountList>().at(0));
 }
 
 TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct)
@@ -204,7 +204,7 @@ TEST_F(GncOptionDBTest, test_register_relative_date_option)
     gnc_gdate_set_prev_year_start(&prev_year_start);
     time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
     ASSERT_TRUE(m_db->set_option("foo", "bar", time1));
-    EXPECT_EQ(time1, m_db->find_option("foo", "bar")->get().get_value<time64>());
+    EXPECT_EQ(time1, m_db->find_option("foo", "bar")->get_value<time64>());
 }
 
 TEST_F(GncOptionDBTest, test_register_absolute_date_option)
@@ -217,7 +217,7 @@ TEST_F(GncOptionDBTest, test_register_absolute_date_option)
     gnc_gdate_set_prev_year_start(&prev_year_start);
     ASSERT_TRUE(m_db->set_option("foo", "bar", time1));
     EXPECT_EQ(time1,
-              m_db->find_option("foo", "bar")->get().get_value<time64>());
+              m_db->find_option("foo", "bar")->get_value<time64>());
 }
 
 /* Copied from gnc-optiondb.cpp for the purpose of finding the index of the
@@ -243,15 +243,15 @@ TEST_F(GncOptionDBTest, test_register_start_date_option)
     gnc_gdate_set_prev_year_start(&prev_year_start);
     time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
     EXPECT_EQ(RelativeDatePeriod::START_ACCOUNTING_PERIOD,
-              m_db->find_option("foo", "bar")->get().get_value<RelativeDatePeriod>());
+              m_db->find_option("foo", "bar")->get_value<RelativeDatePeriod>());
     ASSERT_TRUE(m_db->set_option("foo", "bar", time1));
     EXPECT_EQ(time1,
-              m_db->find_option("foo", "bar")->get().get_value<time64>());
+              m_db->find_option("foo", "bar")->get_value<time64>());
     EXPECT_EQ(RelativeDatePeriod::ABSOLUTE,
-              m_db->find_option("foo", "bar")->get().get_value<RelativeDatePeriod>());
+              m_db->find_option("foo", "bar")->get_value<RelativeDatePeriod>());
     m_db->set_option("foo", "bar", RelativeDatePeriod::START_THIS_MONTH);
     EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH,
-              m_db->find_option("foo", "bar")->get().get_value<RelativeDatePeriod>());
+              m_db->find_option("foo", "bar")->get_value<RelativeDatePeriod>());
 
     auto index(std::find(begin_dates.begin(), begin_dates.end(),
                          RelativeDatePeriod::START_THIS_MONTH) - begin_dates.begin());
@@ -259,12 +259,12 @@ TEST_F(GncOptionDBTest, test_register_start_date_option)
      * gnc-optiondb.cpp.
      */
     EXPECT_EQ(index,
-              m_db->find_option("foo", "bar")->get().get_value<size_t>());
+              m_db->find_option("foo", "bar")->get_value<size_t>());
     m_db->set_option("foo", "bar", RelativeDatePeriod::END_THIS_MONTH);
     EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH,
-              m_db->find_option("foo", "bar")->get().get_value<RelativeDatePeriod>());
+              m_db->find_option("foo", "bar")->get_value<RelativeDatePeriod>());
     m_db->set_option("foo", "bar", static_cast<size_t>(5));
-    EXPECT_EQ(5, m_db->find_option("foo", "bar")->get().get_value<size_t>());
+    EXPECT_EQ(5, m_db->find_option("foo", "bar")->get_value<size_t>());
 
 }
 
@@ -335,7 +335,7 @@ TEST_F(GncOptionDBIOTest, test_option_scheme_output)
     oss.clear();
     m_db->set_option("foo", "sausage", std::string{"pepper"});
     EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
-    EXPECT_TRUE(m_db->find_option("foo", "sausage")->get().is_changed());
+    EXPECT_TRUE(m_db->find_option("foo", "sausage")->is_changed());
     oss.flush();
     m_db->save_option_scheme(oss, "option", "foo", "sausage");
     EXPECT_STREQ("(let ((option (gnc:lookup-option option\n"
@@ -373,7 +373,7 @@ TEST_F(GncOptionDBIOTest, test_date_interval_option_scheme_input)
     gnc_gdate_set_month_end(&month_end);
     auto time1 = time64_from_gdate(&month_end, DayPart::end);
     m_db->load_option_scheme(iss);
-    EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value<time64>());
+    EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value<time64>());
 
 }
 
@@ -389,10 +389,10 @@ TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input)
     input += hpe_guid + "\" \"";
     input += msft_guid + "\")))) option))\n\n";
     std::istringstream iss{input};
-    EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[0]);
+    EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[0]);
     m_db->load_option_scheme(iss);
-    EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[1]);
+    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
 
 }
 
@@ -427,12 +427,12 @@ TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input)
     g_date_subtract_months(&month_end, 1);
     gnc_gdate_set_month_end(&month_end);
     auto time1 = time64_from_gdate(&month_end, DayPart::end);
-    EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[0]);
+    EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[0]);
     m_db->load_from_scheme(iss);
     EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
-    EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value<time64>());
-    EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value<time64>());
+    EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[1]);
+    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
 
 }
 

commit c63db36a60bb30db6636bc42b07f5c769690f6d6
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 17 12:19:26 2020 -0700

    Rename string_equal_charptr to operator==(const std::string&, const char*)
    
    Much prettier and more idiomatic as well as being a lot less typing.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index b7f7fcb5e..1ab1509f1 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -33,6 +33,11 @@ auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
 GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}
 
 GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
+static bool
+operator==(const std::string& str, const char* cstr)
+{
+    return strcmp(str.c_str(), cstr) == 0;
+}
 
 void
 GncOptionDB::save_to_book(QofBook* book, bool do_clear) const
@@ -377,23 +382,14 @@ unquote_scheme_string(const std::string& str)
     return str;
 }
 
-static inline bool
-string_equal_charptr(const std::string& str, const char* chars)
-{
-    if (!chars || str.empty() || strlen(chars) != str.size())
-        return false;
-    return strcmp(str.c_str(), chars) == 0;
-}
-
 static std::optional<std::reference_wrapper<const SchemeId>>
 find_form(const SchemeId& toplevel, IdentType type, const char* name)
 {
-    if (toplevel.m_type == type &&
-        string_equal_charptr(toplevel.m_name.c_str(), name))
+    if (toplevel.m_type == type && toplevel.m_name == name)
         return std::ref(toplevel);
     for (const auto& id : toplevel.m_ids)
     {
-        if (id.m_type == type && string_equal_charptr(id.m_name.c_str(), name))
+        if (id.m_type == type && id.m_name == name)
             return std::ref(id);
         auto child{find_form(id, type, name)};
         if (child)

commit 0106176436569bcf46d51b90cf4ad6b9fcd45096
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Mar 16 10:27:59 2020 -0700

    Provide GncOption::is_alternate() for GncOptionRangeValue<>.
    
    Needed for GncOptionUIType::PLOT_SIZE to indicate whether the option
    value represents pixels or percent.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index e26e29dcf..de985b437 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -107,6 +107,7 @@ struct OptionClassifier
     std::string m_doc_string;
 };
 
+
 #ifndef SWIG
 auto constexpr size_t_max = std::numeric_limits<std::size_t>::max();
 #endif
@@ -207,14 +208,18 @@ std::string qof_instance_to_string(const QofInstance* inst);
  * to parse the templates to figure out the symbols.
  */
 #ifndef SWIG
-template<class OptionValueClass,
+template<class OptType,
          typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
-                                                     std::decay_t<OptionValueClass>> &&
-                                   !(std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValue<const QofInstance*>> ||
-                                     std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValidatedValue<const QofInstance*>>), int> = 0>
-std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
+                                                     std::decay_t<OptType>> &&
+                                   ! (std::is_same_v<std::decay_t<OptType>,
+                                      GncOptionValue<const QofInstance*>> ||
+                                      std::is_same_v<std::decay_t<OptType>,
+                                      GncOptionValidatedValue<const QofInstance*>> ||
+                                      std::is_same_v<std::decay_t<OptType>,
+                                      GncOptionRangeValue<int>> ||
+                                      std::is_same_v<std::decay_t<OptType>,
+                                      GncOptionRangeValue<double>>), int> = 0>
+std::ostream& operator<<(std::ostream& oss, const OptType& opt)
 {
     oss << opt.get_value();
     return oss;
@@ -228,7 +233,11 @@ operator<< <GncOptionValue<bool>>(std::ostream& oss,
     return oss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*> >, int> = 0>
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionValidatedValue<const QofInstance*>> ||
+                                   std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionValue<const QofInstance*> >, int> = 0>
 inline std::ostream&
 operator<< (std::ostream& oss, const OptType& opt)
 {
@@ -249,13 +258,18 @@ operator<< (std::ostream& oss, const OptType& opt)
     return oss;
 }
 
-template<class OptionValueClass,
-         typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>> &&
-                                   !(std::is_same_v<std::decay_t<OptionValueClass>,
+template<class OptType,
+         typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
+                                                     std::decay_t<OptType>> &&
+                                   !(std::is_same_v<std::decay_t<OptType>,
                                      GncOptionValue<const QofInstance*>> ||
-                                     std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValidatedValue<const QofInstance*>>), int> = 0>
-std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
+                                     std::is_same_v<std::decay_t<OptType>,
+                                     GncOptionValidatedValue<const QofInstance*>> ||
+                                      std::is_same_v<std::decay_t<OptType>,
+                                      GncOptionRangeValue<int>> ||
+                                      std::is_same_v<std::decay_t<OptType>,
+                                      GncOptionRangeValue<double>>), int> = 0>
+std::istream& operator>>(std::istream& iss, OptType& opt)
 {
     std::decay_t<decltype(opt.get_value())> value;
     iss >> value;
@@ -263,7 +277,12 @@ std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
     return iss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*> >, int> = 0>
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionValidatedValue<const QofInstance*>> ||
+                                   std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionValue<const QofInstance*> >,
+                                   int> = 0>
 std::istream&
 operator>> (std::istream& iss, OptType& opt)
 {
@@ -409,6 +428,11 @@ public:
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+    bool is_alternate() const noexcept { return m_alternate; }
+    void set_alternate(bool value) noexcept {
+        if (m_ui_type == GncOptionUIType::PLOT_SIZE)
+            m_alternate = value;
+    }
 private:
     GncOptionUIType m_ui_type = GncOptionUIType::NUMBER_RANGE;
     ValueType m_value;
@@ -416,8 +440,56 @@ private:
     ValueType m_min;
     ValueType m_max;
     ValueType m_step;
+    bool m_alternate = false;
 };
 
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionRangeValue<int>> ||
+                                   std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionRangeValue<double>>,
+                                   int> = 0>
+inline std::ostream&
+operator<< (std::ostream& oss, const OptType& opt)
+{
+    if (opt.get_ui_type() == GncOptionUIType::PLOT_SIZE)
+        oss << (opt.is_alternate() ? "pixels" : "percent") << " ";
+    oss << opt.get_value();
+    return oss;
+}
+
+template<class OptType,
+         typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionRangeValue<int>> ||
+                                   std::is_same_v<std::decay_t<OptType>,
+                                                  GncOptionRangeValue<double>>,
+                                   int> = 0>
+inline std::istream&
+operator>> (std::istream& iss, OptType& opt)
+{
+    if (opt.get_ui_type() == GncOptionUIType::PLOT_SIZE)
+    {
+        std::string alt;
+        iss >> alt;
+        opt.set_alternate(strncmp(alt.c_str(), "percent",
+                                  strlen("percent")) == 0);
+    }
+    if constexpr (std::is_same_v<std::decay_t<OptType>,
+                  GncOptionRangeValue<double>>)
+    {
+        double d;
+        iss >> d;
+        opt.set_value(d);
+    }
+    else
+    {
+        int i;
+        iss >> i;
+        opt.set_value(i);
+    }
+    return iss;
+}
+
 using GncMultichoiceOptionEntry = std::tuple<const std::string,
                                              const std::string,
                                              const std::string>;
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 9f9d9c28c..42c069742 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -350,6 +350,31 @@ GncOption::account_type_list() const noexcept
                       }, *m_option);
 }
 
+bool
+GncOption::is_alternate() const noexcept
+{
+    return std::visit([](auto& option) -> bool {
+                          if constexpr(std::is_same_v<std::decay_t<decltype(option)>,
+                                       GncOptionRangeValue<int>> ||
+                       std::is_same_v<std::decay_t<decltype(option)>,
+                                       GncOptionRangeValue<double>>)
+                              return option.is_alternate();
+                          return false;
+                      }, *m_option);
+}
+
+void
+GncOption::set_alternate(bool alt) noexcept
+{
+    std::visit([alt](auto& option) {
+                   if constexpr(std::is_same_v<std::decay_t<decltype(option)>,
+                                GncOptionRangeValue<int>> ||
+                       std::is_same_v<std::decay_t<decltype(option)>,
+                                GncOptionRangeValue<double>>)
+                       option.set_alternate(alt);
+               }, *m_option);
+}
+
 std::ostream&
 GncOption::out_stream(std::ostream& oss) const
 {
@@ -390,6 +415,19 @@ GncOption::to_scheme(std::ostream& oss) const
                               (std::is_same_v<std::decay_t<decltype(option.get_value())>,
                                std::string>)
                                   oss << '"' << option << '"';
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionRangeValue<int>> ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionRangeValue<double>>)
+                          {
+                              if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
+                                  oss << "'(" << (option.is_alternate() ?
+                                          "\"percent\" . " : "\"pixels\" . ");
+                              oss << option.get_value();
+                              if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
+                                  oss << ")";
+                          }
                           else
                               oss << option;
                           return oss;
@@ -427,6 +465,36 @@ GncOption::from_scheme(std::istream& iss)
                               std::getline(iss, input, '"');
                               option.set_value(input);
                           }
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionRangeValue<int>> ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionRangeValue<double>>)
+                          {
+                              if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
+                              {
+                                  iss.ignore(3, '"');
+                                  std::string alt;
+                                  iss >> alt;
+                                  option.set_alternate(
+                                      strncmp(alt.c_str(), "pixels",
+                                              strlen("pixels")) == 0);
+                                  iss.ignore(4, ' ');
+                              }
+                              if constexpr(std::is_same_v<std::decay_t<decltype(option)>,
+                                           GncOptionRangeValue<int>>)
+                              {
+                                  int val;
+                                  iss >> val;
+                                  option.set_value(val);
+                              }
+                              else
+                              {
+                                  double val;
+                                  iss >> val;
+                                  option.set_value(val);
+                              }
+                          }
                           else
                               iss >> option;
                           return iss;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index befa221ea..2d901d303 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -96,6 +96,8 @@ public:
     const char* permissible_value_name(std::size_t index) const;
     const char* permissible_value_description(std::size_t index) const;
     GList* account_type_list() const noexcept;
+    bool is_alternate() const noexcept;
+    void set_alternate(bool) noexcept;
     std::ostream& out_stream(std::ostream& oss) const;
     std::istream& in_stream(std::istream& iss);
     std::ostream& to_scheme(std::ostream& oss) const;

commit 5fd53c94defb6d08bdf5069a2e561006d7e313a5
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Mar 15 13:39:34 2020 -0700

    Provide GncOption::GetLimits for setting a NUMBER_RANGE spin button.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 69951dbb1..e26e29dcf 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -400,6 +400,12 @@ public:
         else
             throw std::invalid_argument("Validation failed, value not set.");
     }
+    void get_limits(ValueType& upper, ValueType& lower, ValueType& step) const noexcept
+    {
+        upper = m_max;
+        lower = m_min;
+        step = m_step;
+    }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index b4b8e33f7..9f9d9c28c 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -123,6 +123,17 @@ GncOption::set_value(ValueType value)
                }, *m_option);
 }
 
+template <typename ValueType> void
+GncOption::get_limits(ValueType& max, ValueType& min, ValueType& step) const noexcept
+{
+    std::visit([&max, &min, &step](const auto& option) {
+                   if constexpr
+                       (std::is_same_v<std::decay_t<decltype(option)>,
+                        GncOptionRangeValue<ValueType>>)
+                       option.get_limits(max, min, step);
+               }, *m_option);
+}
+
 const std::string&
 GncOption::get_section() const
 {
@@ -478,6 +489,8 @@ template void GncOption::set_value(RelativeDatePeriod);
 template void GncOption::set_value(size_t);
 template void GncOption::set_value(GncMultichoiceOptionIndexVec);
 
+template void GncOption::get_limits(double&, double&, double&) const noexcept;
+template void GncOption::get_limits(int&, int&, int&) const noexcept;
 template bool GncOption::validate(bool) const;
 template bool GncOption::validate(int) const;
 template bool GncOption::validate(int64_t) const;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 8a1b126ba..befa221ea 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -87,6 +87,8 @@ public:
     void make_internal();
     bool is_changed() const noexcept;
     bool is_multiselect() const noexcept;
+    template <typename ValueType> void get_limits(ValueType&, ValueType&,
+                                                  ValueType&) const noexcept;
     template <typename ValueType> bool validate(ValueType value) const;
     std::size_t num_permissible_values() const;
     std::size_t permissible_value_index(const char* value) const;
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index d36b19b75..790303b7f 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -622,6 +622,20 @@ TEST_F(GncRangeOption, test_setter)
     EXPECT_EQ(1.5, m_doubleoption.get_default_value<double>());
 }
 
+TEST_F(GncRangeOption, test_get_info)
+{
+    int imax{}, imin{}, istep{};
+    double dmax{}, dmin{}, dstep{};
+    m_intoption.get_limits(imax, imin, istep);
+    m_doubleoption.get_limits(dmax, dmin, dstep);
+    EXPECT_EQ(30, imax);
+    EXPECT_EQ(1, imin);
+    EXPECT_EQ(1, istep);
+    EXPECT_EQ(1.0, dmin);
+    EXPECT_EQ(3.0, dmax);
+    EXPECT_EQ(0.1, dstep);
+}
+
 TEST_F(GncRangeOption, test_range_out)
 {
     std::ostringstream oss;

commit a995343a8b9b65d1e517225572c2756018764ee3
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 12 17:30:52 2020 -0700

    Provide for multiple selections in GncOptionMultichoiceValue
    
    To support the GncOptionUIType::LIST. This UI type is unused in GnuCash
    code but might be used in user custom reports.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 9ac10c012..38b1f8e04 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -32,6 +32,9 @@ extern "C"
 #include "gnc-ui-util.h"
 }
 
+const std::string GncOptionMultichoiceValue::c_empty_string{""};
+const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
+
 bool
 GncOptionAccountValue::validate(const GncOptionAccountList& values) const
 {
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 9716fdb26..69951dbb1 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -412,13 +412,14 @@ private:
     ValueType m_step;
 };
 
-using GncMultiChoiceOptionEntry = std::tuple<const std::string,
+using GncMultichoiceOptionEntry = std::tuple<const std::string,
                                              const std::string,
                                              const std::string>;
-using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
+using GncMultichoiceOptionIndexVec = std::vector<std::size_t>;
+using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
 
-/** MultiChoice options have a vector of valid options
- * (GncMultiChoiceOptionChoices) and validate the selection as being one of
+/** Multichoice options have a vector of valid options
+ * (GncMultichoiceOptionChoices) and validate the selection as being one of
  * those values. The value is the index of the selected item in the vector. The
  * tuple contains three strings, a key, a display
  * name and a brief description for the tooltip. Both name and description
@@ -433,22 +434,48 @@ public:
     GncOptionMultichoiceValue(const char* section, const char* name,
                               const char* key, const char* doc_string,
                               const char* value,
-                              GncMultiChoiceOptionChoices&& choices,
+                              GncMultichoiceOptionChoices&& choices,
                               GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
         OptionClassifier{section, name, key, doc_string},
         m_ui_type{ui_type},
-        m_value{}, m_default_value{}, m_choices{std::move(choices)} {
-            if (value)
+        m_value{}, m_default_value{}, m_choices{std::move(choices)}
+    {
+        if (value)
+        {
+            if (auto index = find_key(value);
+                index != size_t_max)
             {
-                if (auto index = find_key(value);
-                    index != size_t_max)
-                {
-                    m_value = index;
-                    m_default_value = index;
-                }
+                m_value.push_back(index);
+                m_default_value.push_back(index);
             }
         }
+    }
+
+    GncOptionMultichoiceValue(const char* section, const char* name,
+                              const char* key, const char* doc_string,
+                              size_t index,
+                              GncMultichoiceOptionChoices&& choices,
+                              GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
+        OptionClassifier{section, name, key, doc_string},
+        m_ui_type{ui_type},
+        m_value{}, m_default_value{}, m_choices{std::move(choices)}
+    {
+        if (index < m_choices.size())
+        {
+            m_value.push_back(index);
+            m_default_value.push_back(index);
+        }
+    }
 
+    GncOptionMultichoiceValue(const char* section, const char* name,
+                              const char* key, const char* doc_string,
+                              GncMultichoiceOptionIndexVec&& indices,
+                              GncMultichoiceOptionChoices&& choices,
+                              GncOptionUIType ui_type = GncOptionUIType::LIST) :
+        OptionClassifier{section, name, key, doc_string},
+        m_ui_type{ui_type},
+        m_value{indices}, m_default_value{std::move(indices)},
+        m_choices{std::move(choices)} {}
     GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default;
     GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default;
     GncOptionMultichoiceValue& operator=(const GncOptionMultichoiceValue&) = default;
@@ -456,27 +483,84 @@ public:
 
     const std::string& get_value() const
     {
-        return std::get<0>(m_choices.at(m_value));
+        auto vec{m_value.size() > 0 ? m_value : m_default_value};
+        if (vec.size() == 0)
+            return c_empty_string;
+        if (vec.size() == 1)
+            return std::get<0>(m_choices.at(vec[0]));
+        else
+            return c_list_string;
     }
     const std::string& get_default_value() const
     {
-        return std::get<0>(m_choices.at(m_default_value));
+        if (m_default_value.size() == 1)
+            return std::get<0>(m_choices.at(m_default_value[0]));
+        else if (m_default_value.size() == 0)
+            return c_empty_string;
+        else
+            return c_list_string;
+    }
+
+    size_t get_index() const
+    {
+        if (m_value.size() > 0)
+            return m_value[0];
+        if (m_default_value.size() > 0)
+            return m_default_value[0];
+        return 0;
+    }
+    const GncMultichoiceOptionIndexVec& get_multiple() const noexcept
+    {
+        return m_value;
     }
-     bool validate(const std::string& value) const noexcept
+    const GncMultichoiceOptionIndexVec& get_default_multiple() const noexcept
+    {
+        return m_default_value;
+    }
+    bool validate(const std::string& value) const noexcept
     {
         auto index = find_key(value);
         return index != size_t_max;
 
+    }
+    bool validate(const GncMultichoiceOptionIndexVec& indexes) const noexcept
+    {
+        for (auto index : indexes)
+            if (index >= m_choices.size())
+                return false;
+        return true;
+
     }
     void set_value(const std::string& value)
     {
         auto index = find_key(value);
         if (index != size_t_max)
-            m_value = index;
+        {
+            m_value.clear();
+            m_value.push_back(index);
+        }
+        else
+            throw std::invalid_argument("Value not a valid choice.");
+
+    }
+    void set_value(size_t index)
+    {
+        if (index < m_choices.size())
+        {
+            m_value.clear();
+            m_value.push_back(index);
+        }
         else
             throw std::invalid_argument("Value not a valid choice.");
 
     }
+    void set_multiple(const GncMultichoiceOptionIndexVec& indexes)
+    {
+        if (validate(indexes))
+            m_value = indexes;
+        else
+            throw std::invalid_argument("One of the supplied indexes was out of range.");
+    }
     std::size_t num_permissible_values() const noexcept
     {
         return m_choices.size();
@@ -513,11 +597,119 @@ private:
 
     }
     GncOptionUIType m_ui_type;
-    std::size_t m_value;
-    std::size_t m_default_value;
-    GncMultiChoiceOptionChoices m_choices;
+    GncMultichoiceOptionIndexVec m_value;
+    GncMultichoiceOptionIndexVec m_default_value;
+    GncMultichoiceOptionChoices m_choices;
+    static const std::string c_empty_string;
+    static const std::string c_list_string;
 };
 
+template<> inline std::ostream&
+operator<< <GncOptionMultichoiceValue>(std::ostream& oss,
+                                       const GncOptionMultichoiceValue& opt)
+{
+    auto vec{opt.get_multiple()};
+    bool first{true};
+    for (auto index : vec)
+    {
+        if (first)
+            first = false;
+        else
+            oss << " ";
+        oss << opt.permissible_value(index);
+    }
+    return oss;
+}
+
+template<> inline std::istream&
+operator>> <GncOptionMultichoiceValue>(std::istream& iss,
+                                       GncOptionMultichoiceValue& opt)
+{
+    GncMultichoiceOptionIndexVec values;
+    while (true)
+    {
+        std::string str;
+        std::getline(iss, str, ' ');
+        if (!str.empty())
+        {
+            auto index = opt.permissible_value_index(str.c_str());
+            if (index != size_t_max)
+                values.push_back(index);
+            else
+            {
+                std::string err = str + " is not one of ";
+                err += opt.m_name;
+                err += "'s permissible values.";
+                throw std::invalid_argument(err);
+            }
+        }
+        else
+            break;
+    }
+    opt.set_multiple(values);
+    iss.clear();
+    return iss;
+}
+
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionMultichoiceValue>, int> = 0>
+inline std::ostream&
+gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
+{
+    auto indexes{opt.get_multiple()};
+    if (indexes.size() > 1)
+        oss << "'(";
+    bool first = true;
+    for (auto index : indexes)
+    {
+        if (first)
+            first = false;
+        else
+            oss << " ";
+        oss << "'" << opt.permissible_value(index);
+    }
+    if (indexes.size() > 1)
+        oss << ')';
+    return oss;
+}
+
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionMultichoiceValue>, int> = 0>
+inline std::istream&
+gnc_option_from_scheme(std::istream& iss, OptType& opt)
+{
+    iss.ignore(3, '\'');
+    auto c{iss.peek()};
+    if (static_cast<char>(c) == '(')
+    {
+        GncMultichoiceOptionIndexVec values;
+        iss.ignore(3, '\'');
+        while (true)
+        {
+            std::string str;
+            std::getline(iss, str, ' ');
+            if (!str.empty())
+            {
+                if (str.back() == ')')
+                {
+                    str.pop_back();
+                    break;
+                }
+                values.push_back(opt.permissible_value_index(str.c_str()));
+                iss.ignore(2, '\'');
+            }
+            else
+                break;
+        }
+        opt.set_multiple(values);
+    }
+    else
+    {
+        std::string str;
+        std::getline(iss, str, ' ');
+        opt.set_value(str);
+    }
+    return iss;
+}
+
 using GncOptionAccountList = std::vector<const Account*>;
 
 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 5abc02622..b4b8e33f7 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -58,6 +58,18 @@ GncOption::get_value() const
                                         std::is_same_v<std::decay_t<ValueType>,
                                         size_t>)
                               return option.get_period_index();
+                          if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionMultichoiceValue> &&
+                               std::is_same_v<std::decay_t<ValueType>,
+                               size_t>)
+                              return option.get_index();
+                          if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionMultichoiceValue> &&
+                               std::is_same_v<std::decay_t<ValueType>,
+                               GncMultichoiceOptionIndexVec>)
+                              return option.get_multiple();
                           return ValueType {};
                       }, *m_option);
 }
@@ -78,6 +90,12 @@ GncOption::get_default_value() const
                                         std::is_same_v<std::decay_t<ValueType>,
                                         size_t>)
                               return option.get_default_period_index();
+                          if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionMultichoiceValue> &&
+                               std::is_same_v<std::decay_t<ValueType>,
+                               GncMultichoiceOptionIndexVec>)
+                              return option.get_default_multiple();
                           return ValueType {};
                       }, *m_option);
 
@@ -96,6 +114,12 @@ GncOption::set_value(ValueType value)
                          RelativeDatePeriod> ||
                           std::is_same_v<std::decay_t<ValueType>, size_t>)))
                            option.set_value(value);
+                   if constexpr
+                       (std::is_same_v<std::decay_t<decltype(option)>,
+                        GncOptionMultichoiceValue> &&
+                        std::is_same_v<std::decay_t<ValueType>,
+                        GncMultichoiceOptionIndexVec>)
+                       option.set_multiple(value);
                }, *m_option);
 }
 
@@ -221,6 +245,10 @@ GncOption::validate(ValueType value) const
                                          GncOptionMultichoiceValue> &&
                                          std::is_same_v<std::decay_t<ValueType>,
                                          std::string>) ||
+                                        (std::is_same_v<std::decay_t<decltype(option)>,
+                                         GncOptionMultichoiceValue> &&
+                                         std::is_same_v<std::decay_t<ValueType>,
+                                         GncMultichoiceOptionIndexVec>) ||
                                         std::is_same_v<std::decay_t<decltype(option)>,
                                         GncOptionValidatedValue<ValueType>>)
                                            return option.validate(value);
@@ -334,19 +362,15 @@ GncOption::to_scheme(std::ostream& oss) const
 {
     return std::visit([&oss](auto& option) ->std::ostream& {
                           if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionAccountValue>)
-                                  gnc_option_to_scheme(oss, option);
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionMultichoiceValue>)
-                                  oss << "'" << option;
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
+                              ((std::is_same_v<std::decay_t<decltype(option)>,
+                                GncOptionAccountValue>) ||
+                               (std::is_same_v<std::decay_t<decltype(option)>,
+                                GncOptionMultichoiceValue>) ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionValue<const QofInstance*>> ||
                                std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionValidatedValue<const QofInstance*>>)
-                                  gnc_option_to_scheme(oss, option);
+                              gnc_option_to_scheme(oss, option);
                           else if constexpr
                               (std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionDateValue>)
@@ -366,18 +390,11 @@ GncOption::from_scheme(std::istream& iss)
 {
     return std::visit([&iss](auto& option) -> std::istream& {
                           if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
-                               GncOptionAccountValue>)
-                              gnc_option_from_scheme(iss, option);
-                          else if constexpr
                               ((std::is_same_v<std::decay_t<decltype(option)>,
-                                GncOptionMultichoiceValue>))
-                          {
-                              iss.ignore(1, '\'');
-                              iss >> option;
-                          }
-                          else if constexpr
-                              (std::is_same_v<std::decay_t<decltype(option)>,
+                                GncOptionAccountValue>) ||
+                               (std::is_same_v<std::decay_t<decltype(option)>,
+                                GncOptionMultichoiceValue>) ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionValue<const QofInstance*>> ||
                                std::is_same_v<std::decay_t<decltype(option)>,
                                GncOptionValidatedValue<const QofInstance*>>)
@@ -438,6 +455,7 @@ template std::string GncOption::get_value<std::string>() const;
 template const QofInstance* GncOption::get_value<const QofInstance*>() const;
 template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
 template GncOptionAccountList GncOption::get_value<GncOptionAccountList>() const;
+template GncMultichoiceOptionIndexVec GncOption::get_value<GncMultichoiceOptionIndexVec>() const;
 
 template bool GncOption::get_default_value<bool>() const;
 template int GncOption::get_default_value<int>() const;
@@ -447,6 +465,7 @@ template const char* GncOption::get_default_value<const char*>() const;
 template std::string GncOption::get_default_value<std::string>() const;
 template const QofInstance* GncOption::get_default_value<const QofInstance*>() const;
 template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() const;
+template GncMultichoiceOptionIndexVec GncOption::get_default_value<GncMultichoiceOptionIndexVec>() const;
 
 template void GncOption::set_value(bool);
 template void GncOption::set_value(int);
@@ -457,6 +476,7 @@ template void GncOption::set_value(std::string);
 template void GncOption::set_value(const QofInstance*);
 template void GncOption::set_value(RelativeDatePeriod);
 template void GncOption::set_value(size_t);
+template void GncOption::set_value(GncMultichoiceOptionIndexVec);
 
 template bool GncOption::validate(bool) const;
 template bool GncOption::validate(int) const;
@@ -466,3 +486,4 @@ template bool GncOption::validate(const char*) const;
 template bool GncOption::validate(std::string) const;
 template bool GncOption::validate(const QofInstance*) const;
 template bool GncOption::validate(RelativeDatePeriod) const;
+template bool GncOption::validate(GncMultichoiceOptionIndexVec) const;
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index a1144a4e0..d36b19b75 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -1033,6 +1033,95 @@ TEST_F(GncMultichoiceOption, test_multichoice_scheme_in)
     EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
 }
 
+class GncOptionListTest : public ::testing::Test
+{
+protected:
+    GncOptionListTest() :
+        m_option{GncOptionMultichoiceValue{"foo", "bar", "baz",
+                                           "Phony Option",
+                                           GncMultichoiceOptionIndexVec{0, 2},
+                                           {
+                                               {"plugh", "xyzzy", "thud"},
+                                               {"waldo", "pepper", "salt"},
+                                               {"pork", "sausage", "links"},
+                                               {"corge", "grault", "garply"}
+                                           }}} {}
+    GncOption m_option;
+};
+
+using GncListOption = GncOptionListTest;
+
+TEST_F(GncListOption, test_option_ui_type)
+{
+    EXPECT_EQ(GncOptionUIType::LIST, m_option.get_ui_type());
+}
+
+TEST_F(GncListOption, test_validate)
+{
+    EXPECT_TRUE(m_option.validate(std::string{"pork"}));
+    EXPECT_TRUE(m_option.validate(GncMultichoiceOptionIndexVec{1, 3}));
+    EXPECT_FALSE(m_option.validate(GncMultichoiceOptionIndexVec{2, 6}));
+}
+
+TEST_F(GncListOption, test_set_value)
+{
+    EXPECT_NO_THROW({
+            m_option.set_value(GncMultichoiceOptionIndexVec{1, 3});
+            EXPECT_STREQ("multiple values",
+                         m_option.get_value<std::string>().c_str());
+            EXPECT_EQ(1U, m_option.get_value<size_t>());
+            auto vec{m_option.get_value<GncMultichoiceOptionIndexVec>()};
+            ASSERT_EQ(2U, vec.size());
+            EXPECT_EQ(1U, vec[0]);
+            EXPECT_EQ(3U, vec[1]);
+        });
+    EXPECT_THROW({ m_option.set_value(GncMultichoiceOptionIndexVec{2, 5}); }, std::invalid_argument);
+    EXPECT_EQ(1U, m_option.get_value<size_t>());
+}
+
+TEST_F(GncListOption, test_list_out)
+{
+    auto vec{m_option.get_value<GncMultichoiceOptionIndexVec>()};
+    std::string value{m_option.permissible_value(vec[0])};
+    value += " ";
+    value += m_option.permissible_value(vec[1]);
+    std::ostringstream oss;
+    oss << m_option;
+    EXPECT_EQ(oss.str(), value);
+}
+
+TEST_F(GncListOption, test_list_in)
+{
+    std::istringstream iss{"grault"};
+    EXPECT_THROW({
+            iss >> m_option;
+        }, std::invalid_argument);
+    iss.clear(); //reset failedbit
+    iss.str("pork");
+    iss >> m_option;
+    EXPECT_EQ(iss.str(), m_option.get_value<std::string>());
+}
+
+TEST_F(GncListOption, test_list_scheme_out)
+{
+    std::ostringstream oss;
+    m_option.to_scheme(oss);
+    std::string value{"'('"};
+    auto vec{m_option.get_value<GncMultichoiceOptionIndexVec>()};
+    value += m_option.permissible_value(vec[0]);
+    value += " '";
+    value += m_option.permissible_value(vec[1]);
+    value += ")";
+    EXPECT_EQ(value, oss.str());
+}
+
+TEST_F(GncListOption, test_list_scheme_in)
+{
+    std::istringstream iss{"'('pork 'waldo)"};
+    m_option.from_scheme(iss);
+    EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
+}
+
 static time64
 time64_from_gdate(const GDate* g_date, DayPart when)
 {

commit 16da3208fcfa2d393d9f92e6d9c1f08e64c102bd
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 12 17:29:12 2020 -0700

    Make GncOptionDateValue::get_period_index return value size_t.
    
    Instead of int8_t to match the built-in vector index type.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 382b8fb48..9ac10c012 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -97,7 +97,7 @@ GncOptionDateValue::get_default_value() const noexcept
 /* Use asserts for pre- and post-conditions to deliberately crash if they're not
  * met as the program design should prevent that from happening.
  */
-int8_t
+size_t
 GncOptionDateValue::get_period_index() const noexcept
 {
     assert (m_period != RelativeDatePeriod::ABSOLUTE);
@@ -107,7 +107,7 @@ GncOptionDateValue::get_period_index() const noexcept
     return item - m_period_set.begin();
 }
 
-int8_t
+size_t
 GncOptionDateValue::get_default_period_index() const noexcept
 {
     assert(m_period != RelativeDatePeriod::ABSOLUTE);
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index b7bae3c07..9716fdb26 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -731,8 +731,8 @@ public:
     time64 get_default_value() const noexcept;
     RelativeDatePeriod get_period() const noexcept { return m_period; }
     RelativeDatePeriod get_default_period() const noexcept { return m_default_period; }
-    int8_t get_period_index() const noexcept;
-    int8_t get_default_period_index() const noexcept;
+    size_t get_period_index() const noexcept;
+    size_t get_default_period_index() const noexcept;
     std::ostream& out_stream(std::ostream& oss) const noexcept;
     std::istream& in_stream(std::istream& iss);
     bool validate(RelativeDatePeriod value);

commit ac0e7063c79bbc86ab01dbf57d955db131b74deb
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 10 14:49:49 2020 -0700

    Move the GncOptionUIType and dirty members to GncOptionUIUtem.
    
    Even though it makes the class not pure virtual any more it is necessary
    behavior common to all possible subclasses.

diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp
index 847813b48..87ca289a8 100644
--- a/libgnucash/app-utils/gnc-option-ui.hpp
+++ b/libgnucash/app-utils/gnc-option-ui.hpp
@@ -44,14 +44,17 @@ using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
 class GncOptionUIItem
 {
 public:
-    GncOptionUIItem() = default;
+    GncOptionUIItem(GncOptionUIType type) : m_type{type} {}
     virtual ~GncOptionUIItem() = default;
-    virtual GncOptionUIType get_ui_type() const noexcept = 0;
-    virtual void set_dirty(bool status) noexcept = 0;
-    virtual bool get_dirty() const noexcept = 0;
+    GncOptionUIType get_ui_type() const noexcept { return m_type; }
+    virtual void set_dirty(bool status) noexcept { m_dirty = status; }
+    virtual bool get_dirty() const noexcept { return m_dirty; }
     virtual void clear_ui_item() = 0;
     virtual void set_ui_item_from_option(GncOption& option) noexcept = 0;
     virtual void set_option_from_ui_item(GncOption& option) noexcept = 0;
+private:
+    GncOptionUIType m_type;
+    bool m_dirty = false;
 };
 
 #endif //GNC_OPTION_UI_HPP__
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 6be3328ae..a1144a4e0 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -522,8 +522,8 @@ class OptionUIItem : public GncOptionUIItem
     GncUIType m_widget;
     bool m_dirty = false;
 public:
+    OptionUIItem() : GncOptionUIItem{GncOptionUIType::STRING} {}
     ~OptionUIItem() = default;
-    GncOptionUIType get_ui_type() const noexcept override { return GncOptionUIType::STRING; }
     void set_dirty(bool status) noexcept override { m_dirty = status; }
     bool get_dirty() const noexcept override { return m_dirty; }
     void clear_ui_item() override { m_widget.clear(); }

commit 3200bd4966ce5f8a3a8d78f36f5834178b1288b5
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Mar 10 14:48:06 2020 -0700

    Add a multiselect boolean member to GncOptionAccountValue.
    
    To support a variation available in dialog-options. Even though it's not
    used in any GnuCash code nor documented anywhere it might be in some
    custom reports.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index e927f5dae..382b8fb48 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -37,7 +37,8 @@ GncOptionAccountValue::validate(const GncOptionAccountList& values) const
 {
     if (values.empty())
         return false;
-    if (get_ui_type() == GncOptionUIType::ACCOUNT_SEL && values.size() != 1)
+    if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) &&
+        values.size() != 1)
         return false;
     if (m_allowed.empty())
         return true;
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 6977208c1..b7bae3c07 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -544,32 +544,32 @@ class GncOptionAccountValue : public OptionClassifier
 public:
     GncOptionAccountValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
-                          GncOptionUIType ui_type) :
-        OptionClassifier{section, name, key, doc_string},
-        m_ui_type{ui_type}, m_value{}, m_default_value{}, m_allowed{} {}
+                          GncOptionUIType ui_type, bool multi=true) :
+        OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
+        m_value{}, m_default_value{}, m_allowed{}, m_multiselect{multi} {}
 
     GncOptionAccountValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
-                          const GncOptionAccountList& value) :
-        OptionClassifier{section, name, key, doc_string},
-        m_ui_type{ui_type}, m_value{value},
-        m_default_value{std::move(value)}, m_allowed{} {}
+                          const GncOptionAccountList& value, bool multi=true) :
+        OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
+        m_value{value}, m_default_value{std::move(value)}, m_allowed{},
+        m_multiselect{multi}  {}
     GncOptionAccountValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
-                          GncOptionAccountTypeList&& allowed) :
-        OptionClassifier{section, name, key, doc_string},
-        m_ui_type{ui_type}, m_value{},
-        m_default_value{}, m_allowed{std::move(allowed)} {}
+                          GncOptionAccountTypeList&& allowed, bool multi=true) :
+        OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
+        m_value{}, m_default_value{}, m_allowed{std::move(allowed)},
+        m_multiselect{multi} {}
     GncOptionAccountValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           const GncOptionAccountList& value,
-                          GncOptionAccountTypeList&& allowed) :
-        OptionClassifier{section, name, key, doc_string},
-        m_ui_type{ui_type}, m_value{},
-        m_default_value{}, m_allowed{std::move(allowed)} {
+                          GncOptionAccountTypeList&& allowed, bool multi=true) :
+        OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
+        m_value{}, m_default_value{}, m_allowed{std::move(allowed)},
+        m_multiselect{multi} {
             if (!validate(value))
                 throw std::invalid_argument("Supplied Value not in allowed set.");
             m_value = value;
@@ -588,11 +588,13 @@ public:
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+    bool is_multiselect() const noexcept { return m_multiselect; }
 private:
     GncOptionUIType m_ui_type;
     GncOptionAccountList m_value;
     GncOptionAccountList m_default_value;
     GncOptionAccountTypeList m_allowed;
+    bool m_multiselect;
 };
 
 template<> inline std::ostream&
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 7e6393765..5abc02622 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -201,6 +201,18 @@ GncOption::is_changed() const noexcept
                       }, *m_option);
 }
 
+bool
+GncOption::is_multiselect() const noexcept
+{
+    return std::visit([](const auto& option)->bool {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionAccountValue>)
+                              return option.is_multiselect();
+                          else
+                              return false;
+                      }, *m_option);
+}
+
 template<typename ValueType> bool
 GncOption::validate(ValueType value) const
 {
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 6e8cbd384..8a1b126ba 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -86,6 +86,7 @@ public:
     void set_option_from_ui_item();
     void make_internal();
     bool is_changed() const noexcept;
+    bool is_multiselect() const noexcept;
     template <typename ValueType> bool validate(ValueType value) const;
     std::size_t num_permissible_values() const;
     std::size_t permissible_value_index(const char* value) const;
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 0e939ae50..6be3328ae 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -757,29 +757,34 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
     EXPECT_THROW({
             GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
                                          GncOptionUIType::ACCOUNT_LIST,
-                                         acclistbad, {ACCT_TYPE_BANK});
+                                         acclistbad,
+                                         GncOptionAccountTypeList{ACCT_TYPE_BANK});
         }, std::invalid_argument);
 
     EXPECT_THROW({
             GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
                                          GncOptionUIType::ACCOUNT_SEL,
-                                         acclistgood, {ACCT_TYPE_BANK});
+                                         acclistgood,
+                                         GncOptionAccountTypeList{ACCT_TYPE_BANK});
         }, std::invalid_argument);
 
     EXPECT_NO_THROW({
             GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
                                          GncOptionUIType::ACCOUNT_LIST,
-                                         acclistgood, {ACCT_TYPE_BANK});
+                                         acclistgood,
+                                         GncOptionAccountTypeList{ACCT_TYPE_BANK});
         });
 
     EXPECT_NO_THROW({
             GncOptionAccountList accsel{acclistgood[0]};
             GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
                                          GncOptionUIType::ACCOUNT_LIST,
-                                         accsel, {ACCT_TYPE_BANK});
+                                         accsel,
+                                         GncOptionAccountTypeList{ACCT_TYPE_BANK});
         });
     GncOptionAccountValue option {"foo", "bar", "baz", "Bogus Option",
-            GncOptionUIType::ACCOUNT_LIST, acclistgood, {ACCT_TYPE_BANK}};
+                                  GncOptionUIType::ACCOUNT_LIST, acclistgood,
+                                  GncOptionAccountTypeList{ACCT_TYPE_BANK}};
     EXPECT_FALSE(option.get_value().empty());
     EXPECT_FALSE(option.get_default_value().empty());
     EXPECT_EQ(true, option.validate(acclistgood));
@@ -804,7 +809,8 @@ TEST_F(GncOptionAccountTest, test_account_list_out)
     GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
                                                "Bogus Option",
                                                GncOptionUIType::ACCOUNT_LIST,
-                                               accsel, {ACCT_TYPE_BANK}}};
+                                               accsel,
+                                               GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
     acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string();
 
     oss.str("");
@@ -830,7 +836,8 @@ TEST_F(GncOptionAccountTest, test_account_list_in)
     GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
                                                "Bogus Option",
                                                GncOptionUIType::ACCOUNT_LIST,
-                                               accsel, {ACCT_TYPE_BANK}}};
+                                               accsel,
+                                               GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
     acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string();
     acc_guids += " ";
@@ -885,7 +892,8 @@ TEST_F(GncOptionAccountTest, test_account_list_to_scheme)
     GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
                                                "Bogus Option",
                                                GncOptionUIType::ACCOUNT_LIST,
-                                               accsel, {ACCT_TYPE_BANK}}};
+                                               accsel,
+                                               GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
     acc_guids = make_account_list_SCM_str(accsel);
 
     oss.str("");
@@ -909,7 +917,8 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
     GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
                                                "Bogus Option",
                                                GncOptionUIType::ACCOUNT_LIST,
-                                               accsel, {ACCT_TYPE_BANK}}};
+                                               accsel,
+                                               GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
     acc_guids = make_account_list_SCM_str(acclistbad);
     iss.str(acc_guids);

commit fce33799afcf2d1bb1633d77e8513dc757ce9e1b
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 5 20:00:48 2020 -0800

    Change GncOptionUIItem to be a pure virtual class instead of a templated one.
    
    Simplifies the design because derived classes can have whatever save UI class
    they need and implement the void(void) set_ui_item_from option and
    set_option_from_ui_item with whatever functions are appropriate for
    the UI class. No need for callbacks or std::function members.

diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp
index ef2a8a4a5..847813b48 100644
--- a/libgnucash/app-utils/gnc-option-ui.hpp
+++ b/libgnucash/app-utils/gnc-option-ui.hpp
@@ -25,17 +25,10 @@
 #define GNC_OPTION_UI_HPP_
 
 #include "gnc-option-uitype.hpp"
-template <typename UIType>
-class GncUIItem
-{
-public:
-    GncUIItem(UIType* widget) : m_widget{widget} {}
-    UIType* m_widget;
-};
 
-class GncUIType;
-using OptionUIItem = GncUIItem<GncUIType>;
-using OptionSyncFunc = std::function<void(OptionUIItem&, GncOption&)>;
+class GncOptionUIItem;
+using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
+
 /**
  * Holds a pointer to the UI item which will control the option and an enum
  * representing the type of the option for dispatch purposes; all of that
@@ -47,47 +40,18 @@ using OptionSyncFunc = std::function<void(OptionUIItem&, GncOption&)>;
  * clear_ui_item function can be used as a weak_ptr's destruction callback to
  * ensure that the ptr will be nulled if the ui_item is destroyed elsewhere.
  */
+
 class GncOptionUIItem
 {
 public:
-    GncOptionUIItem(OptionUIItem&& ui_item, GncOptionUIType type,
-                    OptionSyncFunc to_ui, OptionSyncFunc from_ui) :
-        m_ui_item{std::move(ui_item)}, m_ui_type{type},
-        m_set_ui_item_from_option{to_ui}, m_set_option_from_ui_item{from_ui} {}
-    GncOptionUIItem(GncOptionUIType ui_type) :
-        m_ui_item{nullptr}, m_ui_type{ui_type} {}
-    GncOptionUIItem(const GncOptionUIItem&) = default;
-    GncOptionUIItem(GncOptionUIItem&&) = default;
-    ~GncOptionUIItem() = default;
-    GncOptionUIItem& operator=(const GncOptionUIItem&) = default;
-    GncOptionUIItem& operator=(GncOptionUIItem&&) = default;
-    GncOptionUIType get_ui_type() const { return m_ui_type; }
-    const OptionUIItem& get_ui_item() const {return m_ui_item; }
-    void clear_ui_item() { m_ui_item = nullptr; }
-    void set_ui_item(OptionUIItem&& ui_item)
-    {
-        if (m_ui_type == GncOptionUIType::INTERNAL)
-        {
-            std::string error{"INTERNAL option, setting the UI item forbidden."};
-            throw std::logic_error(std::move(error));
-        }
-        m_ui_item = std::move(ui_item);
-    }
-    void set_ui_item_from_option(GncOption& option)
-    {
-        m_set_ui_item_from_option(m_ui_item, option);
-    }
-    void set_option_from_ui_item(GncOption& option)
-    {
-        m_set_option_from_ui_item(m_ui_item, option);
-    }
-private:
-    OptionUIItem m_ui_item;
-    GncOptionUIType m_ui_type;
-    OptionSyncFunc m_set_ui_item_from_option;
-    OptionSyncFunc m_set_option_from_ui_item;
+    GncOptionUIItem() = default;
+    virtual ~GncOptionUIItem() = default;
+    virtual GncOptionUIType get_ui_type() const noexcept = 0;
+    virtual void set_dirty(bool status) noexcept = 0;
+    virtual bool get_dirty() const noexcept = 0;
+    virtual void clear_ui_item() = 0;
+    virtual void set_ui_item_from_option(GncOption& option) noexcept = 0;
+    virtual void set_option_from_ui_item(GncOption& option) noexcept = 0;
 };
 
-using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
-
 #endif //GNC_OPTION_UI_HPP__

commit 00aa0f603deb3af1a8936006f1a8c310474b6fda
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 5 18:07:35 2020 -0800

    Add a PLOTSIZE option UI type.
    
    I'd missed this one earlier.

diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index b33a4f95e..ac3889f4a 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -41,6 +41,7 @@ enum GncOptionUIType
     NUMBER_RANGE,
     COLOR,
     FONT,
+    PLOT_SIZE,
     BUDGET,
     PIXMAP,
     RADIOBUTTON,

commit 6c8e0e23f7a1ae44edb173243db70a96c99acd81
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 5 18:06:46 2020 -0800

    Redesign GncOptionDateValue.
    
    After getting a better understanding of how it interacts with dialog-options and
    finding some additional scheme API needing implementation (exposed only in
    hello-world.scm, but possibly used in user-written reports).
    
    Creates a new file-pair, gnc-options-date.[ch]pp to separate the creation of
    the static structs with the type info and strings for the various
    RelativeDatePeriod values from the already too large gnc-option-impl.
    Although the result is a rather C-ish design the alternative with classes
    or template specializations would be more complicated and offer no
    benefit.
    
    Implements the permissible_values functions of GncOptionMultichoiceValue
    for GncDateValue and provides for absolute, relative, and both
    UI values for the three widget configs afforded by dialog-options.

diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt
index e384adb2c..20cd3c6d9 100644
--- a/libgnucash/app-utils/CMakeLists.txt
+++ b/libgnucash/app-utils/CMakeLists.txt
@@ -11,6 +11,7 @@ set (app_utils_noinst_HEADERS
   calculation/finproto.h
   calculation/fin_spl_protos.h
   calculation/fin_static_proto.h
+  gnc-option-date.hpp
   gnc-option-impl.hpp
   gnc-optiondb-impl.hpp
 )
@@ -69,6 +70,7 @@ set (app_utils_SOURCES
   gnc-exp-parser.c
   gnc-gsettings.c
   gnc-helpers.c
+  gnc-option-date.cpp
   gnc-option.cpp
   gnc-option-impl.cpp
   gnc-optiondb.cpp
diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp
new file mode 100644
index 000000000..e7f113226
--- /dev/null
+++ b/libgnucash/app-utils/gnc-option-date.cpp
@@ -0,0 +1,553 @@
+/********************************************************************\
+ * gnc-option-date.cpp -- Relative Dates for options                *
+ * Copyright (C) 2020 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+
+#include "gnc-option-date.hpp"
+#include <array>
+#include <gnc-datetime.hpp>
+#include <iostream>
+
+extern "C"
+{
+#include <gnc-accounting-period.h>
+}
+
+#define N_(string) string //So that xgettext will find it
+
+enum RelativeDateType
+{
+    ABSOLUTE,
+    LAST,
+    NEXT,
+    START,
+    END
+};
+
+enum RelativeDateOffset
+{
+    NONE,
+    WEEK,
+    MONTH,
+    QUARTER,
+    THREE,
+    SIX,
+    YEAR
+};
+
+struct GncRelativeDate
+{
+    RelativeDatePeriod m_period;
+    RelativeDateType m_type;
+    RelativeDateOffset m_offset;
+    const char* m_storage;
+    const char* m_display;
+    const char* m_description;
+};
+
+
+/* The fixed values and strings for date periods. Accessor functions will use
+ * the RelativeDatePeriod as an index so any changes need to be reflected in the
+ * RelativeDatePeriod enum class in gnc-option-date.hpp and vice-versa.
+ *
+ * The double curly braces are actually correct and required for a std::array
+ * initializer list.
+ */
+static const std::array<GncRelativeDate, 31> reldates
+{{
+    {
+        RelativeDatePeriod::TODAY,
+        RelativeDateType::LAST,
+        RelativeDateOffset::NONE,
+        "today",
+        N_("Today"),
+        N_("The current date.")
+    },
+    {
+        RelativeDatePeriod::ONE_WEEK_AGO,
+        RelativeDateType::LAST,
+        RelativeDateOffset::WEEK,
+        "one-week-ago",
+        N_("One Week Ago"),
+        N_("One Week Ago.")
+    },
+    {
+        RelativeDatePeriod::ONE_WEEK_AHEAD,
+        RelativeDateType::NEXT,
+        RelativeDateOffset::WEEK,
+        "one-week-ahead",
+        N_("One Week Ahead"),
+        N_("One Week Ahead.")
+    },
+    {
+        RelativeDatePeriod::ONE_MONTH_AGO,
+        RelativeDateType::LAST,
+        RelativeDateOffset::MONTH,
+        "one-month-ago",
+        N_("One Month Ago"),
+        N_("One Month Ago.")
+    },
+    {
+        RelativeDatePeriod::ONE_MONTH_AHEAD,
+        RelativeDateType::NEXT,
+        RelativeDateOffset::MONTH,
+        "one-month-ahead",
+        N_("One Month Ahead"),
+        N_("One Month Ahead.")
+    },
+    {
+        RelativeDatePeriod::THREE_MONTHS_AGO,
+        RelativeDateType::LAST,
+        RelativeDateOffset::THREE,
+        "three-months-ago",
+        N_("Three Months Ago"),
+        N_("Three Months Ago.")
+    },
+    {
+        RelativeDatePeriod::THREE_MONTHS_AHEAD,
+        RelativeDateType::NEXT,
+        RelativeDateOffset::THREE,
+        "three-months-ahead",
+        N_("Three Months Ahead"),
+        N_("Three Months Ahead.")
+    },
+    {
+        RelativeDatePeriod::SIX_MONTHS_AGO,
+        RelativeDateType::LAST,
+        RelativeDateOffset::SIX,
+        "six-months-ago",
+        N_("Six Months Ago"),
+        N_("Six Months Ago.")
+    },
+    {
+        RelativeDatePeriod::SIX_MONTHS_AHEAD,
+        RelativeDateType::NEXT,
+        RelativeDateOffset::SIX,
+        "six-months-ahead",
+        N_("Six Months Ahead"),
+        N_("Six Months Ahead.")
+    },
+    {
+        RelativeDatePeriod::ONE_YEAR_AGO,
+        RelativeDateType::LAST,
+        RelativeDateOffset::YEAR,
+        "one-year-ago",
+        N_("One Year Ago"),
+        N_("One Year Ago.")
+    },
+    {
+        RelativeDatePeriod::ONE_YEAR_AHEAD,
+        RelativeDateType::NEXT,
+        RelativeDateOffset::YEAR,
+        "one-year-ahead",
+        N_("One Year Ahead"),
+        N_("One Year Ahead.")
+    },
+    {
+        RelativeDatePeriod::START_THIS_MONTH,
+        RelativeDateType::START,
+        RelativeDateOffset::MONTH,
+        "start-this-month",
+        N_("Start of this month"),
+        N_("First day of the current month.")
+    },
+    {
+        RelativeDatePeriod::END_THIS_MONTH,
+        RelativeDateType::END,
+        RelativeDateOffset::MONTH,
+        "end-this-month",
+        N_("End of this month"),
+        N_("Last day of the current month.")
+    },
+    {
+        RelativeDatePeriod::START_PREV_MONTH,
+        RelativeDateType::START,
+        RelativeDateOffset::MONTH,
+        "start-prev-month",
+        N_("Start of previous month"),
+        N_("First day of the previous month.")
+    },
+    {
+        RelativeDatePeriod::END_PREV_MONTH,
+        RelativeDateType::END,
+        RelativeDateOffset::MONTH,
+        "end-prev-month",
+        N_("End of previous month"),
+        N_("Last day of previous month.")
+    },
+    {
+        RelativeDatePeriod::START_NEXT_MONTH,
+        RelativeDateType::START,
+        RelativeDateOffset::MONTH,
+        "start-next-month",
+        N_("Start of next month"),
+        N_("First day of the next month.")
+    },
+    {
+        RelativeDatePeriod::END_NEXT_MONTH,
+        RelativeDateType::END,
+        RelativeDateOffset::MONTH,
+        "end-next-month",
+        N_("End of next month"),
+        N_("Last day of next month.")
+    },
+    {
+        RelativeDatePeriod::START_CURRENT_QUARTER,
+        RelativeDateType::START,
+        RelativeDateOffset::QUARTER,
+        "start-current-quarter",
+        N_("Start of current quarter"),
+        N_("First day of the current quarterly accounting period.")
+    },
+    {
+        RelativeDatePeriod::END_CURRENT_QUARTER,
+        RelativeDateType::END,
+        RelativeDateOffset::QUARTER,
+        "end-current-quarter",
+        N_("End of current quarter"),
+        N_("Last day of the current quarterly accounting period.")
+    },
+    {
+        RelativeDatePeriod::START_PREV_QUARTER,
+        RelativeDateType::START,
+        RelativeDateOffset::QUARTER,
+        "start-prev-quarter",
+        N_("Start of previous quarter"),
+        N_("First day of the previous quarterly accounting period.")
+    },
+    {
+        RelativeDatePeriod::END_PREV_QUARTER,
+        RelativeDateType::END,
+        RelativeDateOffset::QUARTER,
+        "end-prev-quarter",
+        N_("End of previous quarter"),
+        N_("Last day of previous quarterly accounting period.")
+    },
+    {
+        RelativeDatePeriod::START_NEXT_QUARTER,
+        RelativeDateType::START,
+        RelativeDateOffset::QUARTER,
+        "start-next-quarter",
+        N_("Start of next quarter"),
+        N_("First day of the next quarterly accounting period.")
+    },
+    {
+        RelativeDatePeriod::END_NEXT_QUARTER,
+        RelativeDateType::END,
+        RelativeDateOffset::QUARTER,
+        "end-next-quarter",
+        N_("End of next quarter"),
+        N_("Last day of next quarterly accounting period.")
+    },
+    {
+        RelativeDatePeriod::START_CAL_YEAR,
+        RelativeDateType::START,
+        RelativeDateOffset::YEAR,
+        "start-cal-year",
+        N_("Start of this year"),
+        N_("First day of the current calendar year.")
+    },
+    {
+        RelativeDatePeriod::END_CAL_YEAR,
+        RelativeDateType::END,
+        RelativeDateOffset::YEAR,
+        "end-cal-year",
+        N_("End of this year"),
+        N_("Last day of the current calendar year.")
+    },
+    {
+        RelativeDatePeriod::START_PREV_YEAR,
+        RelativeDateType::START,
+        RelativeDateOffset::YEAR,
+        "start-prev-year",
+        N_("Start of previous year"),
+        N_("First day of the previous calendar year.")
+    },
+    {
+        RelativeDatePeriod::END_PREV_YEAR,
+        RelativeDateType::END,
+        RelativeDateOffset::YEAR,
+        "end-prev-year",
+        N_("End of previous year"),
+        N_("Last day of the previous calendar year.")
+    },
+    {
+        RelativeDatePeriod::START_NEXT_YEAR,
+        RelativeDateType::START,
+        RelativeDateOffset::YEAR,
+        "start-next-year",
+        N_("Start of next year"),
+        N_("First day of the next calendar year.")
+    },
+    {
+        RelativeDatePeriod::END_NEXT_YEAR,
+        RelativeDateType::END,
+        RelativeDateOffset::YEAR,
+        "end-next-year",
+        N_("End of next year"),
+        N_("Last day of the next calendar year.")
+    },
+    {
+        RelativeDatePeriod::START_ACCOUNTING_PERIOD,
+        RelativeDateType::START,
+        RelativeDateOffset::YEAR,
+        "start-prev-fin-year",
+        N_("Start of accounting period"),
+        N_("First day of the accounting period, as set in the global preferences.")
+    },
+    {
+        RelativeDatePeriod::END_ACCOUNTING_PERIOD,
+        RelativeDateType::END,
+        RelativeDateOffset::YEAR,
+        "end-prev-fin-year",
+        N_("End of accounting period"),
+        N_("Last day of the accounting period, as set in the global preferences.")
+    }
+    }};
+
+static const GncRelativeDate&
+checked_reldate(RelativeDatePeriod per)
+{
+    assert (reldates[static_cast<int>(per)].m_period == per);
+    return reldates[static_cast<int>(per)];
+}
+
+bool
+gnc_relative_date_is_single(RelativeDatePeriod per)
+{
+    if (per == RelativeDatePeriod::ABSOLUTE)
+        return false;
+    auto reldate = checked_reldate(per);
+    return reldate.m_type == RelativeDateType::LAST ||
+        reldate.m_type == RelativeDateType::NEXT;
+}
+
+bool
+gnc_relative_date_is_starting(RelativeDatePeriod per)
+{
+    if (per == RelativeDatePeriod::ABSOLUTE)
+        return false;
+    return checked_reldate(per).m_type == RelativeDateType::START;
+}
+
+bool
+gnc_relative_date_is_ending(RelativeDatePeriod per)
+{
+    if (per == RelativeDatePeriod::ABSOLUTE)
+        return false;
+    return checked_reldate(per).m_type == RelativeDateType::END;
+}
+
+const char*
+gnc_relative_date_storage_string(RelativeDatePeriod per)
+{
+    if (per == RelativeDatePeriod::ABSOLUTE)
+        return nullptr;
+    return checked_reldate(per).m_storage;
+}
+
+const char*
+gnc_relative_date_display_string(RelativeDatePeriod per)
+{
+    if (per == RelativeDatePeriod::ABSOLUTE)
+        return nullptr;
+    return checked_reldate(per).m_display;
+}
+const char*
+gnc_relative_date_description(RelativeDatePeriod per)
+{
+    if (per == RelativeDatePeriod::ABSOLUTE)
+        return nullptr;
+    return checked_reldate(per).m_description;
+}
+
+RelativeDatePeriod
+gnc_relative_date_from_storage_string(const char* str)
+{
+    auto per = std::find_if(reldates.begin(), reldates.end(),
+                         [str](auto rel) -> bool
+                         {
+                             return strcmp(str, rel.m_storage) == 0;
+                         });
+    return per != reldates.end() ? per->m_period : RelativeDatePeriod::ABSOLUTE;
+}
+
+static bool
+reldate_is_prev(RelativeDatePeriod per)
+{
+    auto rdate{checked_reldate(per)};
+    return per == RelativeDatePeriod::START_PREV_YEAR ||
+        per == RelativeDatePeriod::END_PREV_YEAR ||
+        per == RelativeDatePeriod::START_PREV_QUARTER ||
+        per == RelativeDatePeriod::END_PREV_QUARTER ||
+        per == RelativeDatePeriod::START_PREV_MONTH ||
+        per == RelativeDatePeriod::END_PREV_MONTH ||
+        rdate.m_type == LAST;
+}
+
+static bool
+reldate_is_next(RelativeDatePeriod per)
+{
+    auto rdate{checked_reldate(per)};
+    return per == RelativeDatePeriod::START_NEXT_YEAR ||
+        per == RelativeDatePeriod::END_NEXT_YEAR ||
+        per == RelativeDatePeriod::START_NEXT_QUARTER ||
+        per == RelativeDatePeriod::END_NEXT_QUARTER ||
+        per == RelativeDatePeriod::START_NEXT_MONTH ||
+        per == RelativeDatePeriod::END_NEXT_MONTH ||
+        rdate.m_type == NEXT;
+}
+
+static RelativeDateOffset
+reldate_offset(RelativeDatePeriod per)
+{
+    return checked_reldate(per).m_offset;
+}
+
+static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static void
+normalize_tm(struct tm& now)
+{
+    auto tmp_mon = now.tm_mon + (now.tm_mon < 0 ? 12 :
+                                 now.tm_mon > 11 ? -12 : 0);
+    if (now.tm_mday < 1)
+    {
+        --now.tm_mon;
+        now.tm_mday += days_in_month[tmp_mon - 1];
+    }
+    else if (now.tm_mday > days_in_month[tmp_mon])
+    {
+        ++now.tm_mon;
+        now.tm_mday -= days_in_month[tmp_mon];
+    }
+    if (now.tm_mon < 0)
+    {
+        now.tm_mon += 12;
+        --now.tm_year;
+    }
+    else if (now.tm_mon > 11)
+    {
+        now.tm_mon -= 12;
+        ++now.tm_year;
+    }
+}
+
+static void
+set_day_and_time(struct tm& now, RelativeDateType type)
+{
+    if (type == RelativeDateType::START)
+    {
+        now.tm_hour = now.tm_min = now.tm_sec = 0;
+        now.tm_mday = 1;
+    }
+    else if (type == RelativeDateType::END)
+    {
+        now.tm_min = now.tm_sec = 59;
+        now.tm_hour = 23;
+        now.tm_mday = days_in_month[now.tm_mon];
+        // Check for Februrary in a leap year
+        if (int year = now.tm_year + 1900; now.tm_mon == 1 &&
+            year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+            ++now.tm_mday;
+    }
+    // Do nothing for LAST and NEXT.
+};
+
+time64
+gnc_relative_date_to_time64(RelativeDatePeriod period)
+{
+    if (period == RelativeDatePeriod::TODAY)
+        return static_cast<time64>(GncDateTime());
+    if (period == RelativeDatePeriod::START_ACCOUNTING_PERIOD)
+        return gnc_accounting_period_fiscal_start();
+    if (period == RelativeDatePeriod::END_ACCOUNTING_PERIOD)
+        return gnc_accounting_period_fiscal_end();
+
+    GncDateTime now_t;
+    if (period == RelativeDatePeriod::TODAY)
+        return static_cast<time64>(now_t);
+    auto now{static_cast<tm>(now_t)};
+    auto acct_per{static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()))};
+
+    if (acct_per.tm_mon == now.tm_mon && acct_per.tm_mday == now.tm_mday)
+    {
+        //No set accounting period, use the calendar year
+        acct_per.tm_mon = 0;
+        acct_per.tm_mday = 0;
+    }
+
+    switch(reldate_offset(period))
+    {
+        case RelativeDateOffset::NONE:
+// Report on today so nothing to do
+            break;
+        case RelativeDateOffset::YEAR:
+            if (reldate_is_prev(period))
+                --now.tm_year;
+            else if (reldate_is_next(period))
+                ++now.tm_year;
+            if (gnc_relative_date_is_starting(period))
+                now.tm_mon = 0;
+            else if (gnc_relative_date_is_ending(period))
+                now.tm_mon = 11;
+            now.tm_mday = days_in_month[now.tm_mon];
+            break;
+       case RelativeDateOffset::SIX:
+            if (reldate_is_prev(period))
+                now.tm_mon -= 6;
+            else if (reldate_is_next(period))
+                now.tm_mon += 6;
+            now.tm_mday = days_in_month[now.tm_mon];
+            break;
+        case RelativeDateOffset::QUARTER:
+        {
+            auto delta = (now.tm_mon > acct_per.tm_mon ?
+                          now.tm_mon - acct_per.tm_mon :
+                           acct_per.tm_mon - now.tm_mon) % 3;
+            now.tm_mon = now.tm_mon - delta;
+        }
+            [[fallthrough]];
+        case RelativeDateOffset::THREE:
+            if (reldate_is_prev(period))
+                now.tm_mon -= 3;
+            else if (reldate_is_next(period))
+                now.tm_mon += 3;
+            if (gnc_relative_date_is_ending(period))
+                now.tm_mon += 2;
+            now.tm_mday = days_in_month[now.tm_mon];
+            break;
+       case RelativeDateOffset::MONTH:
+            if (reldate_is_prev(period))
+                --now.tm_mon;
+            else if (reldate_is_next(period))
+                ++now.tm_mon;
+            now.tm_mday = days_in_month[now.tm_mon];
+            break;
+        case RelativeDateOffset::WEEK:
+            if (reldate_is_prev(period))
+                now.tm_mday -= 7;
+            else if (reldate_is_next(period))
+                now.tm_mday += 7;
+    }
+    normalize_tm(now);
+    set_day_and_time(now, checked_reldate(period).m_type);
+    return static_cast<time64>(GncDateTime(now));
+}
diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp
new file mode 100644
index 000000000..954e0a1cc
--- /dev/null
+++ b/libgnucash/app-utils/gnc-option-date.hpp
@@ -0,0 +1,88 @@
+/********************************************************************\
+ * gnc-option-date.hpp -- Relative dates for options                *
+ * Copyright (C) 2020 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#ifndef GNC_OPTION_DATE_HPP_
+#define GNC_OPTION_DATE_HPP_
+
+extern "C"
+{
+#include <gnc-date.h>
+}
+
+#include <vector>
+
+/**
+ * Reporting periods relative to the current date.
+ *
+ * The original design allowed custom RelativeDatePeriods, but that facility is
+ * unused so we'll go with compiled-in enums.
+ */
+enum class RelativeDatePeriod : int
+{
+    ABSOLUTE = -1,
+    TODAY,
+    ONE_WEEK_AGO,
+    ONE_WEEK_AHEAD,
+    ONE_MONTH_AGO,
+    ONE_MONTH_AHEAD,
+    THREE_MONTHS_AGO,
+    THREE_MONTHS_AHEAD,
+    SIX_MONTHS_AGO,
+    SIX_MONTHS_AHEAD,
+    ONE_YEAR_AGO,
+    ONE_YEAR_AHEAD,
+    START_THIS_MONTH,
+    END_THIS_MONTH,
+    START_PREV_MONTH,
+    END_PREV_MONTH,
+    START_NEXT_MONTH,
+    END_NEXT_MONTH,
+    START_CURRENT_QUARTER,
+    END_CURRENT_QUARTER,
+    START_PREV_QUARTER,
+    END_PREV_QUARTER,
+    START_NEXT_QUARTER,
+    END_NEXT_QUARTER,
+    START_CAL_YEAR,
+    END_CAL_YEAR,
+    START_PREV_YEAR,
+    END_PREV_YEAR,
+    START_NEXT_YEAR,
+    END_NEXT_YEAR,
+    START_ACCOUNTING_PERIOD,
+    END_ACCOUNTING_PERIOD,
+};
+
+using RelativeDatePeriodVec = std::vector<RelativeDatePeriod>;
+
+bool gnc_relative_date_is_single(RelativeDatePeriod);
+bool gnc_relative_date_is_starting(RelativeDatePeriod);
+bool gnc_relative_date_is_ending(RelativeDatePeriod);
+const char* gnc_relative_date_storage_string(RelativeDatePeriod);
+const char* gnc_relative_date_display_string(RelativeDatePeriod);
+const char* gnc_relative_date_description(RelativeDatePeriod);
+RelativeDatePeriod gnc_relative_date_from_storage_string(const char*);
+time64 gnc_relative_date_to_time64(RelativeDatePeriod);
+
+
+#endif //GNC_OPTION_DATE_HPP_
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index d8160860d..6977208c1 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -692,52 +692,97 @@ public:
                        const char* key, const char* doc_string,
                        GncOptionUIType ui_type) :
         OptionClassifier{section, name, key, doc_string},
-        m_ui_type{ui_type}, m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
-        m_date{INT64_MAX},
-        m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
-        m_default_date{INT64_MAX} {}
+        m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
+        m_period{RelativeDatePeriod::TODAY},
+        m_default_period{RelativeDatePeriod::TODAY},
+        m_period_set{} {}
     GncOptionDateValue(const char* section, const char* name,
                        const char* key, const char* doc_string,
                        GncOptionUIType ui_type, time64 time) :
         OptionClassifier{section, name, key, doc_string},
-        m_ui_type{ui_type}, m_period{RelativeDatePeriod::ABSOLUTE},
-        m_date{time}, m_default_period{RelativeDatePeriod::ABSOLUTE},
-        m_default_date{time} {}
+        m_ui_type{ui_type}, m_date{time}, m_default_date{time},
+        m_period{RelativeDatePeriod::ABSOLUTE},
+        m_default_period{RelativeDatePeriod::ABSOLUTE},
+        m_period_set{} {}
     GncOptionDateValue(const char* section, const char* name,
                        const char* key, const char* doc_string,
                        GncOptionUIType ui_type,
-                       const RelativeDatePeriod period) :
+                       RelativeDatePeriod period) :
         OptionClassifier{section, name, key, doc_string},
-        m_ui_type{ui_type}, m_period{period}, m_date{INT64_MAX},
-        m_default_period{period}, m_default_date{INT64_MAX} {}
-        GncOptionDateValue(const GncOptionDateValue&) = default;
-        GncOptionDateValue(GncOptionDateValue&&) = default;
-        GncOptionDateValue& operator=(const GncOptionDateValue&) = default;
-        GncOptionDateValue& operator=(GncOptionDateValue&&) = default;
-    time64 get_value() const;
-    time64 get_default_value() const { return static_cast<time64>(GncDateTime()); }
+        m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
+        m_period{period}, m_default_period{period},
+        m_period_set{} {}
+    GncOptionDateValue(const char* section, const char* name,
+                       const char* key, const char* doc_string,
+                       GncOptionUIType ui_type,
+                       const RelativeDatePeriodVec& period_set) :
+        OptionClassifier{section, name, key, doc_string},
+        m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
+        m_period{period_set.back()},
+        m_default_period{period_set.back()},
+        m_period_set{period_set} {}
+    GncOptionDateValue(const GncOptionDateValue&) = default;
+    GncOptionDateValue(GncOptionDateValue&&) = default;
+    GncOptionDateValue& operator=(const GncOptionDateValue&) = default;
+    GncOptionDateValue& operator=(GncOptionDateValue&&) = default;
+    time64 get_value() const noexcept;
+    time64 get_default_value() const noexcept;
     RelativeDatePeriod get_period() const noexcept { return m_period; }
     RelativeDatePeriod get_default_period() const noexcept { return m_default_period; }
+    int8_t get_period_index() const noexcept;
+    int8_t get_default_period_index() const noexcept;
     std::ostream& out_stream(std::ostream& oss) const noexcept;
     std::istream& in_stream(std::istream& iss);
+    bool validate(RelativeDatePeriod value);
+    bool validate(time64 time) {
+        if (time > MINTIME && time < MAXTIME)
+            return true;
+        return false;
+    }
     void set_value(RelativeDatePeriod value) {
-        m_period = value;
-        m_date = INT64_MAX;
+        if (validate(value))
+        {
+            m_period = value;
+            m_date = INT64_MAX;
+        }
     }
     void set_value(time64 time) {
-        m_period = RelativeDatePeriod::ABSOLUTE;
-        m_date = time;
+        if (validate(time))
+        {
+            m_period = RelativeDatePeriod::ABSOLUTE;
+            m_date = time;
+        }
+    }
+    void set_value(size_t index) noexcept;
+    std::size_t num_permissible_values() const noexcept
+    {
+        return m_period_set.size();
+    }
+    std::size_t permissible_value_index(const char* key) const noexcept;
+    const char* permissible_value(std::size_t index) const
+    {
+        return gnc_relative_date_storage_string(m_period_set.at(index));
+    }
+    const char* permissible_value_name(std::size_t index) const
+    {
+        return gnc_relative_date_display_string(m_period_set.at(index));
+    }
+    const char* permissible_value_description(std::size_t index) const
+    {
+        return gnc_relative_date_description(m_period_set.at(index));
     }
     bool is_changed() const noexcept { return m_period != m_default_period &&
             m_date != m_default_date; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
+    const RelativeDatePeriodVec& get_period_set() const { return m_period_set; }
 private:
     GncOptionUIType m_ui_type;
-    RelativeDatePeriod m_period;
     time64 m_date;
-    RelativeDatePeriod m_default_period;
     time64 m_default_date;
+    RelativeDatePeriod m_period;
+    RelativeDatePeriod m_default_period;
+    RelativeDatePeriodVec m_period_set;
 };
 
 template<> inline std::ostream&
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index 9814d709f..b33a4f95e 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -33,10 +33,8 @@ enum GncOptionUIType
     COMMODITY,
     MULTICHOICE,
     DATE_ABSOLUTE,
-    DATE_RELATIVE_BEGIN,
-    DATE_BOTH_BEGIN,
-    DATE_RELATIVE_END,
-    DATE_BOTH_END,
+    DATE_RELATIVE,
+    DATE_BOTH,
     ACCOUNT_LIST,
     ACCOUNT_SEL,
     LIST,
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 321258066..7e6393765 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -49,10 +49,15 @@ GncOption::get_value() const
                           if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
                                            return option.get_value();
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                         GncOptionDateValue> &&
-                                         std::is_same_v<std::decay_t<ValueType>,
+                                        GncOptionDateValue> &&
+                                        std::is_same_v<std::decay_t<ValueType>,
                                         RelativeDatePeriod>)
                               return option.get_period();
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionDateValue> &&
+                                        std::is_same_v<std::decay_t<ValueType>,
+                                        size_t>)
+                              return option.get_period_index();
                           return ValueType {};
                       }, *m_option);
 }
@@ -63,6 +68,16 @@ GncOption::get_default_value() const
     return std::visit([](const auto option)->ValueType {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
                                            return option.get_default_value();
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionDateValue> &&
+                                        std::is_same_v<std::decay_t<ValueType>,
+                                        RelativeDatePeriod>)
+                              return option.get_default_period();
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionDateValue> &&
+                                        std::is_same_v<std::decay_t<ValueType>,
+                                        size_t>)
+                              return option.get_default_period_index();
                           return ValueType {};
                       }, *m_option);
 
@@ -77,8 +92,9 @@ GncOption::set_value(ValueType value)
                         std::decay_t<ValueType>> ||
                         (std::is_same_v<std::decay_t<decltype(option)>,
                          GncOptionDateValue> &&
-                         std::is_same_v<std::decay_t<ValueType>,
-                         RelativeDatePeriod>))
+                         (std::is_same_v<std::decay_t<ValueType>,
+                         RelativeDatePeriod> ||
+                          std::is_same_v<std::decay_t<ValueType>, size_t>)))
                            option.set_value(value);
                }, *m_option);
 }
@@ -206,7 +222,9 @@ GncOption::num_permissible_values() const
 {
     return std::visit([] (const auto& option) -> size_t {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionMultichoiceValue>)
+                                        GncOptionMultichoiceValue>  ||
+                                        std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionDateValue>)
                                            return option.num_permissible_values();
                           else
                               return size_t_max;
@@ -218,7 +236,9 @@ GncOption::permissible_value_index(const char* value) const
 {
     return std::visit([&value] (const auto& option) -> size_t {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionMultichoiceValue>)
+                                        GncOptionMultichoiceValue>  ||
+                                        std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionDateValue>)
                                            return option.permissible_value_index(value);
                           else
                               return size_t_max;;
@@ -230,7 +250,9 @@ GncOption::permissible_value(std::size_t index) const
 {
     return std::visit([index] (const auto& option) -> const char* {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionMultichoiceValue>)
+                                        GncOptionMultichoiceValue>  ||
+                                        std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionDateValue>)
                                            return option.permissible_value(index);
                           else
                               return "";
@@ -242,7 +264,9 @@ GncOption::permissible_value_name(std::size_t index) const
 {
     return std::visit([index] (const auto& option) -> const char* {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionMultichoiceValue>)
+                                        GncOptionMultichoiceValue>  ||
+                                        std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionDateValue>)
                                            return option.permissible_value_name(index);
                           else
                               return "";
@@ -254,7 +278,9 @@ GncOption::permissible_value_description(std::size_t index) const
 {
     return std::visit([index] (const auto& option) -> const char* {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                        GncOptionMultichoiceValue>)
+                                        GncOptionMultichoiceValue>  ||
+                                        std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionDateValue>)
                                            return option.permissible_value_description(index);
                           else
                               return "";
@@ -394,6 +420,7 @@ template bool GncOption::get_value<bool>() const;
 template int GncOption::get_value<int>() const;
 template int64_t GncOption::get_value<int64_t>() const;
 template double GncOption::get_value<double>() const;
+template size_t GncOption::get_value<size_t>() const;
 template const char* GncOption::get_value<const char*>() const;
 template std::string GncOption::get_value<std::string>() const;
 template const QofInstance* GncOption::get_value<const QofInstance*>() const;
@@ -417,6 +444,7 @@ template void GncOption::set_value(const char*);
 template void GncOption::set_value(std::string);
 template void GncOption::set_value(const QofInstance*);
 template void GncOption::set_value(RelativeDatePeriod);
+template void GncOption::set_value(size_t);
 
 template bool GncOption::validate(bool) const;
 template bool GncOption::validate(int) const;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 4f226ae5f..6e8cbd384 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -34,6 +34,7 @@ extern "C"
 #include <variant>
 #include <memory>
 #include "gnc-option-uitype.hpp"
+#include "gnc-option-date.hpp"
 
 class GncOptionUIItem;
 using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
@@ -117,31 +118,6 @@ operator>>(std::istream& iss, GncOption& opt)
 {
     return opt.in_stream(iss);
 }
-/**
- * Reporting periods relative to the current date.
- *
- * The original design allowed custom RelativeDatePeriods, but that facility is
- * unused so we'll go with compiled-in enums.
- */
-enum class RelativeDatePeriod : int
-{
-    ABSOLUTE = -1,
-    TODAY,
-    START_THIS_MONTH,
-    END_THIS_MONTH,
-    START_PREV_MONTH,
-    END_PREV_MONTH,
-    START_CURRENT_QUARTER,
-    END_CURRENT_QUARTER,
-    START_PREV_QUARTER,
-    END_PREV_QUARTER,
-    START_CAL_YEAR,
-    END_CAL_YEAR,
-    START_PREV_YEAR,
-    END_PREV_YEAR,
-    START_ACCOUNTING_PERIOD,
-    END_ACCOUNTING_PERIOD
-};
 
 
 #endif //GNC_OPTION_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 0b78e7be3..b7f7fcb5e 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -966,14 +966,92 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section,
-                                  const char* name, const char* key,
-                                  const char* doc_string,
-                                  RelativeDatePeriod period)
+gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+                         const char* name, const char* key,
+                         const char* doc_string, time64 time,
+                         RelativeDateUI ui)
 {
-    auto ui_type = static_cast<int>(period) % 2 ?
-        GncOptionUIType::DATE_BOTH_END : GncOptionUIType::DATE_BOTH_BEGIN;
+    auto ui_type = ui == RelativeDateUI::BOTH ? GncOptionUIType::DATE_BOTH :
+        ui == RelativeDateUI::RELATIVE ? GncOptionUIType::DATE_RELATIVE :
+        GncOptionUIType::DATE_ABSOLUTE;
+    GncOption option{GncOptionDateValue(section, name, key, doc_string,
+                                        ui_type, time)};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+                         const char* name, const char* key,
+                         const char* doc_string, RelativeDatePeriod period,
+                         RelativeDateUI ui)
+{
+    auto ui_type = ui == RelativeDateUI::BOTH ? GncOptionUIType::DATE_BOTH :
+        ui == RelativeDateUI::RELATIVE ? GncOptionUIType::DATE_RELATIVE :
+        GncOptionUIType::DATE_ABSOLUTE;
     GncOption option{GncOptionDateValue(section, name, key, doc_string,
                                         ui_type, period)};
     db->register_option(section, std::move(option));
 }
+
+void
+gnc_register_date_option(const GncOptionDBPtr& db,
+                                  const char* section, const char* name,
+                                  const char* key, const char* doc_string,
+                                  RelativeDatePeriodVec& period_set,
+                                  bool both)
+{
+    auto ui_type = both ? GncOptionUIType::DATE_BOTH :
+        GncOptionUIType::DATE_RELATIVE;
+    GncOption option{GncOptionDateValue(section, name, key, doc_string,
+                                        ui_type, period_set)};
+    db->register_option(section, std::move(option));
+}
+
+
+static const RelativeDatePeriodVec begin_dates
+{
+    RelativeDatePeriod::TODAY,
+    RelativeDatePeriod::START_THIS_MONTH,
+    RelativeDatePeriod::START_PREV_MONTH,
+    RelativeDatePeriod::START_CURRENT_QUARTER,
+    RelativeDatePeriod::START_PREV_QUARTER,
+    RelativeDatePeriod::START_CAL_YEAR,
+    RelativeDatePeriod::START_PREV_YEAR,
+    RelativeDatePeriod::START_ACCOUNTING_PERIOD
+};
+
+void
+gnc_register_start_date_option(const GncOptionDBPtr& db, const char* section,
+                               const char* name, const char* key,
+                               const char* doc_string, bool both)
+{
+    auto ui_type = both ? GncOptionUIType::DATE_BOTH :
+        GncOptionUIType::DATE_RELATIVE;
+    GncOption option{GncOptionDateValue(section, name, key, doc_string,
+                                        ui_type, begin_dates)};
+    db->register_option(section, std::move(option));
+}
+
+static const RelativeDatePeriodVec end_dates
+{
+    RelativeDatePeriod::TODAY,
+    RelativeDatePeriod::END_THIS_MONTH,
+    RelativeDatePeriod::END_PREV_MONTH,
+    RelativeDatePeriod::END_CURRENT_QUARTER,
+    RelativeDatePeriod::END_PREV_QUARTER,
+    RelativeDatePeriod::END_CAL_YEAR,
+    RelativeDatePeriod::END_PREV_YEAR,
+    RelativeDatePeriod::END_ACCOUNTING_PERIOD
+};
+
+void
+gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section,
+                             const char* name, const char* key,
+                             const char* doc_string, bool both)
+{
+    auto ui_type = both ? GncOptionUIType::DATE_BOTH :
+        GncOptionUIType::DATE_RELATIVE;
+    GncOption option{GncOptionDateValue(section, name, key, doc_string,
+                                        ui_type, end_dates)};
+    db->register_option(section, std::move(option));
+}
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index eebf933f8..26d5a3c89 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -202,9 +202,39 @@ void gnc_register_dateformat_option(const GncOptionDBPtr& db,
                                     const char* key, const char* doc_string,
                                     std::string value);
 
-void gnc_register_date_interval_option(const GncOptionDBPtr& db,
-                                       const char* section, const char* name,
-                                       const char* key, const char* doc_string,
-                                       RelativeDatePeriod period);
+enum RelativeDateUI : uint8_t
+{
+    ABSOLUTE,
+    RELATIVE,
+    BOTH
+};
+
+void gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string,
+                              RelativeDatePeriod period =
+                              RelativeDatePeriod::TODAY,
+                              RelativeDateUI ui = RelativeDateUI::BOTH);
+
+void gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string, time64 time,
+                              RelativeDateUI ui = RelativeDateUI::BOTH);
+
+void gnc_register_date_option(const GncOptionDBPtr& db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string,
+                              RelativeDatePeriodVec& period_set,
+                              bool both = true);
+
+void gnc_register_start_date_option(const GncOptionDBPtr& db,
+                                    const char* section,
+                                    const char* name, const char* key,
+                                    const char* doc_string, bool both = true);
+
+void gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section,
+                                  const char* name, const char* key,
+                                  const char* doc_string, bool both = true);
+
 
 #endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 1f2c70c85..823f51c31 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -42,6 +42,8 @@ namespace std {
 %{
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
+#include "gnc-option-date.hpp"
+
 SCM scm_init_sw_gnc_optiondb_module(void);
 %}
 
@@ -179,12 +181,23 @@ gnc_option_test_book_destroy(QofBook* book)
 %ignore GncOptionDateValue(GncOptionDateValue&&);
 %ignore GncOptionDateValue::operator=(const GncOptionDateValue&);
 %ignore GncOptionDateValue::operator=(GncOptionDateValue&&);
+%ignore GncOptionDateValue::set_value(size_t); // Used only by dialog-options
 %ignore operator<<(std::ostream&, const GncOption&);
 %ignore operator>>(std::istream&, GncOption&);
 %ignore GncOption::_get_option();
 
 %rename(absolute) RelativeDatePeriod::ABSOLUTE;
 %rename(today) RelativeDatePeriod::TODAY;
+%rename(one_week_ago) RelativeDatePeriod::ONE_WEEK_AGO;
+%rename(one_week_ahead) RelativeDatePeriod::ONE_WEEK_AHEAD;
+%rename(one_month_ago) RelativeDatePeriod::ONE_MONTH_AGO;
+%rename(one_month_ahead) RelativeDatePeriod::ONE_MONTH_AHEAD;
+%rename(three_months_ago) RelativeDatePeriod::THREE_MONTHS_AGO;
+%rename(three_months_ahead) RelativeDatePeriod::THREE_MONTHS_AHEAD;
+%rename(six_months_ago) RelativeDatePeriod::SIX_MONTHS_AGO;
+%rename(six_months_ahead) RelativeDatePeriod::SIX_MONTHS_AHEAD;
+%rename(one_year_ago) RelativeDatePeriod::ONE_YEAR_AGO;
+%rename(one_year_ahead) RelativeDatePeriod::ONE_YEAR_AHEAD;
 %rename(start_this_month) RelativeDatePeriod::START_THIS_MONTH;
 %rename(end_this_month) RelativeDatePeriod::END_THIS_MONTH;
 %rename(start_prev_month) RelativeDatePeriod::START_PREV_MONTH;
@@ -200,10 +213,26 @@ gnc_option_test_book_destroy(QofBook* book)
 %rename(start_accounting_period) RelativeDatePeriod::START_ACCOUNTING_PERIOD;
 %rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD;
 
+%rename(gnc_register_date_option_set)
+    gnc_register_date_option(const GncOptionDBPtr&, const char*, const char*,
+                             const char*, const char*, RelativeDatePeriodVec&,
+                             bool);
+
 %typemap(typecheck, precedence=SWIG_TYPECHECK_INT64) time64 {
     $1 = scm_is_signed_integer($input, INT64_MAX, INT64_MIN);
 }
 
+%typemap(in) RelativeDatePeriodVec& (RelativeDatePeriodVec period_set)
+{
+    auto len = scm_to_size_t(scm_length($input));
+    for (std::size_t i = 0; i < len; ++i)
+    {
+        SCM s_reldateperiod = scm_list_ref($input, scm_from_size_t(i));
+        period_set.push_back((RelativeDatePeriod)scm_to_int(s_reldateperiod));
+    }
+    $1 = &period_set;
+}
+
 %typemap(in) GncMultiChoiceOptionChoices&& (GncMultiChoiceOptionChoices choices)
 {
     auto len = scm_to_size_t(scm_length($input));
@@ -313,6 +342,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     }
 %}
 
+%include "gnc-option-date.hpp"
 %include "gnc-option.hpp"
 %include "gnc-option-impl.hpp"
 %include "gnc-optiondb.hpp"
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 992294af1..0e939ae50 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -509,35 +509,52 @@ TEST_F(GncOptionCommodityTest, test_commodity_from_scheme)
 class GncUIType
 {
 public:
-    void set_value(const std::string& value) { m_value = value; }
-    const std::string& get_value() { return m_value; }
+    void set_value(const std::string& value) const noexcept { m_value = value; }
+    const std::string& get_value() const noexcept { return m_value; }
+    void clear() noexcept { m_value.clear(); }
 private:
-    std::string m_value;
+    mutable std::string m_value;
 };
 
-using OptionUIItem = GncUIItem<GncUIType>;
+
+class OptionUIItem : public GncOptionUIItem
+{
+    GncUIType m_widget;
+    bool m_dirty = false;
+public:
+    ~OptionUIItem() = default;
+    GncOptionUIType get_ui_type() const noexcept override { return GncOptionUIType::STRING; }
+    void set_dirty(bool status) noexcept override { m_dirty = status; }
+    bool get_dirty() const noexcept override { return m_dirty; }
+    void clear_ui_item() override { m_widget.clear(); }
+    void set_ui_item_from_option(GncOption& option) noexcept override
+    {
+        m_widget.set_value(option.get_value<std::string>());
+    }
+    void set_option_from_ui_item(GncOption& option) noexcept override
+    {
+        option.set_value(m_widget.get_value());
+    }
+    void set_widget_value(const std::string& value) const noexcept
+    {
+        m_widget.set_value(value);
+    }
+    const std::string& get_widget_value() const noexcept
+    {
+        return m_widget.get_value();
+    }
+};
 
 class GncOptionUITest : public ::testing::Test
 {
 protected:
     GncOptionUITest() :
-        m_widget{},
         m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"},
                  GncOptionUIType::STRING}
     {
-        auto to_ui = [](OptionUIItem& ui, GncOption& opt) {
-                         ui.m_widget->set_value(opt.get_value<std::string>());
-                     };
-        auto from_ui = [](OptionUIItem& ui, GncOption& opt) {
-                           opt.set_value<std::string>(ui.m_widget->get_value());
-                       };
-        auto ui_item{std::make_unique<GncOptionUIItem>(
-                OptionUIItem{&m_widget},
-                GncOptionUIType::STRING,
-                to_ui, from_ui)};
+        auto ui_item{std::make_unique<OptionUIItem>()};
         m_option.set_ui_item(std::move(ui_item));
     }
-    GncUIType m_widget;
     GncOption m_option;
 };
 
@@ -548,24 +565,23 @@ TEST_F(GncOptionUI, test_option_ui_type)
     EXPECT_EQ(GncOptionUIType::STRING, m_option.get_ui_type());
 }
 
-TEST_F(GncOptionUI, test_set_option_ui_item)
-{
-    EXPECT_EQ(&m_widget, m_option.get_ui_item()->get_ui_item().m_widget);
-}
-
 TEST_F(GncOptionUI, test_ui_value_from_option)
 {
     const char* value{"waldo"};
 
     m_option.set_value(value);
     m_option.set_ui_item_from_option();
-    EXPECT_STREQ(value, m_widget.get_value().c_str());
+    auto ui_item{dynamic_cast<const OptionUIItem*>(m_option.get_ui_item())};
+    ASSERT_TRUE(ui_item != nullptr);
+    EXPECT_STREQ(value, ui_item->get_widget_value().c_str());
 }
 
 TEST_F(GncOptionUI, test_option_value_from_ui)
 {
     const char* value{"pepper"};
-    m_widget.set_value(value);
+    auto ui_item{dynamic_cast<const OptionUIItem*>(m_option.get_ui_item())};
+    ASSERT_TRUE(ui_item != nullptr);
+    ui_item->set_widget_value(value);
     m_option.set_option_from_ui_item();
     EXPECT_STREQ(value, m_option.get_value<std::string>().c_str());
 }
@@ -958,10 +974,10 @@ TEST_F(GncMultichoiceOption, test_permissible_value_stuff)
 {
     EXPECT_NO_THROW({
             EXPECT_EQ(3U, m_option.permissible_value_index("corge"));
-            EXPECT_STREQ("waldo", m_option.permissible_value(1).c_str());
-            EXPECT_STREQ("sausage", m_option.permissible_value_name(2).c_str());
+            EXPECT_STREQ("waldo", m_option.permissible_value(1));
+            EXPECT_STREQ("sausage", m_option.permissible_value_name(2));
             EXPECT_STREQ("thud",
-                         m_option.permissible_value_description(0).c_str());
+                         m_option.permissible_value_description(0));
         });
     EXPECT_THROW({ auto result = m_option.permissible_value(7); },
                  std::out_of_range);
@@ -1008,18 +1024,6 @@ TEST_F(GncMultichoiceOption, test_multichoice_scheme_in)
     EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
 }
 
-class GncOptionDateOptionTest : public ::testing::Test
-{
-protected:
-    GncOptionDateOptionTest() :
-        m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option",
-                                    GncOptionUIType::DATE_BOTH_END}} {}
-
-    GncOption m_option;
-};
-
-using GncDateOption = GncOptionDateOptionTest;
-
 static time64
 time64_from_gdate(const GDate* g_date, DayPart when)
 {
@@ -1029,131 +1033,197 @@ time64_from_gdate(const GDate* g_date, DayPart when)
     return static_cast<time64>(time);
 }
 
-TEST_F(GncDateOption, test_set_and_get_absolute)
-{
-    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
-    m_option.set_value(time1);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
-
-TEST_F(GncDateOption, test_set_and_get_month_start)
-{
-    GDate month_start;
-    g_date_set_time_t(&month_start, time(nullptr));
-    gnc_gdate_set_month_start(&month_start);
-    time64 time1{time64_from_gdate(&month_start, DayPart::start)};
-    m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
-
-TEST_F(GncDateOption, test_set_and_get_month_end)
-{
-    GDate month_end;
-    g_date_set_time_t(&month_end, time(nullptr));
-    gnc_gdate_set_month_end(&month_end);
-    time64 time1{time64_from_gdate(&month_end, DayPart::end)};
-    m_option.set_value(RelativeDatePeriod::END_THIS_MONTH);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
-
-TEST_F(GncDateOption, test_set_and_get_prev_month_start)
+TEST(GncOptionDate, test_gnc_relative_date_is_single)
 {
-    GDate prev_month_start;
-    g_date_set_time_t(&prev_month_start, time(nullptr));
-    gnc_gdate_set_prev_month_start(&prev_month_start);
-    time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)};
-    m_option.set_value(RelativeDatePeriod::START_PREV_MONTH);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
+    EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::ABSOLUTE));
+    EXPECT_TRUE(gnc_relative_date_is_single(RelativeDatePeriod::TODAY));
+    EXPECT_TRUE(gnc_relative_date_is_single(RelativeDatePeriod::ONE_YEAR_AHEAD));
+    EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::START_THIS_MONTH));
+    EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::END_CURRENT_QUARTER));
+    EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::START_ACCOUNTING_PERIOD));
+    EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::END_ACCOUNTING_PERIOD));
 }
 
-TEST_F(GncDateOption, test_set_and_get_prev_month_end)
+TEST(GncOptionDate, test_gnc_relative_date_is_starting)
 {
-    GDate prev_month_end;
-    g_date_set_time_t(&prev_month_end, time(nullptr));
-    gnc_gdate_set_prev_month_end(&prev_month_end);
-    time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)};
-    m_option.set_value(RelativeDatePeriod::END_PREV_MONTH);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
-
-TEST_F(GncDateOption, test_set_and_get_quarter_start)
-{
-    GDate quarter_start;
-    g_date_set_time_t(&quarter_start, time(nullptr));
-    gnc_gdate_set_quarter_start(&quarter_start);
-    time64 time1{time64_from_gdate(&quarter_start, DayPart::start)};
-    m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
+    EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::ABSOLUTE));
+    EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::TODAY));
+    EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::ONE_YEAR_AHEAD));
+    EXPECT_TRUE(gnc_relative_date_is_starting(RelativeDatePeriod::START_THIS_MONTH));
+    EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::END_CURRENT_QUARTER));
+    EXPECT_TRUE(gnc_relative_date_is_starting(RelativeDatePeriod::START_ACCOUNTING_PERIOD));
+    EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::END_ACCOUNTING_PERIOD));
+}
+
+TEST(GncOptionDate, test_gnc_relative_date_is_ending)
+{
+    EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::ABSOLUTE));
+    EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::TODAY));
+    EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::ONE_YEAR_AHEAD));
+    EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::START_CURRENT_QUARTER));
+    EXPECT_TRUE(gnc_relative_date_is_ending(RelativeDatePeriod::END_CURRENT_QUARTER));
+    EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::START_ACCOUNTING_PERIOD));
+    EXPECT_TRUE(gnc_relative_date_is_ending(RelativeDatePeriod::END_ACCOUNTING_PERIOD));
+}
+
+TEST(GncOptionDate, test_gnc_relative_date_storage_string)
+{
+    EXPECT_EQ(nullptr, gnc_relative_date_storage_string(RelativeDatePeriod::ABSOLUTE));
+    EXPECT_STREQ("one-month-ago", gnc_relative_date_storage_string(RelativeDatePeriod::ONE_MONTH_AGO));
+}
+
+TEST(GncOptionDate, test_gnc_relative_date_display_string)
+{
+    EXPECT_EQ(nullptr, gnc_relative_date_display_string(RelativeDatePeriod::ABSOLUTE));
+    EXPECT_STREQ("Start of next month", gnc_relative_date_display_string(RelativeDatePeriod::START_NEXT_MONTH));
+}
+
+TEST(GncOptionDate, test_gnc_relative_date_description)
+{
+    EXPECT_EQ(nullptr, gnc_relative_date_description(RelativeDatePeriod::ABSOLUTE));
+    EXPECT_STREQ("First day of the next month.", gnc_relative_date_description(RelativeDatePeriod::START_NEXT_MONTH));
+}
+
+TEST(GncOptionDate, test_gnc_relative_date_from_storage_string)
+{
+    EXPECT_EQ(RelativeDatePeriod::ABSOLUTE, gnc_relative_date_from_storage_string("foo"));
+    EXPECT_EQ(RelativeDatePeriod::ONE_MONTH_AHEAD,  gnc_relative_date_from_storage_string("one-month-ahead"));
+    EXPECT_EQ(RelativeDatePeriod::START_CURRENT_QUARTER,  gnc_relative_date_from_storage_string("start-current-quarter"));
+    EXPECT_EQ(RelativeDatePeriod::END_ACCOUNTING_PERIOD,  gnc_relative_date_from_storage_string("end-prev-fin-year"));
+}
+
+TEST(GncOptionDate, test_gnc_relative_date_to_time64)
+{
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_month_start(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::start)};
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::START_THIS_MONTH));
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_month_end(&date);
+    time1 = time64_from_gdate(&date, DayPart::end);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::END_THIS_MONTH));
+
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_month_start(&date);
+    time1 = time64_from_gdate(&date, DayPart::start);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::START_PREV_MONTH));
+
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_month_end(&date);
+    time1 = time64_from_gdate(&date, DayPart::end);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::END_PREV_MONTH));
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_quarter_start(&date);
+    time1 = time64_from_gdate(&date, DayPart::start);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::START_CURRENT_QUARTER));
+
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_quarter_end(&date);
+    time1 = time64_from_gdate(&date, DayPart::end);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::END_CURRENT_QUARTER));
+
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_quarter_start(&date);
+    time1 = time64_from_gdate(&date, DayPart::start);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::START_PREV_QUARTER));
+
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_quarter_end(&date);
+    time1 = time64_from_gdate(&date, DayPart::end);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::END_PREV_QUARTER));
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_year_start(&date);
+    time1 = time64_from_gdate(&date, DayPart::start);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::START_CAL_YEAR));
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_year_end(&date);
+    time1 = time64_from_gdate(&date, DayPart::end);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::END_CAL_YEAR));
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_year_start(&date);
+    time1 = time64_from_gdate(&date, DayPart::start);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::START_PREV_YEAR));
+
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_year_end(&date);
+    time1 = time64_from_gdate(&date, DayPart::end);
+    EXPECT_EQ(time1,
+              gnc_relative_date_to_time64(RelativeDatePeriod::END_PREV_YEAR));
 }
 
-TEST_F(GncDateOption, test_set_and_get_quarter_end)
+class GncOptionDateOptionTest : public ::testing::Test
 {
-    GDate quarter_end;
-    g_date_set_time_t(&quarter_end, time(nullptr));
-    gnc_gdate_set_quarter_end(&quarter_end);
-    time64 time1{time64_from_gdate(&quarter_end, DayPart::end)};
-    m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
+protected:
+    GncOptionDateOptionTest() :
+        m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option",
+                                    GncOptionUIType::DATE_BOTH}} {}
+    GncOption m_option;
+};
 
-TEST_F(GncDateOption, test_set_and_get_prev_quarter_start)
+class GncOptionDateOptionListTest : public ::testing::Test
 {
-    GDate prev_quarter_start;
-    g_date_set_time_t(&prev_quarter_start, time(nullptr));
-    gnc_gdate_set_prev_quarter_start(&prev_quarter_start);
-    time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)};
-    m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
+protected:
+    GncOptionDateOptionListTest() :
+        m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option",
+                                    GncOptionUIType::DATE_BOTH, c_begin_dates}} {}
+    GncOption m_option;
 
-TEST_F(GncDateOption, test_set_and_get_prev_quarter_end)
-{
-    GDate prev_quarter_end;
-    g_date_set_time_t(&prev_quarter_end, time(nullptr));
-    gnc_gdate_set_prev_quarter_end(&prev_quarter_end);
-    time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)};
-    m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
+    static const RelativeDatePeriodVec c_begin_dates;
+};
 
-TEST_F(GncDateOption, test_set_and_get_year_start)
+const RelativeDatePeriodVec GncOptionDateOptionListTest::c_begin_dates
 {
-    GDate year_start;
-    g_date_set_time_t(&year_start, time(nullptr));
-    gnc_gdate_set_year_start(&year_start);
-    time64 time1{time64_from_gdate(&year_start, DayPart::start)};
-    m_option.set_value(RelativeDatePeriod::START_CAL_YEAR);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
+    RelativeDatePeriod::TODAY,
+    RelativeDatePeriod::START_THIS_MONTH,
+    RelativeDatePeriod::START_PREV_MONTH,
+    RelativeDatePeriod::START_CURRENT_QUARTER,
+    RelativeDatePeriod::START_PREV_QUARTER,
+    RelativeDatePeriod::START_CAL_YEAR,
+    RelativeDatePeriod::START_PREV_YEAR,
+    RelativeDatePeriod::START_ACCOUNTING_PERIOD
+};
 
-TEST_F(GncDateOption, test_set_and_get_year_end)
-{
-    GDate year_end;
-    g_date_set_time_t(&year_end, time(nullptr));
-    gnc_gdate_set_year_end(&year_end);
-    time64 time1{time64_from_gdate(&year_end, DayPart::end)};
-    m_option.set_value(RelativeDatePeriod::END_CAL_YEAR);
-    EXPECT_EQ(time1, m_option.get_value<time64>());
-}
+using GncDateOption = GncOptionDateOptionTest;
+using GncDateOptionList = GncOptionDateOptionListTest;
 
-TEST_F(GncDateOption, test_set_and_get_prev_year_start)
+TEST_F(GncDateOption, test_set_and_get_absolute)
 {
-    GDate prev_year_start;
-    g_date_set_time_t(&prev_year_start, time(nullptr));
-    gnc_gdate_set_prev_year_start(&prev_year_start);
-    time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
-    m_option.set_value(RelativeDatePeriod::START_PREV_YEAR);
+    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
+    m_option.set_value(time1);
     EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
-TEST_F(GncDateOption, test_set_and_get_prev_year_end)
+TEST_F(GncDateOptionList, test_set_and_get_relative)
 {
-    GDate prev_year_end;
-    g_date_set_time_t(&prev_year_end, time(nullptr));
-    gnc_gdate_set_prev_year_end(&prev_year_end);
-    time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)};
-    m_option.set_value(RelativeDatePeriod::END_PREV_YEAR);
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_month_start(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::start)};
+    EXPECT_EQ(RelativeDatePeriod::START_ACCOUNTING_PERIOD, m_option.get_value<RelativeDatePeriod>());
+    m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
     EXPECT_EQ(time1, m_option.get_value<time64>());
+    EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, m_option.get_value<RelativeDatePeriod>());
+    auto index(std::find(c_begin_dates.begin(), c_begin_dates.end(),
+                         RelativeDatePeriod::START_THIS_MONTH) - c_begin_dates.begin());
+    EXPECT_EQ(index, m_option.get_value<size_t>());
+    // And check that nothing happens when we try to set m_option to an end date
+    m_option.set_value(RelativeDatePeriod::END_THIS_MONTH);
+    EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, m_option.get_value<RelativeDatePeriod>());
+    m_option.set_value(static_cast<size_t>(5));
+    EXPECT_EQ(RelativeDatePeriod::START_CAL_YEAR, m_option.get_value<RelativeDatePeriod>());
+    EXPECT_EQ(5, m_option.get_value<size_t>());
 }
 
 TEST_F(GncDateOption, test_stream_out)
@@ -1271,10 +1341,10 @@ TEST_F(GncDateOption, test_stream_in_month_start)
 
 TEST_F(GncDateOption, test_stream_in_month_end)
 {
-    GDate month_end;
-    g_date_set_time_t(&month_end, time(nullptr));
-    gnc_gdate_set_month_end(&month_end);
-    time64 time1{time64_from_gdate(&month_end, DayPart::end)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_month_end(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::end)};
     std::istringstream iss{"relative . end-this-month"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1293,10 +1363,10 @@ TEST_F(GncDateOption, test_stream_in_prev_month_start)
 
 TEST_F(GncDateOption, test_stream_in_prev_month_end)
 {
-    GDate prev_month_end;
-    g_date_set_time_t(&prev_month_end, time(nullptr));
-    gnc_gdate_set_prev_month_end(&prev_month_end);
-    time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_month_end(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::end)};
     std::istringstream iss{"relative . end-prev-month"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1304,10 +1374,10 @@ TEST_F(GncDateOption, test_stream_in_prev_month_end)
 
 TEST_F(GncDateOption, test_stream_in_quarter_start)
 {
-    GDate quarter_start;
-    g_date_set_time_t(&quarter_start, time(nullptr));
-    gnc_gdate_set_quarter_start(&quarter_start);
-    time64 time1{time64_from_gdate(&quarter_start, DayPart::start)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_quarter_start(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::start)};
     std::istringstream iss{"relative . start-current-quarter"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1315,10 +1385,10 @@ TEST_F(GncDateOption, test_stream_in_quarter_start)
 
 TEST_F(GncDateOption, test_stream_in_quarter_end)
 {
-    GDate quarter_end;
-    g_date_set_time_t(&quarter_end, time(nullptr));
-    gnc_gdate_set_quarter_end(&quarter_end);
-    time64 time1{time64_from_gdate(&quarter_end, DayPart::end)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_quarter_end(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::end)};
     std::istringstream iss{"relative . end-current-quarter"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1326,10 +1396,10 @@ TEST_F(GncDateOption, test_stream_in_quarter_end)
 
 TEST_F(GncDateOption, test_stream_in_prev_quarter_start)
 {
-    GDate prev_quarter_start;
-    g_date_set_time_t(&prev_quarter_start, time(nullptr));
-    gnc_gdate_set_prev_quarter_start(&prev_quarter_start);
-    time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_quarter_start(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::start)};
     std::istringstream iss{"relative . start-prev-quarter"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1337,10 +1407,10 @@ TEST_F(GncDateOption, test_stream_in_prev_quarter_start)
 
 TEST_F(GncDateOption, test_stream_in_prev_quarter_end)
 {
-    GDate prev_quarter_end;
-    g_date_set_time_t(&prev_quarter_end, time(nullptr));
-    gnc_gdate_set_prev_quarter_end(&prev_quarter_end);
-    time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_quarter_end(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::end)};
     std::istringstream iss{"relative . end-prev-quarter"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1348,10 +1418,10 @@ TEST_F(GncDateOption, test_stream_in_prev_quarter_end)
 
 TEST_F(GncDateOption, test_stream_in_year_start)
 {
-    GDate year_start;
-    g_date_set_time_t(&year_start, time(nullptr));
-    gnc_gdate_set_year_start(&year_start);
-    time64 time1{time64_from_gdate(&year_start, DayPart::start)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_year_start(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::start)};
     std::istringstream iss{"relative . start-cal-year"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1359,10 +1429,10 @@ TEST_F(GncDateOption, test_stream_in_year_start)
 
 TEST_F(GncDateOption, test_stream_in_year_end)
 {
-    GDate year_end;
-    g_date_set_time_t(&year_end, time(nullptr));
-    gnc_gdate_set_year_end(&year_end);
-    time64 time1{time64_from_gdate(&year_end, DayPart::end)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_year_end(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::end)};
     std::istringstream iss{"relative . end-cal-year"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1370,10 +1440,10 @@ TEST_F(GncDateOption, test_stream_in_year_end)
 
 TEST_F(GncDateOption, test_stream_in_prev_year_start)
 {
-    GDate prev_year_start;
-    g_date_set_time_t(&prev_year_start, time(nullptr));
-    gnc_gdate_set_prev_year_start(&prev_year_start);
-    time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_year_start(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::start)};
     std::istringstream iss{"relative . start-prev-year"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1381,10 +1451,10 @@ TEST_F(GncDateOption, test_stream_in_prev_year_start)
 
 TEST_F(GncDateOption, test_stream_in_prev_year_end)
 {
-    GDate prev_year_end;
-    g_date_set_time_t(&prev_year_end, time(nullptr));
-    gnc_gdate_set_prev_year_end(&prev_year_end);
-    time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)};
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_prev_year_end(&date);
+    time64 time1{time64_from_gdate(&date, DayPart::end)};
     std::istringstream iss{"relative . end-prev-year"};
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
@@ -1441,10 +1511,10 @@ TEST_F(GncDateOption, test_date_option_from_scheme)
     m_option.from_scheme(iss);
     EXPECT_EQ(time1, m_option.get_value<time64>());
 
-    GDate month_end;
-    g_date_set_time_t(&month_end, time(nullptr));
-    gnc_gdate_set_month_end(&month_end);
-    time1 = time64_from_gdate(&month_end, DayPart::end);
+    GDate date;
+    g_date_set_time_t(&date, time(nullptr));
+    gnc_gdate_set_month_end(&date);
+    time1 = time64_from_gdate(&date, DayPart::end);
     iss.clear();
     iss.str("'(relative . end-this-month)");
     m_option.from_scheme(iss);
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index e0199c8c9..993577342 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -190,21 +190,82 @@ time64_from_gdate(const GDate* g_date, DayPart when)
 {
     GncDate date{g_date_get_year(g_date), g_date_get_month(g_date),
             g_date_get_day(g_date)};
-    GncDateTime time{date, when};
-    return static_cast<time64>(time);
+    GncDateTime time1{date, when};
+    return static_cast<time64>(time1);
 }
 
 
-TEST_F(GncOptionDBTest, test_register_date_interval_option)
+TEST_F(GncOptionDBTest, test_register_relative_date_option)
 {
-    gnc_register_date_interval_option(m_db, "foo", "bar", "baz", "Phony Option",
-                                      RelativeDatePeriod::START_ACCOUNTING_PERIOD);
+    gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option",
+                             RelativeDatePeriod::START_ACCOUNTING_PERIOD);
     GDate prev_year_start;
     g_date_set_time_t(&prev_year_start, time(nullptr));
     gnc_gdate_set_prev_year_start(&prev_year_start);
-    time64 time{time64_from_gdate(&prev_year_start, DayPart::start)};
-    ASSERT_TRUE(m_db->set_option("foo", "bar", time));
-    EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value<time64>());
+    time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
+    ASSERT_TRUE(m_db->set_option("foo", "bar", time1));
+    EXPECT_EQ(time1, m_db->find_option("foo", "bar")->get().get_value<time64>());
+}
+
+TEST_F(GncOptionDBTest, test_register_absolute_date_option)
+{
+    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
+
+    gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option", time1);
+    GDate prev_year_start;
+    g_date_set_time_t(&prev_year_start, time(nullptr));
+    gnc_gdate_set_prev_year_start(&prev_year_start);
+    ASSERT_TRUE(m_db->set_option("foo", "bar", time1));
+    EXPECT_EQ(time1,
+              m_db->find_option("foo", "bar")->get().get_value<time64>());
+}
+
+/* Copied from gnc-optiondb.cpp for the purpose of finding the index of the
+ * option in the following test.
+ */
+static const RelativeDatePeriodVec begin_dates
+{
+    RelativeDatePeriod::TODAY,
+    RelativeDatePeriod::START_THIS_MONTH,
+    RelativeDatePeriod::START_PREV_MONTH,
+    RelativeDatePeriod::START_CURRENT_QUARTER,
+    RelativeDatePeriod::START_PREV_QUARTER,
+    RelativeDatePeriod::START_CAL_YEAR,
+    RelativeDatePeriod::START_PREV_YEAR,
+    RelativeDatePeriod::START_ACCOUNTING_PERIOD
+};
+
+TEST_F(GncOptionDBTest, test_register_start_date_option)
+{
+    gnc_register_start_date_option(m_db, "foo", "bar", "baz", "Phony Option");
+    GDate prev_year_start;
+    g_date_set_time_t(&prev_year_start, time(nullptr));
+    gnc_gdate_set_prev_year_start(&prev_year_start);
+    time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
+    EXPECT_EQ(RelativeDatePeriod::START_ACCOUNTING_PERIOD,
+              m_db->find_option("foo", "bar")->get().get_value<RelativeDatePeriod>());
+    ASSERT_TRUE(m_db->set_option("foo", "bar", time1));
+    EXPECT_EQ(time1,
+              m_db->find_option("foo", "bar")->get().get_value<time64>());
+    EXPECT_EQ(RelativeDatePeriod::ABSOLUTE,
+              m_db->find_option("foo", "bar")->get().get_value<RelativeDatePeriod>());
+    m_db->set_option("foo", "bar", RelativeDatePeriod::START_THIS_MONTH);
+    EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH,
+              m_db->find_option("foo", "bar")->get().get_value<RelativeDatePeriod>());
+
+    auto index(std::find(begin_dates.begin(), begin_dates.end(),
+                         RelativeDatePeriod::START_THIS_MONTH) - begin_dates.begin());
+    /* If this fails check that the begin_dates vector above matches the one in
+     * gnc-optiondb.cpp.
+     */
+    EXPECT_EQ(index,
+              m_db->find_option("foo", "bar")->get().get_value<size_t>());
+    m_db->set_option("foo", "bar", RelativeDatePeriod::END_THIS_MONTH);
+    EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH,
+              m_db->find_option("foo", "bar")->get().get_value<RelativeDatePeriod>());
+    m_db->set_option("foo", "bar", static_cast<size_t>(5));
+    EXPECT_EQ(5, m_db->find_option("foo", "bar")->get().get_value<size_t>());
+
 }
 
 class GncOptionDBIOTest : public ::testing::Test
@@ -246,9 +307,9 @@ protected:
                                    std::string{""});
         gnc_register_text_option(m_db, "qux", "garply", "fred",
                                    "Phony Option", std::string{"waldo"});
-        gnc_register_date_interval_option(m_db, "pork", "garply", "first",
-                                          "Phony Date Option",
-                                          RelativeDatePeriod::START_CURRENT_QUARTER);
+        gnc_register_date_option(m_db, "pork", "garply", "first",
+                                 "Phony Date Option",
+                                 RelativeDatePeriod::START_CURRENT_QUARTER);
         gnc_register_account_list_option(m_db, "quux", "xyzzy", "second",
                                          "Phony AccountList Option",
                                          {aapl, hpe});
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 966c5669f..89f683c81 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -39,6 +39,7 @@
   (test-gnc-make-multichoice-option)
   (test-gnc-make-list-option)
   (test-gnc-make-date-option)
+  (test-gnc-make-date-set-option)
   (test-gnc-make-number-range-option)
   (test-end "test-gnc-optiondb-scheme"))
 
@@ -181,15 +182,32 @@
 (define (test-gnc-make-date-option)
   (test-begin "test-gnc-test-date-option")
   (let* ((option-db (gnc-option-db-new))
-         (date-opt (gnc-register-date-interval-option option-db "foo" "bar"
-                                                      "baz" "Phony Option"
-                                                      (RelativeDatePeriod-today)))
+         (date-opt (gnc-register-date-option option-db "foo" "bar"
+                                             "baz" "Phony Option"
+                                             (RelativeDatePeriod-today)))
          (a-time (gnc-dmy2time64 11 07 2019)))
     (test-equal (current-time) (gnc-option-value option-db "foo" "bar"))
     (gnc-set-option option-db "foo" "bar" a-time)
     (test-equal a-time (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-test-date-option"))
 
+(define (test-gnc-make-date-set-option)
+  (test-begin "test-gnc-test-date-set-option")
+  (let* ((option-db (gnc-option-db-new))
+         (date-opt (gnc-register-date-option-set
+                    option-db "foo" "bar" "baz" "Phony Option"
+                    (list (RelativeDatePeriod-today)
+                          (RelativeDatePeriod-start-this-month)
+                          (RelativeDatePeriod-start-prev-month)
+                          (RelativeDatePeriod-start-current-quarter)
+                          (RelativeDatePeriod-start-prev-quarter)
+                          (RelativeDatePeriod-start-cal-year)
+                          (RelativeDatePeriod-start-prev-year)
+                          (RelativeDatePeriod-start-accounting-period)) #t)))
+    (test-equal (gnc-accounting-period-fiscal-start)
+                (gnc-option-value option-db "foo" "bar")))
+  (test-end "test-gnc-test-date-set-option"))
+
 (define (test-gnc-make-number-range-option)
   (test-begin "test-gnc-number-range-option")
   (let* ((option-db (gnc-option-db-new))

commit 3b78b6e894623109c0f075b1deaceeaa931da495
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Mar 5 17:55:15 2020 -0800

    Change the GncOptionMultichoiceValue permissible value return types to const char*.
    
    From std::string. The consumer is a GtkWidget so we might as well do the conversion
    inside the class and this will simplify adding these functions to GncOptionDateValue
    in the next commit.

diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index d561ba665..e927f5dae 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -25,6 +25,7 @@
 #include "gnc-option-impl.hpp"
 #include <gnc-datetime.hpp>
 #include <guid.hpp>
+
 extern "C"
 {
 #include "gnc-accounting-period.h"
@@ -66,122 +67,77 @@ GncOptionAccountValue::account_type_list() const noexcept
     return g_list_reverse(retval);
 }
 
-static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-static void
-normalize_month(struct tm& now)
-{
-    if (now.tm_mon < 0)
-    {
-        now.tm_mon += 12;
-        --now.tm_year;
-    }
-    else if (now.tm_mon > 11)
-    {
-        now.tm_mon -= 12;
-        ++now.tm_year;
-    }
+bool
+GncOptionDateValue::validate(RelativeDatePeriod value) {
+    if (m_period_set.empty())
+        return true; // No restrictions
+    if (std::find(m_period_set.begin(), m_period_set.end(),
+                  value) != m_period_set.end())
+        return true;
+    return false;
 }
 
-static void
-set_day_and_time(struct tm& now, bool starting)
-{
-    if (starting)
-    {
-        now.tm_hour = now.tm_min = now.tm_sec = 0;
-        now.tm_mday = 1;
-    }
-    else
-    {
-        now.tm_min = now.tm_sec = 59;
-        now.tm_hour = 23;
-        now.tm_mday = days_in_month[now.tm_mon];
-        // Check for Februrary in a leap year
-        if (int year = now.tm_year + 1900; now.tm_mon == 1 &&
-            year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
-            ++now.tm_mday;
-    }
-};
-
 time64
-GncOptionDateValue::get_value() const
+GncOptionDateValue::get_value() const noexcept
 {
     if (m_period == RelativeDatePeriod::ABSOLUTE)
         return m_date;
-    if (m_period == RelativeDatePeriod::TODAY)
-        return static_cast<time64>(GncDateTime());
-    if (m_period == RelativeDatePeriod::START_ACCOUNTING_PERIOD)
-        return gnc_accounting_period_fiscal_start();
-    if (m_period == RelativeDatePeriod::END_ACCOUNTING_PERIOD)
-        return gnc_accounting_period_fiscal_end();
-
-    GncDateTime now_t;
-    if (m_period == RelativeDatePeriod::TODAY)
-        return static_cast<time64>(now_t);
-    struct tm now{static_cast<tm>(now_t)};
-    struct tm period{static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()))};
-    bool starting =  m_period == RelativeDatePeriod::START_PREV_MONTH ||
-        m_period == RelativeDatePeriod::START_THIS_MONTH ||
-        m_period == RelativeDatePeriod::START_CAL_YEAR ||
-        m_period == RelativeDatePeriod::START_PREV_YEAR ||
-        m_period == RelativeDatePeriod::START_CURRENT_QUARTER ||
-        m_period == RelativeDatePeriod::START_PREV_QUARTER;
+    return gnc_relative_date_to_time64(m_period);
+}
 
-    bool prev = m_period == RelativeDatePeriod::START_PREV_YEAR ||
-        m_period == RelativeDatePeriod::END_PREV_YEAR ||
-        m_period == RelativeDatePeriod::START_PREV_QUARTER ||
-        m_period == RelativeDatePeriod::END_PREV_QUARTER;
+time64
+GncOptionDateValue::get_default_value() const noexcept
+{
+    if (m_default_period == RelativeDatePeriod::ABSOLUTE)
+        return m_default_date;
+    return gnc_relative_date_to_time64(m_default_period);
+}
 
-    if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday)
-    {
-        //No set accounting period, use the calendar year
-        period.tm_mon = 0;
-        period.tm_mday = 0;
-    }
+/* Use asserts for pre- and post-conditions to deliberately crash if they're not
+ * met as the program design should prevent that from happening.
+ */
+int8_t
+GncOptionDateValue::get_period_index() const noexcept
+{
+    assert (m_period != RelativeDatePeriod::ABSOLUTE);
+    assert(!m_period_set.empty());
+    auto item{std::find(m_period_set.begin(), m_period_set.end(), m_period)};
+    assert(item != m_period_set.end());
+    return item - m_period_set.begin();
+}
 
-    if (m_period == RelativeDatePeriod::START_CAL_YEAR ||
-        m_period == RelativeDatePeriod::END_CAL_YEAR ||
-        m_period == RelativeDatePeriod::START_PREV_YEAR ||
-        m_period == RelativeDatePeriod::END_PREV_YEAR)
-    {
+int8_t
+GncOptionDateValue::get_default_period_index() const noexcept
+{
+    assert(m_period != RelativeDatePeriod::ABSOLUTE);
+    assert(!m_period_set.empty());
+    auto item{std::find(m_period_set.begin(), m_period_set.end(),
+                        m_default_period)};
+    assert (item != m_period_set.end());
+    return item - m_period_set.begin();
+}
 
-        if (prev)
-            --now.tm_year;
-        now.tm_mon = starting ? 0 : 11;
-    }
-    else if (m_period == RelativeDatePeriod::START_PREV_QUARTER ||
-             m_period == RelativeDatePeriod::END_PREV_QUARTER ||
-             m_period == RelativeDatePeriod::START_CURRENT_QUARTER ||
-             m_period == RelativeDatePeriod::END_CURRENT_QUARTER)
-    {
-        auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon :
-                       period.tm_mon - now.tm_mon) % 3;
-        now.tm_mon = now.tm_mon - offset;
-        if (prev)
-            now.tm_mon -= 3;
-        if (!starting)
-            now.tm_mon += 2;
-    }
-    else if (m_period == RelativeDatePeriod::START_PREV_MONTH ||
-             m_period == RelativeDatePeriod::END_PREV_MONTH)
-        --now.tm_mon;
-    normalize_month(now);
-    set_day_and_time(now, starting);
-    return static_cast<time64>(GncDateTime(now));
+void
+GncOptionDateValue::set_value(size_t index) noexcept
+{
+    assert(!m_period_set.empty());
+    assert(index < m_period_set.size());
+    m_date = INT64_MAX;
+    m_period = m_period_set[index];
 }
-static const char* date_type_str[] {"absolute", "relative"};
-static const std::array<const char*, 15> date_period_str
+
+size_t
+GncOptionDateValue::permissible_value_index(const char* key) const noexcept
 {
-    "today",
-    "start-this-month", "end-this-month",
-    "start-prev-month", "end-prev-month",
-    "start-current-quarter", "end-current-quarter",
-    "start-prev-quarter", "end-prev-quarter",
-    "start-cal-year", "end-cal-year",
-    "start-prev-year", "end-prev-year",
-    "start-prev-fin-year", "end-prev-fin-year"
-};
+    auto index = std::find_if(m_period_set.begin(), m_period_set.end(),
+                              [key](auto period) -> bool {
+                                  return strcmp(gnc_relative_date_display_string(period),
+                                                key) == 0;
+                              });
+    return index != m_period_set.end() ? index - m_period_set.begin() : 0;
+}
 
+static const char* date_type_str[] {"absolute", "relative"};
 
 std::ostream&
 GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
@@ -190,7 +146,7 @@ GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
         oss << date_type_str[0] << " . " << m_date;
     else
         oss << date_type_str[1] << " . " <<
-            date_period_str[static_cast<int>(m_period)];
+            gnc_relative_date_storage_string(m_period);
     return oss;
 }
 
@@ -216,9 +172,8 @@ GncOptionDateValue::in_stream(std::istream& iss)
         iss >> period_str;
         if (period_str.back() == ')')
             period_str.pop_back();
-        auto period = std::find(date_period_str.begin(), date_period_str.end(),
-                                period_str);
-        if (period == date_period_str.end())
+        auto period = gnc_relative_date_from_storage_string(period_str.c_str());
+        if (period == RelativeDatePeriod::ABSOLUTE)
         {
             std::string err{"Unknown period string in date option: '"};
             err += period_str;
@@ -226,8 +181,7 @@ GncOptionDateValue::in_stream(std::istream& iss)
             throw std::invalid_argument(err);
         }
 
-        int64_t index = period - date_period_str.begin();
-        set_value(static_cast<RelativeDatePeriod>(index));
+        set_value(period);
     }
     else
     {
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 5d221011a..d8160860d 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -481,21 +481,21 @@ public:
     {
         return m_choices.size();
     }
-    std::size_t permissible_value_index(const std::string& key) const noexcept
+    std::size_t permissible_value_index(const char* key) const noexcept
     {
             return find_key(key);
     }
-    const std::string& permissible_value(std::size_t index) const
+    const char* permissible_value(std::size_t index) const
     {
-        return std::get<0>(m_choices.at(index));
+        return std::get<0>(m_choices.at(index)).c_str();
     }
-    const std::string& permissible_value_name(std::size_t index) const
+    const char* permissible_value_name(std::size_t index) const
     {
-        return std::get<1>(m_choices.at(index));
+        return std::get<1>(m_choices.at(index)).c_str();
     }
-    const std::string& permissible_value_description(std::size_t index) const
+    const char* permissible_value_description(std::size_t index) const
     {
-        return std::get<2>(m_choices.at(index));
+        return std::get<2>(m_choices.at(index)).c_str();
     }
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 14fb90e39..321258066 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -214,7 +214,7 @@ GncOption::num_permissible_values() const
 }
 
 std::size_t
-GncOption::permissible_value_index(const std::string& value) const
+GncOption::permissible_value_index(const char* value) const
 {
     return std::visit([&value] (const auto& option) -> size_t {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
@@ -225,39 +225,39 @@ GncOption::permissible_value_index(const std::string& value) const
                       }, *m_option);
 }
 
-const std::string&
+const char*
 GncOption::permissible_value(std::size_t index) const
 {
-    return std::visit([index] (const auto& option) -> const std::string& {
+    return std::visit([index] (const auto& option) -> const char* {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                         GncOptionMultichoiceValue>)
                                            return option.permissible_value(index);
                           else
-                              return c_empty_string;
+                              return "";
                       }, *m_option);
 }
 
-const std::string&
+const char*
 GncOption::permissible_value_name(std::size_t index) const
 {
-    return std::visit([index] (const auto& option) -> const std::string& {
+    return std::visit([index] (const auto& option) -> const char* {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                         GncOptionMultichoiceValue>)
                                            return option.permissible_value_name(index);
                           else
-                              return c_empty_string;
+                              return "";
                       }, *m_option);
 }
 
-const std::string&
+const char*
 GncOption::permissible_value_description(std::size_t index) const
 {
-    return std::visit([index] (const auto& option) -> const std::string& {
+    return std::visit([index] (const auto& option) -> const char* {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                         GncOptionMultichoiceValue>)
                                            return option.permissible_value_description(index);
                           else
-                              return c_empty_string;
+                              return "";
                       }, *m_option);
 }
 
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index be8e93101..4f226ae5f 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -87,10 +87,10 @@ public:
     bool is_changed() const noexcept;
     template <typename ValueType> bool validate(ValueType value) const;
     std::size_t num_permissible_values() const;
-    std::size_t permissible_value_index(const std::string& value) const;
-    const std::string& permissible_value(std::size_t index) const;
-    const std::string& permissible_value_name(std::size_t index) const;
-    const std::string& permissible_value_description(std::size_t index) const;
+    std::size_t permissible_value_index(const char* value) const;
+    const char* permissible_value(std::size_t index) const;
+    const char* permissible_value_name(std::size_t index) const;
+    const char* permissible_value_description(std::size_t index) const;
     GList* account_type_list() const noexcept;
     std::ostream& out_stream(std::ostream& oss) const;
     std::istream& in_stream(std::istream& iss);

commit d5f6a2539b13c6c9a5984d96575ac52de89cb1a7
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 20 12:09:21 2020 -0800

    Replace GncOption::_get_option() with a friend function swig_get_option.
    
    Implemented in gnc-optiondb.i it more clearly indicates the intended
    use and restricts the access. Unfortunately further limiting the
    friend declaration with #ifdef SWIG prevented the declaration from
    working, raising the error "m_option is a private member of GncOption".

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 548690dbb..be8e93101 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -96,7 +96,10 @@ public:
     std::istream& in_stream(std::istream& iss);
     std::ostream& to_scheme(std::ostream& oss) const;
     std::istream& from_scheme(std::istream& iss);
-    GncOptionVariant* const _get_option() { return m_option.get(); }
+
+
+    friend GncOptionVariant& swig_get_option(GncOption*);
+
 private:
     inline static const std::string c_empty_string{""};
     GncOptionVariantPtr m_option;
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 818dd876d..1f2c70c85 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -302,34 +302,43 @@ gnc_option_test_book_destroy(QofBook* book)
 
 wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
+%ignore swig_get_option(GncOption&);
+%inline %{
+#include "gnc-option.hpp"
+#include "gnc-option-ui.hpp"
+
+    GncOptionVariant& swig_get_option(GncOption* option)
+    {
+        return *option->m_option;
+    }
+%}
+
 %include "gnc-option.hpp"
 %include "gnc-option-impl.hpp"
 %include "gnc-optiondb.hpp"
 %include "gnc-optiondb-impl.hpp"
-%inline %{
-#include "gnc-option-ui.hpp"
-%}
 
 %extend GncOption {
+
     SCM get_scm_value()
     {
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_value()};
                 return scm_from_value(static_cast<decltype(value)>(value));
-            }, *($self->_get_option()));
+            }, swig_get_option($self));
     }
     SCM get_scm_default_value()
     {
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_default_value()};
                 return scm_from_value(static_cast<decltype(value)>(value));
-            }, *($self->_get_option()));
+            }, swig_get_option($self));
     }
     void set_value_from_scm(SCM new_value)
     {
         std::visit([new_value](auto& option) {
                 option.set_value(scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value));
-            }, *($self->_get_option()));
+            }, swig_get_option($self));
     }
 };
 %extend GncOptionDB {

commit 93a3716c00f77420f495de1ffd3099e80ddfc1d5
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Feb 18 14:29:29 2020 -0800

    Enable retrieval of a GncOptionDateValue's relative period.
    
    So that it can be displayed by dialog-option.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index a833fdab3..14fb90e39 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -48,6 +48,11 @@ GncOption::get_value() const
     return std::visit([](const auto option)->ValueType {
                           if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
                                            return option.get_value();
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                         GncOptionDateValue> &&
+                                         std::is_same_v<std::decay_t<ValueType>,
+                                        RelativeDatePeriod>)
+                              return option.get_period();
                           return ValueType {};
                       }, *m_option);
 }

commit dc876d4041729af8fdbae28c2e97bfe808f7b48d
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Feb 18 14:27:41 2020 -0800

    Provide an accessor to GncOptionAccountValue::m_allowed
    
    To enable its use by gnc_account_sel_filter

diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt
index 0f05e930a..e384adb2c 100644
--- a/libgnucash/app-utils/CMakeLists.txt
+++ b/libgnucash/app-utils/CMakeLists.txt
@@ -92,6 +92,7 @@ set(app_utils_ALL_LIBRARIES
     gnc-engine
     gnc-locale-tax
     gnucash-guile
+    ${GLIB_LDFLAGS}
     ${GIO_LDFLAGS}
     ${LIBXML2_LDFLAGS}
     ${LIBXSLT_LDFLAGS}
diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
index 7ab458413..d561ba665 100644
--- a/libgnucash/app-utils/gnc-option-impl.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -48,6 +48,24 @@ GncOptionAccountValue::validate(const GncOptionAccountList& values) const
     return true;
 }
 
+/**
+ * Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
+ * gnc_account_sel_set_acct_filters copies the list so the intermediary caller
+ * is responsible for freeing the list.
+ *
+ * @return an allocated GList* or nullptr if the list is empty.
+ */
+GList*
+GncOptionAccountValue::account_type_list() const noexcept
+{
+    if (m_allowed.empty())
+        return nullptr;
+    GList* retval;
+    for (auto type : m_allowed)
+        retval = g_list_prepend(retval, GINT_TO_POINTER(type));
+    return g_list_reverse(retval);
+}
+
 static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
 static void
diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 700efaa34..5d221011a 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -584,6 +584,7 @@ public:
             //throw!
             m_value = values;
     }
+    GList* account_type_list() const noexcept;
     bool is_changed() const noexcept { return m_value != m_default_value; }
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 4bedeb4a2..a833fdab3 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -256,6 +256,18 @@ GncOption::permissible_value_description(std::size_t index) const
                       }, *m_option);
 }
 
+GList*
+GncOption::account_type_list() const noexcept
+{
+    return std::visit([] (const auto& option) -> GList* {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionAccountValue>)
+                              return option.account_type_list();
+                          else
+                              return nullptr;
+                      }, *m_option);
+}
+
 std::ostream&
 GncOption::out_stream(std::ostream& oss) const
 {
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 8a990e3ba..548690dbb 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -24,6 +24,11 @@
 #ifndef GNC_OPTION_HPP_
 #define GNC_OPTION_HPP_
 
+extern "C"
+{
+#include <glib.h>
+}
+
 #include <string>
 #include <iostream>
 #include <variant>
@@ -86,6 +91,7 @@ public:
     const std::string& permissible_value(std::size_t index) const;
     const std::string& permissible_value_name(std::size_t index) const;
     const std::string& permissible_value_description(std::size_t index) const;
+    GList* account_type_list() const noexcept;
     std::ostream& out_stream(std::ostream& oss) const;
     std::istream& in_stream(std::istream& iss);
     std::ostream& to_scheme(std::ostream& oss) const;

commit 102f36c3be237967842cb6df073fa5a7ffa98b61
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Feb 18 14:23:59 2020 -0800

    Provide more than one Date UI type to match options available in dialog-option.c
    
    Also differentiates begin-period and end-period controls.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index adc47a40f..700efaa34 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -680,31 +680,34 @@ gnc_option_from_scheme(std::istream& iss, OptType& opt)
 gnc-date-option-show-time? -- option_data[1]
 gnc-date-option-get-subtype -- option_data[0]
 gnc-date-option-value-type m_value
-gnc-date-option-absolute-time m_type == DateTyupe::Absolute
-gnc-date-option-relative-time m_type != DateTyupe::Absolute
+gnc-date-option-absolute-time m_type == RelativeDatePeriod::ABSOLUTE
+gnc-date-option-relative-time m_type != RelativeDatePeriod::ABSOLUTE
  */
 
 class GncOptionDateValue : public OptionClassifier
 {
 public:
     GncOptionDateValue(const char* section, const char* name,
-                              const char* key, const char* doc_string) :
+                       const char* key, const char* doc_string,
+                       GncOptionUIType ui_type) :
         OptionClassifier{section, name, key, doc_string},
-        m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
+        m_ui_type{ui_type}, m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
         m_date{INT64_MAX},
         m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
         m_default_date{INT64_MAX} {}
     GncOptionDateValue(const char* section, const char* name,
                        const char* key, const char* doc_string,
-                       time64 time) :
+                       GncOptionUIType ui_type, time64 time) :
         OptionClassifier{section, name, key, doc_string},
-        m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time},
-        m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {}
+        m_ui_type{ui_type}, m_period{RelativeDatePeriod::ABSOLUTE},
+        m_date{time}, m_default_period{RelativeDatePeriod::ABSOLUTE},
+        m_default_date{time} {}
     GncOptionDateValue(const char* section, const char* name,
                        const char* key, const char* doc_string,
+                       GncOptionUIType ui_type,
                        const RelativeDatePeriod period) :
         OptionClassifier{section, name, key, doc_string},
-        m_period{period}, m_date{INT64_MAX},
+        m_ui_type{ui_type}, m_period{period}, m_date{INT64_MAX},
         m_default_period{period}, m_default_date{INT64_MAX} {}
         GncOptionDateValue(const GncOptionDateValue&) = default;
         GncOptionDateValue(GncOptionDateValue&&) = default;
@@ -712,6 +715,8 @@ public:
         GncOptionDateValue& operator=(GncOptionDateValue&&) = default;
     time64 get_value() const;
     time64 get_default_value() const { return static_cast<time64>(GncDateTime()); }
+    RelativeDatePeriod get_period() const noexcept { return m_period; }
+    RelativeDatePeriod get_default_period() const noexcept { return m_default_period; }
     std::ostream& out_stream(std::ostream& oss) const noexcept;
     std::istream& in_stream(std::istream& iss);
     void set_value(RelativeDatePeriod value) {
@@ -727,7 +732,7 @@ public:
     GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
     void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
 private:
-    GncOptionUIType m_ui_type = GncOptionUIType::DATE;
+    GncOptionUIType m_ui_type;
     RelativeDatePeriod m_period;
     time64 m_date;
     RelativeDatePeriod m_default_period;
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
index 48c75df1e..9814d709f 100644
--- a/libgnucash/app-utils/gnc-option-uitype.hpp
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -32,7 +32,11 @@ enum GncOptionUIType
     CURRENCY,
     COMMODITY,
     MULTICHOICE,
-    DATE,
+    DATE_ABSOLUTE,
+    DATE_RELATIVE_BEGIN,
+    DATE_BOTH_BEGIN,
+    DATE_RELATIVE_END,
+    DATE_BOTH_END,
     ACCOUNT_LIST,
     ACCOUNT_SEL,
     LIST,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 82735a610..0b78e7be3 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -971,6 +971,9 @@ gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section,
                                   const char* doc_string,
                                   RelativeDatePeriod period)
 {
-    GncOption option{GncOptionDateValue(section, name, key, doc_string, period)};
+    auto ui_type = static_cast<int>(period) % 2 ?
+        GncOptionUIType::DATE_BOTH_END : GncOptionUIType::DATE_BOTH_BEGIN;
+    GncOption option{GncOptionDateValue(section, name, key, doc_string,
+                                        ui_type, period)};
     db->register_option(section, std::move(option));
 }
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 03c4f4dba..992294af1 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -1012,7 +1012,8 @@ class GncOptionDateOptionTest : public ::testing::Test
 {
 protected:
     GncOptionDateOptionTest() :
-        m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option"}} {}
+        m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option",
+                                    GncOptionUIType::DATE_BOTH_END}} {}
 
     GncOption m_option;
 };

commit 6c7f976a65dcb952087b1d8f3af62cf38bc053d9
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Feb 16 15:30:26 2020 -0800

    Make second arg to gnc_glist_to_scm_list const char*
    
    So that C++ won't complain when passed a static string.

diff --git a/bindings/guile/glib-guile.c b/bindings/guile/glib-guile.c
index 6a36178fd..cce037272 100644
--- a/bindings/guile/glib-guile.c
+++ b/bindings/guile/glib-guile.c
@@ -77,7 +77,7 @@ glist_to_scm_list_helper(GList *glist, swig_type_info *wct)
 }
 
 SCM
-gnc_glist_to_scm_list(GList *glist, gchar *wct)
+gnc_glist_to_scm_list(GList *glist, const gchar *wct)
 {
     swig_type_info *stype = SWIG_TypeQuery(wct);
     g_return_val_if_fail(stype, SCM_UNDEFINED);
diff --git a/bindings/guile/glib-guile.h b/bindings/guile/glib-guile.h
index 2b1e6d047..fd642a428 100644
--- a/bindings/guile/glib-guile.h
+++ b/bindings/guile/glib-guile.h
@@ -28,7 +28,7 @@
 #include <glib.h>
 #include <libguile.h>
 
-SCM gnc_glist_to_scm_list(GList *glist, gchar *wct);
+SCM gnc_glist_to_scm_list(GList *glist, const gchar *wct);
 GList* gnc_scm_list_to_glist(SCM wcp_list);
 
 SCM     gnc_glist_string_to_scm(GList * list);

commit 99c2c5e4395a92dff6da76b6cdac2b218f8e2e4b
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Feb 11 13:35:27 2020 -0800

    GncOptionUIItem from the GncOptionVariant classes to GncOption.
    
    Separating the UI from the data model. Note that the GncOptionVariant
    classes still have a GncOptionUIType member to ensure that a
    GncOptionUIItem of the right type is attached.

diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
index 71e9d4924..adc47a40f 100644
--- a/libgnucash/app-utils/gnc-option-impl.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -1,5 +1,5 @@
 /********************************************************************\
- * gnc-option-impl.hpp -- Application options system                     *
+ * gnc-option-impl.hpp -- Application options system                *
  * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
@@ -107,62 +107,12 @@ struct OptionClassifier
     std::string m_doc_string;
 };
 
-class GncOptionUIItem;
-
-/**
- * Holds a pointer to the UI item which will control the option and an enum
- * representing the type of the option for dispatch purposes; all of that
- * happens in gnucash/gnome-utils/dialog-options and
- * gnucash/gnome/business-option-gnome.
- *
- * This class takes no ownership responsibility, so calling code is responsible
- * for ensuring that the UI_Item is alive. For convenience the public
- * clear_ui_item function can be used as a weak_ptr's destruction callback to
- * ensure that the ptr will be nulled if the ui_item is destroyed elsewhere.
- */
-class OptionUIItem
-{
-public:
-    GncOptionUIType get_ui_type() const { return m_ui_type; }
-    GncOptionUIItem* const get_ui_item() const {return m_ui_item; }
-    void clear_ui_item() { m_ui_item = nullptr; }
-    void set_ui_item(GncOptionUIItem* ui_item)
-    {
-        if (m_ui_type == GncOptionUIType::INTERNAL)
-        {
-            std::string error{"INTERNAL option, setting the UI item forbidden."};
-            throw std::logic_error(std::move(error));
-        }
-        m_ui_item = ui_item;
-    }
-    void make_internal()
-    {
-        if (m_ui_item != nullptr)
-        {
-            std::string error("Option has a UI Element, can't be INTERNAL.");
-            throw std::logic_error(std::move(error));
-        }
-        m_ui_type = GncOptionUIType::INTERNAL;
-    }
-protected:
-    OptionUIItem(GncOptionUIType ui_type) :
-        m_ui_item{nullptr}, m_ui_type{ui_type} {}
-    OptionUIItem(const OptionUIItem&) = default;
-    OptionUIItem(OptionUIItem&&) = default;
-    ~OptionUIItem() = default;
-    OptionUIItem& operator=(const OptionUIItem&) = default;
-    OptionUIItem& operator=(OptionUIItem&&) = default;
-private:
-    GncOptionUIItem* m_ui_item;
-    GncOptionUIType m_ui_type;
-};
-
 #ifndef SWIG
 auto constexpr size_t_max = std::numeric_limits<std::size_t>::max();
 #endif
 
 template <typename ValueType>
-class GncOptionValue : public OptionClassifier, public OptionUIItem
+class GncOptionValue : public OptionClassifier
 {
 public:
     GncOptionValue<ValueType>(const char* section, const char* name,
@@ -170,8 +120,7 @@ public:
                               ValueType value,
                               GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{value}, m_default_value{value} {}
+        m_ui_type(ui_type), m_value{value}, m_default_value{value} {}
     GncOptionValue<ValueType>(const GncOptionValue<ValueType>&) = default;
     GncOptionValue<ValueType>(GncOptionValue<ValueType>&&) = default;
     GncOptionValue<ValueType>& operator=(const GncOptionValue<ValueType>&) = default;
@@ -180,13 +129,16 @@ public:
     ValueType get_default_value() const { return m_default_value; }
     void set_value(ValueType new_value) { m_value = new_value; }
     bool is_changed() const noexcept { return m_value != m_default_value; }
+    GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
+    void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
 private:
+    GncOptionUIType m_ui_type;
     ValueType m_value;
     ValueType m_default_value;
 };
 
 template <typename ValueType>
-class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem
+class GncOptionValidatedValue : public OptionClassifier
 {
 public:
     GncOptionValidatedValue<ValueType>() = delete;
@@ -197,8 +149,8 @@ public:
                                        GncOptionUIType ui_type = GncOptionUIType::INTERNAL
         ) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem{ui_type},
-        m_value{value}, m_default_value{value}, m_validator{validator}
+        m_ui_type{ui_type}, m_value{value}, m_default_value{value},
+        m_validator{validator}
         {
             if (!this->validate(value))
             throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
@@ -209,7 +161,7 @@ public:
                                        std::function<bool(ValueType)>validator,
                                        ValueType val_data) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem{GncOptionUIType::INTERNAL}, m_value{value},
+        m_ui_type{GncOptionUIType::INTERNAL}, m_value{value},
         m_default_value{value}, m_validator{validator}, m_validation_data{val_data}
     {
             if (!this->validate(value))
@@ -232,7 +184,10 @@ public:
     bool is_changed() const noexcept { return m_value != m_default_value; }
     std::ostream& to_scheme(std::ostream&) const;
     std::istream& from_scheme(std::istream&);
+    GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
+    void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
 private:
+    GncOptionUIType m_ui_type;
     ValueType m_value;
     ValueType m_default_value;
     std::function<bool(ValueType)> m_validator;                         //11
@@ -419,8 +374,7 @@ gnc_option_from_scheme (std::istream& iss, OptType& opt)
  */
 
 template <typename ValueType>
-class GncOptionRangeValue :
-    public OptionClassifier, public OptionUIItem
+class GncOptionRangeValue : public OptionClassifier
 {
 public:
     GncOptionRangeValue<ValueType>(const char* section, const char* name,
@@ -428,7 +382,6 @@ public:
                                    ValueType value, ValueType min,
                                    ValueType max, ValueType step) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(GncOptionUIType::NUMBER_RANGE),
         m_value{value >= min && value <= max ? value : min},
         m_default_value{value >= min && value <= max ? value : min},
         m_min{min}, m_max{max}, m_step{step} {}
@@ -448,7 +401,10 @@ public:
             throw std::invalid_argument("Validation failed, value not set.");
     }
     bool is_changed() const noexcept { return m_value != m_default_value; }
+    GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
+    void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
 private:
+    GncOptionUIType m_ui_type = GncOptionUIType::NUMBER_RANGE;
     ValueType m_value;
     ValueType m_default_value;
     ValueType m_min;
@@ -471,8 +427,7 @@ using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
  *
  */
 
-class GncOptionMultichoiceValue :
-    public OptionClassifier, public OptionUIItem
+class GncOptionMultichoiceValue : public OptionClassifier
 {
 public:
     GncOptionMultichoiceValue(const char* section, const char* name,
@@ -481,7 +436,7 @@ public:
                               GncMultiChoiceOptionChoices&& choices,
                               GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
+        m_ui_type{ui_type},
         m_value{}, m_default_value{}, m_choices{std::move(choices)} {
             if (value)
             {
@@ -543,6 +498,8 @@ public:
         return std::get<2>(m_choices.at(index));
     }
     bool is_changed() const noexcept { return m_value != m_default_value; }
+    GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
+    void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
 private:
     std::size_t find_key (const std::string& key) const noexcept
     {
@@ -555,6 +512,7 @@ private:
             return size_t_max;
 
     }
+    GncOptionUIType m_ui_type;
     std::size_t m_value;
     std::size_t m_default_value;
     GncMultiChoiceOptionChoices m_choices;
@@ -581,31 +539,28 @@ using GncOptionAccountTypeList = std::vector<GNCAccountType>;
 
  */
 
-class GncOptionAccountValue :
-    public OptionClassifier, public OptionUIItem
+class GncOptionAccountValue : public OptionClassifier
 {
 public:
     GncOptionAccountValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {}
+        m_ui_type{ui_type}, m_value{}, m_default_value{}, m_allowed{} {}
 
     GncOptionAccountValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           const GncOptionAccountList& value) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{value},
+        m_ui_type{ui_type}, m_value{value},
         m_default_value{std::move(value)}, m_allowed{} {}
     GncOptionAccountValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           GncOptionUIType ui_type,
                           GncOptionAccountTypeList&& allowed) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{},
+        m_ui_type{ui_type}, m_value{},
         m_default_value{}, m_allowed{std::move(allowed)} {}
     GncOptionAccountValue(const char* section, const char* name,
                           const char* key, const char* doc_string,
@@ -613,8 +568,7 @@ public:
                           const GncOptionAccountList& value,
                           GncOptionAccountTypeList&& allowed) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{},
+        m_ui_type{ui_type}, m_value{},
         m_default_value{}, m_allowed{std::move(allowed)} {
             if (!validate(value))
                 throw std::invalid_argument("Supplied Value not in allowed set.");
@@ -631,7 +585,10 @@ public:
             m_value = values;
     }
     bool is_changed() const noexcept { return m_value != m_default_value; }
+    GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
+    void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
 private:
+    GncOptionUIType m_ui_type;
     GncOptionAccountList m_value;
     GncOptionAccountList m_default_value;
     GncOptionAccountTypeList m_allowed;
@@ -727,13 +684,12 @@ gnc-date-option-absolute-time m_type == DateTyupe::Absolute
 gnc-date-option-relative-time m_type != DateTyupe::Absolute
  */
 
-class GncOptionDateValue : public OptionClassifier, public OptionUIItem
+class GncOptionDateValue : public OptionClassifier
 {
 public:
     GncOptionDateValue(const char* section, const char* name,
                               const char* key, const char* doc_string) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(GncOptionUIType::DATE),
         m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
         m_date{INT64_MAX},
         m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
@@ -742,14 +698,12 @@ public:
                        const char* key, const char* doc_string,
                        time64 time) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(GncOptionUIType::DATE),
         m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time},
         m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {}
     GncOptionDateValue(const char* section, const char* name,
                        const char* key, const char* doc_string,
                        const RelativeDatePeriod period) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(GncOptionUIType::DATE),
         m_period{period}, m_date{INT64_MAX},
         m_default_period{period}, m_default_date{INT64_MAX} {}
         GncOptionDateValue(const GncOptionDateValue&) = default;
@@ -770,7 +724,10 @@ public:
     }
     bool is_changed() const noexcept { return m_period != m_default_period &&
             m_date != m_default_date; }
+    GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
+    void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
 private:
+    GncOptionUIType m_ui_type = GncOptionUIType::DATE;
     RelativeDatePeriod m_period;
     time64 m_date;
     RelativeDatePeriod m_default_period;
diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp
new file mode 100644
index 000000000..ef2a8a4a5
--- /dev/null
+++ b/libgnucash/app-utils/gnc-option-ui.hpp
@@ -0,0 +1,93 @@
+/********************************************************************\
+ * gnc-option-ui.hpp -- UI association for GncOption                *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#ifndef GNC_OPTION_UI_HPP_
+#define GNC_OPTION_UI_HPP_
+
+#include "gnc-option-uitype.hpp"
+template <typename UIType>
+class GncUIItem
+{
+public:
+    GncUIItem(UIType* widget) : m_widget{widget} {}
+    UIType* m_widget;
+};
+
+class GncUIType;
+using OptionUIItem = GncUIItem<GncUIType>;
+using OptionSyncFunc = std::function<void(OptionUIItem&, GncOption&)>;
+/**
+ * Holds a pointer to the UI item which will control the option and an enum
+ * representing the type of the option for dispatch purposes; all of that
+ * happens in gnucash/gnome-utils/dialog-options and
+ * gnucash/gnome/business-option-gnome.
+ *
+ * This class takes no ownership responsibility, so calling code is responsible
+ * for ensuring that the UI_Item is alive. For convenience the public
+ * clear_ui_item function can be used as a weak_ptr's destruction callback to
+ * ensure that the ptr will be nulled if the ui_item is destroyed elsewhere.
+ */
+class GncOptionUIItem
+{
+public:
+    GncOptionUIItem(OptionUIItem&& ui_item, GncOptionUIType type,
+                    OptionSyncFunc to_ui, OptionSyncFunc from_ui) :
+        m_ui_item{std::move(ui_item)}, m_ui_type{type},
+        m_set_ui_item_from_option{to_ui}, m_set_option_from_ui_item{from_ui} {}
+    GncOptionUIItem(GncOptionUIType ui_type) :
+        m_ui_item{nullptr}, m_ui_type{ui_type} {}
+    GncOptionUIItem(const GncOptionUIItem&) = default;
+    GncOptionUIItem(GncOptionUIItem&&) = default;
+    ~GncOptionUIItem() = default;
+    GncOptionUIItem& operator=(const GncOptionUIItem&) = default;
+    GncOptionUIItem& operator=(GncOptionUIItem&&) = default;
+    GncOptionUIType get_ui_type() const { return m_ui_type; }
+    const OptionUIItem& get_ui_item() const {return m_ui_item; }
+    void clear_ui_item() { m_ui_item = nullptr; }
+    void set_ui_item(OptionUIItem&& ui_item)
+    {
+        if (m_ui_type == GncOptionUIType::INTERNAL)
+        {
+            std::string error{"INTERNAL option, setting the UI item forbidden."};
+            throw std::logic_error(std::move(error));
+        }
+        m_ui_item = std::move(ui_item);
+    }
+    void set_ui_item_from_option(GncOption& option)
+    {
+        m_set_ui_item_from_option(m_ui_item, option);
+    }
+    void set_option_from_ui_item(GncOption& option)
+    {
+        m_set_option_from_ui_item(m_ui_item, option);
+    }
+private:
+    OptionUIItem m_ui_item;
+    GncOptionUIType m_ui_type;
+    OptionSyncFunc m_set_ui_item_from_option;
+    OptionSyncFunc m_set_option_from_ui_item;
+};
+
+using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
+
+#endif //GNC_OPTION_UI_HPP__
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 421826271..4bedeb4a2 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -24,6 +24,14 @@
 #include "gnc-option.hpp"
 #include "gnc-option-impl.hpp"
 #include "gnc-option-uitype.hpp"
+#include "gnc-option-ui.hpp"
+
+static const char* log_module{"gnc.app-utils.gnc-option"};
+
+extern "C"
+{
+#include <qoflog.h>
+}
 
 template <typename ValueType>
 GncOption::GncOption(const char* section, const char* name,
@@ -103,11 +111,21 @@ GncOption::get_docstring() const
 }
 
 void
-GncOption::set_ui_item(GncOptionUIItem* ui_elem)
+GncOption::set_ui_item(GncOptionUIItemPtr&& ui_item)
 {
-    std::visit([ui_elem](auto& option) {
-                   option.set_ui_item(ui_elem);
-               }, *m_option);
+
+    auto opt_ui_type = std::visit([](const auto& option)->GncOptionUIType {
+                                      return option.get_ui_type();
+                                  }, *m_option);
+
+    if (ui_item->get_ui_type() != opt_ui_type)
+    {
+        PERR("Setting option %s:%s UI element failed, mismatched UI types.",
+              get_section().c_str(), get_name().c_str());
+        return;
+    }
+
+    m_ui_item = std::move(ui_item);
 }
 
 const GncOptionUIType
@@ -118,17 +136,37 @@ GncOption::get_ui_type() const
                       }, *m_option);
 }
 
-GncOptionUIItem* const
+const GncOptionUIItem*
 GncOption::get_ui_item() const
 {
-    return std::visit([](const auto& option)->GncOptionUIItem* {
-                          return option.get_ui_item();
-                      }, *m_option);
+    return m_ui_item.get();
+}
+
+void
+GncOption::set_ui_item_from_option()
+{
+    if (!m_ui_item)
+        return;
+    m_ui_item->set_ui_item_from_option(*this);
+}
+
+void
+GncOption::set_option_from_ui_item()
+{
+    if (!m_ui_item)
+        return;
+    m_ui_item->set_option_from_ui_item(*this);
 }
 
 void
 GncOption::make_internal()
 {
+    if (!m_ui_item)
+    {
+        PERR("Option %s:%s has a UI Element, can't be INTERNAL.",
+             get_section().c_str(), get_name().c_str());
+        return;
+    }
     std::visit([](auto& option) {
                    option.make_internal();
                }, *m_option);
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 96f0db984..8a990e3ba 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -31,6 +31,7 @@
 #include "gnc-option-uitype.hpp"
 
 class GncOptionUIItem;
+using GncOptionUIItemPtr = std::unique_ptr<GncOptionUIItem>;
 struct QofInstance_s;
 using QofInstance = QofInstance_s;
 template <typename ValueType> class GncOptionValue;
@@ -72,9 +73,11 @@ public:
     const std::string& get_name() const;
     const std::string& get_key() const;
     const std::string& get_docstring() const;
-    void set_ui_item(GncOptionUIItem* ui_elem);
+    void set_ui_item(GncOptionUIItemPtr&& ui_elem);
     const GncOptionUIType get_ui_type() const;
-    GncOptionUIItem* const get_ui_item() const;
+    const GncOptionUIItem* get_ui_item() const;
+    void set_ui_item_from_option();
+    void set_option_from_ui_item();
     void make_internal();
     bool is_changed() const noexcept;
     template <typename ValueType> bool validate(ValueType value) const;
@@ -87,10 +90,11 @@ public:
     std::istream& in_stream(std::istream& iss);
     std::ostream& to_scheme(std::ostream& oss) const;
     std::istream& from_scheme(std::istream& iss);
-    GncOptionVariantPtr& _get_option() { return m_option; }
+    GncOptionVariant* const _get_option() { return m_option.get(); }
 private:
     inline static const std::string c_empty_string{""};
     GncOptionVariantPtr m_option;
+    GncOptionUIItemPtr m_ui_item{nullptr};
 };
 
 inline std::ostream&
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 476ee6ede..82735a610 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -27,6 +27,7 @@
 #include <kvp-value.hpp>
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
+#include "gnc-option-ui.hpp"
 
 auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
 GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index f29e81774..eebf933f8 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -206,4 +206,5 @@ void gnc_register_date_interval_option(const GncOptionDBPtr& db,
                                        const char* section, const char* name,
                                        const char* key, const char* doc_string,
                                        RelativeDatePeriod period);
+
 #endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 4aab78075..818dd876d 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -181,6 +181,7 @@ gnc_option_test_book_destroy(QofBook* book)
 %ignore GncOptionDateValue::operator=(GncOptionDateValue&&);
 %ignore operator<<(std::ostream&, const GncOption&);
 %ignore operator>>(std::istream&, GncOption&);
+%ignore GncOption::_get_option();
 
 %rename(absolute) RelativeDatePeriod::ABSOLUTE;
 %rename(today) RelativeDatePeriod::TODAY;
@@ -305,6 +306,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %include "gnc-option-impl.hpp"
 %include "gnc-optiondb.hpp"
 %include "gnc-optiondb-impl.hpp"
+%inline %{
+#include "gnc-option-ui.hpp"
+%}
 
 %extend GncOption {
     SCM get_scm_value()
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 08aa941b6..03c4f4dba 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -24,6 +24,7 @@
 #include <gtest/gtest.h>
 #include <gnc-option.hpp>
 #include <gnc-option-impl.hpp>
+#include <gnc-option-ui.hpp>
 #include <guid.hpp>
 extern "C"
 {
@@ -505,7 +506,7 @@ TEST_F(GncOptionCommodityTest, test_commodity_from_scheme)
     EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
 }
 
-class GncUIItem
+class GncUIType
 {
 public:
     void set_value(const std::string& value) { m_value = value; }
@@ -514,20 +515,29 @@ private:
     std::string m_value;
 };
 
-class GncOptionUIItem
-{
-public:
-    GncOptionUIItem(GncUIItem* widget) : m_widget{widget} {}
-    GncUIItem* m_widget;
-};
+using OptionUIItem = GncUIItem<GncUIType>;
 
 class GncOptionUITest : public ::testing::Test
 {
 protected:
     GncOptionUITest() :
+        m_widget{},
         m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"},
-            GncOptionUIType::STRING} {}
-
+                 GncOptionUIType::STRING}
+    {
+        auto to_ui = [](OptionUIItem& ui, GncOption& opt) {
+                         ui.m_widget->set_value(opt.get_value<std::string>());
+                     };
+        auto from_ui = [](OptionUIItem& ui, GncOption& opt) {
+                           opt.set_value<std::string>(ui.m_widget->get_value());
+                       };
+        auto ui_item{std::make_unique<GncOptionUIItem>(
+                OptionUIItem{&m_widget},
+                GncOptionUIType::STRING,
+                to_ui, from_ui)};
+        m_option.set_ui_item(std::move(ui_item));
+    }
+    GncUIType m_widget;
     GncOption m_option;
 };
 
@@ -540,10 +550,24 @@ TEST_F(GncOptionUI, test_option_ui_type)
 
 TEST_F(GncOptionUI, test_set_option_ui_item)
 {
-    GncUIItem ui_item;
-    GncOptionUIItem option_ui_item{&ui_item};
-    m_option.set_ui_item(&option_ui_item);
-    EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget);
+    EXPECT_EQ(&m_widget, m_option.get_ui_item()->get_ui_item().m_widget);
+}
+
+TEST_F(GncOptionUI, test_ui_value_from_option)
+{
+    const char* value{"waldo"};
+
+    m_option.set_value(value);
+    m_option.set_ui_item_from_option();
+    EXPECT_STREQ(value, m_widget.get_value().c_str());
+}
+
+TEST_F(GncOptionUI, test_option_value_from_ui)
+{
+    const char* value{"pepper"};
+    m_widget.set_value(value);
+    m_option.set_option_from_ui_item();
+    EXPECT_STREQ(value, m_option.get_value<std::string>().c_str());
 }
 
 class GncOptionRangeTest : public ::testing::Test
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 1c7ee7de4..e0199c8c9 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -24,6 +24,7 @@
 #include <gtest/gtest.h>
 #include <gnc-optiondb.hpp>
 #include <gnc-optiondb-impl.hpp>
+#include <gnc-option-ui.hpp>
 #include <kvp-value.hpp>
 extern "C"
 {
@@ -205,6 +206,7 @@ TEST_F(GncOptionDBTest, test_register_date_interval_option)
     ASSERT_TRUE(m_db->set_option("foo", "bar", time));
     EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value<time64>());
 }
+
 class GncOptionDBIOTest : public ::testing::Test
 {
 protected:

commit 1bea809cec542cd81f21c94a837b75ca5d22776a
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Feb 14 16:17:41 2020 -0800

    Remove the UI interface from GncOptionDB.
    
    UI operations occur only in dialog-option so there's no need to expose
    them from GncOptionDB.

diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
index 6a9c5470a..074b36247 100644
--- a/libgnucash/app-utils/gnc-optiondb-impl.hpp
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -56,13 +56,6 @@ public:
     void unregister_option(const char* section, const char* name);
     void set_default_section(const char* section);
     const GncOptionSection* const get_default_section() const noexcept;
-    void set_ui_item(const char* section, const char* name, GncOptionUIItem* ui_item);
-    GncOptionUIItem* const get_ui_item(const char* section, const char* name);
-    GncOptionUIType get_ui_type(const char* section, const char* name);
-    void set_ui_from_option(const char* section, const char* name,
-                            std::function<void(GncOption&)> func);
-    void set_option_from_ui(const char* section, const char* name,
-                            std::function<void(GncOption&)> func);
     std::string lookup_string_option(const char* section,
                                             const char* name);
     template <typename ValueType>
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 9bdb5de8e..476ee6ede 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -87,50 +87,6 @@ GncOptionDB::get_default_section() const noexcept
     return nullptr;
 }
 
-void
-GncOptionDB::set_ui_item(const char* section, const char* name,
-                         GncOptionUIItem* ui_item)
-{
-    auto option = find_option(section, name);
-    if (!option) return;
-    option->get().set_ui_item(ui_item);
-}
-
-GncOptionUIItem* const
-GncOptionDB::get_ui_item(const char* section, const char* name)
-{
-    auto option = find_option(section, name);
-    if (!option) return nullptr;
-    return option->get().get_ui_item();
-}
-
-GncOptionUIType
-GncOptionDB::get_ui_type(const char* section, const char* name)
-{
-    auto option = find_option(section, name);
-    if (!option) return GncOptionUIType::INTERNAL;
-    return option->get().get_ui_type();
-}
-
-void
-GncOptionDB::set_ui_from_option(const char* section, const char* name,
-                        std::function<void(GncOption&)> func)
-{
-    auto option = find_option(section, name);
-    if (!option) return;
-    func(option->get());
-}
-
-void
-GncOptionDB::set_option_from_ui(const char* section, const char* name,
-                        std::function<void(GncOption&)> func)
-{
-    auto option = find_option(section, name);
-    if (!option) return;
-    func(option->get());
-}
-
-
 std::optional<std::reference_wrapper<GncOptionSection>>
 GncOptionDB::find_section(const std::string& section)
 {
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 5d3db8021..1c7ee7de4 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -205,86 +205,6 @@ TEST_F(GncOptionDBTest, test_register_date_interval_option)
     ASSERT_TRUE(m_db->set_option("foo", "bar", time));
     EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value<time64>());
 }
-
-class GncUIType
-{
-public:
-    void set_value(const std::string& value) { m_value = value; }
-    const std::string& get_value() const { return m_value; }
-private:
-    std::string m_value;
-};
-
-class GncOptionUIItem
-{
-public:
-    GncOptionUIItem(GncUIType* widget) : m_widget{widget} {}
-    GncUIType* m_widget;
-};
-
-class GncOptionUITest : public ::testing::Test
-{
-protected:
-    GncOptionUITest() :
-        m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"},
-            GncOptionUIType::STRING} {}
-
-    GncOption m_option;
-};
-
-class GncOptionDBUITest : public ::testing::Test
-{
-protected:
-    GncOptionDBUITest() : m_db{gnc_option_db_new()}
-    {
-        gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
-                                   std::string{"waldo"});
-        gnc_register_text_option(m_db, "foo", "sausage", "links",
-                                 "Phony Option", std::string{"waldo"});
-        gnc_register_string_option(m_db, "qux", "grault", "baz", "Phony Option",
-                                   std::string{""});
-        gnc_register_text_option(m_db, "qux", "garply", "fred",
-                                   "Phony Option", std::string{"waldo"});
-    }
-
-    GncOptionDBPtr m_db;
-};
-
-TEST_F(GncOptionDBUITest, test_set_ui_item)
-{
-    GncUIType entry;
-    GncOptionUIItem ui_item(&entry);
-    m_db->set_ui_item("foo", "bar", &ui_item);
-    EXPECT_EQ(&entry, m_db->get_ui_item("foo", "bar")->m_widget);
-}
-
-TEST_F(GncOptionDBUITest, test_ui_value_from_option)
-{
-    GncUIType entry;
-    GncOptionUIItem ui_item(&entry);
-    const char* value{"waldo"};
-    m_db->set_ui_item("foo", "bar", &ui_item);
-    m_db->set_ui_from_option("foo", "bar", [](GncOption& option){
-            auto new_ui_item = option.get_ui_item();
-            new_ui_item->m_widget->set_value(option.get_value<std::string>());
-        });
-    EXPECT_STREQ(value, entry.get_value().c_str());
-}
-
-TEST_F(GncOptionDBUITest, test_option_value_from_ui)
-{
-    GncUIType entry;
-    GncOptionUIItem ui_item(&entry);
-    const char* value{"pepper"};
-    m_db->set_ui_item("foo", "bar", &ui_item);
-    entry.set_value(value);
-    m_db->set_option_from_ui("foo", "bar", [](GncOption& option){
-            auto new_ui_item = option.get_ui_item()->m_widget;
-            option.set_value(new_ui_item->get_value());
-        });
-    EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str());
-}
-
 class GncOptionDBIOTest : public ::testing::Test
 {
 protected:

commit cbf7d70ecdcf60615c3748aa30df7df6496929f8
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Feb 13 10:36:54 2020 -0800

    Integrate gnc-option into app-utils.
    
    Includes converting swig-app-utils-guile to c++.

diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt
index 7a0df5d35..0f05e930a 100644
--- a/libgnucash/app-utils/CMakeLists.txt
+++ b/libgnucash/app-utils/CMakeLists.txt
@@ -11,6 +11,8 @@ set (app_utils_noinst_HEADERS
   calculation/finproto.h
   calculation/fin_spl_protos.h
   calculation/fin_static_proto.h
+  gnc-option-impl.hpp
+  gnc-optiondb-impl.hpp
 )
 
 set (app_utils_HEADERS
@@ -27,8 +29,10 @@ set (app_utils_HEADERS
   gnc-gsettings.h
   gnc-help-utils.h
   gnc-helpers.h
+  gnc-option.hpp
+  gnc-optiondb.hpp
   gnc-prefs-utils.h
-  gnc-state.h  
+  gnc-state.h
   gnc-sx-instance-model.h
   gnc-ui-util.h
   gnc-ui-balances.h
@@ -36,10 +40,13 @@ set (app_utils_HEADERS
 )
 
 # Command to generate the swig-app-utils-guile.c wrapper file
-gnc_add_swig_guile_command (swig-apputils-guile-c
-    SWIG_APP_UTILS_GUILE_C swig-app-utils-guile.c
-    ${CMAKE_CURRENT_SOURCE_DIR}/app-utils.i ""
+set(SWIG_ARGS "-c++" "-procdoc" "sw-gnc-option-doc" "-procdocformat" "plain")
+gnc_add_swig_guile_command (swig-apputils-guile-cpp
+    SWIG_APP_UTILS_GUILE_CPP swig-app-utils-guile.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/app-utils.i
+    ${CMAKE_CURRENT_SOURCE_DIR}/gnc-optiondb.i ""
 )
+unset(SWIG_ARGS)
 
 # Command to generate the swig-app-utils-python.c wrapper file
 gnc_add_swig_python_command (swig-app-utils-python
@@ -62,6 +69,9 @@ set (app_utils_SOURCES
   gnc-exp-parser.c
   gnc-gsettings.c
   gnc-helpers.c
+  gnc-option.cpp
+  gnc-option-impl.cpp
+  gnc-optiondb.cpp
   gnc-prefs-utils.c
   gnc-sx-instance-model.c
   gnc-state.c
@@ -103,7 +113,7 @@ if (WIN32)
 endif()
 
 
-add_library (gnc-app-utils ${app_utils_ALL_SOURCES} ${SWIG_APP_UTILS_GUILE_C})
+add_library (gnc-app-utils ${app_utils_ALL_SOURCES} ${SWIG_APP_UTILS_GUILE_CPP})
 target_link_libraries(gnc-app-utils ${app_utils_ALL_LIBRARIES})
 
 target_include_directories (gnc-app-utils
diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i
index 28730bc61..274bc696f 100644
--- a/libgnucash/app-utils/app-utils.i
+++ b/libgnucash/app-utils/app-utils.i
@@ -21,6 +21,10 @@
 %module sw_app_utils
 %{
 /* Includes the header in the wrapper code */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
 #include <config.h>
 #include <option-util.h>
 #include <gnc-euro.h>
@@ -33,14 +37,22 @@
 #include <gnc-sx-instance-model.h>
 
 #include "gnc-engine-guile.h"
+#ifdef __cplusplus
+}
+#endif
 %}
 
-#if defined(SWIGGUILE)
+#if defined(SWIGGUILE) //Always C++
 %{
+extern "C"
+{
 #include "guile-mappings.h"
 
 SCM scm_init_sw_app_utils_module (void);
+}
 %}
+
+%include "gnc-optiondb.i"
 #endif
 
 #if defined(SWIGPYTHON)
@@ -76,7 +88,7 @@ void gnc_option_db_set_option_selectable_by_name(SCM guile_option,
   GList *node;
 
   for (node = $1; node; node = node->next)
-    list = scm_cons(gnc_quoteinfo2scm(node->data), list);
+      list = scm_cons(gnc_quoteinfo2scm(static_cast<gnc_commodity*>(node->data)), list);
 
   $result = scm_reverse(list);
 }
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 08f025195..4aab78075 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -3,6 +3,7 @@
  *
  * unique_ptr SWIG wrapper from https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig
  */
+#if defined(SWIGGUILE)
 
 namespace std {
   %feature("novaluewrapper") unique_ptr;
@@ -37,17 +38,11 @@ namespace std {
 
 %enddef
 
-%module sw_gnc_optiondb
+ //%module sw_gnc_optiondb
 %{
-extern "C"
-{
-#include <config.h>
-#include <libguile.h>
-#include <gnc-engine-guile.h>
-}
 #include "gnc-optiondb.hpp"
 #include "gnc-optiondb-impl.hpp"
-extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
+SCM scm_init_sw_gnc_optiondb_module(void);
 %}
 
 %include <std_string.i>
@@ -118,27 +113,6 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
     return scm_guid;
 }
 
-/* Account is actually a typedef for struct account_s and SWIG insists on using
- * the struct name (i.e. account_s) in C++ and the alias (i.e. Account) in
- * C. Oddly the compiler's type resolution also fails to consider them the same
- * so we have to use the struct name here to get the template to resolve
- * correctly.
- */
-using GncOptionAccount_sList = std::vector<const account_s*>;
-
-template <>inline SCM
-scm_from_value<GncOptionAccount_sList>(GncOptionAccount_sList value)
-{
-    SCM s_list = SCM_EOL;
-    for (auto acct : value)
-    {
-        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
-        s_list = scm_append(scm_list_2(s_list, elem));
-    }
-
-    return s_list;
-}
-
 template <typename ValueType> inline ValueType
 scm_to_value(SCM new_value)
 {
@@ -166,19 +140,34 @@ scm_to_value<int64_t>(SCM new_value)
     return scm_to_int64(new_value);
 }
 
+template <>inline SCM
+scm_from_value<GncOptionAccountList>(GncOptionAccountList value)
+{
+    SCM s_list = SCM_EOL;
+    for (auto acct : value)
+    {
+        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0));
+        s_list = scm_append(scm_list_2(s_list, elem));
+    }
+
+    return s_list;
+}
+
+QofBook* gnc_option_test_book_new();
+void gnc_option_test_book_destroy(QofBook*);
+
 QofBook*
-qof_book_new()
+gnc_option_test_book_new()
 {
     return static_cast<QofBook*>(g_object_new(QOF_TYPE_BOOK, nullptr));
 }
 
 void
-qof_book_destroy(QofBook* book)
+gnc_option_test_book_destroy(QofBook* book)
 {
     g_object_unref(book);
 }
 
-using Account = struct account_s;
 %}
 
 %ignore OptionClassifier;
@@ -228,6 +217,19 @@ using Account = struct account_s;
     $1 = &choices;
  }
 
+
+%typemap(in) GncOptionAccountList
+{
+    auto len = scm_to_size_t(scm_length($input));
+    for (std::size_t i = 0; i < len; ++i)
+    {
+        SCM s_account = scm_list_ref($input, scm_from_size_t(i));
+        Account* acct = (Account*)SWIG_MustGetPtr(s_account,
+                                                  SWIGTYPE_p_Account, 1, 0);
+        $1.push_back(acct);
+    }
+}
+
 %typemap(in) GncOptionAccountTypeList& (GncOptionAccountTypeList types)
 {
     auto len = scm_to_size_t(scm_length($input));
@@ -259,7 +261,7 @@ using Account = struct account_s;
     {
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));
         Account* acct = (Account*)SWIG_MustGetPtr(s_account,
-                                                  SWIGTYPE_p_account_s, 1, 0);
+                                                  SWIGTYPE_p_Account, 1, 0);
         $1.push_back(acct);
     }
 }
@@ -271,7 +273,7 @@ using Account = struct account_s;
     {
         SCM s_account = scm_list_ref($input, scm_from_size_t(i));
         Account* acct = (Account*)SWIG_MustGetPtr(s_account,
-                                                  SWIGTYPE_p_account_s, 1, 0);
+                                                  SWIGTYPE_p_Account, 1, 0);
         acclist.push_back(acct);
     }
     $1 = &acclist;
@@ -282,7 +284,7 @@ using Account = struct account_s;
     $result = SCM_EOL;
     for (auto acct : $1)
     {
-        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
+        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0));
         $result = scm_append(scm_list_2($result, elem));
     }
 }
@@ -292,7 +294,7 @@ using Account = struct account_s;
     $result = SCM_EOL;
     for (auto acct : *$1)
     {
-        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
+        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0));
         $result = scm_append(scm_list_2($result, elem));
     }
 }
@@ -326,7 +328,6 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
             }, *($self->_get_option()));
     }
 };
-
 %extend GncOptionDB {
     %template(set_option_string) set_option<std::string>;
     %template(set_option_int) set_option<int>;
@@ -369,3 +370,5 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         GncOption_set_value_from_scm(&(db_opt->get()), new_value);
     }
 %}
+
+#endif //SWIGGUILE
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 0bbf98d3c..eb6b27c0e 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -30,9 +30,6 @@ gnc_add_test_with_guile(test-scm-query-string test-scm-query-string.cpp
 add_app_utils_test(test-sx test-sx.cpp)
 
 set(gtest_gnc_option_SOURCES
-  ${MODULEPATH}/gnc-option.cpp
-  ${MODULEPATH}/gnc-option-impl.cpp
-  ${MODULEPATH}/gnc-optiondb.cpp
   gtest-gnc-option.cpp
   gtest-gnc-optiondb.cpp)
 
@@ -91,49 +88,10 @@ if (HAVE_SRFI64)
     FALSE
     )
 
-  set(SWIG_ARGS "-c++" "-procdoc" "sw-gnc-option-doc" "-procdocformat" "plain")
-  gnc_add_swig_guile_command(swig-gnc-optiondb-guile
-    SWIG_GNC_OPTIONDB_GUILE_CPP swig-gnc-optiondb-guile.cpp
-    ${MODULEPATH}/gnc-optiondb.i
-    ""
-    )
-  add_library(swig-gnc-optiondb MODULE
-    ${MODULEPATH}/gnc-option.cpp
-    ${MODULEPATH}/gnc-option-impl.cpp
-    ${MODULEPATH}/gnc-optiondb.cpp
-    ${SWIG_GNC_OPTIONDB_GUILE_CPP}
-    )
-  set(swig_gnc_optiondb_INCLUDES
-    ${MODULEPATH}
-    ${CMAKE_SOURCE_DIR}/bindings/guile
-    ${CMAKE_SOURCE_DIR}/libgnucash/engine
-    ${CMAKE_BINARY_DIR}/common # for config.h
-    ${GLIB2_INCLUDE_DIRS}
-    ${GUILE_INCLUDE_DIRS}
-    )
-
-  set(swig_gnc_optiondb_LIBS
-    gnc-engine
-    gnc-app-utils
-    gnucash-guile
-    ${GLIB2_LDFLAGS}
-    ${GUILE_LDFLAGS}
-    )
-
-  target_link_libraries(swig-gnc-optiondb ${swig_gnc_optiondb_LIBS})
-  target_include_directories(swig-gnc-optiondb
-    PRIVATE ${swig_gnc_optiondb_INCLUDES})
-
-  install(TARGETS swig-gnc-optiondb
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-    )
-
   gnc_add_scheme_test_targets(scm-test-gnc-optiondb
     "test-gnc-optiondb.scm"
     "tests"
-    "swig-gnc-optiondb;scm-srfi64-extras"
+    "swig-apputils-guile-cpp;scm-srfi64-extras"
     FALSE
     )
   gnc_add_scheme_tests("test-gnc-optiondb.scm")
@@ -173,4 +131,5 @@ set_dist_list(test_app_utils_DIST
   ${test_app_utils_scheme_SOURCES}
   ${test_app_utils_SOURCES}
   ${test_autoclear_SOURCES}
+  ${gtest_gnc_option_SOURCES}
 )
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index d1db9d738..966c5669f 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -26,10 +26,10 @@
 (use-modules (gnucash gnc-module))
 (eval-when
  (compile load eval expand)
- (load-extension "libswig-gnc-optiondb" "scm_init_sw_gnc_optiondb_module"))
+ (load-extension "_sw_app_utils" "scm_init_sw_app_utils_module"))
 
 (use-modules (gnucash engine))
-(use-modules (sw_gnc_optiondb))
+(use-modules (sw_app_utils))
 
 (define (run-test)
   (test-runner-factory gnc:test-runner)
@@ -84,7 +84,7 @@
 
   (define (cleanup book root)
 ;; Destroying the book destroys the account tree too
-    (qof-book-destroy book))
+    (gnc-option-test-book-destroy book))
 
   (define (test-make-account-list-option book)
     (test-group "test-make-account-list-option"
@@ -122,7 +122,7 @@
       (let ((acct (gnc-option-value optiondb "salt" "pork")))
         (test-equal (list (cadr acctlist)) acct)))))
 
-  (let* ((book (qof-book-new))
+  (let* ((book (gnc-option-test-book-new))
          (root-account (gnc-account-create-root book)))
     (test-group-with-cleanup "test-gnc-make-account-list-options"
                              (make-account-tree book root-account)

commit 81c5ac66897fe7cdc5c8e078911b2bd4333df157
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Feb 8 15:54:34 2020 -0800

    Remove the incomplete book-currency code.
    
    Not everything from the 6 Book-Currency commits is removed: Switching
    the Num and split-action fields and restricting edits of transactions
    older than n days were included and those changes are left in place.
    
    Some other partly-implemented features were also part of these commits
    and were removed: Options for setting a default capital gains account
    and currency, completion of the LIFO cap-gains policy, and creation of
    a list of cap-gains policies.
    
    If any of these are to be revived they should each be done in a separate
    feature branch and submitted via Github pull request for a code review
    before merging; a design discussion on gnucash-devel before restarting
    work is also advisable.

diff --git a/bindings/engine.i b/bindings/engine.i
index e7af4047a..c7c686699 100644
--- a/bindings/engine.i
+++ b/bindings/engine.i
@@ -396,10 +396,6 @@ void qof_book_set_string_option(QofBook* book, const char* opt_name, const char*
 
     SET_ENUM("OPTION-SECTION-ACCOUNTS");
     SET_ENUM("OPTION-NAME-TRADING-ACCOUNTS");
-    SET_ENUM("OPTION-NAME-CURRENCY-ACCOUNTING");
-    SET_ENUM("OPTION-NAME-BOOK-CURRENCY");
-    SET_ENUM("OPTION-NAME-DEFAULT-GAINS-POLICY");
-    SET_ENUM("OPTION-NAME-DEFAULT-GAINS-LOSS-ACCT-GUID");
     SET_ENUM("OPTION-NAME-AUTO-READONLY-DAYS");
     SET_ENUM("OPTION-NAME-NUM-FIELD-SOURCE");
 
diff --git a/gnucash/gnome-utils/dialog-options.c b/gnucash/gnome-utils/dialog-options.c
index e3f9d27e6..ec0a9fda2 100644
--- a/gnucash/gnome-utils/dialog-options.c
+++ b/gnucash/gnome-utils/dialog-options.c
@@ -66,7 +66,6 @@
 static QofLogModule log_module = GNC_MOD_GUI;
 
 #define DIALOG_OPTIONS_CM_CLASS      "dialog-options"
-#define DIALOG_BOOK_OPTIONS_CM_CLASS "dialog-book-options"
 
 #define GNC_PREFS_GROUP              "dialogs.options"
 
@@ -82,8 +81,6 @@ static QofLogModule log_module = GNC_MOD_GUI;
 /* A Hash-table of GNCOptionDef_t keyed with option names. */
 static GHashTable *optionTable = NULL;
 
-static int gain_loss_accounts_in_filter = 0;
-
 struct gnc_option_win
 {
     GtkWidget  * window;
@@ -127,43 +124,12 @@ enum page_tree
     NUM_COLUMNS
 };
 
-typedef struct
-{
-    GtkWidget *gnc_currency_radiobutton_0;
-    GtkWidget *gnc_currency_radiobutton_1;
-    GtkWidget *gnc_currency_radiobutton_2;
-    GtkWidget *book_currency_widget;
-    GtkWidget *default_cost_policy_widget;
-    GtkWidget *default_gain_loss_account_widget;
-    GtkWidget *book_currency_table;
-    GtkWidget *book_currency_vbox;
-    GtkWidget *gain_loss_account_del_button;
-    GtkWidget *gain_loss_account_table;
-    GtkWidget *default_gain_loss_account_text;
-    GNCOption *option;
-    Account   *prior_gain_loss_account;
-    gnc_commodity *retrieved_book_currency;
-
-    SCM retrieved_policy_scm;
-    SCM retrieved_gain_loss_acct_guid_scm;
-
-} currency_accounting_data;
-
-static currency_accounting_data *book_currency_data = NULL;
-
 static GNCOptionWinCallback global_help_cb = NULL;
 gpointer global_help_cb_data = NULL;
 
 static void gnc_options_dialog_reset_cb (GtkWidget * w, gpointer data);
 void gnc_options_dialog_list_select_cb (GtkTreeSelection *selection,
                                         gpointer data);
-void gnc_set_default_cost_policy_widget (SCM list_symbol);
-void gnc_set_default_gain_loss_account_widget (gnc_commodity *commodity);
-void gnc_option_changed_book_currency_widget_cb (GtkWidget *widget);
-void gnc_option_changed_gain_loss_account_widget_cb (GtkTreeSelection *selection,
-                                                     gpointer data);
-void gnc_option_changed_gain_loss_account_del_button_widget_cb (GtkButton *button,
-                                                                gpointer data);
 static void component_close_handler (gpointer data);
 
 GtkWidget *
@@ -520,6 +486,7 @@ gnc_option_radiobutton_cb (GtkWidget *w, gpointer data)
     gnc_option_changed_widget_cb (widget, option);
 }
 
+<<<<<<< HEAD
 static gboolean
 gnc_gain_loss_account_view_filter (Account  *account, gpointer  data)
 {
@@ -975,6 +942,8 @@ gnc_option_currency_accounting_book_cb (GtkWidget *widget, gpointer data)
     gtk_widget_set_sensitive (book_currency_data->book_currency_vbox, TRUE);
     gnc_option_radiobutton_cb (widget, (gpointer) book_currency_data->option);
 }
+=======
+>>>>>>> f87d57145 (Remove the incomplete book-currency code.)
 
 static GtkWidget *
 gnc_option_create_date_widget (GNCOption *option)
@@ -1186,6 +1155,7 @@ gnc_option_create_radiobutton_widget (char *name, GNCOption *option)
     return frame;
 }
 
+<<<<<<< HEAD
 static GtkWidget *
 gnc_option_create_currency_accounting_widget (char *name, GNCOption *option)
 {
@@ -1358,6 +1328,8 @@ gnc_option_create_currency_accounting_widget (char *name, GNCOption *option)
     return frame;
 }
 
+=======
+>>>>>>> f87d57145 (Remove the incomplete book-currency code.)
 static void
 gnc_option_account_cb (GtkTreeSelection *selection, gpointer data)
 {
@@ -2161,27 +2133,6 @@ component_close_handler (gpointer data)
     gnc_options_dialog_cancel_button_cb (NULL, win);
 }
 
-static void
-refresh_handler (GHashTable *changes, gpointer user_data)
-{
-    gnc_commodity *commodity = NULL;
-    GtkTreeIter iter;
-
-    /* The default_gain_loss_account_widget needs to be refreshed if any
-       changes have been made via account maintenance, if it exists and
-       if the book currency widget has a selection */
-/*    if (book_currency_data->default_gain_loss_account_widget &&
-        gtk_combo_box_get_active_iter(
-            GTK_COMBO_BOX(book_currency_data->book_currency_widget), &iter))
-    {
-        commodity = gnc_currency_edit_get_currency(
-                                GNC_CURRENCY_EDIT(
-                                    book_currency_data->book_currency_widget));
-        gnc_set_default_gain_loss_account_widget(commodity);
-        gtk_widget_show_all(book_currency_data->book_currency_vbox);
-    } */
-}
-
 /* gnc_options_dialog_new:
  *
  *   - Opens the dialog-options glade file
@@ -2294,21 +2245,11 @@ gnc_options_dialog_new_modal (gboolean modal, gchar *title,
     gtk_widget_show (retval->notebook);
     gtk_box_pack_start (GTK_BOX(hbox), retval->notebook, TRUE, TRUE, 5);
 
-    retval->component_class =
-                (component_class ? component_class : DIALOG_OPTIONS_CM_CLASS);
     component_id = gnc_register_gui_component (retval->component_class,
-                    refresh_handler, component_close_handler,
-                    retval);
+                                               NULL, component_close_handler,
+                                               retval);
     gnc_gui_component_set_session (component_id, gnc_get_current_session());
 
-    /* Watch account maintenance events only if book option dialog */
-    if (g_strcmp0 (retval->component_class, DIALOG_BOOK_OPTIONS_CM_CLASS) == 0)
-    {
-        gnc_gui_component_watch_entity_type (component_id,
-                                             GNC_ID_ACCOUNT,
-                                             QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
-    }
-
     g_signal_connect (retval->window, "destroy",
                       G_CALLBACK(gnc_options_dialog_destroy_cb), retval);
 
@@ -3106,28 +3047,6 @@ gnc_option_set_ui_widget_budget (GNCOption *option, GtkGrid *page_box,
     return value;
 }
 
-static GtkWidget *
-gnc_option_set_ui_widget_currency_accounting (GNCOption *option,
-                                              GtkGrid *page_box,
-                                              GtkLabel *name_label, char *documentation,
-                                              /* Return values */
-                                              GtkWidget **enclosing,
-                                              gboolean *packed)
-{
-    GtkWidget *value;
-
-    *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
-    gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE);
-
-    value = gnc_option_create_currency_accounting_widget (NULL, option);
-    gnc_option_set_widget (option, value);
-
-    gnc_option_set_ui_value (option, FALSE);
-    gtk_box_pack_start (GTK_BOX(*enclosing), value, TRUE, TRUE, 0);
-    gtk_widget_show_all (*enclosing);
-    return value;
-}
-
 /*************************
  *       SET VALUE       *
  *************************
@@ -3617,113 +3536,6 @@ gnc_option_set_ui_value_plot_size (GNCOption *option, gboolean use_default,
     return TRUE;
 }
 
-static gboolean
-gnc_option_set_ui_value_currency_accounting (GNCOption *option,
-                                             gboolean use_default,
-                                             GtkWidget *widget, SCM value)
-{
-    if (scm_is_pair (value))
-    {
-        SCM rb_symbol;
-
-        rb_symbol = gnc_currency_accounting_option_value_get_method (value);
-
-        if (rb_symbol)
-        {
-            int index;
-
-            index = gnc_option_permissible_value_index (option, rb_symbol);
-            if (index < 0)
-                return TRUE;
-            else
-            {
-                GtkWidget *button = NULL;
-                gpointer val;
-
-                switch (index)
-                {
-                    case 0:
-                        button = book_currency_data->gnc_currency_radiobutton_0;
-                        break;
-                    case 1:
-                        button = book_currency_data->gnc_currency_radiobutton_1;
-                        break;
-                    case 2:
-                        button = book_currency_data->gnc_currency_radiobutton_2;
-                        break;
-                    default:
-                        return TRUE;
-                }
-
-                val = g_object_get_data (G_OBJECT(button),
-                                            "gnc_radiobutton_index");
-                g_return_val_if_fail (GPOINTER_TO_INT(val) == index, TRUE);
-
-                if (g_strcmp0 (gnc_option_permissible_value_name (option,
-                                                                  index),
-                                "Use a Book Currency") == 0)
-                {
-                    gnc_commodity *commodity = NULL;
-                    SCM curr_scm =
-                        gnc_currency_accounting_option_value_get_book_currency
-                            (value);
-                    SCM list_symbol =
-                        gnc_currency_accounting_option_value_get_default_policy
-                            (value);
-                    SCM acct_guid_scm =
-                        gnc_currency_accounting_option_value_get_default_account
-                            (value);
-
-                    commodity = gnc_scm_to_commodity (curr_scm);
-                    if (commodity)
-                    {
-                        book_currency_data->retrieved_book_currency = commodity;
-                    }
-                    else
-                    {
-                        book_currency_data->retrieved_book_currency = NULL;
-                    }
-                    if (list_symbol)
-                    {
-                        book_currency_data->retrieved_policy_scm = list_symbol;
-                    }
-                    else
-                    {
-                        book_currency_data->retrieved_policy_scm = NULL;
-                    }
-                    if (acct_guid_scm)
-                    {
-                        book_currency_data->retrieved_gain_loss_acct_guid_scm =
-                                                                acct_guid_scm;
-                    }
-                    else
-                    {
-                        book_currency_data->retrieved_gain_loss_acct_guid_scm =
-                                                                        NULL;
-                    }
-                }
-                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE);
-                /* when an unselected button in a group is clicked the clicked
-                   button receives the “toggled” signal, as does the
-                   previously selected button; however, if the first button
-                   is active when the currency-accounting dialog is created,
-                   that is, it's read from the option, the "toggled" handler
-                   is not called while it is if any other button is active.
-                   To get desired result, that is, to set sensitivity to
-                   FALSE, explicitly call the handler here if first button */
-                if (index == 0)
-                {
-                    gnc_option_currency_accounting_non_book_cb (button,
-                        (gpointer) book_currency_data);
-                }
-                return FALSE;
-            }
-        }
-        return TRUE;
-    }
-    return TRUE;
-}
-
 /*************************
  *       GET VALUE       *
  *************************
@@ -4045,6 +3857,7 @@ gnc_option_get_ui_value_plot_size (GNCOption *option, GtkWidget *widget)
     return scm_cons (type, val);
 }
 
+<<<<<<< HEAD
 static SCM
 gnc_option_get_ui_value_currency_accounting (GNCOption *option,
                                              GtkWidget *widget)
@@ -4148,6 +3961,8 @@ gnc_option_get_ui_value_currency_accounting (GNCOption *option,
     return (scm_cons (gnc_option_permissible_value (option, index), value));
 }
 
+=======
+>>>>>>> f87d57145 (Remove the incomplete book-currency code.)
 /************************************/
 /*          INITIALIZATION          */
 /************************************/
@@ -4228,12 +4043,6 @@ static void gnc_options_initialize_options (void)
             "budget", gnc_option_set_ui_widget_budget,
             gnc_option_set_ui_value_budget, gnc_option_get_ui_value_budget
         },
-        {
-            "currency-accounting",
-            gnc_option_set_ui_widget_currency_accounting,
-            gnc_option_set_ui_value_currency_accounting,
-            gnc_option_get_ui_value_currency_accounting
-        },
         { NULL, NULL, NULL, NULL }
     };
     int i;
diff --git a/gnucash/gnome-utils/dialog-utils.c b/gnucash/gnome-utils/dialog-utils.c
index eddb02c3c..622386db4 100644
--- a/gnucash/gnome-utils/dialog-utils.c
+++ b/gnucash/gnome-utils/dialog-utils.c
@@ -820,51 +820,6 @@ gnc_new_book_option_display (GtkWidget *parent)
     return TRUE;
 }
 
-/* This function returns a widget for selecting a cost policy
- */
-GtkWidget *
-gnc_cost_policy_select_new (void)
-{
-    GtkWidget *cost_policy_widget = NULL;
-    GList *list_of_policies = NULL;
-
-    list_of_policies = gnc_get_valid_policy_list();
-
-    g_return_val_if_fail(g_list_length (list_of_policies) >= 0, NULL);
-    if (list_of_policies)
-    {
-        GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);
-        GtkTreeIter  iter;
-        GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
-        const char *description;
-        GList *l = NULL;
-
-        /* Add values to the list store, entry and tooltip */
-        for (l = list_of_policies; l != NULL; l = l->next)
-        {
-            GNCPolicy *pcy = l->data;
-            description = PolicyGetDescription (pcy);
-
-            gtk_list_store_append (store, &iter);
-            gtk_list_store_set (store, &iter,
-                    0, (description && *description) ? _(description) : "",
-                    -1);
-        }
-        g_list_free (list_of_policies);
-        /* Create the new Combo with the store */
-        cost_policy_widget = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store));
-
-        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(cost_policy_widget), renderer, TRUE);
-        gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(cost_policy_widget),
-                                       renderer, "text", 0);
-        g_object_unref (store);
-    }
-    return cost_policy_widget;
-}
-
-/* This function returns a string for the CSS 'gnc-class-negative-numbers' class,
- * the returned string must be freed
- */
 gchar*
 gnc_get_negative_color (void)
 {
diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.c
index f9ceea985..ddea6b2f2 100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.c
@@ -4169,12 +4169,9 @@ gnc_book_options_dialog_apply_helper(GNCOptionDB * options)
     QofBook *book = gnc_get_current_book ();
     gboolean use_split_action_for_num_before =
         qof_book_use_split_action_for_num_field (book);
-    gboolean use_book_currency_before =
-        gnc_book_use_book_currency (book);
     gint use_read_only_threshold_before =
         qof_book_get_num_days_autoreadonly (book);
     gboolean use_split_action_for_num_after;
-    gboolean use_book_currency_after;
     gint use_read_only_threshold_after;
     gboolean return_val = FALSE;
     GList *results = NULL, *iter;
@@ -4199,7 +4196,6 @@ gnc_book_options_dialog_apply_helper(GNCOptionDB * options)
     qof_book_save_options (book, gnc_option_db_save, options, TRUE);
     use_split_action_for_num_after =
         qof_book_use_split_action_for_num_field (book);
-    use_book_currency_after = gnc_book_use_book_currency (book);
 
     // mark cached value as invalid so we get new value
     book->cached_num_days_autoreadonly_isvalid = FALSE;
@@ -4211,11 +4207,6 @@ gnc_book_options_dialog_apply_helper(GNCOptionDB * options)
                                                 use_split_action_for_num_after);
         return_val = TRUE;
     }
-    if (use_book_currency_before != use_book_currency_after)
-    {
-        gnc_book_option_book_currency_selected_cb (use_book_currency_after);
-        return_val = TRUE;
-    }
     if (use_read_only_threshold_before != use_read_only_threshold_after)
         return_val = TRUE;
 
@@ -4264,25 +4255,6 @@ gnc_book_option_num_field_source_change_cb (gboolean num_action)
     gnc_resume_gui_refresh ();
 }
 
-/** Calls gnc_book_option_book_currency_selected to initiate registered
- * callbacks when currency accounting book option changes to book-currency so
- * that registers/reports can update themselves; sets feature flag */
-void
-gnc_book_option_book_currency_selected_cb (gboolean use_book_currency)
-{
-    gnc_suspend_gui_refresh ();
-    if (use_book_currency)
-    {
-        /* Set a feature flag in the book for use of book currency. This will
-         * prevent older GnuCash versions that don't support this feature from
-         * opening this file. */
-        gnc_features_set_used (gnc_get_current_book(),
-                               GNC_FEATURE_BOOK_CURRENCY);
-    }
-    gnc_book_option_book_currency_selected (use_book_currency);
-    gnc_resume_gui_refresh ();
-}
-
 static gboolean
 show_handler (const char *class_name, gint component_id,
               gpointer user_data, gpointer iter_data)
diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.c
index 6d39f37ad..aa8c59a0e 100644
--- a/gnucash/gnome/assistant-hierarchy.c
+++ b/gnucash/gnome/assistant-hierarchy.c
@@ -1473,24 +1473,11 @@ on_select_currency_prepare (hierarchy_data  *data)
     {
         gnc_book_options_dialog_apply_helper(data->options);
 
-        if (gnc_book_use_book_currency (gnc_get_current_book ()))
-        {
-            gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(data->currency_selector),
-                gnc_book_get_book_currency (gnc_get_current_book ()));
-            gtk_label_set_text (GTK_LABEL(data->currency_selector_label),
-                ( _("You selected a book currency and it will be used for\n" \
-                    "new accounts. Accounts in other currencies must be\n" \
-                    "added manually.") ));
-            gtk_widget_set_sensitive(data->currency_selector, FALSE);
-        }
-        else
-        {
-            gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(data->currency_selector),
-                gnc_default_currency());
-            gtk_label_set_text (GTK_LABEL(data->currency_selector_label),
-                ( _("Please choose the currency to use for new accounts.") ));
-            gtk_widget_set_sensitive(data->currency_selector, TRUE);
-        }
+        gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(data->currency_selector),
+                                        gnc_default_currency());
+        gtk_label_set_text (GTK_LABEL(data->currency_selector_label),
+                            ( _("Please choose the currency to use for new accounts.") ));
+        gtk_widget_set_sensitive(data->currency_selector, TRUE);
     }
 }
 
diff --git a/libgnucash/app-utils/app-utils.scm b/libgnucash/app-utils/app-utils.scm
index b9f218a36..94bcfa55d 100644
--- a/libgnucash/app-utils/app-utils.scm
+++ b/libgnucash/app-utils/app-utils.scm
@@ -33,6 +33,46 @@
                    (gnucash app-utils options)
                    (gnucash app-utils c-interface))
 
+(export gnc:make-internal-option)
+(export gnc:make-query-option)
+(export gnc:make-color-option)
+(export gnc:make-dateformat-option)
+(export gnc:dateformat-get-format)
+
+(export gnc:color->html)
+(export gnc:color-option->html)
+(export gnc:color-option->hex-string)
+(export gnc:new-options)
+
+(export gnc:register-option)
+(export gnc:unregister-option)
+(export gnc:options-register-callback)
+(export gnc:options-register-c-callback)
+(export gnc:options-unregister-callback-id)
+(export gnc:options-for-each)
+(export gnc:options-for-each-general)
+(export gnc:lookup-option)
+(export gnc:generate-restore-forms)
+(export gnc:options-fancy-date)
+(export gnc:options-scm->kvp)
+(export gnc:options-kvp->scm)
+(export gnc:options-clear-changes)
+(export gnc:options-touch)
+(export gnc:options-run-callbacks)
+(export gnc:options-set-default-section)
+(export gnc:options-get-default-section)
+(export gnc:options-copy-values)
+(export gnc:send-options)
+
+(define (gnc:option-get-value book category key)
+  ;;Access an option directly
+  (qof-book-get-option book
+                       (if (list? key)
+                           (append (list category) key)
+                           (list category key))))
+(export gnc:option-get-value)
+
+;; gw-engine-spec.scm
 (re-export HOOK-SAVE-OPTIONS)
 
 (export gnc:get-debit-string)
diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c
index aea6bbba3..6deba2fa4 100644
--- a/libgnucash/app-utils/gnc-ui-util.c
+++ b/libgnucash/app-utils/gnc-ui-util.c
@@ -438,122 +438,6 @@ gnc_get_current_book_tax_type (void)
     }
 }
 
-/** Returns TRUE if both book-currency and default gain/loss policy KVPs exist
-  * and are valid and trading accounts are not used. */
-gboolean
-gnc_book_use_book_currency (QofBook *book)
-{
-    const gchar *policy;
-    const gchar *currency;
-
-    if (!book) return FALSE;
-
-    policy = qof_book_get_default_gains_policy (book);
-    currency = qof_book_get_book_currency_name (book);
-
-    /* If either a default gain/loss policy or a book-currency does not exist,
-       book-currency accounting method not valid */
-    if (!policy || !currency)
-       return FALSE;
-
-    /* If both exist, both must be valid */
-    if (!gnc_valid_policy_name (policy) || !gnc_commodity_table_lookup
-                                            (gnc_commodity_table_get_table
-                                              (gnc_get_current_book()),
-                                                GNC_COMMODITY_NS_CURRENCY,
-                                                 currency))
-       return FALSE;
-
-    /* If both exist and are valid, there must be no trading accounts flag */
-    if (qof_book_use_trading_accounts (book))
-       return FALSE;
-
-    return TRUE;
-}
-
-/** Returns pointer to Book Currency name for book or NULL; determines
-  * that both book-currency and default gain/loss policy KVPs exist and that
-  * both are valid, a requirement for the 'book-currency' currency accounting
-  * method to apply. */
-const gchar *
-gnc_book_get_book_currency_name (QofBook *book)
-{
-    if (!book) return NULL;
-
-    if (gnc_book_use_book_currency (book))
-        return qof_book_get_book_currency_name (book);
-
-    return NULL;
-}
-
-/** Returns pointer to Book Currency for book or NULL; determines
-  * that both book-currency and default gain/loss policy KVPs exist and that
-  * both are valid, a requirement for the 'book-currency' currency accounting
-  * method to apply. */
-gnc_commodity *
-gnc_book_get_book_currency (QofBook *book)
-{
-    if (!book) return NULL;
-
-    if (gnc_book_use_book_currency (book))
-        return gnc_commodity_table_lookup
-                    (gnc_commodity_table_get_table(book),
-                     GNC_COMMODITY_NS_CURRENCY,
-                     qof_book_get_book_currency_name (book));
-
-    return NULL;
-}
-
-/** Returns pointer to default gain/loss policy for book or NULL; determines
-  * that both book-currency and default gain/loss policy KVPs exist and that
-  * both are valid, a requirement for the 'book-currency' currency accounting
-  * method to apply. */
-const gchar *
-gnc_book_get_default_gains_policy (QofBook *book)
-{
-    if (!book) return NULL;
-
-    if (gnc_book_use_book_currency (book))
-        return qof_book_get_default_gains_policy (book);
-
-    return NULL;
-}
-
-/** Returns pointer to default gain/loss account for book or NULL; determines
-  * that both book-currency and default gain/loss policy KVPs exist and that
-  * both are valid, a requirement for the 'book-currency' currency accounting
-  * method to apply. Also, account must not be hidden or a placeholder, and
-  * must be of same currency as book-currency and income or expense type */
-Account *
-gnc_book_get_default_gain_loss_acct (QofBook *book)
-{
-    Account *gains_account = NULL;
-
-    if (!book) return NULL;
-
-    if (gnc_book_use_book_currency (book))
-    {
-        GncGUID *guid = qof_book_get_default_gain_loss_acct_guid (book);
-        gains_account = xaccAccountLookup (guid, book);
-        guid_free (guid);
-    }
-
-    if (gains_account &&
-        !xaccAccountGetPlaceholder(gains_account) &&
-        !xaccAccountGetHidden(gains_account) &&
-        (gnc_commodity_equal(xaccAccountGetCommodity(gains_account),
-                                    gnc_book_get_book_currency(book))) &&
-        ((xaccAccountGetType(gains_account) == ACCT_TYPE_INCOME) ||
-            (xaccAccountGetType(gains_account) == ACCT_TYPE_EXPENSE)))
-    {
-        return gains_account;
-    }
-    else
-    {
-        return NULL;
-    }
-}
-
 Account *
 gnc_get_current_root_account (void)
 {
@@ -1168,9 +1052,6 @@ gnc_default_currency_common (gchar *requested_currency,
                                           GNC_COMMODITY_NS_CURRENCY,
                                           requested_currency);
 
-    if (gnc_book_use_book_currency (gnc_get_current_book ()))
-        return gnc_book_get_book_currency (gnc_get_current_book ());
-
     if (gnc_prefs_get_bool (section, GNC_PREF_CURRENCY_CHOICE_OTHER))
     {
         mnemonic = gnc_prefs_get_string(section, GNC_PREF_CURRENCY_OTHER);
diff --git a/libgnucash/app-utils/gnc-ui-util.h b/libgnucash/app-utils/gnc-ui-util.h
index b9bb385c6..7a56a0a0b 100644
--- a/libgnucash/app-utils/gnc-ui-util.h
+++ b/libgnucash/app-utils/gnc-ui-util.h
@@ -97,40 +97,6 @@ const gchar * gnc_get_current_book_tax_type (void);
   * callbacks when num_field_source book option changes so that
   * registers/reports can update themselves; sets feature flag */
 void gnc_book_option_num_field_source_change_cb (gboolean num_action);
-
-/** Calls gnc_book_option_book_currency_selected to initiate registered
-  * callbacks when currency accounting book option changes to book-currency so
-  * that registers/reports can update themselves; sets feature flag */
-void gnc_book_option_book_currency_selected_cb (gboolean use_book_currency);
-
-/** Returns TRUE if both book-currency and default gain/loss policy KVPs exist
-  * and are valid and trading accounts are not used */
-gboolean gnc_book_use_book_currency (QofBook *book);
-
-/** Returns pointer to Book Currency name for book or NULL; determines
-  * that both book-currency and default gain/loss policy KVPs exist and that
-  * both are valid, a requirement for the 'book-currency' currency accounting
-  * method to apply. */
-const gchar * gnc_book_get_book_currency_name (QofBook *book);
-
-/** Returns pointer to Book Currency for book or NULL; determines
-  * that both book-currency and default gain/loss policy KVPs exist and that
-  * both are valid, a requirement for the 'book-currency' currency accounting
-  * method to apply. */
-gnc_commodity * gnc_book_get_book_currency (QofBook *book);
-
-/** Returns pointer to default gain/loss policy for book or NULL; determines
-  * that both book-currency and default gain/loss policy KVPs exist and that
-  * both are valid, a requirement for the 'book-currency' currency accounting
-  * method to apply. */
-const gchar * gnc_book_get_default_gains_policy (QofBook *book);
-
-/** Returns pointer to default gain/loss account for book or NULL; determines
-  * that both book-currency and default gain/loss policy KVPs exist and that
-  * both are valid, a requirement for the 'book-currency' currency accounting
-  * method to apply. */
-Account * gnc_book_get_default_gain_loss_acct (QofBook *book);
-
 Account * gnc_get_current_root_account (void);
 gnc_commodity_table * gnc_get_current_commodities (void);
 
diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c
index 01c475204..04ff31ea0 100644
--- a/libgnucash/app-utils/option-util.c
+++ b/libgnucash/app-utils/option-util.c
@@ -106,15 +106,6 @@ struct _Getters
     SCM date_option_value_relative;
     SCM plot_size_option_value_type;
     SCM plot_size_option_value;
-    SCM currency_accounting_option_currency_doc_string;
-    SCM currency_accounting_option_default_currency;
-    SCM currency_accounting_option_policy_doc_string;
-    SCM currency_accounting_option_default_policy;
-    SCM currency_accounting_option_gain_loss_account_doc_string;
-    SCM currency_accounting_option_method;
-    SCM currency_accounting_option_book_currency;
-    SCM currency_accounting_option_selected_default_policy;
-    SCM currency_accounting_option_selected_default_gain_loss_account;
 };
 
 
@@ -565,24 +556,6 @@ initialize_getters(void)
         scm_c_eval_string ("gnc:date-option-relative-time");
     getters.plot_size_option_value_type = scm_c_eval_string ("gnc:plot-size-option-value-type");
     getters.plot_size_option_value = scm_c_eval_string ("gnc:plot-size-option-value");
-    getters.currency_accounting_option_currency_doc_string =
-        scm_c_eval_string ("gnc:currency-accounting-option-get-curr-doc-string");
-    getters.currency_accounting_option_default_currency =
-        scm_c_eval_string ("gnc:currency-accounting-option-get-default-curr");
-    getters.currency_accounting_option_policy_doc_string =
-        scm_c_eval_string ("gnc:currency-accounting-option-get-policy-doc-string");
-    getters.currency_accounting_option_default_policy =
-        scm_c_eval_string ("gnc:currency-accounting-option-get-default-policy");
-    getters.currency_accounting_option_gain_loss_account_doc_string =
-        scm_c_eval_string ("gnc:currency-accounting-option-get-gain-loss-account-doc-string");
-    getters.currency_accounting_option_method =
-        scm_c_eval_string ("gnc:currency-accounting-option-selected-method");
-    getters.currency_accounting_option_book_currency =
-        scm_c_eval_string ("gnc:currency-accounting-option-selected-currency");
-    getters.currency_accounting_option_selected_default_policy =
-        scm_c_eval_string ("gnc:currency-accounting-option-selected-policy");
-    getters.currency_accounting_option_selected_default_gain_loss_account =
-        scm_c_eval_string ("gnc:currency-accounting-option-selected-gain-loss-account");
 
     getters_initialized = TRUE;
 }
@@ -728,7 +701,7 @@ gnc_option_default_getter (GNCOption *option)
  * Args: option - the GNCOption                                     *
  * Returns: SCM handle to function                                  *
 \********************************************************************/
-static SCM
+SCM
 gnc_option_value_validator(GNCOption *option)
 {
     initialize_getters ();
@@ -749,7 +722,7 @@ gnc_option_value_validator(GNCOption *option)
  * Returns: SCM handle to function                                  *
  *          If no such function exists, returns SCM_UNDEFINED.      *
 \********************************************************************/
-static SCM
+SCM
 gnc_option_widget_changed_proc_getter(GNCOption *option)
 {
     SCM cb;
@@ -2133,169 +2106,6 @@ gnc_plot_size_option_value_get_value (SCM option_value)
         return 1.0;
 }
 
-/********************************************************************\
- * gnc_currency_accounting_option_currency_documentation            *
- *   returns the malloc'ed documentation string for currency        *
- *   selector of the currency-accounting option, or NULL if it      *
- *   can't be retrieved.                                            *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: malloc'ed char * or NULL                                *
-\********************************************************************/
-char *
-gnc_currency_accounting_option_currency_documentation (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_string
-              (getters.currency_accounting_option_currency_doc_string,
-                                     option->guile_option);
-}
-
-/********************************************************************\
- * gnc_currency_accounting_option_get_default_currency              *
- *   returns the SCM value for the currency-accounting option       *
- *   default currency.                                              *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: SCM value                                               *
-\********************************************************************/
-SCM
-gnc_currency_accounting_option_get_default_currency (GNCOption *option)
-{
-    initialize_getters ();
-
-    return scm_call_1
-              (getters.currency_accounting_option_default_currency,
-                                        option->guile_option);
-}
-
-/********************************************************************\
- * gnc_currency_accounting_option_policy_documentation              *
- *   returns the malloc'ed documentation string for policy          *
- *   selector of the currency-accounting option, or NULL if it      *
- *   can't be retrieved.                                            *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: malloc'ed char * or NULL                                *
-\********************************************************************/
-char *
-gnc_currency_accounting_option_policy_documentation (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_string
-              (getters.currency_accounting_option_policy_doc_string,
-                                     option->guile_option);
-}
-
-/********************************************************************\
- * gnc_currency_accounting_option_get_default_policy                *
- *   returns the SCM value for the currency-accounting option       *
- *   default policy.                                                *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: SCM value                                               *
-\********************************************************************/
-SCM
-gnc_currency_accounting_option_get_default_policy (GNCOption *option)
-{
-    initialize_getters ();
-
-    return scm_call_1
-              (getters.currency_accounting_option_default_policy,
-                                        option->guile_option);
-}
-
-/********************************************************************\
- * gnc_currency_accounting_option_gain_loss_account_documentation   *
- *   returns the malloc'ed documentation string for account         *
- *   selector of the currency-accounting option, or NULL if it      *
- *   can't be retrieved.                                            *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: malloc'ed char * or NULL                                *
-\********************************************************************/
-char *
-gnc_currency_accounting_option_gain_loss_account_documentation (GNCOption *option)
-{
-    initialize_getters ();
-
-    return gnc_scm_call_1_to_string
-              (getters.currency_accounting_option_gain_loss_account_doc_string,
-                                     option->guile_option);
-}
-
-/*******************************************************************\
- * gnc_currency_accounting_option_value_get_method                 *
- *   get the currency accounting method of the option as a symbol  *
- *                                                                 *
- * Args: option_value - option value to get method of              *
- * Return: SCM value                                               *
-\*******************************************************************/
-SCM
-gnc_currency_accounting_option_value_get_method (SCM option_value)
-{
-    initialize_getters ();
-
-    return scm_call_1 (getters.currency_accounting_option_method,
-                       option_value);
-}
-
-/*******************************************************************\
- * gnc_currency_accounting_option_value_get_book_currency          *
- *   get the book-currency if that is the currency accounting      *
- *   method of the option as a symbol                              *
- *                                                                 *
- * Args: option_value - option value to get method of              *
- * Return: SCM value                                               *
-\*******************************************************************/
-SCM
-gnc_currency_accounting_option_value_get_book_currency (SCM option_value)
-{
-    initialize_getters ();
-
-    return scm_call_1 (getters.currency_accounting_option_book_currency,
-                       option_value);
-}
-
-/*******************************************************************\
- * gnc_currency_accounting_option_value_get_default_policy         *
- *   get the default policy if book-currency is the currency       *
- *   accounting  method of the option as a symbol                  *
- *                                                                 *
- * Args: option_value - option value to get method of              *
- * Return: SCM value                                               *
-\*******************************************************************/
-SCM
-gnc_currency_accounting_option_value_get_default_policy (SCM option_value)
-{
-    initialize_getters ();
-
-    return scm_call_1
-        (getters.currency_accounting_option_selected_default_policy,
-          option_value);
-}
-
-/*******************************************************************\
- * gnc_currency_accounting_option_value_get_default_account        *
- *   get the default gain/loss account if book-currency is the     *
- *   currency accounting method, if one is specified, of the       *
- *   option as a symbol                                            *
- *                                                                 *
- * Args: option_value - option value to get method of              *
- * Return: SCM value                                               *
-\*******************************************************************/
-SCM
-gnc_currency_accounting_option_value_get_default_account (SCM option_value)
-{
-    initialize_getters ();
-
-    return scm_call_1
-        (getters.currency_accounting_option_selected_default_gain_loss_account,
-          option_value);
-}
-
 static int
 find_option_db_with_selectable_pred (gpointer key, gpointer value, gpointer data)
 {
diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h
index c059e1896..23742f011 100644
--- a/libgnucash/app-utils/option-util.h
+++ b/libgnucash/app-utils/option-util.h
@@ -201,89 +201,6 @@ guint32 gnc_option_db_lookup_color_option_argb (GNCOptionDB *odb,
                                                 const char *name,
                                                 guint32 default_value);
 
-void gnc_option_db_unregister_change_callback_id(GNCOptionDB *odb,
-        SCM callback_id);
-
-char * gnc_option_section(GNCOption *option);
-char * gnc_option_name(GNCOption *option);
-char * gnc_option_type(GNCOption *option);
-char * gnc_option_sort_tag(GNCOption *option);
-char * gnc_option_documentation(GNCOption *option);
-SCM    gnc_option_getter(GNCOption *option);
-SCM    gnc_option_setter(GNCOption *option);
-SCM    gnc_option_default_getter(GNCOption *option);
-SCM    gnc_option_get_option_data(GNCOption *option);
-
-int    gnc_option_num_permissible_values(GNCOption *option);
-int    gnc_option_permissible_value_index(GNCOption *option, SCM value);
-SCM    gnc_option_permissible_value(GNCOption *option, int index);
-char * gnc_option_permissible_value_name(GNCOption *option, int index);
-char * gnc_option_permissible_value_description(GNCOption *option, int index);
-
-gboolean gnc_option_show_time(GNCOption *option);
-
-gboolean gnc_option_multiple_selection(GNCOption *option);
-GList * gnc_option_get_account_type_list(GNCOption *option);
-
-gboolean gnc_option_get_range_info(GNCOption *option,
-                                   double *lower_bound,
-                                   double *upper_bound,
-                                   int    *num_decimals,
-                                   double *step_size);
-
-gdouble  gnc_option_color_range(GNCOption *option);
-gdouble  gnc_option_use_alpha(GNCOption *option);
-gboolean gnc_option_get_color_info(GNCOption *option,
-                                   gboolean use_default,
-                                   gdouble *red,
-                                   gdouble *green,
-                                   gdouble *blue,
-                                   gdouble *alpha);
-
-void gnc_option_call_option_widget_changed_proc (GNCOption *option);
-
-void gnc_option_set_default(GNCOption *option);
-
-guint gnc_option_db_num_sections(GNCOptionDB *odb);
-
-const char * gnc_option_section_name(GNCOptionSection *section);
-guint  gnc_option_section_num_options(GNCOptionSection *section);
-
-GNCOptionSection * gnc_option_db_get_section(GNCOptionDB *odb, gint i);
-
-GNCOption * gnc_get_option_section_option(GNCOptionSection *section, int i);
-
-GNCOption * gnc_option_db_get_option_by_name(GNCOptionDB *odb,
-        const char *section_name,
-        const char *name);
-
-void     gnc_option_db_clean(GNCOptionDB *odb);
-
-gboolean gnc_option_db_get_changed(GNCOptionDB *odb);
-GList* gnc_option_db_commit(GNCOptionDB *odb);
-
-char * gnc_option_db_get_default_section(GNCOptionDB *odb);
-
-SCM gnc_option_db_lookup_option(GNCOptionDB *odb,
-                                const char *section,
-                                const char *name,
-                                SCM default_value);
-
-gboolean gnc_option_db_lookup_boolean_option(GNCOptionDB *odb,
-        const char *section,
-        const char *name,
-        gboolean default_value);
-
-char * gnc_option_db_lookup_string_option(GNCOptionDB *odb,
-        const char *section,
-        const char *name,
-        const char *default_value);
-
-gdouble gnc_option_db_lookup_number_option(GNCOptionDB *odb,
-        const char *section,
-        const char *name,
-        gdouble default_value);
-
 gboolean gnc_option_db_set_option(GNCOptionDB *odb,
                                   const char *section,
                                   const char *name,
@@ -313,16 +230,6 @@ SCM gnc_date_option_value_get_relative (SCM option_value);
 char * gnc_plot_size_option_value_get_type (SCM option_value);
 gdouble gnc_plot_size_option_value_get_value (SCM option_value);
 
-char * gnc_currency_accounting_option_currency_documentation (GNCOption *option);
-SCM gnc_currency_accounting_option_get_default_currency (GNCOption *option);
-char * gnc_currency_accounting_option_policy_documentation (GNCOption *option);
-SCM gnc_currency_accounting_option_get_default_policy (GNCOption *option);
-char * gnc_currency_accounting_option_gain_loss_account_documentation (GNCOption *option);
-SCM gnc_currency_accounting_option_value_get_method (SCM option_value);
-SCM gnc_currency_accounting_option_value_get_book_currency (SCM option_value);
-SCM gnc_currency_accounting_option_value_get_default_policy (SCM option_value);
-SCM gnc_currency_accounting_option_value_get_default_account (SCM option_value);
-
 void gnc_option_db_set_option_selectable_by_name (SCM guile_options,
                                                   const char *section,
                                                   const char *name,
diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm
index 0b64b1f81..2188c4eaa 100644
--- a/libgnucash/app-utils/options.scm
+++ b/libgnucash/app-utils/options.scm
@@ -141,11 +141,6 @@
 
 (define gnc:*option-section-accounts* OPTION-SECTION-ACCOUNTS)
 (define gnc:*option-name-trading-accounts* OPTION-NAME-TRADING-ACCOUNTS)
-(define gnc:*option-name-currency-accounting* OPTION-NAME-CURRENCY-ACCOUNTING)
-(define gnc:*option-name-book-currency* OPTION-NAME-BOOK-CURRENCY)
-(define gnc:*option-name-default-gains-policy* OPTION-NAME-DEFAULT-GAINS-POLICY)
-(define gnc:*option-name-default-gain-loss-account*
-  OPTION-NAME-DEFAULT-GAINS-LOSS-ACCT-GUID)
 
 (define (gnc:option-get-value book category key)
   (define acc (if (pair? key) cons list))
@@ -1529,262 +1524,6 @@ the option '~a'."))
 (define (gnc:dateformat-get-format v)
   (cadddr v))
 
-(define (gnc:make-currency-accounting-option
-         section
-         name
-         sort-tag
-         radiobutton-documentation-string
-         default-radiobutton-value
-         ok-radiobutton-values
-         book-currency-documentation-string
-         default-book-currency-value
-         default-cap-gains-policy-documentation-string
-         default-cap-gains-policy-value
-         default-gains-loss-account-documentation-string
-        )
-  (define (legal-val val p-vals)
-    (cond ((null? p-vals) #f)
-          ((not (symbol? val)) #f)
-          ((eq? val (vector-ref (car p-vals) 0)) #t)
-          (else (legal-val val (cdr p-vals)))))
-
-  (define (currency-lookup currency-string)
-    (if (string? currency-string)
-        (gnc-commodity-table-lookup
-         (gnc-commodity-table-get-table (gnc-get-current-book))
-         GNC_COMMODITY_NS_CURRENCY currency-string)
-        #f))
-
-  (define (currency? val)
-    (gnc-commodity-is-currency (currency-lookup val)))
-
-  (define (vector-strings p-vals)
-    (if (null? p-vals)
-        '()
-        (cons (vector-ref (car p-vals) 1)
-              (cons (vector-ref (car p-vals) 2)
-                    (vector-strings (cdr p-vals))))))
-
-  (define (currency->scm currency)
-    (if (string? currency)
-        currency
-        (gnc-commodity-get-mnemonic currency)))
-
-  (define (scm->currency currency)
-    (currency-lookup currency))
-
-  (define (valid-gains-loss-account? book-currency gains-loss-account-guid)
-    ;; xaccAccountLookup returns Account if guid valid otherwise NULL; also must
-    ;; be in book-currency, income or expense, and not placeholder nor hidden
-    (let* ((account (xaccAccountLookup gains-loss-account-guid
-                                       (gnc-get-current-book))))
-      (and account
-           (not (null? account))
-           (not (xaccAccountIsHidden account))
-           (not (xaccAccountGetPlaceholder account))
-           (memv (xaccAccountGetType account)
-                 (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE))
-           (gnc-commodity-equal
-            (currency-lookup book-currency)
-            (xaccAccountGetCommodity account)))))
-
-  (let* ((value (if (eq? 'book-currency default-radiobutton-value)
-                    (list default-radiobutton-value
-                          default-book-currency-value
-                          default-cap-gains-policy-value)
-                    (list default-radiobutton-value)))
-         (value->string (lambda ()
-                          (string-append "'" (gnc:value->string
-                                               (car value)))))
-         (trading-accounts-path (list gnc:*option-section-accounts*
-                                      gnc:*option-name-trading-accounts*))
-         (book-currency-path (list gnc:*option-section-accounts*
-                                   gnc:*option-name-book-currency*))
-         (gains-policy-path (list gnc:*option-section-accounts*
-                                  gnc:*option-name-default-gains-policy*))
-         (gains-loss-account-path (list gnc:*option-section-accounts*
-                                  gnc:*option-name-default-gain-loss-account*)))
-    (gnc:make-option
-     section name sort-tag 'currency-accounting
-     radiobutton-documentation-string
-     (lambda () value) ;; getter
-     (lambda (x)
-       (if (legal-val (car x) ok-radiobutton-values)
-           (set! value x)
-           (gnc:error "Illegal Radiobutton option set"))) ;;setter
-     (lambda () (if (eq? 'book-currency default-radiobutton-value)
-                    (list default-radiobutton-value
-                          default-book-currency-value
-                          default-cap-gains-policy-value)
-                    (list default-radiobutton-value))) ;; default-getter
-     (gnc:restore-form-generator value->string)
-     (lambda (b p) ;; scm->kvp
-       (case (car value)
-         ((book-currency)
-          ;; Currency = selected currency
-          (qof-book-set-option b (currency->scm (cadr value))
-                               book-currency-path)
-          ;; Default Gains Policy = selected policy
-          (qof-book-set-option b (symbol->string (caddr value))
-                               gains-policy-path)
-          ;; Default Gains Account = if selected, selected account
-          (if (car (cdddr value))
-              (qof-book-set-option b (car (cdddr value))
-                                   gains-loss-account-path)))
-         ((trading)
-          ;; Use Trading Accounts = "t"
-          (qof-book-set-option b "t" trading-accounts-path))))
-     (lambda (b p) ;; kvp->scm
-       (let* ((trading-option-path-kvp?
-                       (qof-book-get-option b trading-accounts-path))
-              (trading? (and trading-option-path-kvp?
-                             (string=? "t" trading-option-path-kvp?)))
-              (book-currency #f)
-              (cap-gains-policy #f)
-              (gains-loss-account-guid #f)
-              (v (if trading?
-                     'trading
-                     (let* ((book-currency-option-path-kvp?
-                                 (qof-book-get-option
-                                     b book-currency-path))
-                            (gains-policy-option-path-kvp?
-                                 (qof-book-get-option
-                                     b gains-policy-path))
-                            (gains-loss-account-option-path-kvp?
-                                 (qof-book-get-option
-                                     b gains-loss-account-path))
-                            (book-currency?
-                               (if (and book-currency-option-path-kvp?
-                                        gains-policy-option-path-kvp?
-                                        (string?
-                                           book-currency-option-path-kvp?)
-                                        (string?
-                                           gains-policy-option-path-kvp?)
-                                        (if book-currency-option-path-kvp?
-                                            (currency?
-                                              book-currency-option-path-kvp?))
-                                        (if gains-policy-option-path-kvp?
-                                            (gnc-valid-policy-name
-                                              gains-policy-option-path-kvp?)))
-                                   (begin
-                                     (set! book-currency
-                                               book-currency-option-path-kvp?)
-                                     (set! cap-gains-policy
-                                               gains-policy-option-path-kvp?)
-                                     (if gains-loss-account-option-path-kvp?
-                                         (if (valid-gains-loss-account?
-                                               book-currency
-                                               gains-loss-account-option-path-kvp?)
-                                             (set! gains-loss-account-guid
-                                               gains-loss-account-option-path-kvp?)))
-                                     #t)
-                                    #f)))
-                           (if book-currency?
-                               'book-currency
-                               'neither)))))
-             (if (and v (symbol? v) (legal-val v ok-radiobutton-values))
-                 (set! value (cons v (if (eq? 'book-currency v)
-                                         (list (scm->currency book-currency)
-                                               (string->symbol cap-gains-policy)
-                                               gains-loss-account-guid)
-                                         '())))
-                 (set! value (list 'neither)))))
-     (lambda (x) ;; value validator
-       (cond
-        ((not (list? x))
-         (list #f "value not a list"))
-        ((not (legal-val (car x) ok-radiobutton-values))
-         (list #f "radiobutton-option: illegal choice"))
-        ((not (eq? 'book-currency (car x)))
-         (list #t x))
-        ((not (currency? (currency->scm (cadr x))))
-         (list #f "currency-option: illegal value"))
-        ((not (gnc-valid-policy-name (symbol->string (caddr x))))
-         (list #f "cap-gains-policy-option: illegal value"))
-        ((not (car (cdddr x)))
-         (list #t x))
-        ((not (valid-gains-loss-account? (currency->scm (cadr x))
-                                         (car (cdddr x))))
-         (list #f "gains-loss-account-option: illegal value"))
-        (else
-         (list #t x))))
-     (vector book-currency-documentation-string
-             default-book-currency-value
-             default-cap-gains-policy-documentation-string
-             default-cap-gains-policy-value
-             default-gains-loss-account-documentation-string)
-     (vector (lambda () (length ok-radiobutton-values))
-             (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 0))
-             (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 1))
-             (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 2))
-             (lambda (x)
-               (gnc:multichoice-list-lookup ok-radiobutton-values x)))
-     (lambda () (vector-strings ok-radiobutton-values))
-     #f)))
-
-(define (gnc:get-currency-accounting-option-data-curr-doc-string option-data)
-  (vector-ref option-data 0))
-
-(define (gnc:get-currency-accounting-option-data-default-curr option-data)
-  (vector-ref option-data 1))
-
-(define (gnc:get-currency-accounting-option-data-policy-doc-string option-data)
-  (vector-ref option-data 2))
-
-(define (gnc:get-currency-accounting-option-data-policy-default option-data)
-  (vector-ref option-data 3))
-
-(define (gnc:get-currency-accounting-option-data-gain-loss-account-doc-string option-data)
-  (vector-ref option-data 4))
-
-(define (gnc:currency-accounting-option-get-curr-doc-string option)
-  (if (eq? (gnc:option-type option) 'currency-accounting)
-      (gnc:get-currency-accounting-option-data-curr-doc-string
-        (gnc:option-data option))
-      (gnc:error "Not a currency accounting option")))
-
-(define (gnc:currency-accounting-option-get-default-curr option)
-  (if (eq? (gnc:option-type option) 'currency-accounting)
-      (gnc:get-currency-accounting-option-data-default-curr
-        (gnc:option-data option))
-      (gnc:error "Not a currency accounting option")))
-
-(define (gnc:currency-accounting-option-get-policy-doc-string option)
-  (if (eq? (gnc:option-type option) 'currency-accounting)
-      (gnc:get-currency-accounting-option-data-policy-doc-string
-        (gnc:option-data option))
-      (gnc:error "Not a currency accounting option")))
-
-(define (gnc:currency-accounting-option-get-default-policy option)
-  (if (eq? (gnc:option-type option) 'currency-accounting)
-      (gnc:get-currency-accounting-option-data-policy-default
-        (gnc:option-data option))
-      (gnc:error "Not a currency accounting option")))
-
-(define (gnc:currency-accounting-option-get-gain-loss-account-doc-string option)
-  (if (eq? (gnc:option-type option) 'currency-accounting)
-      (gnc:get-currency-accounting-option-data-gain-loss-account-doc-string
-        (gnc:option-data option))
-      (gnc:error "Not a currency accounting option")))
-
-(define (gnc:currency-accounting-option-selected-method option-value)
-  (car option-value))
-
-(define (gnc:currency-accounting-option-selected-currency option-value)
-  (if (eq? (car option-value) 'book-currency)
-      (cadr option-value)
-      #f))
-
-(define (gnc:currency-accounting-option-selected-policy option-value)
-  (if (eq? (car option-value) 'book-currency)
-      (caddr option-value)
-      #f))
-
-(define (gnc:currency-accounting-option-selected-gain-loss-account option-value)
-  (if (eq? (car option-value) 'book-currency)
-      (car (cdddr option-value))
-      #f))
-
 ;; Create a new options database
 (define (gnc:new-options)
   (define option-hash (make-hash-table 23))
@@ -1968,11 +1707,8 @@ the option '~a'."))
              (default-value (gnc:option-default-value option))
              (section (gnc:option-section option))
              (name (gnc:option-name option)))
-;;         (gnc:debug "value: " value "; default: " default-value
-;;                   "; section: " section "; name: " name)
          (if (not (equal? value default-value))
              (let ((save-fcn (gnc:option-scm->kvp option)))
-;;               (gnc:debug "save-fcn: " save-fcn)
                (if save-fcn
                    (save-fcn book (list section name)))))))))
 
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index deb1da2e9..0bbf98d3c 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -12,7 +12,7 @@ set(APP_UTILS_TEST_INCLUDE_DIRS
 
 set(APP_UTILS_TEST_LIBS gnc-app-utils gnc-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)
+set(test_app_utils_SOURCES test-app-utils.c test-option-util.cpp)
 
 macro(add_app_utils_test _TARGET _SOURCE_FILES)
   gnc_add_test(${_TARGET} "${_SOURCE_FILES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS)
diff --git a/libgnucash/app-utils/test/test-app-utils.c b/libgnucash/app-utils/test/test-app-utils.c
index 17289e77f..ae63a01cf 100644
--- a/libgnucash/app-utils/test/test-app-utils.c
+++ b/libgnucash/app-utils/test/test-app-utils.c
@@ -35,7 +35,6 @@ guile_main (void *closure, int argc, char **argv)
     scm_c_use_module("gnucash app-utils");
 
     test_suite_option_util ();
-    test_suite_gnc_ui_util ();
     retval = g_test_run ();
 
     exit (retval);
diff --git a/libgnucash/app-utils/test/test-gnc-ui-util.c b/libgnucash/app-utils/test/test-gnc-ui-util.c
deleted file mode 100644
index 2be3250a5..000000000
--- a/libgnucash/app-utils/test/test-gnc-ui-util.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/********************************************************************
- * test-gnc-ui-util.c: GLib g_test test suite for gnc-ui-util.c.	    *
- * Copyright 2015 Alex Aycinena <alex.aycinena at gmail.com>		    *
- *                                                                  *
- * This program is free software; you can redistribute it and/or    *
- * modify it under the terms of the GNU General Public License as   *
- * published by the Free Software Foundation; either version 2 of   *
- * the License, or (at your option) any later version.              *
- *                                                                  *
- * This program is distributed in the hope that it will be useful,  *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
- * GNU General Public License for more details.                     *
- *                                                                  *
- * You should have received a copy of the GNU General Public License*
- * along with this program; if not, you can retrieve it from        *
- * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html            *
- * or contact:                                                      *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
- ********************************************************************/
-
-#include <config.h>
-#include <glib.h>
-#include <unittest-support.h>
-#include <qof.h>
-#include "test-engine-stuff.h"
-
-#include "../gnc-ui-util.h"
-
-static const gchar *suitename = "/app-utils/gnc-ui-util";
-void test_suite_gnc_ui_util (void);
-
-typedef struct
-{
-    QofBook *book;
-    GSList *hdlrs;
-} Fixture;
-
-/* Expose a mostly-private QofInstance function to load options into
- * the Book.
- */
-/*extern KvpFrame *qof_instance_get_slots (const QofInstance*); */
-
-static void
-setup (Fixture *fixture, gconstpointer pData)
-{
-    fixture->book = qof_book_new ();
-    fixture->hdlrs = NULL;
-}
-
-static void
-teardown (Fixture *fixture, gconstpointer pData)
-{
-    qof_book_destroy (fixture->book);
-    g_slist_free_full (fixture->hdlrs, test_free_log_handler);
-    test_clear_error_list();
-}
-
-static void
-test_book_use_book_currency( Fixture *fixture, gconstpointer pData )
-{
-    const gchar *cur;
-    const gchar *pol;
-    Account *acct, *acc;
-
-    g_test_message( "Testing with no currency accounting method selected" );
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert (acct == NULL );
-    g_assert( !gnc_book_use_book_currency ( fixture-> book ));
-
-    g_test_message( "Testing with trading accounts set to true - t" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "trading-accts", "t",
-		      NULL);
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert (acct == NULL );
-    g_assert( !gnc_book_use_book_currency ( fixture-> book ));
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with valid book-currency but default-gains-policy set to nonsense and default-gain-loss-account-guid set to random valid acct" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "USD",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "random",
-		      NULL);
-    acc = get_random_account( fixture-> book );
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)),
-		      NULL);
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert (acct == NULL );
-    g_assert( !gnc_book_use_book_currency ( fixture-> book ));
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-/*    g_test_message( "Testing with valid default-gains-policy but book-currency set to nonsense and default-gain-loss-account-guid set to random valid acct" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "myMoney",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "fifo",
-		      NULL);
-    acc = get_random_account( fixture-> book );
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)),
-		      NULL);
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert (acct == NULL );
-    g_assert( !gnc_book_use_book_currency ( fixture-> book ));
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with book-currency and default-gains-policy set to nonsense and default-gain-loss-account-guid set to random valid acct" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "myMoney",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "random",
-		      NULL);
-    acc = get_random_account( fixture-> book );
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)),
-		      NULL);
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert (acct == NULL );
-    g_assert( !gnc_book_use_book_currency ( fixture-> book ));
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with book-currency set and no default-gains-policy and default-gain-loss-account-guid set to random valid acct" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "USD",
-		      NULL);
-    acc = get_random_account( fixture-> book );
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)),
-		      NULL);
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert (acct == NULL );
-    g_assert( !gnc_book_use_book_currency ( fixture-> book ));
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with default-gains-policy set and no book-currency and default-gain-loss-account-guid set to random valid acct" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "fifo",
-		      NULL);
-    acc = get_random_account( fixture-> book );
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)),
-		      NULL);
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert (acct == NULL );
-    g_assert( !gnc_book_use_book_currency ( fixture-> book ));
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with book-currency, default-gains-policy and default-gain-loss-account-guid set to valid values and with trading accounts set to true - t" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "trading-accts", "t",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "USD",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "fifo",
-		      NULL);
-    acc = get_random_account( fixture-> book );
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)),
-		      NULL);
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert (acct == NULL );
-    g_assert( !gnc_book_use_book_currency ( fixture-> book ));
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-     g_test_message( "Testing with book-currency, default-gains-policy and default-gain-loss-account-guid set to valid values and no trading accounts flag" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "USD",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "fifo",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)),
-		      NULL);
-    cur = gnc_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , "USD" );
-    pol = gnc_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , "fifo" );
-    acct = gnc_book_get_default_gain_loss_acct ( fixture-> book );
-    g_assert ( xaccAccountEqual(acct, acc, TRUE) );
-    g_assert( gnc_book_use_book_currency ( fixture-> book ));
-    qof_book_commit_edit (fixture->book); */
-}
-
-void
-test_suite_gnc_ui_util (void)
-{
-    GNC_TEST_ADD( suitename, "use book-currency", Fixture, NULL, setup, test_book_use_book_currency, teardown );
-
-}
diff --git a/libgnucash/engine/engine-helpers.c b/libgnucash/engine/engine-helpers.c
index f6ac9aeb7..8a1395e03 100644
--- a/libgnucash/engine/engine-helpers.c
+++ b/libgnucash/engine/engine-helpers.c
@@ -188,22 +188,6 @@ gnc_book_option_num_field_source_change (gboolean num_action)
     g_hook_list_invoke(bo_final_hook_list, TRUE);
 }
 
-/** Calls registered callbacks when book_currency book option changes so that
-  * registers/reports can update themselves */
-void
-gnc_book_option_book_currency_selected (gboolean use_book_currency)
-{
-    GHookList *hook_list;
-    const gchar *key = OPTION_NAME_BOOK_CURRENCY;
-
-    g_once(&bo_init_once, bo_init, NULL);
-
-    hook_list = g_hash_table_lookup(bo_callback_hash, key);
-    if (hook_list != NULL)
-        g_hook_list_marshal(hook_list, TRUE, bo_call_hook, &use_book_currency);
-    g_hook_list_invoke(bo_final_hook_list, TRUE);
-}
-
 void
 gnc_book_option_register_cb (gchar *key, GncBOCb func, gpointer user_data)
 {
diff --git a/libgnucash/engine/engine-helpers.h b/libgnucash/engine/engine-helpers.h
index 517d4e148..29a504ce9 100644
--- a/libgnucash/engine/engine-helpers.h
+++ b/libgnucash/engine/engine-helpers.h
@@ -74,11 +74,6 @@ void gnc_set_num_action (Transaction *trans, Split *split,
 void
 gnc_book_option_num_field_source_change (gboolean num_action);
 
-/** Calls registered callbacks when book_currency book option changes so that
-  * registers/reports can update themselves */
-void
-gnc_book_option_book_currency_selected (gboolean use_book_currency);
-
 /** Registers callbacks to be called when the book option changes for the
   * specified book option key */
 void
diff --git a/libgnucash/engine/gnc-features.c b/libgnucash/engine/gnc-features.c
index 10ea1009c..7521c55e0 100644
--- a/libgnucash/engine/gnc-features.c
+++ b/libgnucash/engine/gnc-features.c
@@ -44,7 +44,6 @@ static gncFeature known_features[] =
     { GNC_FEATURE_CREDIT_NOTES, "Customer and vendor credit notes (requires at least GnuCash 2.5.0)" },
     { GNC_FEATURE_NUM_FIELD_SOURCE, "User specifies source of 'num' field'; either transaction number or split action (requires at least GnuCash 2.5.0)" },
     { 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 (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)"},
diff --git a/libgnucash/engine/gnc-features.h b/libgnucash/engine/gnc-features.h
index 030c02531..366de78ca 100644
--- a/libgnucash/engine/gnc-features.h
+++ b/libgnucash/engine/gnc-features.h
@@ -48,7 +48,6 @@ extern "C" {
 #define GNC_FEATURE_CREDIT_NOTES "Credit Notes"
 #define GNC_FEATURE_NUM_FIELD_SOURCE "Number Field Source"
 #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"
 #define GNC_FEATURE_SQLITE3_ISO_DATES "ISO-8601 formatted date strings in SQLite3 databases."
diff --git a/libgnucash/engine/policy-p.h b/libgnucash/engine/policy-p.h
index 0ca7b04bc..76cd97e0a 100644
--- a/libgnucash/engine/policy-p.h
+++ b/libgnucash/engine/policy-p.h
@@ -63,9 +63,6 @@
 
 struct gncpolicy_s
 {
-    char *name;
-    char *description;
-    char *hint;
     GNCLot * (*PolicyGetLot) (GNCPolicy *, Split *split);
     Split  * (*PolicyGetSplit) (GNCPolicy *, GNCLot *lot);
     void     (*PolicyGetLotOpening) (GNCPolicy *, GNCLot *lot,
diff --git a/libgnucash/engine/policy.c b/libgnucash/engine/policy.c
index 506da094e..fddd7ff57 100644
--- a/libgnucash/engine/policy.c
+++ b/libgnucash/engine/policy.c
@@ -63,47 +63,6 @@
 
 //static QofLogModule log_module = GNC_MOD_LOT;
 
-GList *
-gnc_get_valid_policy_list (void)
-{
-    GList *return_list = NULL;
-
-/*    return_list = g_list_prepend (return_list, xaccGetManualPolicy());
-    return_list = g_list_prepend (return_list, xaccGetAveragePolicy()); */
-    return_list = g_list_prepend (return_list, xaccGetLIFOPolicy());
-    return_list = g_list_prepend (return_list, xaccGetFIFOPolicy());
-
-    return return_list;
-}
-
-gboolean
-gnc_valid_policy_name (const gchar *policy_name)
-{
-    GList *list_of_policies = NULL;
-    gboolean ret_val = FALSE;
-
-    if (!policy_name)
-        return ret_val;
-
-    list_of_policies = gnc_get_valid_policy_list();
-    if (!list_of_policies)
-    {
-        return ret_val;
-    }
-    else
-    {
-        GList *l = NULL;
-        for (l = list_of_policies; l != NULL; l = l->next)
-        {
-            GNCPolicy *list_pcy = l->data;
-            if (g_strcmp0(PolicyGetName (list_pcy), policy_name) == 0)
-                ret_val = TRUE;
-        }
-        g_list_free(list_of_policies);
-        return ret_val;
-    }
-}
-
 static Split *
 DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse)
 {
@@ -189,26 +148,6 @@ donext:
     return NULL;
 }
 
-const char *
-PolicyGetName (const GNCPolicy *pcy)
-{
-    if(!pcy) return NULL;
-    return pcy->name;
-}
-
-const char *
-PolicyGetDescription (const GNCPolicy *pcy)
-{
-    if(!pcy) return NULL;
-    return pcy->description;
-}
-const char *
-PolicyGetHint (const GNCPolicy *pcy)
-{
-    if(!pcy) return NULL;
-    return pcy->hint;
-}
-
 /* ============================================================== */
 
 static GNCLot *
@@ -255,9 +194,6 @@ xaccGetFIFOPolicy (void)
     if (!pcy)
     {
         pcy = g_new (GNCPolicy, 1);
-        pcy->name = FIFO_POLICY;
-        pcy->description = FIFO_POLICY_DESC;
-        pcy->hint = FIFO_POLICY_HINT;
         pcy->PolicyGetLot = FIFOPolicyGetLot;
         pcy->PolicyGetSplit = FIFOPolicyGetSplit;
         pcy->PolicyGetLotOpening = FIFOPolicyGetLotOpening;
@@ -304,23 +240,4 @@ LIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split)
     return (split == opening_split);
 }
 
-GNCPolicy *
-xaccGetLIFOPolicy (void)
-{
-    static GNCPolicy *pcy = NULL;
-
-    if (!pcy)
-    {
-        pcy = g_new (GNCPolicy, 1);
-        pcy->name = LIFO_POLICY;
-        pcy->description = LIFO_POLICY_DESC;
-        pcy->hint = LIFO_POLICY_HINT;
-        pcy->PolicyGetLot = LIFOPolicyGetLot;
-        pcy->PolicyGetSplit = LIFOPolicyGetSplit;
-        pcy->PolicyGetLotOpening = LIFOPolicyGetLotOpening;
-        pcy->PolicyIsOpeningSplit = LIFOPolicyIsOpeningSplit;
-    }
-    return pcy;
-}
-
 /* =========================== END OF FILE ======================= */
diff --git a/libgnucash/engine/policy.h b/libgnucash/engine/policy.h
index 71e4c0ed8..012172d06 100644
--- a/libgnucash/engine/policy.h
+++ b/libgnucash/engine/policy.h
@@ -43,24 +43,6 @@ extern "C" {
 
 typedef struct gncpolicy_s GNCPolicy;
 
-/** Valid Policy List
- *  Provides a glist of implemented policies.
- *
- *  List must be freed with g_list_free().
- */
-GList * gnc_get_valid_policy_list (void);
-
-/** Valid Policy Name
- *  Uses the Valid Policy List to determine if a policy name is valid.
- */
-gboolean gnc_valid_policy_name (const gchar *policy_name);
-
-const char *PolicyGetName (const GNCPolicy *pcy);
-
-const char *PolicyGetDescription (const GNCPolicy *pcy);
-
-const char *PolicyGetHint (const GNCPolicy *pcy);
-
 /** First-in, First-out Policy
  *  This policy will create FIFO Lots.  FIFO Lots have the following
  *  properties:
@@ -69,24 +51,10 @@ const char *PolicyGetHint (const GNCPolicy *pcy);
  *  -- Splits are added to the lot in date order, with earliest splits
  *     added first.
  *  -- All splits in the lot share the same transaction currency as
- *     the split that opened the lot (if book-currency book option
- *     selected, this will always be book currency).
+ *     the split that opened the lot
  */
 GNCPolicy *xaccGetFIFOPolicy (void);
 
-/** Last-in, Last-out Policy
- *  This policy will create LIFO Lots.  LIFO Lots have the following
- *  properties:
- *  -- The lot is started with the latest posted split that isn't
- *     a part of another lot already.
- *  -- Splits are added to the lot in date order, with latest splits
- *     added first.
- *  -- All splits in the lot share the same transaction currency as
- *     the split that opened the lot (if book-currency book option
- *     selected, this will always be book currency).
- */
-GNCPolicy *xaccGetLIFOPolicy (void);
-
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index c768296ea..53873e8e9 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -70,22 +70,7 @@ enum
     PROP_0,
 //  PROP_ROOT_ACCOUNT,                        /* Table */
 //  PROP_ROOT_TEMPLATE,                       /* Table */
-/*   keep trading accounts property, while adding book-currency, default gains
-     policy and default gains account properties, so that files prior to 2.7 can
-     be read/processed; GUI changed to use all four properties as of 2.7.
-     Trading accounts, on the one hand, and book-currency plus default-gains-
-     policy, and optionally, default gains account, on the other, are mutually
-     exclusive */
     PROP_OPT_TRADING_ACCOUNTS,              /* KVP */
-/*   Book currency and default gains policy properties only apply if currency
-     accounting method selected in GUI is 'book-currency'; both required and
-     both are exclusive with trading accounts */
-    PROP_OPT_BOOK_CURRENCY,                 /* KVP */
-    PROP_OPT_DEFAULT_GAINS_POLICY,          /* KVP */
-/*   Default gains account property only applies if currency accounting method
-     selected in GUI is 'book-currency'; its use is optional but exclusive with
-     trading accounts */
-    PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID,    /* KVP */
     PROP_OPT_AUTO_READONLY_DAYS,            /* KVP */
     PROP_OPT_NUM_FIELD_SOURCE,              /* KVP */
     PROP_OPT_DEFAULT_BUDGET,                /* KVP */
@@ -186,18 +171,6 @@ qof_book_get_property (GObject* object,
         qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
                 str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS});
         break;
-    case PROP_OPT_BOOK_CURRENCY:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
-                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
-        break;
-    case PROP_OPT_DEFAULT_GAINS_POLICY:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
-                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
-        break;
-    case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID:
-        qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
-                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
-        break;
     case PROP_OPT_AUTO_READONLY_DAYS:
         qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
                 str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS});
@@ -241,18 +214,6 @@ qof_book_set_property (GObject      *object,
         qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
                 str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS});
         break;
-    case PROP_OPT_BOOK_CURRENCY:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
-                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY});
-        break;
-    case PROP_OPT_DEFAULT_GAINS_POLICY:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
-                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY});
-        break;
-    case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID:
-        qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
-                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID});
-        break;
     case PROP_OPT_AUTO_READONLY_DAYS:
         qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
                 str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS});
@@ -297,41 +258,6 @@ qof_book_class_init (QofBookClass *klass)
                          NULL,
                          G_PARAM_READWRITE));
 
-    g_object_class_install_property
-    (gobject_class,
-     PROP_OPT_BOOK_CURRENCY,
-     g_param_spec_string("book-currency",
-                         "Select Book Currency",
-                         "The reference currency used to manage multiple-currency "
-                         "transactions when 'book-currency' currency accounting method "
-                         "selected; requires valid default gains/loss policy.",
-                         NULL,
-                         G_PARAM_READWRITE));
-
-    g_object_class_install_property
-    (gobject_class,
-     PROP_OPT_DEFAULT_GAINS_POLICY,
-     g_param_spec_string("default-gains-policy",
-                         "Select Default Gains Policy",
-                         "The default policy to be used to calculate gains/losses on "
-                         "dispositions of currencies/commodities other than "
-                         "'book-currency' when 'book-currency' currency accounting "
-                         "method selected; requires valid book-currency.",
-                         NULL,
-                         G_PARAM_READWRITE));
-
-    g_object_class_install_property
-    (gobject_class,
-     PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID,
-     g_param_spec_boxed("default-gain-loss-account-guid",
-                        "Select Default Gain/Loss Account",
-                        "The default account to be used for calculated gains/losses on "
-                        "dispositions of currencies/commodities other than "
-                        "'book-currency' when 'book-currency' currency accounting "
-                        "method selected; requires valid book-currency.",
-                         GNC_TYPE_GUID,
-                         G_PARAM_READWRITE));
-
     g_object_class_install_property
     (gobject_class,
      PROP_OPT_NUM_FIELD_SOURCE,
@@ -982,52 +908,6 @@ qof_book_normalize_counter_format_internal(const gchar *p,
     return normalized_str;
 }
 
-/** Returns pointer to book-currency name for book, if one exists in the
-  * KVP, or NULL; does not validate contents nor determine if there is a valid
-  * default gain/loss policy, both of which are required, for the
-  * 'book-currency' currency accounting method to apply. Use instead
-  * 'gnc_book_get_book_currency_name' which does these validations. */
-const gchar *
-qof_book_get_book_currency_name (QofBook *book)
-{
-    const gchar *opt = NULL;
-    qof_instance_get (QOF_INSTANCE (book),
-              "book-currency", &opt,
-              NULL);
-    return opt;
-}
-
-/** Returns pointer to default gain/loss policy for book, if one exists in the
-  * KVP, or NULL; does not validate contents nor determine if there is a valid
-  * book-currency, both of which are required, for the 'book-currency'
-  * currency accounting method to apply. Use instead
-  * 'gnc_book_get_default_gains_policy' which does these validations. */
-const gchar *
-qof_book_get_default_gains_policy (QofBook *book)
-{
-    const gchar *opt = NULL;
-    qof_instance_get (QOF_INSTANCE (book),
-              "default-gains-policy", &opt,
-              NULL);
-    return opt;
-}
-
-/** Returns pointer to default gain/loss account GUID for book, if one exists in
-  * the KVP, or NULL; does not validate contents nor determine if there is a
-  * valid book-currency, both of which are required, for the 'book-currency'
-  * currency accounting method to apply. Use instead
-  * 'gnc_book_get_default_gain_loss_acct' which does these validations. */
-GncGUID *
-qof_book_get_default_gain_loss_acct_guid (QofBook *book)
-{
-    GncGUID *guid = NULL;
-    qof_instance_get (QOF_INSTANCE (book),
-              "default-gain-loss-account-guid", &guid,
-              NULL);
-    return guid;
-
-}
-
 /* Determine whether this book uses trading accounts */
 gboolean
 qof_book_use_trading_accounts (const QofBook *book)
diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h
index f48ed9400..b4401382b 100644
--- a/libgnucash/engine/qofbook.h
+++ b/libgnucash/engine/qofbook.h
@@ -267,27 +267,6 @@ gboolean qof_book_empty(const QofBook *book);
 /** Returns flag indicating whether this book uses trading accounts */
 gboolean qof_book_use_trading_accounts (const QofBook *book);
 
-/** Returns pointer to book-currency name for book, if one exists in the
-  * KVP, or NULL; does not validate contents nor determine if there is a valid
-  * default gain/loss policy, both of which are required, for the
-  * 'book-currency' currency accounting method to apply. Use instead
-  * 'gnc_book_get_book_currency_name' which does these validations. */
-const gchar * qof_book_get_book_currency_name (QofBook *book);
-
-/** Returns pointer to default gain/loss policy for book, if one exists in the
-  * KVP, or NULL; does not validate contents nor determine if there is a valid
-  * book-currency, both of which are required, for the 'book-currency'
-  * currency accounting method to apply. Use instead
-  * 'gnc_book_get_default_gains_policy' which does these validations. */
-const gchar * qof_book_get_default_gains_policy (QofBook *book);
-
-/** Returns pointer to default gain/loss account GUID for book, if one exists in
-  * the KVP, or NULL; does not validate contents nor determine if there is a
-  * valid book-currency, both of which are required, for the 'book-currency'
-  * currency accounting method to apply. Use instead
-  * 'gnc_book_get_default_gain_loss_acct' which does these validations. */
-GncGUID * qof_book_get_default_gain_loss_acct_guid (QofBook *book);
-
 /** Returns TRUE if the auto-read-only feature should be used, otherwise
  * FALSE. This is just a wrapper on qof_book_get_num_days_autoreadonly() == 0. */
 gboolean qof_book_uses_autoreadonly (const QofBook *book);
diff --git a/libgnucash/engine/qofbookslots.h b/libgnucash/engine/qofbookslots.h
index 10e1d6450..4ec7b0f30 100644
--- a/libgnucash/engine/qofbookslots.h
+++ b/libgnucash/engine/qofbookslots.h
@@ -64,10 +64,6 @@
 
 #define OPTION_SECTION_ACCOUNTS        N_("Accounts")
 #define OPTION_NAME_TRADING_ACCOUNTS   N_("Use Trading Accounts")
-#define OPTION_NAME_CURRENCY_ACCOUNTING   N_("Currency Accounting")
-#define OPTION_NAME_BOOK_CURRENCY      N_("Book Currency")
-#define OPTION_NAME_DEFAULT_GAINS_POLICY      N_("Default Gains Policy")
-#define OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID      N_("Default Gain or Loss Account")
 #define OPTION_NAME_AUTO_READONLY_DAYS N_("Day Threshold for Read-Only Transactions (red line)")
 #define OPTION_NAME_NUM_FIELD_SOURCE   N_("Use Split Action Field for Number")
 
@@ -80,11 +76,6 @@
  * KVP-OPTION-PATH
  * OPTION-SECTION-ACCOUNTS
  * OPTION-NAME-TRADING-ACCOUNTS
- * OPTION-NAME-CURRENCY-ACCOUNTING
- * OPTION-NAME-BOOK-CURRENCY
- * OPTION_NAME_DEFAULT_GAINS_POLICY
- * OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID
- * OPTION-NAME-AUTO-READONLY-DAYS
  * OPTION-NAME_NUM-FIELD-SOURCE
  * OPTION-SECTION-BUDGETING
  * OPTION-NAME-DEFAULT-BUDGET
diff --git a/libgnucash/engine/test/test-qofbook.c b/libgnucash/engine/test/test-qofbook.c
index 794dc5274..67f4a4e3e 100644
--- a/libgnucash/engine/test/test-qofbook.c
+++ b/libgnucash/engine/test/test-qofbook.c
@@ -453,111 +453,6 @@ test_book_use_trading_accounts( Fixture *fixture, gconstpointer pData )
 
 }
 
-static void
-test_book_use_book_currency( Fixture *fixture, gconstpointer pData )
-{
-    const gchar *cur;
-    const gchar *pol;
-    GncGUID *acct;
-    const GncGUID *acct2;
-
-    cur = qof_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = qof_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book );
-    g_assert (acct2 == NULL );
-
-    g_test_message( "Testing with existing trading accounts set to true - t" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "trading-accts", "t",
-		      NULL);
-    cur = qof_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = qof_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book );
-    g_assert (acct2 == NULL );
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with book-currency set and no default-gains-policy or account" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "USD",
-		      NULL);
-    cur = qof_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , "USD" );
-    pol = qof_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , NULL );
-    acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book );
-    g_assert (acct2 == NULL );
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with default-gains-policy set and no book-currency" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "fifo",
-		      NULL);
-    cur = qof_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , NULL );
-    pol = qof_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , "fifo" );
-    acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book );
-    g_assert (acct2 == NULL );
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with book-currency and default-gains-policy set to nonsense" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "myMoney",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "random",
-		      NULL);
-    cur = qof_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , "myMoney" );
-    pol = qof_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , "random" );
-    acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book );
-    g_assert (acct2 == NULL );
-    qof_book_commit_edit (fixture->book);
-
-    qof_book_destroy( fixture->book );
-    fixture->book = qof_book_new();
-
-    g_test_message( "Testing with book-currency, default-gains-policy and default-gains-account set to valid values" );
-    qof_book_begin_edit (fixture->book);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "book-currency", "USD",
-		      NULL);
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gains-policy", "fifo",
-		      NULL);
-    acct = guid_new();
-    qof_instance_set (QOF_INSTANCE (fixture->book),
-		      "default-gain-loss-account-guid", acct,
-		      NULL);
-    cur = qof_book_get_book_currency_name( fixture-> book );
-    g_assert_cmpstr( cur, == , "USD" );
-    pol = qof_book_get_default_gains_policy( fixture-> book );
-    g_assert_cmpstr( pol, == , "fifo" );
-    acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book );
-    g_assert_cmpstr( guid_to_string (acct), == , guid_to_string (acct2) );
-    g_assert (guid_equal(acct, acct2));
-    guid_free (acct);
-    qof_book_commit_edit (fixture->book);
-}
-
 static void
 test_book_get_num_days_autofreeze( Fixture *fixture, gconstpointer pData )
 {
@@ -931,7 +826,6 @@ test_suite_qofbook ( void )
     GNC_TEST_ADD( suitename, "get counter format", Fixture, NULL, setup, test_book_get_counter_format, teardown );
     GNC_TEST_ADD( suitename, "increment and format counter", Fixture, NULL, setup, test_book_increment_and_format_counter, teardown );
     GNC_TEST_ADD( suitename, "use trading accounts", Fixture, NULL, setup, test_book_use_trading_accounts, teardown );
-    GNC_TEST_ADD( suitename, "use book-currency", Fixture, NULL, setup, test_book_use_book_currency, teardown );
     GNC_TEST_ADD( suitename, "get autofreeze days", Fixture, NULL, setup, test_book_get_num_days_autofreeze, teardown );
     GNC_TEST_ADD( suitename, "use split action for num field", Fixture, NULL, setup, test_book_use_split_action_for_num_field, teardown );
     GNC_TEST_ADD( suitename, "mark session dirty", Fixture, NULL, setup, test_book_mark_session_dirty, teardown );

commit aa246d309693c29a7d08e3fe43b7a4274a3b2c26
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 27 13:40:39 2020 -0800

    Make implementation classes and template code visible only in app-utils.
    
    So only GncOption and the GncOptionDB free-function interface are public.
    We don't want to expose template headers widely, it would blow up compilation
    times and might lead to one definition rule violations.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option-impl.cpp
similarity index 98%
copy from libgnucash/app-utils/gnc-option.cpp
copy to libgnucash/app-utils/gnc-option-impl.cpp
index f9ac5bc08..7ab458413 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option-impl.cpp
@@ -1,5 +1,5 @@
 /********************************************************************\
- * gnc-option.cpp -- Application options system                     *
+ * gnc-option-impl.cpp -- Application options system                     *
  * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
@@ -22,7 +22,7 @@
 \********************************************************************/
 
 //#include "options.h"
-#include "gnc-option.hpp"
+#include "gnc-option-impl.hpp"
 #include <gnc-datetime.hpp>
 #include <guid.hpp>
 extern "C"
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option-impl.hpp
similarity index 68%
copy from libgnucash/app-utils/gnc-option.hpp
copy to libgnucash/app-utils/gnc-option-impl.hpp
index 2ceff7cbc..71e9d4924 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option-impl.hpp
@@ -1,5 +1,5 @@
 /********************************************************************\
- * gnc-option.hpp -- Application options system                     *
+ * gnc-option-impl.hpp -- Application options system                     *
  * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
@@ -21,9 +21,10 @@
  *                                                                  *
 \********************************************************************/
 
-#ifndef GNC_OPTION_HPP_
-#define GNC_OPTION_HPP_
+#ifndef GNC_OPTION_IMPL_HPP_
+#define GNC_OPTION_IMPL_HPP_
 
+#include "gnc-option.hpp"
 extern "C"
 {
 #include <config.h>
@@ -42,6 +43,8 @@ extern "C"
 #include <variant>
 #include <iostream>
 
+#include "gnc-option-uitype.hpp"
+
 /*
  * Unused base class to document the structure of the current Scheme option
  * vector, re-expressed in C++. The comment-numbers on the right indicate which
@@ -88,34 +91,6 @@ protected:
 };
 */
 
-enum GncOptionUIType
-{
-    INTERNAL,
-    BOOLEAN,
-    STRING,
-    TEXT,
-    CURRENCY,
-    COMMODITY,
-    MULTICHOICE,
-    DATE,
-    ACCOUNT_LIST,
-    ACCOUNT_SEL,
-    LIST,
-    NUMBER_RANGE,
-    COLOR,
-    FONT,
-    BUDGET,
-    PIXMAP,
-    RADIOBUTTON,
-    DATE_FORMAT,
-    OWNER,
-    CUSTOMER,
-    VENDOR,
-    EMPLOYEE,
-    INVOICE,
-    TAX_TABLE,
-    QUERY,
-};
 
 static const char* commodity_scm_intro{"'(commodity-scm "};
 #ifndef SWIG
@@ -214,6 +189,7 @@ template <typename ValueType>
 class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem
 {
 public:
+    GncOptionValidatedValue<ValueType>() = delete;
     GncOptionValidatedValue<ValueType>(const char* section, const char* name,
                                        const char* key, const char* doc_string,
                                        ValueType value,
@@ -221,7 +197,7 @@ public:
                                        GncOptionUIType ui_type = GncOptionUIType::INTERNAL
         ) :
         OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
+        OptionUIItem{ui_type},
         m_value{value}, m_default_value{value}, m_validator{validator}
         {
             if (!this->validate(value))
@@ -232,7 +208,8 @@ public:
                                        ValueType value,
                                        std::function<bool(ValueType)>validator,
                                        ValueType val_data) :
-        OptionClassifier{section, name, key, doc_string}, m_value{value},
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem{GncOptionUIType::INTERNAL}, m_value{value},
         m_default_value{value}, m_validator{validator}, m_validation_data{val_data}
     {
             if (!this->validate(value))
@@ -279,9 +256,9 @@ template<class OptionValueClass,
          typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
                                                      std::decay_t<OptionValueClass>> &&
                                    !(std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValue<QofInstance*>> ||
+                                     GncOptionValue<const QofInstance*>> ||
                                      std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValidatedValue<QofInstance*>>), int> = 0>
+                                     GncOptionValidatedValue<const QofInstance*>>), int> = 0>
 std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
 {
     oss << opt.get_value();
@@ -296,7 +273,7 @@ operator<< <GncOptionValue<bool>>(std::ostream& oss,
     return oss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*> >, int> = 0>
 inline std::ostream&
 operator<< (std::ostream& oss, const OptType& opt)
 {
@@ -320,9 +297,9 @@ operator<< (std::ostream& oss, const OptType& opt)
 template<class OptionValueClass,
          typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>> &&
                                    !(std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValue<QofInstance*>> ||
+                                     GncOptionValue<const QofInstance*>> ||
                                      std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValidatedValue<QofInstance*>>), int> = 0>
+                                     GncOptionValidatedValue<const QofInstance*>>), int> = 0>
 std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
 {
     std::decay_t<decltype(opt.get_value())> value;
@@ -331,7 +308,7 @@ std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
     return iss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*> >, int> = 0>
 std::istream&
 operator>> (std::istream& iss, OptType& opt)
 {
@@ -367,7 +344,7 @@ operator>> <GncOptionValue<bool>>(std::istream& iss,
     opt.set_value(instr == "#t" ? true : false);
     return iss;
 }
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*>>, int> = 0>
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*>>, int> = 0>
 inline std::ostream&
 gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
 {
@@ -396,7 +373,7 @@ gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
     return oss;
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*>>, int> = 0>
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*>>, int> = 0>
 inline std::istream&
 gnc_option_from_scheme (std::istream& iss, OptType& opt)
 {
@@ -479,6 +456,11 @@ private:
     ValueType m_step;
 };
 
+using GncMultiChoiceOptionEntry = std::tuple<const std::string,
+                                             const std::string,
+                                             const std::string>;
+using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
+
 /** MultiChoice options have a vector of valid options
  * (GncMultiChoiceOptionChoices) and validate the selection as being one of
  * those values. The value is the index of the selected item in the vector. The
@@ -488,10 +470,6 @@ private:
  *
  *
  */
-using GncMultiChoiceOptionEntry = std::tuple<const std::string,
-                                             const std::string,
-                                             const std::string>;
-using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
 
 class GncOptionMultichoiceValue :
     public OptionClassifier, public OptionUIItem
@@ -582,6 +560,10 @@ private:
     GncMultiChoiceOptionChoices m_choices;
 };
 
+using GncOptionAccountList = std::vector<const Account*>;
+
+using GncOptionAccountTypeList = std::vector<GNCAccountType>;
+
 /** Account options
  *
  * Set one or more accounts on which to report, optionally restricted to certain
@@ -599,9 +581,6 @@ private:
 
  */
 
-using GncOptionAccountList = std::vector<const Account*>;
-using GncOptionAccountTypeList = std::vector<GNCAccountType>;
-
 class GncOptionAccountValue :
     public OptionClassifier, public OptionUIItem
 {
@@ -739,9 +718,6 @@ gnc_option_from_scheme(std::istream& iss, OptType& opt)
 /** Date options
  * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
  * flag and a time64, or for legacy purposes the absolute flag and a timespec.
- *
- * The original design allowed custom RelativeDatePeriods, but that facility is
- * unused so we'll go with compiled-in enums.
  */
 /*
 gnc-date-option-show-time? -- option_data[1]
@@ -751,26 +727,6 @@ gnc-date-option-absolute-time m_type == DateTyupe::Absolute
 gnc-date-option-relative-time m_type != DateTyupe::Absolute
  */
 
-enum class RelativeDatePeriod : int
-{
-    ABSOLUTE = -1,
-    TODAY,
-    START_THIS_MONTH,
-    END_THIS_MONTH,
-    START_PREV_MONTH,
-    END_PREV_MONTH,
-    START_CURRENT_QUARTER,
-    END_CURRENT_QUARTER,
-    START_PREV_QUARTER,
-    END_PREV_QUARTER,
-    START_CAL_YEAR,
-    END_CAL_YEAR,
-    START_PREV_YEAR,
-    END_PREV_YEAR,
-    START_ACCOUNTING_PERIOD,
-    END_ACCOUNTING_PERIOD
-};
-
 class GncOptionDateValue : public OptionClassifier, public OptionUIItem
 {
 public:
@@ -835,288 +791,4 @@ operator>> <GncOptionDateValue>(std::istream& iss,
     return opt.in_stream(iss);
 }
 
-using GncOptionVariant = std::variant<GncOptionValue<std::string>,
-                                      GncOptionValue<bool>,
-                                      GncOptionValue<int64_t>,
-                                      GncOptionValue<QofInstance*>,
-                                      GncOptionAccountValue,
-                                      GncOptionMultichoiceValue,
-                                      GncOptionRangeValue<int>,
-                                      GncOptionRangeValue<double>,
-                                      GncOptionValidatedValue<QofInstance*>,
-                                      GncOptionDateValue>;
-
-class GncOption
-{
-public:
-    template <typename OptionType>
-    GncOption(OptionType option) : m_option{option} {}
-
-    template <typename ValueType>
-    GncOption(const char* section, const char* name,
-              const char* key, const char* doc_string,
-              ValueType value,
-              GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
-        m_option{GncOptionValue<ValueType> {
-            section, name, key, doc_string, value, ui_type}} {}
-
-    template <typename ValueType> ValueType get_value() const
-    {
-        return std::visit([](const auto& option)->ValueType {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
-                    return option.get_value();
-                return ValueType {};
-            }, m_option);
-    }
-
-    template <typename ValueType> ValueType get_default_value() const
-    {
-        return std::visit([](const auto& option)->ValueType {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
-                    return option.get_default_value();
-                return ValueType {};
-            }, m_option);
-
-    }
-
-    template <typename ValueType> void set_value(ValueType value)
-    {
-        std::visit([value](auto& option) {
-                if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                      std::decay_t<ValueType>> ||
-                     (std::is_same_v<std::decay_t<decltype(option)>,
-                      GncOptionDateValue> &&
-                      std::is_same_v<std::decay_t<ValueType>,
-                      RelativeDatePeriod>))
-                                 option.set_value(value);
-            }, m_option);
-    }
-    const std::string& get_section() const
-    {
-        return std::visit([](const auto& option)->const std::string& {
-                return option.m_section;
-            }, m_option);
-    }
-    const std::string& get_name() const
-    {
-        return std::visit([](const auto& option)->const std::string& {
-                return option.m_name;
-            }, m_option);
-    }
-    const std::string& get_key() const
-    {
-        return std::visit([](const auto& option)->const std::string& {
-                return option.m_sort_tag;
-            }, m_option);
-    }
-    const std::string& get_docstring() const
-    {
-          return std::visit([](const auto& option)->const std::string& {
-                return option.m_doc_string;
-              }, m_option);
-    }
-    void set_ui_item(GncOptionUIItem* ui_elem)
-    {
-        std::visit([ui_elem](auto& option) {
-                option.set_ui_item(ui_elem);
-            }, m_option);
-    }
-    const GncOptionUIType get_ui_type() const
-    {
-        return std::visit([](const auto& option)->GncOptionUIType {
-                return option.get_ui_type();
-            }, m_option);
-    }
-    GncOptionUIItem* const get_ui_item() const
-    {
-        return std::visit([](const auto& option)->GncOptionUIItem* {
-                return option.get_ui_item();
-            }, m_option);
-    }
-    void make_internal()
-    {
-        std::visit([](auto& option) {
-                option.make_internal();
-            }, m_option);
-    }
-    bool is_changed()
-    {
-        return std::visit([](const auto& option)->bool {
-                return option.is_changed();
-            }, m_option);
-    }
-
-    template<typename ValueType>
-    bool validate(ValueType value) const {
-        return std::visit([value] (const auto& option) -> bool {
-                              if constexpr ((std::is_same_v<std::decay_t<decltype(option)>,
-                                                   GncOptionMultichoiceValue> &&
-                                      std::is_same_v<std::decay_t<ValueType>,
-                                                     std::string>) ||
-                                            std::is_same_v<std::decay_t<decltype(option)>,
-                                            GncOptionValidatedValue<ValueType>>)
-                                        return option.validate(value);
-                       else
-                           return false;
-                   }, m_option);
-    }
-
-    std::size_t num_permissible_values() const {
-        return std::visit([] (const auto& option) -> size_t {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                                    GncOptionMultichoiceValue>)
-                                        return option.num_permissible_values();
-                       else
-                           return size_t_max;
-                   }, m_option);
-    }
-
-    std::size_t permissible_value_index(const std::string& value) const {
-        return std::visit([&value] (const auto& option) -> size_t {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                                    GncOptionMultichoiceValue>)
-                                        return option.permissible_value_index(value);
-                       else
-                           return size_t_max;;
-                   }, m_option);
-    }
-
-    const std::string& permissible_value(std::size_t index) const {
-        return std::visit([index] (const auto& option) -> const std::string& {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                     GncOptionMultichoiceValue>)
-                                        return option.permissible_value(index);
-                       else
-                           return c_empty_string;
-                   }, m_option);
-    }
-
-    const std::string& permissible_value_name(std::size_t index) const {
-        return std::visit([index] (const auto& option) -> const std::string& {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                     GncOptionMultichoiceValue>)
-                                        return option.permissible_value_name(index);
-                       else
-                           return c_empty_string;
-                   }, m_option);
-    }
-
-    const std::string& permissible_value_description(std::size_t index) const {
-        return std::visit([index] (const auto& option) -> const std::string& {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                     GncOptionMultichoiceValue>)
-                                        return option.permissible_value_description(index);
-                       else
-                           return c_empty_string;
-                   }, m_option);
-    }
-
-    std::ostream& out_stream(std::ostream& oss) const
-    {
-            return std::visit([&oss](auto& option) -> std::ostream& {
-            oss << option;
-            return oss;
-        }, m_option);
-    }
-    std::istream& in_stream(std::istream& iss)
-    {
-    return std::visit([&iss](auto& option) -> std::istream& {
-            iss >> option;
-            return iss;
-        }, m_option);
-    }
-
-    std::ostream& to_scheme(std::ostream& oss) const
-    {
-        return std::visit([&oss](auto& option) ->std::ostream& {
-                if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionAccountValue>)
-                        gnc_option_to_scheme(oss, option);
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionMultichoiceValue>)
-                        oss << "'" << option;
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionValue<QofInstance*>> ||
-                     std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionValidatedValue<QofInstance*>>)
-                        gnc_option_to_scheme(oss, option);
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionDateValue>)
-                        oss << "'(" << option << ")";
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                     std::string>)
-                        oss << '"' << option << '"';
-                else
-                    oss << option;
-                return oss;
-            }, m_option);
-    }
-
-    std::istream& from_scheme(std::istream& iss)
-    {
-        return std::visit([&iss](auto& option) ->std::istream& {
-                if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionAccountValue>)
-                        gnc_option_from_scheme(iss, option);
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionMultichoiceValue>)
-                    {
-                         iss.ignore(1, '\'');
-                         iss >> option;
-                    }
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionValue<QofInstance*>> ||
-                     std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionValidatedValue<QofInstance*>>)
-                        gnc_option_from_scheme(iss, option);
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionDateValue>)
-                    {
-                         iss.ignore(2, '(');
-                         iss >> option;
-                         //operator >> clears the trailing ')'
-                    }
-                 else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                     std::string>)
-                    {
-                         iss.ignore(1, '"');
-                         std::string input;
-                         std::getline(iss, input, '"');
-                         option.set_value(input);
-                    }
-                else
-                    iss >> option;
-                return iss;
-            }, m_option);
-    }
-
-    GncOptionVariant& _get_option() const { return m_option; }
-private:
-    inline static const std::string c_empty_string{""};
-    mutable GncOptionVariant m_option;
-};
-
-inline std::ostream&
-operator<<(std::ostream& oss, const GncOption& opt)
-{
-    return opt.out_stream(oss);
-}
-
-inline std::istream&
-operator>>(std::istream& iss, GncOption& opt)
-{
-    return opt.in_stream(iss);
-}
-
-#endif //GNC_OPTION_HPP_
+#endif //GNC_OPTION_IMPL_HPP_
diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp
new file mode 100644
index 000000000..48c75df1e
--- /dev/null
+++ b/libgnucash/app-utils/gnc-option-uitype.hpp
@@ -0,0 +1,55 @@
+/********************************************************************\
+ * gnc-option-uitype.hpp -- UI Control Enum for GncOption           *
+ * Copyright (C) 2020 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+#ifndef GNC_OPTION_UITYPE_HPP__
+#define GNC_OPTION_UITYPE_HPP__
+
+enum GncOptionUIType
+{
+    INTERNAL,
+    BOOLEAN,
+    STRING,
+    TEXT,
+    CURRENCY,
+    COMMODITY,
+    MULTICHOICE,
+    DATE,
+    ACCOUNT_LIST,
+    ACCOUNT_SEL,
+    LIST,
+    NUMBER_RANGE,
+    COLOR,
+    FONT,
+    BUDGET,
+    PIXMAP,
+    RADIOBUTTON,
+    DATE_FORMAT,
+    OWNER,
+    CUSTOMER,
+    VENDOR,
+    EMPLOYEE,
+    INVOICE,
+    TAX_TABLE,
+    QUERY,
+};
+
+#endif // GNC_OPTION_UITYPE_H__
diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index f9ac5bc08..421826271 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -1,6 +1,6 @@
 /********************************************************************\
  * gnc-option.cpp -- Application options system                     *
- * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ * Copyright (C) 2020 John Ralls <jralls at ceridwen.us>               *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -21,273 +21,353 @@
  *                                                                  *
 \********************************************************************/
 
-//#include "options.h"
 #include "gnc-option.hpp"
-#include <gnc-datetime.hpp>
-#include <guid.hpp>
-extern "C"
+#include "gnc-option-impl.hpp"
+#include "gnc-option-uitype.hpp"
+
+template <typename ValueType>
+GncOption::GncOption(const char* section, const char* name,
+                     const char* key, const char* doc_string,
+                     ValueType value, GncOptionUIType ui_type) :
+    m_option{std::make_unique<GncOptionVariant>(GncOptionValue<ValueType> {
+            section, name, key, doc_string, value, ui_type})}
+{
+}
+
+template <typename ValueType> ValueType
+GncOption::get_value() const
+{
+    return std::visit([](const auto option)->ValueType {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
+                                           return option.get_value();
+                          return ValueType {};
+                      }, *m_option);
+}
+
+template <typename ValueType> ValueType
+GncOption::get_default_value() const
+{
+    return std::visit([](const auto option)->ValueType {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
+                                           return option.get_default_value();
+                          return ValueType {};
+                      }, *m_option);
+
+}
+
+template <typename ValueType> void
+GncOption::set_value(ValueType value)
+{
+    std::visit([value](auto& option) {
+                   if constexpr
+                       (std::is_same_v<std::decay_t<decltype(option.get_value())>,
+                        std::decay_t<ValueType>> ||
+                        (std::is_same_v<std::decay_t<decltype(option)>,
+                         GncOptionDateValue> &&
+                         std::is_same_v<std::decay_t<ValueType>,
+                         RelativeDatePeriod>))
+                           option.set_value(value);
+               }, *m_option);
+}
+
+const std::string&
+GncOption::get_section() const
+{
+    return std::visit([](const auto& option)->const std::string& {
+                          return option.m_section;
+                      }, *m_option);
+}
+
+const std::string&
+GncOption::get_name() const
+{
+    return std::visit([](const auto& option)->const std::string& {
+                          return option.m_name;
+                      }, *m_option);
+}
+
+const std::string&
+GncOption::get_key() const
+{
+    return std::visit([](const auto& option)->const std::string& {
+                          return option.m_sort_tag;
+                      }, *m_option);
+}
+
+const std::string&
+GncOption::get_docstring() const
+{
+    return std::visit([](const auto& option)->const std::string& {
+                          return option.m_doc_string;
+                      }, *m_option);
+}
+
+void
+GncOption::set_ui_item(GncOptionUIItem* ui_elem)
+{
+    std::visit([ui_elem](auto& option) {
+                   option.set_ui_item(ui_elem);
+               }, *m_option);
+}
+
+const GncOptionUIType
+GncOption::get_ui_type() const
+{
+    return std::visit([](const auto& option)->GncOptionUIType {
+                          return option.get_ui_type();
+                      }, *m_option);
+}
+
+GncOptionUIItem* const
+GncOption::get_ui_item() const
+{
+    return std::visit([](const auto& option)->GncOptionUIItem* {
+                          return option.get_ui_item();
+                      }, *m_option);
+}
+
+void
+GncOption::make_internal()
 {
-#include "gnc-accounting-period.h"
-#include "gnc-ui-util.h"
+    std::visit([](auto& option) {
+                   option.make_internal();
+               }, *m_option);
 }
 
 bool
-GncOptionAccountValue::validate(const GncOptionAccountList& values) const
+GncOption::is_changed() const noexcept
 {
-    if (values.empty())
-        return false;
-    if (get_ui_type() == GncOptionUIType::ACCOUNT_SEL && values.size() != 1)
-        return false;
-    if (m_allowed.empty())
-        return true;
-    for(auto account : values) {
-        if (std::find(m_allowed.begin(), m_allowed.end(),
-                      xaccAccountGetType(account)) == m_allowed.end())
-            return false;
-    }
-    return true;
+    return std::visit([](const auto& option)->bool {
+                          return option.is_changed();
+                      }, *m_option);
 }
 
-static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+template<typename ValueType> bool
+GncOption::validate(ValueType value) const
+{
+    return std::visit([value] (const auto& option) -> bool {
+                          if constexpr ((std::is_same_v<std::decay_t<decltype(option)>,
+                                         GncOptionMultichoiceValue> &&
+                                         std::is_same_v<std::decay_t<ValueType>,
+                                         std::string>) ||
+                                        std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionValidatedValue<ValueType>>)
+                                           return option.validate(value);
+                          else
+                              return false;
+                      }, *m_option);
+}
 
-static void
-normalize_month(struct tm& now)
+std::size_t
+GncOption::num_permissible_values() const
 {
-    if (now.tm_mon < 0)
-    {
-        now.tm_mon += 12;
-        --now.tm_year;
-    }
-    else if (now.tm_mon > 11)
-    {
-        now.tm_mon -= 12;
-        ++now.tm_year;
-    }
+    return std::visit([] (const auto& option) -> size_t {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionMultichoiceValue>)
+                                           return option.num_permissible_values();
+                          else
+                              return size_t_max;
+                      }, *m_option);
 }
 
-static void
-set_day_and_time(struct tm& now, bool starting)
+std::size_t
+GncOption::permissible_value_index(const std::string& value) const
 {
-    if (starting)
-    {
-        now.tm_hour = now.tm_min = now.tm_sec = 0;
-        now.tm_mday = 1;
-    }
-    else
-    {
-        now.tm_min = now.tm_sec = 59;
-        now.tm_hour = 23;
-        now.tm_mday = days_in_month[now.tm_mon];
-        // Check for Februrary in a leap year
-        if (int year = now.tm_year + 1900; now.tm_mon == 1 &&
-            year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
-            ++now.tm_mday;
-    }
-};
-
-time64
-GncOptionDateValue::get_value() const
+    return std::visit([&value] (const auto& option) -> size_t {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionMultichoiceValue>)
+                                           return option.permissible_value_index(value);
+                          else
+                              return size_t_max;;
+                      }, *m_option);
+}
+
+const std::string&
+GncOption::permissible_value(std::size_t index) const
 {
-    if (m_period == RelativeDatePeriod::ABSOLUTE)
-        return m_date;
-    if (m_period == RelativeDatePeriod::TODAY)
-        return static_cast<time64>(GncDateTime());
-    if (m_period == RelativeDatePeriod::START_ACCOUNTING_PERIOD)
-        return gnc_accounting_period_fiscal_start();
-    if (m_period == RelativeDatePeriod::END_ACCOUNTING_PERIOD)
-        return gnc_accounting_period_fiscal_end();
-
-    GncDateTime now_t;
-    if (m_period == RelativeDatePeriod::TODAY)
-        return static_cast<time64>(now_t);
-    struct tm now{static_cast<tm>(now_t)};
-    struct tm period{static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()))};
-    bool starting =  m_period == RelativeDatePeriod::START_PREV_MONTH ||
-        m_period == RelativeDatePeriod::START_THIS_MONTH ||
-        m_period == RelativeDatePeriod::START_CAL_YEAR ||
-        m_period == RelativeDatePeriod::START_PREV_YEAR ||
-        m_period == RelativeDatePeriod::START_CURRENT_QUARTER ||
-        m_period == RelativeDatePeriod::START_PREV_QUARTER;
-
-    bool prev = m_period == RelativeDatePeriod::START_PREV_YEAR ||
-        m_period == RelativeDatePeriod::END_PREV_YEAR ||
-        m_period == RelativeDatePeriod::START_PREV_QUARTER ||
-        m_period == RelativeDatePeriod::END_PREV_QUARTER;
-
-    if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday)
-    {
-        //No set accounting period, use the calendar year
-        period.tm_mon = 0;
-        period.tm_mday = 0;
-    }
-
-    if (m_period == RelativeDatePeriod::START_CAL_YEAR ||
-        m_period == RelativeDatePeriod::END_CAL_YEAR ||
-        m_period == RelativeDatePeriod::START_PREV_YEAR ||
-        m_period == RelativeDatePeriod::END_PREV_YEAR)
-    {
-
-        if (prev)
-            --now.tm_year;
-        now.tm_mon = starting ? 0 : 11;
-    }
-    else if (m_period == RelativeDatePeriod::START_PREV_QUARTER ||
-             m_period == RelativeDatePeriod::END_PREV_QUARTER ||
-             m_period == RelativeDatePeriod::START_CURRENT_QUARTER ||
-             m_period == RelativeDatePeriod::END_CURRENT_QUARTER)
-    {
-        auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon :
-                       period.tm_mon - now.tm_mon) % 3;
-        now.tm_mon = now.tm_mon - offset;
-        if (prev)
-            now.tm_mon -= 3;
-        if (!starting)
-            now.tm_mon += 2;
-    }
-    else if (m_period == RelativeDatePeriod::START_PREV_MONTH ||
-             m_period == RelativeDatePeriod::END_PREV_MONTH)
-        --now.tm_mon;
-    normalize_month(now);
-    set_day_and_time(now, starting);
-    return static_cast<time64>(GncDateTime(now));
+    return std::visit([index] (const auto& option) -> const std::string& {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionMultichoiceValue>)
+                                           return option.permissible_value(index);
+                          else
+                              return c_empty_string;
+                      }, *m_option);
 }
-static const char* date_type_str[] {"absolute", "relative"};
-static const std::array<const char*, 15> date_period_str
+
+const std::string&
+GncOption::permissible_value_name(std::size_t index) const
 {
-    "today",
-    "start-this-month", "end-this-month",
-    "start-prev-month", "end-prev-month",
-    "start-current-quarter", "end-current-quarter",
-    "start-prev-quarter", "end-prev-quarter",
-    "start-cal-year", "end-cal-year",
-    "start-prev-year", "end-prev-year",
-    "start-prev-fin-year", "end-prev-fin-year"
-};
+    return std::visit([index] (const auto& option) -> const std::string& {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionMultichoiceValue>)
+                                           return option.permissible_value_name(index);
+                          else
+                              return c_empty_string;
+                      }, *m_option);
+}
 
+const std::string&
+GncOption::permissible_value_description(std::size_t index) const
+{
+    return std::visit([index] (const auto& option) -> const std::string& {
+                          if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                        GncOptionMultichoiceValue>)
+                                           return option.permissible_value_description(index);
+                          else
+                              return c_empty_string;
+                      }, *m_option);
+}
 
 std::ostream&
-GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
+GncOption::out_stream(std::ostream& oss) const
 {
-    if (m_period == RelativeDatePeriod::ABSOLUTE)
-        oss << date_type_str[0] << " . " << m_date;
-    else
-        oss << date_type_str[1] << " . " <<
-            date_period_str[static_cast<int>(m_period)];
-    return oss;
+    return std::visit([&oss](auto& option) -> std::ostream& {
+                          oss << option;
+                          return oss;
+                      }, *m_option);
 }
 
 std::istream&
-GncOptionDateValue::in_stream(std::istream& iss)
+GncOption::in_stream(std::istream& iss)
 {
-    char type_str[10]; //The length of both "absolute" and "relative" plus 1.
-    iss.getline(type_str, sizeof(type_str), '.');
-    if(!iss)
-        throw std::invalid_argument("Date Type separator missing");
-    /* strcmp is safe, istream::getline null terminates the buffer. */
-    if (strcmp(type_str, "absolute ") == 0)
-    {
-        time64 time;
-        iss >> time;
-        set_value(time);
-        if (iss.get() != ')')
-            iss.unget();
-    }
-    else if (strcmp(type_str, "relative ") == 0)
-    {
-        std::string period_str;
-        iss >> period_str;
-        if (period_str.back() == ')')
-            period_str.pop_back();
-        auto period = std::find(date_period_str.begin(), date_period_str.end(),
-                                period_str);
-        if (period == date_period_str.end())
-        {
-            std::string err{"Unknown period string in date option: '"};
-            err += period_str;
-            err += "'";
-            throw std::invalid_argument(err);
-        }
-
-        int64_t index = period - date_period_str.begin();
-        set_value(static_cast<RelativeDatePeriod>(index));
-    }
-    else
-    {
-        std::string err{"Unknown date type string in date option: '"};
-        err += type_str;
-        err += "'";
-        throw std::invalid_argument{err};
-    }
-    return iss;
+    return std::visit([&iss](auto& option) -> std::istream& {
+                          iss >> option;
+                          return iss;
+                      }, *m_option);
 }
 
-QofInstance*
-qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
+std::ostream&
+GncOption::to_scheme(std::ostream& oss) const
 {
-    QofIdType qof_type;
-    switch(type)
-    {
-        case GncOptionUIType::CURRENCY:
-        case GncOptionUIType::COMMODITY:
-            qof_type = "Commodity";
-            break;
-        case GncOptionUIType::BUDGET:
-            qof_type = "Budget";
-            break;
-        case GncOptionUIType::OWNER:
-            qof_type = "gncOwner";
-            break;
-        case GncOptionUIType::CUSTOMER:
-            qof_type = "gncCustomer";
-            break;
-        case GncOptionUIType::VENDOR:
-            qof_type = "gncVendor";
-            break;
-        case GncOptionUIType::EMPLOYEE:
-            qof_type = "gncEmployee";
-            break;
-        case GncOptionUIType::INVOICE:
-            qof_type = "gncInvoice";
-            break;
-        case GncOptionUIType::TAX_TABLE:
-            qof_type = "gncTaxtable";
-            break;
-        case GncOptionUIType::QUERY:
-            qof_type = "gncQuery";
-            break;
-        case GncOptionUIType::ACCOUNT_LIST:
-        case GncOptionUIType::ACCOUNT_SEL:
-        default:
-            qof_type = "Account";
-            break;
-    }
-    auto book{gnc_get_current_book()};
-    auto col{qof_book_get_collection(book, qof_type)};
-    return QOF_INSTANCE(qof_collection_lookup_entity(col, guid));
+    return std::visit([&oss](auto& option) ->std::ostream& {
+                          if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionAccountValue>)
+                                  gnc_option_to_scheme(oss, option);
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionMultichoiceValue>)
+                                  oss << "'" << option;
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionValue<const QofInstance*>> ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionValidatedValue<const QofInstance*>>)
+                                  gnc_option_to_scheme(oss, option);
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionDateValue>)
+                                  oss << "'(" << option << ")";
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option.get_value())>,
+                               std::string>)
+                                  oss << '"' << option << '"';
+                          else
+                              oss << option;
+                          return oss;
+                      }, *m_option);
 }
 
-QofInstance*
-qof_instance_from_string(const std::string& str, GncOptionUIType type)
+std::istream&
+GncOption::from_scheme(std::istream& iss)
 {
-    if (type == GncOptionUIType::CURRENCY ||
-        type == GncOptionUIType::COMMODITY)
-    {
-        auto book{gnc_get_current_book()};
-        auto sep{str.find(":")};
-        auto name_space{str.substr(0, sep)};
-        auto mnemonic{str.substr(sep + 1, -1)};
-        auto table = gnc_commodity_table_get_table(book);
-        return QOF_INSTANCE(gnc_commodity_table_lookup(table,
-                                                       name_space.c_str(),
-                                                       mnemonic.c_str()));
-    }
-    auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
-    return qof_instance_from_guid(&guid, type);
+    return std::visit([&iss](auto& option) -> std::istream& {
+                          if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionAccountValue>)
+                              gnc_option_from_scheme(iss, option);
+                          else if constexpr
+                              ((std::is_same_v<std::decay_t<decltype(option)>,
+                                GncOptionMultichoiceValue>))
+                          {
+                              iss.ignore(1, '\'');
+                              iss >> option;
+                          }
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionValue<const QofInstance*>> ||
+                               std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionValidatedValue<const QofInstance*>>)
+                              gnc_option_from_scheme(iss, option);
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option)>,
+                               GncOptionDateValue>)
+                          {
+                              iss.ignore(2, '(');
+                              iss >> option;
+                              //operator >> clears the trailing ')'
+                          }
+                          else if constexpr
+                              (std::is_same_v<std::decay_t<decltype(option.get_value())>,
+                               std::string>)
+                          {
+                              iss.ignore(1, '"');
+                              std::string input;
+                              std::getline(iss, input, '"');
+                              option.set_value(input);
+                          }
+                          else
+                              iss >> option;
+                          return iss;
+                      }, *m_option);
 }
 
-std::string
-qof_instance_to_string(const QofInstance* inst)
-{
-    gnc::GUID guid{*qof_instance_get_guid(inst)};
-    return guid.to_string();
-}
+/* We must instantiate all of the templates we need here because we don't expose
+ * the template implementation in the public header.
+ */
+
+using GncOptionAccountList = std::vector<const Account*>;
+
+template class GncOptionValidatedValue<const QofInstance*>;
+
+template GncOption::GncOption(const char*, const char*, const char*,
+                              const char*, bool, GncOptionUIType);
+//template GncOption::GncOption(const char*, const char*, const char*,
+//                              const char*, int, GncOptionUIType);
+template GncOption::GncOption(const char*, const char*, const char*,
+                              const char*, int64_t, GncOptionUIType);
+//template GncOption::GncOption(const char*, const char*, const char*,
+//                              const char*, const char*, GncOptionUIType);
+//template GncOption::GncOption(const char*, const char*, const char*,
+//                              const char*, double, GncOptionUIType);
+template GncOption::GncOption(const char*, const char*, const char*,
+                              const char*, std::string, GncOptionUIType);
+template GncOption::GncOption(const char*, const char*, const char*,
+                              const char*, const QofInstance*, GncOptionUIType);
+
+template bool GncOption::get_value<bool>() const;
+template int GncOption::get_value<int>() const;
+template int64_t GncOption::get_value<int64_t>() const;
+template double GncOption::get_value<double>() const;
+template const char* GncOption::get_value<const char*>() const;
+template std::string GncOption::get_value<std::string>() const;
+template const QofInstance* GncOption::get_value<const QofInstance*>() const;
+template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
+template GncOptionAccountList GncOption::get_value<GncOptionAccountList>() const;
+
+template bool GncOption::get_default_value<bool>() const;
+template int GncOption::get_default_value<int>() const;
+template int64_t GncOption::get_default_value<int64_t>() const;
+template double GncOption::get_default_value<double>() const;
+template const char* GncOption::get_default_value<const char*>() const;
+template std::string GncOption::get_default_value<std::string>() const;
+template const QofInstance* GncOption::get_default_value<const QofInstance*>() const;
+template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() const;
+
+template void GncOption::set_value(bool);
+template void GncOption::set_value(int);
+template void GncOption::set_value(int64_t);
+template void GncOption::set_value(double);
+template void GncOption::set_value(const char*);
+template void GncOption::set_value(std::string);
+template void GncOption::set_value(const QofInstance*);
+template void GncOption::set_value(RelativeDatePeriod);
+
+template bool GncOption::validate(bool) const;
+template bool GncOption::validate(int) const;
+template bool GncOption::validate(int64_t) const;
+template bool GncOption::validate(double) const;
+template bool GncOption::validate(const char*) const;
+template bool GncOption::validate(std::string) const;
+template bool GncOption::validate(const QofInstance*) const;
+template bool GncOption::validate(RelativeDatePeriod) const;
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 2ceff7cbc..96f0db984 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -1,6 +1,6 @@
 /********************************************************************\
  * gnc-option.hpp -- Application options system                     *
- * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ * Copyright (C) 2020 John Ralls <jralls at ceridwen.us>               *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -24,733 +24,92 @@
 #ifndef GNC_OPTION_HPP_
 #define GNC_OPTION_HPP_
 
-extern "C"
-{
-#include <config.h>
-#include <qof.h>
-#include <Account.h>
-#include <gnc-budget.h>
-#include <gnc-commodity.h>
-}
-#include <gnc-datetime.hpp>
-#include <libguile.h>
 #include <string>
-#include <utility>
-#include <vector>
-#include <exception>
-#include <functional>
-#include <variant>
 #include <iostream>
-
-/*
- * Unused base class to document the structure of the current Scheme option
- * vector, re-expressed in C++. The comment-numbers on the right indicate which
- * item in the Scheme vector each item implements.
- *
- * Not everything here needs to be implemented, nor will it necessarily be
- * implemented the same way. For example, part of the purpose of this redesign
- * is to convert from saving options as strings of Scheme code to some form of
- * key-value pair in the book options, so generate_restore_form() will likely be
- * supplanted with save_to_book().
-
-template <typename ValueType>
-class GncOptionBase
-{
-public:
-    virtual ~GncOption = default;
-    virtual ValueType get_value() const = 0;                             //5
-    virtual ValueType get_default_value() = 0;
-    virtual SCM get_SCM_value() = 0;
-    virtual SCM get_SCM_default_value() const = 0;                       //7
-    virtual void set_value(ValueType) = 0;                               //6
-// generate_restore_form outputs a Scheme expression (a "form") that finds an
-// option and sets it to the current value. e.g.:
-//(let ((option (gnc:lookup-option options
-//                                 "Display"
-//                                 "Amount")))
-//  ((lambda (option) (if option ((gnc:option-setter option) 'none))) option))
-// it uses gnc:value->string to generate the "'none" (or whatever the option's
-// value would be as input to the scheme interpreter).
-
-    virtual std::string generate_restore_form();                         //8
-    virtual void save_to_book(QofBook*) const noexcept;                  //9
-    virtual void read_from_book(QofBook*);                               //10
-    virtual std::vector<std::string> get_option_strings();               //15
-    virtual set_changed_callback(std::function<void(void*)>);            //14
-protected:
-    const std::string m_section;                                         //0
-    const std::string m_name;                                            //1
-    const std::string m_sort_tag;                                        //2
-    const std::type_info m_kvp_type;                                     //3
-    const std::string m_doc_string;                                      //4
-    std::function<void(void*)> m_changed_callback;   //Part of the make-option closure
-    std::function<void(void*)>m_option_widget_changed_callback;          //16
-};
-*/
-
-enum GncOptionUIType
-{
-    INTERNAL,
-    BOOLEAN,
-    STRING,
-    TEXT,
-    CURRENCY,
-    COMMODITY,
-    MULTICHOICE,
-    DATE,
-    ACCOUNT_LIST,
-    ACCOUNT_SEL,
-    LIST,
-    NUMBER_RANGE,
-    COLOR,
-    FONT,
-    BUDGET,
-    PIXMAP,
-    RADIOBUTTON,
-    DATE_FORMAT,
-    OWNER,
-    CUSTOMER,
-    VENDOR,
-    EMPLOYEE,
-    INVOICE,
-    TAX_TABLE,
-    QUERY,
-};
-
-static const char* commodity_scm_intro{"'(commodity-scm "};
-#ifndef SWIG
-size_t constexpr classifier_size_max{50};
-size_t constexpr sort_tag_size_max{10};
-#endif
-
-struct OptionClassifier
-{
-    std::string m_section;
-    std::string m_name;
-    std::string m_sort_tag;
-//  std::type_info m_kvp_type;
-    std::string m_doc_string;
-};
+#include <variant>
+#include <memory>
+#include "gnc-option-uitype.hpp"
 
 class GncOptionUIItem;
+struct QofInstance_s;
+using QofInstance = QofInstance_s;
+template <typename ValueType> class GncOptionValue;
+class GncOptionAccountValue;
+class GncOptionMultichoiceValue;
+template <typename ValueType> class GncOptionRangeValue;
+template <typename ValueType> class GncOptionValidatedValue;
+class GncOptionDateValue;
 
-/**
- * Holds a pointer to the UI item which will control the option and an enum
- * representing the type of the option for dispatch purposes; all of that
- * happens in gnucash/gnome-utils/dialog-options and
- * gnucash/gnome/business-option-gnome.
- *
- * This class takes no ownership responsibility, so calling code is responsible
- * for ensuring that the UI_Item is alive. For convenience the public
- * clear_ui_item function can be used as a weak_ptr's destruction callback to
- * ensure that the ptr will be nulled if the ui_item is destroyed elsewhere.
- */
-class OptionUIItem
-{
-public:
-    GncOptionUIType get_ui_type() const { return m_ui_type; }
-    GncOptionUIItem* const get_ui_item() const {return m_ui_item; }
-    void clear_ui_item() { m_ui_item = nullptr; }
-    void set_ui_item(GncOptionUIItem* ui_item)
-    {
-        if (m_ui_type == GncOptionUIType::INTERNAL)
-        {
-            std::string error{"INTERNAL option, setting the UI item forbidden."};
-            throw std::logic_error(std::move(error));
-        }
-        m_ui_item = ui_item;
-    }
-    void make_internal()
-    {
-        if (m_ui_item != nullptr)
-        {
-            std::string error("Option has a UI Element, can't be INTERNAL.");
-            throw std::logic_error(std::move(error));
-        }
-        m_ui_type = GncOptionUIType::INTERNAL;
-    }
-protected:
-    OptionUIItem(GncOptionUIType ui_type) :
-        m_ui_item{nullptr}, m_ui_type{ui_type} {}
-    OptionUIItem(const OptionUIItem&) = default;
-    OptionUIItem(OptionUIItem&&) = default;
-    ~OptionUIItem() = default;
-    OptionUIItem& operator=(const OptionUIItem&) = default;
-    OptionUIItem& operator=(OptionUIItem&&) = default;
-private:
-    GncOptionUIItem* m_ui_item;
-    GncOptionUIType m_ui_type;
-};
-
-#ifndef SWIG
-auto constexpr size_t_max = std::numeric_limits<std::size_t>::max();
-#endif
+using GncOptionVariant = std::variant<GncOptionValue<std::string>,
+                                      GncOptionValue<bool>,
+                                      GncOptionValue<int64_t>,
+                                      GncOptionValue<const QofInstance*>,
+                                      GncOptionAccountValue,
+                                      GncOptionMultichoiceValue,
+                                      GncOptionRangeValue<int>,
+                                      GncOptionRangeValue<double>,
+                                      GncOptionValidatedValue<const QofInstance*>,
+                                      GncOptionDateValue>;
 
-template <typename ValueType>
-class GncOptionValue : public OptionClassifier, public OptionUIItem
-{
-public:
-    GncOptionValue<ValueType>(const char* section, const char* name,
-                              const char* key, const char* doc_string,
-                              ValueType value,
-                              GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{value}, m_default_value{value} {}
-    GncOptionValue<ValueType>(const GncOptionValue<ValueType>&) = default;
-    GncOptionValue<ValueType>(GncOptionValue<ValueType>&&) = default;
-    GncOptionValue<ValueType>& operator=(const GncOptionValue<ValueType>&) = default;
-    GncOptionValue<ValueType>& operator=(GncOptionValue<ValueType>&&) = default;
-    ValueType get_value() const { return m_value; }
-    ValueType get_default_value() const { return m_default_value; }
-    void set_value(ValueType new_value) { m_value = new_value; }
-    bool is_changed() const noexcept { return m_value != m_default_value; }
-private:
-    ValueType m_value;
-    ValueType m_default_value;
-};
+using GncOptionVariantPtr = std::unique_ptr<GncOptionVariant>;
 
-template <typename ValueType>
-class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem
+class GncOption
 {
 public:
-    GncOptionValidatedValue<ValueType>(const char* section, const char* name,
-                                       const char* key, const char* doc_string,
-                                       ValueType value,
-                                       std::function<bool(ValueType)>validator,
-                                       GncOptionUIType ui_type = GncOptionUIType::INTERNAL
-        ) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{value}, m_default_value{value}, m_validator{validator}
-        {
-            if (!this->validate(value))
-            throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
-        }
-    GncOptionValidatedValue<ValueType>(const char* section, const char* name,
-                                       const char* key, const char* doc_string,
-                                       ValueType value,
-                                       std::function<bool(ValueType)>validator,
-                                       ValueType val_data) :
-        OptionClassifier{section, name, key, doc_string}, m_value{value},
-        m_default_value{value}, m_validator{validator}, m_validation_data{val_data}
-    {
-            if (!this->validate(value))
-            throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
-    }
-    GncOptionValidatedValue<ValueType>(const GncOptionValidatedValue<ValueType>&) = default;
-    GncOptionValidatedValue<ValueType>(GncOptionValidatedValue<ValueType>&&) = default;
-    GncOptionValidatedValue<ValueType>& operator=(const GncOptionValidatedValue<ValueType>&) = default;
-    GncOptionValidatedValue<ValueType>& operator=(GncOptionValidatedValue<ValueType>&&) = default;
-    ValueType get_value() const { return m_value; }
-    ValueType get_default_value() const { return m_default_value; }
-    bool validate(ValueType value) const { return m_validator(value); }
-    void set_value(ValueType value)
-    {
-        if (this->validate(value))
-            m_value = value;
-        else
-            throw std::invalid_argument("Validation failed, value not set.");
-    }
-    bool is_changed() const noexcept { return m_value != m_default_value; }
-    std::ostream& to_scheme(std::ostream&) const;
-    std::istream& from_scheme(std::istream&);
+    template <typename OptionType>
+    GncOption(OptionType option) :
+        m_option{std::make_unique<GncOptionVariant>(option)} {}
+    template <typename ValueType>
+    GncOption(const char* section, const char* name,
+              const char* key, const char* doc_string,
+              ValueType value,
+              GncOptionUIType ui_type = GncOptionUIType::INTERNAL);
+    template <typename ValueType> void set_value(ValueType value);
+    template <typename ValueType> ValueType get_default_value() const;
+    template <typename ValueType> ValueType get_value() const;
+
+    const std::string& get_section() const;
+    const std::string& get_name() const;
+    const std::string& get_key() const;
+    const std::string& get_docstring() const;
+    void set_ui_item(GncOptionUIItem* ui_elem);
+    const GncOptionUIType get_ui_type() const;
+    GncOptionUIItem* const get_ui_item() const;
+    void make_internal();
+    bool is_changed() const noexcept;
+    template <typename ValueType> bool validate(ValueType value) const;
+    std::size_t num_permissible_values() const;
+    std::size_t permissible_value_index(const std::string& value) const;
+    const std::string& permissible_value(std::size_t index) const;
+    const std::string& permissible_value_name(std::size_t index) const;
+    const std::string& permissible_value_description(std::size_t index) const;
+    std::ostream& out_stream(std::ostream& oss) const;
+    std::istream& in_stream(std::istream& iss);
+    std::ostream& to_scheme(std::ostream& oss) const;
+    std::istream& from_scheme(std::istream& iss);
+    GncOptionVariantPtr& _get_option() { return m_option; }
 private:
-    ValueType m_value;
-    ValueType m_default_value;
-    std::function<bool(ValueType)> m_validator;                         //11
-    ValueType m_validation_data;
+    inline static const std::string c_empty_string{""};
+    GncOptionVariantPtr m_option;
 };
 
-QofInstance* qof_instance_from_string(const std::string& str,
-                                      GncOptionUIType type);
-QofInstance* qof_instance_from_guid(GncGUID*, GncOptionUIType type);
-std::string qof_instance_to_string(const QofInstance* inst);
-
-/* These will work when m_value is a built-in class; GnuCash class and container
- * values will need specialization unless they happen to define operators << and
- * >>.
- * Note that SWIG 3.0.12 chokes on elaborate enable_if so just hide the
- * following templates from SWIG. (Ignoring doesn't work because SWIG still has
- * to parse the templates to figure out the symbols.
- */
-#ifndef SWIG
-template<class OptionValueClass,
-         typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
-                                                     std::decay_t<OptionValueClass>> &&
-                                   !(std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValue<QofInstance*>> ||
-                                     std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValidatedValue<QofInstance*>>), int> = 0>
-std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
-{
-    oss << opt.get_value();
-    return oss;
-}
-
-template<> inline std::ostream&
-operator<< <GncOptionValue<bool>>(std::ostream& oss,
-                                  const GncOptionValue<bool>& opt)
-{
-    oss << (opt.get_value() ? "#t" : "#f");
-    return oss;
-}
-
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
-inline std::ostream&
-operator<< (std::ostream& oss, const OptType& opt)
-{
-    auto value = opt.get_value();
-    if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
-        type == GncOptionUIType::CURRENCY)
-    {
-        if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY)
-        {
-            oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " ";
-        }
-        oss << gnc_commodity_get_mnemonic(GNC_COMMODITY(value));
-    }
-    else
-    {
-        oss << qof_instance_to_string(value);
-    }
-    return oss;
-}
-
-template<class OptionValueClass,
-         typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>> &&
-                                   !(std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValue<QofInstance*>> ||
-                                     std::is_same_v<std::decay_t<OptionValueClass>,
-                                     GncOptionValidatedValue<QofInstance*>>), int> = 0>
-std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
-{
-    std::decay_t<decltype(opt.get_value())> value;
-    iss >> value;
-    opt.set_value(value);
-    return iss;
-}
-
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
-std::istream&
-operator>> (std::istream& iss, OptType& opt)
-{
-    std::string instr;
-    if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
-        type == GncOptionUIType::CURRENCY)
-    {
-        std::string name_space, mnemonic;
-        if (type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY)
-        {
-            iss >> name_space;
-        }
-        else
-            name_space = GNC_COMMODITY_NS_CURRENCY;
-        iss >> mnemonic;
-        instr = name_space + ":";
-        instr += mnemonic;
-     }
-    else
-    {
-        iss >> instr;
-    }
-    opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
-    return iss;
-}
-
-template<> inline std::istream&
-operator>> <GncOptionValue<bool>>(std::istream& iss,
-                                  GncOptionValue<bool>& opt)
-{
-    std::string instr;
-    iss >> instr;
-    opt.set_value(instr == "#t" ? true : false);
-    return iss;
-}
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*>>, int> = 0>
 inline std::ostream&
-gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
+operator<<(std::ostream& oss, const GncOption& opt)
 {
-    auto value = opt.get_value();
-    auto type = opt.get_ui_type();
-    if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
-    {
-        if (type == GncOptionUIType::COMMODITY)
-        {
-            oss << commodity_scm_intro;
-            oss << "\"" <<
-                gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" ";
-        }
-
-        oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\"";
-
-        if (type == GncOptionUIType::COMMODITY)
-        {
-            oss << ")";
-        }
-    }
-    else
-    {
-        oss << "\"" << qof_instance_to_string(value) << "\"";
-    }
-    return oss;
+    return opt.out_stream(oss);
 }
 
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*>>, int> = 0>
 inline std::istream&
-gnc_option_from_scheme (std::istream& iss, OptType& opt)
+operator>>(std::istream& iss, GncOption& opt)
 {
-    std::string instr;
-    auto type = opt.get_ui_type();
-    if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
-    {
-        std::string name_space, mnemonic;
-        if (type == GncOptionUIType::COMMODITY)
-        {
-            iss.ignore(strlen(commodity_scm_intro) + 1, '"');
-            std::getline(iss, name_space, '"');
- // libc++ doesn't consume the end character, libstdc++ does
-#ifdef _LIBCPP_VERSION
-            iss.ignore(1, '"');
-#endif
-        }
-        else
-            name_space = GNC_COMMODITY_NS_CURRENCY;
-        iss.ignore(1, '"');
-        std::getline(iss, mnemonic, '"');
-
-        if (type == GncOptionUIType::COMMODITY)
-            iss.ignore(2, ')');
-        else
-            iss.ignore(1, '"');
-
-        instr = name_space + ":";
-        instr += mnemonic;
-     }
-    else
-    {
-        iss.ignore(1, '"');
-        std::getline(iss, instr, '"');
-    }
-    opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
-    return iss;
+    return opt.in_stream(iss);
 }
-#endif // SWIG
-
 /**
- * Used for numeric ranges and plot sizes.
- */
-
-template <typename ValueType>
-class GncOptionRangeValue :
-    public OptionClassifier, public OptionUIItem
-{
-public:
-    GncOptionRangeValue<ValueType>(const char* section, const char* name,
-                                   const char* key, const char* doc_string,
-                                   ValueType value, ValueType min,
-                                   ValueType max, ValueType step) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(GncOptionUIType::NUMBER_RANGE),
-        m_value{value >= min && value <= max ? value : min},
-        m_default_value{value >= min && value <= max ? value : min},
-        m_min{min}, m_max{max}, m_step{step} {}
-
-    GncOptionRangeValue<ValueType>(const GncOptionRangeValue<ValueType>&) = default;
-    GncOptionRangeValue<ValueType>(GncOptionRangeValue<ValueType>&&) = default;
-    GncOptionRangeValue<ValueType>& operator=(const GncOptionRangeValue<ValueType>&) = default;
-    GncOptionRangeValue<ValueType>& operator=(GncOptionRangeValue<ValueType>&&) = default;
-    ValueType get_value() const { return m_value; }
-    ValueType get_default_value() const { return m_default_value; }
-    bool validate(ValueType value) { return value >= m_min && value <= m_max; }
-    void set_value(ValueType value)
-    {
-        if (this->validate(value))
-            m_value = value;
-        else
-            throw std::invalid_argument("Validation failed, value not set.");
-    }
-    bool is_changed() const noexcept { return m_value != m_default_value; }
-private:
-    ValueType m_value;
-    ValueType m_default_value;
-    ValueType m_min;
-    ValueType m_max;
-    ValueType m_step;
-};
-
-/** MultiChoice options have a vector of valid options
- * (GncMultiChoiceOptionChoices) and validate the selection as being one of
- * those values. The value is the index of the selected item in the vector. The
- * tuple contains three strings, a key, a display
- * name and a brief description for the tooltip. Both name and description
- * should be localized at the point of use. 
- *
- *
- */
-using GncMultiChoiceOptionEntry = std::tuple<const std::string,
-                                             const std::string,
-                                             const std::string>;
-using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
-
-class GncOptionMultichoiceValue :
-    public OptionClassifier, public OptionUIItem
-{
-public:
-    GncOptionMultichoiceValue(const char* section, const char* name,
-                              const char* key, const char* doc_string,
-                              const char* value,
-                              GncMultiChoiceOptionChoices&& choices,
-                              GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{}, m_default_value{}, m_choices{std::move(choices)} {
-            if (value)
-            {
-                if (auto index = find_key(value);
-                    index != size_t_max)
-                {
-                    m_value = index;
-                    m_default_value = index;
-                }
-            }
-        }
-
-    GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default;
-    GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default;
-    GncOptionMultichoiceValue& operator=(const GncOptionMultichoiceValue&) = default;
-    GncOptionMultichoiceValue& operator=(GncOptionMultichoiceValue&&) = default;
-
-    const std::string& get_value() const
-    {
-        return std::get<0>(m_choices.at(m_value));
-    }
-    const std::string& get_default_value() const
-    {
-        return std::get<0>(m_choices.at(m_default_value));
-    }
-     bool validate(const std::string& value) const noexcept
-    {
-        auto index = find_key(value);
-        return index != size_t_max;
-
-    }
-    void set_value(const std::string& value)
-    {
-        auto index = find_key(value);
-        if (index != size_t_max)
-            m_value = index;
-        else
-            throw std::invalid_argument("Value not a valid choice.");
-
-    }
-    std::size_t num_permissible_values() const noexcept
-    {
-        return m_choices.size();
-    }
-    std::size_t permissible_value_index(const std::string& key) const noexcept
-    {
-            return find_key(key);
-    }
-    const std::string& permissible_value(std::size_t index) const
-    {
-        return std::get<0>(m_choices.at(index));
-    }
-    const std::string& permissible_value_name(std::size_t index) const
-    {
-        return std::get<1>(m_choices.at(index));
-    }
-    const std::string& permissible_value_description(std::size_t index) const
-    {
-        return std::get<2>(m_choices.at(index));
-    }
-    bool is_changed() const noexcept { return m_value != m_default_value; }
-private:
-    std::size_t find_key (const std::string& key) const noexcept
-    {
-        auto iter = std::find_if(m_choices.begin(), m_choices.end(),
-                              [key](auto choice) {
-                                  return std::get<0>(choice) == key; });
-        if (iter != m_choices.end())
-            return iter - m_choices.begin();
-        else
-            return size_t_max;
-
-    }
-    std::size_t m_value;
-    std::size_t m_default_value;
-    GncMultiChoiceOptionChoices m_choices;
-};
-
-/** Account options
- *
- * Set one or more accounts on which to report, optionally restricted to certain
- * account types. Many calls to make-account-list-option will pass a get-default
- * function that retrieves all of the accounts of a list of types.
- *
- * Some reports (examples/daily-reports.scm and standard/ account-piechart.scm,
- * advanced-portfolio.scm, category-barchart.scm, net-charts.scm, and
- * portfolio.scm) also provide a validator that rejects accounts that don't meet
- * an account-type criterion.
- *
- * There are two types of option, account-list which permits more than one
- * account selection and account-sel, which doesn't.
- *
-
- */
-
-using GncOptionAccountList = std::vector<const Account*>;
-using GncOptionAccountTypeList = std::vector<GNCAccountType>;
-
-class GncOptionAccountValue :
-    public OptionClassifier, public OptionUIItem
-{
-public:
-    GncOptionAccountValue(const char* section, const char* name,
-                          const char* key, const char* doc_string,
-                          GncOptionUIType ui_type) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {}
-
-    GncOptionAccountValue(const char* section, const char* name,
-                          const char* key, const char* doc_string,
-                          GncOptionUIType ui_type,
-                          const GncOptionAccountList& value) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{value},
-        m_default_value{std::move(value)}, m_allowed{} {}
-    GncOptionAccountValue(const char* section, const char* name,
-                          const char* key, const char* doc_string,
-                          GncOptionUIType ui_type,
-                          GncOptionAccountTypeList&& allowed) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{},
-        m_default_value{}, m_allowed{std::move(allowed)} {}
-    GncOptionAccountValue(const char* section, const char* name,
-                          const char* key, const char* doc_string,
-                          GncOptionUIType ui_type,
-                          const GncOptionAccountList& value,
-                          GncOptionAccountTypeList&& allowed) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(ui_type),
-        m_value{},
-        m_default_value{}, m_allowed{std::move(allowed)} {
-            if (!validate(value))
-                throw std::invalid_argument("Supplied Value not in allowed set.");
-            m_value = value;
-            m_default_value = std::move(value);
-        }
-
-    const GncOptionAccountList& get_value() const { return m_value; }
-    const GncOptionAccountList& get_default_value() const { return m_default_value; }
-    bool validate (const GncOptionAccountList& values) const;
-    void set_value (const GncOptionAccountList& values) {
-        if (validate(values))
-            //throw!
-            m_value = values;
-    }
-    bool is_changed() const noexcept { return m_value != m_default_value; }
-private:
-    GncOptionAccountList m_value;
-    GncOptionAccountList m_default_value;
-    GncOptionAccountTypeList m_allowed;
-};
-
-template<> inline std::ostream&
-operator<< <GncOptionAccountValue>(std::ostream& oss,
-                                       const GncOptionAccountValue& opt)
-{
-    auto values{opt.get_value()};
-    bool first = true;
-    for (auto value : values)
-    {
-        if (first)
-            first = false;
-        else
-            oss << " ";
-        oss << qof_instance_to_string(QOF_INSTANCE(value));
-    }
-    return oss;
-}
-
-template<> inline std::istream&
-operator>> <GncOptionAccountValue>(std::istream& iss,
-                                   GncOptionAccountValue& opt)
-{
-    GncOptionAccountList values;
-    while (true)
-    {
-        std::string str;
-        std::getline(iss, str, ' ');
-        if (!str.empty())
-            values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type()));
-        else
-            break;
-    }
-    opt.set_value(values);
-    iss.clear();
-    return iss;
-}
-
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionAccountValue>, int> = 0>
-inline std::ostream&
-gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
-{
-    auto values{opt.get_value()};
-    oss << "'(\"";
-    bool first = true;
-    for (auto value : values)
-    {
-        if (first)
-            first = false;
-        else
-            oss << " \"";
-        oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"';
-    }
-    oss << ')';
-    return oss;
-}
-
-template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionAccountValue>, int> = 0>
-inline std::istream&
-gnc_option_from_scheme(std::istream& iss, OptType& opt)
-{
-    GncOptionAccountList values;
-    iss.ignore(3, '"');
-    while (true)
-    {
-        std::string str;
-        std::getline(iss, str, '"');
-        if (!str.empty())
-        {
-            values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type()));
-            iss.ignore(2, '"');
-        }
-        else
-            break;
-    }
-    opt.set_value(values);
-    iss.ignore(1, ')');
-    return iss;
-}
-
-/** Date options
- * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
- * flag and a time64, or for legacy purposes the absolute flag and a timespec.
+ * Reporting periods relative to the current date.
  *
  * The original design allowed custom RelativeDatePeriods, but that facility is
  * unused so we'll go with compiled-in enums.
  */
-/*
-gnc-date-option-show-time? -- option_data[1]
-gnc-date-option-get-subtype -- option_data[0]
-gnc-date-option-value-type m_value
-gnc-date-option-absolute-time m_type == DateTyupe::Absolute
-gnc-date-option-relative-time m_type != DateTyupe::Absolute
- */
-
 enum class RelativeDatePeriod : int
 {
     ABSOLUTE = -1,
@@ -771,352 +130,5 @@ enum class RelativeDatePeriod : int
     END_ACCOUNTING_PERIOD
 };
 
-class GncOptionDateValue : public OptionClassifier, public OptionUIItem
-{
-public:
-    GncOptionDateValue(const char* section, const char* name,
-                              const char* key, const char* doc_string) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(GncOptionUIType::DATE),
-        m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
-        m_date{INT64_MAX},
-        m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
-        m_default_date{INT64_MAX} {}
-    GncOptionDateValue(const char* section, const char* name,
-                       const char* key, const char* doc_string,
-                       time64 time) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(GncOptionUIType::DATE),
-        m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time},
-        m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {}
-    GncOptionDateValue(const char* section, const char* name,
-                       const char* key, const char* doc_string,
-                       const RelativeDatePeriod period) :
-        OptionClassifier{section, name, key, doc_string},
-        OptionUIItem(GncOptionUIType::DATE),
-        m_period{period}, m_date{INT64_MAX},
-        m_default_period{period}, m_default_date{INT64_MAX} {}
-        GncOptionDateValue(const GncOptionDateValue&) = default;
-        GncOptionDateValue(GncOptionDateValue&&) = default;
-        GncOptionDateValue& operator=(const GncOptionDateValue&) = default;
-        GncOptionDateValue& operator=(GncOptionDateValue&&) = default;
-    time64 get_value() const;
-    time64 get_default_value() const { return static_cast<time64>(GncDateTime()); }
-    std::ostream& out_stream(std::ostream& oss) const noexcept;
-    std::istream& in_stream(std::istream& iss);
-    void set_value(RelativeDatePeriod value) {
-        m_period = value;
-        m_date = INT64_MAX;
-    }
-    void set_value(time64 time) {
-        m_period = RelativeDatePeriod::ABSOLUTE;
-        m_date = time;
-    }
-    bool is_changed() const noexcept { return m_period != m_default_period &&
-            m_date != m_default_date; }
-private:
-    RelativeDatePeriod m_period;
-    time64 m_date;
-    RelativeDatePeriod m_default_period;
-    time64 m_default_date;
-};
-
-template<> inline std::ostream&
-operator<< <GncOptionDateValue>(std::ostream& oss,
-                                       const GncOptionDateValue& opt)
-{
-    return opt.out_stream(oss);
-}
-
-template<> inline std::istream&
-operator>> <GncOptionDateValue>(std::istream& iss,
-                                   GncOptionDateValue& opt)
-{
-    return opt.in_stream(iss);
-}
-
-using GncOptionVariant = std::variant<GncOptionValue<std::string>,
-                                      GncOptionValue<bool>,
-                                      GncOptionValue<int64_t>,
-                                      GncOptionValue<QofInstance*>,
-                                      GncOptionAccountValue,
-                                      GncOptionMultichoiceValue,
-                                      GncOptionRangeValue<int>,
-                                      GncOptionRangeValue<double>,
-                                      GncOptionValidatedValue<QofInstance*>,
-                                      GncOptionDateValue>;
-
-class GncOption
-{
-public:
-    template <typename OptionType>
-    GncOption(OptionType option) : m_option{option} {}
-
-    template <typename ValueType>
-    GncOption(const char* section, const char* name,
-              const char* key, const char* doc_string,
-              ValueType value,
-              GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
-        m_option{GncOptionValue<ValueType> {
-            section, name, key, doc_string, value, ui_type}} {}
-
-    template <typename ValueType> ValueType get_value() const
-    {
-        return std::visit([](const auto& option)->ValueType {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
-                    return option.get_value();
-                return ValueType {};
-            }, m_option);
-    }
-
-    template <typename ValueType> ValueType get_default_value() const
-    {
-        return std::visit([](const auto& option)->ValueType {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
-                    return option.get_default_value();
-                return ValueType {};
-            }, m_option);
-
-    }
-
-    template <typename ValueType> void set_value(ValueType value)
-    {
-        std::visit([value](auto& option) {
-                if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                      std::decay_t<ValueType>> ||
-                     (std::is_same_v<std::decay_t<decltype(option)>,
-                      GncOptionDateValue> &&
-                      std::is_same_v<std::decay_t<ValueType>,
-                      RelativeDatePeriod>))
-                                 option.set_value(value);
-            }, m_option);
-    }
-    const std::string& get_section() const
-    {
-        return std::visit([](const auto& option)->const std::string& {
-                return option.m_section;
-            }, m_option);
-    }
-    const std::string& get_name() const
-    {
-        return std::visit([](const auto& option)->const std::string& {
-                return option.m_name;
-            }, m_option);
-    }
-    const std::string& get_key() const
-    {
-        return std::visit([](const auto& option)->const std::string& {
-                return option.m_sort_tag;
-            }, m_option);
-    }
-    const std::string& get_docstring() const
-    {
-          return std::visit([](const auto& option)->const std::string& {
-                return option.m_doc_string;
-              }, m_option);
-    }
-    void set_ui_item(GncOptionUIItem* ui_elem)
-    {
-        std::visit([ui_elem](auto& option) {
-                option.set_ui_item(ui_elem);
-            }, m_option);
-    }
-    const GncOptionUIType get_ui_type() const
-    {
-        return std::visit([](const auto& option)->GncOptionUIType {
-                return option.get_ui_type();
-            }, m_option);
-    }
-    GncOptionUIItem* const get_ui_item() const
-    {
-        return std::visit([](const auto& option)->GncOptionUIItem* {
-                return option.get_ui_item();
-            }, m_option);
-    }
-    void make_internal()
-    {
-        std::visit([](auto& option) {
-                option.make_internal();
-            }, m_option);
-    }
-    bool is_changed()
-    {
-        return std::visit([](const auto& option)->bool {
-                return option.is_changed();
-            }, m_option);
-    }
-
-    template<typename ValueType>
-    bool validate(ValueType value) const {
-        return std::visit([value] (const auto& option) -> bool {
-                              if constexpr ((std::is_same_v<std::decay_t<decltype(option)>,
-                                                   GncOptionMultichoiceValue> &&
-                                      std::is_same_v<std::decay_t<ValueType>,
-                                                     std::string>) ||
-                                            std::is_same_v<std::decay_t<decltype(option)>,
-                                            GncOptionValidatedValue<ValueType>>)
-                                        return option.validate(value);
-                       else
-                           return false;
-                   }, m_option);
-    }
-
-    std::size_t num_permissible_values() const {
-        return std::visit([] (const auto& option) -> size_t {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                                    GncOptionMultichoiceValue>)
-                                        return option.num_permissible_values();
-                       else
-                           return size_t_max;
-                   }, m_option);
-    }
-
-    std::size_t permissible_value_index(const std::string& value) const {
-        return std::visit([&value] (const auto& option) -> size_t {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                                    GncOptionMultichoiceValue>)
-                                        return option.permissible_value_index(value);
-                       else
-                           return size_t_max;;
-                   }, m_option);
-    }
-
-    const std::string& permissible_value(std::size_t index) const {
-        return std::visit([index] (const auto& option) -> const std::string& {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                     GncOptionMultichoiceValue>)
-                                        return option.permissible_value(index);
-                       else
-                           return c_empty_string;
-                   }, m_option);
-    }
-
-    const std::string& permissible_value_name(std::size_t index) const {
-        return std::visit([index] (const auto& option) -> const std::string& {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                     GncOptionMultichoiceValue>)
-                                        return option.permissible_value_name(index);
-                       else
-                           return c_empty_string;
-                   }, m_option);
-    }
-
-    const std::string& permissible_value_description(std::size_t index) const {
-        return std::visit([index] (const auto& option) -> const std::string& {
-                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
-                                     GncOptionMultichoiceValue>)
-                                        return option.permissible_value_description(index);
-                       else
-                           return c_empty_string;
-                   }, m_option);
-    }
-
-    std::ostream& out_stream(std::ostream& oss) const
-    {
-            return std::visit([&oss](auto& option) -> std::ostream& {
-            oss << option;
-            return oss;
-        }, m_option);
-    }
-    std::istream& in_stream(std::istream& iss)
-    {
-    return std::visit([&iss](auto& option) -> std::istream& {
-            iss >> option;
-            return iss;
-        }, m_option);
-    }
-
-    std::ostream& to_scheme(std::ostream& oss) const
-    {
-        return std::visit([&oss](auto& option) ->std::ostream& {
-                if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionAccountValue>)
-                        gnc_option_to_scheme(oss, option);
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionMultichoiceValue>)
-                        oss << "'" << option;
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionValue<QofInstance*>> ||
-                     std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionValidatedValue<QofInstance*>>)
-                        gnc_option_to_scheme(oss, option);
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionDateValue>)
-                        oss << "'(" << option << ")";
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                     std::string>)
-                        oss << '"' << option << '"';
-                else
-                    oss << option;
-                return oss;
-            }, m_option);
-    }
-
-    std::istream& from_scheme(std::istream& iss)
-    {
-        return std::visit([&iss](auto& option) ->std::istream& {
-                if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionAccountValue>)
-                        gnc_option_from_scheme(iss, option);
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionMultichoiceValue>)
-                    {
-                         iss.ignore(1, '\'');
-                         iss >> option;
-                    }
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionValue<QofInstance*>> ||
-                     std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionValidatedValue<QofInstance*>>)
-                        gnc_option_from_scheme(iss, option);
-                else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option)>,
-                     GncOptionDateValue>)
-                    {
-                         iss.ignore(2, '(');
-                         iss >> option;
-                         //operator >> clears the trailing ')'
-                    }
-                 else if constexpr
-                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
-                     std::string>)
-                    {
-                         iss.ignore(1, '"');
-                         std::string input;
-                         std::getline(iss, input, '"');
-                         option.set_value(input);
-                    }
-                else
-                    iss >> option;
-                return iss;
-            }, m_option);
-    }
-
-    GncOptionVariant& _get_option() const { return m_option; }
-private:
-    inline static const std::string c_empty_string{""};
-    mutable GncOptionVariant m_option;
-};
-
-inline std::ostream&
-operator<<(std::ostream& oss, const GncOption& opt)
-{
-    return opt.out_stream(oss);
-}
-
-inline std::istream&
-operator>>(std::istream& iss, GncOption& opt)
-{
-    return opt.in_stream(iss);
-}
 
 #endif //GNC_OPTION_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp
new file mode 100644
index 000000000..6a9c5470a
--- /dev/null
+++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp
@@ -0,0 +1,127 @@
+/********************************************************************\
+ * gnc-optiondb.hpp -- Collection of GncOption objects              *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#ifndef GNC_OPTIONDB_P_HPP_
+#define GNC_OPTIONDB_P_HPP_
+
+#include "gnc-option.hpp"
+#include "gnc-option-impl.hpp"
+
+#include <functional>
+#include <exception>
+#include <optional>
+#include <iostream>
+extern "C"
+{
+#include <config.h>
+#include <qof.h>
+#include <gncInvoice.h>
+#include <gncOwner.h>
+#include <gncTaxTable.h>
+}
+
+using GncOptionVec = std::vector<GncOption>;
+using GncOptionSection = std::pair<std::string, GncOptionVec>;
+class GncOptionDB
+{
+public:
+    GncOptionDB();
+    GncOptionDB(QofBook* book);
+    ~GncOptionDB() = default;
+
+    void save_to_book(QofBook* book, bool do_clear) const;
+    int num_sections() const noexcept { return m_sections.size(); }
+    bool get_changed() const noexcept { return m_dirty; }
+    void register_option(const char* section, GncOption&& option);
+    void unregister_option(const char* section, const char* name);
+    void set_default_section(const char* section);
+    const GncOptionSection* const get_default_section() const noexcept;
+    void set_ui_item(const char* section, const char* name, GncOptionUIItem* ui_item);
+    GncOptionUIItem* const get_ui_item(const char* section, const char* name);
+    GncOptionUIType get_ui_type(const char* section, const char* name);
+    void set_ui_from_option(const char* section, const char* name,
+                            std::function<void(GncOption&)> func);
+    void set_option_from_ui(const char* section, const char* name,
+                            std::function<void(GncOption&)> func);
+    std::string lookup_string_option(const char* section,
+                                            const char* name);
+    template <typename ValueType>
+    bool set_option(const char* section, const char* name, ValueType value)
+    {
+        try
+        {
+            auto option{find_option(section, name)};
+            if (!option)
+                return false;
+            option->get().set_value(value);
+            return true;
+        }
+        catch(const std::invalid_argument& err)
+        {
+            printf("Set Failed: %s\n", err.what());
+            return false;
+        }
+    }
+//    void set_selectable(const char* section, const char* name);
+    void make_internal(const char* section, const char* name);
+    void commit() {};
+    std::optional<std::reference_wrapper<GncOptionSection>> find_section(const std::string& section);
+    std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
+        return static_cast<const GncOptionDB&>(*this).find_option(section, name);
+    }
+    std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) const;
+    std::ostream& save_to_scheme(std::ostream& oss,
+                                 const char* options_prolog) const noexcept;
+    std::istream& load_from_scheme(std::istream& iss) noexcept;
+    std::ostream& save_to_key_value(std::ostream& oss) const noexcept;
+    std::istream& load_from_key_value(std::istream& iss);
+    void save_to_kvp(QofBook* book, bool clear_book) const noexcept;
+    void load_from_kvp(QofBook* book) noexcept;
+    std::ostream& save_option_scheme(std::ostream& oss,
+                                     const char* option_prolog,
+                                     const std::string& section,
+                                     const std::string& name) const noexcept;
+    std::istream& load_option_scheme(std::istream& iss);
+    std::ostream& save_option_key_value(std::ostream& oss,
+                                        const std::string& section,
+                                        const std::string& name) const noexcept;
+    std::istream& load_option_key_value(std::istream& iss);
+private:
+    std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
+    std::vector<GncOptionSection> m_sections;
+    bool m_dirty = false;
+
+    std::function<GncOptionUIItem*()> m_get_ui_value;
+    std::function<void(GncOptionUIItem*)> m_set_ui_value;
+    static constexpr char const* const scheme_tags[]
+    {
+        "(let ((option (gnc:lookup-option ",
+        "                                 ",
+        ")))",
+        "   ((lambda (o) (if o (gnc:option-set-value o ",
+        "))) option))"
+        };
+};
+
+
+#endif // GNC_OPTIONDB_P_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index bcde3e597..9bdb5de8e 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -21,10 +21,12 @@
  *                                                                  *
 \********************************************************************/
 
-#include "gnc-optiondb.hpp"
+#include <string>
 #include <limits>
 #include <sstream>
 #include <kvp-value.hpp>
+#include "gnc-optiondb.hpp"
+#include "gnc-optiondb-impl.hpp"
 
 auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
 GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}
@@ -43,7 +45,8 @@ GncOptionDB::register_option(const char* section, GncOption&& option)
 
     if (db_section)
     {
-        db_section->get().second.emplace_back(std::move(option));
+        auto& sec_vec = db_section->get().second;
+        sec_vec.emplace_back(std::move(option));
         return;
     }
 
@@ -59,10 +62,11 @@ GncOptionDB::unregister_option(const char* section, const char* name)
     auto db_section = find_section(section);
     if (db_section)
     {
-        db_section->get().second.erase(
+        auto& sec_vec = db_section->get().second;
+        sec_vec.erase(
             std::remove_if(
-                db_section->get().second.begin(), db_section->get().second.end(),
-                [name](const GncOption& option) -> bool
+                sec_vec.begin(), sec_vec.end(),
+                [name](const auto& option) -> bool
                 {
                     return option.get_name() == std::string{name};
                 }));
@@ -132,7 +136,7 @@ GncOptionDB::find_section(const std::string& section)
 {
     auto db_section = std::find_if(
         m_sections.begin(), m_sections.end(),
-        [&section](GncOptionSection sect) -> bool
+        [&section](auto& sect) -> bool
         {
             return section.compare(0, classifier_size_max, sect.first) == 0;
         });
@@ -147,14 +151,15 @@ GncOptionDB::find_option(const std::string& section, const std::string& name) co
     auto db_section = const_cast<GncOptionDB*>(this)->find_section(section);
     if (!db_section)
         return std::nullopt;
+    auto& sec_vec =  db_section->get().second;
     auto db_opt = std::find_if(
-        db_section->get().second.begin(), db_section->get().second.end(),
+       sec_vec.begin(), sec_vec.end(),
         [&name](GncOption& option) -> bool
         {
             return name.compare(0, classifier_size_max - 1,
                                   option.get_name()) == 0;
         });
-    if (db_opt == db_section->get().second.end())
+    if (db_opt == sec_vec.end())
         return std::nullopt;
     return *db_opt;
 }
@@ -173,6 +178,7 @@ GncOptionDB::lookup_string_option(const char* section, const char* name)
 void
 GncOptionDB::make_internal(const char* section, const char* name)
 {
+
     auto db_opt = find_option(section, name);
     if (db_opt)
         db_opt->get().make_internal();
@@ -306,8 +312,7 @@ struct SchemeId
 
 /**
  * Scheme Parse Tree
- * An identifier is a string and a type (name, const, string, or form). A Form 
- * 
+ * An identifier is a string and a type (name, const, string, or form).
  */
 
 static void scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id);
@@ -488,11 +493,11 @@ GncOptionDB::load_option_scheme(std::istream& iss)
 std::ostream&
 GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept
 {
-    for (auto section : m_sections)
+    for (auto& section : m_sections)
     {
         const auto& [s_name, s_vec] = section;
         oss << "\n; Section: " << s_name << "\n\n";
-        for (auto option : s_vec)
+        for (auto& option : s_vec)
         {
             if (!option.is_changed())
                 continue;
@@ -563,11 +568,11 @@ std::ostream&
 GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept
 {
 
-    for (auto section : m_sections)
+    for (auto& section : m_sections)
     {
         const auto& [s_name, s_vec] = section;
         oss << "[Options]\n";
-        for (auto option : s_vec)
+        for (auto& option : s_vec)
         {
             if (option.is_changed())
                 oss << section.first.substr(0, classifier_size_max) <<
@@ -601,10 +606,10 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
 {
     if (clear_options)
         qof_book_options_delete(book, nullptr);
-    for (auto section : m_sections)
+    for (auto& section : m_sections)
     {
         const auto& [s_name, s_vec] = section;
-        for (auto option : s_vec)
+        for (auto& option : s_vec)
             if (option.is_changed())
             {
                 // qof_book_set_option wants a GSList path. Let's avoid allocating and make one here.
@@ -619,7 +624,7 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
                 }
                 else if (type > GncOptionUIType::DATE_FORMAT)
                 {
-                    QofInstance* inst{QOF_INSTANCE(option.get_value<QofInstance*>())};
+                    const QofInstance* inst{QOF_INSTANCE(option.get_value<const QofInstance*>())};
                     auto guid = guid_copy(qof_instance_get_guid(inst));
                     auto kvp{new KvpValue(guid)};
                     qof_book_set_option(book, kvp, &list_head);
@@ -641,13 +646,14 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
 void
 GncOptionDB::load_from_kvp(QofBook* book) noexcept
 {
-    for (auto section : m_sections)
+    for (auto& section : m_sections)
     {
-        const auto& [s_name, s_vec] = section;
-        for (auto option : s_vec)
+        auto& [s_name, s_vec] = section;
+        for (auto& option : s_vec)
         {
             /* qof_book_set_option wants a GSList path. Let's avoid allocating
-             * and make one here. */
+             * and make one here.
+             */
             GSList list_tail{(void*)option.get_name().c_str(), nullptr};
             GSList list_head{(void*)s_name.c_str(), &list_tail};
             auto kvp = qof_book_get_option(book, &list_head);
@@ -670,7 +676,7 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept
                 case KvpValue::Type::GUID:
                 {
                     auto guid{kvp->get<GncGUID*>()};
-                    option.set_value(qof_instance_from_guid(guid, option.get_ui_type()));
+                    option.set_value((const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type()));
                     break;
                 }
                 default:
@@ -723,7 +729,7 @@ gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, GncBudget *value)
 {
-    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
             GncOptionUIType::BUDGET};
     db->register_option(section, std::move(option));
 }
@@ -743,7 +749,7 @@ gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, gnc_commodity *value)
 {
-    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
             GncOptionUIType::COMMODITY};
     db->register_option(section, std::move(option));
 }
@@ -880,7 +886,8 @@ gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
 }
 
 /* Only balance-forecast.scm, hello-world.scm, and net-charts.scm
- * use decimals and fractional steps and they can be worked around. */
+ * use decimals and fractional steps and they can be worked around.
+ */
 void
 gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section,
                                  const char* name, const char* key,
@@ -908,7 +915,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
                           const char* name, const char* key,
                           const char* doc_string, QofQuery* value)
 {
-    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
             GncOptionUIType::INTERNAL};
     db->register_option(section, std::move(option));
 }
@@ -928,7 +935,7 @@ gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section,
                             const char* name, const char* key,
                             const char* doc_string, GncInvoice* value)
 {
-    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
             GncOptionUIType::INVOICE};
     db->register_option(section, std::move(option));
 }
@@ -938,7 +945,7 @@ gnc_register_owner_option(const GncOptionDBPtr& db, const char* section,
                           const char* name, const char* key,
                           const char* doc_string, GncOwner* value)
 {
-    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
             GncOptionUIType::OWNER};
     db->register_option(section, std::move(option));
 }
@@ -948,7 +955,7 @@ gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, GncTaxTable* value)
 {
-    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+    GncOption option{section, name, key, doc_string, (const QofInstance*)value,
             GncOptionUIType::TAX_TABLE};
     db->register_option(section, std::move(option));
 }
@@ -989,9 +996,9 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, gnc_commodity *value)
 {
-    GncOption option{GncOptionValidatedValue<QofInstance*>{
-        section, name, key, doc_string, QOF_INSTANCE(value),
-        [](QofInstance* new_value) -> bool
+    GncOption option{GncOptionValidatedValue<const QofInstance*>{
+        section, name, key, doc_string, (const QofInstance*)value,
+        [](const QofInstance* new_value) -> bool
             {
                 return GNC_IS_COMMODITY (new_value) &&
                     gnc_commodity_is_currency(GNC_COMMODITY(new_value));
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 95419f4fc..f29e81774 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -24,102 +24,33 @@
 #ifndef GNC_OPTIONDB_HPP_
 #define GNC_OPTIONDB_HPP_
 
-#include "gnc-option.hpp"
+#include <string>
 #include <functional>
 #include <exception>
 #include <optional>
 #include <iostream>
 extern "C"
 {
+#include <config.h>
+#include <Account.h>
+#include <gnc-budget.h>
+#include <gnc-commodity.h>
 #include <gncInvoice.h>
 #include <gncOwner.h>
 #include <gncTaxTable.h>
 }
+#include "gnc-option.hpp"
+#include <gnc-datetime.hpp>
+
 
 class GncOptionDB;
+using GncOptionAccountList = std::vector<const Account*>;
 
-using GncOptionVec = std::vector<GncOption>;
-using GncOptionSection = std::pair<std::string, GncOptionVec>;
-class GncOptionDB
-{
-public:
-    GncOptionDB();
-    GncOptionDB(QofBook* book);
-    ~GncOptionDB() = default;
-
-    void save_to_book(QofBook* book, bool do_clear) const;
-    int num_sections() const noexcept { return m_sections.size(); }
-    bool get_changed() const noexcept { return m_dirty; }
-    void register_option(const char* section, GncOption&& option);
-    void unregister_option(const char* section, const char* name);
-    void set_default_section(const char* section);
-    const GncOptionSection* const get_default_section() const noexcept;
-    void set_ui_item(const char* section, const char* name, GncOptionUIItem* ui_item);
-    GncOptionUIItem* const get_ui_item(const char* section, const char* name);
-    GncOptionUIType get_ui_type(const char* section, const char* name);
-    void set_ui_from_option(const char* section, const char* name,
-                            std::function<void(GncOption&)> func);
-    void set_option_from_ui(const char* section, const char* name,
-                            std::function<void(GncOption&)> func);
-    std::string lookup_string_option(const char* section,
-                                            const char* name);
-    template <typename ValueType>
-    bool set_option(const char* section, const char* name, ValueType value)
-    {
-        try
-        {
-            auto option{find_option(section, name)};
-            if (!option)
-                return false;
-            option->get().set_value(value);
-            return true;
-        }
-        catch(const std::invalid_argument& err)
-        {
-            printf("Set Failed: %s\n", err.what());
-            return false;
-        }
-    }
-//    void set_selectable(const char* section, const char* name);
-    void make_internal(const char* section, const char* name);
-    void commit() {};
-    std::optional<std::reference_wrapper<GncOptionSection>> find_section(const std::string& section);
-    std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
-        return static_cast<const GncOptionDB&>(*this).find_option(section, name);
-    }
-    std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) const;
-    std::ostream& save_to_scheme(std::ostream& oss,
-                                 const char* options_prolog) const noexcept;
-    std::istream& load_from_scheme(std::istream& iss) noexcept;
-    std::ostream& save_to_key_value(std::ostream& oss) const noexcept;
-    std::istream& load_from_key_value(std::istream& iss);
-    void save_to_kvp(QofBook* book, bool clear_book) const noexcept;
-    void load_from_kvp(QofBook* book) noexcept;
-    std::ostream& save_option_scheme(std::ostream& oss,
-                                     const char* option_prolog,
-                                     const std::string& section,
-                                     const std::string& name) const noexcept;
-    std::istream& load_option_scheme(std::istream& iss);
-    std::ostream& save_option_key_value(std::ostream& oss,
-                                        const std::string& section,
-                                        const std::string& name) const noexcept;
-    std::istream& load_option_key_value(std::istream& iss);
-private:
-    std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
-    std::vector<GncOptionSection> m_sections;
-    bool m_dirty = false;
-
-    std::function<GncOptionUIItem*()> m_get_ui_value;
-    std::function<void(GncOptionUIItem*)> m_set_ui_value;
-    static constexpr char const* const scheme_tags[]
-    {
-        "(let ((option (gnc:lookup-option ",
-        "                                 ",
-        ")))",
-        "   ((lambda (o) (if o (gnc:option-set-value o ",
-        "))) option))"
-        };
-};
+using GncOptionAccountTypeList = std::vector<GNCAccountType>;
+using GncMultiChoiceOptionEntry = std::tuple<const std::string,
+                                             const std::string,
+                                             const std::string>;
+using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
 
 /**
  * Extract a list of accounts in the book having one of the GNCAccountTypes in
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 9bad4093f..08f025195 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -46,6 +46,7 @@ extern "C"
 #include <gnc-engine-guile.h>
 }
 #include "gnc-optiondb.hpp"
+#include "gnc-optiondb-impl.hpp"
 extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
 %}
 
@@ -299,28 +300,30 @@ using Account = struct account_s;
 wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
 %include "gnc-option.hpp"
+%include "gnc-option-impl.hpp"
 %include "gnc-optiondb.hpp"
+%include "gnc-optiondb-impl.hpp"
 
 %extend GncOption {
-    SCM get_scm_value() const
+    SCM get_scm_value()
     {
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_value()};
                 return scm_from_value(static_cast<decltype(value)>(value));
-            }, $self->_get_option());
+            }, *($self->_get_option()));
     }
-    SCM get_scm_default_value() const
+    SCM get_scm_default_value()
     {
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_default_value()};
                 return scm_from_value(static_cast<decltype(value)>(value));
-            }, $self->_get_option());
+            }, *($self->_get_option()));
     }
     void set_value_from_scm(SCM new_value)
     {
         std::visit([new_value](auto& option) {
                 option.set_value(scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value));
-            }, $self->_get_option());
+            }, *($self->_get_option()));
     }
 };
 
@@ -331,6 +334,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
  };
 
 %inline %{
+    using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
+
     static SCM
     gnc_option_value(const GncOptionDBPtr& optiondb, const char* section,
                      const char* name)
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 628207f7e..deb1da2e9 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -31,6 +31,7 @@ add_app_utils_test(test-sx test-sx.cpp)
 
 set(gtest_gnc_option_SOURCES
   ${MODULEPATH}/gnc-option.cpp
+  ${MODULEPATH}/gnc-option-impl.cpp
   ${MODULEPATH}/gnc-optiondb.cpp
   gtest-gnc-option.cpp
   gtest-gnc-optiondb.cpp)
@@ -98,6 +99,7 @@ if (HAVE_SRFI64)
     )
   add_library(swig-gnc-optiondb MODULE
     ${MODULEPATH}/gnc-option.cpp
+    ${MODULEPATH}/gnc-option-impl.cpp
     ${MODULEPATH}/gnc-optiondb.cpp
     ${SWIG_GNC_OPTIONDB_GUILE_CPP}
     )
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 99ae8897a..08aa941b6 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -23,9 +23,15 @@
 
 #include <gtest/gtest.h>
 #include <gnc-option.hpp>
+#include <gnc-option-impl.hpp>
 #include <guid.hpp>
 extern "C"
 {
+#include <config.h>
+#include <qof.h>
+#include <Account.h>
+#include <gnc-budget.h>
+#include <gnc-commodity.h>
 #include <gnc-date.h>
 #include <time.h>
 #include <gnc-ui-util.h>
@@ -207,7 +213,7 @@ TEST_F(GncOptionTest, test_budget_ctor)
     auto budget = gnc_budget_new(m_book);
     EXPECT_NO_THROW({
             GncOption option("foo", "bar", "baz", "Phony Option",
-                             QOF_INSTANCE(budget));
+                             (const QofInstance*)budget);
         });
     gnc_budget_destroy(budget);
 }
@@ -215,7 +221,7 @@ TEST_F(GncOptionTest, test_budget_ctor)
 TEST_F(GncOptionTest, test_budget_out)
 {
     auto budget = gnc_budget_new(m_book);
-    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)};
+    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)budget};
 
     auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
     std::ostringstream oss;
@@ -229,16 +235,16 @@ TEST_F(GncOptionTest, test_budget_in)
     auto budget = gnc_budget_new(m_book);
     auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
     std::istringstream iss{budget_guid};
-    GncOption option{GncOptionValue<QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
+    GncOption option{GncOptionValue<const QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
     iss >> option;
-    EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<QofInstance*>());
+    EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<const QofInstance*>());
     gnc_budget_destroy(budget);
 }
 
 TEST_F(GncOptionTest, test_budget_to_scheme)
 {
     auto budget = gnc_budget_new(m_book);
-    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)};
+    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)budget};
 
     auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
     std::ostringstream oss;
@@ -256,9 +262,9 @@ TEST_F(GncOptionTest, test_budget_from_scheme)
     budget_guid.insert(0, "\"");
     budget_guid += "\"";
     std::istringstream iss{budget_guid};
-    GncOption option{GncOptionValue<QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
+    GncOption option{GncOptionValue<const QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
     option.from_scheme(iss);
-    EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<QofInstance*>());
+    EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<const QofInstance*>());
     gnc_budget_destroy(budget);
 }
 
@@ -268,7 +274,7 @@ TEST_F(GncOptionTest, test_commodity_ctor)
                                     "NYSE", "HPE", NULL, 1);
     EXPECT_NO_THROW({
             GncOption option("foo", "bar", "baz", "Phony Option",
-                             QOF_INSTANCE(hpe));
+                             (const QofInstance*)hpe);
         });
     gnc_commodity_destroy(hpe);
 }
@@ -320,9 +326,9 @@ make_currency_option (const char* section, const char* name,
                       const char* key, const char* doc_string,
                       gnc_commodity *value, bool is_currency=false)
 {
-    GncOption option{GncOptionValidatedValue<QofInstance*>{
-        section, name, key, doc_string, QOF_INSTANCE(value),
-        [](QofInstance* new_value) -> bool
+    GncOption option{GncOptionValidatedValue<const QofInstance*>{
+        section, name, key, doc_string, (const QofInstance*)value,
+        [](const QofInstance* new_value) -> bool
             {
                 return GNC_IS_COMMODITY (new_value) &&
                     gnc_commodity_is_currency(GNC_COMMODITY(new_value));
@@ -349,25 +355,26 @@ TEST_F(GncOptionCommodityTest, test_currency_ctor)
 
 TEST_F(GncOptionCommodityTest, test_currency_setter)
 {
-    auto option = make_currency_option("foo", "bar", "baz", "Phony Option", m_eur, true);
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
+                                      m_eur, true);
     EXPECT_NO_THROW({
-            option.set_value(QOF_INSTANCE(m_usd));
+            option.set_value((const QofInstance*)m_usd);
         });
     EXPECT_PRED2(gnc_commodity_equal, m_usd,
-                 GNC_COMMODITY(option.get_value<QofInstance*>()));
+                 GNC_COMMODITY(option.get_value<const QofInstance*>()));
     EXPECT_THROW({
-            option.set_value(QOF_INSTANCE(m_hpe));
+            option.set_value((const QofInstance*)m_hpe);
         }, std::invalid_argument);
     EXPECT_PRED2(gnc_commodity_equal, m_usd,
-                 GNC_COMMODITY(option.get_value<QofInstance*>()));
+                 GNC_COMMODITY(option.get_value<const QofInstance*>()));
 }
 
 TEST_F(GncOptionCommodityTest, test_currency_validator)
 {
     auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
-                                       m_eur, true);
-    EXPECT_TRUE(option.validate(QOF_INSTANCE(m_usd)));
-    EXPECT_FALSE(option.validate(QOF_INSTANCE(m_aapl)));
+                                      m_eur, true);
+    EXPECT_TRUE(option.validate((const QofInstance*)m_usd));
+    EXPECT_FALSE(option.validate((const QofInstance*)m_aapl));
 }
 
 static inline std::string make_currency_str(gnc_commodity* cur)
@@ -416,7 +423,7 @@ TEST_F(GncOptionCommodityTest, test_currency_out)
 
 TEST_F(GncOptionCommodityTest, test_commodity_out)
 {
-    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_hpe),
+    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe,
                      GncOptionUIType::COMMODITY};
     std::string hpe_str{make_commodity_str(m_hpe)};
     std::ostringstream oss;
@@ -439,19 +446,19 @@ TEST_F(GncOptionCommodityTest, test_currency_in)
             std::string usd_str{make_currency_str(m_usd)};
             std::istringstream iss{usd_str};
             iss >> option;
-            EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<QofInstance*>());
+            EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<const QofInstance*>());
         });
 }
 
 TEST_F(GncOptionCommodityTest, test_commodity_in)
 {
-    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_aapl),
+    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl,
                      GncOptionUIType::COMMODITY};
 
     std::string hpe_str{make_commodity_str(m_hpe)};
     std::istringstream iss{hpe_str};
     iss >> option;
-    EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<QofInstance*>());
+    EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
 }
 
 TEST_F(GncOptionCommodityTest, test_currency_to_scheme)
@@ -467,7 +474,7 @@ TEST_F(GncOptionCommodityTest, test_currency_to_scheme)
 
 TEST_F(GncOptionCommodityTest, test_commodity_to_scheme)
 {
-    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_hpe),
+    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe,
                      GncOptionUIType::COMMODITY};
 
     std::string hpe_str{make_commodity_SCM_str(m_hpe)};
@@ -484,18 +491,18 @@ TEST_F(GncOptionCommodityTest, test_currency_from_scheme)
     std::string usd_str{make_currency_SCM_str(m_usd)};
     std::istringstream iss{usd_str};
     option.from_scheme(iss);
-    EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<QofInstance*>());
+    EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<const QofInstance*>());
 }
 
 TEST_F(GncOptionCommodityTest, test_commodity_from_scheme)
 {
-    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_aapl),
+    GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl,
                      GncOptionUIType::COMMODITY};
 
     std::string hpe_str{make_commodity_SCM_str(m_hpe)};
     std::istringstream iss{hpe_str};
     option.from_scheme(iss);
-    EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<QofInstance*>());
+    EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
 }
 
 class GncUIItem
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 2be2c3e7b..5d3db8021 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -23,6 +23,7 @@
 
 #include <gtest/gtest.h>
 #include <gnc-optiondb.hpp>
+#include <gnc-optiondb-impl.hpp>
 #include <kvp-value.hpp>
 extern "C"
 {
@@ -140,7 +141,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_STOCK});
-    EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
     EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
 }
 
@@ -152,7 +153,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", accsel,
                                              {ACCT_TYPE_STOCK});
-    EXPECT_EQ(1U, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
     EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(0));
 }
 
@@ -408,7 +409,7 @@ TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input)
     EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[0]);
     m_db->load_option_scheme(iss);
     EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
 
 }
 
@@ -448,7 +449,7 @@ TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input)
     EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
     EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value<time64>());
     EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
 
 }
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index fce91bb81..39773851c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -529,6 +529,7 @@ libgnucash/app-utils/gnc-helpers.c
 libgnucash/app-utils/gnc-help-utils.c
 libgnucash/app-utils/gnc-option.cpp
 libgnucash/app-utils/gnc-optiondb.cpp
+libgnucash/app-utils/gnc-option-impl.cpp
 libgnucash/app-utils/gnc-prefs-utils.c
 libgnucash/app-utils/gnc-state.c
 libgnucash/app-utils/gnc-sx-instance-model.c

commit 20b3ef8a892fe55fff30a3e946ceaf2f4528c008
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jan 23 10:24:42 2020 -0800

    Handle some minor differences between libc++ (clang) and libstdc++ (gcc).

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index d10e99cb1..2ceff7cbc 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -409,7 +409,10 @@ gnc_option_from_scheme (std::istream& iss, OptType& opt)
         {
             iss.ignore(strlen(commodity_scm_intro) + 1, '"');
             std::getline(iss, name_space, '"');
+ // libc++ doesn't consume the end character, libstdc++ does
+#ifdef _LIBCPP_VERSION
             iss.ignore(1, '"');
+#endif
         }
         else
             name_space = GNC_COMMODITY_NS_CURRENCY;
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index c87c6bf28..bcde3e597 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -256,7 +256,11 @@ scan_scheme_symbol_from_streambuf(std::streambuf* sbuf)
     return retval;
 }
 
+#ifdef _LIBCPP_VERSION
 static inline void constexpr
+#else
+static inline void
+#endif
 consume_scheme_comment(std::streambuf* sbuf)
 {
     while (sbuf->in_avail() && !is_eol(sbuf->sgetc()))
@@ -273,7 +277,11 @@ scan_scheme_string_from_streambuf(std::streambuf* sbuf)
     return retval;
 }
 
+#ifdef _LIBCPP_VERSION
 static inline void constexpr
+#else
+static inline void
+#endif
 consume_scheme_whitespace(std::streambuf* sbuf)
 {
     while (sbuf->in_avail() && is_whitespace(sbuf->sgetc()))
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index bf1b06151..99ae8897a 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -667,9 +667,9 @@ TEST_F(GncOptionAccountTest, test_test_constructor_and_destructor)
     EXPECT_TRUE(m_root != NULL);
     EXPECT_TRUE(GNC_IS_ACCOUNT(m_root));
     GncOptionAccountList list{list_of_types({ACCT_TYPE_BANK})};
-    EXPECT_EQ(2, list.size());
+    EXPECT_EQ(2U, list.size());
     list = list_of_types({ACCT_TYPE_ASSET, ACCT_TYPE_STOCK});
-    EXPECT_EQ(6, list.size());
+    EXPECT_EQ(6U, list.size());
 }
 
 TEST_F(GncOptionAccountTest, test_option_no_value_constructor)
@@ -685,8 +685,8 @@ TEST_F(GncOptionAccountTest, test_option_value_constructor)
     GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
     GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
             GncOptionUIType::ACCOUNT_LIST, acclist};
-    EXPECT_EQ(2, option.get_value().size());
-    EXPECT_EQ(2, option.get_default_value().size());
+    EXPECT_EQ(2U, option.get_value().size());
+    EXPECT_EQ(2U, option.get_default_value().size());
     EXPECT_EQ(acclist[0], option.get_value()[0]);
 }
 
@@ -695,7 +695,8 @@ TEST_F(GncOptionAccountTest, test_option_no_value_limited_constructor)
     GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
     GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
-            GncOptionUIType::ACCOUNT_LIST, {ACCT_TYPE_BANK}};
+            GncOptionUIType::ACCOUNT_LIST,
+            GncOptionAccountTypeList{ACCT_TYPE_BANK}};
     EXPECT_TRUE(option.get_value().empty());
     EXPECT_TRUE(option.get_default_value().empty());
     EXPECT_EQ(true, option.validate(acclistgood));
@@ -919,13 +920,13 @@ TEST_F(GncMultichoiceOption, test_set_value)
 
 TEST_F(GncMultichoiceOption, test_num_permissible)
 {
-    EXPECT_EQ(4, m_option.num_permissible_values());
+    EXPECT_EQ(4U, m_option.num_permissible_values());
 }
 
 TEST_F(GncMultichoiceOption, test_permissible_value_stuff)
 {
     EXPECT_NO_THROW({
-            EXPECT_EQ(3, m_option.permissible_value_index("corge"));
+            EXPECT_EQ(3U, m_option.permissible_value_index("corge"));
             EXPECT_STREQ("waldo", m_option.permissible_value(1).c_str());
             EXPECT_STREQ("sausage", m_option.permissible_value_name(2).c_str());
             EXPECT_STREQ("thud",
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index a8c46a3d5..2be2c3e7b 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -129,7 +129,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_option)
     auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
     gnc_register_account_list_option(m_db, "foo", "bar", "baz", "Phony Option",
                                     acclist);
-    EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
     EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
 }
 
@@ -140,7 +140,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", acclist,
                                              {ACCT_TYPE_STOCK});
-    EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
     EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
 }
 
@@ -152,7 +152,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
     gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
                                              "Phony Option", accsel,
                                              {ACCT_TYPE_STOCK});
-    EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(1U, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
     EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(0));
 }
 
@@ -408,7 +408,7 @@ TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input)
     EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[0]);
     m_db->load_option_scheme(iss);
     EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
 
 }
 
@@ -448,7 +448,7 @@ TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input)
     EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
     EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value<time64>());
     EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
-    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
 
 }
 

commit cd6ccbe3318d6db8427176fa48900cac58a72492
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jan 23 10:23:14 2020 -0800

    Fix account-tree double-free in scheme test when built with gcc.
    
    Oddly when built with clang it doesn't exhibit the crash.

diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 814ca1a97..d1db9d738 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -83,8 +83,7 @@
       (create-account book expenses ACCT-TYPE-EXPENSE "Rent")))
 
   (define (cleanup book root)
-    (xaccAccountBeginEdit root)
-    (xaccAccountDestroy root)
+;; Destroying the book destroys the account tree too
     (qof-book-destroy book))
 
   (define (test-make-account-list-option book)

commit 691cb0992e6c227bbbeb6de01b4e2c5d67986f50
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jan 23 10:21:18 2020 -0800

    Follow change in loading method for engine's guile bindings.

diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 7ecb2f951..814ca1a97 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -28,7 +28,7 @@
  (compile load eval expand)
  (load-extension "libswig-gnc-optiondb" "scm_init_sw_gnc_optiondb_module"))
 
-(gnc:module-load "gnucash/engine" 0)
+(use-modules (gnucash engine))
 (use-modules (sw_gnc_optiondb))
 
 (define (run-test)

commit cbd0607e80e7c17ec3a0cf3bd588b054cd914167
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 20 11:37:21 2020 -0800

    Implement load and store options from/to book options.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index ec67ef449..f9ac5bc08 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -222,16 +222,14 @@ GncOptionDateValue::in_stream(std::istream& iss)
 }
 
 QofInstance*
-qof_instance_from_string(const std::string& str, GncOptionUIType type)
+qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
 {
     QofIdType qof_type;
-    bool commodity_type{false};
     switch(type)
     {
         case GncOptionUIType::CURRENCY:
         case GncOptionUIType::COMMODITY:
             qof_type = "Commodity";
-            commodity_type = true;
             break;
         case GncOptionUIType::BUDGET:
             qof_type = "Budget";
@@ -264,8 +262,17 @@ qof_instance_from_string(const std::string& str, GncOptionUIType type)
             break;
     }
     auto book{gnc_get_current_book()};
-    if (commodity_type)
+    auto col{qof_book_get_collection(book, qof_type)};
+    return QOF_INSTANCE(qof_collection_lookup_entity(col, guid));
+}
+
+QofInstance*
+qof_instance_from_string(const std::string& str, GncOptionUIType type)
+{
+    if (type == GncOptionUIType::CURRENCY ||
+        type == GncOptionUIType::COMMODITY)
     {
+        auto book{gnc_get_current_book()};
         auto sep{str.find(":")};
         auto name_space{str.substr(0, sep)};
         auto mnemonic{str.substr(sep + 1, -1)};
@@ -275,8 +282,7 @@ qof_instance_from_string(const std::string& str, GncOptionUIType type)
                                                        mnemonic.c_str()));
     }
     auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
-    auto col{qof_book_get_collection(book, qof_type)};
-    return QOF_INSTANCE(qof_collection_lookup_entity(col, &guid));
+    return qof_instance_from_guid(&guid, type);
 }
 
 std::string
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index a6c4b5e91..d10e99cb1 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -264,6 +264,7 @@ private:
 
 QofInstance* qof_instance_from_string(const std::string& str,
                                       GncOptionUIType type);
+QofInstance* qof_instance_from_guid(GncGUID*, GncOptionUIType type);
 std::string qof_instance_to_string(const QofInstance* inst);
 
 /* These will work when m_value is a built-in class; GnuCash class and container
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index becae537a..c87c6bf28 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -24,6 +24,7 @@
 #include "gnc-optiondb.hpp"
 #include <limits>
 #include <sstream>
+#include <kvp-value.hpp>
 
 auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
 GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}
@@ -445,7 +446,10 @@ GncOptionDB::load_option_scheme(std::istream& iss)
     }
 
     if (!lookup_id)
-        throw std::runtime_error("No gnc:lookup-option found");
+    {
+        iss.setstate(std::ios_base::eofbit);
+        return iss; // No options
+    }
     const auto& classifier = lookup_id->get().m_ids;
     if (classifier.size() != 3)
         throw std::runtime_error("Malformed option classifier.");
@@ -473,16 +477,55 @@ GncOptionDB::load_option_scheme(std::istream& iss)
     return iss;
 }
 
+std::ostream&
+GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept
+{
+    for (auto section : m_sections)
+    {
+        const auto& [s_name, s_vec] = section;
+        oss << "\n; Section: " << s_name << "\n\n";
+        for (auto option : s_vec)
+        {
+            if (!option.is_changed())
+                continue;
+            oss << scheme_tags[0] << options_prolog << "\n";
+            oss << scheme_tags[1] << '"' << section.first.substr(0, classifier_size_max) << "\"\n";
+            oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"';
+            oss  <<  scheme_tags[2] << "\n" << scheme_tags[3];
+            option.to_scheme(oss);
+            oss << scheme_tags[4] << "\n\n";
+        }
+    }
+    return oss;
+}
+
+std::istream&
+GncOptionDB::load_from_scheme(std::istream& iss) noexcept
+{
+    try {
+        while (iss.good())
+            load_option_scheme(iss);
+        iss.clear(); //unset eofbit and maybe failbit
+    }
+    catch (const std::runtime_error& err)
+    {
+        std::cerr << "Load of options from Scheme failed: " <<
+            err.what() << std::endl;
+    }
+    return iss;
+}
+
 std::ostream&
 GncOptionDB::save_option_key_value(std::ostream& oss,
-                                        const char* section,
-                                        const char* name) const noexcept
+                                   const std::string& section,
+                                   const std::string& name) const noexcept
 {
 
     auto db_opt = find_option(section, name);
     if (!db_opt || !db_opt->get().is_changed())
         return oss;
-    oss << section << ":" << name << "=" << db_opt->get() << ";";
+    oss << section.substr(0, classifier_size_max) << ":" <<
+        name.substr(0, classifier_size_max) << "=" << db_opt->get() << ";";
     return oss;
 }
 
@@ -508,6 +551,128 @@ GncOptionDB::load_option_key_value(std::istream& iss)
     return iss;
 }
 
+std::ostream&
+GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept
+{
+
+    for (auto section : m_sections)
+    {
+        const auto& [s_name, s_vec] = section;
+        oss << "[Options]\n";
+        for (auto option : s_vec)
+        {
+            if (option.is_changed())
+                oss << section.first.substr(0, classifier_size_max) <<
+                    ':' << option.get_name().substr(0, classifier_size_max) <<
+                    '=' << option << '\n';
+       }
+    }
+    return oss;
+}
+
+std::istream&
+GncOptionDB::load_from_key_value(std::istream& iss)
+{
+    if (iss.peek() == '[')
+    {
+        char buf[classifier_size_max];
+        iss.getline(buf, classifier_size_max);
+        if (strcmp(buf, "[Options]") != 0) // safe
+            throw std::runtime_error("Wrong secion header for options.");
+    }
+    // Otherwise assume we were sent here correctly:
+    while (iss.peek() != '[') //Indicates the start of the next file section
+    {
+        load_option_key_value(iss);
+    }
+    return iss;
+}
+
+void
+GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
+{
+    if (clear_options)
+        qof_book_options_delete(book, nullptr);
+    for (auto section : m_sections)
+    {
+        const auto& [s_name, s_vec] = section;
+        for (auto option : s_vec)
+            if (option.is_changed())
+            {
+                // qof_book_set_option wants a GSList path. Let's avoid allocating and make one here.
+                GSList list_tail{(void*)option.get_name().c_str(), nullptr};
+                GSList list_head{(void*)s_name.c_str(), &list_tail};
+                auto type{option.get_ui_type()};
+                if (type == GncOptionUIType::BOOLEAN)
+                {
+                    auto val{option.get_value<bool>()};
+                    auto kvp{new KvpValue(val ? "t" : "f")};
+                    qof_book_set_option(book, kvp, &list_head);
+                }
+                else if (type > GncOptionUIType::DATE_FORMAT)
+                {
+                    QofInstance* inst{QOF_INSTANCE(option.get_value<QofInstance*>())};
+                    auto guid = guid_copy(qof_instance_get_guid(inst));
+                    auto kvp{new KvpValue(guid)};
+                    qof_book_set_option(book, kvp, &list_head);
+                }
+                else if (type == GncOptionUIType::NUMBER_RANGE)
+                {
+                    auto kvp{new KvpValue(option.get_value<int64_t>())};
+                    qof_book_set_option(book, kvp, &list_head);
+                }
+                else
+                {
+                    auto kvp{new KvpValue{g_strdup(option.get_value<std::string>().c_str())}};
+                    qof_book_set_option(book, kvp, &list_head);
+                }
+            }
+    }
+}
+
+void
+GncOptionDB::load_from_kvp(QofBook* book) noexcept
+{
+    for (auto section : m_sections)
+    {
+        const auto& [s_name, s_vec] = section;
+        for (auto option : s_vec)
+        {
+            /* qof_book_set_option wants a GSList path. Let's avoid allocating
+             * and make one here. */
+            GSList list_tail{(void*)option.get_name().c_str(), nullptr};
+            GSList list_head{(void*)s_name.c_str(), &list_tail};
+            auto kvp = qof_book_get_option(book, &list_head);
+            if (!kvp)
+                continue;
+            switch (kvp->get_type())
+            {
+                case KvpValue::Type::INT64:
+                    option.set_value(kvp->get<int64_t>());
+                    break;
+                case KvpValue::Type::STRING:
+                {
+                    auto str{kvp->get<const char*>()};
+                    if (option.get_ui_type() == GncOptionUIType::BOOLEAN)
+                        option.set_value(*str == 't' ? true : false);
+                    else
+                        option.set_value(str);
+                    break;
+                }
+                case KvpValue::Type::GUID:
+                {
+                    auto guid{kvp->get<GncGUID*>()};
+                    option.set_value(qof_instance_from_guid(guid, option.get_ui_type()));
+                    break;
+                }
+                default:
+                    continue;
+                    break;
+            }
+        }
+    }
+}
+
 GncOptionDBPtr
 gnc_option_db_new(void)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 042e78c46..95419f4fc 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -82,7 +82,7 @@ public:
     }
 //    void set_selectable(const char* section, const char* name);
     void make_internal(const char* section, const char* name);
-    void commit();
+    void commit() {};
     std::optional<std::reference_wrapper<GncOptionSection>> find_section(const std::string& section);
     std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
         return static_cast<const GncOptionDB&>(*this).find_option(section, name);
@@ -92,17 +92,17 @@ public:
                                  const char* options_prolog) const noexcept;
     std::istream& load_from_scheme(std::istream& iss) noexcept;
     std::ostream& save_to_key_value(std::ostream& oss) const noexcept;
-    std::istream& load_from_key_value(std::istream& iss) noexcept;
-    void save_to_kvp() const noexcept;
-    void load_from_kvp() noexcept;
+    std::istream& load_from_key_value(std::istream& iss);
+    void save_to_kvp(QofBook* book, bool clear_book) const noexcept;
+    void load_from_kvp(QofBook* book) noexcept;
     std::ostream& save_option_scheme(std::ostream& oss,
                                      const char* option_prolog,
                                      const std::string& section,
                                      const std::string& name) const noexcept;
     std::istream& load_option_scheme(std::istream& iss);
     std::ostream& save_option_key_value(std::ostream& oss,
-                                             const char* section,
-                                             const char* name) const noexcept;
+                                        const std::string& section,
+                                        const std::string& name) const noexcept;
     std::istream& load_option_key_value(std::istream& iss);
 private:
     std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 9ef8884f3..a8c46a3d5 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -23,6 +23,13 @@
 
 #include <gtest/gtest.h>
 #include <gnc-optiondb.hpp>
+#include <kvp-value.hpp>
+extern "C"
+{
+#include <glib-2.0/glib.h>
+#include <gnc-ui-util.h>
+#include <gnc-session.h>
+}
 
 class GncOptionDBTest : public ::testing::Test
 {
@@ -280,8 +287,34 @@ TEST_F(GncOptionDBUITest, test_option_value_from_ui)
 class GncOptionDBIOTest : public ::testing::Test
 {
 protected:
-    GncOptionDBIOTest() : m_db{gnc_option_db_new()}
+    GncOptionDBIOTest() : m_book{gnc_get_current_book()}, m_root{gnc_account_create_root(m_book)}, m_db{gnc_option_db_new()}
     {
+        auto create_account = [this](Account* parent, GNCAccountType type,
+                                       const char* name)->Account* {
+            auto account = xaccMallocAccount(this->m_book);
+            xaccAccountBeginEdit(account);
+            xaccAccountSetType(account, type);
+            xaccAccountSetName(account, name);
+            xaccAccountBeginEdit(parent);
+            gnc_account_append_child(parent, account);
+            xaccAccountCommitEdit(parent);
+            xaccAccountCommitEdit(account);
+            return account;
+        };
+        auto assets = create_account(m_root, ACCT_TYPE_ASSET, "Assets");
+        auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, "Liabilities");
+        auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, "Expenses");
+        create_account(assets, ACCT_TYPE_BANK, "Bank");
+        auto broker = create_account(assets, ACCT_TYPE_ASSET, "Broker");
+        auto stocks = create_account(broker, ACCT_TYPE_STOCK, "Stocks");
+        auto aapl = create_account(stocks, ACCT_TYPE_STOCK, "AAPL");
+        create_account(stocks, ACCT_TYPE_STOCK, "MSFT");
+        auto hpe = create_account(stocks, ACCT_TYPE_STOCK, "HPE");
+        create_account(broker, ACCT_TYPE_BANK, "Cash Management");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Food");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Gas");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Rent");
+
         gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
                                    std::string{"waldo"});
         gnc_register_text_option(m_db, "foo", "sausage", "links",
@@ -290,8 +323,23 @@ protected:
                                    std::string{""});
         gnc_register_text_option(m_db, "qux", "garply", "fred",
                                    "Phony Option", std::string{"waldo"});
+        gnc_register_date_interval_option(m_db, "pork", "garply", "first",
+                                          "Phony Date Option",
+                                          RelativeDatePeriod::START_CURRENT_QUARTER);
+        gnc_register_account_list_option(m_db, "quux", "xyzzy", "second",
+                                         "Phony AccountList Option",
+                                         {aapl, hpe});
     }
 
+    ~GncOptionDBIOTest()
+    {
+        xaccAccountBeginEdit(m_root);
+        xaccAccountDestroy(m_root); //It does the commit
+        gnc_clear_current_session();
+    }
+
+    QofBook* m_book;
+    Account* m_root;
     GncOptionDBPtr m_db;
 };
 
@@ -313,7 +361,7 @@ TEST_F(GncOptionDBIOTest, test_option_scheme_output)
                  "))) option))\n\n", oss.str().c_str());
 }
 
-TEST_F(GncOptionDBIOTest, test_option_scheme_input)
+TEST_F(GncOptionDBIOTest, test_string_option_scheme_input)
 {
     const char* input{"(let ((option (gnc:lookup-option option\n"
                  "                                 \"foo\"\n"
@@ -326,6 +374,84 @@ TEST_F(GncOptionDBIOTest, test_option_scheme_input)
     EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
 }
 
+TEST_F(GncOptionDBIOTest, test_date_interval_option_scheme_input)
+{
+    const char* input{"(let ((option (gnc:lookup-option option\n"
+                 "                                 \"pork\"\n"
+                 "                                 \"garply\")))\n"
+                 "   ((lambda (o) (if o (gnc:option-set-value o "
+            "'(relative . end-prev-month)"
+            "))) option))\n\n"};
+    std::istringstream iss{input};
+    GDate month_end;
+    g_date_set_time_t(&month_end, time(nullptr));
+    g_date_subtract_months(&month_end, 1);
+    gnc_gdate_set_month_end(&month_end);
+    auto time1 = time64_from_gdate(&month_end, DayPart::end);
+    m_db->load_option_scheme(iss);
+    EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value<time64>());
+
+}
+
+TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input)
+{
+    auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
+    auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))};
+    auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))};
+    std::string input{"(let ((option (gnc:lookup-option option\n"
+                            "                                 \"quux\"\n"
+                            "                                 \"xyzzy\")))\n"
+                            "   ((lambda (o) (if o (gnc:option-set-value o '(\""};
+    input += hpe_guid + "\" \"";
+    input += msft_guid + "\")))) option))\n\n";
+    std::istringstream iss{input};
+    EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[0]);
+    m_db->load_option_scheme(iss);
+    EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
+    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
+
+}
+
+TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input)
+{
+    auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
+    auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))};
+    auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))};
+    std::string input{";; Foo\n\n"
+                      "(let ((option (gnc:lookup-option option\n"
+                      "                                 \"foo\"\n"
+                      "                                 \"sausage\")))\n"
+                      "   ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
+                      "))) option))\n\n"
+                      ";; Pork\n\n"
+                      "(let ((option (gnc:lookup-option option\n"
+                      "                                 \"pork\"\n"
+                      "                                 \"garply\")))\n"
+                      "   ((lambda (o) (if o (gnc:option-set-value o "
+                      "'(relative . end-prev-month)"
+                      "))) option))\n\n"
+                      ";; Quux\n\n"
+                      "(let ((option (gnc:lookup-option option\n"
+                      "                                 \"quux\"\n"
+                      "                                 \"xyzzy\")))\n"
+                      "   ((lambda (o) (if o (gnc:option-set-value o '(\""};
+    input += hpe_guid + "\" \"";
+    input += msft_guid + "\")))) option))\n\n";
+    std::istringstream iss{input};
+    GDate month_end;
+    g_date_set_time_t(&month_end, time(nullptr));
+    g_date_subtract_months(&month_end, 1);
+    gnc_gdate_set_month_end(&month_end);
+    auto time1 = time64_from_gdate(&month_end, DayPart::end);
+    EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[0]);
+    m_db->load_from_scheme(iss);
+    EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
+    EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value<time64>());
+    EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
+    EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
+
+}
+
 TEST_F(GncOptionDBIOTest, test_option_key_value_output)
 {
     std::ostringstream oss;
@@ -345,3 +471,31 @@ TEST_F(GncOptionDBIOTest, test_option_key_value_input)
     m_db->load_option_key_value(iss);
     EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
 }
+
+TEST_F(GncOptionDBIOTest, test_option_kvp_save)
+{
+    m_db->save_to_kvp(m_book, false);
+    auto foo = "foo";
+    auto bar = "bar";
+    auto sausage = "sausage";
+    auto grault = "grault";
+    auto garply = "garply";
+    GSList foo_bar_tail{(void*)bar, nullptr};
+    GSList foo_bar_head{(void*)foo, &foo_bar_tail};
+    GSList foo_sausage_tail{(void*)sausage, nullptr};
+    GSList foo_sausage_head{(void*)foo, &foo_sausage_tail};
+    GSList qux_grault_tail{(void*)grault, nullptr};
+    GSList qux_grault_head{(void*)foo, &qux_grault_tail};
+    GSList qux_garply_tail{(void*)garply, nullptr};
+    GSList qux_garply_head{(void*)foo, &qux_grault_tail};
+    m_db->set_option("foo", "sausage", std::string{"pepper"});
+    m_db->save_to_kvp(m_book, true);
+    auto foo_bar = qof_book_get_option(m_book, &foo_bar_head);
+    auto foo_sausage = qof_book_get_option(m_book, &foo_sausage_head);
+    auto qux_garply = qof_book_get_option(m_book, &qux_garply_head);
+    auto qux_grault = qof_book_get_option(m_book, &qux_grault_head);
+    EXPECT_EQ(nullptr, foo_bar);
+    EXPECT_EQ(nullptr, qux_garply);
+    EXPECT_EQ(nullptr, qux_grault);
+    EXPECT_STREQ("pepper", foo_sausage->get<const char*>());
+}

commit 5a82aac639230c20b7ee77116a9287ad4e54af96
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 20 11:36:37 2020 -0800

    Hide constexpr constants from SWIG.
    
    Swig bindings don't need them and SWIG can't digest constexpr.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index a5adc3198..a6c4b5e91 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -118,9 +118,10 @@ enum GncOptionUIType
 };
 
 static const char* commodity_scm_intro{"'(commodity-scm "};
-
+#ifndef SWIG
 size_t constexpr classifier_size_max{50};
 size_t constexpr sort_tag_size_max{10};
+#endif
 
 struct OptionClassifier
 {
@@ -181,7 +182,9 @@ private:
     GncOptionUIType m_ui_type;
 };
 
+#ifndef SWIG
 auto constexpr size_t_max = std::numeric_limits<std::size_t>::max();
+#endif
 
 template <typename ValueType>
 class GncOptionValue : public OptionClassifier, public OptionUIItem

commit ffc68664063d65c32378fbff370be4db7cf8fdb1
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Jan 20 11:32:10 2020 -0800

    Changes to accommodate moving guile engine bindings to bindings/guile.
    
    Plus for changing targets gncmod-engine and gncmod-app-utils to
    gnc-engine and gnc-app-utils.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 77ec297b1..815cd28f6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -569,7 +569,7 @@ endif()
 
 add_definitions(-D_GNU_SOURCE)
 
-# Also, set the C++ version to c++11
+# Set up the language standards:
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index a5fee701b..9bad4093f 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -39,7 +39,12 @@ namespace std {
 
 %module sw_gnc_optiondb
 %{
+extern "C"
+{
+#include <config.h>
 #include <libguile.h>
+#include <gnc-engine-guile.h>
+}
 #include "gnc-optiondb.hpp"
 extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
 %}
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index dc41f3471..628207f7e 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -43,8 +43,8 @@ set(gtest_gnc_option_INCLUDES
   ${GUILE_INCLUDE_DIRS})
 
 set(gtest_gnc_option_LIBS
-  gncmod-app-utils
-  gncmod-engine
+  gnc-app-utils
+  gnc-engine
   ${GLIB2_LDFLAGS}
   ${GUILE_LDFLAGS}
   gtest)
@@ -94,6 +94,7 @@ if (HAVE_SRFI64)
   gnc_add_swig_guile_command(swig-gnc-optiondb-guile
     SWIG_GNC_OPTIONDB_GUILE_CPP swig-gnc-optiondb-guile.cpp
     ${MODULEPATH}/gnc-optiondb.i
+    ""
     )
   add_library(swig-gnc-optiondb MODULE
     ${MODULEPATH}/gnc-option.cpp
@@ -102,6 +103,7 @@ if (HAVE_SRFI64)
     )
   set(swig_gnc_optiondb_INCLUDES
     ${MODULEPATH}
+    ${CMAKE_SOURCE_DIR}/bindings/guile
     ${CMAKE_SOURCE_DIR}/libgnucash/engine
     ${CMAKE_BINARY_DIR}/common # for config.h
     ${GLIB2_INCLUDE_DIRS}
@@ -109,8 +111,9 @@ if (HAVE_SRFI64)
     )
 
   set(swig_gnc_optiondb_LIBS
-    gncmod-engine
-    gncmod-app-utils
+    gnc-engine
+    gnc-app-utils
+    gnucash-guile
     ${GLIB2_LDFLAGS}
     ${GUILE_LDFLAGS}
     )
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0f92fb3c2..fce91bb81 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -527,7 +527,6 @@ libgnucash/app-utils/gnc-exp-parser.c
 libgnucash/app-utils/gnc-gsettings.c
 libgnucash/app-utils/gnc-helpers.c
 libgnucash/app-utils/gnc-help-utils.c
-libgnucash/app-utils/gncmod-app-utils.c
 libgnucash/app-utils/gnc-option.cpp
 libgnucash/app-utils/gnc-optiondb.cpp
 libgnucash/app-utils/gnc-prefs-utils.c

commit d2535fe21bf599eb58803673ca58b9e050628a3a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Jan 19 15:05:28 2020 -0800

    KvpValue: Use boost::typeindex::type_id instead of buildtin typeid.
    
    Under some build conditions boost::typeindex will use an internal type
    identifier that's different from the C++ builtin. In that case type equality
    tests to C++ typeid in boost::variant will fail, breaking it. Using
    boost::typeindex::type_id ensures that the comparisons always work.

diff --git a/libgnucash/engine/kvp-value.cpp b/libgnucash/engine/kvp-value.cpp
index bbfedf6aa..456138a39 100644
--- a/libgnucash/engine/kvp-value.cpp
+++ b/libgnucash/engine/kvp-value.cpp
@@ -29,6 +29,8 @@
 #include <iomanip>
 #include <stdexcept>
 
+using boost::typeindex::type_id;
+
 KvpValueImpl::KvpValueImpl(KvpValueImpl const & other) noexcept
 {
     duplicate(other);
@@ -58,7 +60,7 @@ KvpValueImpl *
 KvpValueImpl::add(KvpValueImpl * val) noexcept
 {
     /* If already a glist here, just append */
-    if (this->datastore.type() == typeid(GList*))
+    if (this->datastore.type() == type_id<GList*>())
     {
         GList * list = boost::get<GList*>(datastore);
         datastore = g_list_append (list, val);
@@ -75,23 +77,23 @@ KvpValueImpl::add(KvpValueImpl * val) noexcept
 KvpValue::Type
 KvpValueImpl::get_type() const noexcept
 {
-    if (datastore.type() == typeid(int64_t))
+    if (datastore.type() == type_id<int64_t>())
         return KvpValue::Type::INT64;
-    else if (datastore.type() == typeid(double))
+    else if (datastore.type() == type_id<double>())
         return KvpValue::Type::DOUBLE;
-    else if (datastore.type() == typeid(gnc_numeric))
+    else if (datastore.type() == type_id<gnc_numeric>())
         return KvpValue::Type::NUMERIC;
-    else if (datastore.type() == typeid(const gchar *))
+    else if (datastore.type() == type_id<const gchar *>())
         return KvpValue::Type::STRING;
-    else if (datastore.type() == typeid(GncGUID *))
+    else if (datastore.type() == type_id<GncGUID *>())
         return KvpValue::Type::GUID;
-    else if (datastore.type() == typeid(Time64))
+    else if (datastore.type() == type_id<Time64>())
         return KvpValue::Type::TIME64;
-    else if (datastore.type() == typeid(GList *))
+    else if (datastore.type() == type_id<GList *>())
         return KvpValue::Type::GLIST;
-    else if (datastore.type() == typeid(KvpFrameImpl *))
+    else if (datastore.type() == type_id<KvpFrameImpl *>())
         return KvpValue::Type::FRAME;
-    else if (datastore.type() == typeid(GDate))
+    else if (datastore.type() == type_id<GDate>())
         return KvpValue::Type::GDATE;
 
     return KvpValue::Type::INVALID;
@@ -100,7 +102,7 @@ KvpValueImpl::get_type() const noexcept
 KvpFrame *
 KvpValueImpl::replace_frame_nc (KvpFrame * new_value) noexcept
 {
-    if (datastore.type() != typeid(KvpFrame *))
+    if (datastore.type() != type_id<KvpFrame *>())
         return {};
     auto ret = boost::get<KvpFrame *>(datastore);
     datastore = new_value;
@@ -110,7 +112,7 @@ KvpValueImpl::replace_frame_nc (KvpFrame * new_value) noexcept
 GList *
 KvpValueImpl::replace_glist_nc (GList * new_value) noexcept
 {
-    if (datastore.type() != typeid(GList *))
+    if (datastore.type() != type_id<GList *>())
         return {};
     auto ret = boost::get<GList *>(datastore);
     datastore = new_value;
@@ -206,7 +208,7 @@ struct to_string_visitor : boost::static_visitor<void>
 std::string
 KvpValueImpl::to_string(std::string const & prefix) const noexcept
 {
-    if (this->datastore.type() == typeid(KvpFrame*))
+    if (this->datastore.type() == type_id<KvpFrame*>())
         return this->get<KvpFrame*>()->to_string(prefix);
     std::ostringstream ret;
     to_string_visitor visitor {ret};
@@ -390,13 +392,13 @@ KvpValueImpl::~KvpValueImpl() noexcept
 void
 KvpValueImpl::duplicate(const KvpValueImpl& other) noexcept
 {
-    if (other.datastore.type() == typeid(const gchar *))
+    if (other.datastore.type() == type_id<const gchar *>())
         this->datastore = const_cast<const gchar *>(g_strdup(other.get<const gchar *>()));
-    else if (other.datastore.type() == typeid(GncGUID*))
+    else if (other.datastore.type() == type_id<GncGUID*>())
         this->datastore = guid_copy(other.get<GncGUID *>());
-    else if (other.datastore.type() == typeid(GList*))
+    else if (other.datastore.type() == type_id<GList*>())
         this->datastore = kvp_glist_copy(other.get<GList *>());
-    else if (other.datastore.type() == typeid(KvpFrame*))
+    else if (other.datastore.type() == type_id<KvpFrame*>())
         this->datastore = new KvpFrame(*other.get<KvpFrame *>());
     else
         this->datastore = other.datastore;
diff --git a/libgnucash/engine/kvp-value.hpp b/libgnucash/engine/kvp-value.hpp
index 61b7dfd99..f5f176870 100644
--- a/libgnucash/engine/kvp-value.hpp
+++ b/libgnucash/engine/kvp-value.hpp
@@ -174,7 +174,7 @@ KvpValueImpl::KvpValueImpl(T newvalue) noexcept:
 template <typename T> T
 KvpValueImpl::get() const noexcept
 {
-    if (this->datastore.type() != typeid(T)) return {};
+    if (this->datastore.type() != boost::typeindex::type_id<T>()) return {};
     return boost::get<T>(datastore);
 }
 

commit 009219c63d3f6a075a51fdfd28fbd5c113d42294
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 14 16:44:16 2019 -0800

    Much less ugly and fragile.
    
    Finding the right form now relies on the form name instead of its
    position in the parse tree.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index d09fa2317..becae537a 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -406,24 +406,47 @@ unquote_scheme_string(const std::string& str)
     return str;
 }
 
+static inline bool
+string_equal_charptr(const std::string& str, const char* chars)
+{
+    if (!chars || str.empty() || strlen(chars) != str.size())
+        return false;
+    return strcmp(str.c_str(), chars) == 0;
+}
+
+static std::optional<std::reference_wrapper<const SchemeId>>
+find_form(const SchemeId& toplevel, IdentType type, const char* name)
+{
+    if (toplevel.m_type == type &&
+        string_equal_charptr(toplevel.m_name.c_str(), name))
+        return std::ref(toplevel);
+    for (const auto& id : toplevel.m_ids)
+    {
+        if (id.m_type == type && string_equal_charptr(id.m_name.c_str(), name))
+            return std::ref(id);
+        auto child{find_form(id, type, name)};
+        if (child)
+            return child;
+    }
+    return std::nullopt;
+}
+
 std::istream&
 GncOptionDB::load_option_scheme(std::istream& iss)
 {
     auto sbuf{iss.rdbuf()};
     SchemeId toplevel;
+    std::optional<std::reference_wrapper<const SchemeId>> lookup_id;
     bool form_found = false;
-    while (sbuf->in_avail()  && !form_found)
+    while (sbuf->in_avail()  && !lookup_id)
     {
         scan_scheme_id_from_streambuf(sbuf, toplevel);
-        if (toplevel.m_type == IdentType::FORM &&
-            toplevel.m_name == "let" && toplevel.m_ids.size() == 2)
-            if (const auto& let_block{toplevel.m_ids[0]};
-                let_block.m_ids.size() == 1)
-                if (const auto& first_form{let_block.m_ids[0]};
-                    first_form.m_name == "option")
-                    form_found = true;
+        lookup_id = find_form(toplevel, IdentType::FORM, "gnc:lookup-option");
     }
-    const auto& classifier = toplevel.m_ids[0].m_ids[0].m_ids[0].m_ids;
+
+    if (!lookup_id)
+        throw std::runtime_error("No gnc:lookup-option found");
+    const auto& classifier = lookup_id->get().m_ids;
     if (classifier.size() != 3)
         throw std::runtime_error("Malformed option classifier.");
     const auto& section = unquote_scheme_string(classifier[1].m_name);
@@ -438,22 +461,14 @@ GncOptionDB::load_option_scheme(std::istream& iss)
         err += option_str;
         throw std::runtime_error(err);
     }
-    if (!(toplevel.m_type == IdentType::FORM && toplevel.m_ids.size() == 2 &&
-          toplevel.m_ids[1].m_type == IdentType::FORM &&
-          toplevel.m_ids[1].m_ids.size() == 2 &&
-          toplevel.m_ids[1].m_ids[0].m_type == IdentType::FORM &&
-          toplevel.m_ids[1].m_ids[0].m_ids.size() == 2 &&
-          toplevel.m_ids[1].m_ids[0].m_ids[1].m_type == IdentType::FORM &&
-          toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids.size() == 2 &&
-          toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_type == IdentType::FORM &&
-          toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_ids.size() == 2))
+    auto value_id = find_form(toplevel, IdentType::FORM, "gnc:option-set-value");
+    if (!(value_id && value_id->get().m_ids.size() == 2))
     {
         std::string err{"Option "};
         err += option_str;
         throw std::runtime_error(err + " malformed value lambda form.");
     }
-    auto value_id = toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_ids[1];
-    std::istringstream value_iss{value_id.m_name};
+    std::istringstream value_iss{value_id->get().m_ids[1].m_name};
     option->get().from_scheme(value_iss);
     return iss;
 }

commit 276641ef157a189e34b559fc70014a8878a8d9d5
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 14 15:43:24 2019 -0800

    Change parse of option input to generate a parse tree.
    
    This is I hope less brittle than the previous character-counting, though
    it's still brittle because it relies instead on counting form-depth.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 239a21f06..d09fa2317 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -196,30 +196,265 @@ GncOptionDB::save_option_scheme(std::ostream& oss,
 
     return oss;
 }
+
+static inline bool constexpr
+is_eol(char c)
+{
+    return c == '\n';
+}
+
+static inline bool constexpr
+is_whitespace(char c)
+{
+    return c == ' ' || c == '\n' || c == '\t';
+}
+
+static inline bool constexpr
+is_begin_paren(char c)
+{
+    return c == '(';
+}
+
+static inline bool constexpr
+is_end_paren(char c)
+{
+    return c == ')';
+}
+
+static inline bool constexpr
+is_double_quote(char c)
+{
+    return c == '"';
+}
+
+static inline bool constexpr
+is_single_quote(char c)
+{
+    return c == '\'';
+}
+
+static inline bool constexpr
+is_semicolon(char c)
+{
+    return c == ';';
+}
+
+static inline bool constexpr
+is_delim(char c)
+{
+    return is_begin_paren(c) || is_end_paren(c) || is_whitespace(c) ||
+        is_single_quote(c) || is_double_quote(c) || is_semicolon(c);
+}
+
+static std::string
+scan_scheme_symbol_from_streambuf(std::streambuf* sbuf)
+{
+    std::string retval;
+    while(sbuf->in_avail() && !is_delim(sbuf->sgetc()))
+        retval += sbuf->sbumpc();
+    return retval;
+}
+
+static inline void constexpr
+consume_scheme_comment(std::streambuf* sbuf)
+{
+    while (sbuf->in_avail() && !is_eol(sbuf->sgetc()))
+           sbuf->sbumpc();
+}
+
+static inline std::string
+scan_scheme_string_from_streambuf(std::streambuf* sbuf)
+{
+    std::string retval{static_cast<char>(sbuf->sbumpc())};
+    while(sbuf->in_avail() && !is_double_quote(sbuf->sgetc()))
+        retval += sbuf->sbumpc();
+    retval += sbuf->sbumpc(); // Add the closing quote.
+    return retval;
+}
+
+static inline void constexpr
+consume_scheme_whitespace(std::streambuf* sbuf)
+{
+    while (sbuf->in_avail() && is_whitespace(sbuf->sgetc()))
+           sbuf->sbumpc();
+}
+
+enum class IdentType
+{
+    NAME, //no introducing mark
+    CONST, //introduced with single quote
+    STRING, //delimited by double-quotes.
+    LIST, //introduced ' and delimited by parentheses
+    FORM //delimited by parentheses without ' introduction.
+};
+
+struct SchemeId
+{
+    IdentType m_type;
+    std::string m_name;
+    std::vector<SchemeId> m_ids;
+};
+
+/**
+ * Scheme Parse Tree
+ * An identifier is a string and a type (name, const, string, or form). A Form 
+ * 
+ */
+
+static void scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id);
+
+static void
+scan_scheme_form_from_streambuf(std::streambuf* sbuf, SchemeId& id)
+{
+    sbuf->sbumpc();
+    if (!sbuf->in_avail())
+        return;
+    char c = sbuf->sgetc();
+    while (sbuf->in_avail() && !is_end_paren(c))
+    {
+        SchemeId next_id;
+        scan_scheme_id_from_streambuf(sbuf, next_id);
+        if (id.m_name.empty() && next_id.m_type == IdentType::NAME)
+        {
+            id.m_name = std::move(next_id.m_name);
+            continue;
+        }
+        id.m_ids.emplace_back(std::move(next_id));
+        if (!sbuf->in_avail())
+        {
+            std::string err{"End of streambuf before end of form "};
+            err += id.m_name;
+            throw std::runtime_error(err);
+        }
+        c = sbuf->sgetc();
+    }
+    sbuf->sbumpc();
+}
+
+static void
+scan_scheme_list_from_streambuf(std::streambuf* sbuf, std::string& str)
+{
+
+    consume_scheme_whitespace(sbuf);
+    if (!sbuf->in_avail())
+        return;
+    char c = sbuf->sgetc();
+    while (sbuf->in_avail() && !is_end_paren(c))
+    {
+        str += static_cast<char>(sbuf->sbumpc());
+        if (!sbuf->in_avail())
+            return;
+        c = sbuf->sgetc();
+    }
+    str += static_cast<char>(sbuf->sbumpc());
+}
+
+static void
+scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id)
+{
+    consume_scheme_whitespace(sbuf);
+    if (!sbuf->in_avail())
+        return;
+    auto c{sbuf->sgetc()};
+    switch(c)
+    {
+        case ';':
+            consume_scheme_comment(sbuf);
+            break;
+        case '"':
+            id.m_type = IdentType::STRING;
+            id.m_name = scan_scheme_string_from_streambuf(sbuf);
+            break;
+        case '\'':
+        {
+            std::string value{static_cast<char>(sbuf->sbumpc())};
+            if (sbuf->sgetc() == '(')
+            {
+                id.m_type == IdentType::LIST;
+                scan_scheme_list_from_streambuf(sbuf, value);
+                if (value.back() != ')')
+                    throw std::runtime_error("End of streambuf before end of form ");
+            }
+            else if (sbuf->sgetc() == '"')
+                throw std::runtime_error("Malformed scheme particle starts '\"");
+            else
+            {
+                id.m_type = IdentType::CONST;
+                value += scan_scheme_symbol_from_streambuf(sbuf);
+            }
+            id.m_name = std::move(value);
+            break;
+        }
+        case '(':
+            id.m_type = IdentType::FORM;
+            scan_scheme_form_from_streambuf(sbuf, id);
+            break;
+        default:
+            id.m_type = IdentType::NAME;
+            id.m_name = scan_scheme_symbol_from_streambuf(sbuf);
+            break;
+    }
+    return;
+}
+
+static inline std::string
+unquote_scheme_string(const std::string& str)
+{
+    if (str.front() == '"' && str.back() == '"')
+       return str.substr(1, str.size() - 2);
+
+    return str;
+}
+
 std::istream&
-GncOptionDB::load_option_scheme(std::istream& iss) noexcept
-{
-    std::string section{classifier_size_max};
-    std::string name{classifier_size_max};
-    iss.ignore(strlen(scheme_tags[0]) + 7, '\n'); //throw away the scheme noise;
-    iss >> section; // Whitespace automatically discarded.
-    iss >> name; // Ditto
-    if (section.size() > 2)
-        section = section.substr(1, section.size() - 2); //Trim the quotes
-    if (name.size() > 2 + strlen(scheme_tags[2]))
-    name = name.substr(1, name.size() - (2 + strlen(scheme_tags[2])));
+GncOptionDB::load_option_scheme(std::istream& iss)
+{
+    auto sbuf{iss.rdbuf()};
+    SchemeId toplevel;
+    bool form_found = false;
+    while (sbuf->in_avail()  && !form_found)
+    {
+        scan_scheme_id_from_streambuf(sbuf, toplevel);
+        if (toplevel.m_type == IdentType::FORM &&
+            toplevel.m_name == "let" && toplevel.m_ids.size() == 2)
+            if (const auto& let_block{toplevel.m_ids[0]};
+                let_block.m_ids.size() == 1)
+                if (const auto& first_form{let_block.m_ids[0]};
+                    first_form.m_name == "option")
+                    form_found = true;
+    }
+    const auto& classifier = toplevel.m_ids[0].m_ids[0].m_ids[0].m_ids;
+    if (classifier.size() != 3)
+        throw std::runtime_error("Malformed option classifier.");
+    const auto& section = unquote_scheme_string(classifier[1].m_name);
+    const auto& name = unquote_scheme_string(classifier[2].m_name);
     auto option = find_option(section.c_str(), name.c_str());
+    std::string option_str{section};
+    option_str += ':';
+    option_str += name;
     if (!option)
     {
-        std::cerr << "Option " << section << ":" << name << " not found." << std::endl;
-        iss.ignore(stream_max, '\n'); // No option, discard the line
-        iss.ignore(stream_max, '\n'); // And the trailing newline
-        return iss;
+        std::string err{"Option not found: "};
+        err += option_str;
+        throw std::runtime_error(err);
+    }
+    if (!(toplevel.m_type == IdentType::FORM && toplevel.m_ids.size() == 2 &&
+          toplevel.m_ids[1].m_type == IdentType::FORM &&
+          toplevel.m_ids[1].m_ids.size() == 2 &&
+          toplevel.m_ids[1].m_ids[0].m_type == IdentType::FORM &&
+          toplevel.m_ids[1].m_ids[0].m_ids.size() == 2 &&
+          toplevel.m_ids[1].m_ids[0].m_ids[1].m_type == IdentType::FORM &&
+          toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids.size() == 2 &&
+          toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_type == IdentType::FORM &&
+          toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_ids.size() == 2))
+    {
+        std::string err{"Option "};
+        err += option_str;
+        throw std::runtime_error(err + " malformed value lambda form.");
     }
-    iss.ignore(strlen(scheme_tags[2]) +1, '\n');
-    iss.ignore(strlen(scheme_tags[3]));
-    option->get().from_scheme(iss);
-    iss.ignore(strlen(scheme_tags[4]) + 2, '\n'); //discard the noise at the end.
+    auto value_id = toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_ids[1];
+    std::istringstream value_iss{value_id.m_name};
+    option->get().from_scheme(value_iss);
     return iss;
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 078224f6b..042e78c46 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -88,11 +88,18 @@ public:
         return static_cast<const GncOptionDB&>(*this).find_option(section, name);
     }
     std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) const;
+    std::ostream& save_to_scheme(std::ostream& oss,
+                                 const char* options_prolog) const noexcept;
+    std::istream& load_from_scheme(std::istream& iss) noexcept;
+    std::ostream& save_to_key_value(std::ostream& oss) const noexcept;
+    std::istream& load_from_key_value(std::istream& iss) noexcept;
+    void save_to_kvp() const noexcept;
+    void load_from_kvp() noexcept;
     std::ostream& save_option_scheme(std::ostream& oss,
                                      const char* option_prolog,
                                      const std::string& section,
                                      const std::string& name) const noexcept;
-    std::istream& load_option_scheme(std::istream& iss) noexcept;
+    std::istream& load_option_scheme(std::istream& iss);
     std::ostream& save_option_key_value(std::ostream& oss,
                                              const char* section,
                                              const char* name) const noexcept;

commit 51a1430c24443799ac0b7f7daab66d4d5fb54804
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Dec 14 15:37:02 2019 -0800

    Remove stray diagnostic.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 03a2dfe61..a5adc3198 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -967,7 +967,6 @@ public:
 
     std::size_t permissible_value_index(const std::string& value) const {
         return std::visit([&value] (const auto& option) -> size_t {
-                              std::cerr << typeid(option).name() << std::endl;
                               if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
                                                     GncOptionMultichoiceValue>)
                                         return option.permissible_value_index(value);

commit 76172af239edcfb3df3bfba94c41b3c5dbb5bd25
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 5 18:00:07 2019 -0800

    Implement saving and loading OptionDB items to/from scheme and
    key-value string representations.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 7c66f05c6..239a21f06 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -22,7 +22,10 @@
 \********************************************************************/
 
 #include "gnc-optiondb.hpp"
+#include <limits>
+#include <sstream>
 
+auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
 GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}
 
 GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
@@ -175,24 +178,53 @@ GncOptionDB::make_internal(const char* section, const char* name)
 }
 
 std::ostream&
-GncOptionDB::serialize_option_scheme(std::ostream& oss,
-                                     const char* option_prolog,
-                                     const char* section, const char* name) const noexcept
+GncOptionDB::save_option_scheme(std::ostream& oss,
+                                const char* option_prolog,
+                                const std::string& section,
+                                const std::string& name) const noexcept
 {
     auto db_opt = find_option(section, name);
+
     if (!db_opt || !db_opt->get().is_changed())
         return oss;
-    oss << c_scheme_serialization_elements[0] << option_prolog;
-    oss << c_scheme_serialization_elements[1] << section;
-    oss << c_scheme_serialization_elements[1] << name; //repeats, not an error!
-//    oss << c_scheme_serialization_elements[2] << db_opt->get();
-    oss << c_scheme_serialization_elements[3];
+    oss << scheme_tags[0] << option_prolog << "\n";
+    oss << scheme_tags[1] << '"' << section.substr(0, classifier_size_max) << "\"\n";
+    oss << scheme_tags[1] << '"' << name.substr(0, classifier_size_max) << '"';
+    oss  <<  scheme_tags[2] << "\n" << scheme_tags[3];
+    db_opt->get().to_scheme(oss);
+    oss << scheme_tags[4] << "\n\n";
 
     return oss;
 }
+std::istream&
+GncOptionDB::load_option_scheme(std::istream& iss) noexcept
+{
+    std::string section{classifier_size_max};
+    std::string name{classifier_size_max};
+    iss.ignore(strlen(scheme_tags[0]) + 7, '\n'); //throw away the scheme noise;
+    iss >> section; // Whitespace automatically discarded.
+    iss >> name; // Ditto
+    if (section.size() > 2)
+        section = section.substr(1, section.size() - 2); //Trim the quotes
+    if (name.size() > 2 + strlen(scheme_tags[2]))
+    name = name.substr(1, name.size() - (2 + strlen(scheme_tags[2])));
+    auto option = find_option(section.c_str(), name.c_str());
+    if (!option)
+    {
+        std::cerr << "Option " << section << ":" << name << " not found." << std::endl;
+        iss.ignore(stream_max, '\n'); // No option, discard the line
+        iss.ignore(stream_max, '\n'); // And the trailing newline
+        return iss;
+    }
+    iss.ignore(strlen(scheme_tags[2]) +1, '\n');
+    iss.ignore(strlen(scheme_tags[3]));
+    option->get().from_scheme(iss);
+    iss.ignore(strlen(scheme_tags[4]) + 2, '\n'); //discard the noise at the end.
+    return iss;
+}
 
 std::ostream&
-GncOptionDB::serialize_option_key_value(std::ostream& oss,
+GncOptionDB::save_option_key_value(std::ostream& oss,
                                         const char* section,
                                         const char* name) const noexcept
 {
@@ -200,13 +232,30 @@ GncOptionDB::serialize_option_key_value(std::ostream& oss,
     auto db_opt = find_option(section, name);
     if (!db_opt || !db_opt->get().is_changed())
         return oss;
-    oss << section << ":" << name << "=" /* << db_opt->get() */ << ";";
+    oss << section << ":" << name << "=" << db_opt->get() << ";";
     return oss;
 }
 
-void
-GncOptionDB::commit()
+std::istream&
+GncOptionDB::load_option_key_value(std::istream& iss)
 {
+
+    char section[classifier_size_max], name[classifier_size_max];
+    iss.getline(section, classifier_size_max, ':');
+    iss.getline(name, classifier_size_max, '=');
+    if (!iss)
+        throw std::invalid_argument("Section or name delimiter not found or values too long");
+    auto option = find_option(section, name);
+    if (!option)
+        iss.ignore(stream_max, ';');
+    else
+    {
+        std::string value;
+        std::getline(iss, value, ';');
+        std::istringstream item_iss{value};
+        item_iss >> option->get();
+    }
+    return iss;
 }
 
 GncOptionDBPtr
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 8d6187400..078224f6b 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -87,30 +87,31 @@ public:
     std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
         return static_cast<const GncOptionDB&>(*this).find_option(section, name);
     }
-private:
-    std::ostream& serialize_option_scheme(std::ostream& oss,
     std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) const;
-                                          const char* option_prolog,
-                                          const char* section, const char* name) const noexcept;
-    std::ostream& serialize_option_key_value(std::ostream& oss,
+    std::ostream& save_option_scheme(std::ostream& oss,
+                                     const char* option_prolog,
+                                     const std::string& section,
+                                     const std::string& name) const noexcept;
+    std::istream& load_option_scheme(std::istream& iss) noexcept;
+    std::ostream& save_option_key_value(std::ostream& oss,
                                              const char* section,
                                              const char* name) const noexcept;
-    void load_option_scheme(std::istream iss);
-    void load_option_key_value(std::istream iss);
+    std::istream& load_option_key_value(std::istream& iss);
+private:
     std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
     std::vector<GncOptionSection> m_sections;
     bool m_dirty = false;
 
     std::function<GncOptionUIItem*()> m_get_ui_value;
     std::function<void(GncOptionUIItem*)> m_set_ui_value;
-    static constexpr char const* const c_scheme_serialization_elements[]
+    static constexpr char const* const scheme_tags[]
     {
         "(let ((option (gnc:lookup-option ",
-        "\n                                 ",
-        ")))\n   ((lambda (o) (if o (gnc:option-set-value o",
-        "))) option))\n\n"
+        "                                 ",
+        ")))",
+        "   ((lambda (o) (if o (gnc:option-set-value o ",
+        "))) option))"
         };
-
 };
 
 /**
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 1dd088dc1..9ef8884f3 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -276,3 +276,72 @@ TEST_F(GncOptionDBUITest, test_option_value_from_ui)
         });
     EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str());
 }
+
+class GncOptionDBIOTest : public ::testing::Test
+{
+protected:
+    GncOptionDBIOTest() : m_db{gnc_option_db_new()}
+    {
+        gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
+                                   std::string{"waldo"});
+        gnc_register_text_option(m_db, "foo", "sausage", "links",
+                                 "Phony Option", std::string{"waldo"});
+        gnc_register_string_option(m_db, "qux", "grault", "baz", "Phony Option",
+                                   std::string{""});
+        gnc_register_text_option(m_db, "qux", "garply", "fred",
+                                   "Phony Option", std::string{"waldo"});
+    }
+
+    GncOptionDBPtr m_db;
+};
+
+TEST_F(GncOptionDBIOTest, test_option_scheme_output)
+{
+    std::ostringstream oss;
+    m_db->save_option_scheme(oss, "option", "foo", "sausage");
+    EXPECT_STREQ("", oss.str().c_str());
+    oss.clear();
+    m_db->set_option("foo", "sausage", std::string{"pepper"});
+    EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
+    EXPECT_TRUE(m_db->find_option("foo", "sausage")->get().is_changed());
+    oss.flush();
+    m_db->save_option_scheme(oss, "option", "foo", "sausage");
+    EXPECT_STREQ("(let ((option (gnc:lookup-option option\n"
+                 "                                 \"foo\"\n"
+                 "                                 \"sausage\")))\n"
+                 "   ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
+                 "))) option))\n\n", oss.str().c_str());
+}
+
+TEST_F(GncOptionDBIOTest, test_option_scheme_input)
+{
+    const char* input{"(let ((option (gnc:lookup-option option\n"
+                 "                                 \"foo\"\n"
+                 "                                 \"sausage\")))\n"
+                 "   ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
+            "))) option))\n\n"};
+    std::istringstream iss{input};
+    EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "sausage").c_str());
+    m_db->load_option_scheme(iss);
+    EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
+}
+
+TEST_F(GncOptionDBIOTest, test_option_key_value_output)
+{
+    std::ostringstream oss;
+    m_db->save_option_key_value(oss, "foo", "sausage");
+    EXPECT_STREQ("", oss.str().c_str());
+    m_db->set_option("foo", "sausage", std::string{"pepper"});
+//    EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
+//    EXPECT_TRUE(m_db->find_option("foo", "sausage")->get().is_changed());
+    m_db->save_option_key_value(oss, "foo", "sausage");
+    EXPECT_STREQ("foo:sausage=pepper;", oss.str().c_str());
+}
+
+TEST_F(GncOptionDBIOTest, test_option_key_value_input)
+{
+    std::istringstream iss{"foo:sausage=pepper;"};
+    EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "sausage").c_str());
+    m_db->load_option_key_value(iss);
+    EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
+}

commit 6ab5618b768cc4512acaa68f82c874a1cb3fef36
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 5 17:54:02 2019 -0800

    Set a 50-character limit on the saved size of option section and name.
    
    Allows use of istream::getline() to retrieve the values, simplifying
    delimiter detection.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index a72268688..03a2dfe61 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -119,6 +119,9 @@ enum GncOptionUIType
 
 static const char* commodity_scm_intro{"'(commodity-scm "};
 
+size_t constexpr classifier_size_max{50};
+size_t constexpr sort_tag_size_max{10};
+
 struct OptionClassifier
 {
     std::string m_section;
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index c4c7fe4d4..7c66f05c6 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -124,13 +124,13 @@ GncOptionDB::set_option_from_ui(const char* section, const char* name,
 
 
 std::optional<std::reference_wrapper<GncOptionSection>>
-GncOptionDB::find_section(const char* section)
+GncOptionDB::find_section(const std::string& section)
 {
     auto db_section = std::find_if(
         m_sections.begin(), m_sections.end(),
-        [section](GncOptionSection sect) -> bool
+        [&section](GncOptionSection sect) -> bool
         {
-            return sect.first == std::string{section};
+            return section.compare(0, classifier_size_max, sect.first) == 0;
         });
     if (db_section == m_sections.end())
         return std::nullopt;
@@ -138,16 +138,17 @@ GncOptionDB::find_section(const char* section)
 }
 
 std::optional<std::reference_wrapper<GncOption>>
-GncOptionDB::find_option(const char* section, const char* name) const
+GncOptionDB::find_option(const std::string& section, const std::string& name) const
 {
     auto db_section = const_cast<GncOptionDB*>(this)->find_section(section);
     if (!db_section)
         return std::nullopt;
     auto db_opt = std::find_if(
         db_section->get().second.begin(), db_section->get().second.end(),
-        [name](GncOption& option) -> bool
+        [&name](GncOption& option) -> bool
         {
-            return option.get_name() == std::string{name};
+            return name.compare(0, classifier_size_max - 1,
+                                  option.get_name()) == 0;
         });
     if (db_opt == db_section->get().second.end())
         return std::nullopt;
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index b8c515133..8d6187400 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -83,13 +83,13 @@ public:
 //    void set_selectable(const char* section, const char* name);
     void make_internal(const char* section, const char* name);
     void commit();
-    std::optional<std::reference_wrapper<GncOptionSection>> find_section(const char* section);
-    std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name) {
+    std::optional<std::reference_wrapper<GncOptionSection>> find_section(const std::string& section);
+    std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
         return static_cast<const GncOptionDB&>(*this).find_option(section, name);
     }
-    std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name) const;
 private:
     std::ostream& serialize_option_scheme(std::ostream& oss,
+    std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) const;
                                           const char* option_prolog,
                                           const char* section, const char* name) const noexcept;
     std::ostream& serialize_option_key_value(std::ostream& oss,

commit d7a2a0ffff75251d66002564016d74a520467ac3
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 5 17:47:30 2019 -0800

    Make a constant for std::numeric_limits<std::size_t>max().
    
    Improves readability.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index fca5aba34..a72268688 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -178,6 +178,8 @@ private:
     GncOptionUIType m_ui_type;
 };
 
+auto constexpr size_t_max = std::numeric_limits<std::size_t>::max();
+
 template <typename ValueType>
 class GncOptionValue : public OptionClassifier, public OptionUIItem
 {
@@ -496,7 +498,7 @@ public:
             if (value)
             {
                 if (auto index = find_key(value);
-                    index != std::numeric_limits<std::size_t>::max())
+                    index != size_t_max)
                 {
                     m_value = index;
                     m_default_value = index;
@@ -520,13 +522,13 @@ public:
      bool validate(const std::string& value) const noexcept
     {
         auto index = find_key(value);
-        return index != std::numeric_limits<std::size_t>::max();
+        return index != size_t_max;
 
     }
     void set_value(const std::string& value)
     {
         auto index = find_key(value);
-        if (index != std::numeric_limits<std::size_t>::max())
+        if (index != size_t_max)
             m_value = index;
         else
             throw std::invalid_argument("Value not a valid choice.");
@@ -562,7 +564,7 @@ private:
         if (iter != m_choices.end())
             return iter - m_choices.begin();
         else
-            return std::numeric_limits<std::size_t>::max();
+            return size_t_max;
 
     }
     std::size_t m_value;
@@ -956,7 +958,7 @@ public:
                                                     GncOptionMultichoiceValue>)
                                         return option.num_permissible_values();
                        else
-                           return std::numeric_limits<std::size_t>::max();
+                           return size_t_max;
                    }, m_option);
     }
 
@@ -967,7 +969,7 @@ public:
                                                     GncOptionMultichoiceValue>)
                                         return option.permissible_value_index(value);
                        else
-                           return std::numeric_limits<std::size_t>::max();;
+                           return size_t_max;;
                    }, m_option);
     }
 

commit 826d75af16b277bcbab02e1b14e75c3f8c703d23
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 5 17:46:13 2019 -0800

    Use istream::getline instead of std::getline for date option type parsing.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 78f75bedb..ec67ef449 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -179,9 +179,12 @@ GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
 std::istream&
 GncOptionDateValue::in_stream(std::istream& iss)
 {
-    std::string type_str;
-    std::getline(iss, type_str, '.');
-    if (type_str == "absolute ")
+    char type_str[10]; //The length of both "absolute" and "relative" plus 1.
+    iss.getline(type_str, sizeof(type_str), '.');
+    if(!iss)
+        throw std::invalid_argument("Date Type separator missing");
+    /* strcmp is safe, istream::getline null terminates the buffer. */
+    if (strcmp(type_str, "absolute ") == 0)
     {
         time64 time;
         iss >> time;
@@ -189,7 +192,7 @@ GncOptionDateValue::in_stream(std::istream& iss)
         if (iss.get() != ')')
             iss.unget();
     }
-    else if (type_str == "relative ")
+    else if (strcmp(type_str, "relative ") == 0)
     {
         std::string period_str;
         iss >> period_str;

commit c5294ed6b3b4cc742ef4821d0ea3ff8f9e43df11
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Dec 5 10:03:57 2019 -0800

    Fix missing-declaration errors in swig-gnc-optiondb-guile.cpp.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 8ad19c7f4..a5fee701b 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -326,8 +326,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
  };
 
 %inline %{
-    SCM gnc_option_value(const GncOptionDBPtr& optiondb, const char* section,
-                         const char* name)
+    static SCM
+    gnc_option_value(const GncOptionDBPtr& optiondb, const char* section,
+                     const char* name)
     {
         auto db_opt = optiondb->find_option(section, name);
         if (!db_opt)
@@ -335,8 +336,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return GncOption_get_scm_value(&(db_opt->get()));
     }
 
-    SCM gnc_option_default_value(const GncOptionDBPtr& optiondb,
-                                 const char* section, const char* name)
+    static SCM
+    gnc_option_default_value(const GncOptionDBPtr& optiondb,
+                             const char* section, const char* name)
     {
         auto db_opt = optiondb->find_option(section, name);
         if (!db_opt)
@@ -344,8 +346,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return GncOption_get_scm_default_value(&(db_opt->get()));
     }
 
-    void gnc_set_option(const GncOptionDBPtr& optiondb, const char* section,
-                        const char* name, SCM new_value)
+    static void
+    gnc_set_option(const GncOptionDBPtr& optiondb, const char* section,
+                   const char* name, SCM new_value)
     {
         auto db_opt = optiondb->find_option(section, name);
         if (!db_opt)

commit 7ccba53739f1d07a58cd605c739523fc56fefb12
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 3 11:18:34 2019 -0800

    Add stream functions to_scheme and from_scheme to GncOption.
    
    These add or parse the textual noise needed to replicate the Scheme options'
    serialization technique of saving scheme forms for saving report options
    and then evaluating those forms to restore the option values. Required for
    backward saved-reports compatibility.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 1ced4cdde..78f75bedb 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -186,11 +186,15 @@ GncOptionDateValue::in_stream(std::istream& iss)
         time64 time;
         iss >> time;
         set_value(time);
+        if (iss.get() != ')')
+            iss.unget();
     }
     else if (type_str == "relative ")
     {
         std::string period_str;
         iss >> period_str;
+        if (period_str.back() == ')')
+            period_str.pop_back();
         auto period = std::find(date_period_str.begin(), date_period_str.end(),
                                 period_str);
         if (period == date_period_str.end())
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index b1b881d4b..fca5aba34 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -117,6 +117,8 @@ enum GncOptionUIType
     QUERY,
 };
 
+static const char* commodity_scm_intro{"'(commodity-scm "};
+
 struct OptionClassifier
 {
     std::string m_section;
@@ -243,6 +245,8 @@ public:
             throw std::invalid_argument("Validation failed, value not set.");
     }
     bool is_changed() const noexcept { return m_value != m_default_value; }
+    std::ostream& to_scheme(std::ostream&) const;
+    std::istream& from_scheme(std::istream&);
 private:
     ValueType m_value;
     ValueType m_default_value;
@@ -280,7 +284,7 @@ operator<< <GncOptionValue<bool>>(std::ostream& oss,
                                   const GncOptionValue<bool>& opt)
 {
     oss << (opt.get_value() ? "#t" : "#f");
-        return oss;
+    return oss;
 }
 
 template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
@@ -677,6 +681,49 @@ operator>> <GncOptionAccountValue>(std::istream& iss,
     iss.clear();
     return iss;
 }
+
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionAccountValue>, int> = 0>
+inline std::ostream&
+gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
+{
+    auto values{opt.get_value()};
+    oss << "'(\"";
+    bool first = true;
+    for (auto value : values)
+    {
+        if (first)
+            first = false;
+        else
+            oss << " \"";
+        oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"';
+    }
+    oss << ')';
+    return oss;
+}
+
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionAccountValue>, int> = 0>
+inline std::istream&
+gnc_option_from_scheme(std::istream& iss, OptType& opt)
+{
+    GncOptionAccountList values;
+    iss.ignore(3, '"');
+    while (true)
+    {
+        std::string str;
+        std::getline(iss, str, '"');
+        if (!str.empty())
+        {
+            values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type()));
+            iss.ignore(2, '"');
+        }
+        else
+            break;
+    }
+    opt.set_value(values);
+    iss.ignore(1, ')');
+    return iss;
+}
+
 /** Date options
  * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
  * flag and a time64, or for legacy purposes the absolute flag and a timespec.
@@ -969,8 +1016,83 @@ public:
         }, m_option);
     }
 
+    std::ostream& to_scheme(std::ostream& oss) const
+    {
+        return std::visit([&oss](auto& option) ->std::ostream& {
+                if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionAccountValue>)
+                        gnc_option_to_scheme(oss, option);
+                else if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionMultichoiceValue>)
+                        oss << "'" << option;
+                else if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionValue<QofInstance*>> ||
+                     std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionValidatedValue<QofInstance*>>)
+                        gnc_option_to_scheme(oss, option);
+                else if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionDateValue>)
+                        oss << "'(" << option << ")";
+                else if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
+                     std::string>)
+                        oss << '"' << option << '"';
+                else
+                    oss << option;
+                return oss;
+            }, m_option);
+    }
+
+    std::istream& from_scheme(std::istream& iss)
+    {
+        return std::visit([&iss](auto& option) ->std::istream& {
+                if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionAccountValue>)
+                        gnc_option_from_scheme(iss, option);
+                else if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionMultichoiceValue>)
+                    {
+                         iss.ignore(1, '\'');
+                         iss >> option;
+                    }
+                else if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionValue<QofInstance*>> ||
+                     std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionValidatedValue<QofInstance*>>)
+                        gnc_option_from_scheme(iss, option);
+                else if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option)>,
+                     GncOptionDateValue>)
+                    {
+                         iss.ignore(2, '(');
+                         iss >> option;
+                         //operator >> clears the trailing ')'
+                    }
+                 else if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
+                     std::string>)
+                    {
+                         iss.ignore(1, '"');
+                         std::string input;
+                         std::getline(iss, input, '"');
+                         option.set_value(input);
+                    }
+                else
+                    iss >> option;
+                return iss;
+            }, m_option);
+    }
+
     GncOptionVariant& _get_option() const { return m_option; }
 private:
+    inline static const std::string c_empty_string{""};
     mutable GncOptionVariant m_option;
 };
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 530caefd0..bf1b06151 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -84,6 +84,26 @@ TEST(GncOption, test_string_stream_in)
     EXPECT_EQ(pepper, option.get_value<std::string>());
 }
 
+TEST(GncOption, test_string_to_scheme)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
+    std::ostringstream oss;
+    option.to_scheme(oss);
+    std::string scheme_str{"\""};
+    scheme_str += option.get_value<std::string>() + "\"";
+    EXPECT_EQ(oss.str(), scheme_str);
+}
+
+TEST(GncOption, test_string_from_scheme)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
+    std::string pepper{"pepper"};
+    std::string scheme_str{"\"pepper\""};
+    std::istringstream iss{scheme_str};
+    option.from_scheme(iss);
+    EXPECT_EQ(pepper, option.get_value<std::string>());
+}
+
 TEST(GncOption, test_int64_t_value)
 {
     GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
@@ -109,6 +129,23 @@ TEST(GncOption, test_int64_stream_in)
     EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
 }
 
+TEST(GncOption, test_int64_to_scheme)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option",  INT64_C(123456789));
+    std::ostringstream oss;
+    option.to_scheme(oss);
+    EXPECT_STREQ(oss.str().c_str(), "123456789");
+}
+
+TEST(GncOption, test_int64_from_scheme)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option",  INT64_C(123456789));
+    std::string number{"987654321"};
+    std::istringstream iss{number};
+    option.from_scheme(iss);
+    EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
+}
+
 TEST(GncOption, test_bool_stream_out)
 {
     GncOption option("foo", "bar", "baz", "Phony Option", false);
@@ -132,6 +169,29 @@ TEST(GncOption, test_bool_stream_in)
     EXPECT_FALSE(option.get_value<bool>());
 }
 
+TEST(GncOption, test_bool_to_scheme)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", false);
+    std::ostringstream oss;
+    oss << option;
+    EXPECT_STREQ(oss.str().c_str(), "#f");
+    oss.str("");
+    option.set_value(true);
+    option.to_scheme(oss);
+    EXPECT_STREQ(oss.str().c_str(), "#t");
+}
+
+TEST(GncOption, test_bool_from_scheme)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", false);
+    std::istringstream iss("#t");
+    iss >> option;
+    EXPECT_TRUE(option.get_value<bool>());
+    iss.str("#f");
+    option.from_scheme(iss);
+    EXPECT_FALSE(option.get_value<bool>());
+}
+
 class GncOptionTest : public ::testing::Test
 {
 protected:
@@ -174,6 +234,34 @@ TEST_F(GncOptionTest, test_budget_in)
     EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<QofInstance*>());
     gnc_budget_destroy(budget);
 }
+
+TEST_F(GncOptionTest, test_budget_to_scheme)
+{
+    auto budget = gnc_budget_new(m_book);
+    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)};
+
+    auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
+    std::ostringstream oss;
+    budget_guid.insert(0, "\"");
+    budget_guid += "\"";
+    option.to_scheme(oss);
+    EXPECT_EQ(budget_guid, oss.str());
+    gnc_budget_destroy(budget);
+}
+
+TEST_F(GncOptionTest, test_budget_from_scheme)
+{
+    auto budget = gnc_budget_new(m_book);
+    auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
+    budget_guid.insert(0, "\"");
+    budget_guid += "\"";
+    std::istringstream iss{budget_guid};
+    GncOption option{GncOptionValue<QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
+    option.from_scheme(iss);
+    EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<QofInstance*>());
+    gnc_budget_destroy(budget);
+}
+
 TEST_F(GncOptionTest, test_commodity_ctor)
 {
     auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
@@ -366,6 +454,48 @@ TEST_F(GncOptionCommodityTest, test_commodity_in)
     EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<QofInstance*>());
 }
 
+TEST_F(GncOptionCommodityTest, test_currency_to_scheme)
+{
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
+                                       m_eur, true);
+
+    std::string eur_str{make_currency_SCM_str(m_eur)};
+    std::ostringstream oss;
+    option.to_scheme(oss);
+    EXPECT_EQ(eur_str, oss.str());
+}
+
+TEST_F(GncOptionCommodityTest, test_commodity_to_scheme)
+{
+    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_hpe),
+                     GncOptionUIType::COMMODITY};
+
+    std::string hpe_str{make_commodity_SCM_str(m_hpe)};
+    std::ostringstream oss;
+    option.to_scheme(oss);
+    EXPECT_EQ(hpe_str, oss.str());
+}
+
+TEST_F(GncOptionCommodityTest, test_currency_from_scheme)
+{
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
+                                       m_eur, true);
+
+    std::string usd_str{make_currency_SCM_str(m_usd)};
+    std::istringstream iss{usd_str};
+    option.from_scheme(iss);
+    EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<QofInstance*>());
+}
+
+TEST_F(GncOptionCommodityTest, test_commodity_from_scheme)
+{
+    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_aapl),
+                     GncOptionUIType::COMMODITY};
+
+    std::string hpe_str{make_commodity_SCM_str(m_hpe)};
+    std::istringstream iss{hpe_str};
+    option.from_scheme(iss);
+    EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<QofInstance*>());
 }
 
 class GncUIItem
@@ -459,8 +589,8 @@ TEST_F(GncRangeOption, test_range_in)
     EXPECT_THROW({ iss >> m_doubleoption; }, std::invalid_argument);
     EXPECT_NO_THROW({ iss >> m_intoption; });
     EXPECT_NO_THROW({ iss >> m_doubleoption; });
-    EXPECT_EQ(20, m_intoption.get_value());
-    EXPECT_EQ(2.0, m_doubleoption.get_value());
+    EXPECT_EQ(20, m_intoption.get_value<int>());
+    EXPECT_EQ(2.0, m_doubleoption.get_value<double>());
 }
 
 using AccountPair = std::pair<GncOptionAccountList&,
@@ -625,7 +755,7 @@ TEST_F(GncOptionAccountTest, test_account_list_out)
     GncOptionAccountList accsel{acclist[0]};
     GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
                                                "Bogus Option",
-                                 GncOptionUIType::ACCOUNT_LIST,
+                                               GncOptionUIType::ACCOUNT_LIST,
                                                accsel, {ACCT_TYPE_BANK}}};
     acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string();
 
@@ -651,7 +781,7 @@ TEST_F(GncOptionAccountTest, test_account_list_in)
     GncOptionAccountList accsel{acclist[0]};
     GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
                                                "Bogus Option",
-                                 GncOptionUIType::ACCOUNT_LIST,
+                                               GncOptionUIType::ACCOUNT_LIST,
                                                accsel, {ACCT_TYPE_BANK}}};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
     acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string();
@@ -670,6 +800,81 @@ TEST_F(GncOptionAccountTest, test_account_list_in)
     EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[0]);
 }
 
+static inline std::string
+make_account_list_SCM_str(const GncOptionAccountList& acclist)
+{
+    std::string retval{"'("};
+    bool first = true;
+    for (auto acc : acclist)
+    {
+        if (first)
+        {
+            first = false;
+            retval += '"';
+        }
+        else
+            retval += " \"";
+        retval += gnc::GUID{*qof_instance_get_guid(acc)}.to_string();
+        retval += '"';
+    }
+    retval += ')';
+    return retval;
+}
+
+TEST_F(GncOptionAccountTest, test_account_list_to_scheme)
+{
+    GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
+    GncOption option{GncOptionAccountValue {"foo", "bar", "baz", "Bogus Option",
+                                            GncOptionUIType::ACCOUNT_LIST,
+                                            acclist}};
+    std::ostringstream oss;
+    std::string acc_guids{make_account_list_SCM_str(acclist)};
+
+    option.to_scheme(oss);
+    EXPECT_EQ(acc_guids, oss.str());
+
+    GncOptionAccountList accsel{acclist[0]};
+    GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
+                                               "Bogus Option",
+                                               GncOptionUIType::ACCOUNT_LIST,
+                                               accsel, {ACCT_TYPE_BANK}}};
+    acc_guids = make_account_list_SCM_str(accsel);
+
+    oss.str("");
+    sel_option.to_scheme(oss);
+    EXPECT_EQ(acc_guids, oss.str());
+}
+
+TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
+{
+    GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
+    GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option",
+                                           GncOptionUIType::ACCOUNT_LIST,
+                                           acclist}};
+
+    std::string acc_guids{make_account_list_SCM_str(acclist)};
+    std::istringstream iss{acc_guids};
+    option.from_scheme(iss);
+    EXPECT_EQ(acclist, option.get_value<GncOptionAccountList>());
+
+    GncOptionAccountList accsel{acclist[0]};
+    GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
+                                               "Bogus Option",
+                                               GncOptionUIType::ACCOUNT_LIST,
+                                               accsel, {ACCT_TYPE_BANK}}};
+    GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
+    acc_guids = make_account_list_SCM_str(acclistbad);
+    iss.str(acc_guids);
+    sel_option.from_scheme(iss);
+    EXPECT_EQ(accsel, sel_option.get_value<GncOptionAccountList>());
+
+    iss.clear();  //Reset the failedbit from the invalid selection type.
+    acc_guids = make_account_list_SCM_str(GncOptionAccountList{acclist[1]});
+    EXPECT_NO_THROW({
+            iss.str(acc_guids);
+            sel_option.from_scheme(iss);
+        });
+    EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[0]);
 }
 
 class GncOptionMultichoiceTest : public ::testing::Test
@@ -678,11 +883,11 @@ protected:
     GncOptionMultichoiceTest() :
         m_option{GncOptionMultichoiceValue{"foo", "bar", "baz",
                                            "Phony Option", "plugh",
-            {
-                {"plugh", "xyzzy", "thud"},
-                {"waldo", "pepper", "salt"},
-                {"pork", "sausage", "links"},
-                {"corge", "grault", "garply"}
+                                           {
+                                               {"plugh", "xyzzy", "thud"},
+                                               {"waldo", "pepper", "salt"},
+                                               {"pork", "sausage", "links"},
+                                               {"corge", "grault", "garply"}
                                            }}} {}
     GncOption m_option;
 };
@@ -754,6 +959,23 @@ TEST_F(GncMultichoiceOption, test_multichoice_in)
     iss >> m_option;
     EXPECT_EQ(iss.str(), m_option.get_value<std::string>());
 }
+
+TEST_F(GncMultichoiceOption, test_multichoice_scheme_out)
+{
+    std::ostringstream oss;
+    m_option.to_scheme(oss);
+    std::string scm_str{'\''};
+    scm_str += m_option.get_value<std::string>();
+    EXPECT_EQ(scm_str, oss.str());
+}
+
+TEST_F(GncMultichoiceOption, test_multichoice_scheme_in)
+{
+    std::istringstream iss{"'pork"};
+    m_option.from_scheme(iss);
+    EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
+}
+
 class GncOptionDateOptionTest : public ::testing::Test
 {
 protected:
@@ -1134,3 +1356,64 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end)
     iss >> m_option;
     EXPECT_EQ(time1, m_option.get_value<time64>());
 }
+
+/* We only need wiggle tests for to_scheme and from_scheme as they just wrap or
+unwrap the normal stream output with "'(" and ")".
+*/
+
+TEST_F(GncDateOption, test_date_option_to_scheme)
+{
+    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
+    m_option.set_value(time1);
+    std::ostringstream oss;
+    oss << time1;
+    std::string timestr{"'(absolute . "};
+    timestr += oss.str();
+    timestr += ')';
+    oss.str("");
+    m_option.to_scheme(oss);
+    EXPECT_EQ(oss.str(), timestr);
+
+    m_option.set_value(RelativeDatePeriod::TODAY);
+    oss.str("");
+    m_option.to_scheme(oss);
+    EXPECT_STREQ(oss.str().c_str(), "'(relative . today)");
+
+    m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
+    oss.str("");
+    m_option.to_scheme(oss);
+    EXPECT_STREQ(oss.str().c_str(), "'(relative . start-this-month)");
+
+}
+
+TEST_F(GncDateOption, test_date_option_from_scheme)
+{
+    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
+    std::ostringstream oss;
+    oss << time1;
+    std::string timestr{"'(absolute . "};
+    timestr += oss.str();
+    timestr += ')';
+
+    std::istringstream iss{timestr};
+    m_option.from_scheme(iss);
+    EXPECT_EQ(time1, m_option.get_value<time64>());
+
+    GDate month_start;
+    g_date_set_time_t(&month_start, time(nullptr));
+    gnc_gdate_set_month_start(&month_start);
+    time1 = time64_from_gdate(&month_start, DayPart::start);
+    iss.clear();
+    iss.str("'(relative . start-this-month)");
+    m_option.from_scheme(iss);
+    EXPECT_EQ(time1, m_option.get_value<time64>());
+
+    GDate month_end;
+    g_date_set_time_t(&month_end, time(nullptr));
+    gnc_gdate_set_month_end(&month_end);
+    time1 = time64_from_gdate(&month_end, DayPart::end);
+    iss.clear();
+    iss.str("'(relative . end-this-month)");
+    m_option.from_scheme(iss);
+    EXPECT_EQ(time1, m_option.get_value<time64>());
+}

commit 1ea382266524566d239f5d32e01d4de9f612b159
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 3 11:14:06 2019 -0800

    More operator <</>> Fixups.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index b3a750b25..b1b881d4b 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -176,31 +176,8 @@ private:
     GncOptionUIType m_ui_type;
 };
 
-/* These will work when m_value is a built-in class; GnuCash class and container
- * values will need specialization unless they happen to define operators << and
- * >>.
- * Note that SWIG 3.0.12 chokes on the typename = std::enable_if_t<> form so we
- * have to use the non-type parameter form.
- */
-template<class OptionValueClass, typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>>, int> = 0>
-std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
-{
-    oss << opt.get_value();
-    return oss;
-}
-
-template<class OptionValueClass, typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>>, int> = 0>
-std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
-{
-    std::decay_t<decltype(opt.get_value())> value;
-    iss >> value;
-    opt.set_value(value);
-    return iss;
-}
-
 template <typename ValueType>
-class GncOptionValue :
-    public OptionClassifier, public OptionUIItem
+class GncOptionValue : public OptionClassifier, public OptionUIItem
 {
 public:
     GncOptionValue<ValueType>(const char* section, const char* name,
@@ -223,49 +200,8 @@ private:
     ValueType m_default_value;
 };
 
-QofInstance* qof_instance_from_string(const std::string& str,
-                                      GncOptionUIType type);
-std::string qof_instance_to_string(const QofInstance* inst);
-
-template<> inline std::ostream&
-operator<< <GncOptionValue<bool>>(std::ostream& oss,
-                                  const GncOptionValue<bool>& opt)
-{
-    oss << (opt.get_value() ? "#t" : "#f");
-    return oss;
-}
-
-template<> inline std::ostream&
-operator<< <GncOptionValue<QofInstance*>>(std::ostream& oss,
-                                       const GncOptionValue<QofInstance*>& opt)
-{
-    oss << qof_instance_to_string(opt.get_value());
-    return oss;
-}
-
-template<> inline std::istream&
-operator>> <GncOptionValue<bool>>(std::istream& iss,
-                                  GncOptionValue<bool>& opt)
-{
-    std::string instr;
-    iss >> instr;
-    opt.set_value(instr == "#t" ? true : false);
-    return iss;
-}
-
-template<> inline std::istream&
-operator>> <GncOptionValue<QofInstance*>>(std::istream& iss,
-                                       GncOptionValue<QofInstance*>& opt)
-{
-    std::string instr;
-    iss >> instr;
-    opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
-    return iss;
-}
-
 template <typename ValueType>
-class GncOptionValidatedValue :
-    public OptionClassifier, public OptionUIItem
+class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem
 {
 public:
     GncOptionValidatedValue<ValueType>(const char* section, const char* name,
@@ -314,24 +250,176 @@ private:
     ValueType m_validation_data;
 };
 
+QofInstance* qof_instance_from_string(const std::string& str,
+                                      GncOptionUIType type);
+std::string qof_instance_to_string(const QofInstance* inst);
+
+/* These will work when m_value is a built-in class; GnuCash class and container
+ * values will need specialization unless they happen to define operators << and
+ * >>.
+ * Note that SWIG 3.0.12 chokes on elaborate enable_if so just hide the
+ * following templates from SWIG. (Ignoring doesn't work because SWIG still has
+ * to parse the templates to figure out the symbols.
+ */
+#ifndef SWIG
+template<class OptionValueClass,
+         typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
+                                                     std::decay_t<OptionValueClass>> &&
+                                   !(std::is_same_v<std::decay_t<OptionValueClass>,
+                                     GncOptionValue<QofInstance*>> ||
+                                     std::is_same_v<std::decay_t<OptionValueClass>,
+                                     GncOptionValidatedValue<QofInstance*>>), int> = 0>
+std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
+{
+    oss << opt.get_value();
+    return oss;
+}
+
 template<> inline std::ostream&
-operator<< <GncOptionValidatedValue<QofInstance*>>(std::ostream& oss,
-                                       const GncOptionValidatedValue<QofInstance*>& opt)
+operator<< <GncOptionValue<bool>>(std::ostream& oss,
+                                  const GncOptionValue<bool>& opt)
 {
-        oss << qof_instance_to_string(opt.get_value());
-        std::cerr << qof_instance_to_string(opt.get_value());
+    oss << (opt.get_value() ? "#t" : "#f");
         return oss;
 }
 
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
+inline std::ostream&
+operator<< (std::ostream& oss, const OptType& opt)
+{
+    auto value = opt.get_value();
+    if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
+        type == GncOptionUIType::CURRENCY)
+    {
+        if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY)
+        {
+            oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " ";
+        }
+        oss << gnc_commodity_get_mnemonic(GNC_COMMODITY(value));
+    }
+    else
+    {
+        oss << qof_instance_to_string(value);
+    }
+    return oss;
+}
+
+template<class OptionValueClass,
+         typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>> &&
+                                   !(std::is_same_v<std::decay_t<OptionValueClass>,
+                                     GncOptionValue<QofInstance*>> ||
+                                     std::is_same_v<std::decay_t<OptionValueClass>,
+                                     GncOptionValidatedValue<QofInstance*>>), int> = 0>
+std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
+{
+    std::decay_t<decltype(opt.get_value())> value;
+    iss >> value;
+    opt.set_value(value);
+    return iss;
+}
+
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*> >, int> = 0>
+std::istream&
+operator>> (std::istream& iss, OptType& opt)
+{
+    std::string instr;
+    if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY ||
+        type == GncOptionUIType::CURRENCY)
+    {
+        std::string name_space, mnemonic;
+        if (type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY)
+        {
+            iss >> name_space;
+        }
+        else
+            name_space = GNC_COMMODITY_NS_CURRENCY;
+        iss >> mnemonic;
+        instr = name_space + ":";
+        instr += mnemonic;
+     }
+    else
+    {
+        iss >> instr;
+    }
+    opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
+    return iss;
+}
+
 template<> inline std::istream&
-operator>> <GncOptionValidatedValue<QofInstance*>>(std::istream& iss,
-                                       GncOptionValidatedValue<QofInstance*>& opt)
+operator>> <GncOptionValue<bool>>(std::istream& iss,
+                                  GncOptionValue<bool>& opt)
 {
     std::string instr;
     iss >> instr;
+    opt.set_value(instr == "#t" ? true : false);
+    return iss;
+}
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*>>, int> = 0>
+inline std::ostream&
+gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
+{
+    auto value = opt.get_value();
+    auto type = opt.get_ui_type();
+    if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
+    {
+        if (type == GncOptionUIType::COMMODITY)
+        {
+            oss << commodity_scm_intro;
+            oss << "\"" <<
+                gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" ";
+        }
+
+        oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\"";
+
+        if (type == GncOptionUIType::COMMODITY)
+        {
+            oss << ")";
+        }
+    }
+    else
+    {
+        oss << "\"" << qof_instance_to_string(value) << "\"";
+    }
+    return oss;
+}
+
+template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<QofInstance*>>, int> = 0>
+inline std::istream&
+gnc_option_from_scheme (std::istream& iss, OptType& opt)
+{
+    std::string instr;
+    auto type = opt.get_ui_type();
+    if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
+    {
+        std::string name_space, mnemonic;
+        if (type == GncOptionUIType::COMMODITY)
+        {
+            iss.ignore(strlen(commodity_scm_intro) + 1, '"');
+            std::getline(iss, name_space, '"');
+            iss.ignore(1, '"');
+        }
+        else
+            name_space = GNC_COMMODITY_NS_CURRENCY;
+        iss.ignore(1, '"');
+        std::getline(iss, mnemonic, '"');
+
+        if (type == GncOptionUIType::COMMODITY)
+            iss.ignore(2, ')');
+        else
+            iss.ignore(1, '"');
+
+        instr = name_space + ":";
+        instr += mnemonic;
+     }
+    else
+    {
+        iss.ignore(1, '"');
+        std::getline(iss, instr, '"');
+    }
     opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
     return iss;
 }
+#endif // SWIG
 
 /**
  * Used for numeric ranges and plot sizes.
@@ -559,8 +647,15 @@ operator<< <GncOptionAccountValue>(std::ostream& oss,
                                        const GncOptionAccountValue& opt)
 {
     auto values{opt.get_value()};
+    bool first = true;
     for (auto value : values)
-        oss << qof_instance_to_string(QOF_INSTANCE(value)) << " ";
+    {
+        if (first)
+            first = false;
+        else
+            oss << " ";
+        oss << qof_instance_to_string(QOF_INSTANCE(value));
+    }
     return oss;
 }
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 2f4a9afd2..530caefd0 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -152,6 +152,28 @@ TEST_F(GncOptionTest, test_budget_ctor)
     gnc_budget_destroy(budget);
 }
 
+TEST_F(GncOptionTest, test_budget_out)
+{
+    auto budget = gnc_budget_new(m_book);
+    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)};
+
+    auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
+    std::ostringstream oss;
+    oss << option;
+    EXPECT_EQ(budget_guid, oss.str());
+    gnc_budget_destroy(budget);
+}
+
+TEST_F(GncOptionTest, test_budget_in)
+{
+    auto budget = gnc_budget_new(m_book);
+    auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
+    std::istringstream iss{budget_guid};
+    GncOption option{GncOptionValue<QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
+    iss >> option;
+    EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<QofInstance*>());
+    gnc_budget_destroy(budget);
+}
 TEST_F(GncOptionTest, test_commodity_ctor)
 {
     auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
@@ -589,23 +611,23 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
 TEST_F(GncOptionAccountTest, test_account_list_out)
 {
     GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
-            GncOptionUIType::ACCOUNT_LIST, acclist};
+    GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option",
+                                           GncOptionUIType::ACCOUNT_LIST,
+                                           acclist}};
     std::ostringstream oss;
     std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
     acc_guids += " ";
     acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
-    acc_guids += " ";
 
     oss << option;
     EXPECT_EQ(acc_guids, oss.str());
 
     GncOptionAccountList accsel{acclist[0]};
-    GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option",
+    GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
+                                               "Bogus Option",
                                  GncOptionUIType::ACCOUNT_LIST,
-                                 accsel, {ACCT_TYPE_BANK});
+                                               accsel, {ACCT_TYPE_BANK}}};
     acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string();
-    acc_guids += " ";
 
     oss.str("");
     oss << sel_option;
@@ -615,35 +637,39 @@ TEST_F(GncOptionAccountTest, test_account_list_out)
 TEST_F(GncOptionAccountTest, test_account_list_in)
 {
     GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
-    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
-            GncOptionUIType::ACCOUNT_LIST, acclist};
+    GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option",
+                                           GncOptionUIType::ACCOUNT_LIST,
+                                           acclist}};
     std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
     acc_guids += " ";
     acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
-    acc_guids += " ";
 
     std::istringstream iss{acc_guids};
     iss >> option;
-    EXPECT_EQ(acclist, option.get_value());
+    EXPECT_EQ(acclist, option.get_value<GncOptionAccountList>());
 
     GncOptionAccountList accsel{acclist[0]};
-    GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option",
+    GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz",
+                                               "Bogus Option",
                                  GncOptionUIType::ACCOUNT_LIST,
-                                 accsel, {ACCT_TYPE_BANK});
+                                               accsel, {ACCT_TYPE_BANK}}};
     GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
     acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string();
     acc_guids += " ";
 
     iss.str(acc_guids);
     iss >> sel_option;
-    EXPECT_EQ(accsel, sel_option.get_value());
+    EXPECT_EQ(accsel, sel_option.get_value<GncOptionAccountList>());
 
+    iss.clear();  //Reset the failedbit from the invalid selection type.
     acc_guids = gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
     EXPECT_NO_THROW({
             iss.str(acc_guids);
             iss >> sel_option;
         });
-    EXPECT_EQ(acclist[1], sel_option.get_value()[0]);
+    EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[0]);
+}
+
 }
 
 class GncOptionMultichoiceTest : public ::testing::Test
@@ -710,6 +736,24 @@ TEST_F(GncMultichoiceOption, test_permissible_value_stuff)
               m_option.permissible_value_index("xyzzy"));
 }
 
+TEST_F(GncMultichoiceOption, test_multichoice_out)
+{
+    std::ostringstream oss;
+    oss << m_option;
+    EXPECT_EQ(oss.str(), m_option.get_value<std::string>());
+}
+
+TEST_F(GncMultichoiceOption, test_multichoice_in)
+{
+    std::istringstream iss{"grault"};
+    EXPECT_THROW({
+            iss >> m_option;
+        }, std::invalid_argument);
+    iss.clear(); //reset failedbit
+    iss.str("pork");
+    iss >> m_option;
+    EXPECT_EQ(iss.str(), m_option.get_value<std::string>());
+}
 class GncOptionDateOptionTest : public ::testing::Test
 {
 protected:

commit 98ca190700cae549d355e4b5508e9e653c8a452e
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 3 11:04:17 2019 -0800

    Fix commodity in/out to use namespace & mnemonic instead of GUID.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 9492d7d31..1ced4cdde 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -113,7 +113,7 @@ GncOptionDateValue::get_value() const
         m_period == RelativeDatePeriod::END_PREV_YEAR ||
         m_period == RelativeDatePeriod::START_PREV_QUARTER ||
         m_period == RelativeDatePeriod::END_PREV_QUARTER;
-    
+
     if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday)
     {
         //No set accounting period, use the calendar year
@@ -217,13 +217,14 @@ GncOptionDateValue::in_stream(std::istream& iss)
 QofInstance*
 qof_instance_from_string(const std::string& str, GncOptionUIType type)
 {
-    auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
     QofIdType qof_type;
+    bool commodity_type{false};
     switch(type)
     {
         case GncOptionUIType::CURRENCY:
         case GncOptionUIType::COMMODITY:
             qof_type = "Commodity";
+            commodity_type = true;
             break;
         case GncOptionUIType::BUDGET:
             qof_type = "Budget";
@@ -256,6 +257,17 @@ qof_instance_from_string(const std::string& str, GncOptionUIType type)
             break;
     }
     auto book{gnc_get_current_book()};
+    if (commodity_type)
+    {
+        auto sep{str.find(":")};
+        auto name_space{str.substr(0, sep)};
+        auto mnemonic{str.substr(sep + 1, -1)};
+        auto table = gnc_commodity_table_get_table(book);
+        return QOF_INSTANCE(gnc_commodity_table_lookup(table,
+                                                       name_space.c_str(),
+                                                       mnemonic.c_str()));
+    }
+    auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
     auto col{qof_book_get_collection(book, qof_type)};
     return QOF_INSTANCE(qof_collection_lookup_entity(col, &guid));
 }
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 98cbc908a..8ad19c7f4 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -184,6 +184,9 @@ using Account = struct account_s;
 %ignore GncOptionDateValue(GncOptionDateValue&&);
 %ignore GncOptionDateValue::operator=(const GncOptionDateValue&);
 %ignore GncOptionDateValue::operator=(GncOptionDateValue&&);
+%ignore operator<<(std::ostream&, const GncOption&);
+%ignore operator>>(std::istream&, GncOption&);
+
 %rename(absolute) RelativeDatePeriod::ABSOLUTE;
 %rename(today) RelativeDatePeriod::TODAY;
 %rename(start_this_month) RelativeDatePeriod::START_THIS_MONTH;
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index fb2038d9f..2f4a9afd2 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -260,50 +260,90 @@ TEST_F(GncOptionCommodityTest, test_currency_validator)
     EXPECT_FALSE(option.validate(QOF_INSTANCE(m_aapl)));
 }
 
+static inline std::string make_currency_str(gnc_commodity* cur)
+{
+    std::string cur_str{gnc_commodity_get_mnemonic(cur)};
+    return cur_str;
+}
+
+static inline std::string make_commodity_str(gnc_commodity* com)
+{
+    std::string com_str{gnc_commodity_get_namespace(com)};
+    com_str += " ";
+    com_str += gnc_commodity_get_mnemonic(com);
+    return com_str;
 }
 
-TEST_F(GncOptionTest, test_qofinstance_out)
+static inline std::string make_currency_SCM_str(gnc_commodity* cur)
 {
-    auto table = gnc_commodity_table_new();
-    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
-    auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
-    auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
+    std::string cur_str{gnc_commodity_get_mnemonic(cur)};
+    cur_str.insert(0, "\"");
+    cur_str += "\"";
+    return cur_str;
+}
+
+static inline std::string make_commodity_SCM_str(gnc_commodity* com)
+{
+    std::string com_str{commodity_scm_intro};
+    com_str += "\"";
+    com_str += gnc_commodity_get_namespace(com);
+    com_str += "\" \"";
+    com_str += gnc_commodity_get_mnemonic(com);
+    com_str += "\")";
+    return com_str;
+}
 
-    std::string eur_guid{gnc::GUID{*qof_instance_get_guid(eur)}.to_string()};
+TEST_F(GncOptionCommodityTest, test_currency_out)
+{
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
+                                       m_eur, true);
+
+    std::string eur_str{make_currency_str(m_eur)};
     std::ostringstream oss;
     oss << option;
-    EXPECT_EQ(eur_guid, oss.str());
-    gnc_commodity_destroy(eur);
-    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
-    gnc_commodity_table_destroy(table);
+    EXPECT_EQ(eur_str, oss.str());
 }
 
-TEST_F(GncOptionTest, test_qofinstance_in)
+TEST_F(GncOptionCommodityTest, test_commodity_out)
 {
-    auto table = gnc_commodity_table_new();
-    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
-    auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
-    auto usd = gnc_commodity_new(m_book, "United States Dollar",
-                                 "CURRENCY", "USD", NULL, 100);
-    auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
-                                    "NYSE", "HPE", NULL, 1);
-    auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
+    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_hpe),
+                     GncOptionUIType::COMMODITY};
+    std::string hpe_str{make_commodity_str(m_hpe)};
+    std::ostringstream oss;
+    oss << option;
+    EXPECT_EQ(hpe_str, oss.str());
+}
+
+TEST_F(GncOptionCommodityTest, test_currency_in)
+{
+
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
+                                       m_eur, true);
 
     EXPECT_THROW({
-            std::string hpe_guid{gnc::GUID{*qof_instance_get_guid(hpe)}.to_string()};
-            std::istringstream iss{hpe_guid};
+            std::string hpe_str{make_commodity_str(m_hpe)};
+            std::istringstream iss{hpe_str};
             iss >> option;
         }, std::invalid_argument);
     EXPECT_NO_THROW({
-            std::string usd_guid{gnc::GUID{*qof_instance_get_guid(usd)}.to_string()};
-            std::istringstream iss{usd_guid};
+            std::string usd_str{make_currency_str(m_usd)};
+            std::istringstream iss{usd_str};
             iss >> option;
-            EXPECT_EQ(QOF_INSTANCE(usd), option.get_value<QofInstance*>());
+            EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<QofInstance*>());
         });
-    gnc_commodity_destroy(eur);
-    gnc_commodity_destroy(usd);
-    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
-    gnc_commodity_table_destroy(table);
+}
+
+TEST_F(GncOptionCommodityTest, test_commodity_in)
+{
+    GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_aapl),
+                     GncOptionUIType::COMMODITY};
+
+    std::string hpe_str{make_commodity_str(m_hpe)};
+    std::istringstream iss{hpe_str};
+    iss >> option;
+    EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<QofInstance*>());
+}
+
 }
 
 class GncUIItem

commit 4b997cd025b9a65a3a7ee354f52fb787e65f7cb8
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Dec 3 10:43:35 2019 -0800

    Fixups for GncOptionDateValue better design.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 55c52bb60..9492d7d31 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -152,7 +152,7 @@ GncOptionDateValue::get_value() const
     return static_cast<time64>(GncDateTime(now));
 }
 static const char* date_type_str[] {"absolute", "relative"};
-static const std::array<const char*, 16> date_period_str
+static const std::array<const char*, 15> date_period_str
 {
     "today",
     "start-this-month", "end-this-month",
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index f3e58f226..98cbc908a 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -184,6 +184,22 @@ using Account = struct account_s;
 %ignore GncOptionDateValue(GncOptionDateValue&&);
 %ignore GncOptionDateValue::operator=(const GncOptionDateValue&);
 %ignore GncOptionDateValue::operator=(GncOptionDateValue&&);
+%rename(absolute) RelativeDatePeriod::ABSOLUTE;
+%rename(today) RelativeDatePeriod::TODAY;
+%rename(start_this_month) RelativeDatePeriod::START_THIS_MONTH;
+%rename(end_this_month) RelativeDatePeriod::END_THIS_MONTH;
+%rename(start_prev_month) RelativeDatePeriod::START_PREV_MONTH;
+%rename(end_prev_month) RelativeDatePeriod::END_PREV_MONTH;
+%rename(start_current_quarter) RelativeDatePeriod::START_CURRENT_QUARTER;
+%rename(end_current_quarter) RelativeDatePeriod::END_CURRENT_QUARTER;
+%rename(start_prev_quarter) RelativeDatePeriod::START_PREV_QUARTER;
+%rename(end_prev_quarter) RelativeDatePeriod::END_PREV_QUARTER;
+%rename(start_cal_year) RelativeDatePeriod::START_CAL_YEAR;
+%rename(end_cal_yea) RelativeDatePeriod::END_CAL_YEAR;
+%rename(start_prev_year) RelativeDatePeriod::START_PREV_YEAR;
+%rename(end_prev_year) RelativeDatePeriod::END_PREV_YEAR;
+%rename(start_accounting_period) RelativeDatePeriod::START_ACCOUNTING_PERIOD;
+%rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD;
 
 %typemap(typecheck, precedence=SWIG_TYPECHECK_INT64) time64 {
     $1 = scm_is_signed_integer($input, INT64_MAX, INT64_MIN);
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index dde592513..7ecb2f951 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -183,12 +183,13 @@
   (test-begin "test-gnc-test-date-option")
   (let* ((option-db (gnc-option-db-new))
          (date-opt (gnc-register-date-interval-option option-db "foo" "bar"
-                                                      "baz" "Phony Option"))
+                                                      "baz" "Phony Option"
+                                                      (RelativeDatePeriod-today)))
          (a-time (gnc-dmy2time64 11 07 2019)))
     (test-equal (current-time) (gnc-option-value option-db "foo" "bar"))
     (gnc-set-option option-db "foo" "bar" a-time)
-    (test-equal a-time (gnc-option-value option-db "foo" "bar"))
-    (test-end "test-gnc-test-date-option")))
+    (test-equal a-time (gnc-option-value option-db "foo" "bar")))
+  (test-end "test-gnc-test-date-option"))
 
 (define (test-gnc-make-number-range-option)
   (test-begin "test-gnc-number-range-option")

commit b2fb57d39edb6798eaad084e168363dc76438e2a
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Dec 1 15:58:52 2019 -0800

    Fix GncOption::set_value() to work with GncOptionDateValue::set_value(RelativeDatePeriod).
    
    Adjusting tests as necessary.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 210293049..b3a750b25 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -728,7 +728,13 @@ public:
     template <typename ValueType> void set_value(ValueType value)
     {
         std::visit([value](auto& option) {
-                if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
+                if constexpr
+                    (std::is_same_v<std::decay_t<decltype(option.get_value())>,
+                      std::decay_t<ValueType>> ||
+                     (std::is_same_v<std::decay_t<decltype(option)>,
+                      GncOptionDateValue> &&
+                      std::is_same_v<std::decay_t<ValueType>,
+                      RelativeDatePeriod>))
                                  option.set_value(value);
             }, m_option);
     }
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 20bc43441..fb2038d9f 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -676,7 +676,7 @@ protected:
     GncOptionDateOptionTest() :
         m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option"}} {}
 
-    GncOptionDateValue m_option;
+    GncOption m_option;
 };
 
 using GncDateOption = GncOptionDateOptionTest;
@@ -694,7 +694,7 @@ TEST_F(GncDateOption, test_set_and_get_absolute)
 {
     time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
     m_option.set_value(time1);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_month_start)
@@ -704,7 +704,7 @@ TEST_F(GncDateOption, test_set_and_get_month_start)
     gnc_gdate_set_month_start(&month_start);
     time64 time1{time64_from_gdate(&month_start, DayPart::start)};
     m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_month_end)
@@ -714,7 +714,7 @@ TEST_F(GncDateOption, test_set_and_get_month_end)
     gnc_gdate_set_month_end(&month_end);
     time64 time1{time64_from_gdate(&month_end, DayPart::end)};
     m_option.set_value(RelativeDatePeriod::END_THIS_MONTH);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_prev_month_start)
@@ -724,7 +724,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_month_start)
     gnc_gdate_set_prev_month_start(&prev_month_start);
     time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)};
     m_option.set_value(RelativeDatePeriod::START_PREV_MONTH);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_prev_month_end)
@@ -734,7 +734,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_month_end)
     gnc_gdate_set_prev_month_end(&prev_month_end);
     time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)};
     m_option.set_value(RelativeDatePeriod::END_PREV_MONTH);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_quarter_start)
@@ -744,7 +744,7 @@ TEST_F(GncDateOption, test_set_and_get_quarter_start)
     gnc_gdate_set_quarter_start(&quarter_start);
     time64 time1{time64_from_gdate(&quarter_start, DayPart::start)};
     m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_quarter_end)
@@ -754,7 +754,7 @@ TEST_F(GncDateOption, test_set_and_get_quarter_end)
     gnc_gdate_set_quarter_end(&quarter_end);
     time64 time1{time64_from_gdate(&quarter_end, DayPart::end)};
     m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_prev_quarter_start)
@@ -764,7 +764,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_quarter_start)
     gnc_gdate_set_prev_quarter_start(&prev_quarter_start);
     time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)};
     m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_prev_quarter_end)
@@ -774,7 +774,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_quarter_end)
     gnc_gdate_set_prev_quarter_end(&prev_quarter_end);
     time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)};
     m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_year_start)
@@ -784,7 +784,7 @@ TEST_F(GncDateOption, test_set_and_get_year_start)
     gnc_gdate_set_year_start(&year_start);
     time64 time1{time64_from_gdate(&year_start, DayPart::start)};
     m_option.set_value(RelativeDatePeriod::START_CAL_YEAR);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_year_end)
@@ -794,7 +794,7 @@ TEST_F(GncDateOption, test_set_and_get_year_end)
     gnc_gdate_set_year_end(&year_end);
     time64 time1{time64_from_gdate(&year_end, DayPart::end)};
     m_option.set_value(RelativeDatePeriod::END_CAL_YEAR);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_prev_year_start)
@@ -804,7 +804,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_start)
     gnc_gdate_set_prev_year_start(&prev_year_start);
     time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
     m_option.set_value(RelativeDatePeriod::START_PREV_YEAR);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_set_and_get_prev_year_end)
@@ -814,7 +814,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_end)
     gnc_gdate_set_prev_year_end(&prev_year_end);
     time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)};
     m_option.set_value(RelativeDatePeriod::END_PREV_YEAR);
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_out)
@@ -915,7 +915,7 @@ TEST_F(GncDateOption, test_stream_in_absolute)
 
     std::istringstream iss{timestr};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_month_start)
@@ -926,7 +926,7 @@ TEST_F(GncDateOption, test_stream_in_month_start)
     time64 time1{time64_from_gdate(&month_start, DayPart::start)};
     std::istringstream iss{"relative . start-this-month"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 
@@ -938,7 +938,7 @@ TEST_F(GncDateOption, test_stream_in_month_end)
     time64 time1{time64_from_gdate(&month_end, DayPart::end)};
     std::istringstream iss{"relative . end-this-month"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_prev_month_start)
@@ -949,7 +949,7 @@ TEST_F(GncDateOption, test_stream_in_prev_month_start)
     time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)};
     std::istringstream iss{"relative . start-prev-month"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_prev_month_end)
@@ -960,7 +960,7 @@ TEST_F(GncDateOption, test_stream_in_prev_month_end)
     time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)};
     std::istringstream iss{"relative . end-prev-month"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_quarter_start)
@@ -971,7 +971,7 @@ TEST_F(GncDateOption, test_stream_in_quarter_start)
     time64 time1{time64_from_gdate(&quarter_start, DayPart::start)};
     std::istringstream iss{"relative . start-current-quarter"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_quarter_end)
@@ -982,7 +982,7 @@ TEST_F(GncDateOption, test_stream_in_quarter_end)
     time64 time1{time64_from_gdate(&quarter_end, DayPart::end)};
     std::istringstream iss{"relative . end-current-quarter"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_prev_quarter_start)
@@ -993,7 +993,7 @@ TEST_F(GncDateOption, test_stream_in_prev_quarter_start)
     time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)};
     std::istringstream iss{"relative . start-prev-quarter"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_prev_quarter_end)
@@ -1004,7 +1004,7 @@ TEST_F(GncDateOption, test_stream_in_prev_quarter_end)
     time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)};
     std::istringstream iss{"relative . end-prev-quarter"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_year_start)
@@ -1015,7 +1015,7 @@ TEST_F(GncDateOption, test_stream_in_year_start)
     time64 time1{time64_from_gdate(&year_start, DayPart::start)};
     std::istringstream iss{"relative . start-cal-year"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_year_end)
@@ -1026,7 +1026,7 @@ TEST_F(GncDateOption, test_stream_in_year_end)
     time64 time1{time64_from_gdate(&year_end, DayPart::end)};
     std::istringstream iss{"relative . end-cal-year"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_prev_year_start)
@@ -1037,7 +1037,7 @@ TEST_F(GncDateOption, test_stream_in_prev_year_start)
     time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
     std::istringstream iss{"relative . start-prev-year"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }
 
 TEST_F(GncDateOption, test_stream_in_prev_year_end)
@@ -1048,5 +1048,5 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end)
     time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)};
     std::istringstream iss{"relative . end-prev-year"};
     iss >> m_option;
-    EXPECT_EQ(time1, m_option.get_value());
+    EXPECT_EQ(time1, m_option.get_value<time64>());
 }

commit 4dcf4a0e6a4df3360017a2750cac75037db8046c
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Dec 1 15:20:46 2019 -0800

    Test GncOptionRangeValue as a GncOption.
    
    Better representation of how it will be used.

diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 23a0f1d99..20bc43441 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -351,34 +351,36 @@ class GncOptionRangeTest : public ::testing::Test
 {
 protected:
     GncOptionRangeTest() :
-        m_intoption{"foo", "bar", "baz", "Phony Option", 15, 1, 30, 1},
-        m_doubleoption{"waldo", "pepper", "salt", "Phonier Option",
-                1.5, 1.0, 3.0, 0.1} {}
-
-    GncOptionRangeValue<int> m_intoption;
-    GncOptionRangeValue<double> m_doubleoption;
+        m_intoption{GncOptionRangeValue<int>{"foo", "bar", "baz",
+                                             "Phony Option", 15, 1, 30, 1}},
+        m_doubleoption{GncOptionRangeValue<double>{"waldo", "pepper", "salt",
+                                                   "Phonier Option", 1.5, 1.0,
+                                                   3.0, 0.1}} {}
+
+    GncOption m_intoption;
+    GncOption m_doubleoption;
 };
 
 using GncRangeOption = GncOptionRangeTest;
 
 TEST_F(GncRangeOption, test_initialization)
 {
-    EXPECT_EQ(15, m_intoption.get_value());
-    EXPECT_EQ(1.5, m_doubleoption.get_value());
-    EXPECT_EQ(15, m_intoption.get_default_value());
-    EXPECT_EQ(1.5, m_doubleoption.get_default_value());
+    EXPECT_EQ(15, m_intoption.get_value<int>());
+    EXPECT_EQ(1.5, m_doubleoption.get_value<double>());
+    EXPECT_EQ(15, m_intoption.get_default_value<int>());
+    EXPECT_EQ(1.5, m_doubleoption.get_default_value<double>());
 }
 
 TEST_F(GncRangeOption, test_setter)
 {
     EXPECT_THROW({ m_intoption.set_value(45); }, std::invalid_argument);
     EXPECT_NO_THROW({ m_intoption.set_value(20); });
-    EXPECT_EQ(20, m_intoption.get_value());
-    EXPECT_EQ(15, m_intoption.get_default_value());
+    EXPECT_EQ(20, m_intoption.get_value<int>());
+    EXPECT_EQ(15, m_intoption.get_default_value<int>());
     EXPECT_THROW({ m_doubleoption.set_value(4.5); }, std::invalid_argument);
     EXPECT_NO_THROW({ m_doubleoption.set_value(2.0); });
-    EXPECT_EQ(2.0, m_doubleoption.get_value());
-    EXPECT_EQ(1.5, m_doubleoption.get_default_value());
+    EXPECT_EQ(2.0, m_doubleoption.get_value<double>());
+    EXPECT_EQ(1.5, m_doubleoption.get_default_value<double>());
 }
 
 TEST_F(GncRangeOption, test_range_out)

commit 4cabd6c0529fd85b30c7df1425033e94b0fc5936
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Dec 1 15:10:03 2019 -0800

    Change the GncOptionMultichoiceValue test to use a GncOption.
    
    Requires exposing the permissible values functions to GncOption.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index e953c3d8c..210293049 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -801,6 +801,58 @@ public:
                            return false;
                    }, m_option);
     }
+
+    std::size_t num_permissible_values() const {
+        return std::visit([] (const auto& option) -> size_t {
+                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                                    GncOptionMultichoiceValue>)
+                                        return option.num_permissible_values();
+                       else
+                           return std::numeric_limits<std::size_t>::max();
+                   }, m_option);
+    }
+
+    std::size_t permissible_value_index(const std::string& value) const {
+        return std::visit([&value] (const auto& option) -> size_t {
+                              std::cerr << typeid(option).name() << std::endl;
+                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                                    GncOptionMultichoiceValue>)
+                                        return option.permissible_value_index(value);
+                       else
+                           return std::numeric_limits<std::size_t>::max();;
+                   }, m_option);
+    }
+
+    const std::string& permissible_value(std::size_t index) const {
+        return std::visit([index] (const auto& option) -> const std::string& {
+                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                     GncOptionMultichoiceValue>)
+                                        return option.permissible_value(index);
+                       else
+                           return c_empty_string;
+                   }, m_option);
+    }
+
+    const std::string& permissible_value_name(std::size_t index) const {
+        return std::visit([index] (const auto& option) -> const std::string& {
+                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                     GncOptionMultichoiceValue>)
+                                        return option.permissible_value_name(index);
+                       else
+                           return c_empty_string;
+                   }, m_option);
+    }
+
+    const std::string& permissible_value_description(std::size_t index) const {
+        return std::visit([index] (const auto& option) -> const std::string& {
+                              if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
+                                     GncOptionMultichoiceValue>)
+                                        return option.permissible_value_description(index);
+                       else
+                           return c_empty_string;
+                   }, m_option);
+    }
+
     std::ostream& out_stream(std::ostream& oss) const
     {
             return std::visit([&oss](auto& option) -> std::ostream& {
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 10e97297b..23a0f1d99 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -608,14 +608,15 @@ class GncOptionMultichoiceTest : public ::testing::Test
 {
 protected:
     GncOptionMultichoiceTest() :
-        m_option{"foo", "bar", "baz", "Phony Option", "plugh",
+        m_option{GncOptionMultichoiceValue{"foo", "bar", "baz",
+                                           "Phony Option", "plugh",
             {
                 {"plugh", "xyzzy", "thud"},
                 {"waldo", "pepper", "salt"},
                 {"pork", "sausage", "links"},
                 {"corge", "grault", "garply"}
-            }} {}
-    GncOptionMultichoiceValue m_option;
+                                           }}} {}
+    GncOption m_option;
 };
 
 using GncMultichoiceOption = GncOptionMultichoiceTest;
@@ -627,18 +628,20 @@ TEST_F(GncMultichoiceOption, test_option_ui_type)
 
 TEST_F(GncMultichoiceOption, test_validate)
 {
-    EXPECT_TRUE(m_option.validate("waldo"));
-    EXPECT_FALSE(m_option.validate("grault"));
+    EXPECT_TRUE(
+        m_option.validate(std::string{"waldo"})
+        );
+    EXPECT_FALSE(m_option.validate(std::string{"grault"}));
 }
 
 TEST_F(GncMultichoiceOption, test_set_value)
 {
     EXPECT_NO_THROW({
-            m_option.set_value("pork");
-            EXPECT_STREQ("pork", m_option.get_value().c_str());
+            m_option.set_value(std::string{"pork"});
+            EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
         });
-    EXPECT_THROW({ m_option.set_value("salt"); }, std::invalid_argument);
-    EXPECT_STREQ("pork", m_option.get_value().c_str());
+    EXPECT_THROW({ m_option.set_value(std::string{"salt"}); }, std::invalid_argument);
+    EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
 }
 
 TEST_F(GncMultichoiceOption, test_num_permissible)

commit e2a36a8be3221a5f7a7584697e712bfe45d06af0
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Dec 1 15:05:18 2019 -0800

    Expose function validate to GncOption and test it on GncOptionValidatedValue.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index c85559f05..e953c3d8c 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -298,7 +298,7 @@ public:
     GncOptionValidatedValue<ValueType>& operator=(GncOptionValidatedValue<ValueType>&&) = default;
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
-    bool validate(ValueType value) { return m_validator(value); }
+    bool validate(ValueType value) const { return m_validator(value); }
     void set_value(ValueType value)
     {
         if (this->validate(value))
@@ -787,6 +787,20 @@ public:
             }, m_option);
     }
 
+    template<typename ValueType>
+    bool validate(ValueType value) const {
+        return std::visit([value] (const auto& option) -> bool {
+                              if constexpr ((std::is_same_v<std::decay_t<decltype(option)>,
+                                                   GncOptionMultichoiceValue> &&
+                                      std::is_same_v<std::decay_t<ValueType>,
+                                                     std::string>) ||
+                                            std::is_same_v<std::decay_t<decltype(option)>,
+                                            GncOptionValidatedValue<ValueType>>)
+                                        return option.validate(value);
+                       else
+                           return false;
+                   }, m_option);
+    }
     std::ostream& out_stream(std::ostream& oss) const
     {
             return std::visit([&oss](auto& option) -> std::ostream& {
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index dbf93a79d..10e97297b 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -252,6 +252,14 @@ TEST_F(GncOptionCommodityTest, test_currency_setter)
                  GNC_COMMODITY(option.get_value<QofInstance*>()));
 }
 
+TEST_F(GncOptionCommodityTest, test_currency_validator)
+{
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
+                                       m_eur, true);
+    EXPECT_TRUE(option.validate(QOF_INSTANCE(m_usd)));
+    EXPECT_FALSE(option.validate(QOF_INSTANCE(m_aapl)));
+}
+
 }
 
 TEST_F(GncOptionTest, test_qofinstance_out)

commit 6df516dfcdbefe59ba60914a98f14ae2832dd8b6
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Dec 1 15:00:04 2019 -0800

    Clean up the commodity/currency tests with a better fixture class.

diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 2d2ed45c2..dbf93a79d 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -163,6 +163,48 @@ TEST_F(GncOptionTest, test_commodity_ctor)
     gnc_commodity_destroy(hpe);
 }
 
+class GncOptionCommodityTest : public ::testing::Test
+{
+protected:
+    GncOptionCommodityTest() : m_session{gnc_get_current_session()},
+                               m_book{gnc_get_current_book()},
+                               m_table{gnc_commodity_table_new()}
+    {
+/* We can't initialize the commodities with their values because we first must
+ * set the book's commodity table.
+ */
+        qof_book_set_data(m_book, GNC_COMMODITY_TABLE, m_table);
+        m_eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
+        gnc_commodity_table_insert(m_table, m_eur);
+        m_usd = gnc_commodity_new(m_book, "United States Dollar", "CURRENCY",
+                                  "USD", NULL, 100);
+        gnc_commodity_table_insert(m_table, m_usd);
+        m_aapl = gnc_commodity_new(m_book, "Apple", "NASDAQ", "AAPL", NULL, 1);
+        gnc_commodity_table_insert(m_table, m_aapl);
+        m_hpe = gnc_commodity_new(m_book, "Hewlett Packard", "NYSE", "HPE",
+                                  NULL, 1);
+        gnc_commodity_table_insert(m_table, m_hpe);
+    }
+    ~GncOptionCommodityTest()
+    {
+        gnc_commodity_destroy(m_hpe);
+        gnc_commodity_destroy(m_aapl);
+        gnc_commodity_destroy(m_usd);
+        gnc_commodity_destroy(m_eur);
+        qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
+        gnc_commodity_table_destroy(m_table);
+        gnc_clear_current_session();
+    }
+
+    QofSession* m_session;
+    QofBook* m_book;
+    gnc_commodity_table * m_table;
+    gnc_commodity *m_eur;
+    gnc_commodity *m_usd;
+    gnc_commodity *m_aapl;
+    gnc_commodity *m_hpe;
+};
+
 static GncOption
 make_currency_option (const char* section, const char* name,
                       const char* key, const char* doc_string,
@@ -179,57 +221,37 @@ make_currency_option (const char* section, const char* name,
     return option;
 }
 
-TEST_F(GncOptionTest, test_currency_ctor)
+TEST_F(GncOptionCommodityTest, test_currency_ctor)
 {
-    auto table = gnc_commodity_table_new();
-    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
-    auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
-                                    "NYSE", "HPE", NULL, 1);
     EXPECT_THROW({
             auto option = make_currency_option("foo", "bar", "baz",
-                                               "Phony Option", hpe, false);
+                                               "Phony Option", m_hpe, false);
         }, std::invalid_argument);
-    gnc_commodity_destroy(hpe);
-    auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
     EXPECT_NO_THROW({
             auto option = make_currency_option("foo", "bar", "baz",
-                                               "Phony Option", eur, true);
+                                               "Phony Option", m_eur, true);
         });
-    gnc_commodity_destroy(eur);
-    auto usd = gnc_commodity_new(m_book, "United States Dollar",
-                                 "CURRENCY", "USD", NULL, 100);
     EXPECT_NO_THROW({
             auto option = make_currency_option("foo", "bar", "baz",
-                                               "Phony Option", usd, true);
+                                               "Phony Option", m_usd, true);
         });
-    gnc_commodity_destroy(usd);
-    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
-    gnc_commodity_table_destroy(table);
 }
 
-TEST_F(GncOptionTest, test_currency_setter)
+TEST_F(GncOptionCommodityTest, test_currency_setter)
 {
-    auto table = gnc_commodity_table_new();
-    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
-    auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
-                                    "NYSE", "HPE", NULL, 1);
-    auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
-    auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
-    auto usd = gnc_commodity_new(m_book, "United States Dollar",
-                                 "CURRENCY", "USD", NULL, 100);
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option", m_eur, true);
     EXPECT_NO_THROW({
-            option.set_value(QOF_INSTANCE(usd));
+            option.set_value(QOF_INSTANCE(m_usd));
         });
-    EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option.get_value<QofInstance*>()));
+    EXPECT_PRED2(gnc_commodity_equal, m_usd,
+                 GNC_COMMODITY(option.get_value<QofInstance*>()));
     EXPECT_THROW({
-            option.set_value(QOF_INSTANCE(hpe));
+            option.set_value(QOF_INSTANCE(m_hpe));
         }, std::invalid_argument);
-    EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option.get_value<QofInstance*>()));
-    gnc_commodity_destroy(hpe);
-    gnc_commodity_destroy(usd);
-    gnc_commodity_destroy(eur);
-    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
-    gnc_commodity_table_destroy(table);
+    EXPECT_PRED2(gnc_commodity_equal, m_usd,
+                 GNC_COMMODITY(option.get_value<QofInstance*>()));
+}
+
 }
 
 TEST_F(GncOptionTest, test_qofinstance_out)

commit 52d0ec52901fca6919e4d9ce09a525d992d15361
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Nov 25 11:20:44 2019 -0800

    Change GncOptionDateValue design to better match usage.
    
    Added advantage that it handles (not correctly, but in the same way as
    the old code) the ambiguity between a period starting or ending with
    the stored value "relative . today".

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 3587c9372..55c52bb60 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -88,18 +88,32 @@ set_day_and_time(struct tm& now, bool starting)
 time64
 GncOptionDateValue::get_value() const
 {
-    if (m_type == DateType::ABSOLUTE)
+    if (m_period == RelativeDatePeriod::ABSOLUTE)
         return m_date;
     if (m_period == RelativeDatePeriod::TODAY)
         return static_cast<time64>(GncDateTime());
-    if (m_period == RelativeDatePeriod::ACCOUNTING_PERIOD)
-        return m_type == DateType::STARTING ?
-            gnc_accounting_period_fiscal_start() :
-            gnc_accounting_period_fiscal_end();
+    if (m_period == RelativeDatePeriod::START_ACCOUNTING_PERIOD)
+        return gnc_accounting_period_fiscal_start();
+    if (m_period == RelativeDatePeriod::END_ACCOUNTING_PERIOD)
+        return gnc_accounting_period_fiscal_end();
 
-    struct tm now{static_cast<tm>(GncDateTime())};
+    GncDateTime now_t;
+    if (m_period == RelativeDatePeriod::TODAY)
+        return static_cast<time64>(now_t);
+    struct tm now{static_cast<tm>(now_t)};
     struct tm period{static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()))};
+    bool starting =  m_period == RelativeDatePeriod::START_PREV_MONTH ||
+        m_period == RelativeDatePeriod::START_THIS_MONTH ||
+        m_period == RelativeDatePeriod::START_CAL_YEAR ||
+        m_period == RelativeDatePeriod::START_PREV_YEAR ||
+        m_period == RelativeDatePeriod::START_CURRENT_QUARTER ||
+        m_period == RelativeDatePeriod::START_PREV_QUARTER;
 
+    bool prev = m_period == RelativeDatePeriod::START_PREV_YEAR ||
+        m_period == RelativeDatePeriod::END_PREV_YEAR ||
+        m_period == RelativeDatePeriod::START_PREV_QUARTER ||
+        m_period == RelativeDatePeriod::END_PREV_QUARTER;
+    
     if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday)
     {
         //No set accounting period, use the calendar year
@@ -107,34 +121,40 @@ GncOptionDateValue::get_value() const
         period.tm_mday = 0;
     }
 
-    if (m_period == RelativeDatePeriod::CAL_YEAR ||
-        m_period == RelativeDatePeriod::PREV_YEAR)
+    if (m_period == RelativeDatePeriod::START_CAL_YEAR ||
+        m_period == RelativeDatePeriod::END_CAL_YEAR ||
+        m_period == RelativeDatePeriod::START_PREV_YEAR ||
+        m_period == RelativeDatePeriod::END_PREV_YEAR)
     {
-        if (m_period == RelativeDatePeriod::PREV_YEAR)
+
+        if (prev)
             --now.tm_year;
-        now.tm_mon = m_type == DateType::STARTING ? 0 : 11;
+        now.tm_mon = starting ? 0 : 11;
     }
-    else if (m_period == RelativeDatePeriod::PREV_QUARTER ||
-             m_period == RelativeDatePeriod::CURRENT_QUARTER)
+    else if (m_period == RelativeDatePeriod::START_PREV_QUARTER ||
+             m_period == RelativeDatePeriod::END_PREV_QUARTER ||
+             m_period == RelativeDatePeriod::START_CURRENT_QUARTER ||
+             m_period == RelativeDatePeriod::END_CURRENT_QUARTER)
     {
         auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon :
                        period.tm_mon - now.tm_mon) % 3;
         now.tm_mon = now.tm_mon - offset;
-        if (m_period == RelativeDatePeriod::PREV_QUARTER)
+        if (prev)
             now.tm_mon -= 3;
-        if (m_type == DateType::ENDING)
+        if (!starting)
             now.tm_mon += 2;
     }
-    else if (m_period == RelativeDatePeriod::PREV_MONTH)
+    else if (m_period == RelativeDatePeriod::START_PREV_MONTH ||
+             m_period == RelativeDatePeriod::END_PREV_MONTH)
         --now.tm_mon;
     normalize_month(now);
-    set_day_and_time(now, m_type == DateType::STARTING);
+    set_day_and_time(now, starting);
     return static_cast<time64>(GncDateTime(now));
 }
 static const char* date_type_str[] {"absolute", "relative"};
 static const std::array<const char*, 16> date_period_str
 {
-    "today", "today",
+    "today",
     "start-this-month", "end-this-month",
     "start-prev-month", "end-prev-month",
     "start-current-quarter", "end-current-quarter",
@@ -148,14 +168,11 @@ static const std::array<const char*, 16> date_period_str
 std::ostream&
 GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
 {
-    if (m_type == DateType::ABSOLUTE)
+    if (m_period == RelativeDatePeriod::ABSOLUTE)
         oss << date_type_str[0] << " . " << m_date;
     else
-    {
-        int n{ m_type == DateType::STARTING ? 0 : 1};
-        n += 2 * static_cast<int>(m_period);
-        oss << date_type_str[1] << " . " << date_period_str[n];
-    }
+        oss << date_type_str[1] << " . " <<
+            date_period_str[static_cast<int>(m_period)];
     return oss;
 }
 
@@ -185,8 +202,7 @@ GncOptionDateValue::in_stream(std::istream& iss)
         }
 
         int64_t index = period - date_period_str.begin();
-        DateType type = index % 2 ? DateType::ENDING : DateType::STARTING;
-        set_value(std::make_pair(type, index / 2));
+        set_value(static_cast<RelativeDatePeriod>(index));
     }
     else
     {
@@ -198,23 +214,6 @@ GncOptionDateValue::in_stream(std::istream& iss)
     return iss;
 }
 
-void
-GncOptionDateValue::set_value(DateSetterValue value)
-{
-    auto [type, val] = value;
-    m_type = type;
-    if (type == DateType::ABSOLUTE)
-    {
-        m_period = RelativeDatePeriod::TODAY;
-        m_date = static_cast<time64>(val);
-        return;
-    }
-
-    m_period = static_cast<RelativeDatePeriod>(val);
-    m_date = 0;
-}
-
-
 QofInstance*
 qof_instance_from_string(const std::string& str, GncOptionUIType type)
 {
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index ece53edc3..c85559f05 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -597,26 +597,26 @@ gnc-date-option-absolute-time m_type == DateTyupe::Absolute
 gnc-date-option-relative-time m_type != DateTyupe::Absolute
  */
 
-enum class DateType
-{
-    ABSOLUTE,
-    STARTING,
-    ENDING,
-};
-
-enum class RelativeDatePeriod : int64_t
+enum class RelativeDatePeriod : int
 {
+    ABSOLUTE = -1,
     TODAY,
-    THIS_MONTH,
-    PREV_MONTH,
-    CURRENT_QUARTER,
-    PREV_QUARTER,
-    CAL_YEAR,
-    PREV_YEAR,
-    ACCOUNTING_PERIOD
+    START_THIS_MONTH,
+    END_THIS_MONTH,
+    START_PREV_MONTH,
+    END_PREV_MONTH,
+    START_CURRENT_QUARTER,
+    END_CURRENT_QUARTER,
+    START_PREV_QUARTER,
+    END_PREV_QUARTER,
+    START_CAL_YEAR,
+    END_CAL_YEAR,
+    START_PREV_YEAR,
+    END_PREV_YEAR,
+    START_ACCOUNTING_PERIOD,
+    END_ACCOUNTING_PERIOD
 };
 
-using DateSetterValue = std::tuple<DateType, int64_t>;
 class GncOptionDateValue : public OptionClassifier, public OptionUIItem
 {
 public:
@@ -624,8 +624,24 @@ public:
                               const char* key, const char* doc_string) :
         OptionClassifier{section, name, key, doc_string},
         OptionUIItem(GncOptionUIType::DATE),
-        m_type{DateType::ABSOLUTE}, m_period{RelativeDatePeriod::TODAY},
-        m_date{static_cast<time64>(GncDateTime())} {}
+        m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
+        m_date{INT64_MAX},
+        m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD},
+        m_default_date{INT64_MAX} {}
+    GncOptionDateValue(const char* section, const char* name,
+                       const char* key, const char* doc_string,
+                       time64 time) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(GncOptionUIType::DATE),
+        m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time},
+        m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {}
+    GncOptionDateValue(const char* section, const char* name,
+                       const char* key, const char* doc_string,
+                       const RelativeDatePeriod period) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(GncOptionUIType::DATE),
+        m_period{period}, m_date{INT64_MAX},
+        m_default_period{period}, m_default_date{INT64_MAX} {}
         GncOptionDateValue(const GncOptionDateValue&) = default;
         GncOptionDateValue(GncOptionDateValue&&) = default;
         GncOptionDateValue& operator=(const GncOptionDateValue&) = default;
@@ -634,17 +650,21 @@ public:
     time64 get_default_value() const { return static_cast<time64>(GncDateTime()); }
     std::ostream& out_stream(std::ostream& oss) const noexcept;
     std::istream& in_stream(std::istream& iss);
-    void set_value(DateSetterValue);
+    void set_value(RelativeDatePeriod value) {
+        m_period = value;
+        m_date = INT64_MAX;
+    }
     void set_value(time64 time) {
-        m_type = DateType::ABSOLUTE;
-        m_period = RelativeDatePeriod::TODAY;
+        m_period = RelativeDatePeriod::ABSOLUTE;
         m_date = time;
     }
-    bool is_changed() const noexcept { return true; }
+    bool is_changed() const noexcept { return m_period != m_default_period &&
+            m_date != m_default_date; }
 private:
-    DateType m_type;
     RelativeDatePeriod m_period;
     time64 m_date;
+    RelativeDatePeriod m_default_period;
+    time64 m_default_date;
 };
 
 template<> inline std::ostream&
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index e93e7f249..c4c7fe4d4 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -530,9 +530,10 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
 
 void
 gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section,
-                             const char* name, const char* key,
-                             const char* doc_string)
+                                  const char* name, const char* key,
+                                  const char* doc_string,
+                                  RelativeDatePeriod period)
 {
-    GncOption option{GncOptionDateValue(section, name, key, doc_string)};
+    GncOption option{GncOptionDateValue(section, name, key, doc_string, period)};
     db->register_option(section, std::move(option));
 }
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 81564448c..b8c515133 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -265,5 +265,6 @@ void gnc_register_dateformat_option(const GncOptionDBPtr& db,
 
 void gnc_register_date_interval_option(const GncOptionDBPtr& db,
                                        const char* section, const char* name,
-                                       const char* key, const char* doc_string);
+                                       const char* key, const char* doc_string,
+                                       RelativeDatePeriod period);
 #endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 9876cb81a..2d2ed45c2 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -658,8 +658,7 @@ time64_from_gdate(const GDate* g_date, DayPart when)
 TEST_F(GncDateOption, test_set_and_get_absolute)
 {
     time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
-    DateSetterValue value1{DateType::ABSOLUTE, time1};
-    m_option.set_value(value1);
+    m_option.set_value(time1);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -669,8 +668,7 @@ TEST_F(GncDateOption, test_set_and_get_month_start)
     g_date_set_time_t(&month_start, time(nullptr));
     gnc_gdate_set_month_start(&month_start);
     time64 time1{time64_from_gdate(&month_start, DayPart::start)};
-    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -680,8 +678,7 @@ TEST_F(GncDateOption, test_set_and_get_month_end)
     g_date_set_time_t(&month_end, time(nullptr));
     gnc_gdate_set_month_end(&month_end);
     time64 time1{time64_from_gdate(&month_end, DayPart::end)};
-    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::END_THIS_MONTH);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -691,8 +688,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_month_start)
     g_date_set_time_t(&prev_month_start, time(nullptr));
     gnc_gdate_set_prev_month_start(&prev_month_start);
     time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)};
-    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::START_PREV_MONTH);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -702,8 +698,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_month_end)
     g_date_set_time_t(&prev_month_end, time(nullptr));
     gnc_gdate_set_prev_month_end(&prev_month_end);
     time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)};
-    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::END_PREV_MONTH);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -713,8 +708,7 @@ TEST_F(GncDateOption, test_set_and_get_quarter_start)
     g_date_set_time_t(&quarter_start, time(nullptr));
     gnc_gdate_set_quarter_start(&quarter_start);
     time64 time1{time64_from_gdate(&quarter_start, DayPart::start)};
-    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -724,8 +718,7 @@ TEST_F(GncDateOption, test_set_and_get_quarter_end)
     g_date_set_time_t(&quarter_end, time(nullptr));
     gnc_gdate_set_quarter_end(&quarter_end);
     time64 time1{time64_from_gdate(&quarter_end, DayPart::end)};
-    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -735,8 +728,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_quarter_start)
     g_date_set_time_t(&prev_quarter_start, time(nullptr));
     gnc_gdate_set_prev_quarter_start(&prev_quarter_start);
     time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)};
-    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -746,8 +738,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_quarter_end)
     g_date_set_time_t(&prev_quarter_end, time(nullptr));
     gnc_gdate_set_prev_quarter_end(&prev_quarter_end);
     time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)};
-    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -757,8 +748,7 @@ TEST_F(GncDateOption, test_set_and_get_year_start)
     g_date_set_time_t(&year_start, time(nullptr));
     gnc_gdate_set_year_start(&year_start);
     time64 time1{time64_from_gdate(&year_start, DayPart::start)};
-    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::START_CAL_YEAR);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -768,8 +758,7 @@ TEST_F(GncDateOption, test_set_and_get_year_end)
     g_date_set_time_t(&year_end, time(nullptr));
     gnc_gdate_set_year_end(&year_end);
     time64 time1{time64_from_gdate(&year_end, DayPart::end)};
-    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::END_CAL_YEAR);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -779,8 +768,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_start)
     g_date_set_time_t(&prev_year_start, time(nullptr));
     gnc_gdate_set_prev_year_start(&prev_year_start);
     time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
-    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::START_PREV_YEAR);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
@@ -790,16 +778,14 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_end)
     g_date_set_time_t(&prev_year_end, time(nullptr));
     gnc_gdate_set_prev_year_end(&prev_year_end);
     time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)};
-    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
-    m_option.set_value(value1);
+    m_option.set_value(RelativeDatePeriod::END_PREV_YEAR);
     EXPECT_EQ(time1, m_option.get_value());
 }
 
 TEST_F(GncDateOption, test_stream_out)
 {
     time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
-    DateSetterValue value1{DateType::ABSOLUTE, time1};
-    m_option.set_value(value1);
+    m_option.set_value(time1);
     std::ostringstream oss;
     oss << time1;
     std::string timestr{"absolute . "};
@@ -808,98 +794,77 @@ TEST_F(GncDateOption, test_stream_out)
     oss << m_option;
     EXPECT_EQ(oss.str(), timestr);
 
-    DateSetterValue value2{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::TODAY)};
-    m_option.set_value(value2);
+    m_option.set_value(RelativeDatePeriod::TODAY);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . today");
 
-    DateSetterValue value3{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::TODAY)};
-    m_option.set_value(value3);
-    oss.str("");
-    oss << m_option;
-    EXPECT_STREQ(oss.str().c_str(), "relative . today");
-
-    DateSetterValue value4{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
-    m_option.set_value(value4);
+    m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . start-this-month");
 
-    DateSetterValue value5{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
-    m_option.set_value(value5);
+    m_option.set_value(RelativeDatePeriod::END_THIS_MONTH);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . end-this-month");
 
-    DateSetterValue value6{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
-    m_option.set_value(value6);
+    m_option.set_value(RelativeDatePeriod::START_PREV_MONTH);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-month");
 
-    DateSetterValue value7{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
-    m_option.set_value(value7);
+    m_option.set_value(RelativeDatePeriod::END_PREV_MONTH);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-month");
 
-    DateSetterValue value8{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
-    m_option.set_value(value8);
+    m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . start-current-quarter");
 
-    DateSetterValue value9{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
-    m_option.set_value(value9);
+    m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . end-current-quarter");
 
-    DateSetterValue value10{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
-    m_option.set_value(value10);
+    m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-quarter");
 
-    DateSetterValue value11{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
-    m_option.set_value(value11);
+    m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-quarter");
 
-    DateSetterValue value12{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
-    m_option.set_value(value12);
+    m_option.set_value(RelativeDatePeriod::START_CAL_YEAR);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . start-cal-year");
 
-    DateSetterValue value13{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
-    m_option.set_value(value13);
+    m_option.set_value(RelativeDatePeriod::END_CAL_YEAR);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . end-cal-year");
 
-    DateSetterValue value14{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
-    m_option.set_value(value14);
+    m_option.set_value(RelativeDatePeriod::START_PREV_YEAR);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-year");
 
-    DateSetterValue value15{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
-    m_option.set_value(value15);
+    m_option.set_value(RelativeDatePeriod::END_PREV_YEAR);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-year");
 
-    DateSetterValue value16{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::ACCOUNTING_PERIOD)};
-    m_option.set_value(value16);
+    m_option.set_value(RelativeDatePeriod::START_ACCOUNTING_PERIOD);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-fin-year");
 
-    DateSetterValue value17{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::ACCOUNTING_PERIOD)};
-    m_option.set_value(value17);
+    m_option.set_value(RelativeDatePeriod::END_ACCOUNTING_PERIOD);
     oss.str("");
     oss << m_option;
     EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-fin-year");
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index fd0b709f8..1dd088dc1 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -176,10 +176,24 @@ TEST_F(GncOptionDBTest, test_register_multichoice_option)
     EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str());
 }
 
+static time64
+time64_from_gdate(const GDate* g_date, DayPart when)
+{
+    GncDate date{g_date_get_year(g_date), g_date_get_month(g_date),
+            g_date_get_day(g_date)};
+    GncDateTime time{date, when};
+    return static_cast<time64>(time);
+}
+
+
 TEST_F(GncOptionDBTest, test_register_date_interval_option)
 {
-    gnc_register_date_interval_option(m_db, "foo", "bar", "baz", "Phony Option");
-    auto time{gnc_dmy2time64(11, 7, 2019)};
+    gnc_register_date_interval_option(m_db, "foo", "bar", "baz", "Phony Option",
+                                      RelativeDatePeriod::START_ACCOUNTING_PERIOD);
+    GDate prev_year_start;
+    g_date_set_time_t(&prev_year_start, time(nullptr));
+    gnc_gdate_set_prev_year_start(&prev_year_start);
+    time64 time{time64_from_gdate(&prev_year_start, DayPart::start)};
     ASSERT_TRUE(m_db->set_option("foo", "bar", time));
     EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value<time64>());
 }

commit 883127a59d7081ac2a791331ae9e9aa1af3959dc
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 22 15:45:42 2019 -0800

    Implement operators >> and << on GncOption.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 06f2abf9b..3587c9372 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -24,9 +24,11 @@
 //#include "options.h"
 #include "gnc-option.hpp"
 #include <gnc-datetime.hpp>
+#include <guid.hpp>
 extern "C"
 {
 #include "gnc-accounting-period.h"
+#include "gnc-ui-util.h"
 }
 
 bool
@@ -129,6 +131,72 @@ GncOptionDateValue::get_value() const
     set_day_and_time(now, m_type == DateType::STARTING);
     return static_cast<time64>(GncDateTime(now));
 }
+static const char* date_type_str[] {"absolute", "relative"};
+static const std::array<const char*, 16> date_period_str
+{
+    "today", "today",
+    "start-this-month", "end-this-month",
+    "start-prev-month", "end-prev-month",
+    "start-current-quarter", "end-current-quarter",
+    "start-prev-quarter", "end-prev-quarter",
+    "start-cal-year", "end-cal-year",
+    "start-prev-year", "end-prev-year",
+    "start-prev-fin-year", "end-prev-fin-year"
+};
+
+
+std::ostream&
+GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
+{
+    if (m_type == DateType::ABSOLUTE)
+        oss << date_type_str[0] << " . " << m_date;
+    else
+    {
+        int n{ m_type == DateType::STARTING ? 0 : 1};
+        n += 2 * static_cast<int>(m_period);
+        oss << date_type_str[1] << " . " << date_period_str[n];
+    }
+    return oss;
+}
+
+std::istream&
+GncOptionDateValue::in_stream(std::istream& iss)
+{
+    std::string type_str;
+    std::getline(iss, type_str, '.');
+    if (type_str == "absolute ")
+    {
+        time64 time;
+        iss >> time;
+        set_value(time);
+    }
+    else if (type_str == "relative ")
+    {
+        std::string period_str;
+        iss >> period_str;
+        auto period = std::find(date_period_str.begin(), date_period_str.end(),
+                                period_str);
+        if (period == date_period_str.end())
+        {
+            std::string err{"Unknown period string in date option: '"};
+            err += period_str;
+            err += "'";
+            throw std::invalid_argument(err);
+        }
+
+        int64_t index = period - date_period_str.begin();
+        DateType type = index % 2 ? DateType::ENDING : DateType::STARTING;
+        set_value(std::make_pair(type, index / 2));
+    }
+    else
+    {
+        std::string err{"Unknown date type string in date option: '"};
+        err += type_str;
+        err += "'";
+        throw std::invalid_argument{err};
+    }
+    return iss;
+}
 
 void
 GncOptionDateValue::set_value(DateSetterValue value)
@@ -145,3 +213,57 @@ GncOptionDateValue::set_value(DateSetterValue value)
     m_period = static_cast<RelativeDatePeriod>(val);
     m_date = 0;
 }
+
+
+QofInstance*
+qof_instance_from_string(const std::string& str, GncOptionUIType type)
+{
+    auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
+    QofIdType qof_type;
+    switch(type)
+    {
+        case GncOptionUIType::CURRENCY:
+        case GncOptionUIType::COMMODITY:
+            qof_type = "Commodity";
+            break;
+        case GncOptionUIType::BUDGET:
+            qof_type = "Budget";
+            break;
+        case GncOptionUIType::OWNER:
+            qof_type = "gncOwner";
+            break;
+        case GncOptionUIType::CUSTOMER:
+            qof_type = "gncCustomer";
+            break;
+        case GncOptionUIType::VENDOR:
+            qof_type = "gncVendor";
+            break;
+        case GncOptionUIType::EMPLOYEE:
+            qof_type = "gncEmployee";
+            break;
+        case GncOptionUIType::INVOICE:
+            qof_type = "gncInvoice";
+            break;
+        case GncOptionUIType::TAX_TABLE:
+            qof_type = "gncTaxtable";
+            break;
+        case GncOptionUIType::QUERY:
+            qof_type = "gncQuery";
+            break;
+        case GncOptionUIType::ACCOUNT_LIST:
+        case GncOptionUIType::ACCOUNT_SEL:
+        default:
+            qof_type = "Account";
+            break;
+    }
+    auto book{gnc_get_current_book()};
+    auto col{qof_book_get_collection(book, qof_type)};
+    return QOF_INSTANCE(qof_collection_lookup_entity(col, &guid));
+}
+
+std::string
+qof_instance_to_string(const QofInstance* inst)
+{
+    gnc::GUID guid{*qof_instance_get_guid(inst)};
+    return guid.to_string();
+}
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index d8e483943..ece53edc3 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -33,7 +33,6 @@ extern "C"
 #include <gnc-commodity.h>
 }
 #include <gnc-datetime.hpp>
-#include <gnc-numeric.hpp>
 #include <libguile.h>
 #include <string>
 #include <utility>
@@ -41,6 +40,7 @@ extern "C"
 #include <exception>
 #include <functional>
 #include <variant>
+#include <iostream>
 
 /*
  * Unused base class to document the structure of the current Scheme option
@@ -176,6 +176,28 @@ private:
     GncOptionUIType m_ui_type;
 };
 
+/* These will work when m_value is a built-in class; GnuCash class and container
+ * values will need specialization unless they happen to define operators << and
+ * >>.
+ * Note that SWIG 3.0.12 chokes on the typename = std::enable_if_t<> form so we
+ * have to use the non-type parameter form.
+ */
+template<class OptionValueClass, typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>>, int> = 0>
+std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
+{
+    oss << opt.get_value();
+    return oss;
+}
+
+template<class OptionValueClass, typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>>, int> = 0>
+std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
+{
+    std::decay_t<decltype(opt.get_value())> value;
+    iss >> value;
+    opt.set_value(value);
+    return iss;
+}
+
 template <typename ValueType>
 class GncOptionValue :
     public OptionClassifier, public OptionUIItem
@@ -195,11 +217,52 @@ public:
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
     void set_value(ValueType new_value) { m_value = new_value; }
+    bool is_changed() const noexcept { return m_value != m_default_value; }
 private:
     ValueType m_value;
     ValueType m_default_value;
 };
 
+QofInstance* qof_instance_from_string(const std::string& str,
+                                      GncOptionUIType type);
+std::string qof_instance_to_string(const QofInstance* inst);
+
+template<> inline std::ostream&
+operator<< <GncOptionValue<bool>>(std::ostream& oss,
+                                  const GncOptionValue<bool>& opt)
+{
+    oss << (opt.get_value() ? "#t" : "#f");
+    return oss;
+}
+
+template<> inline std::ostream&
+operator<< <GncOptionValue<QofInstance*>>(std::ostream& oss,
+                                       const GncOptionValue<QofInstance*>& opt)
+{
+    oss << qof_instance_to_string(opt.get_value());
+    return oss;
+}
+
+template<> inline std::istream&
+operator>> <GncOptionValue<bool>>(std::istream& iss,
+                                  GncOptionValue<bool>& opt)
+{
+    std::string instr;
+    iss >> instr;
+    opt.set_value(instr == "#t" ? true : false);
+    return iss;
+}
+
+template<> inline std::istream&
+operator>> <GncOptionValue<QofInstance*>>(std::istream& iss,
+                                       GncOptionValue<QofInstance*>& opt)
+{
+    std::string instr;
+    iss >> instr;
+    opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
+    return iss;
+}
+
 template <typename ValueType>
 class GncOptionValidatedValue :
     public OptionClassifier, public OptionUIItem
@@ -243,6 +306,7 @@ public:
         else
             throw std::invalid_argument("Validation failed, value not set.");
     }
+    bool is_changed() const noexcept { return m_value != m_default_value; }
 private:
     ValueType m_value;
     ValueType m_default_value;
@@ -250,6 +314,25 @@ private:
     ValueType m_validation_data;
 };
 
+template<> inline std::ostream&
+operator<< <GncOptionValidatedValue<QofInstance*>>(std::ostream& oss,
+                                       const GncOptionValidatedValue<QofInstance*>& opt)
+{
+        oss << qof_instance_to_string(opt.get_value());
+        std::cerr << qof_instance_to_string(opt.get_value());
+        return oss;
+}
+
+template<> inline std::istream&
+operator>> <GncOptionValidatedValue<QofInstance*>>(std::istream& iss,
+                                       GncOptionValidatedValue<QofInstance*>& opt)
+{
+    std::string instr;
+    iss >> instr;
+    opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
+    return iss;
+}
+
 /**
  * Used for numeric ranges and plot sizes.
  */
@@ -283,6 +366,7 @@ public:
         else
             throw std::invalid_argument("Validation failed, value not set.");
     }
+    bool is_changed() const noexcept { return m_value != m_default_value; }
 private:
     ValueType m_value;
     ValueType m_default_value;
@@ -376,6 +460,7 @@ public:
     {
         return std::get<2>(m_choices.at(index));
     }
+    bool is_changed() const noexcept { return m_value != m_default_value; }
 private:
     std::size_t find_key (const std::string& key) const noexcept
     {
@@ -462,13 +547,41 @@ public:
             //throw!
             m_value = values;
     }
-
+    bool is_changed() const noexcept { return m_value != m_default_value; }
 private:
     GncOptionAccountList m_value;
     GncOptionAccountList m_default_value;
     GncOptionAccountTypeList m_allowed;
 };
 
+template<> inline std::ostream&
+operator<< <GncOptionAccountValue>(std::ostream& oss,
+                                       const GncOptionAccountValue& opt)
+{
+    auto values{opt.get_value()};
+    for (auto value : values)
+        oss << qof_instance_to_string(QOF_INSTANCE(value)) << " ";
+    return oss;
+}
+
+template<> inline std::istream&
+operator>> <GncOptionAccountValue>(std::istream& iss,
+                                   GncOptionAccountValue& opt)
+{
+    GncOptionAccountList values;
+    while (true)
+    {
+        std::string str;
+        std::getline(iss, str, ' ');
+        if (!str.empty())
+            values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type()));
+        else
+            break;
+    }
+    opt.set_value(values);
+    iss.clear();
+    return iss;
+}
 /** Date options
  * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
  * flag and a time64, or for legacy purposes the absolute flag and a timespec.
@@ -503,7 +616,7 @@ enum class RelativeDatePeriod : int64_t
     ACCOUNTING_PERIOD
 };
 
-using DateSetterValue = std::pair<DateType, int64_t>;
+using DateSetterValue = std::tuple<DateType, int64_t>;
 class GncOptionDateValue : public OptionClassifier, public OptionUIItem
 {
 public:
@@ -519,23 +632,39 @@ public:
         GncOptionDateValue& operator=(GncOptionDateValue&&) = default;
     time64 get_value() const;
     time64 get_default_value() const { return static_cast<time64>(GncDateTime()); }
+    std::ostream& out_stream(std::ostream& oss) const noexcept;
+    std::istream& in_stream(std::istream& iss);
     void set_value(DateSetterValue);
     void set_value(time64 time) {
         m_type = DateType::ABSOLUTE;
         m_period = RelativeDatePeriod::TODAY;
         m_date = time;
     }
+    bool is_changed() const noexcept { return true; }
 private:
     DateType m_type;
     RelativeDatePeriod m_period;
     time64 m_date;
 };
 
+template<> inline std::ostream&
+operator<< <GncOptionDateValue>(std::ostream& oss,
+                                       const GncOptionDateValue& opt)
+{
+    return opt.out_stream(oss);
+}
+
+template<> inline std::istream&
+operator>> <GncOptionDateValue>(std::istream& iss,
+                                   GncOptionDateValue& opt)
+{
+    return opt.in_stream(iss);
+}
+
 using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<bool>,
                                       GncOptionValue<int64_t>,
                                       GncOptionValue<QofInstance*>,
-                                      GncOptionValue<QofQuery*>,
                                       GncOptionAccountValue,
                                       GncOptionMultichoiceValue,
                                       GncOptionRangeValue<int>,
@@ -631,9 +760,43 @@ public:
                 option.make_internal();
             }, m_option);
     }
+    bool is_changed()
+    {
+        return std::visit([](const auto& option)->bool {
+                return option.is_changed();
+            }, m_option);
+    }
+
+    std::ostream& out_stream(std::ostream& oss) const
+    {
+            return std::visit([&oss](auto& option) -> std::ostream& {
+            oss << option;
+            return oss;
+        }, m_option);
+    }
+    std::istream& in_stream(std::istream& iss)
+    {
+    return std::visit([&iss](auto& option) -> std::istream& {
+            iss >> option;
+            return iss;
+        }, m_option);
+    }
+
     GncOptionVariant& _get_option() const { return m_option; }
 private:
     mutable GncOptionVariant m_option;
 };
 
+inline std::ostream&
+operator<<(std::ostream& oss, const GncOption& opt)
+{
+    return opt.out_stream(oss);
+}
+
+inline std::istream&
+operator>>(std::istream& iss, GncOption& opt)
+{
+    return opt.in_stream(iss);
+}
+
 #endif //GNC_OPTION_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 8fac00eb8..e93e7f249 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -138,9 +138,9 @@ GncOptionDB::find_section(const char* section)
 }
 
 std::optional<std::reference_wrapper<GncOption>>
-GncOptionDB::find_option(const char* section, const char* name)
+GncOptionDB::find_option(const char* section, const char* name) const
 {
-    auto db_section = find_section(section);
+    auto db_section = const_cast<GncOptionDB*>(this)->find_section(section);
     if (!db_section)
         return std::nullopt;
     auto db_opt = std::find_if(
@@ -173,6 +173,36 @@ GncOptionDB::make_internal(const char* section, const char* name)
         db_opt->get().make_internal();
 }
 
+std::ostream&
+GncOptionDB::serialize_option_scheme(std::ostream& oss,
+                                     const char* option_prolog,
+                                     const char* section, const char* name) const noexcept
+{
+    auto db_opt = find_option(section, name);
+    if (!db_opt || !db_opt->get().is_changed())
+        return oss;
+    oss << c_scheme_serialization_elements[0] << option_prolog;
+    oss << c_scheme_serialization_elements[1] << section;
+    oss << c_scheme_serialization_elements[1] << name; //repeats, not an error!
+//    oss << c_scheme_serialization_elements[2] << db_opt->get();
+    oss << c_scheme_serialization_elements[3];
+
+    return oss;
+}
+
+std::ostream&
+GncOptionDB::serialize_option_key_value(std::ostream& oss,
+                                        const char* section,
+                                        const char* name) const noexcept
+{
+
+    auto db_opt = find_option(section, name);
+    if (!db_opt || !db_opt->get().is_changed())
+        return oss;
+    oss << section << ":" << name << "=" /* << db_opt->get() */ << ";";
+    return oss;
+}
+
 void
 GncOptionDB::commit()
 {
@@ -304,7 +334,7 @@ gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
     }
     catch (const std::invalid_argument& err)
     {
-        std::cerr << "Account List Limited Option, value failed validation, option not registerd.\n";
+        std::cerr << "Account List Limited Option, value failed validation, option not registered.\n";
     }
 }
 
@@ -405,7 +435,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
                           const char* name, const char* key,
                           const char* doc_string, QofQuery* value)
 {
-    GncOption option{section, name, key, doc_string, value,
+    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
             GncOptionUIType::INTERNAL};
     db->register_option(section, std::move(option));
 }
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 34a1af78d..81564448c 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -28,6 +28,7 @@
 #include <functional>
 #include <exception>
 #include <optional>
+#include <iostream>
 extern "C"
 {
 #include <gncInvoice.h>
@@ -83,14 +84,33 @@ public:
     void make_internal(const char* section, const char* name);
     void commit();
     std::optional<std::reference_wrapper<GncOptionSection>> find_section(const char* section);
-    std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name);
+    std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name) {
+        return static_cast<const GncOptionDB&>(*this).find_option(section, name);
+    }
+    std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name) const;
 private:
+    std::ostream& serialize_option_scheme(std::ostream& oss,
+                                          const char* option_prolog,
+                                          const char* section, const char* name) const noexcept;
+    std::ostream& serialize_option_key_value(std::ostream& oss,
+                                             const char* section,
+                                             const char* name) const noexcept;
+    void load_option_scheme(std::istream iss);
+    void load_option_key_value(std::istream iss);
     std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
     std::vector<GncOptionSection> m_sections;
     bool m_dirty = false;
 
     std::function<GncOptionUIItem*()> m_get_ui_value;
     std::function<void(GncOptionUIItem*)> m_set_ui_value;
+    static constexpr char const* const c_scheme_serialization_elements[]
+    {
+        "(let ((option (gnc:lookup-option ",
+        "\n                                 ",
+        ")))\n   ((lambda (o) (if o (gnc:option-set-value o",
+        "))) option))\n\n"
+        };
+
 };
 
 /**
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index e92ea84ed..f3e58f226 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -337,5 +337,3 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         GncOption_set_value_from_scm(&(db_opt->get()), new_value);
     }
 %}
-
-*/
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 7491b4333..9876cb81a 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -23,10 +23,13 @@
 
 #include <gtest/gtest.h>
 #include <gnc-option.hpp>
+#include <guid.hpp>
 extern "C"
 {
 #include <gnc-date.h>
 #include <time.h>
+#include <gnc-ui-util.h>
+#include <gnc-session.h>
 }
 
 TEST(GncOption, test_string_ctor)
@@ -64,6 +67,23 @@ TEST(GncOption, test_string_value)
         });
 }
 
+TEST(GncOption, test_string_stream_out)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
+    std::ostringstream oss;
+    oss << option;
+    EXPECT_EQ(oss.str(), option.get_value<std::string>());
+}
+
+TEST(GncOption, test_string_stream_in)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
+    std::string pepper{"pepper"};
+    std::istringstream iss{pepper};
+    iss >> option;
+    EXPECT_EQ(pepper, option.get_value<std::string>());
+}
+
 TEST(GncOption, test_int64_t_value)
 {
     GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
@@ -72,35 +92,81 @@ TEST(GncOption, test_int64_t_value)
     EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
 }
 
-TEST(GNCOption, test_budget_ctor)
+TEST(GncOption, test_int64_stream_out)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option",  INT64_C(123456789));
+    std::ostringstream oss;
+    oss << option;
+    EXPECT_STREQ(oss.str().c_str(), "123456789");
+}
+
+TEST(GncOption, test_int64_stream_in)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option",  INT64_C(123456789));
+    std::string number{"987654321"};
+    std::istringstream iss{number};
+    iss >> option;
+    EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
+}
+
+TEST(GncOption, test_bool_stream_out)
 {
-    auto book = qof_book_new();
-    auto budget = gnc_budget_new(book);
+    GncOption option("foo", "bar", "baz", "Phony Option", false);
+    std::ostringstream oss;
+    oss << option;
+    EXPECT_STREQ(oss.str().c_str(), "#f");
+    oss.str("");
+    option.set_value(true);
+    oss << option;
+    EXPECT_STREQ(oss.str().c_str(), "#t");
+}
+
+TEST(GncOption, test_bool_stream_in)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", false);
+    std::istringstream iss("#t");
+    iss >> option;
+    EXPECT_TRUE(option.get_value<bool>());
+    iss.str("#f");
+    iss >> option;
+    EXPECT_FALSE(option.get_value<bool>());
+}
+
+class GncOptionTest : public ::testing::Test
+{
+protected:
+    GncOptionTest() : m_session{gnc_get_current_session()}, m_book{gnc_get_current_book()} {}
+    ~GncOptionTest() { gnc_clear_current_session(); }
+
+    QofSession* m_session;
+    QofBook* m_book;
+};
+
+TEST_F(GncOptionTest, test_budget_ctor)
+{
+    auto budget = gnc_budget_new(m_book);
     EXPECT_NO_THROW({
             GncOption option("foo", "bar", "baz", "Phony Option",
                              QOF_INSTANCE(budget));
         });
     gnc_budget_destroy(budget);
-    qof_book_destroy(book);
 }
 
-TEST(GNCOption, test_commodity_ctor)
+TEST_F(GncOptionTest, test_commodity_ctor)
 {
-    auto book = qof_book_new();
-    auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
+    auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
                                     "NYSE", "HPE", NULL, 1);
     EXPECT_NO_THROW({
             GncOption option("foo", "bar", "baz", "Phony Option",
                              QOF_INSTANCE(hpe));
         });
     gnc_commodity_destroy(hpe);
-    qof_book_destroy(book);
 }
 
 static GncOption
 make_currency_option (const char* section, const char* name,
                       const char* key, const char* doc_string,
-                      gnc_commodity *value)
+                      gnc_commodity *value, bool is_currency=false)
 {
     GncOption option{GncOptionValidatedValue<QofInstance*>{
         section, name, key, doc_string, QOF_INSTANCE(value),
@@ -108,52 +174,48 @@ make_currency_option (const char* section, const char* name,
             {
                 return GNC_IS_COMMODITY (new_value) &&
                     gnc_commodity_is_currency(GNC_COMMODITY(new_value));
-            }
-        }};
+            }, is_currency ? GncOptionUIType::CURRENCY : GncOptionUIType::COMMODITY}
+    };
     return option;
 }
 
-TEST(GNCOption, test_currency_ctor)
+TEST_F(GncOptionTest, test_currency_ctor)
 {
-    auto book = qof_book_new();
     auto table = gnc_commodity_table_new();
-    qof_book_set_data(book, GNC_COMMODITY_TABLE, table);
-    auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
+    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
+    auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
                                     "NYSE", "HPE", NULL, 1);
     EXPECT_THROW({
             auto option = make_currency_option("foo", "bar", "baz",
-                                               "Phony Option", hpe);
+                                               "Phony Option", hpe, false);
         }, std::invalid_argument);
     gnc_commodity_destroy(hpe);
-    auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100);
+    auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
     EXPECT_NO_THROW({
             auto option = make_currency_option("foo", "bar", "baz",
-                                               "Phony Option", eur);
+                                               "Phony Option", eur, true);
         });
     gnc_commodity_destroy(eur);
-    auto usd = gnc_commodity_new(book, "United States Dollar",
+    auto usd = gnc_commodity_new(m_book, "United States Dollar",
                                  "CURRENCY", "USD", NULL, 100);
     EXPECT_NO_THROW({
             auto option = make_currency_option("foo", "bar", "baz",
-                                               "Phony Option",usd);
+                                               "Phony Option", usd, true);
         });
     gnc_commodity_destroy(usd);
-    qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr);
+    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
     gnc_commodity_table_destroy(table);
-    qof_book_destroy(book);
 }
 
-TEST(GNCOption, test_currency_setter)
+TEST_F(GncOptionTest, test_currency_setter)
 {
-    auto book = qof_book_new();
     auto table = gnc_commodity_table_new();
-    qof_book_set_data(book, GNC_COMMODITY_TABLE, table);
-    auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
+    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
+    auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
                                     "NYSE", "HPE", NULL, 1);
-    auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100);
-            auto option = make_currency_option("foo", "bar", "baz",
-                                               "Phony Option",eur);
-    auto usd = gnc_commodity_new(book, "United States Dollar",
+    auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
+    auto usd = gnc_commodity_new(m_book, "United States Dollar",
                                  "CURRENCY", "USD", NULL, 100);
     EXPECT_NO_THROW({
             option.set_value(QOF_INSTANCE(usd));
@@ -166,9 +228,52 @@ TEST(GNCOption, test_currency_setter)
     gnc_commodity_destroy(hpe);
     gnc_commodity_destroy(usd);
     gnc_commodity_destroy(eur);
-    qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr);
+    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
+    gnc_commodity_table_destroy(table);
+}
+
+TEST_F(GncOptionTest, test_qofinstance_out)
+{
+    auto table = gnc_commodity_table_new();
+    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
+    auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
+
+    std::string eur_guid{gnc::GUID{*qof_instance_get_guid(eur)}.to_string()};
+    std::ostringstream oss;
+    oss << option;
+    EXPECT_EQ(eur_guid, oss.str());
+    gnc_commodity_destroy(eur);
+    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
+    gnc_commodity_table_destroy(table);
+}
+
+TEST_F(GncOptionTest, test_qofinstance_in)
+{
+    auto table = gnc_commodity_table_new();
+    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
+    auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
+    auto usd = gnc_commodity_new(m_book, "United States Dollar",
+                                 "CURRENCY", "USD", NULL, 100);
+    auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
+                                    "NYSE", "HPE", NULL, 1);
+    auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
+
+    EXPECT_THROW({
+            std::string hpe_guid{gnc::GUID{*qof_instance_get_guid(hpe)}.to_string()};
+            std::istringstream iss{hpe_guid};
+            iss >> option;
+        }, std::invalid_argument);
+    EXPECT_NO_THROW({
+            std::string usd_guid{gnc::GUID{*qof_instance_get_guid(usd)}.to_string()};
+            std::istringstream iss{usd_guid};
+            iss >> option;
+            EXPECT_EQ(QOF_INSTANCE(usd), option.get_value<QofInstance*>());
+        });
+    gnc_commodity_destroy(eur);
+    gnc_commodity_destroy(usd);
+    qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
     gnc_commodity_table_destroy(table);
-    qof_book_destroy(book);
 }
 
 class GncUIItem
@@ -246,6 +351,24 @@ TEST_F(GncRangeOption, test_setter)
     EXPECT_EQ(1.5, m_doubleoption.get_default_value());
 }
 
+TEST_F(GncRangeOption, test_range_out)
+{
+    std::ostringstream oss;
+    oss << "Integer " << m_intoption << " Double " << m_doubleoption << ".";
+    EXPECT_STREQ("Integer 15 Double 1.5.", oss.str().c_str());
+}
+
+TEST_F(GncRangeOption, test_range_in)
+{
+    std::istringstream iss{std::string{"45 4.5 20 2.0"}};
+    EXPECT_THROW({ iss >> m_intoption; }, std::invalid_argument);
+    EXPECT_THROW({ iss >> m_doubleoption; }, std::invalid_argument);
+    EXPECT_NO_THROW({ iss >> m_intoption; });
+    EXPECT_NO_THROW({ iss >> m_doubleoption; });
+    EXPECT_EQ(20, m_intoption.get_value());
+    EXPECT_EQ(2.0, m_doubleoption.get_value());
+}
+
 using AccountPair = std::pair<GncOptionAccountList&,
                               const GncOptionAccountTypeList&>;
 static void
@@ -264,7 +387,8 @@ class GncOptionAccountTest : public ::testing::Test
 {
 protected:
     GncOptionAccountTest() :
-        m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)}
+        m_session{gnc_get_current_session()}, m_book{gnc_get_current_book()},
+        m_root{gnc_account_create_root(m_book)}
     {
         auto create_account = [this](Account* parent, GNCAccountType type,
                                        const char* name)->Account* {
@@ -296,7 +420,7 @@ protected:
     {
         xaccAccountBeginEdit(m_root);
         xaccAccountDestroy(m_root); //It does the commit
-        qof_book_destroy(m_book);
+        gnc_clear_current_session();
     }
     GncOptionAccountList list_of_types(const GncOptionAccountTypeList& types)
     {
@@ -307,6 +431,7 @@ protected:
         return list;
     }
 
+    QofSession* m_session;
     QofBook* m_book;
     Account* m_root;
 };
@@ -389,6 +514,65 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
     EXPECT_EQ(false, option.validate(acclistbad));
 }
 
+TEST_F(GncOptionAccountTest, test_account_list_out)
+{
+    GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
+    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
+            GncOptionUIType::ACCOUNT_LIST, acclist};
+    std::ostringstream oss;
+    std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
+    acc_guids += " ";
+    acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
+    acc_guids += " ";
+
+    oss << option;
+    EXPECT_EQ(acc_guids, oss.str());
+
+    GncOptionAccountList accsel{acclist[0]};
+    GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option",
+                                 GncOptionUIType::ACCOUNT_LIST,
+                                 accsel, {ACCT_TYPE_BANK});
+    acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string();
+    acc_guids += " ";
+
+    oss.str("");
+    oss << sel_option;
+    EXPECT_EQ(acc_guids, oss.str());
+}
+
+TEST_F(GncOptionAccountTest, test_account_list_in)
+{
+    GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
+    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
+            GncOptionUIType::ACCOUNT_LIST, acclist};
+    std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
+    acc_guids += " ";
+    acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
+    acc_guids += " ";
+
+    std::istringstream iss{acc_guids};
+    iss >> option;
+    EXPECT_EQ(acclist, option.get_value());
+
+    GncOptionAccountList accsel{acclist[0]};
+    GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option",
+                                 GncOptionUIType::ACCOUNT_LIST,
+                                 accsel, {ACCT_TYPE_BANK});
+    GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
+    acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string();
+    acc_guids += " ";
+
+    iss.str(acc_guids);
+    iss >> sel_option;
+    EXPECT_EQ(accsel, sel_option.get_value());
+
+    acc_guids = gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
+    EXPECT_NO_THROW({
+            iss.str(acc_guids);
+            iss >> sel_option;
+        });
+    EXPECT_EQ(acclist[1], sel_option.get_value()[0]);
+}
 
 class GncOptionMultichoiceTest : public ::testing::Test
 {
@@ -611,3 +795,258 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_end)
     EXPECT_EQ(time1, m_option.get_value());
 }
 
+TEST_F(GncDateOption, test_stream_out)
+{
+    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
+    DateSetterValue value1{DateType::ABSOLUTE, time1};
+    m_option.set_value(value1);
+    std::ostringstream oss;
+    oss << time1;
+    std::string timestr{"absolute . "};
+    timestr += oss.str();
+    oss.str("");
+    oss << m_option;
+    EXPECT_EQ(oss.str(), timestr);
+
+    DateSetterValue value2{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::TODAY)};
+    m_option.set_value(value2);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . today");
+
+    DateSetterValue value3{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::TODAY)};
+    m_option.set_value(value3);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . today");
+
+    DateSetterValue value4{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
+    m_option.set_value(value4);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . start-this-month");
+
+    DateSetterValue value5{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
+    m_option.set_value(value5);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . end-this-month");
+
+    DateSetterValue value6{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
+    m_option.set_value(value6);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-month");
+
+    DateSetterValue value7{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
+    m_option.set_value(value7);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-month");
+
+    DateSetterValue value8{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
+    m_option.set_value(value8);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . start-current-quarter");
+
+    DateSetterValue value9{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
+    m_option.set_value(value9);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . end-current-quarter");
+
+    DateSetterValue value10{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
+    m_option.set_value(value10);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-quarter");
+
+    DateSetterValue value11{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
+    m_option.set_value(value11);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-quarter");
+
+    DateSetterValue value12{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
+    m_option.set_value(value12);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . start-cal-year");
+
+    DateSetterValue value13{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
+    m_option.set_value(value13);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . end-cal-year");
+
+    DateSetterValue value14{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
+    m_option.set_value(value14);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-year");
+
+    DateSetterValue value15{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
+    m_option.set_value(value15);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-year");
+
+    DateSetterValue value16{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::ACCOUNTING_PERIOD)};
+    m_option.set_value(value16);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-fin-year");
+
+    DateSetterValue value17{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::ACCOUNTING_PERIOD)};
+    m_option.set_value(value17);
+    oss.str("");
+    oss << m_option;
+    EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-fin-year");
+}
+
+TEST_F(GncDateOption, test_stream_in_absolute)
+{
+    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
+    std::ostringstream oss;
+    oss << time1;
+    std::string timestr{"absolute . "};
+    timestr += oss.str();
+
+    std::istringstream iss{timestr};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_month_start)
+{
+    GDate month_start;
+    g_date_set_time_t(&month_start, time(nullptr));
+    gnc_gdate_set_month_start(&month_start);
+    time64 time1{time64_from_gdate(&month_start, DayPart::start)};
+    std::istringstream iss{"relative . start-this-month"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+
+TEST_F(GncDateOption, test_stream_in_month_end)
+{
+    GDate month_end;
+    g_date_set_time_t(&month_end, time(nullptr));
+    gnc_gdate_set_month_end(&month_end);
+    time64 time1{time64_from_gdate(&month_end, DayPart::end)};
+    std::istringstream iss{"relative . end-this-month"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_prev_month_start)
+{
+    GDate prev_month_start;
+    g_date_set_time_t(&prev_month_start, time(nullptr));
+    gnc_gdate_set_prev_month_start(&prev_month_start);
+    time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)};
+    std::istringstream iss{"relative . start-prev-month"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_prev_month_end)
+{
+    GDate prev_month_end;
+    g_date_set_time_t(&prev_month_end, time(nullptr));
+    gnc_gdate_set_prev_month_end(&prev_month_end);
+    time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)};
+    std::istringstream iss{"relative . end-prev-month"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_quarter_start)
+{
+    GDate quarter_start;
+    g_date_set_time_t(&quarter_start, time(nullptr));
+    gnc_gdate_set_quarter_start(&quarter_start);
+    time64 time1{time64_from_gdate(&quarter_start, DayPart::start)};
+    std::istringstream iss{"relative . start-current-quarter"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_quarter_end)
+{
+    GDate quarter_end;
+    g_date_set_time_t(&quarter_end, time(nullptr));
+    gnc_gdate_set_quarter_end(&quarter_end);
+    time64 time1{time64_from_gdate(&quarter_end, DayPart::end)};
+    std::istringstream iss{"relative . end-current-quarter"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_prev_quarter_start)
+{
+    GDate prev_quarter_start;
+    g_date_set_time_t(&prev_quarter_start, time(nullptr));
+    gnc_gdate_set_prev_quarter_start(&prev_quarter_start);
+    time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)};
+    std::istringstream iss{"relative . start-prev-quarter"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_prev_quarter_end)
+{
+    GDate prev_quarter_end;
+    g_date_set_time_t(&prev_quarter_end, time(nullptr));
+    gnc_gdate_set_prev_quarter_end(&prev_quarter_end);
+    time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)};
+    std::istringstream iss{"relative . end-prev-quarter"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_year_start)
+{
+    GDate year_start;
+    g_date_set_time_t(&year_start, time(nullptr));
+    gnc_gdate_set_year_start(&year_start);
+    time64 time1{time64_from_gdate(&year_start, DayPart::start)};
+    std::istringstream iss{"relative . start-cal-year"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_year_end)
+{
+    GDate year_end;
+    g_date_set_time_t(&year_end, time(nullptr));
+    gnc_gdate_set_year_end(&year_end);
+    time64 time1{time64_from_gdate(&year_end, DayPart::end)};
+    std::istringstream iss{"relative . end-cal-year"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_prev_year_start)
+{
+    GDate prev_year_start;
+    g_date_set_time_t(&prev_year_start, time(nullptr));
+    gnc_gdate_set_prev_year_start(&prev_year_start);
+    time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
+    std::istringstream iss{"relative . start-prev-year"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_stream_in_prev_year_end)
+{
+    GDate prev_year_end;
+    g_date_set_time_t(&prev_year_end, time(nullptr));
+    gnc_gdate_set_prev_year_end(&prev_year_end);
+    time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)};
+    std::istringstream iss{"relative . end-prev-year"};
+    iss >> m_option;
+    EXPECT_EQ(time1, m_option.get_value());
+}

commit e583c84f4e0291077eb04c673afd69b41b1fe0f2
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Nov 17 15:47:42 2019 -0800

    Clean up comments for GncOptionDateValue.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 110d633cb..d8e483943 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -470,14 +470,18 @@ private:
 };
 
 /** Date options
- * A legal date value is a pair of either  and a RelativeDatePeriod, the absolute flag and a time64, or for legacy purposes the absolute flag and a timespec.
- * The original design allowed custom RelativeDatePeriods, but that facility is unused so we'll go with compiled-in enums.
-
+ * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
+ * flag and a time64, or for legacy purposes the absolute flag and a timespec.
+ *
+ * The original design allowed custom RelativeDatePeriods, but that facility is
+ * unused so we'll go with compiled-in enums.
+ */
+/*
 gnc-date-option-show-time? -- option_data[1]
 gnc-date-option-get-subtype -- option_data[0]
 gnc-date-option-value-type m_value
-gnc-date-option-absolute-time 
-gnc-date-option-relative-time
+gnc-date-option-absolute-time m_type == DateTyupe::Absolute
+gnc-date-option-relative-time m_type != DateTyupe::Absolute
  */
 
 enum class DateType

commit aaf6b14c67d158aa3bfd997a1973181ea9a2fb67
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Nov 17 15:47:17 2019 -0800

    Remove commented-out code.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 92ff7caa1..e92ea84ed 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -112,15 +112,6 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
     return scm_guid;
 }
 
-
-/* Not needed now, the default template will do this
-template <> inline SCM
-scm_from_value<QofQuery*>(const QofQuery* value)
-{
-    return SCM_BOOL_F;
-}
-*/
-
 /* Account is actually a typedef for struct account_s and SWIG insists on using
  * the struct name (i.e. account_s) in C++ and the alias (i.e. Account) in
  * C. Oddly the compiler's type resolution also fails to consider them the same
@@ -141,14 +132,6 @@ scm_from_value<GncOptionAccount_sList>(GncOptionAccount_sList value)
 
     return s_list;
 }
-/* default template
-template <>inline SCM
-    scm_from_value<(const std::vector<std::pair<std::string, std::string>>&)
-{
-    return SCM_BOOL_F;
-}
-*/
-
 
 template <typename ValueType> inline ValueType
 scm_to_value(SCM new_value)
@@ -164,16 +147,7 @@ scm_to_value<std::string>(SCM new_value)
     free(strval);
     return retval;
 }
-/*
-template <> inline std::string
-scm_to_value<char*>(SCM new_value)
-{
-    auto strval = scm_to_utf8_stringn(new_value, nullptr);
-    std::string retval{strval};
-    free(strval);
-    return retval;
-}
-*/
+
 template <> inline int
 scm_to_value<int>(SCM new_value)
 {
@@ -363,33 +337,5 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         GncOption_set_value_from_scm(&(db_opt->get()), new_value);
     }
 %}
-/*
-TEST(GncOption, test_string_scm_functions)
-{
-    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
-    auto scm_value = option.get_scm_value();
-    auto str_value = scm_to_utf8_string(scm_value);
-    EXPECT_STREQ("waldo", str_value);
-    g_free(str_value);
-    scm_value = option.get_scm_default_value();
-    str_value = scm_to_utf8_string(scm_value);
-    EXPECT_STREQ("waldo", str_value);
-    g_free(str_value);
-}
-
-TEST(GNCOption, test_budget_scm_functions)
-{
-    auto book = qof_book_new();
-    auto budget = gnc_budget_new(book);
-    GncOption option("foo", "bar", "baz", "Phony Option",
-                     QOF_INSTANCE(budget));
-    auto scm_budget = option.get_scm_value();
-    auto str_value = scm_to_utf8_string(scm_budget);
-    auto guid = guid_to_string(qof_instance_get_guid(budget));
-    EXPECT_STREQ(guid, str_value);
-    g_free(guid);
-    gnc_budget_destroy(budget);
-    qof_book_destroy(book);
-}
 
 */

commit 1b00399b14929961a532fd13a97c1f49712bed07
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Nov 17 12:50:57 2019 -0800

    Remove GncOptionValue<std::Vector<GncGUID>>.
    
    This going to be the Account List option implementation, but a more
    customized approach is better.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index c48d9f23f..110d633cb 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -34,7 +34,6 @@ extern "C"
 }
 #include <gnc-datetime.hpp>
 #include <gnc-numeric.hpp>
-#include <guid.hpp>
 #include <libguile.h>
 #include <string>
 #include <utility>
@@ -533,7 +532,6 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<int64_t>,
                                       GncOptionValue<QofInstance*>,
                                       GncOptionValue<QofQuery*>,
-                                      GncOptionValue<std::vector<GncGUID>>,
                                       GncOptionAccountValue,
                                       GncOptionMultichoiceValue,
                                       GncOptionRangeValue<int>,
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index a22b3bf98..92ff7caa1 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -72,11 +72,6 @@ scm_from_value<QofInstance*>(QofInstance* value)
 {
         return SCM_BOOL_F;
 }
-template <> inline SCM
-    scm_from_value<std::vector<GncGUID>>(std::vector<GncGUID> value)
-{
-        return SCM_BOOL_F;
-}
 
 template <> inline SCM
 scm_from_value<std::string>(std::string value)

commit 5a9c4ccaf88174973cfc848d263a7cba1e2eaa04
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Nov 17 12:48:10 2019 -0800

    Clean out copies of the option.scm comments used as a development guide.

diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index f094a9656..34a1af78d 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -144,20 +144,6 @@ void gnc_register_commodity_option(const GncOptionDBPtr& db,
                                    const char* key, const char* doc_string,
                                    gnc_commodity* value);
 
-/* Complex boolean options are the same as simple boolean options with the
- * addition of two function arguments. (If both of them are #f, you have exactly
- * a simple-boolean-option.) Both functions should expect one boolean argument.
- * When the option's value is changed, the function option-widget-changed-cb
- * will be called with the new option value at the time that the GUI widget
- * representing the option is changed, and the function
- * setter-function-called-cb will be called when the option's setter is called
- * (that is, when the user selects "OK" or "Apply").
-
- * The option-widget-changed-cb is tested for procedurehood before it is called,
- * so it is not validated to be a procedure here. However, since there could be
- * an option-widget-changed-cb but not a setter-function-called-cb, the
- * procedurehood of the setter-function-called-cb is checked here.
- */
 void gnc_register_simple_boolean_option(const GncOptionDBPtr& db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
@@ -173,13 +159,6 @@ void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, std::string value);
 
-/* account-list options use the option-data as a pair; the car is a boolean
- * value, the cdr is a list of account-types. If the boolean is true, the gui
- * should allow the user to select multiple accounts. If the cdr is an empty
- * list, then all account types are shown. Internally, values are always a list
- * of guids. Externally, both guids and account pointers may be used to set the
- * value of the option. The option always returns a list of account pointers.
- */
 void gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
                                              const char* section,
                                              const char* name, const char* key,
@@ -187,21 +166,12 @@ void gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
                                              const GncOptionAccountList& value,
                                              GncOptionAccountTypeList&& allowed);
 
-/* Just like gnc:make-account-list-limited-option except it does not limit the
- * types of accounts that are available to the user.
- */
 void gnc_register_account_list_option(const GncOptionDBPtr& db,
                                       const char* section,
                                       const char* name, const char* key,
                                       const char* doc_string,
                                       const GncOptionAccountList& value);
 
-/* account-sel options use the option-data as a pair; the car is ignored, the
- * cdr is a list of account-types. If the cdr is an empty list, then all account
- * types are shown.  Internally, the value is always a guid.  Externally, both
- * guids and account pointers may be used to set the value of the option. The
- * option always returns the "current" account pointer.
- */
 void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
                                              const char* section,
                                              const char* name, const char* key,
@@ -209,44 +179,21 @@ void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
                                              const GncOptionAccountList& value,
                                              GncOptionAccountTypeList&& allowed);
 
-/* Multichoice options use the option-data as a list of vectors. Each vector
- * contains a permissible value (scheme symbol), a name, and a description
- * string.
- *
- * The multichoice-option with callback function is the same as the usual
- * multichoice options (see above), with the addition of two function
- * arguments. (If both of them are #f, you have exactly a multichoice-option.)
- * Both functions should expect one argument. When the option's value is
- * changed, the function option-widget-changed-cb will be called with the new
- * option value at the time that the GUI widget representing the option is
- * changed, and the function setter-function-called-cb will be called when the
- * option's setter is called (that is, when the user selects "OK" or "Apply").
- */
-
 void gnc_register_multichoice_option(const GncOptionDBPtr& db,
                                      const char* section, const char* name,
                                      const char* key, const char* doc_string,
                                      GncMultiChoiceOptionChoices&& value);
 
-/* List options use the option-data in the same way as multichoice options. List
- * options allow the user to select more than one option.
- */
 void gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, const char* value,
                               GncMultiChoiceOptionChoices&& list);
 
-/* Number range options use the option-data as a list whose elements are:
- * (lower-bound upper-bound step-size). 
-*/
 void gnc_register_number_range_option(const GncOptionDBPtr& db,
                                       const char* section, const char* name,
                                       const char* key, const char* doc_string,
                                       int value, int min, int max, int step);
 
-/* Number plot size options are a convenience wrapper on number range options
- * with fixed min, max, and step.
-*/
 void gnc_register_number_plot_size_option(const GncOptionDBPtr& db,
                                           const char* section, const char* name,
                                           const char* key,
@@ -257,10 +204,6 @@ void gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, QofQuery* value);
 
-/* Color options store rgba values in a list. The option-data is a list, whose
- * first element is the range of possible rgba values and whose second element
- * is a boolean indicating whether to use alpha transparency.
- */
 void gnc_register_color_option(const GncOptionDBPtr& db, const char* section,
                                const char* name, const char* key,
                                const char* doc_string, std::string value);

commit b95ea2c4aa4518ae7babc4cb1a237f107bccf670
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Nov 17 11:15:56 2019 -0800

    Add Account-list options.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 87c4eb683..06f2abf9b 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -29,6 +29,23 @@ extern "C"
 #include "gnc-accounting-period.h"
 }
 
+bool
+GncOptionAccountValue::validate(const GncOptionAccountList& values) const
+{
+    if (values.empty())
+        return false;
+    if (get_ui_type() == GncOptionUIType::ACCOUNT_SEL && values.size() != 1)
+        return false;
+    if (m_allowed.empty())
+        return true;
+    for(auto account : values) {
+        if (std::find(m_allowed.begin(), m_allowed.end(),
+                      xaccAccountGetType(account)) == m_allowed.end())
+            return false;
+    }
+    return true;
+}
+
 static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
 static void
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 61171c4b8..c48d9f23f 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -28,6 +28,7 @@ extern "C"
 {
 #include <config.h>
 #include <qof.h>
+#include <Account.h>
 #include <gnc-budget.h>
 #include <gnc-commodity.h>
 }
@@ -393,6 +394,82 @@ private:
     GncMultiChoiceOptionChoices m_choices;
 };
 
+/** Account options
+ *
+ * Set one or more accounts on which to report, optionally restricted to certain
+ * account types. Many calls to make-account-list-option will pass a get-default
+ * function that retrieves all of the accounts of a list of types.
+ *
+ * Some reports (examples/daily-reports.scm and standard/ account-piechart.scm,
+ * advanced-portfolio.scm, category-barchart.scm, net-charts.scm, and
+ * portfolio.scm) also provide a validator that rejects accounts that don't meet
+ * an account-type criterion.
+ *
+ * There are two types of option, account-list which permits more than one
+ * account selection and account-sel, which doesn't.
+ *
+
+ */
+
+using GncOptionAccountList = std::vector<const Account*>;
+using GncOptionAccountTypeList = std::vector<GNCAccountType>;
+
+class GncOptionAccountValue :
+    public OptionClassifier, public OptionUIItem
+{
+public:
+    GncOptionAccountValue(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          GncOptionUIType ui_type) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {}
+
+    GncOptionAccountValue(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          GncOptionUIType ui_type,
+                          const GncOptionAccountList& value) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(ui_type),
+        m_value{value},
+        m_default_value{std::move(value)}, m_allowed{} {}
+    GncOptionAccountValue(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          GncOptionUIType ui_type,
+                          GncOptionAccountTypeList&& allowed) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(ui_type),
+        m_value{},
+        m_default_value{}, m_allowed{std::move(allowed)} {}
+    GncOptionAccountValue(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          GncOptionUIType ui_type,
+                          const GncOptionAccountList& value,
+                          GncOptionAccountTypeList&& allowed) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(ui_type),
+        m_value{},
+        m_default_value{}, m_allowed{std::move(allowed)} {
+            if (!validate(value))
+                throw std::invalid_argument("Supplied Value not in allowed set.");
+            m_value = value;
+            m_default_value = std::move(value);
+        }
+
+    const GncOptionAccountList& get_value() const { return m_value; }
+    const GncOptionAccountList& get_default_value() const { return m_default_value; }
+    bool validate (const GncOptionAccountList& values) const;
+    void set_value (const GncOptionAccountList& values) {
+        if (validate(values))
+            //throw!
+            m_value = values;
+    }
+
+private:
+    GncOptionAccountList m_value;
+    GncOptionAccountList m_default_value;
+    GncOptionAccountTypeList m_allowed;
+};
+
 /** Date options
  * A legal date value is a pair of either  and a RelativeDatePeriod, the absolute flag and a time64, or for legacy purposes the absolute flag and a timespec.
  * The original design allowed custom RelativeDatePeriods, but that facility is unused so we'll go with compiled-in enums.
@@ -457,6 +534,7 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<QofInstance*>,
                                       GncOptionValue<QofQuery*>,
                                       GncOptionValue<std::vector<GncGUID>>,
+                                      GncOptionAccountValue,
                                       GncOptionMultichoiceValue,
                                       GncOptionRangeValue<int>,
                                       GncOptionRangeValue<double>,
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index f7fd62e73..8fac00eb8 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -231,7 +231,7 @@ gnc_register_color_option(const GncOptionDBPtr& db, const char* section,
                          const char* doc_string, std::string value)
 {
     GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::FONT};
+            GncOptionUIType::COLOR};
     db->register_option(section, std::move(option));
 }
 
@@ -278,36 +278,80 @@ gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
 }
 
 void
-gnc_register_account_liat_option(const GncOptionDBPtr& db, const char* section,
+gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section,
                                  const char* name, const char* key,
                                  const char* doc_string,
-                                 std::vector<GncGUID> value)
+                                 const GncOptionAccountList& value)
 {
-    GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::ACCOUNT_LIST};
+    GncOption option{GncOptionAccountValue{section, name, key, doc_string,
+                GncOptionUIType::ACCOUNT_LIST, value}};
     db->register_option(section, std::move(option));
 }
 
 void
-gnc_register_acount_list_limited_option(const GncOptionDBPtr& db,
-                                        const char* section, const char* name,
-                                        const char* key, const char* doc_string,
-                                        std::vector<GncGUID> value)
+gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
+                                         const char* section, const char* name,
+                                         const char* key,
+                                         const char* doc_string,
+                                         const GncOptionAccountList& value,
+                                         GncOptionAccountTypeList&& allowed)
 {
-    GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::ACCOUNT_LIST};
-    db->register_option(section, std::move(option));
+    try
+    {
+        GncOption option{GncOptionAccountValue{section, name, key, doc_string,
+                    GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}};
+        db->register_option(section, std::move(option));
+    }
+    catch (const std::invalid_argument& err)
+    {
+        std::cerr << "Account List Limited Option, value failed validation, option not registerd.\n";
+    }
+}
+
+using AccountPair = std::pair<GncOptionAccountList&,
+                              const GncOptionAccountTypeList&>;
+static void
+find_children(Account* account, void* data)
+{
+    auto datapair =
+        (AccountPair*)data;
+    GncOptionAccountList& list = datapair->first;
+    const GncOptionAccountTypeList& types = datapair->second;
+    if (std::find(types.begin(), types.end(),
+                  xaccAccountGetType(account)) != types.end())
+        list.push_back(account);
+}
+
+GncOptionAccountList
+gnc_account_list_from_types(QofBook *book,
+                            const GncOptionAccountTypeList& types)
+{
+    GncOptionAccountList list;
+    AccountPair funcdata{list, types};
+    Account* base_acct = gnc_book_get_root_account(book);
+    gnc_account_foreach_descendant(base_acct, (AccountCb)find_children,
+                                   &funcdata);
+    return list;
 }
 
+
 void
 gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
                                         const char* section, const char* name,
                                         const char* key, const char* doc_string,
-                                        std::vector<GncGUID> value)
+                                        const GncOptionAccountList& value,
+                                        GncOptionAccountTypeList&& allowed)
 {
-    GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::ACCOUNT_SEL};
+    try
+    {
+        GncOption option{GncOptionAccountValue{section, name, key, doc_string,
+                    GncOptionUIType::ACCOUNT_SEL, value, std::move(allowed)}};
     db->register_option(section, std::move(option));
+    }
+    catch (const std::invalid_argument& err)
+    {
+        std::cerr <<"Account Sel Limited Option, value failed validation, option not registerd.\n";
+    }
 }
 
 void
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 72936f23f..f094a9656 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -93,6 +93,26 @@ private:
     std::function<void(GncOptionUIItem*)> m_set_ui_value;
 };
 
+/**
+ * Extract a list of accounts in the book having one of the GNCAccountTypes in
+ * types.
+ *
+ * Note that in Scheme it's important to use this function and not to create a
+ * list of accounts using gnc-get-descendants-sorted because the latter method
+ * produces a SWIGTYPE for the accounts that's incompatible with the SWIGTYPE
+ * used in this module.
+ *
+ * @param book The book whose accounts to search
+ * @param types A std::vector of GNCAccountType containing the Account types to
+ *             include in ther result
+ * @return A std::vector<const Account*> of all accounts in the book having the
+ *         Account types in the types parameter.
+ */
+GncOptionAccountList
+gnc_account_list_from_types(QofBook *book,
+                            const GncOptionAccountTypeList& types);
+
+
 using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
 /**
  * Create an empty option database.
@@ -160,20 +180,21 @@ void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
  * of guids. Externally, both guids and account pointers may be used to set the
  * value of the option. The option always returns a list of account pointers.
  */
-void gnc_register_acount_list_limited_option(const GncOptionDBPtr& db,
+void gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
                                              const char* section,
                                              const char* name, const char* key,
                                              const char* doc_string,
-                                             std::vector<GncGUID> value);
+                                             const GncOptionAccountList& value,
+                                             GncOptionAccountTypeList&& allowed);
 
 /* Just like gnc:make-account-list-limited-option except it does not limit the
  * types of accounts that are available to the user.
  */
-void gnc_register_account_liat_option(const GncOptionDBPtr& db,
+void gnc_register_account_list_option(const GncOptionDBPtr& db,
                                       const char* section,
                                       const char* name, const char* key,
                                       const char* doc_string,
-                                      std::vector<GncGUID> value);
+                                      const GncOptionAccountList& value);
 
 /* account-sel options use the option-data as a pair; the car is ignored, the
  * cdr is a list of account-types. If the cdr is an empty list, then all account
@@ -185,7 +206,8 @@ void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
                                              const char* section,
                                              const char* name, const char* key,
                                              const char* doc_string,
-                                             std::vector<GncGUID> value);
+                                             const GncOptionAccountList& value,
+                                             GncOptionAccountTypeList&& allowed);
 
 /* Multichoice options use the option-data as a list of vectors. Each vector
  * contains a permissible value (scheme symbol), a name, and a description
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index a9e872916..a22b3bf98 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -57,10 +57,27 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
   */
 %inline %{
 template <typename ValueType> inline SCM
-scm_from_value(ValueType value)
-{
+    scm_from_value(ValueType value);
+/*{
     return SCM_BOOL_F;
+    }*/
+template <> inline SCM
+scm_from_value<QofQuery*>(QofQuery* value)
+{
+        return SCM_BOOL_F;
+}
+
+template <> inline SCM
+scm_from_value<QofInstance*>(QofInstance* value)
+{
+        return SCM_BOOL_F;
 }
+template <> inline SCM
+    scm_from_value<std::vector<GncGUID>>(std::vector<GncGUID> value)
+{
+        return SCM_BOOL_F;
+}
+
 template <> inline SCM
 scm_from_value<std::string>(std::string value)
 {
@@ -103,24 +120,30 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
 
 /* Not needed now, the default template will do this
 template <> inline SCM
-scm_from_value<QofQuer*>(const QofQuery* value)
+scm_from_value<QofQuery*>(const QofQuery* value)
 {
     return SCM_BOOL_F;
 }
 */
 
+/* Account is actually a typedef for struct account_s and SWIG insists on using
+ * the struct name (i.e. account_s) in C++ and the alias (i.e. Account) in
+ * C. Oddly the compiler's type resolution also fails to consider them the same
+ * so we have to use the struct name here to get the template to resolve
+ * correctly.
+ */
+using GncOptionAccount_sList = std::vector<const account_s*>;
+
 template <>inline SCM
-scm_from_value<const std::vector<GncGUID>&>(const std::vector<GncGUID>& value)
+scm_from_value<GncOptionAccount_sList>(GncOptionAccount_sList value)
 {
-    SCM s_list;
-    for (auto guid : value)
+    SCM s_list = SCM_EOL;
+    for (auto acct : value)
     {
-        auto guid_s = guid_to_string(qof_instance_get_guid(&guid));
-        auto scm_guid = scm_from_utf8_string(guid_s);
-        auto scm_guid_list1 = scm_list_1(scm_guid);
-        s_list = scm_append(scm_list_2(s_list, scm_guid_list1));
-        g_free(guid_s);
+        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
+        s_list = scm_append(scm_list_2(s_list, elem));
     }
+
     return s_list;
 }
 /* default template
@@ -131,6 +154,7 @@ template <>inline SCM
 }
 */
 
+
 template <typename ValueType> inline ValueType
 scm_to_value(SCM new_value)
 {
@@ -167,8 +191,21 @@ scm_to_value<int64_t>(SCM new_value)
     return scm_to_int64(new_value);
 }
 
+QofBook*
+qof_book_new()
+{
+    return static_cast<QofBook*>(g_object_new(QOF_TYPE_BOOK, nullptr));
+}
+
+void
+qof_book_destroy(QofBook* book)
+{
+    g_object_unref(book);
+}
 
+using Account = struct account_s;
 %}
+
 %ignore OptionClassifier;
 %ignore OptionUIItem;
 %nodefaultctor GncOption;
@@ -197,7 +234,77 @@ scm_to_value<int64_t>(SCM new_value)
     $1 = &choices;
  }
 
+%typemap(in) GncOptionAccountTypeList& (GncOptionAccountTypeList types)
+{
+    auto len = scm_to_size_t(scm_length($input));
+    for (std::size_t i = 0; i < len; ++i)
+    {
+        SCM s_type = scm_list_ref($input, scm_from_size_t(i));
+        GNCAccountType type = (GNCAccountType)scm_to_int(s_type);
+        types.push_back(type);
+    }
+    $1 = &types;
+}
+
+%typemap(in) GncOptionAccountTypeList&& (GncOptionAccountTypeList types)
+{
+    auto len = scm_to_size_t(scm_length($input));
+    for (std::size_t i = 0; i < len; ++i)
+    {
+        SCM s_type = scm_list_ref($input, scm_from_size_t(i));
+        GNCAccountType type = (GNCAccountType)scm_to_int(s_type);
+        types.push_back(type);
+    }
+    $1 = &types;
+}
+
+%typemap(in) GncOptionAccountList
+{
+    auto len = scm_to_size_t(scm_length($input));
+    for (std::size_t i = 0; i < len; ++i)
+    {
+        SCM s_account = scm_list_ref($input, scm_from_size_t(i));
+        Account* acct = (Account*)SWIG_MustGetPtr(s_account,
+                                                  SWIGTYPE_p_account_s, 1, 0);
+        $1.push_back(acct);
+    }
+}
+
+%typemap(in) GncOptionAccountList& (GncOptionAccountList acclist)
+{
+    auto len = scm_to_size_t(scm_length($input));
+    for (std::size_t i = 0; i < len; ++i)
+    {
+        SCM s_account = scm_list_ref($input, scm_from_size_t(i));
+        Account* acct = (Account*)SWIG_MustGetPtr(s_account,
+                                                  SWIGTYPE_p_account_s, 1, 0);
+        acclist.push_back(acct);
+    }
+    $1 = &acclist;
+}
+
+%typemap(out) GncOptionAccountList
+{
+    $result = SCM_EOL;
+    for (auto acct : $1)
+    {
+        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
+        $result = scm_append(scm_list_2($result, elem));
+    }
+}
+
+%typemap(out) GncOptionAccountList&
+{
+    $result = SCM_EOL;
+    for (auto acct : *$1)
+    {
+        SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0));
+        $result = scm_append(scm_list_2($result, elem));
+    }
+}
+
 wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
+
 %include "gnc-option.hpp"
 %include "gnc-optiondb.hpp"
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 4a1345c8a..7491b4333 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -246,6 +246,149 @@ TEST_F(GncRangeOption, test_setter)
     EXPECT_EQ(1.5, m_doubleoption.get_default_value());
 }
 
+using AccountPair = std::pair<GncOptionAccountList&,
+                              const GncOptionAccountTypeList&>;
+static void
+find_children(Account* account, void* data)
+{
+    auto datapair =
+        (AccountPair*)data;
+    GncOptionAccountList& list = datapair->first;
+    const GncOptionAccountTypeList& types = datapair->second;
+    if (std::find(types.begin(), types.end(),
+                  xaccAccountGetType(account)) != types.end())
+        list.push_back(account);
+}
+
+class GncOptionAccountTest : public ::testing::Test
+{
+protected:
+    GncOptionAccountTest() :
+        m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)}
+    {
+        auto create_account = [this](Account* parent, GNCAccountType type,
+                                       const char* name)->Account* {
+            auto account = xaccMallocAccount(this->m_book);
+            xaccAccountBeginEdit(account);
+            xaccAccountSetType(account, type);
+            xaccAccountSetName(account, name);
+            xaccAccountBeginEdit(parent);
+            gnc_account_append_child(parent, account);
+            xaccAccountCommitEdit(parent);
+            xaccAccountCommitEdit(account);
+            return account;
+        };
+        auto assets = create_account(m_root, ACCT_TYPE_ASSET, "Assets");
+        auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, "Liabilities");
+        auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, "Expenses");
+        create_account(assets, ACCT_TYPE_BANK, "Bank");
+        auto broker = create_account(assets, ACCT_TYPE_ASSET, "Broker");
+        auto stocks = create_account(broker, ACCT_TYPE_STOCK, "Stocks");
+        create_account(stocks, ACCT_TYPE_STOCK, "AAPL");
+        create_account(stocks, ACCT_TYPE_STOCK, "MSFT");
+        create_account(stocks, ACCT_TYPE_STOCK, "HPE");
+        create_account(broker, ACCT_TYPE_BANK, "Cash Management");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Food");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Gas");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Rent");
+   }
+    ~GncOptionAccountTest()
+    {
+        xaccAccountBeginEdit(m_root);
+        xaccAccountDestroy(m_root); //It does the commit
+        qof_book_destroy(m_book);
+    }
+    GncOptionAccountList list_of_types(const GncOptionAccountTypeList& types)
+    {
+        GncOptionAccountList list;
+        AccountPair funcdata{list, types};
+        gnc_account_foreach_descendant(m_root, (AccountCb)find_children,
+                                       &funcdata);
+        return list;
+    }
+
+    QofBook* m_book;
+    Account* m_root;
+};
+
+TEST_F(GncOptionAccountTest, test_test_constructor_and_destructor)
+{
+    EXPECT_TRUE(m_book != NULL);
+    EXPECT_TRUE(QOF_IS_BOOK(m_book));
+    EXPECT_TRUE(m_root != NULL);
+    EXPECT_TRUE(GNC_IS_ACCOUNT(m_root));
+    GncOptionAccountList list{list_of_types({ACCT_TYPE_BANK})};
+    EXPECT_EQ(2, list.size());
+    list = list_of_types({ACCT_TYPE_ASSET, ACCT_TYPE_STOCK});
+    EXPECT_EQ(6, list.size());
+}
+
+TEST_F(GncOptionAccountTest, test_option_no_value_constructor)
+{
+    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
+            GncOptionUIType::ACCOUNT_LIST};
+    EXPECT_TRUE(option.get_value().empty());
+    EXPECT_TRUE(option.get_default_value().empty());
+}
+
+TEST_F(GncOptionAccountTest, test_option_value_constructor)
+{
+    GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
+    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
+            GncOptionUIType::ACCOUNT_LIST, acclist};
+    EXPECT_EQ(2, option.get_value().size());
+    EXPECT_EQ(2, option.get_default_value().size());
+    EXPECT_EQ(acclist[0], option.get_value()[0]);
+}
+
+TEST_F(GncOptionAccountTest, test_option_no_value_limited_constructor)
+{
+    GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})};
+    GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
+    GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
+            GncOptionUIType::ACCOUNT_LIST, {ACCT_TYPE_BANK}};
+    EXPECT_TRUE(option.get_value().empty());
+    EXPECT_TRUE(option.get_default_value().empty());
+    EXPECT_EQ(true, option.validate(acclistgood));
+    EXPECT_EQ(false, option.validate(acclistbad));
+}
+
+TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
+{
+    GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})};
+    GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
+    EXPECT_THROW({
+            GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
+                                         GncOptionUIType::ACCOUNT_LIST,
+                                         acclistbad, {ACCT_TYPE_BANK});
+        }, std::invalid_argument);
+
+    EXPECT_THROW({
+            GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
+                                         GncOptionUIType::ACCOUNT_SEL,
+                                         acclistgood, {ACCT_TYPE_BANK});
+        }, std::invalid_argument);
+
+    EXPECT_NO_THROW({
+            GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
+                                         GncOptionUIType::ACCOUNT_LIST,
+                                         acclistgood, {ACCT_TYPE_BANK});
+        });
+
+    EXPECT_NO_THROW({
+            GncOptionAccountList accsel{acclistgood[0]};
+            GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option",
+                                         GncOptionUIType::ACCOUNT_LIST,
+                                         accsel, {ACCT_TYPE_BANK});
+        });
+    GncOptionAccountValue option {"foo", "bar", "baz", "Bogus Option",
+            GncOptionUIType::ACCOUNT_LIST, acclistgood, {ACCT_TYPE_BANK}};
+    EXPECT_FALSE(option.get_value().empty());
+    EXPECT_FALSE(option.get_default_value().empty());
+    EXPECT_EQ(true, option.validate(acclistgood));
+    EXPECT_EQ(false, option.validate(acclistbad));
+}
+
 
 class GncOptionMultichoiceTest : public ::testing::Test
 {
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 447fcda45..fd0b709f8 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -69,6 +69,100 @@ TEST_F(GncOptionDBTest, test_register_string_option)
     EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
 }
 
+/* Note: The following test-fixture code is also present in slightly different
+ * form in gtest-gnc-option.cpp.
+ */
+
+
+struct AccountTestBook
+{
+    AccountTestBook() :
+        m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)}
+    {
+        auto create_account = [this](Account* parent, GNCAccountType type,
+                                       const char* name)->Account* {
+            auto account = xaccMallocAccount(this->m_book);
+            xaccAccountBeginEdit(account);
+            xaccAccountSetType(account, type);
+            xaccAccountSetName(account, name);
+            xaccAccountBeginEdit(parent);
+            gnc_account_append_child(parent, account);
+            xaccAccountCommitEdit(parent);
+            xaccAccountCommitEdit(account);
+            return account;
+        };
+        auto assets = create_account(m_root, ACCT_TYPE_ASSET, "Assets");
+        auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, "Liabilities");
+        auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, "Expenses");
+        create_account(assets, ACCT_TYPE_BANK, "Bank");
+        auto broker = create_account(assets, ACCT_TYPE_ASSET, "Broker");
+        auto stocks = create_account(broker, ACCT_TYPE_STOCK, "Stocks");
+        create_account(stocks, ACCT_TYPE_STOCK, "AAPL");
+        create_account(stocks, ACCT_TYPE_STOCK, "MSFT");
+        create_account(stocks, ACCT_TYPE_STOCK, "HPE");
+        create_account(broker, ACCT_TYPE_BANK, "Cash Management");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Food");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Gas");
+        create_account(expenses, ACCT_TYPE_EXPENSE, "Rent");
+   }
+    ~AccountTestBook()
+    {
+        xaccAccountBeginEdit(m_root);
+        xaccAccountDestroy(m_root); //It does the commit
+        qof_book_destroy(m_book);
+    }
+
+    QofBook* m_book;
+    Account* m_root;
+};
+
+TEST_F(GncOptionDBTest, test_register_account_list_option)
+{
+    AccountTestBook book;
+    auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
+    gnc_register_account_list_option(m_db, "foo", "bar", "baz", "Phony Option",
+                                    acclist);
+    EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
+}
+
+TEST_F(GncOptionDBTest, test_register_account_list_limited_option)
+{
+    AccountTestBook book;
+    auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
+    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
+                                             "Phony Option", acclist,
+                                             {ACCT_TYPE_STOCK});
+    EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(3));
+}
+
+TEST_F(GncOptionDBTest, test_register_account_sel_limited_option)
+{
+    AccountTestBook book;
+    auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
+    GncOptionAccountList accsel{acclist[2]};
+    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
+                                             "Phony Option", accsel,
+                                             {ACCT_TYPE_STOCK});
+    EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
+    EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().at(0));
+}
+
+TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct)
+{
+    AccountTestBook book;
+    auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})};
+    GncOptionAccountList accsel{acclist[2]};
+    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option",
+                                     accsel, {ACCT_TYPE_BANK});
+    EXPECT_FALSE(m_db->find_option("foo", "bar"));
+    gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz",
+                                             "Phony Option", acclist,
+                                             {ACCT_TYPE_BANK});
+    EXPECT_FALSE(m_db->find_option("foo", "bar"));
+}
+
 TEST_F(GncOptionDBTest, test_register_multichoice_option)
 {
     GncMultiChoiceOptionChoices choices{
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index c27f21a74..dde592513 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -35,6 +35,7 @@
   (test-runner-factory gnc:test-runner)
   (test-begin "test-gnc-optiondb-scheme")
   (test-gnc-make-text-option)
+  (test-gnc-make-account-list-options)
   (test-gnc-make-multichoice-option)
   (test-gnc-make-list-option)
   (test-gnc-make-date-option)
@@ -52,6 +53,86 @@
     (test-equal "pepper" (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-make-string-option"))
 
+(define (test-gnc-make-account-list-options)
+  (define (create-account book parent type name)
+    (let ((account (xaccMallocAccount book)))
+      (xaccAccountBeginEdit account)
+      (xaccAccountSetType account type)
+      (xaccAccountSetName account name)
+      (xaccAccountBeginEdit parent)
+      (gnc-account-append-child parent account)
+      (xaccAccountCommitEdit parent)
+      (xaccAccountCommitEdit account)
+      account))
+
+  (define (make-account-tree book root)
+    (let* ((assets (create-account book root ACCT-TYPE-ASSET "Assets"))
+           (liabilities  (create-account book root ACCT-TYPE-LIABILITY "Liabilities"))
+           (equity  (create-account book root ACCT-TYPE-EQUITY "Equity"))
+           (expenses  (create-account book root ACCT-TYPE-EXPENSE "Expenses"))
+           (equity  (create-account book root ACCT-TYPE-INCOME "Income"))
+           (broker  (create-account book assets ACCT-TYPE-EQUITY "broker"))
+           (stocks  (create-account book broker ACCT-TYPE-STOCK "Stocks")))
+      (create-account book assets ACCT-TYPE-BANK "Bank")
+      (create-account book stocks ACCT-TYPE-STOCK "AAPL")
+      (create-account book stocks ACCT-TYPE-STOCK "MSFT")
+      (create-account book stocks ACCT-TYPE-STOCK "HPE")
+      (create-account book broker ACCT-TYPE-BANK "Cash Management")
+      (create-account book expenses ACCT-TYPE-EXPENSE "Food")
+      (create-account book expenses ACCT-TYPE-EXPENSE "Gas")
+      (create-account book expenses ACCT-TYPE-EXPENSE "Rent")))
+
+  (define (cleanup book root)
+    (xaccAccountBeginEdit root)
+    (xaccAccountDestroy root)
+    (qof-book-destroy book))
+
+  (define (test-make-account-list-option book)
+    (test-group "test-make-account-list-option"
+    (let ((optiondb (gnc-option-db-new))
+          (acctlist (gnc-account-list-from-types book
+                               (list ACCT-TYPE-STOCK))))
+      (gnc-register-account-list-option optiondb "foo" "bar" "baz"
+                                        "Phony Option" acctlist)
+      (let ((acct-list (gnc-option-value optiondb "foo" "bar")))
+        (test-equal (length acctlist) (length acct-list))
+        (test-equal (car acctlist) (car acct-list))) )))
+
+  (define (test-make-account-list-limited-option book)
+    (test-group "test-make-account-list-option"
+    (let ((optiondb (gnc-option-db-new))
+          (acctlist (gnc-account-list-from-types book
+                               (list ACCT-TYPE-STOCK))))
+      (gnc-register-account-list-limited-option optiondb "foo" "bar" "baz"
+                                        "Phony Option" acctlist (list ACCT-TYPE-STOCK))
+      (let ((acct-list (gnc-option-value optiondb "foo" "bar")))
+        (test-equal (length acctlist) (length acct-list))
+        (test-equal (cadr acctlist) (cadr acct-list)))
+      (gnc-register-account-list-limited-option optiondb "waldo" "pepper" "baz"
+                                        "Phony Option" acctlist (list ACCT-TYPE-BANK))
+      (let ((acct-list (gnc-option-value optiondb "waldo" "pepper")))
+        (test-equal #f (length acct-list))))))
+
+  (define (test-make-account-sel-limited-option book)
+    (test-group "test-make-account-list-option"
+    (let ((optiondb (gnc-option-db-new))
+          (acctlist (gnc-account-list-from-types book
+                               (list ACCT-TYPE-STOCK))))
+      (gnc-register-account-sel-limited-option optiondb "salt" "pork" "baz"
+                                        "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK))
+      (let ((acct (gnc-option-value optiondb "salt" "pork")))
+        (test-equal (list (cadr acctlist)) acct)))))
+
+  (let* ((book (qof-book-new))
+         (root-account (gnc-account-create-root book)))
+    (test-group-with-cleanup "test-gnc-make-account-list-options"
+                             (make-account-tree book root-account)
+                             (test-make-account-list-option book)
+                             (test-make-account-list-limited-option book)
+                             (test-make-account-sel-limited-option book)
+                             (cleanup book root-account))))
+
+
 (define (test-gnc-make-multichoice-option)
 
   (define (keylist->vectorlist keylist)

commit 7183e7c43ab47d0d334d6433325b38eff9c1fa9f
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Nov 10 15:34:21 2019 -0800

    Fix begin/end of quarter calculations.
    
    Set the period to the calendar year if it's not set in prefs.
    Handle correctly the FY start month being after the current month.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 278ca112c..87c4eb683 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -81,6 +81,13 @@ GncOptionDateValue::get_value() const
     struct tm now{static_cast<tm>(GncDateTime())};
     struct tm period{static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()))};
 
+    if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday)
+    {
+        //No set accounting period, use the calendar year
+        period.tm_mon = 0;
+        period.tm_mday = 0;
+    }
+
     if (m_period == RelativeDatePeriod::CAL_YEAR ||
         m_period == RelativeDatePeriod::PREV_YEAR)
     {
@@ -91,7 +98,9 @@ GncOptionDateValue::get_value() const
     else if (m_period == RelativeDatePeriod::PREV_QUARTER ||
              m_period == RelativeDatePeriod::CURRENT_QUARTER)
     {
-        now.tm_mon = now.tm_mon - (now.tm_mon - period.tm_mon) % 3;
+        auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon :
+                       period.tm_mon - now.tm_mon) % 3;
+        now.tm_mon = now.tm_mon - offset;
         if (m_period == RelativeDatePeriod::PREV_QUARTER)
             now.tm_mon -= 3;
         if (m_type == DateType::ENDING)

commit 399573a89ddf508ee86214e480d974ce11043461
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 8 16:31:36 2019 -0800

    Fix multichoice test construction.

diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index c50561290..4a1345c8a 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -251,7 +251,7 @@ class GncOptionMultichoiceTest : public ::testing::Test
 {
 protected:
     GncOptionMultichoiceTest() :
-        m_option{"foo", "bar", "baz", "Phony Option",
+        m_option{"foo", "bar", "baz", "Phony Option", "plugh",
             {
                 {"plugh", "xyzzy", "thud"},
                 {"waldo", "pepper", "salt"},

commit 12c5b9443000aafcd21214175762d04a49365ce5
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Nov 2 11:06:49 2019 -0700

    New scheme function gnc_option_default_value.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 2971ec9d4..a9e872916 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -240,6 +240,15 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return GncOption_get_scm_value(&(db_opt->get()));
     }
 
+    SCM gnc_option_default_value(const GncOptionDBPtr& optiondb,
+                                 const char* section, const char* name)
+    {
+        auto db_opt = optiondb->find_option(section, name);
+        if (!db_opt)
+            return SCM_BOOL_F;
+        return GncOption_get_scm_default_value(&(db_opt->get()));
+    }
+
     void gnc_set_option(const GncOptionDBPtr& optiondb, const char* section,
                         const char* name, SCM new_value)
     {
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 78ab8b94d..c27f21a74 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -77,8 +77,10 @@
          (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz"
                                                      "Phony Option" multichoice)))
 
+    (test-equal "plugh" (gnc-option-value option-db "foo" "bar"))
     (gnc-set-option option-db "foo" "bar" "corge")
-    (test-equal "corge" (gnc-option-value option-db "foo" "bar")))
+    (test-equal "corge" (gnc-option-value option-db "foo" "bar"))
+    (test-equal "plugh" (gnc-option-default-value option-db "foo" "bar")))
     (test-end "test-gnc-test-multichoice-option"))
 
 (define (test-gnc-make-list-option)
@@ -93,6 +95,7 @@
     (test-equal "AvgBalPlot" (gnc-option-value option-db "foo" "bar"))
     (gnc-set-option option-db "foo" "bar" "GLPlot")
     (test-equal "GLPlot" (gnc-option-value option-db "foo" "bar"))
+    (test-equal "AvgBalPlot" (gnc-option-default-value option-db "foo" "bar")))
   (test-end "test-gnc-test-list-option"))
 
 (define (test-gnc-make-date-option)

commit 3f576671aa6246fc8d9d394400ce9f2852a0f0ba
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Nov 2 11:06:12 2019 -0700

    Adapt GncOptionMultiChoiceValue to support list options.
    
    Main change adds a value field to the constructor to set the value and
    default value.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index b4710719c..61171c4b8 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -311,11 +311,22 @@ class GncOptionMultichoiceValue :
 public:
     GncOptionMultichoiceValue(const char* section, const char* name,
                               const char* key, const char* doc_string,
+                              const char* value,
                               GncMultiChoiceOptionChoices&& choices,
                               GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
         OptionClassifier{section, name, key, doc_string},
         OptionUIItem(ui_type),
-        m_value{}, m_default_value{}, m_choices{std::move(choices)} {}
+        m_value{}, m_default_value{}, m_choices{std::move(choices)} {
+            if (value)
+            {
+                if (auto index = find_key(value);
+                    index != std::numeric_limits<std::size_t>::max())
+                {
+                    m_value = index;
+                    m_default_value = index;
+                }
+            }
+        }
 
     GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default;
     GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default;
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 814dccd9e..f7fd62e73 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -314,21 +314,21 @@ void
 gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string,
-                                GncMultiChoiceOptionChoices&& value)
+                                GncMultiChoiceOptionChoices&& choices)
 {
     GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
-                std::move(value)}};
+                std::get<0>(choices.at(0)).c_str(), std::move(choices)}};
     db->register_option(section, std::move(option));
 }
 
 void
 gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
                          const char* name, const char* key,
-                         const char* doc_string,
-                         GncMultiChoiceOptionChoices&& value)
+                         const char* doc_string, const char* value,
+                         GncMultiChoiceOptionChoices&& list)
 {
     GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
-                std::move(value), GncOptionUIType::LIST}};
+                value,  std::move(list), GncOptionUIType::LIST}};
     db->register_option(section, std::move(option));
 }
 
@@ -362,7 +362,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
                           const char* doc_string, QofQuery* value)
 {
     GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::QUERY};
+            GncOptionUIType::INTERNAL};
     db->register_option(section, std::move(option));
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 95319447f..72936f23f 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -211,8 +211,8 @@ void gnc_register_multichoice_option(const GncOptionDBPtr& db,
  */
 void gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
                               const char* name, const char* key,
-                              const char* doc_string,
-                              GncMultiChoiceOptionChoices&& value);
+                              const char* doc_string, const char* value,
+                              GncMultiChoiceOptionChoices&& list);
 
 /* Number range options use the option-data as a list whose elements are:
  * (lower-bound upper-bound step-size). 
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 09fc1e6d9..78ab8b94d 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -36,6 +36,7 @@
   (test-begin "test-gnc-optiondb-scheme")
   (test-gnc-make-text-option)
   (test-gnc-make-multichoice-option)
+  (test-gnc-make-list-option)
   (test-gnc-make-date-option)
   (test-gnc-make-number-range-option)
   (test-end "test-gnc-optiondb-scheme"))
@@ -80,6 +81,20 @@
     (test-equal "corge" (gnc-option-value option-db "foo" "bar")))
     (test-end "test-gnc-test-multichoice-option"))
 
+(define (test-gnc-make-list-option)
+  (test-begin "test-gnc-test-list-option")
+  (let* ((option-db (gnc-option-db-new))
+         (value-list (list (vector "AvgBalPlot" "Average" "Average Balance")
+                           (vector "GainPlot" "Profit" "Profit (Gain minus Loss)")
+                           (vector "GLPlot" "Gain/Loss" "Gain and Loss")))
+         (list-op (gnc-register-list-option option-db "foo" "bar" "baz"
+                                            "Phony Option" "AvgBalPlot"
+                                            value-list)))
+    (test-equal "AvgBalPlot" (gnc-option-value option-db "foo" "bar"))
+    (gnc-set-option option-db "foo" "bar" "GLPlot")
+    (test-equal "GLPlot" (gnc-option-value option-db "foo" "bar"))
+  (test-end "test-gnc-test-list-option"))
+
 (define (test-gnc-make-date-option)
   (test-begin "test-gnc-test-date-option")
   (let* ((option-db (gnc-option-db-new))

commit 2f2ac99944e8326168ce8e086bfe1d0c6975861a
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 1 16:14:30 2019 -0700

    Replace the direct wrapping of GncOptionDB-set-option-string-foo.
    
    With a simple type-free function gnc_option_set. This mirrors the current
    (gnc:option-set(gnc:lookup-option... and takes care of SCM type conversion.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 463ec762d..b4710719c 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -540,9 +540,9 @@ public:
                 option.make_internal();
             }, m_option);
     }
-    const GncOptionVariant& _get_option() const { return m_option; }
+    GncOptionVariant& _get_option() const { return m_option; }
 private:
-    GncOptionVariant m_option;
+    mutable GncOptionVariant m_option;
 };
 
 #endif //GNC_OPTION_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 844ce542e..2971ec9d4 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -100,6 +100,7 @@ scm_from_value<const QofInstance*>(const QofInstance* value)
     return scm_guid;
 }
 
+
 /* Not needed now, the default template will do this
 template <> inline SCM
 scm_from_value<QofQuer*>(const QofQuery* value)
@@ -129,6 +130,44 @@ template <>inline SCM
     return SCM_BOOL_F;
 }
 */
+
+template <typename ValueType> inline ValueType
+scm_to_value(SCM new_value)
+{
+    return ValueType{};
+}
+
+template <> inline std::string
+scm_to_value<std::string>(SCM new_value)
+{
+    auto strval = scm_to_utf8_stringn(new_value, nullptr);
+    std::string retval{strval};
+    free(strval);
+    return retval;
+}
+/*
+template <> inline std::string
+scm_to_value<char*>(SCM new_value)
+{
+    auto strval = scm_to_utf8_stringn(new_value, nullptr);
+    std::string retval{strval};
+    free(strval);
+    return retval;
+}
+*/
+template <> inline int
+scm_to_value<int>(SCM new_value)
+{
+    return scm_to_int(new_value);
+}
+
+template <> inline int64_t
+scm_to_value<int64_t>(SCM new_value)
+{
+    return scm_to_int64(new_value);
+}
+
+
 %}
 %ignore OptionClassifier;
 %ignore OptionUIItem;
@@ -177,6 +216,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
                 return scm_from_value(static_cast<decltype(value)>(value));
             }, $self->_get_option());
     }
+    void set_value_from_scm(SCM new_value)
+    {
+        std::visit([new_value](auto& option) {
+                option.set_value(scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value));
+            }, $self->_get_option());
+    }
 };
 
 %extend GncOptionDB {
@@ -195,6 +240,17 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         return GncOption_get_scm_value(&(db_opt->get()));
     }
 
+    void gnc_set_option(const GncOptionDBPtr& optiondb, const char* section,
+                        const char* name, SCM new_value)
+    {
+        auto db_opt = optiondb->find_option(section, name);
+        if (!db_opt)
+        {
+//          PWARN("Attempt to write non-existent option %s/%s", section, name);
+            return;
+        }
+        GncOption_set_value_from_scm(&(db_opt->get()), new_value);
+    }
 %}
 /*
 TEST(GncOption, test_string_scm_functions)
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index fc523001f..09fc1e6d9 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -47,7 +47,7 @@
                                                  "Phony Option" "waldo")))
     (test-equal "waldo" (gnc-option-value option-db "foo" "bar"))
 
-    (GncOptionDB-set-option-string (GncOptionDBPtr-get option-db) "foo" "bar" "pepper")
+    (gnc-set-option option-db "foo" "bar" "pepper")
     (test-equal "pepper" (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-make-string-option"))
 
@@ -76,8 +76,7 @@
          (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz"
                                                      "Phony Option" multichoice)))
 
-    (GncOptionDB-set-option-string
-     (GncOptionDBPtr-get option-db) "foo" "bar" "corge")
+    (gnc-set-option option-db "foo" "bar" "corge")
     (test-equal "corge" (gnc-option-value option-db "foo" "bar")))
     (test-end "test-gnc-test-multichoice-option"))
 
@@ -88,7 +87,7 @@
                                                       "baz" "Phony Option"))
          (a-time (gnc-dmy2time64 11 07 2019)))
     (test-equal (current-time) (gnc-option-value option-db "foo" "bar"))
-    (GncOptionDB-set-option-time64 (GncOptionDBPtr-get option-db) "foo" "bar" a-time)
+    (gnc-set-option option-db "foo" "bar" a-time)
     (test-equal a-time (gnc-option-value option-db "foo" "bar"))
     (test-end "test-gnc-test-date-option")))
 
@@ -99,6 +98,6 @@
                                                        "baz" "Phony Option"
                                                        15 5 30 1)))
     (test-equal 15 (gnc-option-value option-db "foo" "bar"))
-    (GncOptionDB-set-option-int (GncOptionDBPtr-get option-db) "foo" "bar" 20)
+    (gnc-set-option option-db "foo" "bar" 20)
     (test-equal 20 (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-number-range-option"))

commit 252ba9b4779b9b6a3c66d2e54368a93b89394ddf
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Nov 1 14:59:49 2019 -0700

    Change GncOptionDB::lookup_option to gnc_option_value().
    
    The old gnc:lookup-option returned the option object so that it could be
    manipulated rather than getting its value; gnc_option_value replicates
    the behavior of (gnc:option-value (gnc:lookup-option)).

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index b382c3d06..844ce542e 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -180,20 +180,22 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 };
 
 %extend GncOptionDB {
-    SCM lookup_option(const char* section, const char* name)
-    {
-        auto db_opt = $self->find_option(section, name);
-        if (!db_opt)
-            return SCM_BOOL_F;
-        return GncOption_get_scm_value(&(db_opt->get()));
-    }
-
     %template(set_option_string) set_option<std::string>;
     %template(set_option_int) set_option<int>;
     %template(set_option_time64) set_option<time64>;
  };
 
+%inline %{
+    SCM gnc_option_value(const GncOptionDBPtr& optiondb, const char* section,
+                         const char* name)
+    {
+        auto db_opt = optiondb->find_option(section, name);
+        if (!db_opt)
+            return SCM_BOOL_F;
+        return GncOption_get_scm_value(&(db_opt->get()));
+    }
 
+%}
 /*
 TEST(GncOption, test_string_scm_functions)
 {
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 683432bfb..fc523001f 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -45,12 +45,10 @@
   (let* ((option-db (gnc-option-db-new))
          (string-opt (gnc-register-string-option option-db "foo" "bar" "baz"
                                                  "Phony Option" "waldo")))
-    (test-equal "waldo" (GncOptionDB-lookup-option
-                         (GncOptionDBPtr-get option-db) "foo" "bar"))
+    (test-equal "waldo" (gnc-option-value option-db "foo" "bar"))
 
     (GncOptionDB-set-option-string (GncOptionDBPtr-get option-db) "foo" "bar" "pepper")
-    (test-equal "pepper" (GncOptionDB-lookup-option
-                          (GncOptionDBPtr-get option-db) "foo" "bar")))
+    (test-equal "pepper" (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-make-string-option"))
 
 (define (test-gnc-make-multichoice-option)
@@ -80,8 +78,7 @@
 
     (GncOptionDB-set-option-string
      (GncOptionDBPtr-get option-db) "foo" "bar" "corge")
-    (test-equal "corge" (GncOptionDB-lookup-option
-                         (GncOptionDBPtr-get option-db) "foo" "bar")))
+    (test-equal "corge" (gnc-option-value option-db "foo" "bar")))
     (test-end "test-gnc-test-multichoice-option"))
 
 (define (test-gnc-make-date-option)
@@ -90,11 +87,9 @@
          (date-opt (gnc-register-date-interval-option option-db "foo" "bar"
                                                       "baz" "Phony Option"))
          (a-time (gnc-dmy2time64 11 07 2019)))
-    (test-equal (current-time) (GncOptionDB-lookup-option
-                                (GncOptionDBPtr-get option-db) "foo" "bar"))
+    (test-equal (current-time) (gnc-option-value option-db "foo" "bar"))
     (GncOptionDB-set-option-time64 (GncOptionDBPtr-get option-db) "foo" "bar" a-time)
-    (test-equal a-time (GncOptionDB-lookup-option
-                        (GncOptionDBPtr-get option-db) "foo" "bar"))
+    (test-equal a-time (gnc-option-value option-db "foo" "bar"))
     (test-end "test-gnc-test-date-option")))
 
 (define (test-gnc-make-number-range-option)
@@ -103,9 +98,7 @@
          (number-opt (gnc-register-number-range-option option-db "foo" "bar"
                                                        "baz" "Phony Option"
                                                        15 5 30 1)))
-    (test-equal 15 (GncOptionDB-lookup-option
-                                (GncOptionDBPtr-get option-db) "foo" "bar"))
+    (test-equal 15 (gnc-option-value option-db "foo" "bar"))
     (GncOptionDB-set-option-int (GncOptionDBPtr-get option-db) "foo" "bar" 20)
-    (test-equal 20 (GncOptionDB-lookup-option
-                        (GncOptionDBPtr-get option-db) "foo" "bar")))
+    (test-equal 20 (gnc-option-value option-db "foo" "bar")))
   (test-end "test-gnc-number-range-option"))

commit ff7b263a5f98cc7e48765a571b86f39ac3360aaf
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 31 18:17:03 2019 -0700

    Fix up and test the int specialization of GncOptionRangeValue.
    
    Not sure yet that a double one is really needed.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 522c2870b..463ec762d 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -250,6 +250,10 @@ private:
     ValueType m_validation_data;
 };
 
+/**
+ * Used for numeric ranges and plot sizes.
+ */
+
 template <typename ValueType>
 class GncOptionRangeValue :
     public OptionClassifier, public OptionUIItem
@@ -263,7 +267,7 @@ public:
         OptionUIItem(GncOptionUIType::NUMBER_RANGE),
         m_value{value >= min && value <= max ? value : min},
         m_default_value{value >= min && value <= max ? value : min},
-        m_min{min}, m_step{step} {}
+        m_min{min}, m_max{max}, m_step{step} {}
 
     GncOptionRangeValue<ValueType>(const GncOptionRangeValue<ValueType>&) = default;
     GncOptionRangeValue<ValueType>(GncOptionRangeValue<ValueType>&&) = default;
@@ -444,7 +448,7 @@ using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                       GncOptionValue<std::vector<GncGUID>>,
                                       GncOptionMultichoiceValue,
                                       GncOptionRangeValue<int>,
-                                      GncOptionRangeValue<GncNumeric>,
+                                      GncOptionRangeValue<double>,
                                       GncOptionValidatedValue<QofInstance*>,
                                       GncOptionDateValue>;
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 59b04aaee..c50561290 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -212,6 +212,41 @@ TEST_F(GncOptionUI, test_set_option_ui_item)
     EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget);
 }
 
+class GncOptionRangeTest : public ::testing::Test
+{
+protected:
+    GncOptionRangeTest() :
+        m_intoption{"foo", "bar", "baz", "Phony Option", 15, 1, 30, 1},
+        m_doubleoption{"waldo", "pepper", "salt", "Phonier Option",
+                1.5, 1.0, 3.0, 0.1} {}
+
+    GncOptionRangeValue<int> m_intoption;
+    GncOptionRangeValue<double> m_doubleoption;
+};
+
+using GncRangeOption = GncOptionRangeTest;
+
+TEST_F(GncRangeOption, test_initialization)
+{
+    EXPECT_EQ(15, m_intoption.get_value());
+    EXPECT_EQ(1.5, m_doubleoption.get_value());
+    EXPECT_EQ(15, m_intoption.get_default_value());
+    EXPECT_EQ(1.5, m_doubleoption.get_default_value());
+}
+
+TEST_F(GncRangeOption, test_setter)
+{
+    EXPECT_THROW({ m_intoption.set_value(45); }, std::invalid_argument);
+    EXPECT_NO_THROW({ m_intoption.set_value(20); });
+    EXPECT_EQ(20, m_intoption.get_value());
+    EXPECT_EQ(15, m_intoption.get_default_value());
+    EXPECT_THROW({ m_doubleoption.set_value(4.5); }, std::invalid_argument);
+    EXPECT_NO_THROW({ m_doubleoption.set_value(2.0); });
+    EXPECT_EQ(2.0, m_doubleoption.get_value());
+    EXPECT_EQ(1.5, m_doubleoption.get_default_value());
+}
+
+
 class GncOptionMultichoiceTest : public ::testing::Test
 {
 protected:
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 5916311b6..683432bfb 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -37,6 +37,7 @@
   (test-gnc-make-text-option)
   (test-gnc-make-multichoice-option)
   (test-gnc-make-date-option)
+  (test-gnc-make-number-range-option)
   (test-end "test-gnc-optiondb-scheme"))
 
 (define (test-gnc-make-text-option)
@@ -88,10 +89,23 @@
   (let* ((option-db (gnc-option-db-new))
          (date-opt (gnc-register-date-interval-option option-db "foo" "bar"
                                                       "baz" "Phony Option"))
-         (a-time (gnc-dmy2time64 2019 07 11)))
+         (a-time (gnc-dmy2time64 11 07 2019)))
     (test-equal (current-time) (GncOptionDB-lookup-option
                                 (GncOptionDBPtr-get option-db) "foo" "bar"))
     (GncOptionDB-set-option-time64 (GncOptionDBPtr-get option-db) "foo" "bar" a-time)
     (test-equal a-time (GncOptionDB-lookup-option
                         (GncOptionDBPtr-get option-db) "foo" "bar"))
     (test-end "test-gnc-test-date-option")))
+
+(define (test-gnc-make-number-range-option)
+  (test-begin "test-gnc-number-range-option")
+  (let* ((option-db (gnc-option-db-new))
+         (number-opt (gnc-register-number-range-option option-db "foo" "bar"
+                                                       "baz" "Phony Option"
+                                                       15 5 30 1)))
+    (test-equal 15 (GncOptionDB-lookup-option
+                                (GncOptionDBPtr-get option-db) "foo" "bar"))
+    (GncOptionDB-set-option-int (GncOptionDBPtr-get option-db) "foo" "bar" 20)
+    (test-equal 20 (GncOptionDB-lookup-option
+                        (GncOptionDBPtr-get option-db) "foo" "bar")))
+  (test-end "test-gnc-number-range-option"))

commit 39b7c9c74da0c903a6a59292c3d99fee416b839c
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 31 18:14:41 2019 -0700

    Convert scm_from_value() to templates for stricter overload resolution.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 33df360cf..b382c3d06 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -47,34 +47,52 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
 %include <std_string.i>
 %import <base-typemaps.i>
 
-
+ /* Implementation Note: Plain overloads failed to compile because
+  *    auto value{option.get_value()};
+  *    return scm_from_value(value);
+  * applied implicit conversions among bool, int, and int64_t and ranked them as
+  * equal to the non-converted types in overload resolution. Template type
+  * resolution is more strict so the overload prefers the exact decltype(value)
+  * to implicit conversion candidates.
+  */
 %inline %{
-inline SCM
-scm_from_value(std::string value)
+template <typename ValueType> inline SCM
+scm_from_value(ValueType value)
+{
+    return SCM_BOOL_F;
+}
+template <> inline SCM
+scm_from_value<std::string>(std::string value)
 {
     return scm_from_utf8_string(value.c_str());
 }
 
-inline SCM
-scm_from_value(bool value)
+template <> inline SCM
+scm_from_value<bool>(bool value)
 {
     return value ? SCM_BOOL_T : SCM_BOOL_F;
 }
 
-inline SCM
-scm_from_value(int64_t value)
+template <> inline SCM
+scm_from_value<int64_t>(int64_t value)
 {
     return scm_from_int64(value);
 }
 
-inline SCM
-scm_from_value(int value)
+template <> inline SCM
+scm_from_value<int>(int value)
 {
     return scm_from_int(value);
 }
 
-inline SCM
-scm_from_value(QofInstance* value)
+template <> inline SCM
+scm_from_value<double>(double value)
+{
+    return scm_from_double(value);
+}
+
+template <> inline SCM
+scm_from_value<const QofInstance*>(const QofInstance* value)
 {
     auto guid = guid_to_string(qof_instance_get_guid(value));
     auto scm_guid = scm_from_utf8_string(guid);
@@ -82,20 +100,16 @@ scm_from_value(QofInstance* value)
     return scm_guid;
 }
 
-inline SCM
-scm_from_value(QofQuery* value)
-{
-    return SCM_BOOL_F;
-}
-
-inline SCM
-scm_from_value(GncNumeric value)
+/* Not needed now, the default template will do this
+template <> inline SCM
+scm_from_value<QofQuer*>(const QofQuery* value)
 {
     return SCM_BOOL_F;
 }
+*/
 
-inline SCM
-scm_from_value(std::vector<GncGUID> value)
+template <>inline SCM
+scm_from_value<const std::vector<GncGUID>&>(const std::vector<GncGUID>& value)
 {
     SCM s_list;
     for (auto guid : value)
@@ -108,12 +122,13 @@ scm_from_value(std::vector<GncGUID> value)
     }
     return s_list;
 }
-
-inline SCM
-    scm_from_value(std::vector<std::pair<std::string, std::string>>)
+/* default template
+template <>inline SCM
+    scm_from_value<(const std::vector<std::pair<std::string, std::string>>&)
 {
     return SCM_BOOL_F;
 }
+*/
 %}
 %ignore OptionClassifier;
 %ignore OptionUIItem;
@@ -152,14 +167,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     {
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_value()};
-                return scm_from_value(value);
+                return scm_from_value(static_cast<decltype(value)>(value));
             }, $self->_get_option());
     }
     SCM get_scm_default_value() const
     {
         return std::visit([](const auto& option)->SCM {
                 auto value{option.get_default_value()};
-                return scm_from_value(value);
+                return scm_from_value(static_cast<decltype(value)>(value));
             }, $self->_get_option());
     }
 };

commit 3dc4bc237730adecd9b375c53c48a5e4958291d6
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Oct 29 16:34:44 2019 -0700

    Implement GncOptionDateValue.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 8543efa20..278ca112c 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -23,5 +23,99 @@
 
 //#include "options.h"
 #include "gnc-option.hpp"
-#include <engine-helpers-guile.h>
+#include <gnc-datetime.hpp>
+extern "C"
+{
+#include "gnc-accounting-period.h"
+}
 
+static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static void
+normalize_month(struct tm& now)
+{
+    if (now.tm_mon < 0)
+    {
+        now.tm_mon += 12;
+        --now.tm_year;
+    }
+    else if (now.tm_mon > 11)
+    {
+        now.tm_mon -= 12;
+        ++now.tm_year;
+    }
+}
+
+static void
+set_day_and_time(struct tm& now, bool starting)
+{
+    if (starting)
+    {
+        now.tm_hour = now.tm_min = now.tm_sec = 0;
+        now.tm_mday = 1;
+    }
+    else
+    {
+        now.tm_min = now.tm_sec = 59;
+        now.tm_hour = 23;
+        now.tm_mday = days_in_month[now.tm_mon];
+        // Check for Februrary in a leap year
+        if (int year = now.tm_year + 1900; now.tm_mon == 1 &&
+            year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+            ++now.tm_mday;
+    }
+};
+
+time64
+GncOptionDateValue::get_value() const
+{
+    if (m_type == DateType::ABSOLUTE)
+        return m_date;
+    if (m_period == RelativeDatePeriod::TODAY)
+        return static_cast<time64>(GncDateTime());
+    if (m_period == RelativeDatePeriod::ACCOUNTING_PERIOD)
+        return m_type == DateType::STARTING ?
+            gnc_accounting_period_fiscal_start() :
+            gnc_accounting_period_fiscal_end();
+
+    struct tm now{static_cast<tm>(GncDateTime())};
+    struct tm period{static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()))};
+
+    if (m_period == RelativeDatePeriod::CAL_YEAR ||
+        m_period == RelativeDatePeriod::PREV_YEAR)
+    {
+        if (m_period == RelativeDatePeriod::PREV_YEAR)
+            --now.tm_year;
+        now.tm_mon = m_type == DateType::STARTING ? 0 : 11;
+    }
+    else if (m_period == RelativeDatePeriod::PREV_QUARTER ||
+             m_period == RelativeDatePeriod::CURRENT_QUARTER)
+    {
+        now.tm_mon = now.tm_mon - (now.tm_mon - period.tm_mon) % 3;
+        if (m_period == RelativeDatePeriod::PREV_QUARTER)
+            now.tm_mon -= 3;
+        if (m_type == DateType::ENDING)
+            now.tm_mon += 2;
+    }
+    else if (m_period == RelativeDatePeriod::PREV_MONTH)
+        --now.tm_mon;
+    normalize_month(now);
+    set_day_and_time(now, m_type == DateType::STARTING);
+    return static_cast<time64>(GncDateTime(now));
+}
+
+void
+GncOptionDateValue::set_value(DateSetterValue value)
+{
+    auto [type, val] = value;
+    m_type = type;
+    if (type == DateType::ABSOLUTE)
+    {
+        m_period = RelativeDatePeriod::TODAY;
+        m_date = static_cast<time64>(val);
+        return;
+    }
+
+    m_period = static_cast<RelativeDatePeriod>(val);
+    m_date = 0;
+}
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index b80fb8718..522c2870b 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -31,6 +31,7 @@ extern "C"
 #include <gnc-budget.h>
 #include <gnc-commodity.h>
 }
+#include <gnc-datetime.hpp>
 #include <gnc-numeric.hpp>
 #include <guid.hpp>
 #include <libguile.h>
@@ -295,7 +296,6 @@ private:
  *
  *
  */
-
 using GncMultiChoiceOptionEntry = std::tuple<const std::string,
                                              const std::string,
                                              const std::string>;
@@ -306,7 +306,7 @@ class GncOptionMultichoiceValue :
 {
 public:
     GncOptionMultichoiceValue(const char* section, const char* name,
-                                   const char* key, const char* doc_string,
+                              const char* key, const char* doc_string,
                               GncMultiChoiceOptionChoices&& choices,
                               GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
         OptionClassifier{section, name, key, doc_string},
@@ -378,16 +378,75 @@ private:
     GncMultiChoiceOptionChoices m_choices;
 };
 
+/** Date options
+ * A legal date value is a pair of either  and a RelativeDatePeriod, the absolute flag and a time64, or for legacy purposes the absolute flag and a timespec.
+ * The original design allowed custom RelativeDatePeriods, but that facility is unused so we'll go with compiled-in enums.
+
+gnc-date-option-show-time? -- option_data[1]
+gnc-date-option-get-subtype -- option_data[0]
+gnc-date-option-value-type m_value
+gnc-date-option-absolute-time 
+gnc-date-option-relative-time
+ */
+
+enum class DateType
+{
+    ABSOLUTE,
+    STARTING,
+    ENDING,
+};
+
+enum class RelativeDatePeriod : int64_t
+{
+    TODAY,
+    THIS_MONTH,
+    PREV_MONTH,
+    CURRENT_QUARTER,
+    PREV_QUARTER,
+    CAL_YEAR,
+    PREV_YEAR,
+    ACCOUNTING_PERIOD
+};
+
+using DateSetterValue = std::pair<DateType, int64_t>;
+class GncOptionDateValue : public OptionClassifier, public OptionUIItem
+{
+public:
+    GncOptionDateValue(const char* section, const char* name,
+                              const char* key, const char* doc_string) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(GncOptionUIType::DATE),
+        m_type{DateType::ABSOLUTE}, m_period{RelativeDatePeriod::TODAY},
+        m_date{static_cast<time64>(GncDateTime())} {}
+        GncOptionDateValue(const GncOptionDateValue&) = default;
+        GncOptionDateValue(GncOptionDateValue&&) = default;
+        GncOptionDateValue& operator=(const GncOptionDateValue&) = default;
+        GncOptionDateValue& operator=(GncOptionDateValue&&) = default;
+    time64 get_value() const;
+    time64 get_default_value() const { return static_cast<time64>(GncDateTime()); }
+    void set_value(DateSetterValue);
+    void set_value(time64 time) {
+        m_type = DateType::ABSOLUTE;
+        m_period = RelativeDatePeriod::TODAY;
+        m_date = time;
+    }
+private:
+    DateType m_type;
+    RelativeDatePeriod m_period;
+    time64 m_date;
+};
+
 using GncOptionVariant = std::variant<GncOptionValue<std::string>,
-                                        GncOptionValue<bool>,
-                                        GncOptionValue<int64_t>,
-                                        GncOptionValue<QofInstance*>,
-                                        GncOptionValue<QofQuery*>,
-                                        GncOptionValue<std::vector<GncGUID>>,
-                                        GncOptionMultichoiceValue,
-                                        GncOptionRangeValue<int>,
-                                        GncOptionRangeValue<GncNumeric>,
-                                        GncOptionValidatedValue<QofInstance*>>;
+                                      GncOptionValue<bool>,
+                                      GncOptionValue<int64_t>,
+                                      GncOptionValue<QofInstance*>,
+                                      GncOptionValue<QofQuery*>,
+                                      GncOptionValue<std::vector<GncGUID>>,
+                                      GncOptionMultichoiceValue,
+                                      GncOptionRangeValue<int>,
+                                      GncOptionRangeValue<GncNumeric>,
+                                      GncOptionValidatedValue<QofInstance*>,
+                                      GncOptionDateValue>;
 
 class GncOption
 {
@@ -408,7 +467,7 @@ public:
         return std::visit([](const auto& option)->ValueType {
                 if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
                     return option.get_value();
-                    return ValueType {};
+                return ValueType {};
             }, m_option);
     }
 
@@ -417,7 +476,7 @@ public:
         return std::visit([](const auto& option)->ValueType {
                 if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
                     return option.get_default_value();
-                    return ValueType {};
+                return ValueType {};
             }, m_option);
 
     }
@@ -426,7 +485,7 @@ public:
     {
         std::visit([value](auto& option) {
                 if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
-                   option.set_value(value);
+                                 option.set_value(value);
             }, m_option);
     }
     const std::string& get_section() const
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 46853a349..814dccd9e 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -453,3 +453,12 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
         }};
     db->register_option(section, std::move(option));
 }
+
+void
+gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section,
+                             const char* name, const char* key,
+                             const char* doc_string)
+{
+    GncOption option{GncOptionDateValue(section, name, key, doc_string)};
+    db->register_option(section, std::move(option));
+}
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 2a9c597a2..95319447f 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -278,5 +278,7 @@ void gnc_register_dateformat_option(const GncOptionDBPtr& db,
                                     const char* key, const char* doc_string,
                                     std::string value);
 
-
+void gnc_register_date_interval_option(const GncOptionDBPtr& db,
+                                       const char* section, const char* name,
+                                       const char* key, const char* doc_string);
 #endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index b7314ac17..33df360cf 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -121,6 +121,13 @@ inline SCM
 %ignore GncOptionMultichoiceValue(GncOptionMultichoiceValue&&);
 %ignore GncOptionMultichoiceValue::operator=(const GncOptionMultichoiceValue&);
 %ignore GncOptionMultichoiceValue::operator=(GncOptionMultichoiceValue&&);
+%ignore GncOptionDateValue(GncOptionDateValue&&);
+%ignore GncOptionDateValue::operator=(const GncOptionDateValue&);
+%ignore GncOptionDateValue::operator=(GncOptionDateValue&&);
+
+%typemap(typecheck, precedence=SWIG_TYPECHECK_INT64) time64 {
+    $1 = scm_is_signed_integer($input, INT64_MAX, INT64_MIN);
+}
 
 %typemap(in) GncMultiChoiceOptionChoices&& (GncMultiChoiceOptionChoices choices)
 {
@@ -168,6 +175,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 
     %template(set_option_string) set_option<std::string>;
     %template(set_option_int) set_option<int>;
+    %template(set_option_time64) set_option<time64>;
  };
 
 
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index d8fe3e53f..dc41f3471 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -43,6 +43,7 @@ set(gtest_gnc_option_INCLUDES
   ${GUILE_INCLUDE_DIRS})
 
 set(gtest_gnc_option_LIBS
+  gncmod-app-utils
   gncmod-engine
   ${GLIB2_LDFLAGS}
   ${GUILE_LDFLAGS}
@@ -109,6 +110,7 @@ if (HAVE_SRFI64)
 
   set(swig_gnc_optiondb_LIBS
     gncmod-engine
+    gncmod-app-utils
     ${GLIB2_LDFLAGS}
     ${GUILE_LDFLAGS}
     )
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index a20994098..59b04aaee 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -23,6 +23,11 @@
 
 #include <gtest/gtest.h>
 #include <gnc-option.hpp>
+extern "C"
+{
+#include <gnc-date.h>
+#include <time.h>
+}
 
 TEST(GncOption, test_string_ctor)
 {
@@ -267,3 +272,164 @@ TEST_F(GncMultichoiceOption, test_permissible_value_stuff)
     EXPECT_EQ(std::numeric_limits<std::size_t>::max(),
               m_option.permissible_value_index("xyzzy"));
 }
+
+class GncOptionDateOptionTest : public ::testing::Test
+{
+protected:
+    GncOptionDateOptionTest() :
+        m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option"}} {}
+
+    GncOptionDateValue m_option;
+};
+
+using GncDateOption = GncOptionDateOptionTest;
+
+static time64
+time64_from_gdate(const GDate* g_date, DayPart when)
+{
+    GncDate date{g_date_get_year(g_date), g_date_get_month(g_date),
+            g_date_get_day(g_date)};
+    GncDateTime time{date, when};
+    return static_cast<time64>(time);
+}
+
+TEST_F(GncDateOption, test_set_and_get_absolute)
+{
+    time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
+    DateSetterValue value1{DateType::ABSOLUTE, time1};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_month_start)
+{
+    GDate month_start;
+    g_date_set_time_t(&month_start, time(nullptr));
+    gnc_gdate_set_month_start(&month_start);
+    time64 time1{time64_from_gdate(&month_start, DayPart::start)};
+    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_month_end)
+{
+    GDate month_end;
+    g_date_set_time_t(&month_end, time(nullptr));
+    gnc_gdate_set_month_end(&month_end);
+    time64 time1{time64_from_gdate(&month_end, DayPart::end)};
+    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_prev_month_start)
+{
+    GDate prev_month_start;
+    g_date_set_time_t(&prev_month_start, time(nullptr));
+    gnc_gdate_set_prev_month_start(&prev_month_start);
+    time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)};
+    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_prev_month_end)
+{
+    GDate prev_month_end;
+    g_date_set_time_t(&prev_month_end, time(nullptr));
+    gnc_gdate_set_prev_month_end(&prev_month_end);
+    time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)};
+    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_quarter_start)
+{
+    GDate quarter_start;
+    g_date_set_time_t(&quarter_start, time(nullptr));
+    gnc_gdate_set_quarter_start(&quarter_start);
+    time64 time1{time64_from_gdate(&quarter_start, DayPart::start)};
+    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_quarter_end)
+{
+    GDate quarter_end;
+    g_date_set_time_t(&quarter_end, time(nullptr));
+    gnc_gdate_set_quarter_end(&quarter_end);
+    time64 time1{time64_from_gdate(&quarter_end, DayPart::end)};
+    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_prev_quarter_start)
+{
+    GDate prev_quarter_start;
+    g_date_set_time_t(&prev_quarter_start, time(nullptr));
+    gnc_gdate_set_prev_quarter_start(&prev_quarter_start);
+    time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)};
+    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_prev_quarter_end)
+{
+    GDate prev_quarter_end;
+    g_date_set_time_t(&prev_quarter_end, time(nullptr));
+    gnc_gdate_set_prev_quarter_end(&prev_quarter_end);
+    time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)};
+    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_year_start)
+{
+    GDate year_start;
+    g_date_set_time_t(&year_start, time(nullptr));
+    gnc_gdate_set_year_start(&year_start);
+    time64 time1{time64_from_gdate(&year_start, DayPart::start)};
+    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_year_end)
+{
+    GDate year_end;
+    g_date_set_time_t(&year_end, time(nullptr));
+    gnc_gdate_set_year_end(&year_end);
+    time64 time1{time64_from_gdate(&year_end, DayPart::end)};
+    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_prev_year_start)
+{
+    GDate prev_year_start;
+    g_date_set_time_t(&prev_year_start, time(nullptr));
+    gnc_gdate_set_prev_year_start(&prev_year_start);
+    time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
+    DateSetterValue value1{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
+TEST_F(GncDateOption, test_set_and_get_prev_year_end)
+{
+    GDate prev_year_end;
+    g_date_set_time_t(&prev_year_end, time(nullptr));
+    gnc_gdate_set_prev_year_end(&prev_year_end);
+    time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)};
+    DateSetterValue value1{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
+    m_option.set_value(value1);
+    EXPECT_EQ(time1, m_option.get_value());
+}
+
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 917e07fc5..447fcda45 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -82,6 +82,14 @@ TEST_F(GncOptionDBTest, test_register_multichoice_option)
     EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str());
 }
 
+TEST_F(GncOptionDBTest, test_register_date_interval_option)
+{
+    gnc_register_date_interval_option(m_db, "foo", "bar", "baz", "Phony Option");
+    auto time{gnc_dmy2time64(11, 7, 2019)};
+    ASSERT_TRUE(m_db->set_option("foo", "bar", time));
+    EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value<time64>());
+}
+
 class GncUIType
 {
 public:
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index d9c8450c8..5916311b6 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -23,10 +23,12 @@
 
 (use-modules (srfi srfi-64))
 (use-modules (tests srfi64-extras))
-
+(use-modules (gnucash gnc-module))
 (eval-when
  (compile load eval expand)
  (load-extension "libswig-gnc-optiondb" "scm_init_sw_gnc_optiondb_module"))
+
+(gnc:module-load "gnucash/engine" 0)
 (use-modules (sw_gnc_optiondb))
 
 (define (run-test)
@@ -34,6 +36,7 @@
   (test-begin "test-gnc-optiondb-scheme")
   (test-gnc-make-text-option)
   (test-gnc-make-multichoice-option)
+  (test-gnc-make-date-option)
   (test-end "test-gnc-optiondb-scheme"))
 
 (define (test-gnc-make-text-option)
@@ -79,5 +82,16 @@
     (test-equal "corge" (GncOptionDB-lookup-option
                          (GncOptionDBPtr-get option-db) "foo" "bar")))
     (test-end "test-gnc-test-multichoice-option"))
-                         (GncOptionDBPtr-get option-db) "foo" "bar"))
-    (test-end "test-gnc-test-multichoice-option")))
+
+(define (test-gnc-make-date-option)
+  (test-begin "test-gnc-test-date-option")
+  (let* ((option-db (gnc-option-db-new))
+         (date-opt (gnc-register-date-interval-option option-db "foo" "bar"
+                                                      "baz" "Phony Option"))
+         (a-time (gnc-dmy2time64 2019 07 11)))
+    (test-equal (current-time) (GncOptionDB-lookup-option
+                                (GncOptionDBPtr-get option-db) "foo" "bar"))
+    (GncOptionDB-set-option-time64 (GncOptionDBPtr-get option-db) "foo" "bar" a-time)
+    (test-equal a-time (GncOptionDB-lookup-option
+                        (GncOptionDBPtr-get option-db) "foo" "bar"))
+    (test-end "test-gnc-test-date-option")))

commit 435667e8fe16c9a66b1e765ac417135415a6bab3
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Oct 20 15:13:33 2019 -0700

    Implement GncOptionMultichoiceValue
    
    Replaces GncOptionValue<GncMultiChoiceOptionChoices> because having the
    vector as the value obviously wouldn't work and besides it needs
    additional functions.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 59cda4bb5..b80fb8718 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -125,9 +125,6 @@ struct OptionClassifier
     std::string m_doc_string;
 };
 
-using GncMultiChoiceOptionEntry = std::pair<std::string, std::string>;
-using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
-
 class GncOptionUIItem;
 
 /**
@@ -289,16 +286,109 @@ private:
     ValueType m_step;
 };
 
+/** MultiChoice options have a vector of valid options
+ * (GncMultiChoiceOptionChoices) and validate the selection as being one of
+ * those values. The value is the index of the selected item in the vector. The
+ * tuple contains three strings, a key, a display
+ * name and a brief description for the tooltip. Both name and description
+ * should be localized at the point of use. 
+ *
+ *
+ */
+
+using GncMultiChoiceOptionEntry = std::tuple<const std::string,
+                                             const std::string,
+                                             const std::string>;
+using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
+
+class GncOptionMultichoiceValue :
+    public OptionClassifier, public OptionUIItem
+{
+public:
+    GncOptionMultichoiceValue(const char* section, const char* name,
+                                   const char* key, const char* doc_string,
+                              GncMultiChoiceOptionChoices&& choices,
+                              GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(ui_type),
+        m_value{}, m_default_value{}, m_choices{std::move(choices)} {}
+
+    GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default;
+    GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default;
+    GncOptionMultichoiceValue& operator=(const GncOptionMultichoiceValue&) = default;
+    GncOptionMultichoiceValue& operator=(GncOptionMultichoiceValue&&) = default;
+
+    const std::string& get_value() const
+    {
+        return std::get<0>(m_choices.at(m_value));
+    }
+    const std::string& get_default_value() const
+    {
+        return std::get<0>(m_choices.at(m_default_value));
+    }
+     bool validate(const std::string& value) const noexcept
+    {
+        auto index = find_key(value);
+        return index != std::numeric_limits<std::size_t>::max();
+
+    }
+    void set_value(const std::string& value)
+    {
+        auto index = find_key(value);
+        if (index != std::numeric_limits<std::size_t>::max())
+            m_value = index;
+        else
+            throw std::invalid_argument("Value not a valid choice.");
+
+    }
+    std::size_t num_permissible_values() const noexcept
+    {
+        return m_choices.size();
+    }
+    std::size_t permissible_value_index(const std::string& key) const noexcept
+    {
+            return find_key(key);
+    }
+    const std::string& permissible_value(std::size_t index) const
+    {
+        return std::get<0>(m_choices.at(index));
+    }
+    const std::string& permissible_value_name(std::size_t index) const
+    {
+        return std::get<1>(m_choices.at(index));
+    }
+    const std::string& permissible_value_description(std::size_t index) const
+    {
+        return std::get<2>(m_choices.at(index));
+    }
+private:
+    std::size_t find_key (const std::string& key) const noexcept
+    {
+        auto iter = std::find_if(m_choices.begin(), m_choices.end(),
+                              [key](auto choice) {
+                                  return std::get<0>(choice) == key; });
+        if (iter != m_choices.end())
+            return iter - m_choices.begin();
+        else
+            return std::numeric_limits<std::size_t>::max();
+
+    }
+    std::size_t m_value;
+    std::size_t m_default_value;
+    GncMultiChoiceOptionChoices m_choices;
+};
+
 using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                         GncOptionValue<bool>,
                                         GncOptionValue<int64_t>,
                                         GncOptionValue<QofInstance*>,
                                         GncOptionValue<QofQuery*>,
                                         GncOptionValue<std::vector<GncGUID>>,
-                                    GncOptionValue<GncMultiChoiceOptionChoices>,
+                                        GncOptionMultichoiceValue,
                                         GncOptionRangeValue<int>,
                                         GncOptionRangeValue<GncNumeric>,
                                         GncOptionValidatedValue<QofInstance*>>;
+
 class GncOption
 {
 public:
@@ -316,9 +406,8 @@ public:
     template <typename ValueType> ValueType get_value() const
     {
         return std::visit([](const auto& option)->ValueType {
-                if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
+                if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
                     return option.get_value();
-                else
                     return ValueType {};
             }, m_option);
     }
@@ -326,9 +415,8 @@ public:
     template <typename ValueType> ValueType get_default_value() const
     {
         return std::visit([](const auto& option)->ValueType {
-                if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
+                if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
                     return option.get_default_value();
-                else
                     return ValueType {};
             }, m_option);
 
@@ -337,7 +425,7 @@ public:
     template <typename ValueType> void set_value(ValueType value)
     {
         std::visit([value](auto& option) {
-                if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
+                if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
                    option.set_value(value);
             }, m_option);
     }
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 9276513ac..46853a349 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -316,8 +316,8 @@ gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section,
                                 const char* doc_string,
                                 GncMultiChoiceOptionChoices&& value)
 {
-    GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::MULTICHOICE};
+    GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
+                std::move(value)}};
     db->register_option(section, std::move(option));
 }
 
@@ -327,8 +327,8 @@ gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
                          const char* doc_string,
                          GncMultiChoiceOptionChoices&& value)
 {
-    GncOption option{section, name, key, doc_string, value,
-            GncOptionUIType::LIST};
+    GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
+                std::move(value), GncOptionUIType::LIST}};
     db->register_option(section, std::move(option));
 }
 
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 157d8b7f8..b7314ac17 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -45,6 +45,7 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
 %}
 
 %include <std_string.i>
+%import <base-typemaps.i>
 
 
 %inline %{
@@ -117,6 +118,23 @@ inline SCM
 %ignore OptionClassifier;
 %ignore OptionUIItem;
 %nodefaultctor GncOption;
+%ignore GncOptionMultichoiceValue(GncOptionMultichoiceValue&&);
+%ignore GncOptionMultichoiceValue::operator=(const GncOptionMultichoiceValue&);
+%ignore GncOptionMultichoiceValue::operator=(GncOptionMultichoiceValue&&);
+
+%typemap(in) GncMultiChoiceOptionChoices&& (GncMultiChoiceOptionChoices choices)
+{
+    auto len = scm_to_size_t(scm_length($input));
+    for (std::size_t i = 0; i < len; ++i)
+    {
+        SCM vec = scm_list_ref($input, scm_from_size_t(i));
+        std::string key{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 0))};
+        std::string name{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 1))};
+        std::string desc{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 2))};
+        choices.push_back({std::move(key), std::move(name), std::move(desc)});
+    }
+    $1 = &choices;
+ }
 
 wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %include "gnc-option.hpp"
@@ -152,6 +170,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
     %template(set_option_int) set_option<int>;
  };
 
+
 /*
 TEST(GncOption, test_string_scm_functions)
 {
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index ec52438f9..a20994098 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -91,6 +91,7 @@ TEST(GNCOption, test_commodity_ctor)
     gnc_commodity_destroy(hpe);
     qof_book_destroy(book);
 }
+
 static GncOption
 make_currency_option (const char* section, const char* name,
                       const char* key, const char* doc_string,
@@ -205,3 +206,64 @@ TEST_F(GncOptionUI, test_set_option_ui_item)
     m_option.set_ui_item(&option_ui_item);
     EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget);
 }
+
+class GncOptionMultichoiceTest : public ::testing::Test
+{
+protected:
+    GncOptionMultichoiceTest() :
+        m_option{"foo", "bar", "baz", "Phony Option",
+            {
+                {"plugh", "xyzzy", "thud"},
+                {"waldo", "pepper", "salt"},
+                {"pork", "sausage", "links"},
+                {"corge", "grault", "garply"}
+            }} {}
+    GncOptionMultichoiceValue m_option;
+};
+
+using GncMultichoiceOption = GncOptionMultichoiceTest;
+
+TEST_F(GncMultichoiceOption, test_option_ui_type)
+{
+    EXPECT_EQ(GncOptionUIType::MULTICHOICE, m_option.get_ui_type());
+}
+
+TEST_F(GncMultichoiceOption, test_validate)
+{
+    EXPECT_TRUE(m_option.validate("waldo"));
+    EXPECT_FALSE(m_option.validate("grault"));
+}
+
+TEST_F(GncMultichoiceOption, test_set_value)
+{
+    EXPECT_NO_THROW({
+            m_option.set_value("pork");
+            EXPECT_STREQ("pork", m_option.get_value().c_str());
+        });
+    EXPECT_THROW({ m_option.set_value("salt"); }, std::invalid_argument);
+    EXPECT_STREQ("pork", m_option.get_value().c_str());
+}
+
+TEST_F(GncMultichoiceOption, test_num_permissible)
+{
+    EXPECT_EQ(4, m_option.num_permissible_values());
+}
+
+TEST_F(GncMultichoiceOption, test_permissible_value_stuff)
+{
+    EXPECT_NO_THROW({
+            EXPECT_EQ(3, m_option.permissible_value_index("corge"));
+            EXPECT_STREQ("waldo", m_option.permissible_value(1).c_str());
+            EXPECT_STREQ("sausage", m_option.permissible_value_name(2).c_str());
+            EXPECT_STREQ("thud",
+                         m_option.permissible_value_description(0).c_str());
+        });
+    EXPECT_THROW({ auto result = m_option.permissible_value(7); },
+                 std::out_of_range);
+    EXPECT_THROW({ auto result = m_option.permissible_value_name(9); },
+        std::out_of_range);
+    EXPECT_THROW({ auto result = m_option.permissible_value_description(4); },
+        std::out_of_range);
+    EXPECT_EQ(std::numeric_limits<std::size_t>::max(),
+              m_option.permissible_value_index("xyzzy"));
+}
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index be6221f60..917e07fc5 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -69,6 +69,19 @@ TEST_F(GncOptionDBTest, test_register_string_option)
     EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
 }
 
+TEST_F(GncOptionDBTest, test_register_multichoice_option)
+{
+    GncMultiChoiceOptionChoices choices{
+        { "plugh", "xyzzy", "thud"},
+        { "waldo", "pepper", "salt"},
+        { "pork", "sausage", "links"},
+        { "corge", "grault", "garply"}};
+    gnc_register_multichoice_option(m_db, "foo", "bar", "baz", "Phony Option",
+                                    std::move(choices));
+    ASSERT_TRUE(m_db->set_option("foo", "bar", std::string{"corge"}));
+    EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str());
+}
+
 class GncUIType
 {
 public:
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index cce651fb0..d9c8450c8 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -33,6 +33,7 @@
   (test-runner-factory gnc:test-runner)
   (test-begin "test-gnc-optiondb-scheme")
   (test-gnc-make-text-option)
+  (test-gnc-make-multichoice-option)
   (test-end "test-gnc-optiondb-scheme"))
 
 (define (test-gnc-make-text-option)
@@ -47,3 +48,36 @@
     (test-equal "pepper" (GncOptionDB-lookup-option
                           (GncOptionDBPtr-get option-db) "foo" "bar")))
   (test-end "test-gnc-make-string-option"))
+
+(define (test-gnc-make-multichoice-option)
+
+  (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 (keylist-get-info keylist key info)
+  (assq-ref (assq-ref keylist key) info))
+
+  (test-begin "test-gnc-test-multichoice-option")
+  (let* ((option-db (gnc-option-db-new))
+         (multilist (list
+                       (list "plugh" (cons 'text "xyzzy") (cons 'tip "thud"))
+                       (list "waldo" (cons 'text "pepper") (cons 'tip "salt"))
+                       (list "pork" (cons 'text "sausage") (cons 'tip "links"))
+                       (list "corge" (cons 'text "grault") (cons 'tip "garply"))))
+         (multichoice (keylist->vectorlist multilist))
+         (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz"
+                                                     "Phony Option" multichoice)))
+
+    (GncOptionDB-set-option-string
+     (GncOptionDBPtr-get option-db) "foo" "bar" "corge")
+    (test-equal "corge" (GncOptionDB-lookup-option
+                         (GncOptionDBPtr-get option-db) "foo" "bar")))
+    (test-end "test-gnc-test-multichoice-option"))
+                         (GncOptionDBPtr-get option-db) "foo" "bar"))
+    (test-end "test-gnc-test-multichoice-option")))

commit 8eedcb6d6dcb18b2e536369ee2f4325d21877957
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Oct 15 16:33:55 2019 -0700

    Extract all SCM functions to gnc-optiondb.i.
    
    So that it can be moved to bindings/ when gnc-module-load-begone is ready.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 045bd5541..59cda4bb5 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -178,72 +178,6 @@ private:
     GncOptionUIType m_ui_type;
 };
 
-inline SCM
-scm_from_value(std::string value)
-{
-    return scm_from_utf8_string(value.c_str());
-}
-
-inline SCM
-scm_from_value(bool value)
-{
-    return value ? SCM_BOOL_T : SCM_BOOL_F;
-}
-
-inline SCM
-scm_from_value(int64_t value)
-{
-    return scm_from_int64(value);
-}
-
-inline SCM
-scm_from_value(int value)
-{
-    return scm_from_int(value);
-}
-
-inline SCM
-scm_from_value(QofInstance* value)
-{
-    auto guid = guid_to_string(qof_instance_get_guid(value));
-    auto scm_guid = scm_from_utf8_string(guid);
-    g_free(guid);
-    return scm_guid;
-}
-
-inline SCM
-scm_from_value(QofQuery* value)
-{
-    return SCM_BOOL_F;
-}
-
-inline SCM
-scm_from_value(GncNumeric value)
-{
-    return SCM_BOOL_F;
-}
-
-inline SCM
-scm_from_value(std::vector<GncGUID> value)
-{
-    SCM s_list;
-    for (auto guid : value)
-    {
-        auto guid_s = guid_to_string(qof_instance_get_guid(&guid));
-        auto scm_guid = scm_from_utf8_string(guid_s);
-        auto scm_guid_list1 = scm_list_1(scm_guid);
-        s_list = scm_append(scm_list_2(s_list, scm_guid_list1));
-        g_free(guid_s);
-    }
-    return s_list;
-}
-
-inline SCM
-scm_from_value(GncMultiChoiceOptionChoices value)
-{
-    return SCM_BOOL_F;
-}
-
 template <typename ValueType>
 class GncOptionValue :
     public OptionClassifier, public OptionUIItem
@@ -388,6 +322,7 @@ public:
                     return ValueType {};
             }, m_option);
     }
+
     template <typename ValueType> ValueType get_default_value() const
     {
         return std::visit([](const auto& option)->ValueType {
@@ -398,20 +333,7 @@ public:
             }, m_option);
 
     }
-    SCM get_scm_value() const
-    {
-        return std::visit([](const auto& option)->SCM {
-                auto value{option.get_value()};
-                return scm_from_value(value);
-            }, m_option);
-    }
-    SCM get_scm_default_value() const
-    {
-        return std::visit([](const auto& option)->SCM {
-                auto value{option.get_default_value()};
-                return scm_from_value(value);
-            }, m_option);
-    }
+
     template <typename ValueType> void set_value(ValueType value)
     {
         std::visit([value](auto& option) {
@@ -467,6 +389,7 @@ public:
                 option.make_internal();
             }, m_option);
     }
+    const GncOptionVariant& _get_option() const { return m_option; }
 private:
     GncOptionVariant m_option;
 };
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 7d8af2f71..157d8b7f8 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -46,22 +46,139 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
 
 %include <std_string.i>
 
+
+%inline %{
+inline SCM
+scm_from_value(std::string value)
+{
+    return scm_from_utf8_string(value.c_str());
+}
+
+inline SCM
+scm_from_value(bool value)
+{
+    return value ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+inline SCM
+scm_from_value(int64_t value)
+{
+    return scm_from_int64(value);
+}
+
+inline SCM
+scm_from_value(int value)
+{
+    return scm_from_int(value);
+}
+
+inline SCM
+scm_from_value(QofInstance* value)
+{
+    auto guid = guid_to_string(qof_instance_get_guid(value));
+    auto scm_guid = scm_from_utf8_string(guid);
+    g_free(guid);
+    return scm_guid;
+}
+
+inline SCM
+scm_from_value(QofQuery* value)
+{
+    return SCM_BOOL_F;
+}
+
+inline SCM
+scm_from_value(GncNumeric value)
+{
+    return SCM_BOOL_F;
+}
+
+inline SCM
+scm_from_value(std::vector<GncGUID> value)
+{
+    SCM s_list;
+    for (auto guid : value)
+    {
+        auto guid_s = guid_to_string(qof_instance_get_guid(&guid));
+        auto scm_guid = scm_from_utf8_string(guid_s);
+        auto scm_guid_list1 = scm_list_1(scm_guid);
+        s_list = scm_append(scm_list_2(s_list, scm_guid_list1));
+        g_free(guid_s);
+    }
+    return s_list;
+}
+
+inline SCM
+    scm_from_value(std::vector<std::pair<std::string, std::string>>)
+{
+    return SCM_BOOL_F;
+}
+%}
 %ignore OptionClassifier;
 %ignore OptionUIItem;
-%ignore GncOption;
+%nodefaultctor GncOption;
 
 wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
+%include "gnc-option.hpp"
 %include "gnc-optiondb.hpp"
 
+%extend GncOption {
+    SCM get_scm_value() const
+    {
+        return std::visit([](const auto& option)->SCM {
+                auto value{option.get_value()};
+                return scm_from_value(value);
+            }, $self->_get_option());
+    }
+    SCM get_scm_default_value() const
+    {
+        return std::visit([](const auto& option)->SCM {
+                auto value{option.get_default_value()};
+                return scm_from_value(value);
+            }, $self->_get_option());
+    }
+};
+
 %extend GncOptionDB {
     SCM lookup_option(const char* section, const char* name)
     {
         auto db_opt = $self->find_option(section, name);
         if (!db_opt)
             return SCM_BOOL_F;
-        return db_opt->get().get_scm_value();
+        return GncOption_get_scm_value(&(db_opt->get()));
     }
 
     %template(set_option_string) set_option<std::string>;
     %template(set_option_int) set_option<int>;
  };
+
+/*
+TEST(GncOption, test_string_scm_functions)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
+    auto scm_value = option.get_scm_value();
+    auto str_value = scm_to_utf8_string(scm_value);
+    EXPECT_STREQ("waldo", str_value);
+    g_free(str_value);
+    scm_value = option.get_scm_default_value();
+    str_value = scm_to_utf8_string(scm_value);
+    EXPECT_STREQ("waldo", str_value);
+    g_free(str_value);
+}
+
+TEST(GNCOption, test_budget_scm_functions)
+{
+    auto book = qof_book_new();
+    auto budget = gnc_budget_new(book);
+    GncOption option("foo", "bar", "baz", "Phony Option",
+                     QOF_INSTANCE(budget));
+    auto scm_budget = option.get_scm_value();
+    auto str_value = scm_to_utf8_string(scm_budget);
+    auto guid = guid_to_string(qof_instance_get_guid(budget));
+    EXPECT_STREQ(guid, str_value);
+    g_free(guid);
+    gnc_budget_destroy(budget);
+    qof_book_destroy(book);
+}
+
+*/
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 9267ba630..ec52438f9 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -67,19 +67,6 @@ TEST(GncOption, test_int64_t_value)
     EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
 }
 
-TEST(GncOption, test_string_scm_functions)
-{
-    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
-    auto scm_value = option.get_scm_value();
-    auto str_value = scm_to_utf8_string(scm_value);
-    EXPECT_STREQ("waldo", str_value);
-    g_free(str_value);
-    scm_value = option.get_scm_default_value();
-    str_value = scm_to_utf8_string(scm_value);
-    EXPECT_STREQ("waldo", str_value);
-    g_free(str_value);
-}
-
 TEST(GNCOption, test_budget_ctor)
 {
     auto book = qof_book_new();
@@ -92,21 +79,6 @@ TEST(GNCOption, test_budget_ctor)
     qof_book_destroy(book);
 }
 
-TEST(GNCOption, test_budget_scm_functions)
-{
-    auto book = qof_book_new();
-    auto budget = gnc_budget_new(book);
-    GncOption option("foo", "bar", "baz", "Phony Option",
-                     QOF_INSTANCE(budget));
-    auto scm_budget = option.get_scm_value();
-    auto str_value = scm_to_utf8_string(scm_budget);
-    auto guid = guid_to_string(qof_instance_get_guid(budget));
-    EXPECT_STREQ(guid, str_value);
-    g_free(guid);
-    gnc_budget_destroy(budget);
-    qof_book_destroy(book);
-}
-
 TEST(GNCOption, test_commodity_ctor)
 {
     auto book = qof_book_new();

commit d544f852568cdaa271980153e699cbd3ec242918
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Oct 15 14:49:49 2019 -0700

    Replace boost::variant and boost::optional with the C++17 std equivs.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 36a598107..8543efa20 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -25,68 +25,3 @@
 #include "gnc-option.hpp"
 #include <engine-helpers-guile.h>
 
-template<> SCM
-scm_from_value<std::string>(std::string value)
-{
-    return scm_from_utf8_string(value.c_str());
-}
-
-template<> SCM
-scm_from_value<bool>(bool value)
-{
-    return value ? SCM_BOOL_T : SCM_BOOL_F;
-}
-
-template<> SCM
-scm_from_value<int64_t>(int64_t value)
-{
-    return scm_from_int64(value);
-}
-
-template<> SCM
-scm_from_value<int>(int value)
-{
-    return scm_from_int(value);
-}
-
-template<> SCM
-scm_from_value<QofInstance*>(QofInstance* value)
-{
-    auto guid = guid_to_string(qof_instance_get_guid(value));
-    auto scm_guid = scm_from_utf8_string(guid);
-    g_free(guid);
-    return scm_guid;
-}
-
-template<> SCM
-scm_from_value<QofQuery*>(QofQuery* value)
-{
-    return SCM_BOOL_F;
-}
-
-template<> SCM
-scm_from_value<GncNumeric>(GncNumeric value)
-{
-    return SCM_BOOL_F;
-}
-
-template<> SCM
-scm_from_value<std::vector<GncGUID>>(std::vector<GncGUID> value)
-{
-    SCM s_list;
-    for (auto guid : value)
-    {
-        auto guid_s = guid_to_string(qof_instance_get_guid(&guid));
-        auto scm_guid = scm_from_utf8_string(guid_s);
-        auto scm_guid_list1 = scm_list_1(scm_guid);
-        s_list = scm_append(scm_list_2(s_list, scm_guid_list1));
-        g_free(guid_s);
-    }
-    return s_list;
-}
-
-template<> SCM
-scm_from_value<GncMultiChoiceOptionChoices>(GncMultiChoiceOptionChoices value)
-{
-    return SCM_BOOL_F;
-}
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 9c5daa544..045bd5541 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -32,11 +32,14 @@ extern "C"
 #include <gnc-commodity.h>
 }
 #include <gnc-numeric.hpp>
+#include <guid.hpp>
 #include <libguile.h>
 #include <string>
+#include <utility>
+#include <vector>
 #include <exception>
 #include <functional>
-#include <boost/variant.hpp>
+#include <variant>
 
 /*
  * Unused base class to document the structure of the current Scheme option
@@ -141,8 +144,8 @@ class GncOptionUIItem;
 class OptionUIItem
 {
 public:
-    GncOptionUIType get_ui_type() { return m_ui_type; }
-    GncOptionUIItem* const get_ui_item() {return m_ui_item; }
+    GncOptionUIType get_ui_type() const { return m_ui_type; }
+    GncOptionUIItem* const get_ui_item() const {return m_ui_item; }
     void clear_ui_item() { m_ui_item = nullptr; }
     void set_ui_item(GncOptionUIItem* ui_item)
     {
@@ -175,8 +178,71 @@ private:
     GncOptionUIType m_ui_type;
 };
 
-template <typename ValueType>
-SCM scm_from_value(ValueType);
+inline SCM
+scm_from_value(std::string value)
+{
+    return scm_from_utf8_string(value.c_str());
+}
+
+inline SCM
+scm_from_value(bool value)
+{
+    return value ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+inline SCM
+scm_from_value(int64_t value)
+{
+    return scm_from_int64(value);
+}
+
+inline SCM
+scm_from_value(int value)
+{
+    return scm_from_int(value);
+}
+
+inline SCM
+scm_from_value(QofInstance* value)
+{
+    auto guid = guid_to_string(qof_instance_get_guid(value));
+    auto scm_guid = scm_from_utf8_string(guid);
+    g_free(guid);
+    return scm_guid;
+}
+
+inline SCM
+scm_from_value(QofQuery* value)
+{
+    return SCM_BOOL_F;
+}
+
+inline SCM
+scm_from_value(GncNumeric value)
+{
+    return SCM_BOOL_F;
+}
+
+inline SCM
+scm_from_value(std::vector<GncGUID> value)
+{
+    SCM s_list;
+    for (auto guid : value)
+    {
+        auto guid_s = guid_to_string(qof_instance_get_guid(&guid));
+        auto scm_guid = scm_from_utf8_string(guid_s);
+        auto scm_guid_list1 = scm_list_1(scm_guid);
+        s_list = scm_append(scm_list_2(s_list, scm_guid_list1));
+        g_free(guid_s);
+    }
+    return s_list;
+}
+
+inline SCM
+scm_from_value(GncMultiChoiceOptionChoices value)
+{
+    return SCM_BOOL_F;
+}
 
 template <typename ValueType>
 class GncOptionValue :
@@ -190,6 +256,10 @@ public:
         OptionClassifier{section, name, key, doc_string},
         OptionUIItem(ui_type),
         m_value{value}, m_default_value{value} {}
+    GncOptionValue<ValueType>(const GncOptionValue<ValueType>&) = default;
+    GncOptionValue<ValueType>(GncOptionValue<ValueType>&&) = default;
+    GncOptionValue<ValueType>& operator=(const GncOptionValue<ValueType>&) = default;
+    GncOptionValue<ValueType>& operator=(GncOptionValue<ValueType>&&) = default;
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
     void set_value(ValueType new_value) { m_value = new_value; }
@@ -227,6 +297,10 @@ public:
             if (!this->validate(value))
             throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
     }
+    GncOptionValidatedValue<ValueType>(const GncOptionValidatedValue<ValueType>&) = default;
+    GncOptionValidatedValue<ValueType>(GncOptionValidatedValue<ValueType>&&) = default;
+    GncOptionValidatedValue<ValueType>& operator=(const GncOptionValidatedValue<ValueType>&) = default;
+    GncOptionValidatedValue<ValueType>& operator=(GncOptionValidatedValue<ValueType>&&) = default;
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
     bool validate(ValueType value) { return m_validator(value); }
@@ -259,6 +333,10 @@ public:
         m_default_value{value >= min && value <= max ? value : min},
         m_min{min}, m_step{step} {}
 
+    GncOptionRangeValue<ValueType>(const GncOptionRangeValue<ValueType>&) = default;
+    GncOptionRangeValue<ValueType>(GncOptionRangeValue<ValueType>&&) = default;
+    GncOptionRangeValue<ValueType>& operator=(const GncOptionRangeValue<ValueType>&) = default;
+    GncOptionRangeValue<ValueType>& operator=(GncOptionRangeValue<ValueType>&&) = default;
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
     bool validate(ValueType value) { return value >= m_min && value <= m_max; }
@@ -277,7 +355,7 @@ private:
     ValueType m_step;
 };
 
-using GncOptionVariant = boost::variant<GncOptionValue<std::string>,
+using GncOptionVariant = std::variant<GncOptionValue<std::string>,
                                         GncOptionValue<bool>,
                                         GncOptionValue<int64_t>,
                                         GncOptionValue<QofInstance*>,
@@ -303,185 +381,93 @@ public:
 
     template <typename ValueType> ValueType get_value() const
     {
-        return boost::apply_visitor(GetValueVisitor<ValueType>(), m_option);
+        return std::visit([](const auto& option)->ValueType {
+                if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
+                    return option.get_value();
+                else
+                    return ValueType {};
+            }, m_option);
     }
     template <typename ValueType> ValueType get_default_value() const
     {
-        return boost::apply_visitor(GetDefaultValueVisitor<ValueType>(), m_option);
+        return std::visit([](const auto& option)->ValueType {
+                if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
+                    return option.get_default_value();
+                else
+                    return ValueType {};
+            }, m_option);
+
     }
     SCM get_scm_value() const
     {
-        return boost::apply_visitor(GetSCMVisitor(), m_option);
+        return std::visit([](const auto& option)->SCM {
+                auto value{option.get_value()};
+                return scm_from_value(value);
+            }, m_option);
     }
     SCM get_scm_default_value() const
     {
-        return boost::apply_visitor(GetSCMDefaultVisitor(), m_option);
+        return std::visit([](const auto& option)->SCM {
+                auto value{option.get_default_value()};
+                return scm_from_value(value);
+            }, m_option);
     }
     template <typename ValueType> void set_value(ValueType value)
     {
-        boost::apply_visitor(SetValueVisitor<ValueType>(value), m_option);
+        std::visit([value](auto& option) {
+                if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
+                   option.set_value(value);
+            }, m_option);
     }
     const std::string& get_section() const
     {
-        return boost::apply_visitor(GetSectionVisitor(), m_option);
+        return std::visit([](const auto& option)->const std::string& {
+                return option.m_section;
+            }, m_option);
     }
     const std::string& get_name() const
     {
-        return boost::apply_visitor(GetNameVisitor(), m_option);
+        return std::visit([](const auto& option)->const std::string& {
+                return option.m_name;
+            }, m_option);
     }
     const std::string& get_key() const
     {
-        return boost::apply_visitor(GetKeyVisitor(), m_option);
+        return std::visit([](const auto& option)->const std::string& {
+                return option.m_sort_tag;
+            }, m_option);
     }
     const std::string& get_docstring() const
     {
-        return boost::apply_visitor(GetDocstringVisitor(), m_option);
+          return std::visit([](const auto& option)->const std::string& {
+                return option.m_doc_string;
+              }, m_option);
     }
     void set_ui_item(GncOptionUIItem* ui_elem)
     {
-        return boost::apply_visitor(SetUIItemVisitor(ui_elem), m_option);
+        std::visit([ui_elem](auto& option) {
+                option.set_ui_item(ui_elem);
+            }, m_option);
     }
-    const GncOptionUIType get_ui_type()
+    const GncOptionUIType get_ui_type() const
     {
-        return boost::apply_visitor(GetUITypeVisitor(), m_option);
+        return std::visit([](const auto& option)->GncOptionUIType {
+                return option.get_ui_type();
+            }, m_option);
     }
-    GncOptionUIItem* const get_ui_item()
+    GncOptionUIItem* const get_ui_item() const
     {
-        return boost::apply_visitor(GetUIItemVisitor(), m_option);
+        return std::visit([](const auto& option)->GncOptionUIItem* {
+                return option.get_ui_item();
+            }, m_option);
     }
     void make_internal()
     {
-        return boost::apply_visitor(MakeInternalVisitor(), m_option);
+        std::visit([](auto& option) {
+                option.make_internal();
+            }, m_option);
     }
 private:
-    template <typename ValueType>
-    struct GetValueVisitor : public boost::static_visitor<ValueType>
-    {
-        ValueType operator()(const GncOptionValue<ValueType>& option) const {
-            return option.get_value();
-        }
-        ValueType operator()(const GncOptionValidatedValue<ValueType>& option) const {
-            return option.get_value();
-        }
-        template <class OptionType>
-        ValueType operator()(OptionType& option) const {
-            return ValueType{};
-        }
-    };
-    template <typename ValueType>
-    struct GetDefaultValueVisitor : public boost::static_visitor<ValueType>
-    {
-        ValueType operator()(const GncOptionValue<ValueType>& option) const {
-            return option.get_default_value();
-        }
-        ValueType operator()(const GncOptionValidatedValue<ValueType>& option) const {
-            return option.get_default_value();
-        }
-        template <class OptionType>
-        ValueType operator()(OptionType& option) const {
-            return ValueType();
-        }
-    };
-    template <typename ValueType>
-    struct SetValueVisitor : public boost::static_visitor<>
-    {
-        SetValueVisitor(ValueType value) : m_value{value} {}
-        void operator()(GncOptionValue<ValueType>& option) const {
-            option.set_value(m_value);
-        }
-        void operator()(GncOptionValidatedValue<ValueType>& option) const {
-            option.set_value(m_value);
-        }
-        template <class OptionType>
-        void operator()(OptionType& option) const {
-            std::string msg{"Attempt to set option of type "};
-            msg += typeid(OptionType).name();
-            msg += " with value of type ";
-            msg += typeid(m_value).name();
-            throw std::invalid_argument(msg);
-        }
-    private:
-        ValueType m_value;
-    };
-    struct GetSCMVisitor : public boost::static_visitor<SCM>
-    {
-        template <class OptionType>
-        SCM operator()(OptionType& option) const {
-            auto value{option.get_value()};
-            return scm_from_value<decltype(value)>(value);
-        }
-    };
-    struct GetSCMDefaultVisitor : public boost::static_visitor<SCM>
-    {
-        template <class OptionType>
-        SCM operator()(OptionType& option) const {
-            auto value{option.get_value()};
-            return scm_from_value<decltype(value)>(value);
-        }
-    };
-    struct GetSectionVisitor : public boost::static_visitor<const std::string&>
-    {
-        template <class OptionType>
-        const std::string& operator()(OptionType& option) const {
-            return option.m_section;
-        }
-    };
-    struct GetNameVisitor : public boost::static_visitor<const std::string&>
-    {
-        template <class OptionType>
-        const std::string& operator()(OptionType& option) const {
-            return option.m_name;
-        }
-    };
-    struct GetKeyVisitor : public boost::static_visitor<const std::string&>
-    {
-        template <class OptionType>
-        const std::string& operator()(OptionType& option) const {
-            return option.m_sort_tag;
-        }
-    };
-    struct GetDocstringVisitor :
-        public boost::static_visitor<const std::string&>
-    {
-        template <class OptionType>
-        const std::string& operator()(OptionType& option) const {
-            return option.m_doc_string;
-        }
-    };
-    struct SetUIItemVisitor : public boost::static_visitor<>
-    {
-        SetUIItemVisitor(GncOptionUIItem* ui_item) : m_ui_item{ui_item} {}
-        template <class OptionType>
-        void operator()(OptionType& option) const {
-            option.set_ui_item(m_ui_item);
-        }
-    private:
-        GncOptionUIItem* m_ui_item;
-    };
-    struct GetUITypeVisitor :
-        public boost::static_visitor<GncOptionUIType>
-    {
-        template <class OptionType>
-        const GncOptionUIType operator()(OptionType& option) const {
-            return option.get_ui_type();
-        }
-    };
-    struct GetUIItemVisitor :
-        public boost::static_visitor<GncOptionUIItem* const >
-    {
-        template <class OptionType>
-        GncOptionUIItem* const operator()(OptionType& option) const {
-            return option.get_ui_item();
-        }
-    };
-    struct MakeInternalVisitor : public boost::static_visitor<>
-    {
-        template <class OptionType>
-        void operator()(OptionType& option) const {
-            return option.make_internal();
-        }
-    };
-
     GncOptionVariant m_option;
 };
 
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 169228c06..9276513ac 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -23,7 +23,7 @@
 
 #include "gnc-optiondb.hpp"
 
-GncOptionDB::GncOptionDB() : m_default_section{boost::none} {}
+GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}
 
 GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
 
@@ -39,7 +39,7 @@ GncOptionDB::register_option(const char* section, GncOption&& option)
 
     if (db_section)
     {
-        db_section->second.emplace_back(std::move(option));
+        db_section->get().second.emplace_back(std::move(option));
         return;
     }
 
@@ -55,9 +55,9 @@ GncOptionDB::unregister_option(const char* section, const char* name)
     auto db_section = find_section(section);
     if (db_section)
     {
-        db_section->second.erase(
+        db_section->get().second.erase(
             std::remove_if(
-                db_section->second.begin(), db_section->second.end(),
+                db_section->get().second.begin(), db_section->get().second.end(),
                 [name](const GncOption& option) -> bool
                 {
                     return option.get_name() == std::string{name};
@@ -75,7 +75,7 @@ const GncOptionSection* const
 GncOptionDB::get_default_section() const noexcept
 {
     if (m_default_section)
-        return &(m_default_section.get());
+        return &(m_default_section.value().get());
     return nullptr;
 }
 
@@ -85,7 +85,7 @@ GncOptionDB::set_ui_item(const char* section, const char* name,
 {
     auto option = find_option(section, name);
     if (!option) return;
-    option->set_ui_item(ui_item);
+    option->get().set_ui_item(ui_item);
 }
 
 GncOptionUIItem* const
@@ -93,7 +93,7 @@ GncOptionDB::get_ui_item(const char* section, const char* name)
 {
     auto option = find_option(section, name);
     if (!option) return nullptr;
-    return option->get_ui_item();
+    return option->get().get_ui_item();
 }
 
 GncOptionUIType
@@ -101,7 +101,7 @@ GncOptionDB::get_ui_type(const char* section, const char* name)
 {
     auto option = find_option(section, name);
     if (!option) return GncOptionUIType::INTERNAL;
-    return option->get_ui_type();
+    return option->get().get_ui_type();
 }
 
 void
@@ -110,7 +110,7 @@ GncOptionDB::set_ui_from_option(const char* section, const char* name,
 {
     auto option = find_option(section, name);
     if (!option) return;
-    func(option.get());
+    func(option->get());
 }
 
 void
@@ -119,11 +119,11 @@ GncOptionDB::set_option_from_ui(const char* section, const char* name,
 {
     auto option = find_option(section, name);
     if (!option) return;
-    func(option.get());
+    func(option->get());
 }
 
 
-boost::optional<GncOptionSection&>
+std::optional<std::reference_wrapper<GncOptionSection>>
 GncOptionDB::find_section(const char* section)
 {
     auto db_section = std::find_if(
@@ -133,24 +133,24 @@ GncOptionDB::find_section(const char* section)
             return sect.first == std::string{section};
         });
     if (db_section == m_sections.end())
-        return boost::none;
+        return std::nullopt;
     return *db_section;
 }
 
-boost::optional<GncOption&>
+std::optional<std::reference_wrapper<GncOption>>
 GncOptionDB::find_option(const char* section, const char* name)
 {
     auto db_section = find_section(section);
     if (!db_section)
-        return boost::none;
+        return std::nullopt;
     auto db_opt = std::find_if(
-        db_section->second.begin(), db_section->second.end(),
+        db_section->get().second.begin(), db_section->get().second.end(),
         [name](GncOption& option) -> bool
         {
             return option.get_name() == std::string{name};
         });
-    if (db_opt == db_section->second.end())
-        return boost::none;
+    if (db_opt == db_section->get().second.end())
+        return std::nullopt;
     return *db_opt;
 }
 
@@ -162,7 +162,7 @@ GncOptionDB::lookup_string_option(const char* section, const char* name)
     auto db_opt = find_option(section, name);
     if (!db_opt)
         return empty_string;
-    return db_opt->get_value<std::string>();
+    return db_opt->get().get_value<std::string>();
 }
 
 void
@@ -170,7 +170,7 @@ GncOptionDB::make_internal(const char* section, const char* name)
 {
     auto db_opt = find_option(section, name);
     if (db_opt)
-        db_opt->make_internal();
+        db_opt->get().make_internal();
 }
 
 void
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 1b2de18f3..2a9c597a2 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -27,7 +27,7 @@
 #include "gnc-option.hpp"
 #include <functional>
 #include <exception>
-#include <boost/optional.hpp>
+#include <optional>
 extern "C"
 {
 #include <gncInvoice.h>
@@ -70,7 +70,7 @@ public:
             auto option{find_option(section, name)};
             if (!option)
                 return false;
-            option->set_value<ValueType>(value);
+            option->get().set_value(value);
             return true;
         }
         catch(const std::invalid_argument& err)
@@ -82,10 +82,10 @@ public:
 //    void set_selectable(const char* section, const char* name);
     void make_internal(const char* section, const char* name);
     void commit();
-    boost::optional<GncOptionSection&> find_section(const char* section);
-    boost::optional<GncOption&> find_option(const char* section, const char* name);
+    std::optional<std::reference_wrapper<GncOptionSection>> find_section(const char* section);
+    std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name);
 private:
-    boost::optional<GncOptionSection&> m_default_section;
+    std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
     std::vector<GncOptionSection> m_sections;
     bool m_dirty = false;
 
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index ad81c0450..7d8af2f71 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -59,7 +59,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
         auto db_opt = $self->find_option(section, name);
         if (!db_opt)
             return SCM_BOOL_F;
-        return db_opt->get_scm_value();
+        return db_opt->get().get_scm_value();
     }
 
     %template(set_option_string) set_option<std::string>;

commit 694a15ed21ff8f21b521ea64c5f901dfc0b539bf
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Oct 13 09:40:08 2019 -0700

    Extract SCM GncOptionDB::lookup_option to gnc-optiondb.i.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 80fb76891..169228c06 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -154,15 +154,6 @@ GncOptionDB::find_option(const char* section, const char* name)
     return *db_opt;
 }
 
-SCM
-GncOptionDB::lookup_option(const char* section, const char* name)
-{
-    auto db_opt = find_option(section, name);
-    if (!db_opt)
-        return SCM_BOOL_F;
-    return db_opt->get_scm_value();
-}
-
 std::string
 GncOptionDB::lookup_string_option(const char* section, const char* name)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index abc783e05..1b2de18f3 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -60,7 +60,6 @@ public:
                             std::function<void(GncOption&)> func);
     void set_option_from_ui(const char* section, const char* name,
                             std::function<void(GncOption&)> func);
-    SCM lookup_option(const char* section, const char* name);
     std::string lookup_string_option(const char* section,
                                             const char* name);
     template <typename ValueType>
@@ -83,10 +82,9 @@ public:
 //    void set_selectable(const char* section, const char* name);
     void make_internal(const char* section, const char* name);
     void commit();
-private:
     boost::optional<GncOptionSection&> find_section(const char* section);
     boost::optional<GncOption&> find_option(const char* section, const char* name);
-
+private:
     boost::optional<GncOptionSection&> m_default_section;
     std::vector<GncOptionSection> m_sections;
     bool m_dirty = false;
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 3ecee41f1..ad81c0450 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -54,6 +54,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
 %include "gnc-optiondb.hpp"
 
 %extend GncOptionDB {
+    SCM lookup_option(const char* section, const char* name)
+    {
+        auto db_opt = $self->find_option(section, name);
+        if (!db_opt)
+            return SCM_BOOL_F;
+        return db_opt->get_scm_value();
+    }
+
     %template(set_option_string) set_option<std::string>;
     %template(set_option_int) set_option<int>;
  };

commit 16d1f0655bc69671bce3ed21eeb3a454fee8f3cf
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Oct 12 18:17:09 2019 -0700

    Get libswig-gnc-optiondb to install in the right place on Windows.

diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 17de9412d..d8fe3e53f 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -117,6 +117,12 @@ if (HAVE_SRFI64)
   target_include_directories(swig-gnc-optiondb
     PRIVATE ${swig_gnc_optiondb_INCLUDES})
 
+  install(TARGETS swig-gnc-optiondb
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+
   gnc_add_scheme_test_targets(scm-test-gnc-optiondb
     "test-gnc-optiondb.scm"
     "tests"

commit cee3cdaff9a29a3f64e4d38faad6f96dc5873be4
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Oct 12 18:16:05 2019 -0700

    Instantiate GncOption::set_option for guile, initial types string and int.
    
    For proof-of-concept. Guile obviously doesn't know about templates.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index ade5c73e2..3ecee41f1 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -51,5 +51,9 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
 %ignore GncOption;
 
 wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
-
 %include "gnc-optiondb.hpp"
+
+%extend GncOptionDB {
+    %template(set_option_string) set_option<std::string>;
+    %template(set_option_int) set_option<int>;
+ };
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index f46b4303e..cce651fb0 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -40,6 +40,10 @@
   (let* ((option-db (gnc-option-db-new))
          (string-opt (gnc-register-string-option option-db "foo" "bar" "baz"
                                                  "Phony Option" "waldo")))
-    (test-equal (GncOptionDB-lookup-option (GncOptionDBPtr-get option-db) "foo" "bar") "waldo"))
+    (test-equal "waldo" (GncOptionDB-lookup-option
+                         (GncOptionDBPtr-get option-db) "foo" "bar"))
 
+    (GncOptionDB-set-option-string (GncOptionDBPtr-get option-db) "foo" "bar" "pepper")
+    (test-equal "pepper" (GncOptionDB-lookup-option
+                          (GncOptionDBPtr-get option-db) "foo" "bar")))
   (test-end "test-gnc-make-string-option"))

commit 9cdcaf0da876dca96630f6e025bdb77843607d29
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Oct 12 18:14:22 2019 -0700

    Remove GncOptionDB::set_selectable and convert set_option to a template.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 3dde5739e..80fb76891 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -174,17 +174,6 @@ GncOptionDB::lookup_string_option(const char* section, const char* name)
     return db_opt->get_value<std::string>();
 }
 
-bool
-GncOptionDB::set_option(const char* section, const char* name, SCM value)
-{
-    return false;
-}
-
-void
-GncOptionDB::set_selectable(const char* section, const char* name)
-{
-}
-
 void
 GncOptionDB::make_internal(const char* section, const char* name)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index bf0cdade3..abc783e05 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -26,6 +26,7 @@
 
 #include "gnc-option.hpp"
 #include <functional>
+#include <exception>
 #include <boost/optional.hpp>
 extern "C"
 {
@@ -62,8 +63,24 @@ public:
     SCM lookup_option(const char* section, const char* name);
     std::string lookup_string_option(const char* section,
                                             const char* name);
-    bool set_option(const char* section, const char* name, SCM value);
-    void set_selectable(const char* section, const char* name);
+    template <typename ValueType>
+    bool set_option(const char* section, const char* name, ValueType value)
+    {
+        try
+        {
+            auto option{find_option(section, name)};
+            if (!option)
+                return false;
+            option->set_value<ValueType>(value);
+            return true;
+        }
+        catch(const std::invalid_argument& err)
+        {
+            printf("Set Failed: %s\n", err.what());
+            return false;
+        }
+    }
+//    void set_selectable(const char* section, const char* name);
     void make_internal(const char* section, const char* name);
     void commit();
 private:

commit 935ce6db99461415bf61e292362ae41118c3c4a3
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Oct 12 18:12:46 2019 -0700

    Move the SCM option value conversion from the GncOptionValue classes to GncOption.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 68d45d3c6..9c5daa544 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -192,14 +192,6 @@ public:
         m_value{value}, m_default_value{value} {}
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
-    SCM get_scm_value() const
-    {
-        return scm_from_value(m_value);
-    }
-    SCM get_scm_default_value() const
-    {
-        return scm_from_value(m_default_value);
-    }
     void set_value(ValueType new_value) { m_value = new_value; }
 private:
     ValueType m_value;
@@ -237,14 +229,6 @@ public:
     }
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
-    SCM get_scm_value() const
-    {
-        return scm_from_value(m_value);
-    }
-    SCM get_scm_default_value() const
-    {
-        return scm_from_value(m_default_value);
-    }
     bool validate(ValueType value) { return m_validator(value); }
     void set_value(ValueType value)
     {
@@ -277,14 +261,6 @@ public:
 
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
-    SCM get_scm_value() const
-    {
-        return scm_from_value(m_value);
-    }
-    SCM get_scm_default_value() const
-    {
-        return scm_from_value(m_default_value);
-    }
     bool validate(ValueType value) { return value >= m_min && value <= m_max; }
     void set_value(ValueType value)
     {
@@ -431,14 +407,16 @@ private:
     {
         template <class OptionType>
         SCM operator()(OptionType& option) const {
-            return option.get_scm_value();
+            auto value{option.get_value()};
+            return scm_from_value<decltype(value)>(value);
         }
     };
     struct GetSCMDefaultVisitor : public boost::static_visitor<SCM>
     {
         template <class OptionType>
         SCM operator()(OptionType& option) const {
-            return option.get_scm_default_value();
+            auto value{option.get_value()};
+            return scm_from_value<decltype(value)>(value);
         }
     };
     struct GetSectionVisitor : public boost::static_visitor<const std::string&>

commit e51faff3e3dd4b4510f8da7edea6ddb047eaf044
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Oct 12 18:10:43 2019 -0700

    Throw an exception if one tries to set a GncOption with an unsupported type.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index e99c8a213..68d45d3c6 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -418,6 +418,11 @@ private:
         }
         template <class OptionType>
         void operator()(OptionType& option) const {
+            std::string msg{"Attempt to set option of type "};
+            msg += typeid(OptionType).name();
+            msg += " with value of type ";
+            msg += typeid(m_value).name();
+            throw std::invalid_argument(msg);
         }
     private:
         ValueType m_value;

commit c5fac51a8b6bf316d47bd6e613efaf1f8f277e87
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Oct 3 13:30:49 2019 -0700

    Change the type of OptionUIItem's m_ui_item from void* to GncOptionUIItem.
    
    A locally-opaque class wrapping whatever sort of widget ptr one needs.
    Thanks, warlord!

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 7a8032962..e99c8a213 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -125,6 +125,8 @@ struct OptionClassifier
 using GncMultiChoiceOptionEntry = std::pair<std::string, std::string>;
 using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
 
+class GncOptionUIItem;
+
 /**
  * Holds a pointer to the UI item which will control the option and an enum
  * representing the type of the option for dispatch purposes; all of that
@@ -140,9 +142,9 @@ class OptionUIItem
 {
 public:
     GncOptionUIType get_ui_type() { return m_ui_type; }
-    void* const get_ui_item() {return m_ui_item; }
+    GncOptionUIItem* const get_ui_item() {return m_ui_item; }
     void clear_ui_item() { m_ui_item = nullptr; }
-    void set_ui_item(void* ui_item)
+    void set_ui_item(GncOptionUIItem* ui_item)
     {
         if (m_ui_type == GncOptionUIType::INTERNAL)
         {
@@ -169,7 +171,7 @@ protected:
     OptionUIItem& operator=(const OptionUIItem&) = default;
     OptionUIItem& operator=(OptionUIItem&&) = default;
 private:
-    void* m_ui_item;
+    GncOptionUIItem* m_ui_item;
     GncOptionUIType m_ui_type;
 };
 
@@ -359,7 +361,7 @@ public:
     {
         return boost::apply_visitor(GetDocstringVisitor(), m_option);
     }
-    void set_ui_item(void* ui_elem)
+    void set_ui_item(GncOptionUIItem* ui_elem)
     {
         return boost::apply_visitor(SetUIItemVisitor(ui_elem), m_option);
     }
@@ -367,7 +369,7 @@ public:
     {
         return boost::apply_visitor(GetUITypeVisitor(), m_option);
     }
-    void* const get_ui_item()
+    GncOptionUIItem* const get_ui_item()
     {
         return boost::apply_visitor(GetUIItemVisitor(), m_option);
     }
@@ -465,13 +467,13 @@ private:
     };
     struct SetUIItemVisitor : public boost::static_visitor<>
     {
-        SetUIItemVisitor(void* ui_item) : m_ui_item{ui_item} {}
+        SetUIItemVisitor(GncOptionUIItem* ui_item) : m_ui_item{ui_item} {}
         template <class OptionType>
         void operator()(OptionType& option) const {
             option.set_ui_item(m_ui_item);
         }
     private:
-        void* m_ui_item;
+        GncOptionUIItem* m_ui_item;
     };
     struct GetUITypeVisitor :
         public boost::static_visitor<GncOptionUIType>
@@ -482,10 +484,10 @@ private:
         }
     };
     struct GetUIItemVisitor :
-        public boost::static_visitor<void* const >
+        public boost::static_visitor<GncOptionUIItem* const >
     {
         template <class OptionType>
-        void* const operator()(OptionType& option) const {
+        GncOptionUIItem* const operator()(OptionType& option) const {
             return option.get_ui_item();
         }
     };
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 250302aa8..3dde5739e 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -80,14 +80,15 @@ GncOptionDB::get_default_section() const noexcept
 }
 
 void
-GncOptionDB::set_ui_item(const char* section, const char* name, void* ui_item)
+GncOptionDB::set_ui_item(const char* section, const char* name,
+                         GncOptionUIItem* ui_item)
 {
     auto option = find_option(section, name);
     if (!option) return;
     option->set_ui_item(ui_item);
 }
 
-void* const
+GncOptionUIItem* const
 GncOptionDB::get_ui_item(const char* section, const char* name)
 {
     auto option = find_option(section, name);
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 3b723dd26..bf0cdade3 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -52,8 +52,8 @@ public:
     void unregister_option(const char* section, const char* name);
     void set_default_section(const char* section);
     const GncOptionSection* const get_default_section() const noexcept;
-    void set_ui_item(const char* section, const char* name, void* ui_item);
-    void* const get_ui_item(const char* section, const char* name);
+    void set_ui_item(const char* section, const char* name, GncOptionUIItem* ui_item);
+    GncOptionUIItem* const get_ui_item(const char* section, const char* name);
     GncOptionUIType get_ui_type(const char* section, const char* name);
     void set_ui_from_option(const char* section, const char* name,
                             std::function<void(GncOption&)> func);
@@ -74,8 +74,8 @@ private:
     std::vector<GncOptionSection> m_sections;
     bool m_dirty = false;
 
-    std::function<void*()> m_get_ui_value;
-    std::function<void(void*)> m_set_ui_value;
+    std::function<GncOptionUIItem*()> m_get_ui_value;
+    std::function<void(GncOptionUIItem*)> m_set_ui_value;
 };
 
 using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 6c3a51889..9267ba630 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -202,6 +202,13 @@ private:
     std::string m_value;
 };
 
+class GncOptionUIItem
+{
+public:
+    GncOptionUIItem(GncUIItem* widget) : m_widget{widget} {}
+    GncUIItem* m_widget;
+};
+
 class GncOptionUITest : public ::testing::Test
 {
 protected:
@@ -222,6 +229,7 @@ TEST_F(GncOptionUI, test_option_ui_type)
 TEST_F(GncOptionUI, test_set_option_ui_item)
 {
     GncUIItem ui_item;
-    m_option.set_ui_item(&ui_item);
-    EXPECT_EQ(&ui_item, static_cast<const GncUIItem*>(m_option.get_ui_item()));
+    GncOptionUIItem option_ui_item{&ui_item};
+    m_option.set_ui_item(&option_ui_item);
+    EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget);
 }
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index 86a8390e6..be6221f60 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -78,6 +78,23 @@ private:
     std::string m_value;
 };
 
+class GncOptionUIItem
+{
+public:
+    GncOptionUIItem(GncUIType* widget) : m_widget{widget} {}
+    GncUIType* m_widget;
+};
+
+class GncOptionUITest : public ::testing::Test
+{
+protected:
+    GncOptionUITest() :
+        m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"},
+            GncOptionUIType::STRING} {}
+
+    GncOption m_option;
+};
+
 class GncOptionDBUITest : public ::testing::Test
 {
 protected:
@@ -99,18 +116,20 @@ protected:
 TEST_F(GncOptionDBUITest, test_set_ui_item)
 {
     GncUIType entry;
-    m_db->set_ui_item("foo", "bar", &entry);
-    EXPECT_EQ(&entry, static_cast<GncUIType*>(m_db->get_ui_item("foo", "bar")));
+    GncOptionUIItem ui_item(&entry);
+    m_db->set_ui_item("foo", "bar", &ui_item);
+    EXPECT_EQ(&entry, m_db->get_ui_item("foo", "bar")->m_widget);
 }
 
 TEST_F(GncOptionDBUITest, test_ui_value_from_option)
 {
     GncUIType entry;
+    GncOptionUIItem ui_item(&entry);
     const char* value{"waldo"};
-    m_db->set_ui_item("foo", "bar", &entry);
+    m_db->set_ui_item("foo", "bar", &ui_item);
     m_db->set_ui_from_option("foo", "bar", [](GncOption& option){
-            auto ui_item = static_cast<GncUIType* const>(option.get_ui_item());
-            ui_item->set_value(option.get_value<std::string>());
+            auto new_ui_item = option.get_ui_item();
+            new_ui_item->m_widget->set_value(option.get_value<std::string>());
         });
     EXPECT_STREQ(value, entry.get_value().c_str());
 }
@@ -118,12 +137,13 @@ TEST_F(GncOptionDBUITest, test_ui_value_from_option)
 TEST_F(GncOptionDBUITest, test_option_value_from_ui)
 {
     GncUIType entry;
+    GncOptionUIItem ui_item(&entry);
     const char* value{"pepper"};
-    m_db->set_ui_item("foo", "bar", &entry);
+    m_db->set_ui_item("foo", "bar", &ui_item);
     entry.set_value(value);
     m_db->set_option_from_ui("foo", "bar", [](GncOption& option){
-            auto ui_item = static_cast<GncUIType* const>(option.get_ui_item());
-            option.set_value(ui_item->get_value());
+            auto new_ui_item = option.get_ui_item()->m_widget;
+            option.set_value(new_ui_item->get_value());
         });
     EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str());
 }

commit 3296212aef4b87b7a37acf56d460cb6eaf25f1fc
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Oct 1 16:03:15 2019 -0700

    Sketch out the rest of the option types.
    
    Minimal implentation to get it to compile and pass tests, not functional yet.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 485b0d403..36a598107 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -23,6 +23,7 @@
 
 //#include "options.h"
 #include "gnc-option.hpp"
+#include <engine-helpers-guile.h>
 
 template<> SCM
 scm_from_value<std::string>(std::string value)
@@ -42,6 +43,12 @@ scm_from_value<int64_t>(int64_t value)
     return scm_from_int64(value);
 }
 
+template<> SCM
+scm_from_value<int>(int value)
+{
+    return scm_from_int(value);
+}
+
 template<> SCM
 scm_from_value<QofInstance*>(QofInstance* value)
 {
@@ -51,3 +58,35 @@ scm_from_value<QofInstance*>(QofInstance* value)
     return scm_guid;
 }
 
+template<> SCM
+scm_from_value<QofQuery*>(QofQuery* value)
+{
+    return SCM_BOOL_F;
+}
+
+template<> SCM
+scm_from_value<GncNumeric>(GncNumeric value)
+{
+    return SCM_BOOL_F;
+}
+
+template<> SCM
+scm_from_value<std::vector<GncGUID>>(std::vector<GncGUID> value)
+{
+    SCM s_list;
+    for (auto guid : value)
+    {
+        auto guid_s = guid_to_string(qof_instance_get_guid(&guid));
+        auto scm_guid = scm_from_utf8_string(guid_s);
+        auto scm_guid_list1 = scm_list_1(scm_guid);
+        s_list = scm_append(scm_list_2(s_list, scm_guid_list1));
+        g_free(guid_s);
+    }
+    return s_list;
+}
+
+template<> SCM
+scm_from_value<GncMultiChoiceOptionChoices>(GncMultiChoiceOptionChoices value)
+{
+    return SCM_BOOL_F;
+}
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 115b43b58..7a8032962 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -31,6 +31,7 @@ extern "C"
 #include <gnc-budget.h>
 #include <gnc-commodity.h>
 }
+#include <gnc-numeric.hpp>
 #include <libguile.h>
 #include <string>
 #include <exception>
@@ -108,7 +109,8 @@ enum GncOptionUIType
     VENDOR,
     EMPLOYEE,
     INVOICE,
-    TAX_TABLE
+    TAX_TABLE,
+    QUERY,
 };
 
 struct OptionClassifier
@@ -120,6 +122,9 @@ struct OptionClassifier
     std::string m_doc_string;
 };
 
+using GncMultiChoiceOptionEntry = std::pair<std::string, std::string>;
+using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
+
 /**
  * Holds a pointer to the UI item which will control the option and an enum
  * representing the type of the option for dispatch purposes; all of that
@@ -129,23 +134,32 @@ struct OptionClassifier
  * This class takes no ownership responsibility, so calling code is responsible
  * for ensuring that the UI_Item is alive. For convenience the public
  * clear_ui_item function can be used as a weak_ptr's destruction callback to
- * ensure that if the ui_item is destroyed elsewhere the ptr will be nulled and
- * the type reset to OptionUIType::INTERNAL.
+ * ensure that the ptr will be nulled if the ui_item is destroyed elsewhere.
  */
 class OptionUIItem
 {
 public:
-     GncOptionUIType get_ui_type() { return m_ui_type; }
+    GncOptionUIType get_ui_type() { return m_ui_type; }
     void* const get_ui_item() {return m_ui_item; }
+    void clear_ui_item() { m_ui_item = nullptr; }
     void set_ui_item(void* ui_item)
     {
         if (m_ui_type == GncOptionUIType::INTERNAL)
         {
-            std::string error{"Can't set ui item with void ui type."};
+            std::string error{"INTERNAL option, setting the UI item forbidden."};
             throw std::logic_error(std::move(error));
         }
         m_ui_item = ui_item;
     }
+    void make_internal()
+    {
+        if (m_ui_item != nullptr)
+        {
+            std::string error("Option has a UI Element, can't be INTERNAL.");
+            throw std::logic_error(std::move(error));
+        }
+        m_ui_type = GncOptionUIType::INTERNAL;
+    }
 protected:
     OptionUIItem(GncOptionUIType ui_type) :
         m_ui_item{nullptr}, m_ui_type{ui_type} {}
@@ -244,11 +258,57 @@ private:
     ValueType m_validation_data;
 };
 
+template <typename ValueType>
+class GncOptionRangeValue :
+    public OptionClassifier, public OptionUIItem
+{
+public:
+    GncOptionRangeValue<ValueType>(const char* section, const char* name,
+                                   const char* key, const char* doc_string,
+                                   ValueType value, ValueType min,
+                                   ValueType max, ValueType step) :
+        OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(GncOptionUIType::NUMBER_RANGE),
+        m_value{value >= min && value <= max ? value : min},
+        m_default_value{value >= min && value <= max ? value : min},
+        m_min{min}, m_step{step} {}
+
+    ValueType get_value() const { return m_value; }
+    ValueType get_default_value() const { return m_default_value; }
+    SCM get_scm_value() const
+    {
+        return scm_from_value(m_value);
+    }
+    SCM get_scm_default_value() const
+    {
+        return scm_from_value(m_default_value);
+    }
+    bool validate(ValueType value) { return value >= m_min && value <= m_max; }
+    void set_value(ValueType value)
+    {
+        if (this->validate(value))
+            m_value = value;
+        else
+            throw std::invalid_argument("Validation failed, value not set.");
+    }
+private:
+    ValueType m_value;
+    ValueType m_default_value;
+    ValueType m_min;
+    ValueType m_max;
+    ValueType m_step;
+};
+
 using GncOptionVariant = boost::variant<GncOptionValue<std::string>,
-                                 GncOptionValue<bool>,
-                                 GncOptionValue<int64_t>,
-                                 GncOptionValue<QofInstance*>,
-                                 GncOptionValidatedValue<QofInstance*>>;
+                                        GncOptionValue<bool>,
+                                        GncOptionValue<int64_t>,
+                                        GncOptionValue<QofInstance*>,
+                                        GncOptionValue<QofQuery*>,
+                                        GncOptionValue<std::vector<GncGUID>>,
+                                    GncOptionValue<GncMultiChoiceOptionChoices>,
+                                        GncOptionRangeValue<int>,
+                                        GncOptionRangeValue<GncNumeric>,
+                                        GncOptionValidatedValue<QofInstance*>>;
 class GncOption
 {
 public:
@@ -311,6 +371,10 @@ public:
     {
         return boost::apply_visitor(GetUIItemVisitor(), m_option);
     }
+    void make_internal()
+    {
+        return boost::apply_visitor(MakeInternalVisitor(), m_option);
+    }
 private:
     template <typename ValueType>
     struct GetValueVisitor : public boost::static_visitor<ValueType>
@@ -425,6 +489,13 @@ private:
             return option.get_ui_item();
         }
     };
+    struct MakeInternalVisitor : public boost::static_visitor<>
+    {
+        template <class OptionType>
+        void operator()(OptionType& option) const {
+            return option.make_internal();
+        }
+    };
 
     GncOptionVariant m_option;
 };
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index b7cb5ae74..250302aa8 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -184,6 +184,14 @@ GncOptionDB::set_selectable(const char* section, const char* name)
 {
 }
 
+void
+GncOptionDB::make_internal(const char* section, const char* name)
+{
+    auto db_opt = find_option(section, name);
+    if (db_opt)
+        db_opt->make_internal();
+}
+
 void
 GncOptionDB::commit()
 {
@@ -216,6 +224,16 @@ gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const ch
 
 }
 
+void
+gnc_register_font_option(const GncOptionDBPtr& db, const char* section,
+                         const char* name, const char* key,
+                         const char* doc_string, std::string value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::FONT};
+    db->register_option(section, std::move(option));
+}
+
 void
 gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
                            const char* name, const char* key,
@@ -226,6 +244,16 @@ gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
     db->register_option(section, std::move(option));
 }
 
+void
+gnc_register_color_option(const GncOptionDBPtr& db, const char* section,
+                         const char* name, const char* key,
+                         const char* doc_string, std::string value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::FONT};
+    db->register_option(section, std::move(option));
+}
+
 void
 gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section,
                               const char* name, const char* key,
@@ -236,6 +264,197 @@ gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section,
     db->register_option(section, std::move(option));
 }
 
+void
+gnc_register_simple_boolean_option(const GncOptionDBPtr& db,
+                                   const char* section, const char* name,
+                                   const char* key, const char* doc_string,
+                                   bool value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::INTERNAL};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_complex_boolean_option(const GncOptionDBPtr& db,
+                                    const char* section, const char* name,
+                                    const char* key, const char* doc_string,
+                                    bool value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::BOOLEAN};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
+                           const char* name, const char* key,
+                           const char* doc_string, std::string value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::PIXMAP};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_account_liat_option(const GncOptionDBPtr& db, const char* section,
+                                 const char* name, const char* key,
+                                 const char* doc_string,
+                                 std::vector<GncGUID> value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::ACCOUNT_LIST};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_acount_list_limited_option(const GncOptionDBPtr& db,
+                                        const char* section, const char* name,
+                                        const char* key, const char* doc_string,
+                                        std::vector<GncGUID> value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::ACCOUNT_LIST};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
+                                        const char* section, const char* name,
+                                        const char* key, const char* doc_string,
+                                        std::vector<GncGUID> value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::ACCOUNT_SEL};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section,
+                                const char* name, const char* key,
+                                const char* doc_string,
+                                GncMultiChoiceOptionChoices&& value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::MULTICHOICE};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
+                         const char* name, const char* key,
+                         const char* doc_string,
+                         GncMultiChoiceOptionChoices&& value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::LIST};
+    db->register_option(section, std::move(option));
+}
+
+/* Only balance-forecast.scm, hello-world.scm, and net-charts.scm
+ * use decimals and fractional steps and they can be worked around. */
+void
+gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section,
+                                 const char* name, const char* key,
+                                 const char* doc_string, int value, int min,
+                                 int max, int step)
+{
+    GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
+                value, min, max, step}};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_number_plot_size_option(const GncOptionDBPtr& db,
+                                     const char* section, const char* name,
+                                     const char* key, const char* doc_string,
+                                     int value)
+{
+    GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
+                value, 100, 20000, 5}};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
+                          const char* name, const char* key,
+                          const char* doc_string, QofQuery* value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::QUERY};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_internal_option(const GncOptionDBPtr& db, const char* section,
+                             const char* name, const char* key,
+                             const char* doc_string, std::string value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::INTERNAL};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section,
+                            const char* name, const char* key,
+                            const char* doc_string, GncInvoice* value)
+{
+    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+            GncOptionUIType::INVOICE};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_owner_option(const GncOptionDBPtr& db, const char* section,
+                          const char* name, const char* key,
+                          const char* doc_string, GncOwner* value)
+{
+    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+            GncOptionUIType::OWNER};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section,
+                             const char* name, const char* key,
+                             const char* doc_string, GncTaxTable* value)
+{
+    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+            GncOptionUIType::TAX_TABLE};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_counter_option(const GncOptionDBPtr& db, const char* section,
+                            const char* name, const char* key,
+                            const char* doc_string, int value)
+{
+    GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
+                value, 0, 999999999, 1}};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_counter_format_option(const GncOptionDBPtr& db,
+                                   const char* section, const char* name,
+                                   const char* key, const char* doc_string,
+                                   std::string value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::STRING};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_dateformat_option(const GncOptionDBPtr& db, const char* section,
+                               const char* name, const char* key,
+                               const char* doc_string, std::string value)
+{
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::DATE_FORMAT};
+    db->register_option(section, std::move(option));
+}
 
 void
 gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index c81fd1cb1..3b723dd26 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -27,6 +27,12 @@
 #include "gnc-option.hpp"
 #include <functional>
 #include <boost/optional.hpp>
+extern "C"
+{
+#include <gncInvoice.h>
+#include <gncOwner.h>
+#include <gncTaxTable.h>
+}
 
 class GncOptionDB;
 
@@ -58,6 +64,7 @@ public:
                                             const char* name);
     bool set_option(const char* section, const char* name, SCM value);
     void set_selectable(const char* section, const char* name);
+    void make_internal(const char* section, const char* name);
     void commit();
 private:
     boost::optional<GncOptionSection&> find_section(const char* section);
@@ -89,19 +96,172 @@ void gnc_register_text_option(const GncOptionDBPtr& db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, std::string value);
 
+void gnc_register_font_option(const GncOptionDBPtr& db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string, std::string value);
+
 void gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, GncBudget* value);
 
-void gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section,
-                                   const char* name, const char* key,
-                                   const char* doc_string,
+void gnc_register_commodity_option(const GncOptionDBPtr& db,
+                                   const char* section, const char* name,
+                                   const char* key, const char* doc_string,
                                    gnc_commodity* value);
 
+/* Complex boolean options are the same as simple boolean options with the
+ * addition of two function arguments. (If both of them are #f, you have exactly
+ * a simple-boolean-option.) Both functions should expect one boolean argument.
+ * When the option's value is changed, the function option-widget-changed-cb
+ * will be called with the new option value at the time that the GUI widget
+ * representing the option is changed, and the function
+ * setter-function-called-cb will be called when the option's setter is called
+ * (that is, when the user selects "OK" or "Apply").
+
+ * The option-widget-changed-cb is tested for procedurehood before it is called,
+ * so it is not validated to be a procedure here. However, since there could be
+ * an option-widget-changed-cb but not a setter-function-called-cb, the
+ * procedurehood of the setter-function-called-cb is checked here.
+ */
+void gnc_register_simple_boolean_option(const GncOptionDBPtr& db,
+                                        const char* section, const char* name,
+                                        const char* key, const char* doc_string,
+                                        bool value);
+
+void gnc_register_complex_boolean_option(const GncOptionDBPtr& db,
+                                         const char* section, const char* name,
+                                         const char* key,
+                                         const char* doc_string,
+                                         bool value);
+
+void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section,
+                                const char* name, const char* key,
+                                const char* doc_string, std::string value);
+
+/* account-list options use the option-data as a pair; the car is a boolean
+ * value, the cdr is a list of account-types. If the boolean is true, the gui
+ * should allow the user to select multiple accounts. If the cdr is an empty
+ * list, then all account types are shown. Internally, values are always a list
+ * of guids. Externally, both guids and account pointers may be used to set the
+ * value of the option. The option always returns a list of account pointers.
+ */
+void gnc_register_acount_list_limited_option(const GncOptionDBPtr& db,
+                                             const char* section,
+                                             const char* name, const char* key,
+                                             const char* doc_string,
+                                             std::vector<GncGUID> value);
+
+/* Just like gnc:make-account-list-limited-option except it does not limit the
+ * types of accounts that are available to the user.
+ */
+void gnc_register_account_liat_option(const GncOptionDBPtr& db,
+                                      const char* section,
+                                      const char* name, const char* key,
+                                      const char* doc_string,
+                                      std::vector<GncGUID> value);
+
+/* account-sel options use the option-data as a pair; the car is ignored, the
+ * cdr is a list of account-types. If the cdr is an empty list, then all account
+ * types are shown.  Internally, the value is always a guid.  Externally, both
+ * guids and account pointers may be used to set the value of the option. The
+ * option always returns the "current" account pointer.
+ */
+void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db,
+                                             const char* section,
+                                             const char* name, const char* key,
+                                             const char* doc_string,
+                                             std::vector<GncGUID> value);
+
+/* Multichoice options use the option-data as a list of vectors. Each vector
+ * contains a permissible value (scheme symbol), a name, and a description
+ * string.
+ *
+ * The multichoice-option with callback function is the same as the usual
+ * multichoice options (see above), with the addition of two function
+ * arguments. (If both of them are #f, you have exactly a multichoice-option.)
+ * Both functions should expect one argument. When the option's value is
+ * changed, the function option-widget-changed-cb will be called with the new
+ * option value at the time that the GUI widget representing the option is
+ * changed, and the function setter-function-called-cb will be called when the
+ * option's setter is called (that is, when the user selects "OK" or "Apply").
+ */
+
+void gnc_register_multichoice_option(const GncOptionDBPtr& db,
+                                     const char* section, const char* name,
+                                     const char* key, const char* doc_string,
+                                     GncMultiChoiceOptionChoices&& value);
+
+/* List options use the option-data in the same way as multichoice options. List
+ * options allow the user to select more than one option.
+ */
+void gnc_register_list_option(const GncOptionDBPtr& db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string,
+                              GncMultiChoiceOptionChoices&& value);
+
+/* Number range options use the option-data as a list whose elements are:
+ * (lower-bound upper-bound step-size). 
+*/
+void gnc_register_number_range_option(const GncOptionDBPtr& db,
+                                      const char* section, const char* name,
+                                      const char* key, const char* doc_string,
+                                      int value, int min, int max, int step);
+
+/* Number plot size options are a convenience wrapper on number range options
+ * with fixed min, max, and step.
+*/
+void gnc_register_number_plot_size_option(const GncOptionDBPtr& db,
+                                          const char* section, const char* name,
+                                          const char* key,
+                                          const char* doc_string,
+                                          int value);
+
+void gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
+                               const char* name, const char* key,
+                               const char* doc_string, QofQuery* value);
+
+/* Color options store rgba values in a list. The option-data is a list, whose
+ * first element is the range of possible rgba values and whose second element
+ * is a boolean indicating whether to use alpha transparency.
+ */
+void gnc_register_color_option(const GncOptionDBPtr& db, const char* section,
+                               const char* name, const char* key,
+                               const char* doc_string, std::string value);
+
+void gnc_register_internal_option(const GncOptionDBPtr& db, const char* section,
+                                  const char* name, const char* key,
+                                  const char* doc_string, std::string value);
+
+
 void gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, gnc_commodity* value);
 
+void gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section,
+                                 const char* name, const char* key,
+                                 const char* doc_string, GncInvoice* value);
+
+void gnc_register_owner_option(const GncOptionDBPtr& db, const char* section,
+                               const char* name, const char* key,
+                               const char* doc_string, GncOwner* value);
+
+void gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section,
+                                  const char* name, const char* key,
+                                  const char* doc_string, GncTaxTable* value);
+
+void gnc_register_counter_option(const GncOptionDBPtr& db, const char* section,
+                                 const char* name, const char* key,
+                                 const char* doc_string, int value);
+
+void gnc_register_counter_format_option(const GncOptionDBPtr& db,
+                                        const char* section, const char* name,
+                                        const char* key, const char* doc_string,
+                                        std::string value);
+
+void gnc_register_dateformat_option(const GncOptionDBPtr& db,
+                                    const char* section, const char* name,
+                                    const char* key, const char* doc_string,
+                                    std::string value);
 
 
 #endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 88d690f54..ade5c73e2 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -21,7 +21,7 @@ namespace std {
      void reset (pointer __p=pointer());
      void swap (unique_ptr &__u);
      pointer get () const;
-     operator bool () const;
+//     operator bool () const;
 
      ~unique_ptr();
   };
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index 8dffa8afa..6c3a51889 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -219,7 +219,7 @@ TEST_F(GncOptionUI, test_option_ui_type)
     EXPECT_EQ(GncOptionUIType::STRING, m_option.get_ui_type());
 }
 
-TEST_F(GncOptionUI, test_set_option_ui_element)
+TEST_F(GncOptionUI, test_set_option_ui_item)
 {
     GncUIItem ui_item;
     m_option.set_ui_item(&ui_item);

commit 0a13b4c51868e0995217dec9783aa1a6f966f43c
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 29 12:12:32 2019 -0700

    Fix up the SWIG wrapper for GncOptionDBPtr.
    
    Thanks to Flexo at stackoverflow for
    https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig
    5 years later, why isn't this in SWIG yet?

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index 01689ef04..88d690f54 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -1,7 +1,42 @@
 /*
  * Temporary swig interface file while developing C++ options.
+ *
+ * unique_ptr SWIG wrapper from https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig
  */
 
+namespace std {
+  %feature("novaluewrapper") unique_ptr;
+  template <typename Type>
+  struct unique_ptr {
+     typedef Type* pointer;
+
+     explicit unique_ptr( pointer Ptr );
+     unique_ptr (unique_ptr&& Right);
+     template<class Type2, Class Del2> unique_ptr( unique_ptr<Type2, Del2>&& Right );
+     unique_ptr( const unique_ptr& Right) = delete;
+
+
+     pointer operator-> () const;
+     pointer release ();
+     void reset (pointer __p=pointer());
+     void swap (unique_ptr &__u);
+     pointer get () const;
+     operator bool () const;
+
+     ~unique_ptr();
+  };
+}
+
+%define wrap_unique_ptr(Name, Type)
+  %template(Name) std::unique_ptr<Type>;
+  %newobject std::unique_ptr<Type>::release;
+
+  %typemap(out) std::unique_ptr<Type> %{
+    $result = SWIG_NewPointerObj(new $1_ltype(std::move($1)), $&1_descriptor, SWIG_POINTER_OWN);
+  %}
+
+%enddef
+
 %module sw_gnc_optiondb
 %{
 #include <libguile.h>
@@ -15,4 +50,6 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
 %ignore OptionUIItem;
 %ignore GncOption;
 
+wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
+
 %include "gnc-optiondb.hpp"
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
index 4d26a803f..f46b4303e 100644
--- a/libgnucash/app-utils/test/test-gnc-optiondb.scm
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -37,10 +37,9 @@
 
 (define (test-gnc-make-text-option)
   (test-begin "test-gnc-test-string-option")
-  (let* ((option-db (new-GncOptionDB))
+  (let* ((option-db (gnc-option-db-new))
          (string-opt (gnc-register-string-option option-db "foo" "bar" "baz"
                                                  "Phony Option" "waldo")))
-    (test-equal (GncOptionDB-lookup-option option-db "foo" "bar") "waldo")
-    (delete-GncOptionDB option-db))
+    (test-equal (GncOptionDB-lookup-option (GncOptionDBPtr-get option-db) "foo" "bar") "waldo"))
 
   (test-end "test-gnc-make-string-option"))

commit 4146251cc79272df458cbfc509eb2ab5bdc89bc6
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 29 10:28:04 2019 -0700

    Add GncOptionUIItem manipulation to GncOptionDB.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index d4559374d..b7cb5ae74 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -78,6 +78,50 @@ GncOptionDB::get_default_section() const noexcept
         return &(m_default_section.get());
     return nullptr;
 }
+
+void
+GncOptionDB::set_ui_item(const char* section, const char* name, void* ui_item)
+{
+    auto option = find_option(section, name);
+    if (!option) return;
+    option->set_ui_item(ui_item);
+}
+
+void* const
+GncOptionDB::get_ui_item(const char* section, const char* name)
+{
+    auto option = find_option(section, name);
+    if (!option) return nullptr;
+    return option->get_ui_item();
+}
+
+GncOptionUIType
+GncOptionDB::get_ui_type(const char* section, const char* name)
+{
+    auto option = find_option(section, name);
+    if (!option) return GncOptionUIType::INTERNAL;
+    return option->get_ui_type();
+}
+
+void
+GncOptionDB::set_ui_from_option(const char* section, const char* name,
+                        std::function<void(GncOption&)> func)
+{
+    auto option = find_option(section, name);
+    if (!option) return;
+    func(option.get());
+}
+
+void
+GncOptionDB::set_option_from_ui(const char* section, const char* name,
+                        std::function<void(GncOption&)> func)
+{
+    auto option = find_option(section, name);
+    if (!option) return;
+    func(option.get());
+}
+
+
 boost::optional<GncOptionSection&>
 GncOptionDB::find_section(const char* section)
 {
@@ -145,14 +189,14 @@ GncOptionDB::commit()
 {
 }
 
-GncOptionDB*
+GncOptionDBPtr
 gnc_option_db_new(void)
 {
-    return new GncOptionDB;
+    return GncOptionDBPtr{new GncOptionDB};
 }
 
 void
-gnc_register_string_option(GncOptionDB* db, const char* section,
+gnc_register_string_option(const GncOptionDBPtr& db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, std::string value)
 {
@@ -162,7 +206,7 @@ gnc_register_string_option(GncOptionDB* db, const char* section,
 }
 
 void
-gnc_register_text_option(GncOptionDB* db, const char* section, const char* name,
+gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const char* name,
                          const char* key, const char* doc_string,
                          std::string value)
 {
@@ -173,7 +217,7 @@ gnc_register_text_option(GncOptionDB* db, const char* section, const char* name,
 }
 
 void
-gnc_register_budget_option(GncOptionDB* db, const char* section,
+gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, GncBudget *value)
 {
@@ -183,7 +227,7 @@ gnc_register_budget_option(GncOptionDB* db, const char* section,
 }
 
 void
-gnc_register_commodity_option(GncOptionDB* db, const char* section,
+gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, gnc_commodity *value)
 {
@@ -194,7 +238,7 @@ gnc_register_commodity_option(GncOptionDB* db, const char* section,
 
 
 void
-gnc_register_currency_option(GncOptionDB* db, const char* section,
+gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
                              const char* name, const char* key,
                              const char* doc_string, gnc_commodity *value)
 {
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 293dc0f63..c81fd1cb1 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -46,6 +46,14 @@ public:
     void unregister_option(const char* section, const char* name);
     void set_default_section(const char* section);
     const GncOptionSection* const get_default_section() const noexcept;
+    void set_ui_item(const char* section, const char* name, void* ui_item);
+    void* const get_ui_item(const char* section, const char* name);
+    GncOptionUIType get_ui_type(const char* section, const char* name);
+    void set_ui_from_option(const char* section, const char* name,
+                            std::function<void(GncOption&)> func);
+    void set_option_from_ui(const char* section, const char* name,
+                            std::function<void(GncOption&)> func);
+    SCM lookup_option(const char* section, const char* name);
     std::string lookup_string_option(const char* section,
                                             const char* name);
     bool set_option(const char* section, const char* name, SCM value);
@@ -78,8 +86,8 @@ void gnc_register_string_option(const GncOptionDBPtr& db, const char* section,
                                 const char* doc_string, std::string value);
 
 void gnc_register_text_option(const GncOptionDBPtr& db, const char* section,
-                                const char* name, const char* key,
-                                const char* doc_string, std::string value);
+                              const char* name, const char* key,
+                              const char* doc_string, std::string value);
 
 void gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index e65629ee2..86a8390e6 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -68,3 +68,62 @@ TEST_F(GncOptionDBTest, test_register_string_option)
                                std::string{"waldo"});
     EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
 }
+
+class GncUIType
+{
+public:
+    void set_value(const std::string& value) { m_value = value; }
+    const std::string& get_value() const { return m_value; }
+private:
+    std::string m_value;
+};
+
+class GncOptionDBUITest : public ::testing::Test
+{
+protected:
+    GncOptionDBUITest() : m_db{gnc_option_db_new()}
+    {
+        gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
+                                   std::string{"waldo"});
+        gnc_register_text_option(m_db, "foo", "sausage", "links",
+                                 "Phony Option", std::string{"waldo"});
+        gnc_register_string_option(m_db, "qux", "grault", "baz", "Phony Option",
+                                   std::string{""});
+        gnc_register_text_option(m_db, "qux", "garply", "fred",
+                                   "Phony Option", std::string{"waldo"});
+    }
+
+    GncOptionDBPtr m_db;
+};
+
+TEST_F(GncOptionDBUITest, test_set_ui_item)
+{
+    GncUIType entry;
+    m_db->set_ui_item("foo", "bar", &entry);
+    EXPECT_EQ(&entry, static_cast<GncUIType*>(m_db->get_ui_item("foo", "bar")));
+}
+
+TEST_F(GncOptionDBUITest, test_ui_value_from_option)
+{
+    GncUIType entry;
+    const char* value{"waldo"};
+    m_db->set_ui_item("foo", "bar", &entry);
+    m_db->set_ui_from_option("foo", "bar", [](GncOption& option){
+            auto ui_item = static_cast<GncUIType* const>(option.get_ui_item());
+            ui_item->set_value(option.get_value<std::string>());
+        });
+    EXPECT_STREQ(value, entry.get_value().c_str());
+}
+
+TEST_F(GncOptionDBUITest, test_option_value_from_ui)
+{
+    GncUIType entry;
+    const char* value{"pepper"};
+    m_db->set_ui_item("foo", "bar", &entry);
+    entry.set_value(value);
+    m_db->set_option_from_ui("foo", "bar", [](GncOption& option){
+            auto ui_item = static_cast<GncUIType* const>(option.get_ui_item());
+            option.set_value(ui_item->get_value());
+        });
+    EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str());
+}

commit 6ccb9dbb9e04e251892fe2fd46441e3f7eb4b932
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 29 10:24:24 2019 -0700

    Use a std::unique_ptr<GncOptionDB> instead of a raw ptr.
    
    Passing references to it to the gnc_register_option functions.
    
    Not tested yet with SWIG, might not work.
    
    Includes introducing fixtures to gtest-gnc-optiondb.cpp.

diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 72dfe0ed0..293dc0f63 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -63,6 +63,7 @@ private:
     std::function<void(void*)> m_set_ui_value;
 };
 
+using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
 /**
  * Create an empty option database.
  *
@@ -70,33 +71,29 @@ private:
  * that for Guile.
  * @return A newly allocated GncOptionDB. Use delete to destroy it.
  */
-GncOptionDB *gnc_option_db_new(void);
+GncOptionDBPtr gnc_option_db_new(void);
 
-void gnc_register_string_option(GncOptionDB* db, const char* section,
+void gnc_register_string_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, std::string value);
 
-
-void gnc_register_string_option(GncOptionDB* db, const char* section,
+void gnc_register_text_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, std::string value);
 
-void gnc_register_text_option(GncOptionDB* db, const char* section,
-                              const char* name, const char* key,
-                              const char* doc_string, std::string value);
-
-void gnc_register_budget_option(GncOptionDB* db, const char* section,
+void gnc_register_budget_option(const GncOptionDBPtr& db, const char* section,
                                 const char* name, const char* key,
                                 const char* doc_string, GncBudget* value);
 
-void gnc_register_commodity_option(GncOptionDB* db, const char* section,
+void gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section,
                                    const char* name, const char* key,
                                    const char* doc_string,
                                    gnc_commodity* value);
 
-void gnc_register_currency_option(GncOptionDB* db, const char* section,
+void gnc_register_currency_option(const GncOptionDBPtr& db, const char* section,
                                   const char* name, const char* key,
                                   const char* doc_string, gnc_commodity* value);
 
 
+
 #endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index a09659fde..e65629ee2 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -24,44 +24,47 @@
 #include <gtest/gtest.h>
 #include <gnc-optiondb.hpp>
 
-TEST(GncOptionDB, test_ctor)
+class GncOptionDBTest : public ::testing::Test
+{
+protected:
+    GncOptionDBTest() : m_db{gnc_option_db_new()} {}
+
+    GncOptionDBPtr m_db;
+};
+
+TEST_F(GncOptionDBTest, test_ctor)
 {
     EXPECT_NO_THROW ({ GncOptionDB optiondb; });
 }
 
-TEST(GncOptionDB, test_register_option)
+TEST_F(GncOptionDBTest, test_register_option)
 {
-    GncOptionDB optiondb;
     GncOption option1{"foo", "bar", "baz", "Phony Option",
                       std::string{"waldo"}};
-    optiondb.register_option("foo", std::move(option1));
-    EXPECT_EQ(optiondb.num_sections(), 1);
+    m_db->register_option("foo", std::move(option1));
+    EXPECT_EQ(m_db->num_sections(), 1);
 }
 
-TEST(GncOptionDB, test_lookup_string_option)
+TEST_F(GncOptionDBTest, test_lookup_string_option)
 {
-    GncOptionDB optiondb;
     GncOption option1{"foo", "bar", "baz", "Phony Option",
                       std::string{"waldo"}};
-    optiondb.register_option("foo", std::move(option1));
-    EXPECT_STREQ("waldo", optiondb.lookup_string_option("foo", "bar").c_str());
+    m_db->register_option("foo", std::move(option1));
+    EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
 }
 
-TEST(GncOptionDB, test_unregister_option)
+TEST_F(GncOptionDBTest, test_unregister_option)
 {
-    GncOptionDB optiondb;
     GncOption option1{"foo", "bar", "baz", "Phony Option",
                       std::string{"waldo"}};
-    optiondb.register_option("foo", std::move(option1));
-    optiondb.unregister_option("foo", "bar");
-    EXPECT_TRUE(optiondb.lookup_string_option("foo", "bar").empty());
+    m_db->register_option("foo", std::move(option1));
+    m_db->unregister_option("foo", "bar");
+    EXPECT_TRUE(m_db->lookup_string_option("foo", "bar").empty());
 }
 
-TEST(GncOptionDB, test_register_string_option)
+TEST_F(GncOptionDBTest, test_register_string_option)
 {
-    GncOptionDB* db = gnc_option_db_new();
-    gnc_register_string_option(db, "foo", "bar", "baz", "Phony Option",
+    gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
                                std::string{"waldo"});
-    EXPECT_STREQ("waldo", db->lookup_string_option("foo", "bar").c_str());
-    delete db;
+    EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str());
 }

commit 94628097e439e53a86d4df216afff6e4ea2741b9
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 29 10:11:48 2019 -0700

    Use GncOptionUIType parameters in gnc_register_option functions.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 4b922363b..d4559374d 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -156,7 +156,8 @@ gnc_register_string_option(GncOptionDB* db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, std::string value)
 {
-    GncOption option{section, name, key, doc_string, value};
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::STRING};
     db->register_option(section, std::move(option));
 }
 
@@ -165,7 +166,10 @@ gnc_register_text_option(GncOptionDB* db, const char* section, const char* name,
                          const char* key, const char* doc_string,
                          std::string value)
 {
-    gnc_register_string_option(db, section, name, key, doc_string, value);
+    GncOption option{section, name, key, doc_string, value,
+            GncOptionUIType::TEXT};
+    db->register_option(section, std::move(option));
+
 }
 
 void
@@ -173,7 +177,8 @@ gnc_register_budget_option(GncOptionDB* db, const char* section,
                            const char* name, const char* key,
                            const char* doc_string, GncBudget *value)
 {
-    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value)};
+    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+            GncOptionUIType::BUDGET};
     db->register_option(section, std::move(option));
 }
 
@@ -182,7 +187,8 @@ gnc_register_commodity_option(GncOptionDB* db, const char* section,
                               const char* name, const char* key,
                               const char* doc_string, gnc_commodity *value)
 {
-    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value)};
+    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
+            GncOptionUIType::COMMODITY};
     db->register_option(section, std::move(option));
 }
 

commit 3769a356d55d918f22a0a2aac556d82415374676
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 29 10:06:00 2019 -0700

    Extract functions find_section and find_option using boost::optional
    
    to handle not-found condition.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 733691fc2..4b922363b 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -23,7 +23,7 @@
 
 #include "gnc-optiondb.hpp"
 
-GncOptionDB::GncOptionDB() : m_default_section{nullptr} {}
+GncOptionDB::GncOptionDB() : m_default_section{boost::none} {}
 
 GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
 
@@ -35,32 +35,25 @@ GncOptionDB::save_to_book(QofBook* book, bool do_clear) const
 void
 GncOptionDB::register_option(const char* section, GncOption&& option)
 {
-    auto db_section = std::find_if(
-        m_sections.begin(), m_sections.end(),
-        [section](GncOptionSection sect) -> bool
-        {
-            return sect.first == std::string{section};
-        });
+    auto db_section = find_section(section);
 
-    if (db_section == m_sections.end())
+    if (db_section)
     {
-        m_sections.emplace_back(std::make_pair(std::string{section},
-                                               GncOptionVec{}));
-        db_section = std::prev(m_sections.end());
+        db_section->second.emplace_back(std::move(option));
+        return;
     }
-    db_section->second.emplace_back(std::move(option));
+
+    m_sections.emplace_back(std::make_pair(std::string{section},
+                                               GncOptionVec{}));
+    auto new_section = std::prev(m_sections.end());
+    new_section->second.emplace_back(std::move(option));
 }
 
 void
 GncOptionDB::unregister_option(const char* section, const char* name)
 {
-    auto db_section = std::find_if(
-        m_sections.begin(), m_sections.end(),
-        [section](GncOptionSection sect) -> bool
-        {
-            return sect.first == std::string{section};
-        });
-    if (db_section != m_sections.end())
+    auto db_section = find_section(section);
+    if (db_section)
     {
         db_section->second.erase(
             std::remove_if(
@@ -75,10 +68,18 @@ GncOptionDB::unregister_option(const char* section, const char* name)
 void
 GncOptionDB::set_default_section(const char* section)
 {
+    m_default_section = find_section(section);
 }
 
-SCM
-GncOptionDB::lookup_option(const char* section, const char* name) const
+const GncOptionSection* const
+GncOptionDB::get_default_section() const noexcept
+{
+    if (m_default_section)
+        return &(m_default_section.get());
+    return nullptr;
+}
+boost::optional<GncOptionSection&>
+GncOptionDB::find_section(const char* section)
 {
     auto db_section = std::find_if(
         m_sections.begin(), m_sections.end(),
@@ -87,37 +88,43 @@ GncOptionDB::lookup_option(const char* section, const char* name) const
             return sect.first == std::string{section};
         });
     if (db_section == m_sections.end())
-        return SCM_BOOL_F;
+        return boost::none;
+    return *db_section;
+}
+
+boost::optional<GncOption&>
+GncOptionDB::find_option(const char* section, const char* name)
+{
+    auto db_section = find_section(section);
+    if (!db_section)
+        return boost::none;
     auto db_opt = std::find_if(
         db_section->second.begin(), db_section->second.end(),
-        [name](const GncOption& option) -> bool
+        [name](GncOption& option) -> bool
         {
             return option.get_name() == std::string{name};
         });
     if (db_opt == db_section->second.end())
+        return boost::none;
+    return *db_opt;
+}
+
+SCM
+GncOptionDB::lookup_option(const char* section, const char* name)
+{
+    auto db_opt = find_option(section, name);
+    if (!db_opt)
         return SCM_BOOL_F;
     return db_opt->get_scm_value();
 }
 
-static const std::string empty_string;
 std::string
-GncOptionDB::lookup_string_option(const char* section, const char* name) const
+GncOptionDB::lookup_string_option(const char* section, const char* name)
 {
-    auto db_section = std::find_if(
-        m_sections.begin(), m_sections.end(),
-        [section](GncOptionSection sect) -> bool
-        {
-            return sect.first == std::string{section};
-        });
-    if (db_section == m_sections.end())
-        return empty_string;
-    auto db_opt = std::find_if(
-        db_section->second.begin(), db_section->second.end(),
-        [name](const GncOption& option) -> bool
-        {
-            return option.get_name() == std::string{name};
-        });
-    if (db_opt == db_section->second.end())
+    static const std::string empty_string{};
+
+    auto db_opt = find_option(section, name);
+    if (!db_opt)
         return empty_string;
     return db_opt->get_value<std::string>();
 }
@@ -191,7 +198,8 @@ gnc_register_currency_option(GncOptionDB* db, const char* section,
             {
                 return GNC_IS_COMMODITY (new_value) &&
                     gnc_commodity_is_currency(GNC_COMMODITY(new_value));
-            }
+            },
+            GncOptionUIType::CURRENCY
         }};
     db->register_option(section, std::move(option));
 }
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 9fb25bd09..72dfe0ed0 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -25,12 +25,13 @@
 #define GNC_OPTIONDB_HPP_
 
 #include "gnc-option.hpp"
+#include <functional>
+#include <boost/optional.hpp>
 
 class GncOptionDB;
 
 using GncOptionVec = std::vector<GncOption>;
 using GncOptionSection = std::pair<std::string, GncOptionVec>;
-using GncOptionSectionPtr = std::shared_ptr<const GncOptionSection>;
 class GncOptionDB
 {
 public:
@@ -44,18 +45,17 @@ public:
     void register_option(const char* section, GncOption&& option);
     void unregister_option(const char* section, const char* name);
     void set_default_section(const char* section);
-    const GncOptionSectionPtr get_default_section() const noexcept
-    {
-        return m_default_section;
-    }
-    SCM lookup_option(const char* section, const char* name) const;
+    const GncOptionSection* const get_default_section() const noexcept;
     std::string lookup_string_option(const char* section,
-                                            const char* name) const;
+                                            const char* name);
     bool set_option(const char* section, const char* name, SCM value);
     void set_selectable(const char* section, const char* name);
     void commit();
 private:
-    GncOptionSectionPtr m_default_section;
+    boost::optional<GncOptionSection&> find_section(const char* section);
+    boost::optional<GncOption&> find_option(const char* section, const char* name);
+
+    boost::optional<GncOptionSection&> m_default_section;
     std::vector<GncOptionSection> m_sections;
     bool m_dirty = false;
 

commit 6deedd441f9fadd6250ee4d1793a4bb05c4b6797
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 29 09:58:07 2019 -0700

    Make the ptr returned by GncOption::get_ui_item() const
    
    but not the GtkWidget it points to.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 326038975..115b43b58 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -136,7 +136,7 @@ class OptionUIItem
 {
 public:
      GncOptionUIType get_ui_type() { return m_ui_type; }
-    const void* get_ui_item() {return m_ui_item; }
+    void* const get_ui_item() {return m_ui_item; }
     void set_ui_item(void* ui_item)
     {
         if (m_ui_type == GncOptionUIType::INTERNAL)
@@ -307,7 +307,7 @@ public:
     {
         return boost::apply_visitor(GetUITypeVisitor(), m_option);
     }
-    const void* get_ui_item()
+    void* const get_ui_item()
     {
         return boost::apply_visitor(GetUIItemVisitor(), m_option);
     }
@@ -418,10 +418,10 @@ private:
         }
     };
     struct GetUIItemVisitor :
-        public boost::static_visitor<const void*>
+        public boost::static_visitor<void* const >
     {
         template <class OptionType>
-        const void* operator()(OptionType& option) const {
+        void* const operator()(OptionType& option) const {
             return option.get_ui_item();
         }
     };

commit 40361ec854e71220964831356b66ba8c4fb7e304
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Sep 29 09:56:18 2019 -0700

    gnc-opption.hpp needs to include <functional>.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index d8400084b..326038975 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -34,6 +34,7 @@ extern "C"
 #include <libguile.h>
 #include <string>
 #include <exception>
+#include <functional>
 #include <boost/variant.hpp>
 
 /*

commit 41ef2c5d44ba8a9667fb9393cd89945c22f432c3
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Sep 20 13:56:54 2019 -0700

    Add OptionUIItem composition class to GncOption.
    
    Provides a type and a raw pointer member with accessors. The type is one of
    enum GncOptionIUType and is either VOID (for internal options that don't
    get UI items) or one of the widget types specified in dialog-option.c or
    business-options-gnome.c.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 6752c8562..d8400084b 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -33,6 +33,7 @@ extern "C"
 }
 #include <libguile.h>
 #include <string>
+#include <exception>
 #include <boost/variant.hpp>
 
 /*
@@ -81,6 +82,34 @@ protected:
 };
 */
 
+enum GncOptionUIType
+{
+    INTERNAL,
+    BOOLEAN,
+    STRING,
+    TEXT,
+    CURRENCY,
+    COMMODITY,
+    MULTICHOICE,
+    DATE,
+    ACCOUNT_LIST,
+    ACCOUNT_SEL,
+    LIST,
+    NUMBER_RANGE,
+    COLOR,
+    FONT,
+    BUDGET,
+    PIXMAP,
+    RADIOBUTTON,
+    DATE_FORMAT,
+    OWNER,
+    CUSTOMER,
+    VENDOR,
+    EMPLOYEE,
+    INVOICE,
+    TAX_TABLE
+};
+
 struct OptionClassifier
 {
     std::string m_section;
@@ -90,18 +119,59 @@ struct OptionClassifier
     std::string m_doc_string;
 };
 
+/**
+ * Holds a pointer to the UI item which will control the option and an enum
+ * representing the type of the option for dispatch purposes; all of that
+ * happens in gnucash/gnome-utils/dialog-options and
+ * gnucash/gnome/business-option-gnome.
+ *
+ * This class takes no ownership responsibility, so calling code is responsible
+ * for ensuring that the UI_Item is alive. For convenience the public
+ * clear_ui_item function can be used as a weak_ptr's destruction callback to
+ * ensure that if the ui_item is destroyed elsewhere the ptr will be nulled and
+ * the type reset to OptionUIType::INTERNAL.
+ */
+class OptionUIItem
+{
+public:
+     GncOptionUIType get_ui_type() { return m_ui_type; }
+    const void* get_ui_item() {return m_ui_item; }
+    void set_ui_item(void* ui_item)
+    {
+        if (m_ui_type == GncOptionUIType::INTERNAL)
+        {
+            std::string error{"Can't set ui item with void ui type."};
+            throw std::logic_error(std::move(error));
+        }
+        m_ui_item = ui_item;
+    }
+protected:
+    OptionUIItem(GncOptionUIType ui_type) :
+        m_ui_item{nullptr}, m_ui_type{ui_type} {}
+    OptionUIItem(const OptionUIItem&) = default;
+    OptionUIItem(OptionUIItem&&) = default;
+    ~OptionUIItem() = default;
+    OptionUIItem& operator=(const OptionUIItem&) = default;
+    OptionUIItem& operator=(OptionUIItem&&) = default;
+private:
+    void* m_ui_item;
+    GncOptionUIType m_ui_type;
+};
+
 template <typename ValueType>
 SCM scm_from_value(ValueType);
 
 template <typename ValueType>
 class GncOptionValue :
-    public OptionClassifier
+    public OptionClassifier, public OptionUIItem
 {
 public:
     GncOptionValue<ValueType>(const char* section, const char* name,
                               const char* key, const char* doc_string,
-                              ValueType value) :
+                              ValueType value,
+                              GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
         OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(ui_type),
         m_value{value}, m_default_value{value} {}
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
@@ -121,14 +191,17 @@ private:
 
 template <typename ValueType>
 class GncOptionValidatedValue :
-    public OptionClassifier
+    public OptionClassifier, public OptionUIItem
 {
 public:
     GncOptionValidatedValue<ValueType>(const char* section, const char* name,
                                        const char* key, const char* doc_string,
                                        ValueType value,
-                                      std::function<bool(ValueType)>validator) :
+                                       std::function<bool(ValueType)>validator,
+                                       GncOptionUIType ui_type = GncOptionUIType::INTERNAL
+        ) :
         OptionClassifier{section, name, key, doc_string},
+        OptionUIItem(ui_type),
         m_value{value}, m_default_value{value}, m_validator{validator}
         {
             if (!this->validate(value))
@@ -184,10 +257,10 @@ public:
     template <typename ValueType>
     GncOption(const char* section, const char* name,
               const char* key, const char* doc_string,
-              ValueType value) :
+              ValueType value,
+              GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
         m_option{GncOptionValue<ValueType> {
-            section, name, key, doc_string, value
-                }} {}
+            section, name, key, doc_string, value, ui_type}} {}
 
     template <typename ValueType> ValueType get_value() const
     {
@@ -225,6 +298,18 @@ public:
     {
         return boost::apply_visitor(GetDocstringVisitor(), m_option);
     }
+    void set_ui_item(void* ui_elem)
+    {
+        return boost::apply_visitor(SetUIItemVisitor(ui_elem), m_option);
+    }
+    const GncOptionUIType get_ui_type()
+    {
+        return boost::apply_visitor(GetUITypeVisitor(), m_option);
+    }
+    const void* get_ui_item()
+    {
+        return boost::apply_visitor(GetUIItemVisitor(), m_option);
+    }
 private:
     template <typename ValueType>
     struct GetValueVisitor : public boost::static_visitor<ValueType>
@@ -313,6 +398,33 @@ private:
             return option.m_doc_string;
         }
     };
+    struct SetUIItemVisitor : public boost::static_visitor<>
+    {
+        SetUIItemVisitor(void* ui_item) : m_ui_item{ui_item} {}
+        template <class OptionType>
+        void operator()(OptionType& option) const {
+            option.set_ui_item(m_ui_item);
+        }
+    private:
+        void* m_ui_item;
+    };
+    struct GetUITypeVisitor :
+        public boost::static_visitor<GncOptionUIType>
+    {
+        template <class OptionType>
+        const GncOptionUIType operator()(OptionType& option) const {
+            return option.get_ui_type();
+        }
+    };
+    struct GetUIItemVisitor :
+        public boost::static_visitor<const void*>
+    {
+        template <class OptionType>
+        const void* operator()(OptionType& option) const {
+            return option.get_ui_item();
+        }
+    };
+
     GncOptionVariant m_option;
 };
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index bd1712094..8dffa8afa 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -192,3 +192,36 @@ TEST(GNCOption, test_currency_setter)
     gnc_commodity_table_destroy(table);
     qof_book_destroy(book);
 }
+
+class GncUIItem
+{
+public:
+    void set_value(const std::string& value) { m_value = value; }
+    const std::string& get_value() { return m_value; }
+private:
+    std::string m_value;
+};
+
+class GncOptionUITest : public ::testing::Test
+{
+protected:
+    GncOptionUITest() :
+        m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"},
+            GncOptionUIType::STRING} {}
+
+    GncOption m_option;
+};
+
+using GncOptionUI = GncOptionUITest;
+
+TEST_F(GncOptionUI, test_option_ui_type)
+{
+    EXPECT_EQ(GncOptionUIType::STRING, m_option.get_ui_type());
+}
+
+TEST_F(GncOptionUI, test_set_option_ui_element)
+{
+    GncUIItem ui_item;
+    m_option.set_ui_item(&ui_item);
+    EXPECT_EQ(&ui_item, static_cast<const GncUIItem*>(m_option.get_ui_item()));
+}

commit 083e5b93dfab92ded9487ab34d0c5adf83eb217c
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Sep 20 13:13:53 2019 -0700

    Replace the CRTP class GncOptionBase with direct calls in its former children.
    
    Benefit of CRTP too limited to accept the cost of understanding it.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 023f8b36e..6752c8562 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -93,42 +93,9 @@ struct OptionClassifier
 template <typename ValueType>
 SCM scm_from_value(ValueType);
 
-/* This design pattern is called the Curiously Recursive Template Pattern, or
- * CRTP. See https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
- * for a detailed explanation.
- */
-template <typename ValueType, class ValueClass>
-class GncOptionBase
-{
-public:
-    ValueType get_value() const
-    {
-        return static_cast<ValueClass const&>(*this).get_value();
-    }
-    void set_value(ValueType value)
-    {
-        static_cast<ValueClass&>(*this).set_value(value);
-    }
-    ValueType get_default_value() const
-    {
-        return static_cast<ValueClass const&>(*this).get_default_value();
-    }
-    SCM get_scm_value() const
-    {
-        ValueType value{static_cast<ValueClass const&>(*this).get_value()};
-        return scm_from_value<ValueType>(value);
-    }
-    SCM get_scm_default_value() const
-    {
-        ValueType value{static_cast<ValueClass const&>(*this).get_default_value()};
-        return scm_from_value<ValueType>(value);
-    }
-};
-
 template <typename ValueType>
 class GncOptionValue :
-    public OptionClassifier,
-    public GncOptionBase<ValueType, GncOptionValue<ValueType>>
+    public OptionClassifier
 {
 public:
     GncOptionValue<ValueType>(const char* section, const char* name,
@@ -138,6 +105,14 @@ public:
         m_value{value}, m_default_value{value} {}
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
+    SCM get_scm_value() const
+    {
+        return scm_from_value(m_value);
+    }
+    SCM get_scm_default_value() const
+    {
+        return scm_from_value(m_default_value);
+    }
     void set_value(ValueType new_value) { m_value = new_value; }
 private:
     ValueType m_value;
@@ -146,8 +121,7 @@ private:
 
 template <typename ValueType>
 class GncOptionValidatedValue :
-    public OptionClassifier,
-    public GncOptionBase<ValueType, GncOptionValidatedValue<ValueType>>
+    public OptionClassifier
 {
 public:
     GncOptionValidatedValue<ValueType>(const char* section, const char* name,
@@ -173,6 +147,14 @@ public:
     }
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
+    SCM get_scm_value() const
+    {
+        return scm_from_value(m_value);
+    }
+    SCM get_scm_default_value() const
+    {
+        return scm_from_value(m_default_value);
+    }
     bool validate(ValueType value) { return m_validator(value); }
     void set_value(ValueType value)
     {
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 9d43b1213..17de9412d 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -103,10 +103,15 @@ if (HAVE_SRFI64)
     ${MODULEPATH}
     ${CMAKE_SOURCE_DIR}/libgnucash/engine
     ${CMAKE_BINARY_DIR}/common # for config.h
+    ${GLIB2_INCLUDE_DIRS}
+    ${GUILE_INCLUDE_DIRS}
     )
 
   set(swig_gnc_optiondb_LIBS
-    gncmod-engine PkgConfig::GLIB2 PkgConfig::GUILE)
+    gncmod-engine
+    ${GLIB2_LDFLAGS}
+    ${GUILE_LDFLAGS}
+    )
 
   target_link_libraries(swig-gnc-optiondb ${swig_gnc_optiondb_LIBS})
   target_include_directories(swig-gnc-optiondb

commit cf0b1da4fa43699b233dd76516ff3971beb366bb
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Sep 20 12:40:43 2019 -0700

    Remove GncOptionWrapper.
    
    Move the GncOptions into the GncOptionDB. This works with tests but
    might not with real reports.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 641bd1194..733691fc2 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -48,8 +48,7 @@ GncOptionDB::register_option(const char* section, GncOption&& option)
                                                GncOptionVec{}));
         db_section = std::prev(m_sections.end());
     }
-    auto wrapper = std::make_shared<GncOptionWrapper>(option, nullptr);
-    db_section->second.emplace_back(wrapper);
+    db_section->second.emplace_back(std::move(option));
 }
 
 void
@@ -66,9 +65,9 @@ GncOptionDB::unregister_option(const char* section, const char* name)
         db_section->second.erase(
             std::remove_if(
                 db_section->second.begin(), db_section->second.end(),
-                [name](GncOptionWrapperPtr option) -> bool
+                [name](const GncOption& option) -> bool
                 {
-                    return option->m_option.get_name() == std::string{name};
+                    return option.get_name() == std::string{name};
                 }));
     }
 }
@@ -91,13 +90,13 @@ GncOptionDB::lookup_option(const char* section, const char* name) const
         return SCM_BOOL_F;
     auto db_opt = std::find_if(
         db_section->second.begin(), db_section->second.end(),
-        [name](GncOptionWrapperPtr option) -> bool
+        [name](const GncOption& option) -> bool
         {
-            return option->m_option.get_name() == std::string{name};
+            return option.get_name() == std::string{name};
         });
-    if (db_opt == db_section->second.end() || !*db_opt)
+    if (db_opt == db_section->second.end())
         return SCM_BOOL_F;
-    return (*db_opt)->m_option.get_scm_value();
+    return db_opt->get_scm_value();
 }
 
 static const std::string empty_string;
@@ -114,13 +113,13 @@ GncOptionDB::lookup_string_option(const char* section, const char* name) const
         return empty_string;
     auto db_opt = std::find_if(
         db_section->second.begin(), db_section->second.end(),
-        [name](GncOptionWrapperPtr option) -> bool
+        [name](const GncOption& option) -> bool
         {
-            return option->m_option.get_name() == std::string{name};
+            return option.get_name() == std::string{name};
         });
-    if (db_opt == db_section->second.end() || !*db_opt)
+    if (db_opt == db_section->second.end())
         return empty_string;
-    return (*db_opt)->m_option.get_value<std::string>();
+    return db_opt->get_value<std::string>();
 }
 
 bool
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 69503c22c..9fb25bd09 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -27,16 +27,8 @@
 #include "gnc-option.hpp"
 
 class GncOptionDB;
-struct GncOptionWrapper
-{
-    GncOptionWrapper(const GncOption& opt, void* ptr) : m_option{opt}, m_widget{ptr} {}
-    GncOptionWrapper(GncOption&& opt, void* ptr) : m_option{opt}, m_widget{ptr} {}
-    GncOption m_option;
-    void* m_widget; /* Don't want widget code in libgnucash! GObject closure?*/
-};
 
-using GncOptionWrapperPtr = std::shared_ptr<GncOptionWrapper>;
-using GncOptionVec = std::vector<GncOptionWrapperPtr>;
+using GncOptionVec = std::vector<GncOption>;
 using GncOptionSection = std::pair<std::string, GncOptionVec>;
 using GncOptionSectionPtr = std::shared_ptr<const GncOptionSection>;
 class GncOptionDB
diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
index d15c8598d..01689ef04 100644
--- a/libgnucash/app-utils/gnc-optiondb.i
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -12,7 +12,7 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
 %include <std_string.i>
 
 %ignore OptionClassifier;
+%ignore OptionUIItem;
 %ignore GncOption;
-%ignore GncOptionWrapper;
 
 %include "gnc-optiondb.hpp"

commit 2ee0edaa163d17d2f2e6bf99b4a246d9e7d6738e
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Sep 20 12:36:45 2019 -0700

    Use targets instead of variables for GncOption* tests.

diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 17de9412d..9d43b1213 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -103,15 +103,10 @@ if (HAVE_SRFI64)
     ${MODULEPATH}
     ${CMAKE_SOURCE_DIR}/libgnucash/engine
     ${CMAKE_BINARY_DIR}/common # for config.h
-    ${GLIB2_INCLUDE_DIRS}
-    ${GUILE_INCLUDE_DIRS}
     )
 
   set(swig_gnc_optiondb_LIBS
-    gncmod-engine
-    ${GLIB2_LDFLAGS}
-    ${GUILE_LDFLAGS}
-    )
+    gncmod-engine PkgConfig::GLIB2 PkgConfig::GUILE)
 
   target_link_libraries(swig-gnc-optiondb ${swig_gnc_optiondb_LIBS})
   target_include_directories(swig-gnc-optiondb

commit d2655d3fb0e4fa78ce0ca3f2f210194ddeac290f
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 22 15:39:39 2019 -0700

    Remove gnc-option-db business convenience functions.
    
    gnc_option_db_lookup_invoice_option used only once, so moved its
    guts there. The others weren't used at all.

diff --git a/gnucash/gnome/business-options-gnome.h b/gnucash/gnome/business-options-gnome.h
index 32db8740b..47d518f22 100644
--- a/gnucash/gnome/business-options-gnome.h
+++ b/gnucash/gnome/business-options-gnome.h
@@ -1,5 +1,5 @@
 /*
- * business-options.h -- Initialize the Business Options
+ * business-options-gnome.h -- Initialize the Business Options
  *
  * Written By: Derek Atkins <warlord at MIT.EDU>
  * Copyright (C) 2002 Derek Atkins
diff --git a/gnucash/gnome/gnc-plugin-page-report.c b/gnucash/gnome/gnc-plugin-page-report.c
index dff95509d..3c5f4104c 100644
--- a/gnucash/gnome/gnc-plugin-page-report.c
+++ b/gnucash/gnome/gnc-plugin-page-report.c
@@ -72,7 +72,6 @@
 #include "window-report.h"
 #include "swig-runtime.h"
 #include "guile-mappings.h"
-#include "business-options.h"
 #include "gnc-icons.h"
 #include "print-session.h"
 
@@ -1785,11 +1784,17 @@ gnc_plugin_page_report_options_cb( GtkAction *action, GncPluginPageReport *repor
         gnc_plugin_page_report_add_edited_report(priv, priv->cur_report);
 }
 
-static GncInvoice *lookup_invoice(GncPluginPageReportPrivate *priv)
+static GncInvoice*
+lookup_invoice(GncPluginPageReportPrivate *priv)
 {
-    g_assert(priv);
-    return gnc_option_db_lookup_invoice_option(priv->cur_odb, "General",
-            "Invoice Number", NULL);
+    SCM opt_val = gnc_option_db_lookup_option(priv->cur_odb, "General",
+                                              "Invoice Number", NULL);
+    if (opt_val == SCM_UNDEFINED)
+        return NULL;
+
+#define FUNC_NAME G_STRFUNC
+    return SWIG_MustGetPtr(opt_val, SWIG_TypeQuery("_p__gncInvoice"), 1, 0);
+#undef FUNC_NAME
 }
 
 #define GNC_PREFS_GROUP_REPORT_PDFEXPORT GNC_PREFS_GROUP_GENERAL_REPORT ".pdf-export"
diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt
index 6a376b7ea..7a0df5d35 100644
--- a/libgnucash/app-utils/CMakeLists.txt
+++ b/libgnucash/app-utils/CMakeLists.txt
@@ -15,7 +15,6 @@ set (app_utils_noinst_HEADERS
 
 set (app_utils_HEADERS
   QuickFill.h
-  business-options.h
   file-utils.h
   gfec.h
   gnc-basic-gobject.h
@@ -52,7 +51,6 @@ gnc_add_swig_python_command (swig-app-utils-python
 set (app_utils_SOURCES
   calculation/expression_parser.c
   calculation/fin.c
-  business-options.c
   QuickFill.c
   file-utils.c
   gfec.c
diff --git a/libgnucash/app-utils/business-options.c b/libgnucash/app-utils/business-options.c
deleted file mode 100644
index 2eb18a416..000000000
--- a/libgnucash/app-utils/business-options.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * business-options.c -- Non-GUI Option Utilities for GNC Business Objects
- *
- * Written By: Derek Atkins <warlord at MIT.EDU>
- * Copyright (C) 2003  Derek Atkins
- *
- * 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 <config.h>
-
-#include "business-options.h"
-#include "swig-runtime.h"
-#include "guile-mappings.h"
-
-#define FUNC_NAME G_STRFUNC
-
-#define LOOKUP_OPTION(fcn) \
-  GNCOption *option; \
-  SCM getter; \
-  SCM value; \
-  \
-  option = gnc_option_db_get_option_by_name (odb, section, name); \
-  \
-  if (option == NULL) \
-    return default_value; \
-  \
-  getter = gnc_option_getter (option); \
-  if (getter == SCM_UNDEFINED) \
-    return default_value; \
-  \
-  value = scm_call_0 (getter); \
-  if (value == SCM_BOOL_F) \
-    return NULL; \
-  SWIG_GetModule(NULL); /* Work-around for SWIG bug. */       \
-  if (!SWIG_IsPointer(value))             \
-    scm_misc_error(fcn, "SCM is not a wrapped pointer.", value)
-
-GncTaxTable*
-gnc_option_db_lookup_taxtable_option(GNCOptionDB *odb,
-                                     const char *section,
-                                     const char *name,
-                                     GncTaxTable * default_value)
-{
-    LOOKUP_OPTION("gnc_option_db_lookup_taxtable_option");
-    return SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncTaxTable"), 1, 0);
-}
-
-GncInvoice*
-gnc_option_db_lookup_invoice_option(GNCOptionDB *odb,
-                                    const char *section,
-                                    const char *name,
-                                    GncInvoice * default_value)
-{
-    LOOKUP_OPTION("gnc_option_db_lookup_invoice_option");
-    return SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncInvoice"), 1, 0);
-}
-
-GncCustomer*
-gnc_option_db_lookup_customer_option(GNCOptionDB *odb,
-                                     const char *section,
-                                     const char *name,
-                                     GncCustomer * default_value)
-{
-    LOOKUP_OPTION("gnc_option_db_lookup_customer_option");
-    return SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncCustomer"), 1, 0);
-}
-
-GncVendor*
-gnc_option_db_lookup_vendor_option(GNCOptionDB *odb,
-                                   const char *section,
-                                   const char *name,
-                                   GncVendor * default_value)
-{
-    LOOKUP_OPTION("gnc_option_db_lookup_vendor_option");
-    return SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncVendor"), 1, 0);
-}
diff --git a/libgnucash/app-utils/business-options.h b/libgnucash/app-utils/business-options.h
deleted file mode 100644
index 285e3e9a4..000000000
--- a/libgnucash/app-utils/business-options.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * business-options.h -- non-GUI Option Utilities for GNC Business Objects
- *
- * Written By: Derek Atkins <warlord at MIT.EDU>
- * Copyright (C) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * 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_BUSINESS_OPTIONS_UTILS_H_
-#define GNC_BUSINESS_OPTIONS_UTILS_H_
-
-#include "option-util.h"
-#include "gncTaxTable.h"
-#include "gncInvoice.h"
-#include "gncCustomer.h"
-#include "gncVendor.h"
-
-
-GncTaxTable* gnc_option_db_lookup_taxtable_option(GNCOptionDB *odb,
-        const char *section,
-        const char *name,
-        GncTaxTable * default_value);
-
-GncInvoice* gnc_option_db_lookup_invoice_option(GNCOptionDB *odb,
-        const char *section,
-        const char *name,
-        GncInvoice * default_value);
-
-GncCustomer* gnc_option_db_lookup_customer_option(GNCOptionDB *odb,
-        const char *section,
-        const char *name,
-        GncCustomer * default_value);
-
-GncVendor* gnc_option_db_lookup_vendor_option(GNCOptionDB *odb,
-        const char *section,
-        const char *name,
-        GncVendor * default_value);
-
-
-#endif /* GNC_BUSINESS_OPTIONS_UTILS_H_ */
diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index fbc08f410..641bd1194 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -137,25 +137,6 @@ GncOptionDB::set_selectable(const char* section, const char* name)
 void
 GncOptionDB::commit()
 {
-    std::for_each(
-        m_sections.begin(), m_sections.end(),
-        [](GncOptionSection section)
-        {
-            std::for_each(
-                section.second.begin(), section.second.end(),
-                [](GncOptionWrapperPtr option)
-                {
-/* FIXME, not implemented.
-                    if (option->m_option.is_dirty())
-                    {
-                        option->m_option.commit();
-
-* FIXME, no Gtk in      gtk_widget_set_value(option->m_widget,
-* libgnucash!                                option->m_option.get_value());
-                  }
-*/
-                });
-        });
 }
 
 GncOptionDB*
diff --git a/po/POTFILES.in b/po/POTFILES.in
index fe2f1ca71..0f92fb3c2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -509,7 +509,6 @@ gnucash/report/stylesheets/head-or-tail.scm
 gnucash/report/stylesheets/plain.scm
 gnucash/report/trep-engine.scm
 libgnucash/app-utils/app-utils.scm
-libgnucash/app-utils/business-options.c
 libgnucash/app-utils/business-options.scm
 libgnucash/app-utils/business-prefs.scm
 libgnucash/app-utils/calculation/expression_parser.c

commit ade7fc8b6e29a43d7b47db246066c57e6c6af309
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 6 15:31:44 2019 -0700

    Initial SWIG of GncOptionDB and Scheme tests.
    
    This class will be heavily used by reports so we need to ensure SWIG and
    Scheme compatibility from the start.

diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i
new file mode 100644
index 000000000..d15c8598d
--- /dev/null
+++ b/libgnucash/app-utils/gnc-optiondb.i
@@ -0,0 +1,18 @@
+/*
+ * Temporary swig interface file while developing C++ options.
+ */
+
+%module sw_gnc_optiondb
+%{
+#include <libguile.h>
+#include "gnc-optiondb.hpp"
+extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
+%}
+
+%include <std_string.i>
+
+%ignore OptionClassifier;
+%ignore GncOption;
+%ignore GncOptionWrapper;
+
+%include "gnc-optiondb.hpp"
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 192c6d9d2..17de9412d 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -82,12 +82,49 @@ gnc_add_scheme_test_targets(scm-test-c-interface
 gnc_add_scheme_tests("${test_app_utils_scheme_SOURCES}")
 
 if (HAVE_SRFI64)
-    gnc_add_scheme_test_targets(scm-test-app-utils-srfi64
-        SOURCES "${test_app_utils_scheme_SRFI64_SOURCES}"
-        OUTPUT_DIR "tests"
-        DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras")
-
-    gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}")
+  gnc_add_scheme_test_targets(scm-test-app-utils-srfi64
+    "${test_app_utils_scheme_SRFI64_SOURCES}"
+    "tests"
+    "${GUILE_DEPENDS};scm-srfi64-extras"
+    FALSE
+    )
+
+  set(SWIG_ARGS "-c++" "-procdoc" "sw-gnc-option-doc" "-procdocformat" "plain")
+  gnc_add_swig_guile_command(swig-gnc-optiondb-guile
+    SWIG_GNC_OPTIONDB_GUILE_CPP swig-gnc-optiondb-guile.cpp
+    ${MODULEPATH}/gnc-optiondb.i
+    )
+  add_library(swig-gnc-optiondb MODULE
+    ${MODULEPATH}/gnc-option.cpp
+    ${MODULEPATH}/gnc-optiondb.cpp
+    ${SWIG_GNC_OPTIONDB_GUILE_CPP}
+    )
+  set(swig_gnc_optiondb_INCLUDES
+    ${MODULEPATH}
+    ${CMAKE_SOURCE_DIR}/libgnucash/engine
+    ${CMAKE_BINARY_DIR}/common # for config.h
+    ${GLIB2_INCLUDE_DIRS}
+    ${GUILE_INCLUDE_DIRS}
+    )
+
+  set(swig_gnc_optiondb_LIBS
+    gncmod-engine
+    ${GLIB2_LDFLAGS}
+    ${GUILE_LDFLAGS}
+    )
+
+  target_link_libraries(swig-gnc-optiondb ${swig_gnc_optiondb_LIBS})
+  target_include_directories(swig-gnc-optiondb
+    PRIVATE ${swig_gnc_optiondb_INCLUDES})
+
+  gnc_add_scheme_test_targets(scm-test-gnc-optiondb
+    "test-gnc-optiondb.scm"
+    "tests"
+    "swig-gnc-optiondb;scm-srfi64-extras"
+    FALSE
+    )
+  gnc_add_scheme_tests("test-gnc-optiondb.scm")
+  gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}")
 endif()
 
 # Doesn't work yet:
diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm
new file mode 100644
index 000000000..4d26a803f
--- /dev/null
+++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm
@@ -0,0 +1,46 @@
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ; test-gnc-option.scm -- unit tests for GncOption class.           ;
+ ; Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               ;
+ ;                                                                  ;
+ ; This program is free software; you can redistribute it and/or    ;
+ ; modify it under the terms of the GNU General Public License as   ;
+ ; published by the Free Software Foundation; either version 2 of   ;
+ ; the License, or (at your option) any later version.              ;
+ ;                                                                  ;
+ ; This program is distributed in the hope that it will be useful,  ;
+ ; but WITHOUT ANY WARRANTY; without even the implied warranty of   ;
+ ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    ;
+ ; GNU General Public License for more details.                     ;
+ ;                                                                  ;
+ ; You should have received a copy of the GNU General Public License;
+ ; along with this program; if not, contact:                        ;
+ ;                                                                  ;
+ ; Free Software Foundation           Voice:  +1-617-542-5942       ;
+ ; 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       ;
+ ; Boston, MA  02110-1301,  USA       gnu at gnu.org                   ;
+ ;                                                                  ;
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-modules (srfi srfi-64))
+(use-modules (tests srfi64-extras))
+
+(eval-when
+ (compile load eval expand)
+ (load-extension "libswig-gnc-optiondb" "scm_init_sw_gnc_optiondb_module"))
+(use-modules (sw_gnc_optiondb))
+
+(define (run-test)
+  (test-runner-factory gnc:test-runner)
+  (test-begin "test-gnc-optiondb-scheme")
+  (test-gnc-make-text-option)
+  (test-end "test-gnc-optiondb-scheme"))
+
+(define (test-gnc-make-text-option)
+  (test-begin "test-gnc-test-string-option")
+  (let* ((option-db (new-GncOptionDB))
+         (string-opt (gnc-register-string-option option-db "foo" "bar" "baz"
+                                                 "Phony Option" "waldo")))
+    (test-equal (GncOptionDB-lookup-option option-db "foo" "bar") "waldo")
+    (delete-GncOptionDB option-db))
+
+  (test-end "test-gnc-make-string-option"))

commit f3eee511e88d406091151cfd4c8122ca0bbc40c8
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 6 15:29:58 2019 -0700

    Add free functions to create a new GncOptionDB and to register options.
    
    The objective of the free functions is to hide the GncOption from language
    bindings so that the GncOptions can be moved into the GncOptionDB instead
    of having shared ptrs splattered around the heap. Nearly all access to
    the options can then be mediated through the GncOptionDB container.
    
    Note that gnc_option_db_new creates the GncOptionDB on the heap and
    returns a raw ptr, so it's up to the creator of the GncOptionDB to
    call delete on it when it's no longer needed.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
index 29b140f1a..fbc08f410 100644
--- a/libgnucash/app-utils/gnc-optiondb.cpp
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -157,3 +157,61 @@ GncOptionDB::commit()
                 });
         });
 }
+
+GncOptionDB*
+gnc_option_db_new(void)
+{
+    return new GncOptionDB;
+}
+
+void
+gnc_register_string_option(GncOptionDB* db, const char* section,
+                           const char* name, const char* key,
+                           const char* doc_string, std::string value)
+{
+    GncOption option{section, name, key, doc_string, value};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_text_option(GncOptionDB* db, const char* section, const char* name,
+                         const char* key, const char* doc_string,
+                         std::string value)
+{
+    gnc_register_string_option(db, section, name, key, doc_string, value);
+}
+
+void
+gnc_register_budget_option(GncOptionDB* db, const char* section,
+                           const char* name, const char* key,
+                           const char* doc_string, GncBudget *value)
+{
+    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value)};
+    db->register_option(section, std::move(option));
+}
+
+void
+gnc_register_commodity_option(GncOptionDB* db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string, gnc_commodity *value)
+{
+    GncOption option{section, name, key, doc_string, QOF_INSTANCE(value)};
+    db->register_option(section, std::move(option));
+}
+
+
+void
+gnc_register_currency_option(GncOptionDB* db, const char* section,
+                             const char* name, const char* key,
+                             const char* doc_string, gnc_commodity *value)
+{
+    GncOption option{GncOptionValidatedValue<QofInstance*>{
+        section, name, key, doc_string, QOF_INSTANCE(value),
+        [](QofInstance* new_value) -> bool
+            {
+                return GNC_IS_COMMODITY (new_value) &&
+                    gnc_commodity_is_currency(GNC_COMMODITY(new_value));
+            }
+        }};
+    db->register_option(section, std::move(option));
+}
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
index 07e384696..69503c22c 100644
--- a/libgnucash/app-utils/gnc-optiondb.hpp
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -66,6 +66,45 @@ private:
     GncOptionSectionPtr m_default_section;
     std::vector<GncOptionSection> m_sections;
     bool m_dirty = false;
+
+    std::function<void*()> m_get_ui_value;
+    std::function<void(void*)> m_set_ui_value;
 };
 
+/**
+ * Create an empty option database.
+ *
+ * It would be nice to use a std::shared_ptr here but Swig doesn't implement
+ * that for Guile.
+ * @return A newly allocated GncOptionDB. Use delete to destroy it.
+ */
+GncOptionDB *gnc_option_db_new(void);
+
+void gnc_register_string_option(GncOptionDB* db, const char* section,
+                                const char* name, const char* key,
+                                const char* doc_string, std::string value);
+
+
+void gnc_register_string_option(GncOptionDB* db, const char* section,
+                                const char* name, const char* key,
+                                const char* doc_string, std::string value);
+
+void gnc_register_text_option(GncOptionDB* db, const char* section,
+                              const char* name, const char* key,
+                              const char* doc_string, std::string value);
+
+void gnc_register_budget_option(GncOptionDB* db, const char* section,
+                                const char* name, const char* key,
+                                const char* doc_string, GncBudget* value);
+
+void gnc_register_commodity_option(GncOptionDB* db, const char* section,
+                                   const char* name, const char* key,
+                                   const char* doc_string,
+                                   gnc_commodity* value);
+
+void gnc_register_currency_option(GncOptionDB* db, const char* section,
+                                  const char* name, const char* key,
+                                  const char* doc_string, gnc_commodity* value);
+
+
 #endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
index d33311bb1..a09659fde 100644
--- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -56,3 +56,12 @@ TEST(GncOptionDB, test_unregister_option)
     optiondb.unregister_option("foo", "bar");
     EXPECT_TRUE(optiondb.lookup_string_option("foo", "bar").empty());
 }
+
+TEST(GncOptionDB, test_register_string_option)
+{
+    GncOptionDB* db = gnc_option_db_new();
+    gnc_register_string_option(db, "foo", "bar", "baz", "Phony Option",
+                               std::string{"waldo"});
+    EXPECT_STREQ("waldo", db->lookup_string_option("foo", "bar").c_str());
+    delete db;
+}

commit 455d3c2d6015c197c4000b2d48b925c0da498669
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 6 15:03:48 2019 -0700

    Add GncOptionDB class.

diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp
new file mode 100644
index 000000000..29b140f1a
--- /dev/null
+++ b/libgnucash/app-utils/gnc-optiondb.cpp
@@ -0,0 +1,159 @@
+/********************************************************************\
+ * gnc-optiondb.cpp -- Collection of GncOption objects              *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#include "gnc-optiondb.hpp"
+
+GncOptionDB::GncOptionDB() : m_default_section{nullptr} {}
+
+GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
+
+void
+GncOptionDB::save_to_book(QofBook* book, bool do_clear) const
+{
+}
+
+void
+GncOptionDB::register_option(const char* section, GncOption&& option)
+{
+    auto db_section = std::find_if(
+        m_sections.begin(), m_sections.end(),
+        [section](GncOptionSection sect) -> bool
+        {
+            return sect.first == std::string{section};
+        });
+
+    if (db_section == m_sections.end())
+    {
+        m_sections.emplace_back(std::make_pair(std::string{section},
+                                               GncOptionVec{}));
+        db_section = std::prev(m_sections.end());
+    }
+    auto wrapper = std::make_shared<GncOptionWrapper>(option, nullptr);
+    db_section->second.emplace_back(wrapper);
+}
+
+void
+GncOptionDB::unregister_option(const char* section, const char* name)
+{
+    auto db_section = std::find_if(
+        m_sections.begin(), m_sections.end(),
+        [section](GncOptionSection sect) -> bool
+        {
+            return sect.first == std::string{section};
+        });
+    if (db_section != m_sections.end())
+    {
+        db_section->second.erase(
+            std::remove_if(
+                db_section->second.begin(), db_section->second.end(),
+                [name](GncOptionWrapperPtr option) -> bool
+                {
+                    return option->m_option.get_name() == std::string{name};
+                }));
+    }
+}
+
+void
+GncOptionDB::set_default_section(const char* section)
+{
+}
+
+SCM
+GncOptionDB::lookup_option(const char* section, const char* name) const
+{
+    auto db_section = std::find_if(
+        m_sections.begin(), m_sections.end(),
+        [section](GncOptionSection sect) -> bool
+        {
+            return sect.first == std::string{section};
+        });
+    if (db_section == m_sections.end())
+        return SCM_BOOL_F;
+    auto db_opt = std::find_if(
+        db_section->second.begin(), db_section->second.end(),
+        [name](GncOptionWrapperPtr option) -> bool
+        {
+            return option->m_option.get_name() == std::string{name};
+        });
+    if (db_opt == db_section->second.end() || !*db_opt)
+        return SCM_BOOL_F;
+    return (*db_opt)->m_option.get_scm_value();
+}
+
+static const std::string empty_string;
+std::string
+GncOptionDB::lookup_string_option(const char* section, const char* name) const
+{
+    auto db_section = std::find_if(
+        m_sections.begin(), m_sections.end(),
+        [section](GncOptionSection sect) -> bool
+        {
+            return sect.first == std::string{section};
+        });
+    if (db_section == m_sections.end())
+        return empty_string;
+    auto db_opt = std::find_if(
+        db_section->second.begin(), db_section->second.end(),
+        [name](GncOptionWrapperPtr option) -> bool
+        {
+            return option->m_option.get_name() == std::string{name};
+        });
+    if (db_opt == db_section->second.end() || !*db_opt)
+        return empty_string;
+    return (*db_opt)->m_option.get_value<std::string>();
+}
+
+bool
+GncOptionDB::set_option(const char* section, const char* name, SCM value)
+{
+    return false;
+}
+
+void
+GncOptionDB::set_selectable(const char* section, const char* name)
+{
+}
+
+void
+GncOptionDB::commit()
+{
+    std::for_each(
+        m_sections.begin(), m_sections.end(),
+        [](GncOptionSection section)
+        {
+            std::for_each(
+                section.second.begin(), section.second.end(),
+                [](GncOptionWrapperPtr option)
+                {
+/* FIXME, not implemented.
+                    if (option->m_option.is_dirty())
+                    {
+                        option->m_option.commit();
+
+* FIXME, no Gtk in      gtk_widget_set_value(option->m_widget,
+* libgnucash!                                option->m_option.get_value());
+                  }
+*/
+                });
+        });
+}
diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp
new file mode 100644
index 000000000..07e384696
--- /dev/null
+++ b/libgnucash/app-utils/gnc-optiondb.hpp
@@ -0,0 +1,71 @@
+/********************************************************************\
+ * gnc-optiondb.hpp -- Collection of GncOption objects              *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#ifndef GNC_OPTIONDB_HPP_
+#define GNC_OPTIONDB_HPP_
+
+#include "gnc-option.hpp"
+
+class GncOptionDB;
+struct GncOptionWrapper
+{
+    GncOptionWrapper(const GncOption& opt, void* ptr) : m_option{opt}, m_widget{ptr} {}
+    GncOptionWrapper(GncOption&& opt, void* ptr) : m_option{opt}, m_widget{ptr} {}
+    GncOption m_option;
+    void* m_widget; /* Don't want widget code in libgnucash! GObject closure?*/
+};
+
+using GncOptionWrapperPtr = std::shared_ptr<GncOptionWrapper>;
+using GncOptionVec = std::vector<GncOptionWrapperPtr>;
+using GncOptionSection = std::pair<std::string, GncOptionVec>;
+using GncOptionSectionPtr = std::shared_ptr<const GncOptionSection>;
+class GncOptionDB
+{
+public:
+    GncOptionDB();
+    GncOptionDB(QofBook* book);
+    ~GncOptionDB() = default;
+
+    void save_to_book(QofBook* book, bool do_clear) const;
+    int num_sections() const noexcept { return m_sections.size(); }
+    bool get_changed() const noexcept { return m_dirty; }
+    void register_option(const char* section, GncOption&& option);
+    void unregister_option(const char* section, const char* name);
+    void set_default_section(const char* section);
+    const GncOptionSectionPtr get_default_section() const noexcept
+    {
+        return m_default_section;
+    }
+    SCM lookup_option(const char* section, const char* name) const;
+    std::string lookup_string_option(const char* section,
+                                            const char* name) const;
+    bool set_option(const char* section, const char* name, SCM value);
+    void set_selectable(const char* section, const char* name);
+    void commit();
+private:
+    GncOptionSectionPtr m_default_section;
+    std::vector<GncOptionSection> m_sections;
+    bool m_dirty = false;
+};
+
+#endif //GNC_OPTIONDB_HPP_
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index 426f4f2a6..192c6d9d2 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -31,7 +31,9 @@ add_app_utils_test(test-sx test-sx.cpp)
 
 set(gtest_gnc_option_SOURCES
   ${MODULEPATH}/gnc-option.cpp
-  gtest-gnc-option.cpp)
+  ${MODULEPATH}/gnc-optiondb.cpp
+  gtest-gnc-option.cpp
+  gtest-gnc-optiondb.cpp)
 
 set(gtest_gnc_option_INCLUDES
   ${MODULEPATH}
diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
new file mode 100644
index 000000000..d33311bb1
--- /dev/null
+++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
@@ -0,0 +1,58 @@
+/********************************************************************
+ * gtest-gnc-optiondb.cpp -- unit tests for GncOption class.        *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+ *******************************************************************/
+
+#include <gtest/gtest.h>
+#include <gnc-optiondb.hpp>
+
+TEST(GncOptionDB, test_ctor)
+{
+    EXPECT_NO_THROW ({ GncOptionDB optiondb; });
+}
+
+TEST(GncOptionDB, test_register_option)
+{
+    GncOptionDB optiondb;
+    GncOption option1{"foo", "bar", "baz", "Phony Option",
+                      std::string{"waldo"}};
+    optiondb.register_option("foo", std::move(option1));
+    EXPECT_EQ(optiondb.num_sections(), 1);
+}
+
+TEST(GncOptionDB, test_lookup_string_option)
+{
+    GncOptionDB optiondb;
+    GncOption option1{"foo", "bar", "baz", "Phony Option",
+                      std::string{"waldo"}};
+    optiondb.register_option("foo", std::move(option1));
+    EXPECT_STREQ("waldo", optiondb.lookup_string_option("foo", "bar").c_str());
+}
+
+TEST(GncOptionDB, test_unregister_option)
+{
+    GncOptionDB optiondb;
+    GncOption option1{"foo", "bar", "baz", "Phony Option",
+                      std::string{"waldo"}};
+    optiondb.register_option("foo", std::move(option1));
+    optiondb.unregister_option("foo", "bar");
+    EXPECT_TRUE(optiondb.lookup_string_option("foo", "bar").empty());
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3c28396e2..fe2f1ca71 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -530,6 +530,7 @@ libgnucash/app-utils/gnc-helpers.c
 libgnucash/app-utils/gnc-help-utils.c
 libgnucash/app-utils/gncmod-app-utils.c
 libgnucash/app-utils/gnc-option.cpp
+libgnucash/app-utils/gnc-optiondb.cpp
 libgnucash/app-utils/gnc-prefs-utils.c
 libgnucash/app-utils/gnc-state.c
 libgnucash/app-utils/gnc-sx-instance-model.c

commit 16fd632ec85a98358449a25579c5491ded376d4c
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 6 10:17:06 2019 -0700

    Make the GncOption member variables private.
    
    GncOption is intended to be final, so there's no point in protected members.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 4b1e17c81..023f8b36e 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -139,7 +139,7 @@ public:
     ValueType get_value() const { return m_value; }
     ValueType get_default_value() const { return m_default_value; }
     void set_value(ValueType new_value) { m_value = new_value; }
-protected:
+private:
     ValueType m_value;
     ValueType m_default_value;
 };

commit 13b94d2370b399a5d88cea5a4766ad5aef96c2a6
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Aug 6 10:24:42 2019 -0700

    Test integer options, calling get_value with wrong type.

diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index d552ba855..bd1712094 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -46,6 +46,7 @@ TEST(GncOption, test_string_default_value)
     GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
     EXPECT_STREQ("waldo", option.get_default_value<std::string>().c_str());
     EXPECT_STREQ("waldo", option.get_value<std::string>().c_str());
+    EXPECT_EQ(0, option.get_value<int64_t>());
 }
 
 TEST(GncOption, test_string_value)
@@ -58,6 +59,14 @@ TEST(GncOption, test_string_value)
         });
 }
 
+TEST(GncOption, test_int64_t_value)
+{
+    GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
+    option.set_value(INT64_C(987654321));
+    EXPECT_TRUE(option.get_default_value<std::string>().empty());
+    EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
+}
+
 TEST(GncOption, test_string_scm_functions)
 {
     GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});

commit d88ec0dc1b59e3c15147dcd832d360331582cf8f
Author: John Ralls <jralls at ceridwen.us>
Date:   Mon Aug 5 11:03:34 2019 -0700

    Replace the gnc_make_foo_option free functions with a template ctor.
    
    The free functions will reappear for GncOptionDB. This is to avoid
    having to pass naked pointers to Scheme with the attendant object
    lifetime issues.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index e57779025..485b0d403 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -51,60 +51,3 @@ scm_from_value<QofInstance*>(QofInstance* value)
     return scm_guid;
 }
 
-GncOption
-gnc_make_string_option(const char* section, const char* name,
-                       const char* key, const char* doc_string,
-                       std::string value)
-{
-    GncOptionValue<std::string> retval {
-        section, name, key, doc_string, value
-            };
-    return retval;
-}
-
-GncOption
-gnc_make_text_option(const char* section, const char* name,
-                     const char* key, const char* doc_string,
-                     std::string value)
-{
-    return gnc_make_string_option(section, name, key, doc_string, value);
-}
-
-GncOption
-gnc_make_budget_option(const char* section, const char* name,
-                       const char* key, const char* doc_string,
-                       GncBudget *value)
-{
-    GncOptionValue<QofInstance*> retval {
-        section, name, key, doc_string, QOF_INSTANCE(value)
-            };
-    return retval;
-}
-
-GncOption
-gnc_make_commodity_option(const char* section, const char* name,
-                     const char* key, const char* doc_string,
-                         gnc_commodity *value)
-{
-    GncOptionValue<QofInstance*> retval {
-        section, name, key, doc_string, QOF_INSTANCE(value)
-            };
-    return retval;
-}
-
-
-GncOption
-gnc_make_currency_option(const char* section, const char* name,
-                         const char* key, const char* doc_string,
-                         gnc_commodity *value)
-{
-    GncOptionValidatedValue<QofInstance*> retval {
-        section, name, key, doc_string, QOF_INSTANCE(value),
-        [](QofInstance* new_value) -> bool
-            {
-                return GNC_IS_COMMODITY (new_value) &&
-                    gnc_commodity_is_currency(GNC_COMMODITY(new_value));
-            }
-    };
-    return retval;
-}
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 15b78876a..4b1e17c81 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -199,6 +199,14 @@ public:
     template <typename OptionType>
     GncOption(OptionType option) : m_option{option} {}
 
+    template <typename ValueType>
+    GncOption(const char* section, const char* name,
+              const char* key, const char* doc_string,
+              ValueType value) :
+        m_option{GncOptionValue<ValueType> {
+            section, name, key, doc_string, value
+                }} {}
+
     template <typename ValueType> ValueType get_value() const
     {
         return boost::apply_visitor(GetValueVisitor<ValueType>(), m_option);
@@ -326,30 +334,4 @@ private:
     GncOptionVariant m_option;
 };
 
-GncOption
-gnc_make_string_option(const char* section, const char* name,
-                       const char* key, const char* doc_string,
-                       std::string value);
-
-GncOption
-gnc_make_text_option(const char* section, const char* name,
-                     const char* key, const char* doc_string,
-                     std::string value);
-
-GncOption
-gnc_make_budget_option(const char* section, const char* name,
-                       const char* key, const char* doc_string,
-                       GncBudget* value);
-
-GncOption
-gnc_make_commodity_option(const char* section, const char* name,
-                          const char* key, const char* doc_string,
-                          gnc_commodity* value);
-
-GncOption
-gnc_make_currency_option(const char* section, const char* name,
-                         const char* key, const char* doc_string,
-                         gnc_commodity* value);
-
-
 #endif //GNC_OPTION_HPP_
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index b4a7f187c..d552ba855 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -27,25 +27,14 @@
 TEST(GncOption, test_string_ctor)
 {
     EXPECT_NO_THROW({
-            auto option = gnc_make_string_option("foo", "bar", "baz",
-                                                 "Phony Option",
-                                                 std::string{"waldo"});
-        });
-}
-
-TEST(GncOption, test_text_ctor)
-{
-    EXPECT_NO_THROW({
-            auto option = gnc_make_text_option("foo", "bar", "baz",
-                                               "Phony Option",
-                                               std::string{"waldo"});
+            GncOption option("foo", "bar", "baz", "Phony Option",
+                             std::string{"waldo"});
         });
 }
 
 TEST(GncOption, test_string_classifier_getters)
 {
-    auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
-                                         std::string{"waldo"});
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
     EXPECT_STREQ("foo", option.get_section().c_str());
     EXPECT_STREQ("bar", option.get_name().c_str());
     EXPECT_STREQ("baz", option.get_key().c_str());
@@ -54,16 +43,14 @@ TEST(GncOption, test_string_classifier_getters)
 
 TEST(GncOption, test_string_default_value)
 {
-    auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
-                                         std::string{"waldo"});
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
     EXPECT_STREQ("waldo", option.get_default_value<std::string>().c_str());
     EXPECT_STREQ("waldo", option.get_value<std::string>().c_str());
 }
 
 TEST(GncOption, test_string_value)
 {
-    auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
-                                         std::string{"waldo"});
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
     option.set_value(std::string{"pepper"});
     EXPECT_STREQ("waldo", option.get_default_value<std::string>().c_str());
     EXPECT_NO_THROW({
@@ -73,8 +60,7 @@ TEST(GncOption, test_string_value)
 
 TEST(GncOption, test_string_scm_functions)
 {
-    auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
-                                         std::string{"waldo"});
+    GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
     auto scm_value = option.get_scm_value();
     auto str_value = scm_to_utf8_string(scm_value);
     EXPECT_STREQ("waldo", str_value);
@@ -90,8 +76,8 @@ TEST(GNCOption, test_budget_ctor)
     auto book = qof_book_new();
     auto budget = gnc_budget_new(book);
     EXPECT_NO_THROW({
-            auto option = gnc_make_budget_option("foo", "bar", "baz",
-                                                 "Phony Option", budget);
+            GncOption option("foo", "bar", "baz", "Phony Option",
+                             QOF_INSTANCE(budget));
         });
     gnc_budget_destroy(budget);
     qof_book_destroy(book);
@@ -101,8 +87,8 @@ TEST(GNCOption, test_budget_scm_functions)
 {
     auto book = qof_book_new();
     auto budget = gnc_budget_new(book);
-    auto option = gnc_make_budget_option("foo", "bar", "baz",
-                                         "Phony Option", budget);
+    GncOption option("foo", "bar", "baz", "Phony Option",
+                     QOF_INSTANCE(budget));
     auto scm_budget = option.get_scm_value();
     auto str_value = scm_to_utf8_string(scm_budget);
     auto guid = guid_to_string(qof_instance_get_guid(budget));
@@ -118,12 +104,27 @@ TEST(GNCOption, test_commodity_ctor)
     auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
                                     "NYSE", "HPE", NULL, 1);
     EXPECT_NO_THROW({
-            auto option = gnc_make_commodity_option("foo", "bar", "baz",
-                                                 "Phony Option", hpe);
+            GncOption option("foo", "bar", "baz", "Phony Option",
+                             QOF_INSTANCE(hpe));
         });
     gnc_commodity_destroy(hpe);
     qof_book_destroy(book);
 }
+static GncOption
+make_currency_option (const char* section, const char* name,
+                      const char* key, const char* doc_string,
+                      gnc_commodity *value)
+{
+    GncOption option{GncOptionValidatedValue<QofInstance*>{
+        section, name, key, doc_string, QOF_INSTANCE(value),
+        [](QofInstance* new_value) -> bool
+            {
+                return GNC_IS_COMMODITY (new_value) &&
+                    gnc_commodity_is_currency(GNC_COMMODITY(new_value));
+            }
+        }};
+    return option;
+}
 
 TEST(GNCOption, test_currency_ctor)
 {
@@ -133,21 +134,21 @@ TEST(GNCOption, test_currency_ctor)
     auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
                                     "NYSE", "HPE", NULL, 1);
     EXPECT_THROW({
-            auto option = gnc_make_currency_option("foo", "bar", "baz",
-                                                 "Phony Option", hpe);
+            auto option = make_currency_option("foo", "bar", "baz",
+                                               "Phony Option", hpe);
         }, std::invalid_argument);
     gnc_commodity_destroy(hpe);
     auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100);
     EXPECT_NO_THROW({
-            auto option = gnc_make_currency_option("foo", "bar", "baz",
-                                                 "Phony Option", eur);
+            auto option = make_currency_option("foo", "bar", "baz",
+                                               "Phony Option", eur);
         });
     gnc_commodity_destroy(eur);
     auto usd = gnc_commodity_new(book, "United States Dollar",
                                  "CURRENCY", "USD", NULL, 100);
     EXPECT_NO_THROW({
-            auto option = gnc_make_currency_option("foo", "bar", "baz",
-                                                 "Phony Option", usd);
+            auto option = make_currency_option("foo", "bar", "baz",
+                                               "Phony Option",usd);
         });
     gnc_commodity_destroy(usd);
     qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr);
@@ -163,8 +164,8 @@ TEST(GNCOption, test_currency_setter)
     auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
                                     "NYSE", "HPE", NULL, 1);
     auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100);
-    auto option = gnc_make_currency_option("foo", "bar", "baz",
-                                                 "Phony Option", eur);
+            auto option = make_currency_option("foo", "bar", "baz",
+                                               "Phony Option",eur);
     auto usd = gnc_commodity_new(book, "United States Dollar",
                                  "CURRENCY", "USD", NULL, 100);
     EXPECT_NO_THROW({

commit 01dc70cc60ae546943200cb726723a419d35928f
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Aug 3 10:38:05 2019 -0700

    Add GncOption accessors for Classifier strings.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 1a91c6138..15b78876a 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -198,6 +198,7 @@ class GncOption
 public:
     template <typename OptionType>
     GncOption(OptionType option) : m_option{option} {}
+
     template <typename ValueType> ValueType get_value() const
     {
         return boost::apply_visitor(GetValueVisitor<ValueType>(), m_option);
@@ -218,6 +219,22 @@ public:
     {
         boost::apply_visitor(SetValueVisitor<ValueType>(value), m_option);
     }
+    const std::string& get_section() const
+    {
+        return boost::apply_visitor(GetSectionVisitor(), m_option);
+    }
+    const std::string& get_name() const
+    {
+        return boost::apply_visitor(GetNameVisitor(), m_option);
+    }
+    const std::string& get_key() const
+    {
+        return boost::apply_visitor(GetKeyVisitor(), m_option);
+    }
+    const std::string& get_docstring() const
+    {
+        return boost::apply_visitor(GetDocstringVisitor(), m_option);
+    }
 private:
     template <typename ValueType>
     struct GetValueVisitor : public boost::static_visitor<ValueType>
@@ -277,6 +294,35 @@ private:
             return option.get_scm_default_value();
         }
     };
+    struct GetSectionVisitor : public boost::static_visitor<const std::string&>
+    {
+        template <class OptionType>
+        const std::string& operator()(OptionType& option) const {
+            return option.m_section;
+        }
+    };
+    struct GetNameVisitor : public boost::static_visitor<const std::string&>
+    {
+        template <class OptionType>
+        const std::string& operator()(OptionType& option) const {
+            return option.m_name;
+        }
+    };
+    struct GetKeyVisitor : public boost::static_visitor<const std::string&>
+    {
+        template <class OptionType>
+        const std::string& operator()(OptionType& option) const {
+            return option.m_sort_tag;
+        }
+    };
+    struct GetDocstringVisitor :
+        public boost::static_visitor<const std::string&>
+    {
+        template <class OptionType>
+        const std::string& operator()(OptionType& option) const {
+            return option.m_doc_string;
+        }
+    };
     GncOptionVariant m_option;
 };
 
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index be9b3b560..b4a7f187c 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -42,6 +42,16 @@ TEST(GncOption, test_text_ctor)
         });
 }
 
+TEST(GncOption, test_string_classifier_getters)
+{
+    auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
+                                         std::string{"waldo"});
+    EXPECT_STREQ("foo", option.get_section().c_str());
+    EXPECT_STREQ("bar", option.get_name().c_str());
+    EXPECT_STREQ("baz", option.get_key().c_str());
+    EXPECT_STREQ("Phony Option", option.get_docstring().c_str());
+}
+
 TEST(GncOption, test_string_default_value)
 {
     auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",

commit 01fcae6ac8aa886db2efdbd6f61e847da1e6a553
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Aug 3 10:11:49 2019 -0700

    Make the OptionClassifier members non-const.
    
    Constness deletes the default copy assignment operator, making GncOption
    not copy-assignable.

diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index 8f3168110..1a91c6138 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -83,11 +83,11 @@ protected:
 
 struct OptionClassifier
 {
-    const std::string m_section;
-    const std::string m_name;
-    const std::string m_sort_tag;
-//    const std::type_info m_kvp_type;
-    const std::string m_doc_string;
+    std::string m_section;
+    std::string m_name;
+    std::string m_sort_tag;
+//  std::type_info m_kvp_type;
+    std::string m_doc_string;
 };
 
 template <typename ValueType>

commit b6fd8447743e2d741ebfb8cf49bc24acdf0f470e
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jul 20 15:09:11 2019 -0700

    Wrap GncOptionValue/GncOptionValidatedValue in Boost::Variant.
    
    To provide a single type for containers.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
index 447328cee..e57779025 100644
--- a/libgnucash/app-utils/gnc-option.cpp
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -51,16 +51,18 @@ scm_from_value<QofInstance*>(QofInstance* value)
     return scm_guid;
 }
 
-std::shared_ptr<GncOptionValue<std::string>>
+GncOption
 gnc_make_string_option(const char* section, const char* name,
                        const char* key, const char* doc_string,
                        std::string value)
 {
-    return std::make_shared<GncOptionValue<std::string>>(
-        section, name, key, doc_string, value);
+    GncOptionValue<std::string> retval {
+        section, name, key, doc_string, value
+            };
+    return retval;
 }
 
-std::shared_ptr<GncOptionValue<std::string>>
+GncOption
 gnc_make_text_option(const char* section, const char* name,
                      const char* key, const char* doc_string,
                      std::string value)
@@ -68,36 +70,41 @@ gnc_make_text_option(const char* section, const char* name,
     return gnc_make_string_option(section, name, key, doc_string, value);
 }
 
-std::shared_ptr<GncOptionValue<QofInstance*>>
+GncOption
 gnc_make_budget_option(const char* section, const char* name,
                        const char* key, const char* doc_string,
                        GncBudget *value)
 {
-    return std::make_shared<GncOptionValue<QofInstance*>>(
-        section, name, key, doc_string, QOF_INSTANCE(value));
+    GncOptionValue<QofInstance*> retval {
+        section, name, key, doc_string, QOF_INSTANCE(value)
+            };
+    return retval;
 }
 
-std::shared_ptr<GncOptionValue<QofInstance*>>
+GncOption
 gnc_make_commodity_option(const char* section, const char* name,
                      const char* key, const char* doc_string,
                          gnc_commodity *value)
 {
-    return std::make_shared<GncOptionValue<QofInstance*>>(
-        section, name, key, doc_string, QOF_INSTANCE(value));
+    GncOptionValue<QofInstance*> retval {
+        section, name, key, doc_string, QOF_INSTANCE(value)
+            };
+    return retval;
 }
 
 
-std::shared_ptr<GncOptionValidatedValue<QofInstance*>>
+GncOption
 gnc_make_currency_option(const char* section, const char* name,
                          const char* key, const char* doc_string,
                          gnc_commodity *value)
 {
-    return std::make_shared<GncOptionValidatedValue<QofInstance*>>(
+    GncOptionValidatedValue<QofInstance*> retval {
         section, name, key, doc_string, QOF_INSTANCE(value),
         [](QofInstance* new_value) -> bool
             {
                 return GNC_IS_COMMODITY (new_value) &&
                     gnc_commodity_is_currency(GNC_COMMODITY(new_value));
             }
-        );
+    };
+    return retval;
 }
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
index f8bcb84ab..8f3168110 100644
--- a/libgnucash/app-utils/gnc-option.hpp
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -33,6 +33,7 @@ extern "C"
 }
 #include <libguile.h>
 #include <string>
+#include <boost/variant.hpp>
 
 /*
  * Unused base class to document the structure of the current Scheme option
@@ -97,7 +98,7 @@ SCM scm_from_value(ValueType);
  * for a detailed explanation.
  */
 template <typename ValueType, class ValueClass>
-class GncOption
+class GncOptionBase
 {
 public:
     ValueType get_value() const
@@ -112,12 +113,12 @@ public:
     {
         return static_cast<ValueClass const&>(*this).get_default_value();
     }
-    SCM get_scm_value()
+    SCM get_scm_value() const
     {
         ValueType value{static_cast<ValueClass const&>(*this).get_value()};
         return scm_from_value<ValueType>(value);
     }
-    SCM get_scm_default_value()
+    SCM get_scm_default_value() const
     {
         ValueType value{static_cast<ValueClass const&>(*this).get_default_value()};
         return scm_from_value<ValueType>(value);
@@ -127,7 +128,7 @@ public:
 template <typename ValueType>
 class GncOptionValue :
     public OptionClassifier,
-    public GncOption<ValueType, GncOptionValue<ValueType>>
+    public GncOptionBase<ValueType, GncOptionValue<ValueType>>
 {
 public:
     GncOptionValue<ValueType>(const char* section, const char* name,
@@ -146,7 +147,7 @@ protected:
 template <typename ValueType>
 class GncOptionValidatedValue :
     public OptionClassifier,
-    public GncOption<ValueType, GncOptionValidatedValue<ValueType>>
+    public GncOptionBase<ValueType, GncOptionValidatedValue<ValueType>>
 {
 public:
     GncOptionValidatedValue<ValueType>(const char* section, const char* name,
@@ -187,29 +188,122 @@ private:
     ValueType m_validation_data;
 };
 
-std::shared_ptr<GncOptionValue<std::string>>
+using GncOptionVariant = boost::variant<GncOptionValue<std::string>,
+                                 GncOptionValue<bool>,
+                                 GncOptionValue<int64_t>,
+                                 GncOptionValue<QofInstance*>,
+                                 GncOptionValidatedValue<QofInstance*>>;
+class GncOption
+{
+public:
+    template <typename OptionType>
+    GncOption(OptionType option) : m_option{option} {}
+    template <typename ValueType> ValueType get_value() const
+    {
+        return boost::apply_visitor(GetValueVisitor<ValueType>(), m_option);
+    }
+    template <typename ValueType> ValueType get_default_value() const
+    {
+        return boost::apply_visitor(GetDefaultValueVisitor<ValueType>(), m_option);
+    }
+    SCM get_scm_value() const
+    {
+        return boost::apply_visitor(GetSCMVisitor(), m_option);
+    }
+    SCM get_scm_default_value() const
+    {
+        return boost::apply_visitor(GetSCMDefaultVisitor(), m_option);
+    }
+    template <typename ValueType> void set_value(ValueType value)
+    {
+        boost::apply_visitor(SetValueVisitor<ValueType>(value), m_option);
+    }
+private:
+    template <typename ValueType>
+    struct GetValueVisitor : public boost::static_visitor<ValueType>
+    {
+        ValueType operator()(const GncOptionValue<ValueType>& option) const {
+            return option.get_value();
+        }
+        ValueType operator()(const GncOptionValidatedValue<ValueType>& option) const {
+            return option.get_value();
+        }
+        template <class OptionType>
+        ValueType operator()(OptionType& option) const {
+            return ValueType{};
+        }
+    };
+    template <typename ValueType>
+    struct GetDefaultValueVisitor : public boost::static_visitor<ValueType>
+    {
+        ValueType operator()(const GncOptionValue<ValueType>& option) const {
+            return option.get_default_value();
+        }
+        ValueType operator()(const GncOptionValidatedValue<ValueType>& option) const {
+            return option.get_default_value();
+        }
+        template <class OptionType>
+        ValueType operator()(OptionType& option) const {
+            return ValueType();
+        }
+    };
+    template <typename ValueType>
+    struct SetValueVisitor : public boost::static_visitor<>
+    {
+        SetValueVisitor(ValueType value) : m_value{value} {}
+        void operator()(GncOptionValue<ValueType>& option) const {
+            option.set_value(m_value);
+        }
+        void operator()(GncOptionValidatedValue<ValueType>& option) const {
+            option.set_value(m_value);
+        }
+        template <class OptionType>
+        void operator()(OptionType& option) const {
+        }
+    private:
+        ValueType m_value;
+    };
+    struct GetSCMVisitor : public boost::static_visitor<SCM>
+    {
+        template <class OptionType>
+        SCM operator()(OptionType& option) const {
+            return option.get_scm_value();
+        }
+    };
+    struct GetSCMDefaultVisitor : public boost::static_visitor<SCM>
+    {
+        template <class OptionType>
+        SCM operator()(OptionType& option) const {
+            return option.get_scm_default_value();
+        }
+    };
+    GncOptionVariant m_option;
+};
+
+GncOption
 gnc_make_string_option(const char* section, const char* name,
                        const char* key, const char* doc_string,
                        std::string value);
 
-std::shared_ptr<GncOptionValue<std::string>>
+GncOption
 gnc_make_text_option(const char* section, const char* name,
                      const char* key, const char* doc_string,
                      std::string value);
 
-std::shared_ptr<GncOptionValue<QofInstance*>>
+GncOption
 gnc_make_budget_option(const char* section, const char* name,
                        const char* key, const char* doc_string,
                        GncBudget* value);
 
-std::shared_ptr<GncOptionValue<QofInstance*>>
+GncOption
 gnc_make_commodity_option(const char* section, const char* name,
                           const char* key, const char* doc_string,
                           gnc_commodity* value);
 
-std::shared_ptr<GncOptionValidatedValue<QofInstance*>>
+GncOption
 gnc_make_currency_option(const char* section, const char* name,
                          const char* key, const char* doc_string,
                          gnc_commodity* value);
 
+
 #endif //GNC_OPTION_HPP_
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
index ca309f499..be9b3b560 100644
--- a/libgnucash/app-utils/test/gtest-gnc-option.cpp
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -46,18 +46,18 @@ TEST(GncOption, test_string_default_value)
 {
     auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
                                          std::string{"waldo"});
-    EXPECT_STREQ("waldo", option->get_default_value().c_str());
-    EXPECT_STREQ("waldo", option->get_value().c_str());
+    EXPECT_STREQ("waldo", option.get_default_value<std::string>().c_str());
+    EXPECT_STREQ("waldo", option.get_value<std::string>().c_str());
 }
 
 TEST(GncOption, test_string_value)
 {
     auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
                                          std::string{"waldo"});
-    option->set_value("pepper");
-    EXPECT_STREQ("waldo", option->get_default_value().c_str());
+    option.set_value(std::string{"pepper"});
+    EXPECT_STREQ("waldo", option.get_default_value<std::string>().c_str());
     EXPECT_NO_THROW({
-            EXPECT_STREQ("pepper", option->get_value().c_str());
+            EXPECT_STREQ("pepper", option.get_value<std::string>().c_str());
         });
 }
 
@@ -65,11 +65,11 @@ TEST(GncOption, test_string_scm_functions)
 {
     auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
                                          std::string{"waldo"});
-    auto scm_value = option->get_scm_value();
+    auto scm_value = option.get_scm_value();
     auto str_value = scm_to_utf8_string(scm_value);
     EXPECT_STREQ("waldo", str_value);
     g_free(str_value);
-    scm_value = option->get_scm_default_value();
+    scm_value = option.get_scm_default_value();
     str_value = scm_to_utf8_string(scm_value);
     EXPECT_STREQ("waldo", str_value);
     g_free(str_value);
@@ -93,7 +93,7 @@ TEST(GNCOption, test_budget_scm_functions)
     auto budget = gnc_budget_new(book);
     auto option = gnc_make_budget_option("foo", "bar", "baz",
                                          "Phony Option", budget);
-    auto scm_budget = option->get_scm_value();
+    auto scm_budget = option.get_scm_value();
     auto str_value = scm_to_utf8_string(scm_budget);
     auto guid = guid_to_string(qof_instance_get_guid(budget));
     EXPECT_STREQ(guid, str_value);
@@ -158,13 +158,13 @@ TEST(GNCOption, test_currency_setter)
     auto usd = gnc_commodity_new(book, "United States Dollar",
                                  "CURRENCY", "USD", NULL, 100);
     EXPECT_NO_THROW({
-            option->set_value(QOF_INSTANCE(usd));
+            option.set_value(QOF_INSTANCE(usd));
         });
-    EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option->get_value()));
+    EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option.get_value<QofInstance*>()));
     EXPECT_THROW({
-            option->set_value(QOF_INSTANCE(hpe));
+            option.set_value(QOF_INSTANCE(hpe));
         }, std::invalid_argument);
-    EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option->get_value()));
+    EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option.get_value<QofInstance*>()));
     gnc_commodity_destroy(hpe);
     gnc_commodity_destroy(usd);
     gnc_commodity_destroy(eur);

commit c0ba3e2706985d15ac53c95a85a81aeec1587ad0
Author: John Ralls <jralls at ceridwen.us>
Date:   Tue Jul 9 09:40:29 2019 -0700

    [C++ Options] Begin Implementation for basic and validated options.

diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp
new file mode 100644
index 000000000..447328cee
--- /dev/null
+++ b/libgnucash/app-utils/gnc-option.cpp
@@ -0,0 +1,103 @@
+/********************************************************************\
+ * gnc-option.cpp -- Application options system                     *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+//#include "options.h"
+#include "gnc-option.hpp"
+
+template<> SCM
+scm_from_value<std::string>(std::string value)
+{
+    return scm_from_utf8_string(value.c_str());
+}
+
+template<> SCM
+scm_from_value<bool>(bool value)
+{
+    return value ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+template<> SCM
+scm_from_value<int64_t>(int64_t value)
+{
+    return scm_from_int64(value);
+}
+
+template<> SCM
+scm_from_value<QofInstance*>(QofInstance* value)
+{
+    auto guid = guid_to_string(qof_instance_get_guid(value));
+    auto scm_guid = scm_from_utf8_string(guid);
+    g_free(guid);
+    return scm_guid;
+}
+
+std::shared_ptr<GncOptionValue<std::string>>
+gnc_make_string_option(const char* section, const char* name,
+                       const char* key, const char* doc_string,
+                       std::string value)
+{
+    return std::make_shared<GncOptionValue<std::string>>(
+        section, name, key, doc_string, value);
+}
+
+std::shared_ptr<GncOptionValue<std::string>>
+gnc_make_text_option(const char* section, const char* name,
+                     const char* key, const char* doc_string,
+                     std::string value)
+{
+    return gnc_make_string_option(section, name, key, doc_string, value);
+}
+
+std::shared_ptr<GncOptionValue<QofInstance*>>
+gnc_make_budget_option(const char* section, const char* name,
+                       const char* key, const char* doc_string,
+                       GncBudget *value)
+{
+    return std::make_shared<GncOptionValue<QofInstance*>>(
+        section, name, key, doc_string, QOF_INSTANCE(value));
+}
+
+std::shared_ptr<GncOptionValue<QofInstance*>>
+gnc_make_commodity_option(const char* section, const char* name,
+                     const char* key, const char* doc_string,
+                         gnc_commodity *value)
+{
+    return std::make_shared<GncOptionValue<QofInstance*>>(
+        section, name, key, doc_string, QOF_INSTANCE(value));
+}
+
+
+std::shared_ptr<GncOptionValidatedValue<QofInstance*>>
+gnc_make_currency_option(const char* section, const char* name,
+                         const char* key, const char* doc_string,
+                         gnc_commodity *value)
+{
+    return std::make_shared<GncOptionValidatedValue<QofInstance*>>(
+        section, name, key, doc_string, QOF_INSTANCE(value),
+        [](QofInstance* new_value) -> bool
+            {
+                return GNC_IS_COMMODITY (new_value) &&
+                    gnc_commodity_is_currency(GNC_COMMODITY(new_value));
+            }
+        );
+}
diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp
new file mode 100644
index 000000000..f8bcb84ab
--- /dev/null
+++ b/libgnucash/app-utils/gnc-option.hpp
@@ -0,0 +1,215 @@
+/********************************************************************\
+ * gnc-option.hpp -- Application options system                     *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#ifndef GNC_OPTION_HPP_
+#define GNC_OPTION_HPP_
+
+extern "C"
+{
+#include <config.h>
+#include <qof.h>
+#include <gnc-budget.h>
+#include <gnc-commodity.h>
+}
+#include <libguile.h>
+#include <string>
+
+/*
+ * Unused base class to document the structure of the current Scheme option
+ * vector, re-expressed in C++. The comment-numbers on the right indicate which
+ * item in the Scheme vector each item implements.
+ *
+ * Not everything here needs to be implemented, nor will it necessarily be
+ * implemented the same way. For example, part of the purpose of this redesign
+ * is to convert from saving options as strings of Scheme code to some form of
+ * key-value pair in the book options, so generate_restore_form() will likely be
+ * supplanted with save_to_book().
+
+template <typename ValueType>
+class GncOptionBase
+{
+public:
+    virtual ~GncOption = default;
+    virtual ValueType get_value() const = 0;                             //5
+    virtual ValueType get_default_value() = 0;
+    virtual SCM get_SCM_value() = 0;
+    virtual SCM get_SCM_default_value() const = 0;                       //7
+    virtual void set_value(ValueType) = 0;                               //6
+// generate_restore_form outputs a Scheme expression (a "form") that finds an
+// option and sets it to the current value. e.g.:
+//(let ((option (gnc:lookup-option options
+//                                 "Display"
+//                                 "Amount")))
+//  ((lambda (option) (if option ((gnc:option-setter option) 'none))) option))
+// it uses gnc:value->string to generate the "'none" (or whatever the option's
+// value would be as input to the scheme interpreter).
+
+    virtual std::string generate_restore_form();                         //8
+    virtual void save_to_book(QofBook*) const noexcept;                  //9
+    virtual void read_from_book(QofBook*);                               //10
+    virtual std::vector<std::string> get_option_strings();               //15
+    virtual set_changed_callback(std::function<void(void*)>);            //14
+protected:
+    const std::string m_section;                                         //0
+    const std::string m_name;                                            //1
+    const std::string m_sort_tag;                                        //2
+    const std::type_info m_kvp_type;                                     //3
+    const std::string m_doc_string;                                      //4
+    std::function<void(void*)> m_changed_callback;   //Part of the make-option closure
+    std::function<void(void*)>m_option_widget_changed_callback;          //16
+};
+*/
+
+struct OptionClassifier
+{
+    const std::string m_section;
+    const std::string m_name;
+    const std::string m_sort_tag;
+//    const std::type_info m_kvp_type;
+    const std::string m_doc_string;
+};
+
+template <typename ValueType>
+SCM scm_from_value(ValueType);
+
+/* This design pattern is called the Curiously Recursive Template Pattern, or
+ * CRTP. See https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
+ * for a detailed explanation.
+ */
+template <typename ValueType, class ValueClass>
+class GncOption
+{
+public:
+    ValueType get_value() const
+    {
+        return static_cast<ValueClass const&>(*this).get_value();
+    }
+    void set_value(ValueType value)
+    {
+        static_cast<ValueClass&>(*this).set_value(value);
+    }
+    ValueType get_default_value() const
+    {
+        return static_cast<ValueClass const&>(*this).get_default_value();
+    }
+    SCM get_scm_value()
+    {
+        ValueType value{static_cast<ValueClass const&>(*this).get_value()};
+        return scm_from_value<ValueType>(value);
+    }
+    SCM get_scm_default_value()
+    {
+        ValueType value{static_cast<ValueClass const&>(*this).get_default_value()};
+        return scm_from_value<ValueType>(value);
+    }
+};
+
+template <typename ValueType>
+class GncOptionValue :
+    public OptionClassifier,
+    public GncOption<ValueType, GncOptionValue<ValueType>>
+{
+public:
+    GncOptionValue<ValueType>(const char* section, const char* name,
+                              const char* key, const char* doc_string,
+                              ValueType value) :
+        OptionClassifier{section, name, key, doc_string},
+        m_value{value}, m_default_value{value} {}
+    ValueType get_value() const { return m_value; }
+    ValueType get_default_value() const { return m_default_value; }
+    void set_value(ValueType new_value) { m_value = new_value; }
+protected:
+    ValueType m_value;
+    ValueType m_default_value;
+};
+
+template <typename ValueType>
+class GncOptionValidatedValue :
+    public OptionClassifier,
+    public GncOption<ValueType, GncOptionValidatedValue<ValueType>>
+{
+public:
+    GncOptionValidatedValue<ValueType>(const char* section, const char* name,
+                                       const char* key, const char* doc_string,
+                                       ValueType value,
+                                      std::function<bool(ValueType)>validator) :
+        OptionClassifier{section, name, key, doc_string},
+        m_value{value}, m_default_value{value}, m_validator{validator}
+        {
+            if (!this->validate(value))
+            throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
+        }
+    GncOptionValidatedValue<ValueType>(const char* section, const char* name,
+                                       const char* key, const char* doc_string,
+                                       ValueType value,
+                                       std::function<bool(ValueType)>validator,
+                                       ValueType val_data) :
+        OptionClassifier{section, name, key, doc_string}, m_value{value},
+        m_default_value{value}, m_validator{validator}, m_validation_data{val_data}
+    {
+            if (!this->validate(value))
+            throw std::invalid_argument("Attempt to create GncValidatedOption with bad value.");
+    }
+    ValueType get_value() const { return m_value; }
+    ValueType get_default_value() const { return m_default_value; }
+    bool validate(ValueType value) { return m_validator(value); }
+    void set_value(ValueType value)
+    {
+        if (this->validate(value))
+            m_value = value;
+        else
+            throw std::invalid_argument("Validation failed, value not set.");
+    }
+private:
+    ValueType m_value;
+    ValueType m_default_value;
+    std::function<bool(ValueType)> m_validator;                         //11
+    ValueType m_validation_data;
+};
+
+std::shared_ptr<GncOptionValue<std::string>>
+gnc_make_string_option(const char* section, const char* name,
+                       const char* key, const char* doc_string,
+                       std::string value);
+
+std::shared_ptr<GncOptionValue<std::string>>
+gnc_make_text_option(const char* section, const char* name,
+                     const char* key, const char* doc_string,
+                     std::string value);
+
+std::shared_ptr<GncOptionValue<QofInstance*>>
+gnc_make_budget_option(const char* section, const char* name,
+                       const char* key, const char* doc_string,
+                       GncBudget* value);
+
+std::shared_ptr<GncOptionValue<QofInstance*>>
+gnc_make_commodity_option(const char* section, const char* name,
+                          const char* key, const char* doc_string,
+                          gnc_commodity* value);
+
+std::shared_ptr<GncOptionValidatedValue<QofInstance*>>
+gnc_make_currency_option(const char* section, const char* name,
+                         const char* key, const char* doc_string,
+                         gnc_commodity* value);
+
+#endif //GNC_OPTION_HPP_
diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt
index e92468496..426f4f2a6 100644
--- a/libgnucash/app-utils/test/CMakeLists.txt
+++ b/libgnucash/app-utils/test/CMakeLists.txt
@@ -1,3 +1,4 @@
+set(MODULEPATH ${CMAKE_SOURCE_DIR}/libgnucash/app-utils)
 
 set(APP_UTILS_TEST_INCLUDE_DIRS
   ${CMAKE_BINARY_DIR}/common # for config.h
@@ -28,6 +29,25 @@ gnc_add_test_with_guile(test-scm-query-string test-scm-query-string.cpp
 )
 add_app_utils_test(test-sx test-sx.cpp)
 
+set(gtest_gnc_option_SOURCES
+  ${MODULEPATH}/gnc-option.cpp
+  gtest-gnc-option.cpp)
+
+set(gtest_gnc_option_INCLUDES
+  ${MODULEPATH}
+  ${CMAKE_SOURCE_DIR}/libgnucash/engine
+  ${CMAKE_BINARY_DIR}/common # for config.h
+  ${GLIB2_INCLUDE_DIRS}
+  ${GUILE_INCLUDE_DIRS})
+
+set(gtest_gnc_option_LIBS
+  gncmod-engine
+  ${GLIB2_LDFLAGS}
+  ${GUILE_LDFLAGS}
+  gtest)
+
+gnc_add_test(test-gnc-option "${gtest_gnc_option_SOURCES}" gtest_gnc_option_INCLUDES gtest_gnc_option_LIBS)
+
 set(GUILE_DEPENDS
   scm-test-engine
   scm-app-utils
diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp
new file mode 100644
index 000000000..ca309f499
--- /dev/null
+++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp
@@ -0,0 +1,174 @@
+/********************************************************************
+ * gtest-gnc-option.cpp -- unit tests for GncOption class.          *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+ *******************************************************************/
+
+#include <gtest/gtest.h>
+#include <gnc-option.hpp>
+
+TEST(GncOption, test_string_ctor)
+{
+    EXPECT_NO_THROW({
+            auto option = gnc_make_string_option("foo", "bar", "baz",
+                                                 "Phony Option",
+                                                 std::string{"waldo"});
+        });
+}
+
+TEST(GncOption, test_text_ctor)
+{
+    EXPECT_NO_THROW({
+            auto option = gnc_make_text_option("foo", "bar", "baz",
+                                               "Phony Option",
+                                               std::string{"waldo"});
+        });
+}
+
+TEST(GncOption, test_string_default_value)
+{
+    auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
+                                         std::string{"waldo"});
+    EXPECT_STREQ("waldo", option->get_default_value().c_str());
+    EXPECT_STREQ("waldo", option->get_value().c_str());
+}
+
+TEST(GncOption, test_string_value)
+{
+    auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
+                                         std::string{"waldo"});
+    option->set_value("pepper");
+    EXPECT_STREQ("waldo", option->get_default_value().c_str());
+    EXPECT_NO_THROW({
+            EXPECT_STREQ("pepper", option->get_value().c_str());
+        });
+}
+
+TEST(GncOption, test_string_scm_functions)
+{
+    auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option",
+                                         std::string{"waldo"});
+    auto scm_value = option->get_scm_value();
+    auto str_value = scm_to_utf8_string(scm_value);
+    EXPECT_STREQ("waldo", str_value);
+    g_free(str_value);
+    scm_value = option->get_scm_default_value();
+    str_value = scm_to_utf8_string(scm_value);
+    EXPECT_STREQ("waldo", str_value);
+    g_free(str_value);
+}
+
+TEST(GNCOption, test_budget_ctor)
+{
+    auto book = qof_book_new();
+    auto budget = gnc_budget_new(book);
+    EXPECT_NO_THROW({
+            auto option = gnc_make_budget_option("foo", "bar", "baz",
+                                                 "Phony Option", budget);
+        });
+    gnc_budget_destroy(budget);
+    qof_book_destroy(book);
+}
+
+TEST(GNCOption, test_budget_scm_functions)
+{
+    auto book = qof_book_new();
+    auto budget = gnc_budget_new(book);
+    auto option = gnc_make_budget_option("foo", "bar", "baz",
+                                         "Phony Option", budget);
+    auto scm_budget = option->get_scm_value();
+    auto str_value = scm_to_utf8_string(scm_budget);
+    auto guid = guid_to_string(qof_instance_get_guid(budget));
+    EXPECT_STREQ(guid, str_value);
+    g_free(guid);
+    gnc_budget_destroy(budget);
+    qof_book_destroy(book);
+}
+
+TEST(GNCOption, test_commodity_ctor)
+{
+    auto book = qof_book_new();
+    auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
+                                    "NYSE", "HPE", NULL, 1);
+    EXPECT_NO_THROW({
+            auto option = gnc_make_commodity_option("foo", "bar", "baz",
+                                                 "Phony Option", hpe);
+        });
+    gnc_commodity_destroy(hpe);
+    qof_book_destroy(book);
+}
+
+TEST(GNCOption, test_currency_ctor)
+{
+    auto book = qof_book_new();
+    auto table = gnc_commodity_table_new();
+    qof_book_set_data(book, GNC_COMMODITY_TABLE, table);
+    auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
+                                    "NYSE", "HPE", NULL, 1);
+    EXPECT_THROW({
+            auto option = gnc_make_currency_option("foo", "bar", "baz",
+                                                 "Phony Option", hpe);
+        }, std::invalid_argument);
+    gnc_commodity_destroy(hpe);
+    auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100);
+    EXPECT_NO_THROW({
+            auto option = gnc_make_currency_option("foo", "bar", "baz",
+                                                 "Phony Option", eur);
+        });
+    gnc_commodity_destroy(eur);
+    auto usd = gnc_commodity_new(book, "United States Dollar",
+                                 "CURRENCY", "USD", NULL, 100);
+    EXPECT_NO_THROW({
+            auto option = gnc_make_currency_option("foo", "bar", "baz",
+                                                 "Phony Option", usd);
+        });
+    gnc_commodity_destroy(usd);
+    qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr);
+    gnc_commodity_table_destroy(table);
+    qof_book_destroy(book);
+}
+
+TEST(GNCOption, test_currency_setter)
+{
+    auto book = qof_book_new();
+    auto table = gnc_commodity_table_new();
+    qof_book_set_data(book, GNC_COMMODITY_TABLE, table);
+    auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
+                                    "NYSE", "HPE", NULL, 1);
+    auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100);
+    auto option = gnc_make_currency_option("foo", "bar", "baz",
+                                                 "Phony Option", eur);
+    auto usd = gnc_commodity_new(book, "United States Dollar",
+                                 "CURRENCY", "USD", NULL, 100);
+    EXPECT_NO_THROW({
+            option->set_value(QOF_INSTANCE(usd));
+        });
+    EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option->get_value()));
+    EXPECT_THROW({
+            option->set_value(QOF_INSTANCE(hpe));
+        }, std::invalid_argument);
+    EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option->get_value()));
+    gnc_commodity_destroy(hpe);
+    gnc_commodity_destroy(usd);
+    gnc_commodity_destroy(eur);
+    qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr);
+    gnc_commodity_table_destroy(table);
+    qof_book_destroy(book);
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1066cf100..3c28396e2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -528,6 +528,8 @@ libgnucash/app-utils/gnc-exp-parser.c
 libgnucash/app-utils/gnc-gsettings.c
 libgnucash/app-utils/gnc-helpers.c
 libgnucash/app-utils/gnc-help-utils.c
+libgnucash/app-utils/gncmod-app-utils.c
+libgnucash/app-utils/gnc-option.cpp
 libgnucash/app-utils/gnc-prefs-utils.c
 libgnucash/app-utils/gnc-state.c
 libgnucash/app-utils/gnc-sx-instance-model.c

commit b495da4e2952fa45c8a98c70e914ec5d12049e2e
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jul 11 14:35:55 2019 -0700

    [C++ Options] Remove some unused C API from options-utils.

diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c
index 05c6e621e..01c475204 100644
--- a/libgnucash/app-utils/option-util.c
+++ b/libgnucash/app-utils/option-util.c
@@ -720,7 +720,7 @@ gnc_option_default_getter (GNCOption *option)
                                         option->guile_option);
 }
 
-/********************************************************************\
+/* ******************************************************************\
  * gnc_option_value_validator                                       *
  *   returns the SCM handle for the option value validator function.*
  *   This value should be tested with scm_procedure_p before use.   *
@@ -728,8 +728,8 @@ gnc_option_default_getter (GNCOption *option)
  * Args: option - the GNCOption                                     *
  * Returns: SCM handle to function                                  *
 \********************************************************************/
-SCM
-gnc_option_value_validator (GNCOption *option)
+static SCM
+gnc_option_value_validator(GNCOption *option)
 {
     initialize_getters ();
 
@@ -737,7 +737,8 @@ gnc_option_value_validator (GNCOption *option)
                                         option->guile_option);
 }
 
-/********************************************************************\
+
+/* ******************************************************************\
  * gnc_option_widget_changed_proc_getter                            *
  *   returns the SCM handle for the function to be called if the    *
  *   GUI widget representing the option is changed.                 *
@@ -748,8 +749,8 @@ gnc_option_value_validator (GNCOption *option)
  * Returns: SCM handle to function                                  *
  *          If no such function exists, returns SCM_UNDEFINED.      *
 \********************************************************************/
-SCM
-gnc_option_widget_changed_proc_getter (GNCOption *option)
+static SCM
+gnc_option_widget_changed_proc_getter(GNCOption *option)
 {
     SCM cb;
 
@@ -1151,36 +1152,6 @@ gnc_option_use_alpha (GNCOption *option)
     return scm_is_true (value);
 }
 
-/********************************************************************\
- * gnc_option_get_color_argb                                        *
- *   returns the argb value of a color option                       *
- *                                                                  *
- * Args: option - the GNCOption                                     *
- * Returns: argb value of option                                    *
-\********************************************************************/
-guint32
-gnc_option_get_color_argb (GNCOption *option)
-{
-    gdouble red, green, blue, alpha;
-    guint32 color = 0;
-
-    if (!gnc_option_get_color_info (option, FALSE, &red, &green, &blue, &alpha))
-        return 0;
-
-    color |= (guint32) (alpha * 255.0);
-    color <<= 8;
-
-    color |= (guint32) (red * 255.0);
-    color <<= 8;
-
-    color |= (guint32) (green * 255.0);
-    color <<= 8;
-
-    color |= (guint32) (blue * 255.0);
-
-    return color;
-}
-
 /********************************************************************\
  * gnc_option_get_color_info                                        *
  *   gets the color information from a color option. rgba values    *
@@ -1317,21 +1288,6 @@ compare_option_tags (gconstpointer a, gconstpointer b)
     return result;
 }
 
-/********************************************************************\
- * gnc_option_db_dirty                                              *
- *   returns true if guile has registered more options into the     *
- *   database since the last time the database was cleaned.         *
- *                                                                  *
- * Returns: dirty flag                                              *
-\********************************************************************/
-gboolean
-gnc_option_db_dirty (GNCOptionDB *odb)
-{
-    g_return_val_if_fail (odb, FALSE);
-
-    return odb->options_dirty;
-}
-
 /********************************************************************\
  * gnc_option_db_clean                                              *
  *   resets the dirty flag of the option database                   *
@@ -1525,38 +1481,6 @@ gnc_option_db_get_option_by_name (GNCOptionDB *odb,
     return NULL;
 }
 
-/********************************************************************\
- * gnc_option_db_get_option_by_SCM                                  *
- *   returns an option given SCM handle. Uses section and name.     *
- *                                                                  *
- * Args: odb          - option database to search in                *
- *       guile_option - SCM handle of option                        *
- * Returns: given option, or NULL if none                           *
-\********************************************************************/
-GNCOption *
-gnc_option_db_get_option_by_SCM (GNCOptionDB *odb, SCM guile_option)
-{
-    GNCOption option_key;
-    GNCOption *option;
-    char *section_name;
-    char *name;
-
-    option_key.guile_option = guile_option;
-
-    section_name = gnc_option_section (&option_key);
-    name = gnc_option_name (&option_key);
-
-    option = gnc_option_db_get_option_by_name (odb, section_name, name);
-
-    if (section_name != NULL)
-        free (section_name);
-
-    if (name != NULL)
-        free (name);
-
-    return option;
-}
-
 static SCM
 gnc_option_valid_value (GNCOption *option, SCM value)
 {
@@ -1776,30 +1700,6 @@ gnc_option_db_section_reset_widgets (GNCOptionSection *section)
     }
 }
 
-/********************************************************************\
- * gnc_option_db_reset_widgets                                      *
- *   reset all option widgets to their default values.              *
- *                                                                  *
- * Args: odb - option database to reset                             *
- * Return: nothing                                                  *
-\********************************************************************/
-void
-gnc_option_db_reset_widgets (GNCOptionDB *odb)
-{
-    GSList *section_node;
-    GNCOptionSection *section;
-
-    g_return_if_fail (odb);
-
-    for (section_node = odb->option_sections;
-            section_node != NULL;
-            section_node = section_node->next)
-    {
-        section = section_node->data;
-        gnc_option_db_section_reset_widgets (section);
-    }
-}
-
 /********************************************************************\
  * gnc_option_db_get_default_section                                *
  *   returns the malloc'd section name of the default section,      *
@@ -1939,69 +1839,6 @@ gnc_option_db_lookup_string_option (GNCOptionDB *odb,
     return strdup (default_value);
 }
 
-/********************************************************************\
- * gnc_option_db_lookup_font_option                                 *
- *   looks up a font option. If present, returns its malloc'ed      *
- *   string value, otherwise returns the strdup'ed default, or NULL *
- *   if default was NULL.                                           *
- *                                                                  *
- * Args: odb     - option database to search in                     *
- *       section - section name of option                           *
- *       name    - name of option                                   *
- *       default - default value if not found                       *
- * Return: char * option value                                      *
-\********************************************************************/
-char *
-gnc_option_db_lookup_font_option (GNCOptionDB *odb,
-                                  const char *section,
-                                  const char *name,
-                                  const char *default_value)
-{
-    return gnc_option_db_lookup_string_option (odb, section, name, default_value);
-}
-
-/********************************************************************\
- * gnc_option_db_lookup_multichoice_option                          *
- *   looks up a multichoice option. If present, returns its         *
- *   name as a malloc'ed string                                     *
- *   value, otherwise returns the strdup'ed default, or NULL if     *
- *   default was NULL.                                              *
- *                                                                  *
- * Args: odb     - option database to search in                     *
- *       section - section name of option                           *
- *       name    - name of option                                   *
- *       default - default value if not found                       *
- * Return: char * option value                                      *
-\********************************************************************/
-char *
-gnc_option_db_lookup_multichoice_option (GNCOptionDB *odb,
-                                         const char *section,
-                                         const char *name,
-                                         const char *default_value)
-{
-    GNCOption *option;
-    SCM getter;
-    SCM value;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-
-    if (option != NULL)
-    {
-        getter = gnc_option_getter (option);
-        if (getter != SCM_UNDEFINED)
-        {
-            value = scm_call_0 (getter);
-            if (scm_is_symbol (value))
-                return gnc_scm_symbol_to_locale_string (value);
-        }
-    }
-
-    if (default_value == NULL)
-        return NULL;
-
-    return strdup (default_value);
-}
-
 /********************************************************************\
  * gnc_option_db_lookup_number_option                               *
  *   looks up a number option. If present, returns its value        *
@@ -2038,185 +1875,6 @@ gnc_option_db_lookup_number_option (GNCOptionDB *odb,
     return default_value;
 }
 
-/********************************************************************\
- * gnc_option_db_lookup_color_option                                *
- *   looks up a color option. If present, returns its value in the  *
- *   color variable, otherwise leaves the color variable alone.     *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       red       - where to store the red value                   *
- *       blue      - where to store the blue value                  *
- *       green     - where to store the green value                 *
- *       alpha     - where to store the alpha value                 *
- * Return: true if option was found                                 *
-\********************************************************************/
-gboolean gnc_option_db_lookup_color_option (GNCOptionDB *odb,
-                                            const char *section,
-                                            const char *name,
-                                            gdouble *red,
-                                            gdouble *green,
-                                            gdouble *blue,
-                                            gdouble *alpha)
-{
-    GNCOption *option;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-
-    return gnc_option_get_color_info (option, FALSE, red, green, blue, alpha);
-}
-
-/********************************************************************\
- * gnc_option_db_lookup_color_option_argb                           *
- *   looks up a color option. If present, returns its argb value,   *
- *   otherwise returns the given default value.                     *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       default_value - default value to return if problem         *
- * Return: argb value                                               *
-\********************************************************************/
-guint32 gnc_option_db_lookup_color_option_argb (GNCOptionDB *odb,
-                                                const char *section,
-                                                const char *name,
-                                                guint32 default_value)
-{
-    GNCOption *option;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-    if (option == NULL)
-        return default_value;
-
-    return gnc_option_get_color_argb (option);
-}
-
-/********************************************************************\
- * gnc_option_db_lookup_list_option                                 *
- *   looks up a list option. If present, returns its value as a     *
- *   list of strings representing the symbols.                      *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       default_value - default value to return if problem         *
- * Return: list of values                                           *
-\********************************************************************/
-GSList *
-gnc_option_db_lookup_list_option (GNCOptionDB *odb,
-                                  const char *section,
-                                  const char *name,
-                                  GSList *default_value)
-{
-    GNCOption *option;
-    GSList *list = NULL;
-    SCM getter;
-    SCM value;
-    SCM item;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-    if (option == NULL)
-        return default_value;
-
-    getter = gnc_option_getter (option);
-    if (getter == SCM_UNDEFINED)
-        return default_value;
-
-    value = scm_call_0 (getter);
-    while (scm_is_list (value) && !scm_is_null (value))
-    {
-        item = SCM_CAR(value);
-        value = SCM_CDR(value);
-
-        if (!scm_is_symbol (item))
-        {
-            gnc_free_list_option_value (list);
-
-            return default_value;
-        }
-
-        list = g_slist_prepend (list, gnc_scm_symbol_to_locale_string (item));
-    }
-
-    if (!scm_is_list (value) || !scm_is_null (value))
-    {
-        gnc_free_list_option_value (list);
-
-        return default_value;
-    }
-    return list;
-}
-
-/********************************************************************\
- * gnc_option_db_lookup_currency_option                             *
- *   looks up a currency option. If present, returns its value as a *
- *   gnc_commodity object.                                          *
- *                                                                  *
- * Args: odb       - option database to search in                   *
- *       section   - section name of option                         *
- *       name      - name of option                                 *
- *       default_value - default value to return if problem         *
- * Return: commodity or NULL if no commodity found                  *
-\********************************************************************/
-gnc_commodity *
-gnc_option_db_lookup_currency_option (GNCOptionDB *odb,
-                                      const char *section,
-                                      const char *name,
-                                      gnc_commodity *default_value)
-{
-    GNCOption *option;
-    SCM getter;
-    SCM value;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-    if (option == NULL)
-        return default_value;
-
-    getter = gnc_option_getter (option);
-    if (getter == SCM_UNDEFINED)
-        return default_value;
-
-    value = scm_call_0 (getter);
-
-    return gnc_scm_to_commodity (value);
-}
-
-static void
-free_helper (gpointer string, gpointer not_used)
-{
-    if (string)
-        free (string);
-}
-
-void
-gnc_free_list_option_value (GSList *list)
-{
-    g_slist_foreach (list, free_helper, NULL);
-    g_slist_free (list);
-}
-
-/********************************************************************\
- * gnc_option_db_set_option_default                                 *
- *   set the option to its default value                            *
- *                                                                  *
- * Args: odb     - option database to search in                     *
- *       section - section name of option                           *
- *       name    - name of option                                   *
- * Returns: nothing                                                 *
-\********************************************************************/
-void
-gnc_option_db_set_option_default (GNCOptionDB *odb,
-                                  const char *section,
-                                  const char *name)
-{
-    GNCOption *option;
-
-    option = gnc_option_db_get_option_by_name (odb, section, name);
-
-    gnc_option_set_default (option);
-}
-
 /********************************************************************\
  * gnc_option_db_set_option                                         *
  *   sets the option to the given value. If successful              *
diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h
index d555495a7..c059e1896 100644
--- a/libgnucash/app-utils/option-util.h
+++ b/libgnucash/app-utils/option-util.h
@@ -201,26 +201,93 @@ guint32 gnc_option_db_lookup_color_option_argb (GNCOptionDB *odb,
                                                 const char *name,
                                                 guint32 default_value);
 
-GSList * gnc_option_db_lookup_list_option (GNCOptionDB *odb,
-                                           const char *section,
-                                           const char *name,
-                                           GSList *default_value);
+void gnc_option_db_unregister_change_callback_id(GNCOptionDB *odb,
+        SCM callback_id);
+
+char * gnc_option_section(GNCOption *option);
+char * gnc_option_name(GNCOption *option);
+char * gnc_option_type(GNCOption *option);
+char * gnc_option_sort_tag(GNCOption *option);
+char * gnc_option_documentation(GNCOption *option);
+SCM    gnc_option_getter(GNCOption *option);
+SCM    gnc_option_setter(GNCOption *option);
+SCM    gnc_option_default_getter(GNCOption *option);
+SCM    gnc_option_get_option_data(GNCOption *option);
+
+int    gnc_option_num_permissible_values(GNCOption *option);
+int    gnc_option_permissible_value_index(GNCOption *option, SCM value);
+SCM    gnc_option_permissible_value(GNCOption *option, int index);
+char * gnc_option_permissible_value_name(GNCOption *option, int index);
+char * gnc_option_permissible_value_description(GNCOption *option, int index);
+
+gboolean gnc_option_show_time(GNCOption *option);
+
+gboolean gnc_option_multiple_selection(GNCOption *option);
+GList * gnc_option_get_account_type_list(GNCOption *option);
+
+gboolean gnc_option_get_range_info(GNCOption *option,
+                                   double *lower_bound,
+                                   double *upper_bound,
+                                   int    *num_decimals,
+                                   double *step_size);
+
+gdouble  gnc_option_color_range(GNCOption *option);
+gdouble  gnc_option_use_alpha(GNCOption *option);
+gboolean gnc_option_get_color_info(GNCOption *option,
+                                   gboolean use_default,
+                                   gdouble *red,
+                                   gdouble *green,
+                                   gdouble *blue,
+                                   gdouble *alpha);
+
+void gnc_option_call_option_widget_changed_proc (GNCOption *option);
+
+void gnc_option_set_default(GNCOption *option);
 
-void gnc_free_list_option_value (GSList *list);
+guint gnc_option_db_num_sections(GNCOptionDB *odb);
 
-gnc_commodity * gnc_option_db_lookup_currency_option (GNCOptionDB *odb,
-                                                      const char *section,
-                                                      const char *name,
-                                                      gnc_commodity *default_value);
+const char * gnc_option_section_name(GNCOptionSection *section);
+guint  gnc_option_section_num_options(GNCOptionSection *section);
 
-void gnc_option_db_set_option_default (GNCOptionDB *odb,
-                                       const char *section,
-                                       const char *name);
+GNCOptionSection * gnc_option_db_get_section(GNCOptionDB *odb, gint i);
 
-gboolean gnc_option_db_set_option (GNCOptionDB *odb,
-                                   const char *section,
-                                   const char *name,
-                                   SCM value);
+GNCOption * gnc_get_option_section_option(GNCOptionSection *section, int i);
+
+GNCOption * gnc_option_db_get_option_by_name(GNCOptionDB *odb,
+        const char *section_name,
+        const char *name);
+
+void     gnc_option_db_clean(GNCOptionDB *odb);
+
+gboolean gnc_option_db_get_changed(GNCOptionDB *odb);
+GList* gnc_option_db_commit(GNCOptionDB *odb);
+
+char * gnc_option_db_get_default_section(GNCOptionDB *odb);
+
+SCM gnc_option_db_lookup_option(GNCOptionDB *odb,
+                                const char *section,
+                                const char *name,
+                                SCM default_value);
+
+gboolean gnc_option_db_lookup_boolean_option(GNCOptionDB *odb,
+        const char *section,
+        const char *name,
+        gboolean default_value);
+
+char * gnc_option_db_lookup_string_option(GNCOptionDB *odb,
+        const char *section,
+        const char *name,
+        const char *default_value);
+
+gdouble gnc_option_db_lookup_number_option(GNCOptionDB *odb,
+        const char *section,
+        const char *name,
+        gdouble default_value);
+
+gboolean gnc_option_db_set_option(GNCOptionDB *odb,
+                                  const char *section,
+                                  const char *name,
+                                  SCM value);
 
 gboolean gnc_option_db_set_number_option (GNCOptionDB *odb,
                                           const char *section,
@@ -281,7 +348,4 @@ void gncp_option_invoke_callback (GNCOptionChangeCallback callback,
 /* Reset all the widgets in one section to their default values */
 void gnc_option_db_section_reset_widgets (GNCOptionSection *section);
 
-/* Reset all the widgets to their default values */
-void gnc_option_db_reset_widgets (GNCOptionDB *odb);
-
 #endif /* OPTION_UTIL_H */

commit 0b77641e9f0506be331c69afbb98a9f103e9d880
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jul 5 14:29:33 2019 -0700

    Remove gnc_options_dialog bindings from gnome-utils.i.
    
    They're unused.

diff --git a/gnucash/gnome-utils/gnome-utils.i b/gnucash/gnome-utils/gnome-utils.i
index c5c4fe310..b9bcccf2f 100644
--- a/gnucash/gnome-utils/gnome-utils.i
+++ b/gnucash/gnome-utils/gnome-utils.i
@@ -24,7 +24,6 @@
 #include <config.h>
 #include <gtk/gtk.h>
 #include <glib-object.h>
-#include <dialog-options.h>
 #include <dialog-utils.h>
 #include <gnc-amount-edit.h>
 #include <gnc-date-edit.h>
@@ -49,13 +48,6 @@ SCM scm_init_sw_gnome_utils_module (void);
 
 %import "base-typemaps.i"
 
-GNCOptionWin * gnc_options_dialog_new(gchar *title, GtkWindow* parent);
-void gnc_options_dialog_destroy(GNCOptionWin * win);
-void gnc_options_dialog_build_contents(GNCOptionWin *propertybox,
-                                       GNCOptionDB  *odb);
-void gnc_options_dialog_set_scm_callbacks (GNCOptionWin *win,
-        SCM apply_cb, SCM close_cb);
-
 gboolean
 gnc_verify_dialog (GtkWindow *parent, gboolean yes_is_default,
 		   const gchar *format, ...);



Summary of changes:
 CMakeLists.txt                                     |    2 +-
 bindings/engine.i                                  |    4 -
 bindings/guile/glib-guile.c                        |    2 +-
 bindings/guile/glib-guile.h                        |    2 +-
 gnucash/gnome-utils/CMakeLists.txt                 |    8 +-
 gnucash/gnome-utils/dialog-options.c               | 4351 --------------------
 gnucash/gnome-utils/dialog-options.cpp             | 2789 +++++++++++++
 gnucash/gnome-utils/dialog-options.h               |  116 -
 gnucash/gnome-utils/dialog-options.hpp             |  200 +
 gnucash/gnome-utils/dialog-utils.c                 |   45 -
 gnucash/gnome-utils/gnc-gnome-utils.c              |   63 -
 gnucash/gnome-utils/gnc-gnome-utils.h              |   16 -
 gnucash/gnome-utils/gnc-gobject-utils.h            |    2 +-
 gnucash/gnome-utils/gnc-icons.c                    |    1 +
 .../{gnc-main-window.c => gnc-main-window.cpp}     |  709 ++--
 gnucash/gnome-utils/gnc-main-window.h              |   11 +-
 gnucash/gnome-utils/gnome-utils.i                  |    8 -
 gnucash/gnome/CMakeLists.txt                       |   14 +-
 ...sistant-hierarchy.c => assistant-hierarchy.cpp} |  241 +-
 gnucash/gnome/assistant-hierarchy.h                |    7 +
 gnucash/gnome/business-options-gnome.c             |  501 ---
 gnucash/gnome/business-options-gnome.cpp           |  224 +
 gnucash/gnome/business-options-gnome.h             |   11 +-
 gnucash/gnome/dialog-custom-report.c               |    2 -
 gnucash/gnome/dialog-report-column-view.c          |  705 ----
 gnucash/gnome/dialog-report-column-view.cpp        |  565 +++
 ...column-view.h => dialog-report-column-view.hpp} |    6 +-
 ...style-sheet.c => dialog-report-style-sheet.cpp} |   96 +-
 gnucash/gnome/dialog-report-style-sheet.h          |   11 +-
 gnucash/gnome/gnc-budget-view.c                    |    2 -
 gnucash/gnome/gnc-plugin-page-budget.c             |    3 +-
 ...in-page-report.c => gnc-plugin-page-report.cpp} |  308 +-
 gnucash/gnome/gnc-plugin-page-report.h             |    9 +-
 gnucash/gnome/top-level.c                          |    1 -
 .../gnome/{window-report.c => window-report.cpp}   |  132 +-
 gnucash/gnome/window-report.h                      |   12 +-
 gnucash/gnucash-commands.cpp                       |    2 +-
 gnucash/gnucash-core-app.cpp                       |    2 +-
 gnucash/gnucash.cpp                                |    5 +-
 gnucash/html/gnc-html-extras.h                     |    2 +-
 gnucash/html/gnc-html-history.h                    |    2 +-
 gnucash/html/gnc-html-webkit2.c                    |    4 +-
 gnucash/html/gnc-html.c                            |   14 +-
 gnucash/html/gnc-html.h                            |    1 +
 gnucash/html/gnc-html.i                            |    4 +-
 gnucash/report/CMakeLists.txt                      |    2 +-
 gnucash/report/{gnc-report.c => gnc-report.cpp}    |   44 +-
 gnucash/report/gnc-report.h                        |   47 +-
 gnucash/report/html-utilities.scm                  |    6 +-
 gnucash/report/report-core.scm                     |    9 +-
 .../report/reports/standard/account-summary.scm    |  111 +-
 .../standard/test/test-equity-statement.scm        |    4 +-
 .../reports/standard/test/test-portfolios.scm      |    4 +-
 .../report/reports/standard/test/test-register.scm |    4 +-
 .../reports/standard/test/test-stress-options.scm  |    9 +-
 .../reports/standard/test/test-trial-balance.scm   |    4 +-
 gnucash/report/reports/standard/view-column.scm    |   27 +-
 libgnucash/app-utils/CMakeLists.txt                |   51 +-
 libgnucash/app-utils/app-utils.i                   |   32 +-
 libgnucash/app-utils/app-utils.scm                 |    4 +-
 libgnucash/app-utils/business-options.c            |   92 -
 libgnucash/app-utils/business-options.h            |   56 -
 libgnucash/app-utils/business-options.scm          |  386 --
 libgnucash/app-utils/business-prefs.scm            |  199 -
 libgnucash/app-utils/gnc-option-date.cpp           |  576 +++
 libgnucash/app-utils/gnc-option-date.hpp           |  189 +
 libgnucash/app-utils/gnc-option-impl.cpp           |  986 +++++
 libgnucash/app-utils/gnc-option-impl.hpp           | 1078 +++++
 libgnucash/app-utils/gnc-option-ui.hpp             |   63 +
 .../app-utils/gnc-option-uitype.hpp                |   79 +-
 libgnucash/app-utils/gnc-option.cpp                |  546 +++
 libgnucash/app-utils/gnc-option.hpp                |  261 ++
 libgnucash/app-utils/gnc-optiondb-impl.hpp         |  191 +
 libgnucash/app-utils/gnc-optiondb.cpp              | 1251 ++++++
 libgnucash/app-utils/gnc-optiondb.h                |  175 +
 libgnucash/app-utils/gnc-optiondb.hpp              |  893 ++++
 libgnucash/app-utils/gnc-optiondb.i                | 1913 +++++++++
 libgnucash/app-utils/gnc-ui-util.c                 |  119 -
 libgnucash/app-utils/gnc-ui-util.h                 |   34 -
 libgnucash/app-utils/option-util.c                 | 2823 -------------
 libgnucash/app-utils/option-util.h                 |  287 --
 libgnucash/app-utils/options.scm                   | 2493 ++---------
 libgnucash/app-utils/test/CMakeLists.txt           |   42 +-
 libgnucash/app-utils/test/gtest-gnc-option.cpp     | 1329 ++++++
 libgnucash/app-utils/test/gtest-gnc-optiondb.cpp   |  408 ++
 libgnucash/app-utils/test/test-app-utils.c         |   55 -
 .../test/test-gnc-option-scheme-output.scm         |  591 +++
 libgnucash/app-utils/test/test-gnc-optiondb.scm    |  249 ++
 libgnucash/app-utils/test/test-gnc-ui-util.c       |  262 --
 libgnucash/app-utils/test/test-option-util.cpp     |  137 -
 libgnucash/app-utils/test/test-options.scm         |   32 +-
 libgnucash/engine/engine-helpers.c                 |   16 -
 libgnucash/engine/engine-helpers.h                 |    5 -
 libgnucash/engine/gnc-date.cpp                     |    4 +-
 libgnucash/engine/gnc-features.c                   |    1 -
 libgnucash/engine/gnc-features.h                   |    1 -
 libgnucash/engine/gnc-numeric.cpp                  |    2 +-
 libgnucash/engine/kvp-value.cpp                    |   36 +-
 libgnucash/engine/kvp-value.hpp                    |    2 +-
 libgnucash/engine/policy-p.h                       |    3 -
 libgnucash/engine/policy.c                         |   83 -
 libgnucash/engine/policy.h                         |   34 +-
 libgnucash/engine/qofbook.cpp                      |  126 +-
 libgnucash/engine/qofbook.h                        |   50 +-
 libgnucash/engine/qofbookslots.h                   |   10 -
 libgnucash/engine/test/test-qofbook.c              |  106 -
 po/POTFILES.in                                     |   26 +-
 107 files changed, 15978 insertions(+), 13895 deletions(-)
 delete mode 100644 gnucash/gnome-utils/dialog-options.c
 create mode 100644 gnucash/gnome-utils/dialog-options.cpp
 delete mode 100644 gnucash/gnome-utils/dialog-options.h
 create mode 100644 gnucash/gnome-utils/dialog-options.hpp
 rename gnucash/gnome-utils/{gnc-main-window.c => gnc-main-window.cpp} (89%)
 rename gnucash/gnome/{assistant-hierarchy.c => assistant-hierarchy.cpp} (91%)
 delete mode 100644 gnucash/gnome/business-options-gnome.c
 create mode 100644 gnucash/gnome/business-options-gnome.cpp
 delete mode 100644 gnucash/gnome/dialog-report-column-view.c
 create mode 100644 gnucash/gnome/dialog-report-column-view.cpp
 rename gnucash/gnome/{dialog-report-column-view.h => dialog-report-column-view.hpp} (95%)
 rename gnucash/gnome/{dialog-report-style-sheet.c => dialog-report-style-sheet.cpp} (88%)
 rename gnucash/gnome/{gnc-plugin-page-report.c => gnc-plugin-page-report.cpp} (90%)
 rename gnucash/gnome/{window-report.c => window-report.cpp} (65%)
 rename gnucash/report/{gnc-report.c => gnc-report.cpp} (91%)
 delete mode 100644 libgnucash/app-utils/business-options.c
 delete mode 100644 libgnucash/app-utils/business-options.h
 delete mode 100644 libgnucash/app-utils/business-options.scm
 delete mode 100644 libgnucash/app-utils/business-prefs.scm
 create mode 100644 libgnucash/app-utils/gnc-option-date.cpp
 create mode 100644 libgnucash/app-utils/gnc-option-date.hpp
 create mode 100644 libgnucash/app-utils/gnc-option-impl.cpp
 create mode 100644 libgnucash/app-utils/gnc-option-impl.hpp
 create mode 100644 libgnucash/app-utils/gnc-option-ui.hpp
 copy gnucash/register/ledger-core/split-register-layout.h => libgnucash/app-utils/gnc-option-uitype.hpp (57%)
 create mode 100644 libgnucash/app-utils/gnc-option.cpp
 create mode 100644 libgnucash/app-utils/gnc-option.hpp
 create mode 100644 libgnucash/app-utils/gnc-optiondb-impl.hpp
 create mode 100644 libgnucash/app-utils/gnc-optiondb.cpp
 create mode 100644 libgnucash/app-utils/gnc-optiondb.h
 create mode 100644 libgnucash/app-utils/gnc-optiondb.hpp
 create mode 100644 libgnucash/app-utils/gnc-optiondb.i
 delete mode 100644 libgnucash/app-utils/option-util.c
 delete mode 100644 libgnucash/app-utils/option-util.h
 create mode 100644 libgnucash/app-utils/test/gtest-gnc-option.cpp
 create mode 100644 libgnucash/app-utils/test/gtest-gnc-optiondb.cpp
 delete mode 100644 libgnucash/app-utils/test/test-app-utils.c
 create mode 100644 libgnucash/app-utils/test/test-gnc-option-scheme-output.scm
 create mode 100644 libgnucash/app-utils/test/test-gnc-optiondb.scm
 delete mode 100644 libgnucash/app-utils/test/test-gnc-ui-util.c
 delete mode 100644 libgnucash/app-utils/test/test-option-util.cpp



More information about the gnucash-changes mailing list