gnucash master: Multiple changes pushed

Geert Janssens gjanssens at code.gnucash.org
Tue Sep 2 17:02:41 EDT 2014


Updated	 via  https://github.com/Gnucash/gnucash/commit/4ecc4cb7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/afa2cc4f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6114a960 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1015a430 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9ca4649e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/afc790d3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e44bb5ff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7b642081 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ac55c953 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b5fa7ee3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/85b910ac (commit)
	 via  https://github.com/Gnucash/gnucash/commit/434b7f6e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/413eb697 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/da9071fe (commit)
	 via  https://github.com/Gnucash/gnucash/commit/58b5a8e1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9cfc22f4 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f8a27b71 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0e701166 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/87654e69 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/ac1990fc (commit)
	 via  https://github.com/Gnucash/gnucash/commit/61cd3f11 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/904afd8b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0a394c5b (commit)
	from  https://github.com/Gnucash/gnucash/commit/48c24d99 (commit)



commit 4ecc4cb7d08c5f7f87f996c59aef7ca15c7c6171
Merge: afa2cc4 6114a96
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Sep 2 22:59:41 2014 +0200

    Merge branch 'maint'
    
    * Bug 711440 - Tab labels have different background colour than containing gui element
    * Bug 434462 - register color don't work correct with system theme color
    * Add scrub function for (business) lots to eliminate unneeded lot links
    * Rewrite invoice payment logic to use lot links only when absolutely needed
    * Bug 684719 - Man pages for gnc-fq-* perl scripts

diff --cc src/engine/gncOwner.c
index 4075425,7ffb5e1..b9d39d9
--- a/src/engine/gncOwner.c
+++ b/src/engine/gncOwner.c
@@@ -51,7 -51,11 +52,9 @@@
  #define _GNC_MOD_NAME   GNC_ID_OWNER
  
  #define GNC_OWNER_ID    "gncOwner"
 -#define GNC_OWNER_TYPE  "owner-type"
 -#define GNC_OWNER_GUID  "owner-guid"
  
+ static QofLogModule log_module = GNC_MOD_ENGINE;
+ 
  GncOwner * gncOwnerNew (void)
  {
      GncOwner *o;

commit afa2cc4febb75b6bd59eee89f8e85277e46f1e2c
Author: Frank H. Ellenberger <frank.h.ellenberger at gmail.com>
Date:   Tue Sep 2 22:59:02 2014 +0200

    Bug 684719 - Man pages for gnc-fq-* perl scripts

diff --git a/src/quotes/Makefile.am b/src/quotes/Makefile.am
index 0391d70..94f0ee4 100644
--- a/src/quotes/Makefile.am
+++ b/src/quotes/Makefile.am
@@ -1,5 +1,9 @@
 bin_SCRIPTS = gnc-fq-check gnc-fq-dump gnc-fq-helper gnc-fq-update
 
+man_MANS = \
+  gnc-fq-dump.1 \
+  gnc-fq-helper.1
+
 EXTRA_DIST = \
   Quote_example.pl \
   gnc-fq-check.in \
@@ -36,4 +40,10 @@ gnc-fq-update: gnc-fq-update.in Makefile
 	chmod +x $@.tmp
 	mv $@.tmp $@
 
+gnc-fq-dump.1: gnc-fq-dump Makefile
+	pod2man $< > $@
+
+gnc-fq-helper.1: gnc-fq-helper Makefile
+	pod2man $< > $@
+
 CLEANFILES = gnc-fq-helper gnc-fq-check gnc-fq-update

commit 6114a960a93065c25352ad36befb5519c7179dc4
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Sep 2 22:45:12 2014 +0200

    Add business lot scrubbing to Check & Repair->All/This transaction(s)

diff --git a/src/gnome/gnc-plugin-page-register.c b/src/gnome/gnc-plugin-page-register.c
index bdbc4da..3acfd35 100644
--- a/src/gnome/gnc-plugin-page-register.c
+++ b/src/gnome/gnc-plugin-page-register.c
@@ -74,6 +74,7 @@
 #include "gnucash-sheet.h"
 #include "dialog-lot-viewer.h"
 #include "Scrub.h"
+#include "ScrubBusiness.h"
 #include "qof.h"
 #include "window-reconcile.h"
 #include "window-autoclear.h"
@@ -3691,6 +3692,8 @@ gnc_plugin_page_register_cmd_scrub_current (GtkAction *action,
     Query *query;
     Account *root;
     Transaction *trans;
+    Split *split;
+    GNCLot *lot;
     SplitRegister *reg;
 
     g_return_if_fail(GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page));
@@ -3717,6 +3720,11 @@ gnc_plugin_page_register_cmd_scrub_current (GtkAction *action,
     root = gnc_get_current_root_account();
     xaccTransScrubOrphans(trans);
     xaccTransScrubImbalance(trans, root, NULL);
+
+    split = gnc_split_register_get_current_split (reg);
+    lot = xaccSplitGetLot (split);
+    if (lot && xaccAccountIsAPARType (xaccAccountGetType (xaccSplitGetAccount (split))))
+        gncScrubBusinessLot (lot);
     gnc_resume_gui_refresh();
     LEAVE(" ");
 }
@@ -3729,6 +3737,7 @@ gnc_plugin_page_register_cmd_scrub_all (GtkAction *action,
     Query *query;
     Account *root;
     Transaction *trans;
+    GNCLot *lot;
     Split *split;
     GList *node;
 
@@ -3754,6 +3763,10 @@ gnc_plugin_page_register_cmd_scrub_all (GtkAction *action,
 
         xaccTransScrubOrphans(trans);
         xaccTransScrubImbalance(trans, root, NULL);
+
+        lot = xaccSplitGetLot (split);
+        if (lot && xaccAccountIsAPARType (xaccAccountGetType (xaccSplitGetAccount (split))))
+            gncScrubBusinessLot (lot);
     }
 
     gnc_resume_gui_refresh();

commit 1015a4309092e09c00cb93d805c286c1904cd4c4
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Thu Aug 28 17:29:49 2014 +0200

    Make scrub in lot viewer scrub business lots using the new scrub functions.

diff --git a/src/gnome/dialog-lot-viewer.c b/src/gnome/dialog-lot-viewer.c
index 0ae82ee..a2558be 100644
--- a/src/gnome/dialog-lot-viewer.c
+++ b/src/gnome/dialog-lot-viewer.c
@@ -38,6 +38,7 @@
 #include "qof.h"
 #include "gnc-lot.h"
 #include "Scrub3.h"
+#include "ScrubBusiness.h"
 #include "Transaction.h"
 #include "engine-helpers.h"
 #include "gncInvoice.h"
@@ -750,14 +751,20 @@ lv_response_cb (GtkDialog *dialog, gint response, gpointer data)
     case RESPONSE_SCRUB_LOT:
         if (NULL == lot)
             return;
-        xaccScrubLot (lot);
+        if (xaccAccountIsAPARType (xaccAccountGetType(lv->account)))
+            gncScrubBusinessLot (lot);
+        else
+            xaccScrubLot (lot);
         gnc_lot_viewer_fill (lv);
         lv_show_splits_in_lot (lv);
         break;
 
     case RESPONSE_SCRUB_ACCOUNT:
         gnc_suspend_gui_refresh ();
-        xaccAccountScrubLots (lv->account);
+        if (xaccAccountIsAPARType (xaccAccountGetType(lv->account)))
+            gncScrubBusinessAccountLots (lv->account);
+        else
+            xaccAccountScrubLots (lv->account);
         gnc_resume_gui_refresh ();
         gnc_lot_viewer_fill (lv);
         lv_show_splits_free (lv);

commit 9ca4649e6a60b0001b8ec67f3aba2f22883063cb
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Wed Aug 27 15:59:00 2014 +0200

    Rewrite invoice payment logic to fix lot link proliferation
    
    - most payments will behave is it did in 2.4.x
    - when two or more documents are involved in the payment
      a lot link between those document lots will be created

diff --git a/src/business/business-gnome/dialog-payment.c b/src/business/business-gnome/dialog-payment.c
index 12fde9b..f7779d1 100644
--- a/src/business/business-gnome/dialog-payment.c
+++ b/src/business/business-gnome/dialog-payment.c
@@ -154,6 +154,7 @@ void gnc_payment_acct_tree_row_activated_cb (GtkWidget *widget, GtkTreePath *pat
         GtkTreeViewColumn *column, PaymentWindow *pw);
 void gnc_payment_leave_amount_cb (GtkWidget *widget, GdkEventFocus *event,
                                   PaymentWindow *pw);
+void gnc_payment_window_fill_docs_list (PaymentWindow *pw);
 
 
 static void
@@ -161,6 +162,7 @@ gnc_payment_window_refresh_handler (GHashTable *changes, gpointer data)
 {
     PaymentWindow *pw = data;
 
+    gnc_payment_window_fill_docs_list (pw);
     pw->post_acct = gnc_account_select_combo_fill (pw->post_combo, pw->book, pw->acct_types, pw->acct_commodities);
 }
 
@@ -324,6 +326,8 @@ gnc_payment_dialog_highlight_document (PaymentWindow *pw)
                 lot = (GNCLot *) g_value_get_pointer (&value);
                 g_value_unset (&value);
 
+                if (!lot)
+                    continue; /* Lot has been deleted behind our back... */
 
                 invoice = gncInvoiceGetInvoiceFromLot (lot);
                 if (!invoice)
@@ -340,7 +344,7 @@ gnc_payment_dialog_highlight_document (PaymentWindow *pw)
     }
 }
 
-static void
+void
 gnc_payment_window_fill_docs_list (PaymentWindow *pw)
 {
     GtkListStore *store;
diff --git a/src/engine/gncOwner.c b/src/engine/gncOwner.c
index e552918..7ffb5e1 100644
--- a/src/engine/gncOwner.c
+++ b/src/engine/gncOwner.c
@@ -43,8 +43,9 @@
 #include "gncVendorP.h"
 #include "gncInvoice.h"
 #include "gnc-commodity.h"
-#include "Transaction.h"
+#include "Scrub2.h"
 #include "Split.h"
+#include "Transaction.h"
 #include "engine-helpers.h"
 
 #define _GNC_MOD_NAME   GNC_ID_OWNER
@@ -53,6 +54,8 @@
 #define GNC_OWNER_TYPE  "owner-type"
 #define GNC_OWNER_GUID  "owner-guid"
 
+static QofLogModule log_module = GNC_MOD_ENGINE;
+
 GncOwner * gncOwnerNew (void)
 {
     GncOwner *o;
@@ -949,6 +952,7 @@ gncOwnerReduceSplitTo (Split *split, gnc_numeric target_value)
 void
 gncOwnerSetLotLinkMemo (Transaction *ll_txn)
 {
+    gchar *memo_prefix = _("Offset between documents: ");
     gchar *new_memo;
     SplitList *lts_iter;
     SplitList *splits = NULL, *siter;
@@ -989,7 +993,7 @@ gncOwnerSetLotLinkMemo (Transaction *ll_txn)
         return; // We didn't find document lots
 
     // Create the memo as we'd want it to be
-    new_memo = g_strconcat (_("Offset between documents: "), titles->data, NULL);
+    new_memo = g_strconcat (memo_prefix, titles->data, NULL);
     for (titer = titles->next; titer; titer = titer->next)
     {
         gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
@@ -1009,9 +1013,199 @@ gncOwnerSetLotLinkMemo (Transaction *ll_txn)
     g_free (new_memo);
 }
 
+/* Find an existing lot link transaction in the given lot
+ * Only use a lot link that already links at least two
+ * documents (to avoid perpetuating the lot link proliferation
+ * that happened in 2.6.0-2.6.3).
+ */
+static Transaction *
+get_ll_transaction_from_lot (GNCLot *lot)
+{
+    SplitList *ls_iter;
+
+    /* This should really only be called on a document lot */
+    if (!gncInvoiceGetInvoiceFromLot (lot))
+        return NULL;
+
+    /* The given lot is a valid document lot. Now iterate over all
+     * other lot links in this lot to find one more document lot.
+     */
+    for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
+    {
+        Split *ls = ls_iter->data;
+        Transaction *ll_txn = xaccSplitGetParent (ls);
+        SplitList *ts_iter;
+
+        if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
+            continue;
+
+        for (ts_iter = xaccTransGetSplitList (ll_txn); ts_iter; ts_iter = ts_iter->next)
+        {
+            Split *ts = ts_iter->data;
+            GNCLot *tslot = xaccSplitGetLot (ts);
+
+            if (!tslot)
+                continue;
+
+            if (tslot == lot)
+                continue;
+
+            if (gncInvoiceGetInvoiceFromLot (lot))
+                return ll_txn; /* Got one more document lot - mission accomplished */
+        }
+    }
+
+    /* The lot doesn't have an ll_txn with the requested criteria... */
+    return NULL;
+}
+
+static void
+gncOwnerCreateLotLink (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
+{
+    const gchar *action = _("Lot Link");
+    Account *acct = gnc_lot_get_account (from_lot);
+    const gchar *name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
+    Transaction *ll_txn = NULL;
+    gnc_numeric from_lot_bal, to_lot_bal;
+    Timespec from_ts, to_ts;
+    time64 time_posted;
+    Split *split;
+
+    /* Sanity check */
+    if (!gncInvoiceGetInvoiceFromLot (from_lot) ||
+        !gncInvoiceGetInvoiceFromLot (to_lot))
+        return;
+
+    /* Determine transaction date based on lot splits */
+    from_ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (from_lot)));
+    to_ts   = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (to_lot)));
+    if (timespecToTime64 (from_ts) >= timespecToTime64 (to_ts))
+        time_posted = timespecToTime64 (from_ts);
+    else
+        time_posted = timespecToTime64 (to_ts);
+
+    /* Figure out how much we can offset between the lots */
+    from_lot_bal = gnc_lot_get_balance (from_lot);
+    to_lot_bal = gnc_lot_get_balance (to_lot);
+    if (gnc_numeric_compare (gnc_numeric_abs (from_lot_bal),
+                             gnc_numeric_abs (to_lot_bal)) > 0)
+        from_lot_bal = gnc_numeric_neg (to_lot_bal);
+    else
+        to_lot_bal = gnc_numeric_neg (from_lot_bal);
+
+    xaccAccountBeginEdit (acct);
+
+    /* Look for a pre-existing lot link we can extend */
+    ll_txn = get_ll_transaction_from_lot (from_lot);
+
+    if (!ll_txn)
+        ll_txn = get_ll_transaction_from_lot (to_lot);
+
+    if (!ll_txn)
+    {
+        /* No pre-existing lot link. Create one. */
+        Timespec ts;
+
+        timespecFromTime64 (&ts, time_posted);
+
+        ll_txn = xaccMallocTransaction (gnc_lot_get_book (from_lot));
+        xaccTransBeginEdit (ll_txn);
+
+        xaccTransSetDescription (ll_txn, name ? name : "(Unknown)");
+        xaccTransSetCurrency (ll_txn, xaccAccountGetCommodity(acct));
+        xaccTransSetDateEnteredSecs (ll_txn, gnc_time (NULL));
+        xaccTransSetDatePostedTS (ll_txn, &ts);
+        xaccTransSetTxnType (ll_txn, TXN_TYPE_LINK);
+    }
+    else
+    {
+        Timespec ts = xaccTransRetDatePostedTS (ll_txn);
+        xaccTransBeginEdit (ll_txn);
+
+        /* Maybe we need to update the post date of the transaction ? */
+        if (time_posted > timespecToTime64 (ts))
+        {
+            timespecFromTime64 (&ts, time_posted);
+            xaccTransSetDatePostedTS (ll_txn, &ts);
+
+        }
+    }
+
+    /* Create a split for the from_lot */
+    split = xaccMallocSplit (gnc_lot_get_book (from_lot));
+    /* set Action using utility function */
+    gnc_set_num_action (NULL, split, NULL, action);
+    xaccAccountInsertSplit (acct, split);
+    xaccTransAppendSplit (ll_txn, split);
+    /* To offset the lot balance, the split must be of the opposite sign */
+    xaccSplitSetBaseValue (split, gnc_numeric_neg (from_lot_bal), xaccAccountGetCommodity(acct));
+    gnc_lot_add_split (from_lot, split);
+
+    /* Create a split for the to_lot */
+    split = xaccMallocSplit (gnc_lot_get_book (to_lot));
+    /* set Action using utility function */
+    gnc_set_num_action (NULL, split, NULL, action);
+    xaccAccountInsertSplit (acct, split);
+    xaccTransAppendSplit (ll_txn, split);
+    /* To offset the lot balance, the split must be of the opposite sign */
+    xaccSplitSetBaseValue (split, gnc_numeric_neg (to_lot_bal), xaccAccountGetCommodity(acct));
+    gnc_lot_add_split (to_lot, split);
+
+    xaccTransCommitEdit (ll_txn);
+
+
+    /* Do some post-cleaning on the lots
+     * The above actions may have created splits that are
+     * in the same transaction and lot. These can be merged.
+     */
+    xaccScrubMergeLotSubSplits (to_lot, FALSE);
+    xaccScrubMergeLotSubSplits (from_lot, FALSE);
+    /* And finally set the same memo for all remaining splits
+     * It's a convenience for the users to identify all documents
+     * involved in the link.
+     */
+    gncOwnerSetLotLinkMemo (ll_txn);
+    xaccAccountCommitEdit (acct);
+}
+
+static void gncOwnerOffsetLots (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
+{
+    gnc_numeric target_offset;
+    Split *split;
+
+    /* from lot should not be a document lot because we're removing a split from there ! */
+    if (gncInvoiceGetInvoiceFromLot (from_lot))
+    {
+        PWARN ("from_lot %p is a document lot. That is not allowed in gncOwnerOffsetLots", from_lot);
+        return;
+    }
+
+    /* Get best matching split from from_lot to offset to_lot */
+    target_offset = gnc_lot_get_balance (to_lot);
+    if (gnc_numeric_zero_p (target_offset))
+        return; // to_lot is already balanced, nothing more to do
+
+    split = gncOwnerFindOffsettingSplit (from_lot, target_offset);
+    if (!split)
+        return; // No suitable offsetting split found, nothing more to do
+
+    /* If the offsetting split is bigger than the amount needed to balance
+     * to_lot, reduce the split so its reduced value closes to_lot exactly.
+     * Note the negation in the reduction function. The split must be of
+     * opposite sign of to_lot's balance in order to be able to close it.
+     */
+    if (gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (split)),
+                             gnc_numeric_abs (target_offset)) > 0)
+        gncOwnerReduceSplitTo (split, gnc_numeric_neg (target_offset));
+
+    /* Move the reduced split from from_lot to to_lot */
+    gnc_lot_add_split (to_lot, split);
+
+}
+
 void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
 {
-    GList *base_iter;
+    GList *left_iter;
 
     /* General note: in the code below the term "payment" can
      * both mean a true payment or a document of
@@ -1025,164 +1219,126 @@ void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
     if (!owner) return;
     if (!lots) return;
 
-    for (base_iter = lots; base_iter; base_iter = base_iter->next)
+    for (left_iter = lots; left_iter; left_iter = left_iter->next)
     {
-        GNCLot *base_lot = base_iter->data;
-        QofBook *book;
+        GNCLot *left_lot = left_iter->data;
+        gnc_numeric left_lot_bal;
+        gboolean left_lot_has_doc;
+        gboolean left_modified = FALSE;
         Account *acct;
-        const gchar *name;
-        GList *lot_list, *lot_iter;
-        Transaction *txn = NULL;
-        gnc_numeric base_lot_bal, val_to_pay, val_paid = { 0, 1 };
-        gboolean base_bal_is_pos;
-        const gchar *action, *memo;
+        GList *right_iter;
 
         /* Only attempt to apply payments to open lots.
          * Note that due to the iterative nature of this function lots
-         * in the list may become closed before they are evaluated as
+         * in the list may become empty/closed before they are evaluated as
          * base lot, so we should check this for each lot. */
-        base_lot_bal = gnc_lot_get_balance (base_lot);
-        if (gnc_numeric_zero_p (base_lot_bal))
+        if (!left_lot)
+            continue;
+        if (gnc_lot_count_splits (left_lot) == 0)
+        {
+            gnc_lot_destroy (left_lot);
+            left_iter->data = NULL;
+            continue;
+        }
+        if (gnc_lot_is_closed (left_lot))
             continue;
 
-        book = gnc_lot_get_book (base_lot);
-        acct = gnc_lot_get_account (base_lot);
-        name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
-        lot_list = base_iter->next;
-
-        /* Strings used when creating splits later on. */
-        action = _("Lot Link");
-        memo   = _("Internal link between invoice and payment lots");
-
-        /* Note: to balance the lot the payment to assign
-         * must have the opposite sign of the existing lot balance */
-        val_to_pay = gnc_numeric_neg (base_lot_bal);
-        base_bal_is_pos = gnc_numeric_positive_p (base_lot_bal);
+        acct = gnc_lot_get_account (left_lot);
+        xaccAccountBeginEdit (acct);
 
+        left_lot_bal = gnc_lot_get_balance (left_lot);
+        left_lot_has_doc = (gncInvoiceGetInvoiceFromLot (left_lot) != NULL);
 
-        /* Create splits in a linking transaction between lots until
-         * - either the invoice lot is balanced
-         * - or there are no more balancing lots.
+        /* Attempt to offset left_lot with any of the remaining lots. To do so
+         * iterate over the remaining lots adding lot links or moving payments
+         * around.
          */
-        for (lot_iter = lot_list; lot_iter; lot_iter = lot_iter->next)
+        for (right_iter = left_iter->next; right_iter; right_iter = right_iter->next)
         {
-            gnc_numeric payment_lot_balance;
-            Split *split;
-            Account *bal_acct;
-            gnc_numeric  split_amt;
-
-            GNCLot *balancing_lot = lot_iter->data;
+            GNCLot *right_lot = right_iter->data;
+            gnc_numeric right_lot_bal;
+            gboolean right_lot_has_doc;
 
             /* Only attempt to use open lots to balance the base lot.
              * Note that due to the iterative nature of this function lots
-             * in the list may become closed before they are evaluated as
+             * in the list may become empty/closed before they are evaluated as
              * base lot, so we should check this for each lot. */
-            if (gnc_lot_is_closed (balancing_lot))
+            if (!right_lot)
+                continue;
+            if (gnc_lot_count_splits (right_lot) == 0)
+            {
+                gnc_lot_destroy (right_lot);
+                right_iter->data = NULL;
+                continue;
+            }
+            if (gnc_lot_is_closed (right_lot))
                 continue;
 
             /* Balancing transactions for invoice/payments can only happen
              * in the same account. */
-            bal_acct = gnc_lot_get_account (balancing_lot);
-            if (acct != bal_acct)
+            if (acct != gnc_lot_get_account (right_lot))
                 continue;
 
-            payment_lot_balance = gnc_lot_get_balance (balancing_lot);
 
             /* Only attempt to balance if the base lot and balancing lot are
              * of the opposite sign. (Otherwise we would increase the balance
              * of the lot - Duh */
-            if (base_bal_is_pos == gnc_numeric_positive_p (payment_lot_balance))
+            right_lot_bal = gnc_lot_get_balance (right_lot);
+            if (gnc_numeric_positive_p (left_lot_bal) == gnc_numeric_positive_p (right_lot_bal))
                 continue;
 
-            /*
-             * If there is less to pay than there's open in the lot; we're done -- apply the base_lot_vale.
-             * Note that payment_value and balance are opposite in sign, so we have to compare absolute values here
-             *
-             * Otherwise, apply the balance, subtract that from the payment_value,
-             * and move on to the next one.
+            /* Ok we found two lots than can (partly) offset each other.
+             * Depending on the lot types, a different action is needed to accomplish this.
+             * 1. Both lots are document lots (invoices/credit notes)
+             *    -> Create a lot linking transaction between the lots
+             * 2. Both lots are payment lots (lots without a document attached)
+             *    -> Use part of the bigger lot to the close the smaller lot
+             * 3. One document lot with one payment lot
+             *    -> Use (part of) the payment to offset (part of) the document lot,
+             *       Which one will be closed depends on which is the bigger one
              */
-            if (gnc_numeric_compare (gnc_numeric_abs (val_to_pay), gnc_numeric_abs (payment_lot_balance)) <= 0)
+            right_lot_has_doc = (gncInvoiceGetInvoiceFromLot (right_lot) != NULL);
+            if (left_lot_has_doc && right_lot_has_doc)
+                gncOwnerCreateLotLink (left_lot, right_lot, owner);
+            else if (!left_lot_has_doc && !right_lot_has_doc)
             {
-                /* abs(val_to_pay) <= abs(balance) */
-                split_amt = val_to_pay;
+                gint cmp = gnc_numeric_compare (gnc_numeric_abs (left_lot_bal),
+                                                gnc_numeric_abs (right_lot_bal));
+                if (cmp >= 0)
+                    gncOwnerOffsetLots (left_lot, right_lot, owner);
+                else
+                    gncOwnerOffsetLots (right_lot, left_lot, owner);
             }
             else
             {
-                /* abs(val_to_pay) > abs(balance)
-                 * Remember payment_value and balance are opposite in sign,
-                 * and we want a payment to neutralize the current balance
-                 * so we need to negate here */
-                split_amt = payment_lot_balance;
+                GNCLot *doc_lot = left_lot_has_doc ? left_lot : right_lot;
+                GNCLot *pay_lot = left_lot_has_doc ? right_lot : left_lot;
+                // Ok, let's try to move a payment from pay_lot to doc_lot
+                gncOwnerOffsetLots (pay_lot, doc_lot, owner);
             }
 
-            /* If not created yet, create a new transaction linking
-             * the base lot and the balancing lot(s) */
-            if (!txn)
-            {
-                Timespec ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (base_lot)));
-
-                xaccAccountBeginEdit (acct);
-
-                txn = xaccMallocTransaction (book);
-                xaccTransBeginEdit (txn);
-
-                xaccTransSetDescription (txn, name ? name : "");
-                xaccTransSetCurrency (txn, xaccAccountGetCommodity(acct));
-                xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
-                xaccTransSetDatePostedTS (txn, &ts);
-                xaccTransSetTxnType (txn, TXN_TYPE_LINK);
-            }
+            /* If we get here, then right_lot was modified
+             * If the lot has a document, send an event for send an event for it as well
+             * so it gets potentially updated as paid */
 
-            /* Create the split for this link in current balancing lot */
-            split = xaccMallocSplit (book);
-            xaccSplitSetMemo (split, memo);
-            /* set Action using utility function */
-            gnc_set_num_action (NULL, split, NULL, action);
-            xaccAccountInsertSplit (acct, split);
-            xaccTransAppendSplit (txn, split);
-            xaccSplitSetBaseValue (split, gnc_numeric_neg (split_amt), xaccAccountGetCommodity(acct));
-            gnc_lot_add_split (balancing_lot, split);
-
-            /* If the balancing lot was linked to a document (invoice/credit note),
-             * send an event for it as well so it gets potentially updated as paid */
             {
-                GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(balancing_lot);
+                GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(right_lot);
                 if (this_invoice)
                     qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
             }
-
-            val_paid   = gnc_numeric_add (val_paid, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-            val_to_pay = gnc_numeric_sub (val_to_pay, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-            if (gnc_numeric_zero_p (val_to_pay))
-                break;
+            left_modified = TRUE;
         }
 
-
-        /* If the above loop managed to create a transaction and some balancing splits,
-         * create the final split for the link transaction in the base lot */
-        if (txn)
+        /* If left_lot was modified and the lot has a document,
+         * send an event for send an event for it as well
+         * so it gets potentially updated as paid */
+        if (left_modified)
         {
-            GncInvoice *this_invoice;
-            Split *split = xaccMallocSplit (book);
-
-            xaccSplitSetMemo (split, memo);
-            /* set Action with utiltity function */
-            gnc_set_num_action (NULL, split, NULL, action);
-            xaccAccountInsertSplit (acct, split);
-            xaccTransAppendSplit (txn, split);
-            xaccSplitSetBaseValue (split, val_paid, xaccAccountGetCommodity(acct));
-            gnc_lot_add_split (base_lot, split);
-
-            xaccTransCommitEdit (txn);
-            xaccAccountCommitEdit (acct);
-
-            /* If the base lot was linked to a document (invoice/credit note),
-             * send an event for it as well so it gets potentially updated as paid */
-            this_invoice = gncInvoiceGetInvoiceFromLot(base_lot);
+            GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(left_lot);
             if (this_invoice)
                 qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
-
         }
+        xaccAccountCommitEdit (acct);
 
     }
 }

commit afc790d3cfe5cc52c787db5949e5d4d73223829b
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Wed Aug 27 11:45:07 2014 +0200

    Refactor get_pay_split into gncOwnerFindOffsettingSplit
    
    In this form it can be used both for scrubbing lot links and for adding new payments

diff --git a/src/engine/ScrubBusiness.c b/src/engine/ScrubBusiness.c
index 8eb6ccc..9c7b308 100644
--- a/src/engine/ScrubBusiness.c
+++ b/src/engine/ScrubBusiness.c
@@ -43,14 +43,6 @@
 
 static QofLogModule log_module = GNC_MOD_LOT;
 
-typedef enum
-{
-    is_equal     = 8,
-    is_more      = 4,
-    is_less      = 2,
-    is_pay_split = 1
-} split_flags;
-
 // A helper function that takes two splits. If the splits are  of opposite sign
 // it reduces the biggest split to have the same value (but with opposite sign)
 // of the smaller split.
@@ -71,85 +63,6 @@ static gboolean reduce_biggest_split (Split *splitA, Split *splitB)
         return gncOwnerReduceSplitTo (splitB, gnc_numeric_neg (valA));
 }
 
-
-// Find a split in pay_lot that best offsets ll_split
-// Obviously it should be of opposite sign.
-// If there are more splits of opposite sign the following
-// criteria are used in order of preference:
-// 1. exact match in abs value is preferred over larger abs value
-// 2. larger abs value is preferred over smaller abs value
-// 3. if previous and new candidate are in the same value category,
-//    prefer real payment splits over lot link splits
-// 6. if previous and new candiate are of same split type
-//    prefer biggest abs value.
-static Split *get_pay_split (GNCLot *pay_lot, Split *ll_split)
-{
-    SplitList *pls_iter = NULL;
-    Split *best_split = NULL;
-    gnc_numeric best_val = { 0, 1};
-    gint best_flags = 0;
-
-    if (!pay_lot)
-        return NULL;
-
-    for (pls_iter = gnc_lot_get_split_list (pay_lot); pls_iter; pls_iter = pls_iter->next)
-    {
-        Split *pay_split = pls_iter->data;
-        Transaction *pay_txn;
-        gnc_numeric ll_val, pay_val;
-        gint new_flags = 0;
-        gint cmp_ll = 0;
-
-        if (!pay_split)
-            continue;
-
-
-        pay_txn = xaccSplitGetParent (pay_split);
-        if (!pay_txn)
-        {
-            // Ooops - the split doesn't belong to any transaction !
-            // This is not expected so issue a warning and continue with next split
-            PWARN("Encountered a split in a payment lot that's not part of any transaction. "
-                  "This is unexpected! Skipping split %p.", pay_split);
-            continue;
-        }
-
-        // Check if this split has the opposite sign of the lot link split we want to scrub
-        ll_val = xaccSplitGetValue (ll_split);
-        pay_val = xaccSplitGetValue (pay_split);
-        if (gnc_numeric_positive_p (ll_val) == gnc_numeric_positive_p (pay_val))
-            continue;
-
-        // Ok we have found a split that
-        // - does belong to a transaction
-        // - has the opposite sign of the lot link split we're scrubbing
-        // Let's see if it's better than what we have found already.
-        cmp_ll = gnc_numeric_compare (gnc_numeric_abs (pay_val),
-                                      gnc_numeric_abs (ll_val));
-        if (cmp_ll == 0)
-            new_flags += is_equal;
-        else if (cmp_ll > 0)
-            new_flags += is_more;
-        else
-            new_flags += is_less;
-
-        if (xaccTransGetTxnType (pay_txn) != TXN_TYPE_LINK)
-            new_flags += is_pay_split;
-
-        if ((new_flags >= best_flags) &&
-            (gnc_numeric_compare (gnc_numeric_abs (pay_val),
-                                  gnc_numeric_abs (best_val)) > 0))
-        {
-            // The new split is a better match than what we found so far
-            best_split = pay_split;
-            best_flags = new_flags;
-            best_val   = pay_val;
-        }
-    }
-
-    return best_split;
-}
-
 // Attempt to eliminate or reduce the lot link splits (ll_*_split)
 // between from_lot and to_lot. To do so this function will attempt
 // to move a payment split from from_lot to to_lot in order to
@@ -181,7 +94,7 @@ scrub_other_link (GNCLot *from_lot, Split *ll_from_split,
 
     // Next we have to find the original payment split so we can
     // add (part of) it to the document lot
-    real_from_split = get_pay_split (from_lot, ll_from_split);
+    real_from_split = gncOwnerFindOffsettingSplit (from_lot, xaccSplitGetValue (ll_from_split));
     if (!real_from_split)
         return modified; // No usable split in the payment lot
 
diff --git a/src/engine/gncOwner.c b/src/engine/gncOwner.c
index b56e489..e552918 100644
--- a/src/engine/gncOwner.c
+++ b/src/engine/gncOwner.c
@@ -841,6 +841,79 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
     return payment_lot;
 }
 
+typedef enum
+{
+    is_equal     = 8,
+    is_more      = 4,
+    is_less      = 2,
+    is_pay_split = 1
+} split_flags;
+
+Split *gncOwnerFindOffsettingSplit (GNCLot *lot, gnc_numeric target_value)
+{
+    SplitList *ls_iter = NULL;
+    Split *best_split = NULL;
+    gnc_numeric best_val = { 0, 1};
+    gint best_flags = 0;
+
+    if (!lot)
+        return NULL;
+
+    for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
+    {
+        Split *split = ls_iter->data;
+        Transaction *txn;
+        gnc_numeric split_value;
+        gint new_flags = 0;
+        gint val_cmp = 0;
+
+        if (!split)
+            continue;
+
+
+        txn = xaccSplitGetParent (split);
+        if (!txn)
+        {
+            // Ooops - the split doesn't belong to any transaction !
+            // This is not expected so issue a warning and continue with next split
+            PWARN("Encountered a split in a payment lot that's not part of any transaction. "
+                  "This is unexpected! Skipping split %p.", split);
+            continue;
+        }
+
+        // Check if this split has the opposite sign of the target value we want to offset
+        split_value = xaccSplitGetValue (split);
+        if (gnc_numeric_positive_p (target_value) == gnc_numeric_positive_p (split_value))
+            continue;
+
+        // Ok we have found a split that potentially can offset the target value
+        // Let's see if it's better than what we have found already.
+        val_cmp = gnc_numeric_compare (gnc_numeric_abs (split_value),
+                                       gnc_numeric_abs (target_value));
+        if (val_cmp == 0)
+            new_flags += is_equal;
+        else if (val_cmp > 0)
+            new_flags += is_more;
+        else
+            new_flags += is_less;
+
+        if (xaccTransGetTxnType (txn) != TXN_TYPE_LINK)
+            new_flags += is_pay_split;
+
+        if ((new_flags >= best_flags) &&
+            (gnc_numeric_compare (gnc_numeric_abs (split_value),
+                                  gnc_numeric_abs (best_val)) > 0))
+        {
+            // The new split is a better match than what we found so far
+            best_split = split;
+            best_flags = new_flags;
+            best_val   = split_value;
+        }
+    }
+
+    return best_split;
+}
+
 gboolean
 gncOwnerReduceSplitTo (Split *split, gnc_numeric target_value)
 {
diff --git a/src/engine/gncOwner.h b/src/engine/gncOwner.h
index 47ae3af..416a89a 100644
--- a/src/engine/gncOwner.h
+++ b/src/engine/gncOwner.h
@@ -266,6 +266,19 @@ gncOwnerApplyPayment (const GncOwner *owner, Transaction *txn, GList *lots,
                       gnc_numeric amount, gnc_numeric exch, Timespec date,
                       const char *memo, const char *num, gboolean auto_pay);
 
+/** Helper function to find a split in lot that best offsets target_value
+ *  Obviously it should be of opposite sign.
+ * If there are more splits of opposite sign the following
+ * criteria are used in order of preference:
+ * 1. exact match in abs value is preferred over larger abs value
+ * 2. larger abs value is preferred over smaller abs value
+ * 3. if previous and new candidate are in the same value category,
+ *    prefer real payment splits over lot link splits
+ * 4. if previous and new candiate are of same split type
+ *    prefer biggest abs value.
+ */
+Split *gncOwnerFindOffsettingSplit (GNCLot *pay_lot, gnc_numeric target_value);
+
 /** Helper function to reduce the value of a split to target_value. To make
  *  sure the split's parent transaction remains balanced a second split
  *  will be created with the remainder. Similarly if the split was part of a

commit e44bb5ffac76beb3171bef310f600b4f3a8cfa9c
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Aug 26 19:24:56 2014 +0200

    Refactor gncOwnerReduceSplitTo out of reduce_biggest_split
    
    It can now be used both for scrubbing and when applying payments

diff --git a/src/engine/ScrubBusiness.c b/src/engine/ScrubBusiness.c
index 979cc45..8eb6ccc 100644
--- a/src/engine/ScrubBusiness.c
+++ b/src/engine/ScrubBusiness.c
@@ -64,41 +64,11 @@ static gboolean reduce_biggest_split (Split *splitA, Split *splitB)
 {
     gnc_numeric valA = xaccSplitGetValue (splitA);
     gnc_numeric valB = xaccSplitGetValue (splitB);
-    gnc_numeric new_val, rem_val;
-    Split *to_reduce, *rem_split;
-    Transaction *txn;
-    GNCLot *lot;
 
-    if (gnc_numeric_positive_p (valA) == gnc_numeric_positive_p (valB))
-        return FALSE; //Splits are not of opposite sign
-
-    if (gnc_numeric_equal (gnc_numeric_abs (valA), gnc_numeric_abs (valB)))
-        return FALSE; // Splits have an equal value (but with opposite sign - nothing to do
-    else if (gnc_numeric_compare (gnc_numeric_abs (valA), gnc_numeric_abs (valB)) > 0)
-    {
-        to_reduce = splitA;
-        new_val = gnc_numeric_neg (valB);
-    }
+    if (gnc_numeric_compare (gnc_numeric_abs (valA), gnc_numeric_abs (valB)) >= 0)
+        return gncOwnerReduceSplitTo (splitA, gnc_numeric_neg (valB));
     else
-    {
-        to_reduce = splitB;
-        new_val = gnc_numeric_neg (valA);
-    }
-
-    rem_val = gnc_numeric_add (valA, valB, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); // note: values are of opposite sign
-    rem_split = xaccMallocSplit (xaccSplitGetBook (to_reduce));
-    xaccSplitCopyOnto (to_reduce, rem_split);
-    xaccSplitSetValue (rem_split, rem_val);
-
-    txn = xaccSplitGetParent (to_reduce);
-    xaccTransBeginEdit (txn);
-    xaccSplitSetValue (to_reduce, new_val);
-    xaccSplitSetParent (rem_split, txn);
-    xaccTransCommitEdit (txn);
-
-    lot = xaccSplitGetLot (to_reduce);
-    gnc_lot_add_split (lot, rem_split);
-    return TRUE;
+        return gncOwnerReduceSplitTo (splitB, gnc_numeric_neg (valA));
 }
 
 
diff --git a/src/engine/gncOwner.c b/src/engine/gncOwner.c
index 468b5fc..b56e489 100644
--- a/src/engine/gncOwner.c
+++ b/src/engine/gncOwner.c
@@ -841,6 +841,38 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
     return payment_lot;
 }
 
+gboolean
+gncOwnerReduceSplitTo (Split *split, gnc_numeric target_value)
+{
+    gnc_numeric split_val = xaccSplitGetValue (split);
+    gnc_numeric rem_val;
+    Split *rem_split;
+    Transaction *txn;
+    GNCLot *lot;
+
+    if (gnc_numeric_positive_p (split_val) != gnc_numeric_positive_p (target_value))
+        return FALSE; // Split and target value have to be of the same sign
+
+    if (gnc_numeric_equal (split_val, target_value))
+        return FALSE; // Split already has the target value
+
+    rem_val = gnc_numeric_sub (split_val, target_value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); // note: values are of opposite sign
+    rem_split = xaccMallocSplit (xaccSplitGetBook (split));
+    xaccSplitCopyOnto (split, rem_split);
+    xaccSplitSetValue (rem_split, rem_val);
+
+    txn = xaccSplitGetParent (split);
+    xaccTransBeginEdit (txn);
+    xaccSplitSetValue (split, target_value);
+    xaccSplitSetParent (rem_split, txn);
+    xaccTransCommitEdit (txn);
+
+    lot = xaccSplitGetLot (split);
+    gnc_lot_add_split (lot, rem_split);
+
+    return TRUE;
+}
+
 void
 gncOwnerSetLotLinkMemo (Transaction *ll_txn)
 {
diff --git a/src/engine/gncOwner.h b/src/engine/gncOwner.h
index 9c4a2e9..47ae3af 100644
--- a/src/engine/gncOwner.h
+++ b/src/engine/gncOwner.h
@@ -266,6 +266,14 @@ gncOwnerApplyPayment (const GncOwner *owner, Transaction *txn, GList *lots,
                       gnc_numeric amount, gnc_numeric exch, Timespec date,
                       const char *memo, const char *num, gboolean auto_pay);
 
+/** Helper function to reduce the value of a split to target_value. To make
+ *  sure the split's parent transaction remains balanced a second split
+ *  will be created with the remainder. Similarly if the split was part of a
+ *  (business) lot, the remainder split will be added to the same lot to
+ *  keep the lot's balance unchanged.
+ */
+gboolean gncOwnerReduceSplitTo (Split *split, gnc_numeric target_value);
+
 /** To help a user understand what a lot link transaction does,
  *  we set the memo to name all documents involved in the link.
  *  The function below calculates this memo and sets it for

commit 7b642081a371be82d4bcb48b2f08552e649e46ae
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Aug 26 19:00:41 2014 +0200

    Refactor gnc_doc_doc_link into gncOwnerSetLotLinkMemo and move it
    
    It can now be used both for scrubbing and when applying payments

diff --git a/src/engine/ScrubBusiness.c b/src/engine/ScrubBusiness.c
index 53315de..979cc45 100644
--- a/src/engine/ScrubBusiness.c
+++ b/src/engine/ScrubBusiness.c
@@ -180,63 +180,6 @@ static Split *get_pay_split (GNCLot *pay_lot, Split *ll_split)
     return best_split;
 }
 
-static void
-scrub_doc_doc_link (Transaction *ll_txn)
-{
-    gchar *new_memo;
-    SplitList *lts_iter;
-    SplitList *splits = NULL, *siter;
-    GList *titles = NULL, *titer;
-
-    // Find all splits in the lot link transaction that are also in a document lot
-    for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
-    {
-        Split *split = lts_iter->data;
-        GNCLot *lot;
-        GncInvoice *invoice;
-        gchar *title;
-
-        if (!split)
-            continue;
-
-        lot = xaccSplitGetLot (split);
-        if (!lot)
-            continue;
-
-        invoice = gncInvoiceGetInvoiceFromLot (lot);
-        if (!invoice)
-            continue;
-
-        title = g_strdup_printf ("%s %s", gncInvoiceGetTypeString (invoice), gncInvoiceGetID (invoice));
-
-        titles = g_list_insert_sorted (titles, title, (GCompareFunc)g_strcmp0);
-        splits = g_list_prepend (splits, split); // splits don't need to be sorted
-    }
-
-    if (!titles)
-        return; // We didn't find document lots
-
-    // Create the memo as we'd want it to be
-    new_memo = g_strconcat (_("Offset between documents: "), titles->data, NULL);
-    for (titer = titles->next; titer; titer = titer->next)
-    {
-        gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
-        g_free (new_memo);
-        new_memo = tmp_memo;
-    }
-    g_list_free_full (titles, g_free);
-
-    // Update the memos of all the splits we found previously (if needed)
-    for (siter = splits; siter; siter = siter->next)
-    {
-        if (g_strcmp0 (xaccSplitGetMemo (siter->data), new_memo) != 0)
-            xaccSplitSetMemo (siter->data, new_memo);
-    }
-
-    g_list_free (splits);
-    g_free (new_memo);
-}
-
 // Attempt to eliminate or reduce the lot link splits (ll_*_split)
 // between from_lot and to_lot. To do so this function will attempt
 // to move a payment split from from_lot to to_lot in order to
@@ -390,7 +333,7 @@ scrub_start:
             //   (Part of) the link will be eliminated and instead (part of) the real
             //   payment will be added to the document lot to handle the payment.
             if (sl_is_doc_lot && rl_is_doc_lot)
-                scrub_doc_doc_link (ll_txn);
+                gncOwnerSetLotLinkMemo (ll_txn);
             else if (!sl_is_doc_lot && !rl_is_doc_lot)
             {
                 gint cmp = gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (sl_split)),
diff --git a/src/engine/gncOwner.c b/src/engine/gncOwner.c
index 6301fba..468b5fc 100644
--- a/src/engine/gncOwner.c
+++ b/src/engine/gncOwner.c
@@ -841,6 +841,69 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
     return payment_lot;
 }
 
+void
+gncOwnerSetLotLinkMemo (Transaction *ll_txn)
+{
+    gchar *new_memo;
+    SplitList *lts_iter;
+    SplitList *splits = NULL, *siter;
+    GList *titles = NULL, *titer;
+
+    if (!ll_txn)
+        return;
+
+    if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
+        return;
+
+    // Find all splits in the lot link transaction that are also in a document lot
+    for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
+    {
+        Split *split = lts_iter->data;
+        GNCLot *lot;
+        GncInvoice *invoice;
+        gchar *title;
+
+        if (!split)
+            continue;
+
+        lot = xaccSplitGetLot (split);
+        if (!lot)
+            continue;
+
+        invoice = gncInvoiceGetInvoiceFromLot (lot);
+        if (!invoice)
+            continue;
+
+        title = g_strdup_printf ("%s %s", gncInvoiceGetTypeString (invoice), gncInvoiceGetID (invoice));
+
+        titles = g_list_insert_sorted (titles, title, (GCompareFunc)g_strcmp0);
+        splits = g_list_prepend (splits, split); // splits don't need to be sorted
+    }
+
+    if (!titles)
+        return; // We didn't find document lots
+
+    // Create the memo as we'd want it to be
+    new_memo = g_strconcat (_("Offset between documents: "), titles->data, NULL);
+    for (titer = titles->next; titer; titer = titer->next)
+    {
+        gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
+        g_free (new_memo);
+        new_memo = tmp_memo;
+    }
+    g_list_free_full (titles, g_free);
+
+    // Update the memos of all the splits we found previously (if needed)
+    for (siter = splits; siter; siter = siter->next)
+    {
+        if (g_strcmp0 (xaccSplitGetMemo (siter->data), new_memo) != 0)
+            xaccSplitSetMemo (siter->data, new_memo);
+    }
+
+    g_list_free (splits);
+    g_free (new_memo);
+}
+
 void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
 {
     GList *base_iter;
diff --git a/src/engine/gncOwner.h b/src/engine/gncOwner.h
index ac987bc..9c4a2e9 100644
--- a/src/engine/gncOwner.h
+++ b/src/engine/gncOwner.h
@@ -266,6 +266,13 @@ gncOwnerApplyPayment (const GncOwner *owner, Transaction *txn, GList *lots,
                       gnc_numeric amount, gnc_numeric exch, Timespec date,
                       const char *memo, const char *num, gboolean auto_pay);
 
+/** To help a user understand what a lot link transaction does,
+ *  we set the memo to name all documents involved in the link.
+ *  The function below calculates this memo and sets it for
+ *  all splits in the lot link transaction.
+ */
+void gncOwnerSetLotLinkMemo (Transaction *ll_txn);
+
 /** Returns a GList of account-types based on the owner type */
 GList * gncOwnerGetAccountTypesList (const GncOwner *owner);
 

commit ac55c953ad0866c4be5479d8fe0b5719e7519b03
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Aug 26 12:41:57 2014 +0200

    Handle the case of scrubbing a lot link between two non-document lots

diff --git a/src/engine/ScrubBusiness.c b/src/engine/ScrubBusiness.c
index a3cd5ba..53315de 100644
--- a/src/engine/ScrubBusiness.c
+++ b/src/engine/ScrubBusiness.c
@@ -382,17 +382,24 @@ scrub_start:
             //   Special treatment - look for all document lots linked via ll_txn
             //   and update the memo to be of more use to the uses.
             // - Two payment lots:
-            //   This situation will be skipped as it's not clear what the best solution
-            //   will be. In the most common case there's at least one document involved
-            //   in the transaction as well and the links will be resolved in a future iteration.
-            //   The more uncommon case of canceling a payment with a refund is not handled yet.
+            //   (Part of) the link will be eliminated and instead (part of)
+            //   one payment will be added to the other lot to keep the balance.
+            //   If the payments are not equal in abs value part of the bigger payment
+            //   will be moved to the smaller payment's lot.
             // - A document and a payment lot:
             //   (Part of) the link will be eliminated and instead (part of) the real
             //   payment will be added to the document lot to handle the payment.
             if (sl_is_doc_lot && rl_is_doc_lot)
                 scrub_doc_doc_link (ll_txn);
             else if (!sl_is_doc_lot && !rl_is_doc_lot)
-                continue; // next lot link transaction split
+            {
+                gint cmp = gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (sl_split)),
+                                                gnc_numeric_abs (xaccSplitGetValue (ll_txn_split)));
+                if (cmp >= 0)
+                    restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
+                else
+                    restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
+            }
             else
             {
                 GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;

commit b5fa7ee380082e11d278f72a9cdbfd4487583ab1
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Aug 26 12:19:16 2014 +0200

    Tweak the get_pay_split function to prefer the best possible match

diff --git a/src/engine/ScrubBusiness.c b/src/engine/ScrubBusiness.c
index 047c1c7..a3cd5ba 100644
--- a/src/engine/ScrubBusiness.c
+++ b/src/engine/ScrubBusiness.c
@@ -43,6 +43,14 @@
 
 static QofLogModule log_module = GNC_MOD_LOT;
 
+typedef enum
+{
+    is_equal     = 8,
+    is_more      = 4,
+    is_less      = 2,
+    is_pay_split = 1
+} split_flags;
+
 // A helper function that takes two splits. If the splits are  of opposite sign
 // it reduces the biggest split to have the same value (but with opposite sign)
 // of the smaller split.
@@ -93,9 +101,23 @@ static gboolean reduce_biggest_split (Split *splitA, Split *splitB)
     return TRUE;
 }
 
+
+// Find a split in pay_lot that best offsets ll_split
+// Obviously it should be of opposite sign.
+// If there are more splits of opposite sign the following
+// criteria are used in order of preference:
+// 1. exact match in abs value is preferred over larger abs value
+// 2. larger abs value is preferred over smaller abs value
+// 3. if previous and new candidate are in the same value category,
+//    prefer real payment splits over lot link splits
+// 6. if previous and new candiate are of same split type
+//    prefer biggest abs value.
 static Split *get_pay_split (GNCLot *pay_lot, Split *ll_split)
 {
     SplitList *pls_iter = NULL;
+    Split *best_split = NULL;
+    gnc_numeric best_val = { 0, 1};
+    gint best_flags = 0;
 
     if (!pay_lot)
         return NULL;
@@ -105,6 +127,8 @@ static Split *get_pay_split (GNCLot *pay_lot, Split *ll_split)
         Split *pay_split = pls_iter->data;
         Transaction *pay_txn;
         gnc_numeric ll_val, pay_val;
+        gint new_flags = 0;
+        gint cmp_ll = 0;
 
         if (!pay_split)
             continue;
@@ -120,26 +144,40 @@ static Split *get_pay_split (GNCLot *pay_lot, Split *ll_split)
             continue;
         }
 
-        // We're only interested in the non-lot link txn splits
-        if (xaccTransGetTxnType (pay_txn) == TXN_TYPE_LINK)
-            continue;
-
         // Check if this split has the opposite sign of the lot link split we want to scrub
         ll_val = xaccSplitGetValue (ll_split);
         pay_val = xaccSplitGetValue (pay_split);
         if (gnc_numeric_positive_p (ll_val) == gnc_numeric_positive_p (pay_val))
             continue;
 
-        // Bingo - if we get to this point, we have found a split that
+        // Ok we have found a split that
         // - does belong to a transaction
         // - has the opposite sign of the lot link split we're scrubbing
-        // - is not a lot link split in itself
-        return pay_split;
-
+        // Let's see if it's better than what we have found already.
+        cmp_ll = gnc_numeric_compare (gnc_numeric_abs (pay_val),
+                                      gnc_numeric_abs (ll_val));
+        if (cmp_ll == 0)
+            new_flags += is_equal;
+        else if (cmp_ll > 0)
+            new_flags += is_more;
+        else
+            new_flags += is_less;
+
+        if (xaccTransGetTxnType (pay_txn) != TXN_TYPE_LINK)
+            new_flags += is_pay_split;
+
+        if ((new_flags >= best_flags) &&
+            (gnc_numeric_compare (gnc_numeric_abs (pay_val),
+                                  gnc_numeric_abs (best_val)) > 0))
+        {
+            // The new split is a better match than what we found so far
+            best_split = pay_split;
+            best_flags = new_flags;
+            best_val   = pay_val;
+        }
     }
 
-    // Not valid split found
-    return NULL;
+    return best_split;
 }
 
 static void

commit 85b910acc37cff304e0caed289441cd3479b6958
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Aug 26 10:19:37 2014 +0200

    Refactor scrub_doc_pay_link into scrub_other_link and make it more generic
    
    It can now be used both to
    - move a payment to a document lot (its original use case)
    - move a refund to a payment lot or the other way around

diff --git a/src/engine/ScrubBusiness.c b/src/engine/ScrubBusiness.c
index 45298ad..047c1c7 100644
--- a/src/engine/ScrubBusiness.c
+++ b/src/engine/ScrubBusiness.c
@@ -199,62 +199,76 @@ scrub_doc_doc_link (Transaction *ll_txn)
     g_free (new_memo);
 }
 
+// Attempt to eliminate or reduce the lot link splits (ll_*_split)
+// between from_lot and to_lot. To do so this function will attempt
+// to move a payment split from from_lot to to_lot in order to
+// balance the lot link split that will be deleted.
+// To ensure everything remains balanced at most
+// min (val-ll-*-split, val-pay-split) (in absolute values) can be moved.
+// If any split involved has a larger value, it will be split in two
+// and only the part matching the other splits' value will be used.
+// The leftover splits are kept in the respective transactions/lots.
+// A future scrub action can still act on those if needed.
+//
+// Note that this function assumes that ll_from_split and ll_to_split are
+// of opposite sign. The calling function should check this.
+
 static gboolean
-scrub_doc_pay_link (GNCLot *doc_lot, Split *ll_doc_split,
-                    GNCLot *pay_lot, Split *ll_pay_split)
+scrub_other_link (GNCLot *from_lot, Split *ll_from_split,
+                  GNCLot *to_lot,   Split *ll_to_split)
 {
-    Split *real_pay_split; // This refers to the split in the payment lot representing the payment itself
-    gnc_numeric doc_val, pay_val, real_pay_val;
+    Split *real_from_split; // This refers to the split in the payment lot representing the payment itself
+    gnc_numeric from_val, real_from_val, to_val;
     gboolean modified = FALSE;
-    Transaction *ll_txn = xaccSplitGetParent (ll_doc_split);
+    Transaction *ll_txn = xaccSplitGetParent (ll_to_split);
 
     // Per iteration we can only scrub at most max (val-doc-split, val-pay-split)
     // So split the bigger one in two if needed and continue with the equal valued splits only
     // The remainder is added to the lot link transaction and the lot to keep everything balanced
     // and will be processed in a future iteration
-    modified = reduce_biggest_split (ll_doc_split, ll_pay_split);
+    modified = reduce_biggest_split (ll_from_split, ll_to_split);
 
     // Next we have to find the original payment split so we can
     // add (part of) it to the document lot
-    real_pay_split = get_pay_split (pay_lot, ll_pay_split);
-    if (!real_pay_split)
+    real_from_split = get_pay_split (from_lot, ll_from_split);
+    if (!real_from_split)
         return modified; // No usable split in the payment lot
 
     // Here again per iteration we can only scrub at most max (val-other-pay-split, val-pay-split)
     // So split the bigger one in two if needed and continue with the equal valued splits only
     // The remainder is added to the lot link transaction and the lot to keep everything balanced
     // and will be processed in a future iteration
-    modified = reduce_biggest_split (real_pay_split, ll_pay_split);
+    modified = reduce_biggest_split (real_from_split, ll_from_split);
 
     // Once more check for max (val-doc-split, val-pay-split), and reduce if necessary.
     // It may have changed while looking for the real payment split
-    modified = reduce_biggest_split (ll_doc_split, ll_pay_split);
+    modified = reduce_biggest_split (ll_from_split, ll_to_split);
 
-    // At this point ll_doc_split and real_pay_split should have the same value
+    // At this point ll_to_split and real_from_split should have the same value
     // If not, flag a warning and skip to the next iteration
-    doc_val = xaccSplitGetValue (ll_doc_split);
-    pay_val = xaccSplitGetValue (ll_pay_split);
-    real_pay_val = xaccSplitGetValue (real_pay_split);
-    if (!gnc_numeric_equal (doc_val, real_pay_val))
+    to_val        = xaccSplitGetValue (ll_to_split);
+    from_val      = xaccSplitGetValue (ll_from_split);
+    real_from_val = xaccSplitGetValue (real_from_split);
+    if (!gnc_numeric_equal (real_from_val, to_val))
     {
         // This is unexpected - write a warning message and skip this split
-        PWARN("real_pay_val and doc_val differ. "
-              "This is unexpected! Skip scrubbing of real_pay_split %p against ll_doc_split %p.", real_pay_split, ll_doc_split);
+        PWARN("real_from_val and to_val differ. "
+              "This is unexpected! Skip scrubbing of real_from_split %p against ll_to_split %p.", real_from_split, ll_to_split);
         return modified;
     }
 
     // Now do the actual split dance
     // - move real payment split to doc lot
     // - delete both lot link splits from the lot link transaction
-    gnc_lot_add_split (doc_lot, real_pay_split);
+    gnc_lot_add_split (to_lot, real_from_split);
     xaccTransBeginEdit (ll_txn);
-    xaccSplitDestroy (ll_doc_split);
-    xaccSplitDestroy (ll_pay_split);
+    xaccSplitDestroy (ll_to_split);
+    xaccSplitDestroy (ll_from_split);
     xaccTransCommitEdit (ll_txn);
 
     // Cleanup the lots
-    xaccScrubMergeLotSubSplits (doc_lot, FALSE);
-    xaccScrubMergeLotSubSplits (pay_lot, FALSE);
+    xaccScrubMergeLotSubSplits (to_lot, FALSE);
+    xaccScrubMergeLotSubSplits (from_lot, FALSE);
 
     return TRUE; // We did change splits/transactions/lots...
 }
@@ -343,12 +357,12 @@ scrub_start:
                 continue; // next lot link transaction split
             else
             {
-                // Now determine the document lot/split and the payment lot/split
                 GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
                 GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
                 Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
                 Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
-                restart_needed = scrub_doc_pay_link ( doc_lot, ll_doc_split, pay_lot, ll_pay_split);
+                // Ok, let's try to move a payment from pay_lot to doc_lot
+                restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
             }
 
             // If we got here, the splits in our lot and ll_txn have been severely mixed up

commit 434b7f6ebb9d61a42d4b34a640a0e58721367b43
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 23 15:55:46 2014 +0200

    Add scrub function to reduce the amount of lot links used.
    
    Only lot links between two or more document lots must remain.
    All lot links between document and payment lots can be scrubbed.

diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am
index 8090a78..a0147c2 100644
--- a/src/engine/Makefile.am
+++ b/src/engine/Makefile.am
@@ -22,6 +22,7 @@ libgncmod_engine_la_SOURCES = \
   Scrub.c \
   Scrub2.c \
   Scrub3.c \
+  ScrubBusiness.c \
   Split.c \
   TransLog.c \
   Transaction.c \
@@ -70,6 +71,7 @@ gncinclude_HEADERS = \
   Scrub.h \
   Scrub2.h \
   Scrub3.h \
+  ScrubBusiness.h \
   Split.h \
   TransLog.h \
   Transaction.h \
diff --git a/src/engine/ScrubBusiness.c b/src/engine/ScrubBusiness.c
new file mode 100644
index 0000000..45298ad
--- /dev/null
+++ b/src/engine/ScrubBusiness.c
@@ -0,0 +1,448 @@
+/********************************************************************\
+ * ScrubBusiness.h -- Cleanup functions for the business objects.   *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+
+/** @file ScrubBusiness.h
+ *  @brief Cleanup functions for business objects
+ *  @author Created by Geert Janssens August 2014
+ *  @author Copyright (c) 2014 Geert Janssens <geert at kobaltwit.be>
+ *
+ * Provides the high-level API for checking and repairing ('scrubbing
+ * clean') the various data objects used by the business functions.*/
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "gnc-engine.h"
+#include "gnc-lot.h"
+#include "policy-p.h"
+#include "Account.h"
+#include "gncInvoice.h"
+#include "Scrub2.h"
+#include "ScrubBusiness.h"
+#include "Transaction.h"
+
+static QofLogModule log_module = GNC_MOD_LOT;
+
+// A helper function that takes two splits. If the splits are  of opposite sign
+// it reduces the biggest split to have the same value (but with opposite sign)
+// of the smaller split.
+// To make sure everything still continues to balance in addition a "remainder" split
+// will be created that will be added to the same lot and transaction as the biggest
+// split.
+// The opposite sign restriction is because that's the only scenario that makes sense
+// in the context of scrubbing business lots below.
+// If we created new splits, return TRUE, otherwise FALSE
+static gboolean reduce_biggest_split (Split *splitA, Split *splitB)
+{
+    gnc_numeric valA = xaccSplitGetValue (splitA);
+    gnc_numeric valB = xaccSplitGetValue (splitB);
+    gnc_numeric new_val, rem_val;
+    Split *to_reduce, *rem_split;
+    Transaction *txn;
+    GNCLot *lot;
+
+    if (gnc_numeric_positive_p (valA) == gnc_numeric_positive_p (valB))
+        return FALSE; //Splits are not of opposite sign
+
+    if (gnc_numeric_equal (gnc_numeric_abs (valA), gnc_numeric_abs (valB)))
+        return FALSE; // Splits have an equal value (but with opposite sign - nothing to do
+    else if (gnc_numeric_compare (gnc_numeric_abs (valA), gnc_numeric_abs (valB)) > 0)
+    {
+        to_reduce = splitA;
+        new_val = gnc_numeric_neg (valB);
+    }
+    else
+    {
+        to_reduce = splitB;
+        new_val = gnc_numeric_neg (valA);
+    }
+
+    rem_val = gnc_numeric_add (valA, valB, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); // note: values are of opposite sign
+    rem_split = xaccMallocSplit (xaccSplitGetBook (to_reduce));
+    xaccSplitCopyOnto (to_reduce, rem_split);
+    xaccSplitSetValue (rem_split, rem_val);
+
+    txn = xaccSplitGetParent (to_reduce);
+    xaccTransBeginEdit (txn);
+    xaccSplitSetValue (to_reduce, new_val);
+    xaccSplitSetParent (rem_split, txn);
+    xaccTransCommitEdit (txn);
+
+    lot = xaccSplitGetLot (to_reduce);
+    gnc_lot_add_split (lot, rem_split);
+    return TRUE;
+}
+
+static Split *get_pay_split (GNCLot *pay_lot, Split *ll_split)
+{
+    SplitList *pls_iter = NULL;
+
+    if (!pay_lot)
+        return NULL;
+
+    for (pls_iter = gnc_lot_get_split_list (pay_lot); pls_iter; pls_iter = pls_iter->next)
+    {
+        Split *pay_split = pls_iter->data;
+        Transaction *pay_txn;
+        gnc_numeric ll_val, pay_val;
+
+        if (!pay_split)
+            continue;
+
+
+        pay_txn = xaccSplitGetParent (pay_split);
+        if (!pay_txn)
+        {
+            // Ooops - the split doesn't belong to any transaction !
+            // This is not expected so issue a warning and continue with next split
+            PWARN("Encountered a split in a payment lot that's not part of any transaction. "
+                  "This is unexpected! Skipping split %p.", pay_split);
+            continue;
+        }
+
+        // We're only interested in the non-lot link txn splits
+        if (xaccTransGetTxnType (pay_txn) == TXN_TYPE_LINK)
+            continue;
+
+        // Check if this split has the opposite sign of the lot link split we want to scrub
+        ll_val = xaccSplitGetValue (ll_split);
+        pay_val = xaccSplitGetValue (pay_split);
+        if (gnc_numeric_positive_p (ll_val) == gnc_numeric_positive_p (pay_val))
+            continue;
+
+        // Bingo - if we get to this point, we have found a split that
+        // - does belong to a transaction
+        // - has the opposite sign of the lot link split we're scrubbing
+        // - is not a lot link split in itself
+        return pay_split;
+
+    }
+
+    // Not valid split found
+    return NULL;
+}
+
+static void
+scrub_doc_doc_link (Transaction *ll_txn)
+{
+    gchar *new_memo;
+    SplitList *lts_iter;
+    SplitList *splits = NULL, *siter;
+    GList *titles = NULL, *titer;
+
+    // Find all splits in the lot link transaction that are also in a document lot
+    for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
+    {
+        Split *split = lts_iter->data;
+        GNCLot *lot;
+        GncInvoice *invoice;
+        gchar *title;
+
+        if (!split)
+            continue;
+
+        lot = xaccSplitGetLot (split);
+        if (!lot)
+            continue;
+
+        invoice = gncInvoiceGetInvoiceFromLot (lot);
+        if (!invoice)
+            continue;
+
+        title = g_strdup_printf ("%s %s", gncInvoiceGetTypeString (invoice), gncInvoiceGetID (invoice));
+
+        titles = g_list_insert_sorted (titles, title, (GCompareFunc)g_strcmp0);
+        splits = g_list_prepend (splits, split); // splits don't need to be sorted
+    }
+
+    if (!titles)
+        return; // We didn't find document lots
+
+    // Create the memo as we'd want it to be
+    new_memo = g_strconcat (_("Offset between documents: "), titles->data, NULL);
+    for (titer = titles->next; titer; titer = titer->next)
+    {
+        gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
+        g_free (new_memo);
+        new_memo = tmp_memo;
+    }
+    g_list_free_full (titles, g_free);
+
+    // Update the memos of all the splits we found previously (if needed)
+    for (siter = splits; siter; siter = siter->next)
+    {
+        if (g_strcmp0 (xaccSplitGetMemo (siter->data), new_memo) != 0)
+            xaccSplitSetMemo (siter->data, new_memo);
+    }
+
+    g_list_free (splits);
+    g_free (new_memo);
+}
+
+static gboolean
+scrub_doc_pay_link (GNCLot *doc_lot, Split *ll_doc_split,
+                    GNCLot *pay_lot, Split *ll_pay_split)
+{
+    Split *real_pay_split; // This refers to the split in the payment lot representing the payment itself
+    gnc_numeric doc_val, pay_val, real_pay_val;
+    gboolean modified = FALSE;
+    Transaction *ll_txn = xaccSplitGetParent (ll_doc_split);
+
+    // Per iteration we can only scrub at most max (val-doc-split, val-pay-split)
+    // So split the bigger one in two if needed and continue with the equal valued splits only
+    // The remainder is added to the lot link transaction and the lot to keep everything balanced
+    // and will be processed in a future iteration
+    modified = reduce_biggest_split (ll_doc_split, ll_pay_split);
+
+    // Next we have to find the original payment split so we can
+    // add (part of) it to the document lot
+    real_pay_split = get_pay_split (pay_lot, ll_pay_split);
+    if (!real_pay_split)
+        return modified; // No usable split in the payment lot
+
+    // Here again per iteration we can only scrub at most max (val-other-pay-split, val-pay-split)
+    // So split the bigger one in two if needed and continue with the equal valued splits only
+    // The remainder is added to the lot link transaction and the lot to keep everything balanced
+    // and will be processed in a future iteration
+    modified = reduce_biggest_split (real_pay_split, ll_pay_split);
+
+    // Once more check for max (val-doc-split, val-pay-split), and reduce if necessary.
+    // It may have changed while looking for the real payment split
+    modified = reduce_biggest_split (ll_doc_split, ll_pay_split);
+
+    // At this point ll_doc_split and real_pay_split should have the same value
+    // If not, flag a warning and skip to the next iteration
+    doc_val = xaccSplitGetValue (ll_doc_split);
+    pay_val = xaccSplitGetValue (ll_pay_split);
+    real_pay_val = xaccSplitGetValue (real_pay_split);
+    if (!gnc_numeric_equal (doc_val, real_pay_val))
+    {
+        // This is unexpected - write a warning message and skip this split
+        PWARN("real_pay_val and doc_val differ. "
+              "This is unexpected! Skip scrubbing of real_pay_split %p against ll_doc_split %p.", real_pay_split, ll_doc_split);
+        return modified;
+    }
+
+    // Now do the actual split dance
+    // - move real payment split to doc lot
+    // - delete both lot link splits from the lot link transaction
+    gnc_lot_add_split (doc_lot, real_pay_split);
+    xaccTransBeginEdit (ll_txn);
+    xaccSplitDestroy (ll_doc_split);
+    xaccSplitDestroy (ll_pay_split);
+    xaccTransCommitEdit (ll_txn);
+
+    // Cleanup the lots
+    xaccScrubMergeLotSubSplits (doc_lot, FALSE);
+    xaccScrubMergeLotSubSplits (pay_lot, FALSE);
+
+    return TRUE; // We did change splits/transactions/lots...
+}
+
+static gboolean
+gncScrubLotLinks (GNCLot *scrub_lot)
+{
+    gboolean modified = FALSE, restart_needed = FALSE;
+    SplitList *sls_iter = NULL;
+
+scrub_start:
+    restart_needed = FALSE;
+
+    // Iterate over all splits in the lot
+    for (sls_iter = gnc_lot_get_split_list (scrub_lot); sls_iter; sls_iter = sls_iter->next)
+    {
+        Split *sl_split = sls_iter->data;
+        Transaction *ll_txn = NULL; // ll_txn = "Lot Link Transaction"
+        SplitList *lts_iter = NULL;
+
+        if (!sl_split)
+            continue; // next scrub lot split
+
+        // Only lot link transactions need to be scrubbed
+        ll_txn = xaccSplitGetParent (sl_split);
+
+        if (!ll_txn)
+        {
+            // Ooops - the split doesn't belong to any transaction !
+            // This is not expected so issue a warning and continue with next split
+            PWARN("Encountered a split in a business lot that's not part of any transaction. "
+                  "This is unexpected! Skipping split %p.", sl_split);
+            continue;
+        }
+
+        if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
+            continue; // next scrub lot split
+
+        // Iterate over all splits in the lot link transaction
+        for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
+        {
+            Split *ll_txn_split = lts_iter->data; // These all refer to splits in the lot link transaction
+            GNCLot *remote_lot = NULL; // lot at the other end of the lot link transaction
+            gboolean sl_is_doc_lot, rl_is_doc_lot;
+
+            if (!ll_txn_split)
+                continue; // next lot link transaction split
+
+            // Skip the split in the lot we're currently scrubbing
+            if (sl_split == ll_txn_split)
+                continue; // next lot link transaction split
+
+            // Only splits of opposite sign can be scrubbed
+            if (gnc_numeric_positive_p (xaccSplitGetValue (sl_split)) ==
+                gnc_numeric_positive_p (xaccSplitGetValue (ll_txn_split)))
+                continue; // next lot link transaction split
+
+            // Find linked lot via split
+            remote_lot = xaccSplitGetLot (ll_txn_split);
+            if (!remote_lot)
+            {
+                // This is unexpected - write a warning message and skip this split
+                PWARN("Encountered a Lot Link transaction with a split that's not in any lot. "
+                      "This is unexpected! Skipping split %p from transaction %p.", ll_txn_split, ll_txn);
+                continue;
+            }
+
+            sl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (scrub_lot) != NULL);
+            rl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (remote_lot) != NULL);
+
+            // Depending on the type of lots we're comparing, we need different actions
+            // - Two document lots (an invoice and a credit note):
+            //   Special treatment - look for all document lots linked via ll_txn
+            //   and update the memo to be of more use to the uses.
+            // - Two payment lots:
+            //   This situation will be skipped as it's not clear what the best solution
+            //   will be. In the most common case there's at least one document involved
+            //   in the transaction as well and the links will be resolved in a future iteration.
+            //   The more uncommon case of canceling a payment with a refund is not handled yet.
+            // - A document and a payment lot:
+            //   (Part of) the link will be eliminated and instead (part of) the real
+            //   payment will be added to the document lot to handle the payment.
+            if (sl_is_doc_lot && rl_is_doc_lot)
+                scrub_doc_doc_link (ll_txn);
+            else if (!sl_is_doc_lot && !rl_is_doc_lot)
+                continue; // next lot link transaction split
+            else
+            {
+                // Now determine the document lot/split and the payment lot/split
+                GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
+                GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
+                Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
+                Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
+                restart_needed = scrub_doc_pay_link ( doc_lot, ll_doc_split, pay_lot, ll_pay_split);
+            }
+
+            // If we got here, the splits in our lot and ll_txn have been severely mixed up
+            // And our iterator lists are probably no longer valid
+            // So let's start over
+            if (restart_needed)
+            {
+                modified = TRUE;
+                goto scrub_start;
+            }
+
+        }
+    }
+
+    return modified;
+}
+
+
+gboolean
+gncScrubBusinessLot (GNCLot *lot)
+{
+    gboolean splits_deleted = FALSE;
+    Account *acc;
+    gchar *lotname=NULL;
+
+    if (!lot) return FALSE;
+    lotname = g_strdup (gnc_lot_get_title (lot));
+    ENTER ("(lot=%p) %s", lot, lotname ? lotname : "(no lotname)");
+
+    acc = gnc_lot_get_account (lot);
+    if (acc)
+        xaccAccountBeginEdit(acc);
+
+    // Scrub lot links.
+    // They should only remain when two document lots are linked together
+    xaccScrubMergeLotSubSplits (lot, FALSE);
+    splits_deleted = gncScrubLotLinks (lot);
+
+    // If lot is empty now, delete it
+    if (0 == gnc_lot_count_splits (lot))
+    {
+        PINFO("All splits were removed from lot, deleting");
+        gnc_lot_destroy (lot);
+    }
+
+    if (acc)
+        xaccAccountCommitEdit(acc);
+
+    LEAVE ("(lot=%s, deleted=%d)", lotname ? lotname : "(no lotname)", splits_deleted);
+    g_free (lotname);
+
+    return splits_deleted;
+}
+
+/* ============================================================== */
+
+void
+gncScrubBusinessAccountLots (Account *acc)
+{
+    LotList *lots, *node;
+    if (!acc) return;
+    if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
+
+    ENTER ("(acc=%s)", xaccAccountGetName(acc));
+    xaccAccountBeginEdit(acc);
+
+    lots = xaccAccountGetLotList(acc);
+    for (node = lots; node; node = node->next)
+    {
+        GNCLot *lot = node->data;
+        if (lot)
+            gncScrubBusinessLot (lot);
+    }
+    g_list_free(lots);
+    xaccAccountCommitEdit(acc);
+    LEAVE ("(acc=%s)", xaccAccountGetName(acc));
+}
+
+/* ============================================================== */
+
+static void
+lot_scrub_cb (Account *acc, gpointer data)
+{
+    if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
+    gncScrubBusinessAccountLots (acc);
+}
+
+void
+gncScrubBusinessAccountTreeLots (Account *acc)
+{
+    if (!acc) return;
+
+    gnc_account_foreach_descendant(acc, lot_scrub_cb, NULL);
+    gncScrubBusinessAccountLots (acc);
+}
+
+/* ========================== END OF FILE  ========================= */
diff --git a/src/engine/ScrubBusiness.h b/src/engine/ScrubBusiness.h
new file mode 100644
index 0000000..59af9f0
--- /dev/null
+++ b/src/engine/ScrubBusiness.h
@@ -0,0 +1,79 @@
+/********************************************************************\
+ * ScrubBusiness.h -- Cleanup functions for the business objects.   *
+ *                                                                  *
+ * 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                   *
+\********************************************************************/
+
+/** @addtogroup Engine
+    @{ */
+/** @addtogroup Scrub
+    @{ */
+
+/** @file ScrubBusiness.h
+ *  @brief Cleanup functions for business objects
+ *  @author Created by Geert Janssens August 2014
+ *  @author Copyright (c) 2014 Geert Janssens <geert at kobaltwit.be>
+ */
+#ifndef GNC_SCRUBBUSINESS_H
+#define GNC_SCRUBBUSINESS_H
+
+#include "gnc-engine.h"
+
+/** @name Cleanup functions for business objects
+ * Provides the high-level API for checking and repairing ('scrubbing
+ * clean') the various data objects used by the business functions.
+ @{ */
+
+/** The gncScrubBusinessLot() function makes sure that the indicated
+ *    lot has all the correct properties required for a lot used in
+ *    the business features.
+ *
+ *    Currently this function only does one thing: eliminate lot link
+ *    transactions between invoice lots and payment lots (which were
+ *    generated by GnuCash versions 2.6.0-2.6.3). Lot links
+ *    between invoices and credit notes will still remain.
+ *
+ *    Scrubbing the lot may cause subsplits to be merged together,
+ *    i.e. for splits to be deleted.  This routine returns true if
+ *    any splits were modified or deleted.
+ */
+gboolean gncScrubBusinessLot (GNCLot *lot);
+
+/** The gncScrubBusinessAccountLots() function will call
+ *    gncScrubBusinessLot() on each lot in the given account.
+ *
+ *    This routine is the primary routine for ensuring that the
+ *    lot structure of every lot of a business account is in good
+ *    order.
+ */
+void gncScrubBusinessAccountLots (Account *acc);
+
+/** The gncScrubBusinessAccountTreeLots() function will call
+ *    gncScrubBusinessAccountLots() on each lot in the given account
+ *    and its sub accounts.
+ *
+ *    This routine is the primary routine for ensuring that the
+ *    lot structure of every lot of a business account is in good
+ *    order.
+ */
+void gncScrubBusinessAccountTreeLots (Account *acc);
+
+/** @} */
+#endif /* GNC_SCRUBBUSINESS_H */
+/** @} */
+/** @} */
diff --git a/src/gnome/gnc-plugin-page-account-tree.c b/src/gnome/gnc-plugin-page-account-tree.c
index d191044..b126eb3 100644
--- a/src/gnome/gnc-plugin-page-account-tree.c
+++ b/src/gnome/gnc-plugin-page-account-tree.c
@@ -42,6 +42,7 @@
 
 #include "Scrub.h"
 #include "Scrub3.h"
+#include "ScrubBusiness.h"
 #include "Transaction.h"
 #include "dialog-account.h"
 #include "dialog-transfer.h"
@@ -1568,10 +1569,13 @@ gnc_plugin_page_account_tree_cmd_scrub (GtkAction *action, GncPluginPageAccountT
     xaccAccountScrubOrphans (account);
     xaccAccountScrubImbalance (account);
 
-    // XXX: Lots are disabled
+    // XXX: Lots/capital gains scrubbing is disabled
     if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
         xaccAccountScrubLots(account);
 
+    gncScrubBusinessAccountLots(account);
+
+
     gnc_resume_gui_refresh ();
 }
 
@@ -1587,10 +1591,12 @@ gnc_plugin_page_account_tree_cmd_scrub_sub (GtkAction *action, GncPluginPageAcco
     xaccAccountTreeScrubOrphans (account);
     xaccAccountTreeScrubImbalance (account);
 
-    // XXX: Lots are disabled
+    // XXX: Lots/capital gains scrubbing is disabled
     if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
         xaccAccountTreeScrubLots(account);
 
+    gncScrubBusinessAccountTreeLots(account);
+
     gnc_resume_gui_refresh ();
 }
 
@@ -1603,10 +1609,12 @@ gnc_plugin_page_account_tree_cmd_scrub_all (GtkAction *action, GncPluginPageAcco
 
     xaccAccountTreeScrubOrphans (root);
     xaccAccountTreeScrubImbalance (root);
-    // XXX: Lots are disabled
+    // XXX: Lots/capital gains scrubbing is disabled
     if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
         xaccAccountTreeScrubLots(root);
 
+    gncScrubBusinessAccountTreeLots(root);
+
     gnc_resume_gui_refresh ();
 }
 

commit 413eb69707b9f56043fddd867b79db452c4d6c7f
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 23 15:20:13 2014 +0200

    Widen the scope of the subsplit merge scrub function
    
    When used in a strict sense it will only merge splits
    that are marked as subsplits of each other.
    In relaxed mode it will merge any split two splits that
    are both in the same lot and the same transaction.

diff --git a/src/engine/Scrub2.c b/src/engine/Scrub2.c
index 3e71cd4..4c9b6a7 100644
--- a/src/engine/Scrub2.c
+++ b/src/engine/Scrub2.c
@@ -344,7 +344,7 @@ merge_splits (Split *sa, Split *sb)
 }
 
 gboolean
-xaccScrubMergeSubSplits (Split *split)
+xaccScrubMergeSubSplits (Split *split, gboolean strict)
 {
     gboolean rc = FALSE;
     Transaction *txn;
@@ -352,7 +352,7 @@ xaccScrubMergeSubSplits (Split *split)
     GNCLot *lot;
     const GncGUID *guid;
 
-    if (FALSE == is_subsplit (split)) return FALSE;
+    if (strict && (FALSE == is_subsplit (split))) return FALSE;
 
     txn = split->parent;
     lot = xaccSplitGetLot (split);
@@ -392,7 +392,7 @@ restart:
 }
 
 gboolean
-xaccScrubMergeLotSubSplits (GNCLot *lot)
+xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict)
 {
     gboolean rc = FALSE;
     SplitList *node;
@@ -404,7 +404,7 @@ restart:
     for (node = gnc_lot_get_split_list(lot); node; node = node->next)
     {
         Split *s = node->data;
-        if (!xaccScrubMergeSubSplits(s)) continue;
+        if (!xaccScrubMergeSubSplits(s, strict)) continue;
 
         rc = TRUE;
         goto restart;
diff --git a/src/engine/Scrub2.h b/src/engine/Scrub2.h
index 6ab0ed0..61527d9 100644
--- a/src/engine/Scrub2.h
+++ b/src/engine/Scrub2.h
@@ -89,19 +89,26 @@ void xaccLotScrubDoubleBalance (GNCLot *lot);
  *    the same lot, or in no lot.  Note that, by definition, all
  *    subsplits belong to the same transaction.
  *
+ *    There are two ways to find matching subsplits. The first
+ *    way will consider splits to be subsplits only if they
+ *    are explicitly marked as such while splitting the original
+ *    split. Set strict to TRUE for this matching algorhythm.
+ *
+ *    The second way is more relaxed. It will consider any two
+ *    splits that happen to be part of the same lot and the
+ *    same transaction to be subsplits. Set strict to FALSE for
+ *    this matching algorhythm.
+ *
  *    The routine returns TRUE if a merger was performed, else
  *    it returns FALSE.
- *
- *  The xaccScrubMergeTransSubSplits() routine does the same, except
- *    that it does it for all of the splits in the transaction.
  */
-gboolean xaccScrubMergeSubSplits (Split *split);
+gboolean xaccScrubMergeSubSplits (Split *split, gboolean strict);
 
 /** The xaccScrubMergeLotSubSplits() routine does the same as
  *    the xaccScrubMergSubSplits, except that it does it
  *    for all of the splits in the lot.
  */
-gboolean xaccScrubMergeLotSubSplits (GNCLot *lot);
+gboolean xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict);
 
 #endif /* XACC_SCRUB2_H */
 /** @} */
diff --git a/src/engine/Scrub3.c b/src/engine/Scrub3.c
index 1890f30..c4c4932 100644
--- a/src/engine/Scrub3.c
+++ b/src/engine/Scrub3.c
@@ -96,7 +96,7 @@ xaccScrubLot (GNCLot *lot)
     acc = gnc_lot_get_account (lot);
     pcy = gnc_account_get_policy(acc);
     xaccAccountBeginEdit(acc);
-    xaccScrubMergeLotSubSplits (lot);
+    xaccScrubMergeLotSubSplits (lot, TRUE);
 
     /* If the lot balance is zero, we don't need to rebalance */
     lot_baln = gnc_lot_get_balance (lot);
@@ -132,7 +132,7 @@ rethin:
         xaccLotFill (lot);
 
         /* Make sure there are no subsplits. */
-        splits_deleted = xaccScrubMergeLotSubSplits (lot);
+        splits_deleted = xaccScrubMergeLotSubSplits (lot, TRUE);
     }
 
     /* Now re-compute cap gains, and then double-check that.

commit da9071fe4989b63587d660daec3a3f682ec3fada
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 23 15:17:51 2014 +0200

    Move account type checking functions into the engine

diff --git a/src/business/business-gnome/dialog-payment.c b/src/business/business-gnome/dialog-payment.c
index 7b7c297..12fde9b 100644
--- a/src/business/business-gnome/dialog-payment.c
+++ b/src/business/business-gnome/dialog-payment.c
@@ -1113,37 +1113,13 @@ gnc_ui_payment_new (GncOwner *owner, QofBook *book)
 }
 
 // ////////////////////////////////////////////////////////////
-
-static gboolean isAssetLiabType(GNCAccountType t)
-{
-    switch (t)
-    {
-    case ACCT_TYPE_RECEIVABLE:
-    case ACCT_TYPE_PAYABLE:
-        return FALSE;
-    default:
-        return (xaccAccountTypesCompatible(ACCT_TYPE_ASSET, t)
-                || xaccAccountTypesCompatible(ACCT_TYPE_LIABILITY, t));
-    }
-}
-static gboolean isAPARType(GNCAccountType t)
-{
-    switch (t)
-    {
-    case ACCT_TYPE_RECEIVABLE:
-    case ACCT_TYPE_PAYABLE:
-        return TRUE;
-    default:
-        return FALSE;
-    }
-}
 static void increment_if_asset_account (gpointer data,
                                         gpointer user_data)
 {
     int *r = user_data;
     const Split *split = data;
     const Account *account = xaccSplitGetAccount(split);
-    if (isAssetLiabType(xaccAccountGetType(account)))
+    if (xaccAccountIsAssetLiabType(xaccAccountGetType(account)))
         ++(*r);
 }
 static int countAssetAccounts(SplitList* slist)
@@ -1158,7 +1134,7 @@ static gint predicate_is_asset_account(gconstpointer a,
 {
     const Split *split = a;
     const Account *account = xaccSplitGetAccount(split);
-    if (isAssetLiabType(xaccAccountGetType(account)))
+    if (xaccAccountIsAssetLiabType(xaccAccountGetType(account)))
         return 0;
     else
         return -1;
@@ -1168,7 +1144,7 @@ static gint predicate_is_apar_account(gconstpointer a,
 {
     const Split *split = a;
     const Account *account = xaccSplitGetAccount(split);
-    if (isAPARType(xaccAccountGetType(account)))
+    if (xaccAccountIsAPARType(xaccAccountGetType(account)))
         return 0;
     else
         return -1;
diff --git a/src/engine/Account.c b/src/engine/Account.c
index 4b30953..69cd072 100644
--- a/src/engine/Account.c
+++ b/src/engine/Account.c
@@ -4036,6 +4036,30 @@ xaccAccountTypesValid(void)
     return mask;
 }
 
+gboolean xaccAccountIsAssetLiabType(GNCAccountType t)
+{
+    switch (t)
+    {
+    case ACCT_TYPE_RECEIVABLE:
+    case ACCT_TYPE_PAYABLE:
+        return FALSE;
+    default:
+        return (xaccAccountTypesCompatible(ACCT_TYPE_ASSET, t)
+                || xaccAccountTypesCompatible(ACCT_TYPE_LIABILITY, t));
+    }
+}
+gboolean xaccAccountIsAPARType(GNCAccountType t)
+{
+    switch (t)
+    {
+    case ACCT_TYPE_RECEIVABLE:
+    case ACCT_TYPE_PAYABLE:
+        return TRUE;
+    default:
+        return FALSE;
+    }
+}
+
 gboolean
 xaccAccountIsPriced(const Account *acc)
 {
diff --git a/src/engine/Account.h b/src/engine/Account.h
index 3098043..23ddbd1 100644
--- a/src/engine/Account.h
+++ b/src/engine/Account.h
@@ -934,6 +934,16 @@ gboolean xaccAccountTypesCompatible (GNCAccountType parent_type,
  *  root account types are stripped. */
 guint32 xaccAccountTypesValid(void);
 
+/** Convenience function to check if the account is a valid
+ *  Asset or Liability type, but not a business account type
+ *  (meaning not an Accounts Payable/Accounts Receivable). */
+gboolean xaccAccountIsAssetLiabType(GNCAccountType t);
+
+/** Convenience function to check if the account is a valid
+ *  business account type
+ *  (meaning an Accounts Payable/Accounts Receivable). */
+gboolean xaccAccountIsAPARType(GNCAccountType t);
+
 
 /** @} */
 

commit 58b5a8e1f9349f1a571d49b786cbc1cc6845fae9
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 16 16:09:49 2014 +0200

    Remove superfluous include statements

diff --git a/src/engine/Split.c b/src/engine/Split.c
index b49389f..64055a3 100644
--- a/src/engine/Split.c
+++ b/src/engine/Split.c
@@ -43,7 +43,6 @@
 #include "Split.h"
 #include "AccountP.h"
 #include "Scrub.h"
-#include "Scrub3.h"
 #include "TransactionP.h"
 #include "TransLog.h"
 #include "cap-gains.h"
diff --git a/src/gnome/assistant-acct-period.c b/src/gnome/assistant-acct-period.c
index f2ee55d..e28a99c 100644
--- a/src/gnome/assistant-acct-period.c
+++ b/src/gnome/assistant-acct-period.c
@@ -40,7 +40,6 @@
 #include "Recurrence.h"
 #include "Query.h"
 #include "Scrub.h"
-#include "Scrub3.h"
 #include "Transaction.h"
 #include "dialog-utils.h"
 #include "assistant-acct-period.h"
diff --git a/src/gnome/window-autoclear.c b/src/gnome/window-autoclear.c
index 6b7be9d..8a1c500 100644
--- a/src/gnome/window-autoclear.c
+++ b/src/gnome/window-autoclear.c
@@ -26,7 +26,6 @@
 #include <glib/gi18n.h>
 
 #include "Scrub.h"
-#include "Scrub3.h"
 #include "dialog-account.h"
 #include "dialog-transfer.h"
 #include "dialog-utils.h"

commit 9cfc22f4f9983e52dd12894803152c0f66d9405d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 16 15:52:43 2014 +0200

    Small corrections in doxygen comments

diff --git a/src/engine/Scrub2.h b/src/engine/Scrub2.h
index e9ac308..6ab0ed0 100644
--- a/src/engine/Scrub2.h
+++ b/src/engine/Scrub2.h
@@ -53,7 +53,7 @@
  *   not in a lot will be used to close the oldest open lot(s).
  *   If there are no open lots, a new lot will be started.
  *   By trying to close the oldest lots, this effectively
- *   implements a FIFO acounting policy.
+ *   implements a FIFO accounting policy.
  */
 void xaccAccountAssignLots (Account *acc);
 
@@ -94,10 +94,13 @@ void xaccLotScrubDoubleBalance (GNCLot *lot);
  *
  *  The xaccScrubMergeTransSubSplits() routine does the same, except
  *    that it does it for all of the splits in the transaction.
- *  The xaccScrubMergeLotSubSplits() routine does the same, except
- *    that it does it for all of the splits in the lot.
  */
 gboolean xaccScrubMergeSubSplits (Split *split);
+
+/** The xaccScrubMergeLotSubSplits() routine does the same as
+ *    the xaccScrubMergSubSplits, except that it does it
+ *    for all of the splits in the lot.
+ */
 gboolean xaccScrubMergeLotSubSplits (GNCLot *lot);
 
 #endif /* XACC_SCRUB2_H */
diff --git a/src/engine/Scrub3.h b/src/engine/Scrub3.h
index 6e5a7f8..2c1a8ea 100644
--- a/src/engine/Scrub3.h
+++ b/src/engine/Scrub3.h
@@ -25,7 +25,7 @@
     @{ */
 
 /** @file Scrub3.h
- *  @brief Hiogh-Level API for imposing Lot constraints
+ *  @brief High-Level API for imposing Lot constraints
  *  @author Created by Linas Vepstas Sept 2003
  *  @author Copyright (c) 2003 Linas Vepstas <linas at linas.org>
  */
@@ -44,7 +44,7 @@
  *    self-consistent and properly balanced, and fixes it if its not.
  *    This is an important routine to call if the amount of any split
  *    in the lot is changed.  That's because (obviously) changing
- *    split values is gaurenteed to throw off lot balances.
+ *    split values is guaranteed to throw off lot balances.
  *    This routine may end up closing the lot, or at least trying
  *    to. It will also cause cap gains to be recomputed.
  *
diff --git a/src/engine/Transaction.h b/src/engine/Transaction.h
index 8091dba..2d3ae9d 100644
--- a/src/engine/Transaction.h
+++ b/src/engine/Transaction.h
@@ -207,7 +207,7 @@ void          xaccTransRollbackEdit (Transaction *trans);
 
 /** The xaccTransIsOpen() method returns TRUE if the transaction
     is open for editing. Otherwise, it returns false.
-    XXX this routne should probably be deprecated.  its, umm,
+    XXX this routine should probably be deprecated.  its, umm,
     hard to imagine legitimate uses (but it is used by
     the import/export code for reasons I can't understand.)
  */
diff --git a/src/gnome-utils/gnc-tree-view.h b/src/gnome-utils/gnc-tree-view.h
index 6cef2a6..e9b56da 100644
--- a/src/gnome-utils/gnc-tree-view.h
+++ b/src/gnome-utils/gnc-tree-view.h
@@ -259,7 +259,7 @@ gnc_tree_view_add_date_column (GncTreeView *view,
  *  column used to determine the foreground color of any text in this
  *  column.  It should be used to display negative numbers in red.
  *  Use GNC_TREE_VIEW_COLUMN_COLOR_NONE if the text in this column
- *  should always be displayed in black.
+ *  should always be displayed in the default theme color for text.
  *
  *  @param model_visibility_column The index of the GtkTreeModel data
  *  column used to determine whether or not a checkbox for each row
diff --git a/src/register/ledger-core/split-register.h b/src/register/ledger-core/split-register.h
index 5e82a04..6c7cd44 100644
--- a/src/register/ledger-core/split-register.h
+++ b/src/register/ledger-core/split-register.h
@@ -256,7 +256,7 @@ struct split_register
                                                 or split action for number
                                                 field in register */
     gboolean is_template;
-    gboolean do_auto_complete; /**< whether to use auto-competion */
+    gboolean do_auto_complete; /**< whether to use auto-completion */
 
     SRInfo * sr_info;   /**< private data; outsiders should not access this */
 };

commit f8a27b714065c7307a6bf529678256e59f7862d0
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 23 16:02:11 2014 +0200

    More ENTER/LEAVE corrections at function exit points

diff --git a/src/engine/Scrub.c b/src/engine/Scrub.c
index 055e95a..2ab318d 100644
--- a/src/engine/Scrub.c
+++ b/src/engine/Scrub.c
@@ -529,7 +529,10 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
 
     /* Return immediately if things are balanced. */
     if (xaccTransIsBalanced (trans))
+    {
+        LEAVE ("transaction is balanced");
         return;
+    }
 
     currency = xaccTransGetCurrency (trans);
 
@@ -626,7 +629,7 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
         imbal_list = xaccTransGetImbalance (trans);
         if (!imbal_list)
         {
-            LEAVE("()");
+            LEAVE("transaction is balanced");
             return;
         }
 
diff --git a/src/engine/gnc-lot.c b/src/engine/gnc-lot.c
index 08fdef6..ce780c4 100644
--- a/src/engine/gnc-lot.c
+++ b/src/engine/gnc-lot.c
@@ -231,6 +231,8 @@ gnc_lot_free(GNCLot* lot)
     priv->is_closed = TRUE;
     /* qof_instance_release (&lot->inst); */
     g_object_unref (lot);
+
+    LEAVE();
 }
 
 void
@@ -559,6 +561,7 @@ gnc_lot_remove_split (GNCLot *lot, Split *split)
     }
     gnc_lot_commit_edit(lot);
     qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, NULL);
+    LEAVE("removed from lot");
 }
 
 /* ============================================================== */

commit 0e7011665a5f2a8a99d2110284d13439c980d6ce
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jul 31 12:00:06 2014 -0700

    Balance ENTER with a LEAVE for all exit points.
    
    For xaccLotFill and xaccLotScrubDoubleBalance, anyway.

diff --git a/src/engine/Scrub2.c b/src/engine/Scrub2.c
index 8c041f5..3e71cd4 100644
--- a/src/engine/Scrub2.c
+++ b/src/engine/Scrub2.c
@@ -106,14 +106,28 @@ xaccLotFill (GNCLot *lot)
     ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
 
     /* If balance already zero, we have nothing to do. */
-    if (gnc_lot_is_closed (lot)) return;
-
+    if (gnc_lot_is_closed (lot))
+    {
+	LEAVE ("Lot Closed (lot=%s, acc=%s)", gnc_lot_get_title(lot),
+	       xaccAccountGetName(acc));
+	return;
+    }
     split = pcy->PolicyGetSplit (pcy, lot);
-    if (!split) return;   /* Handle the common case */
+    if (!split)
+    {
+	LEAVE ("No Split (lot=%s, acc=%s)", gnc_lot_get_title(lot),
+	       xaccAccountGetName(acc));
+	return;   /* Handle the common case */
+    }
 
     /* Reject voided transactions */
     if (gnc_numeric_zero_p(split->amount) &&
-            xaccTransGetVoidStatus(split->parent)) return;
+            xaccTransGetVoidStatus(split->parent))
+    {
+	LEAVE ("Voided transaction (lot=%s, acc=%s)",
+	       gnc_lot_get_title(lot), xaccAccountGetName(acc));
+	return;
+    }
 
     xaccAccountBeginEdit (acc);
 
@@ -166,7 +180,11 @@ xaccLotScrubDoubleBalance (GNCLot *lot)
     }
 
     /* We double-check only closed lots */
-    if (FALSE == gnc_lot_is_closed (lot)) return;
+    if (FALSE == gnc_lot_is_closed (lot))
+    {
+	LEAVE ("lot=%s is closed", gnc_lot_get_title(lot));
+	return;
+    }
 
     for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
     {

commit 87654e69f6ed4e13ffacce4348ccd2e814832636
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Tue Sep 2 14:50:32 2014 +0200

    Bug 434462 - register color don't work correct with system theme color - Part 2
    
    This commit fixes this for (business) entry ledgers.

diff --git a/src/business/business-ledger/gncEntryLedger.h b/src/business/business-ledger/gncEntryLedger.h
index a8668df..3a7532d 100644
--- a/src/business/business-ledger/gncEntryLedger.h
+++ b/src/business/business-ledger/gncEntryLedger.h
@@ -49,17 +49,6 @@ typedef enum
     GNCENTRY_NUM_REGISTER_TYPES
 } GncEntryLedgerType;
 
-typedef struct entry_ledger_colors
-{
-    guint32 header_bg_color;
-
-    guint32 primary_bg_color;
-    guint32 secondary_bg_color;
-
-    guint32 primary_active_bg_color;
-    guint32 secondary_active_bg_color;
-} GncEntryLedgerColors;
-
 #define ENTRY_IACCT_CELL	"inv-account"
 #define ENTRY_BACCT_CELL	"bill-account"
 #define ENTRY_ACTN_CELL		"action"
diff --git a/src/business/business-ledger/gncEntryLedgerModel.c b/src/business/business-ledger/gncEntryLedgerModel.c
index 96a0dca..87b8991 100644
--- a/src/business/business-ledger/gncEntryLedgerModel.c
+++ b/src/business/business-ledger/gncEntryLedgerModel.c
@@ -36,15 +36,6 @@
 #include "gncEntryLedgerP.h"
 #include "gncEntryLedgerModel.h"
 
-static GncEntryLedgerColors reg_colors =
-{
-    0x96B183,
-    0xBFDEB9,
-    0xF6FFDA,
-
-    0xFFEF98,
-    0xFFEF98,
-};
 
 /** Private Interfaces ***********************************************/
 
@@ -927,42 +918,84 @@ static CellIOFlags get_qty_io_flags (VirtualLocation virt_loc, gpointer user_dat
 /* GET BG_COLORS */
 
 static guint32
-gnc_entry_ledger_get_bg_color (VirtualLocation virt_loc,
-                               gboolean *hatching, gpointer user_data)
+gnc_entry_ledger_get_color_internal (VirtualLocation virt_loc,
+                                     GncEntryLedger *ledger,
+                                     const guint32 *color_table,
+                                     gboolean foreground)
 {
-    GncEntryLedger *ledger = user_data;
+    const char *cursor_name;
     VirtualCell *vcell;
-    guint32 bg_color;
     gboolean is_current;
+    guint32 colorbase = 0; /* By default return background colors */
 
-    if (hatching)
-        *hatching = FALSE;
-
-    bg_color = 0xffffff; /* white */
+    if (foreground)
+        colorbase = COLOR_UNKNOWN_FG; /* a bit of enum arithmetic */
 
-    if (!ledger) return bg_color;
+    if (!ledger)
+        return color_table[colorbase + COLOR_UNKNOWN_BG];
 
     if (gnc_table_virtual_location_in_header (ledger->table, virt_loc))
-        return reg_colors.header_bg_color;
+        return color_table[colorbase + COLOR_HEADER_BG];
 
     vcell = gnc_table_get_virtual_cell (ledger->table, virt_loc.vcell_loc);
     if (!vcell || !vcell->cellblock)
-        return bg_color;
+        return color_table[colorbase + COLOR_UNKNOWN_BG];
 
     if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
             (virt_loc.phys_col_offset > vcell->cellblock->stop_col))
-        return bg_color;
+        return color_table[colorbase + COLOR_UNKNOWN_BG];
 
-    is_current = virt_cell_loc_equal
-                 (ledger->table->current_cursor_loc.vcell_loc, virt_loc.vcell_loc);
+    is_current = virt_cell_loc_equal (ledger->table->current_cursor_loc.vcell_loc,
+                                      virt_loc.vcell_loc);
 
     if (is_current)
         return vcell->start_primary_color ?
-               reg_colors.primary_active_bg_color :
-               reg_colors.secondary_active_bg_color;
+                color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
+                color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
 
     return vcell->start_primary_color ?
-           reg_colors.primary_bg_color : reg_colors.secondary_bg_color;
+            color_table[colorbase + COLOR_PRIMARY_BG] : color_table[colorbase + COLOR_SECONDARY_BG];
+
+}
+
+static guint32
+gnc_entry_ledger_get_fg_color (VirtualLocation virt_loc,
+                               gpointer user_data)
+{
+    GncEntryLedger *ledger = user_data;
+    return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_default, TRUE);
+}
+
+static guint32
+gnc_entry_ledger_get_gtkrc_fg_color (VirtualLocation virt_loc,
+                                     gpointer user_data)
+{
+    GncEntryLedger *ledger = user_data;
+    return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_gtkrc, TRUE);
+}
+
+static guint32
+gnc_entry_ledger_get_bg_color (VirtualLocation virt_loc,
+                               gboolean *hatching, gpointer user_data)
+{
+    GncEntryLedger *ledger = user_data;
+
+    if (hatching)
+        *hatching = FALSE;
+
+    return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_default, FALSE);
+}
+
+static guint32
+gnc_entry_ledger_get_gtkrc_bg_color (VirtualLocation virt_loc,
+                                     gboolean *hatching, gpointer user_data)
+{
+    GncEntryLedger *ledger = user_data;
+
+    if (hatching)
+        *hatching = FALSE;
+
+    return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_gtkrc, FALSE);
 }
 
 /* SAVE CELLS */
@@ -1214,9 +1247,18 @@ static void gnc_entry_ledger_model_new_handlers (TableModel *model,
     };
     unsigned int i;
 
+    gnc_table_model_set_default_fg_color_handler
+    (model, gnc_entry_ledger_get_fg_color);
+
+    gnc_table_model_set_fg_color_handler
+    (model, gnc_entry_ledger_get_gtkrc_fg_color, "gtkrc");
+
     gnc_table_model_set_default_bg_color_handler
     (model, gnc_entry_ledger_get_bg_color);
 
+    gnc_table_model_set_bg_color_handler
+    (model, gnc_entry_ledger_get_gtkrc_bg_color, "gtkrc");
+
 
     for (i = 0; i < (sizeof(models) / sizeof(*models)); i++)
     {
diff --git a/src/register/ledger-core/split-register-model.c b/src/register/ledger-core/split-register-model.c
index 4ef9333..253dcac 100644
--- a/src/register/ledger-core/split-register-model.c
+++ b/src/register/ledger-core/split-register-model.c
@@ -39,66 +39,6 @@
 #include "split-register-p.h"
 #include "engine-helpers.h"
 
-
-typedef enum
-{
-    COLOR_TABLE_DEFAULT,
-    COLOR_TABLE_GTKRC,
-} SplitRegisterColorTable;
-
-/* Alternative color tables to use for the register.
- * The colors in this array are ordered according to the RegisterColor Enum
- * Be careful to respect this order !
- */
-static const guint32 reg_colors_default [] =
-{
-    0xFFFFFF,     // COLOR_UNKNOWN_BG
-    0x96B183,     // COLOR_HEADER_BG
-    0xBFDEB9,     // COLOR_PRIMARY_BG
-    0xFFEF98,     // COLOR_PRIMARY_BG_ACTIVE
-    0xF6FFDA,     // COLOR_SECONDARY_BG
-    0xFFEF98,     // COLOR_SECONDARY_BG_ACTIVE
-    0xEDE7D3,     // COLOR_SPLIT_BG
-    0xFFEF98,     // COLOR_SPLIT_BG_ACTIVE
-
-    0x000000,     // COLOR_UNKNOWN_FG
-    0x000000,     // COLOR_HEADER_FG
-    0x000000,     // COLOR_PRIMARY_FG
-    0x000000,     // COLOR_PRIMARY_FG_ACTIVE
-    0x000000,     // COLOR_SECONDARY_FG
-    0x000000,     // COLOR_SECONDARY_FG_ACTIVE
-    0x000000,     // COLOR_SPLIT_FG
-    0x000000,     // COLOR_SPLIT_FG_ACTIVE
-
-    0xFF0000,     // COLOR_NEGATIVE
-};
-
-/* The colors in this array are ordered according to the RegisterColor Enum
- * Be careful to respect this order !
- */
-static const guint32 reg_colors_gtkrc [] =
-{
-    COLOR_UNKNOWN_BG,          // COLOR_UNKNOWN_BG
-    COLOR_HEADER_BG,           // COLOR_HEADER_BG
-    COLOR_PRIMARY_BG,          // COLOR_PRIMARY_BG
-    COLOR_PRIMARY_BG_ACTIVE,   // COLOR_PRIMARY_BG_ACTIVE
-    COLOR_SECONDARY_BG,        // COLOR_SECONDARY_BG
-    COLOR_SECONDARY_BG_ACTIVE, // COLOR_SECONDARY_BG_ACTIVE
-    COLOR_SPLIT_BG,            // COLOR_SPLIT_BG
-    COLOR_SPLIT_BG_ACTIVE,     // COLOR_SPLIT_BG_ACTIVE
-
-    COLOR_UNKNOWN_FG,          // COLOR_UNKNOWN_FG
-    COLOR_HEADER_FG,           // COLOR_HEADER_FG
-    COLOR_PRIMARY_FG,          // COLOR_PRIMARY_FG
-    COLOR_PRIMARY_FG_ACTIVE,   // COLOR_PRIMARY_FG_ACTIVE
-    COLOR_SECONDARY_FG,        // COLOR_SECONDARY_FG
-    COLOR_SECONDARY_FG_ACTIVE, // COLOR_SECONDARY_FG_ACTIVE
-    COLOR_SPLIT_FG,            // COLOR_SPLIT_FG
-    COLOR_SPLIT_FG_ACTIVE,     // COLOR_SPLIT_FG_ACTIVE
-
-    COLOR_NEGATIVE,            // COLOR_NEGATIVE
-};
-
 /* This static indicates the debugging module that this .o belongs to. */
 static QofLogModule log_module = GNC_MOD_LEDGER;
 
diff --git a/src/register/ledger-core/split-register-model.h b/src/register/ledger-core/split-register-model.h
index fc9b0df..1f264c4 100644
--- a/src/register/ledger-core/split-register-model.h
+++ b/src/register/ledger-core/split-register-model.h
@@ -28,33 +28,4 @@
 TableModel * gnc_split_register_model_new (void);
 TableModel * gnc_template_register_model_new (void);
 
-typedef enum
-{
-    /* Colors used for background drawing */
-    COLOR_UNKNOWN_BG,
-    COLOR_HEADER_BG,
-    COLOR_PRIMARY_BG,
-    COLOR_PRIMARY_BG_ACTIVE,
-    COLOR_SECONDARY_BG,
-    COLOR_SECONDARY_BG_ACTIVE,
-    COLOR_SPLIT_BG,
-    COLOR_SPLIT_BG_ACTIVE,
-
-    /* Colors used for foreground drawing (text etc)
-     * ATTENTION: the background and foreground lists should have
-     *            the same types (the same amount of entries) !
-     *            The code relies on this ! */
-    COLOR_UNKNOWN_FG,
-    COLOR_HEADER_FG,
-    COLOR_PRIMARY_FG,
-    COLOR_PRIMARY_FG_ACTIVE,
-    COLOR_SECONDARY_FG,
-    COLOR_SECONDARY_FG_ACTIVE,
-    COLOR_SPLIT_FG,
-    COLOR_SPLIT_FG_ACTIVE,
-
-    /* Other colors */
-    COLOR_NEGATIVE, /* Color to use for negative numbers */
-} RegisterColor;
-
 #endif
diff --git a/src/register/register-core/table-allgui.h b/src/register/register-core/table-allgui.h
index 5fe7a91..2738738 100644
--- a/src/register/register-core/table-allgui.h
+++ b/src/register/register-core/table-allgui.h
@@ -156,6 +156,92 @@ struct table
     gpointer ui_data;
 };
 
+/** Color definitions used for table elements */
+typedef enum
+{
+    /* Colors used for background drawing */
+    COLOR_UNKNOWN_BG,
+    COLOR_HEADER_BG,
+    COLOR_PRIMARY_BG,
+    COLOR_PRIMARY_BG_ACTIVE,
+    COLOR_SECONDARY_BG,
+    COLOR_SECONDARY_BG_ACTIVE,
+    COLOR_SPLIT_BG,
+    COLOR_SPLIT_BG_ACTIVE,
+
+    /* Colors used for foreground drawing (text etc)
+     * ATTENTION: the background and foreground lists should have
+     *            the same types (the same amount of entries) !
+     *            The code relies on this ! */
+    COLOR_UNKNOWN_FG,
+    COLOR_HEADER_FG,
+    COLOR_PRIMARY_FG,
+    COLOR_PRIMARY_FG_ACTIVE,
+    COLOR_SECONDARY_FG,
+    COLOR_SECONDARY_FG_ACTIVE,
+    COLOR_SPLIT_FG,
+    COLOR_SPLIT_FG_ACTIVE,
+
+    /* Other colors */
+    COLOR_NEGATIVE, /* Color to use for negative numbers */
+} RegisterColor;
+
+
+
+
+/* Alternative color tables to use for the register.
+ * The colors in this array are ordered according to the RegisterColor Enum
+ * Be careful to respect this order !
+ */
+static const guint32 reg_colors_default [] =
+{
+    0xFFFFFF,     // COLOR_UNKNOWN_BG
+    0x96B183,     // COLOR_HEADER_BG
+    0xBFDEB9,     // COLOR_PRIMARY_BG
+    0xFFEF98,     // COLOR_PRIMARY_BG_ACTIVE
+    0xF6FFDA,     // COLOR_SECONDARY_BG
+    0xFFEF98,     // COLOR_SECONDARY_BG_ACTIVE
+    0xEDE7D3,     // COLOR_SPLIT_BG
+    0xFFEF98,     // COLOR_SPLIT_BG_ACTIVE
+
+    0x000000,     // COLOR_UNKNOWN_FG
+    0x000000,     // COLOR_HEADER_FG
+    0x000000,     // COLOR_PRIMARY_FG
+    0x000000,     // COLOR_PRIMARY_FG_ACTIVE
+    0x000000,     // COLOR_SECONDARY_FG
+    0x000000,     // COLOR_SECONDARY_FG_ACTIVE
+    0x000000,     // COLOR_SPLIT_FG
+    0x000000,     // COLOR_SPLIT_FG_ACTIVE
+
+    0xFF0000,     // COLOR_NEGATIVE
+};
+
+/* The colors in this array are ordered according to the RegisterColor Enum
+ * Be careful to respect this order !
+ */
+static const guint32 reg_colors_gtkrc [] =
+{
+    COLOR_UNKNOWN_BG,          // COLOR_UNKNOWN_BG
+    COLOR_HEADER_BG,           // COLOR_HEADER_BG
+    COLOR_PRIMARY_BG,          // COLOR_PRIMARY_BG
+    COLOR_PRIMARY_BG_ACTIVE,   // COLOR_PRIMARY_BG_ACTIVE
+    COLOR_SECONDARY_BG,        // COLOR_SECONDARY_BG
+    COLOR_SECONDARY_BG_ACTIVE, // COLOR_SECONDARY_BG_ACTIVE
+    COLOR_SPLIT_BG,            // COLOR_SPLIT_BG
+    COLOR_SPLIT_BG_ACTIVE,     // COLOR_SPLIT_BG_ACTIVE
+
+    COLOR_UNKNOWN_FG,          // COLOR_UNKNOWN_FG
+    COLOR_HEADER_FG,           // COLOR_HEADER_FG
+    COLOR_PRIMARY_FG,          // COLOR_PRIMARY_FG
+    COLOR_PRIMARY_FG_ACTIVE,   // COLOR_PRIMARY_FG_ACTIVE
+    COLOR_SECONDARY_FG,        // COLOR_SECONDARY_FG
+    COLOR_SECONDARY_FG_ACTIVE, // COLOR_SECONDARY_FG_ACTIVE
+    COLOR_SPLIT_FG,            // COLOR_SPLIT_FG
+    COLOR_SPLIT_FG_ACTIVE,     // COLOR_SPLIT_FG_ACTIVE
+
+    COLOR_NEGATIVE,            // COLOR_NEGATIVE
+};
+
 
 /* Set the default gui handlers used by new tables. */
 void gnc_table_set_default_gui_handlers (TableGUIHandlers *gui_handlers);

commit ac1990fc9d0f67beb03a8e132c65031d20a0138d
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 30 17:39:49 2014 +0200

    Bug 434462 - register color don't work correct with system theme color - Part 1
    
    This commit fixes this for ordinary registers. Entry ledgers (business) aren't handled yet.

diff --git a/src/register/ledger-core/split-register-model.c b/src/register/ledger-core/split-register-model.c
index af029da..4ef9333 100644
--- a/src/register/ledger-core/split-register-model.c
+++ b/src/register/ledger-core/split-register-model.c
@@ -60,6 +60,17 @@ static const guint32 reg_colors_default [] =
     0xFFEF98,     // COLOR_SECONDARY_BG_ACTIVE
     0xEDE7D3,     // COLOR_SPLIT_BG
     0xFFEF98,     // COLOR_SPLIT_BG_ACTIVE
+
+    0x000000,     // COLOR_UNKNOWN_FG
+    0x000000,     // COLOR_HEADER_FG
+    0x000000,     // COLOR_PRIMARY_FG
+    0x000000,     // COLOR_PRIMARY_FG_ACTIVE
+    0x000000,     // COLOR_SECONDARY_FG
+    0x000000,     // COLOR_SECONDARY_FG_ACTIVE
+    0x000000,     // COLOR_SPLIT_FG
+    0x000000,     // COLOR_SPLIT_FG_ACTIVE
+
+    0xFF0000,     // COLOR_NEGATIVE
 };
 
 /* The colors in this array are ordered according to the RegisterColor Enum
@@ -75,6 +86,17 @@ static const guint32 reg_colors_gtkrc [] =
     COLOR_SECONDARY_BG_ACTIVE, // COLOR_SECONDARY_BG_ACTIVE
     COLOR_SPLIT_BG,            // COLOR_SPLIT_BG
     COLOR_SPLIT_BG_ACTIVE,     // COLOR_SPLIT_BG_ACTIVE
+
+    COLOR_UNKNOWN_FG,          // COLOR_UNKNOWN_FG
+    COLOR_HEADER_FG,           // COLOR_HEADER_FG
+    COLOR_PRIMARY_FG,          // COLOR_PRIMARY_FG
+    COLOR_PRIMARY_FG_ACTIVE,   // COLOR_PRIMARY_FG_ACTIVE
+    COLOR_SECONDARY_FG,        // COLOR_SECONDARY_FG
+    COLOR_SECONDARY_FG_ACTIVE, // COLOR_SECONDARY_FG_ACTIVE
+    COLOR_SPLIT_FG,            // COLOR_SPLIT_FG
+    COLOR_SPLIT_FG_ACTIVE,     // COLOR_SPLIT_FG_ACTIVE
+
+    COLOR_NEGATIVE,            // COLOR_NEGATIVE
 };
 
 /* This static indicates the debugging module that this .o belongs to. */
@@ -539,84 +561,33 @@ get_trans_total_balance (SplitRegister *reg, Transaction *trans)
 }
 
 static guint32
-gnc_split_register_get_fg_color (VirtualLocation virt_loc,
-                                 gpointer user_data)
-{
-    SplitRegister *reg = user_data;
-    const guint32 black = 0x000000;
-    const guint32 red   = 0xff0000;
-    const char * cell_name;
-    gboolean is_current;
-    gnc_numeric value;
-    Split *split;
-
-    if (!use_red_for_negative)
-        return black;
-
-    split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
-    if (!split)
-        return black;
-
-    cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
-
-    if (gnc_cell_name_equal (cell_name, TSHRS_CELL))
-        value = get_trans_total_amount (reg, xaccSplitGetParent (split));
-    else if (gnc_cell_name_equal (cell_name, SHRS_CELL))
-    {
-        if (virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
-                                      virt_loc.vcell_loc))
-            value = gnc_price_cell_get_value
-                     ((PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
-                             SHRS_CELL));
-        else
-            value = xaccSplitGetAmount (split);
-    }
-    else if (gnc_cell_name_equal (cell_name, BALN_CELL))
-        value = xaccSplitGetBalance (split);
-    else if (gnc_cell_name_equal (cell_name, RBALN_CELL))
-        value = gnc_split_register_get_rbaln (virt_loc, user_data, TRUE);
-    else if (gnc_cell_name_equal (cell_name, TBALN_CELL))
-        value = get_trans_total_balance (reg, xaccSplitGetParent (split));
-
-    if ((gnc_cell_name_equal (cell_name, BALN_CELL)) ||
-            (gnc_cell_name_equal (cell_name, RBALN_CELL)) ||
-            (gnc_cell_name_equal (cell_name, TBALN_CELL)))
-        {
-            Account *account = xaccSplitGetAccount (split);
-            if (gnc_reverse_balance (account))
-                value = gnc_numeric_neg (value);
-        }
-
-    if (gnc_numeric_negative_p (value))
-        return red;
-
-    return black;
-}
-
-static guint32
-gnc_split_register_get_bg_color_internal (VirtualLocation virt_loc,
-                                          SplitRegister *reg,
-                                          const guint32 *color_table,
-                                          guint32 default_color)
+gnc_split_register_get_color_internal (VirtualLocation virt_loc,
+                                       SplitRegister *reg,
+                                       const guint32 *color_table,
+                                       gboolean foreground)
 {
     const char *cursor_name;
     VirtualCell *vcell;
     gboolean is_current;
     gboolean double_alternate_virt;
+    guint32 colorbase = 0; /* By default return background colors */
+
+    if (foreground)
+        colorbase = COLOR_UNKNOWN_FG; /* a bit of enum arithmetic */
 
     if (!reg)
-        return default_color;
+        return color_table[colorbase + COLOR_UNKNOWN_BG];
 
     if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
-        return color_table[COLOR_HEADER_BG];
+        return color_table[colorbase + COLOR_HEADER_BG];
 
     vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
     if (!vcell || !vcell->cellblock)
-        return default_color;
+        return color_table[colorbase + COLOR_UNKNOWN_BG];
 
     if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
             (virt_loc.phys_col_offset > vcell->cellblock->stop_col))
-        return default_color;
+        return color_table[colorbase + COLOR_UNKNOWN_BG];
 
     is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
                                       virt_loc.vcell_loc);
@@ -628,11 +599,11 @@ gnc_split_register_get_bg_color_internal (VirtualLocation virt_loc,
     {
         if (is_current)
             return vcell->start_primary_color ?
-                    color_table[COLOR_PRIMARY_BG_ACTIVE] :
-                    color_table[COLOR_SECONDARY_BG_ACTIVE];
+                    color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
+                    color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
 
         return vcell->start_primary_color ?
-                color_table[COLOR_PRIMARY_BG] : color_table[COLOR_SECONDARY_BG];
+                color_table[colorbase + COLOR_PRIMARY_BG] : color_table[colorbase + COLOR_SECONDARY_BG];
     }
 
     if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
@@ -646,35 +617,108 @@ gnc_split_register_get_bg_color_internal (VirtualLocation virt_loc,
         {
             if (double_alternate_virt)
                 return vcell->start_primary_color ?
-                        color_table[COLOR_PRIMARY_BG_ACTIVE] :
-                        color_table[COLOR_SECONDARY_BG_ACTIVE];
+                        color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
+                        color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
 
             return (virt_loc.phys_row_offset % 2 == 0) ?
-                    color_table[COLOR_PRIMARY_BG_ACTIVE] :
-                    color_table[COLOR_SECONDARY_BG_ACTIVE];
+                    color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
+                    color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
         }
 
         if (double_alternate_virt)
             return vcell->start_primary_color ?
-                    color_table[COLOR_PRIMARY_BG] :
-                    color_table[COLOR_SECONDARY_BG];
+                    color_table[colorbase + COLOR_PRIMARY_BG] :
+                    color_table[colorbase + COLOR_SECONDARY_BG];
 
         return (virt_loc.phys_row_offset % 2 == 0) ?
-                color_table[COLOR_PRIMARY_BG] :
-                color_table[COLOR_SECONDARY_BG];
+                color_table[colorbase + COLOR_PRIMARY_BG] :
+                color_table[colorbase + COLOR_SECONDARY_BG];
     }
 
     if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
     {
         if (is_current)
-            return color_table[COLOR_SPLIT_BG_ACTIVE];
+            return color_table[colorbase + COLOR_SPLIT_BG_ACTIVE];
 
-        return color_table[COLOR_SPLIT_BG];
+        return color_table[colorbase + COLOR_SPLIT_BG];
     }
 
     PWARN ("Unexpected cursor: %s\n", cursor_name);
 
-    return default_color;
+    return color_table[colorbase + COLOR_UNKNOWN_BG];
+}
+
+static guint32
+gnc_split_register_get_fg_color_internal (VirtualLocation virt_loc,
+                                          SplitRegister *reg,
+                                          const guint32 *color_table)
+{
+    const guint32 red_color = color_table[COLOR_NEGATIVE];
+    guint32 fg_color;
+    const char * cell_name;
+    gboolean is_current;
+    gnc_numeric value;
+    Split *split;
+
+    fg_color = gnc_split_register_get_color_internal (virt_loc, reg, color_table, TRUE);
+
+    if (!use_red_for_negative)
+        return fg_color;
+
+    split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
+    if (!split)
+        return fg_color;
+
+    cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
+
+    if (gnc_cell_name_equal (cell_name, TSHRS_CELL))
+        value = get_trans_total_amount (reg, xaccSplitGetParent (split));
+    else if (gnc_cell_name_equal (cell_name, SHRS_CELL))
+    {
+        if (virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
+                                      virt_loc.vcell_loc))
+            value = gnc_price_cell_get_value
+                     ((PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
+                             SHRS_CELL));
+        else
+            value = xaccSplitGetAmount (split);
+    }
+    else if (gnc_cell_name_equal (cell_name, BALN_CELL))
+        value = xaccSplitGetBalance (split);
+    else if (gnc_cell_name_equal (cell_name, RBALN_CELL))
+        value = gnc_split_register_get_rbaln (virt_loc, reg, TRUE);
+    else if (gnc_cell_name_equal (cell_name, TBALN_CELL))
+        value = get_trans_total_balance (reg, xaccSplitGetParent (split));
+
+    if ((gnc_cell_name_equal (cell_name, BALN_CELL)) ||
+            (gnc_cell_name_equal (cell_name, RBALN_CELL)) ||
+            (gnc_cell_name_equal (cell_name, TBALN_CELL)))
+        {
+            Account *account = xaccSplitGetAccount (split);
+            if (gnc_reverse_balance (account))
+                value = gnc_numeric_neg (value);
+        }
+
+    if (gnc_numeric_negative_p (value))
+        return red_color;
+
+    return fg_color;
+}
+
+static guint32
+gnc_split_register_get_fg_color (VirtualLocation virt_loc,
+                                 gpointer user_data)
+{
+    SplitRegister *reg = user_data;
+    return gnc_split_register_get_fg_color_internal (virt_loc, reg, reg_colors_default);
+}
+
+static guint32
+gnc_split_register_get_gtkrc_fg_color (VirtualLocation virt_loc,
+                                       gpointer user_data)
+{
+    SplitRegister *reg = user_data;
+    return gnc_split_register_get_fg_color_internal (virt_loc, reg, reg_colors_gtkrc);
 }
 
 static guint32
@@ -687,7 +731,7 @@ gnc_split_register_get_bg_color (VirtualLocation virt_loc,
     if (hatching)
         *hatching = FALSE;
 
-    return gnc_split_register_get_bg_color_internal (virt_loc, reg, reg_colors_default, 0xffffff);
+    return gnc_split_register_get_color_internal (virt_loc, reg, reg_colors_default, FALSE);
 }
 
 
@@ -701,7 +745,7 @@ gnc_split_register_get_gtkrc_bg_color (VirtualLocation virt_loc,
     if (hatching)
         *hatching = FALSE;
 
-    return gnc_split_register_get_bg_color_internal (virt_loc, reg, reg_colors_gtkrc, COLOR_UNKNOWN_BG);
+    return gnc_split_register_get_color_internal (virt_loc, reg, reg_colors_gtkrc, FALSE);
 }
 
 static guint32
@@ -2611,6 +2655,9 @@ gnc_split_register_model_new (void)
     gnc_table_model_set_fg_color_handler(
         model, gnc_split_register_get_fg_color, RBALN_CELL);
 
+    gnc_table_model_set_fg_color_handler(
+        model, gnc_split_register_get_gtkrc_fg_color, "gtkrc");
+
 
     gnc_table_model_set_default_bg_color_handler(
         model, gnc_split_register_get_bg_color);
diff --git a/src/register/ledger-core/split-register-model.h b/src/register/ledger-core/split-register-model.h
index f26e28c..fc9b0df 100644
--- a/src/register/ledger-core/split-register-model.h
+++ b/src/register/ledger-core/split-register-model.h
@@ -30,6 +30,7 @@ TableModel * gnc_template_register_model_new (void);
 
 typedef enum
 {
+    /* Colors used for background drawing */
     COLOR_UNKNOWN_BG,
     COLOR_HEADER_BG,
     COLOR_PRIMARY_BG,
@@ -38,6 +39,22 @@ typedef enum
     COLOR_SECONDARY_BG_ACTIVE,
     COLOR_SPLIT_BG,
     COLOR_SPLIT_BG_ACTIVE,
+
+    /* Colors used for foreground drawing (text etc)
+     * ATTENTION: the background and foreground lists should have
+     *            the same types (the same amount of entries) !
+     *            The code relies on this ! */
+    COLOR_UNKNOWN_FG,
+    COLOR_HEADER_FG,
+    COLOR_PRIMARY_FG,
+    COLOR_PRIMARY_FG_ACTIVE,
+    COLOR_SECONDARY_FG,
+    COLOR_SECONDARY_FG_ACTIVE,
+    COLOR_SPLIT_FG,
+    COLOR_SPLIT_FG_ACTIVE,
+
+    /* Other colors */
+    COLOR_NEGATIVE, /* Color to use for negative numbers */
 } RegisterColor;
 
 #endif
diff --git a/src/register/register-core/table-allgui.c b/src/register/register-core/table-allgui.c
index 29ec9b0..8e7a010 100644
--- a/src/register/register-core/table-allgui.c
+++ b/src/register/register-core/table-allgui.c
@@ -345,25 +345,39 @@ gnc_table_get_label (Table *table, VirtualLocation virt_loc)
     return label;
 }
 
-guint32
-gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
+static guint32
+gnc_table_get_fg_color_internal (Table *table, VirtualLocation virt_loc,
+                                 gboolean want_gtkrc)
 {
     TableGetFGColorHandler fg_color_handler;
-    const char *cell_name;
+    const char *handler_name = "gtkrc";
 
     if (!table || !table->model)
         return 0x0; /* black */
 
-    cell_name = gnc_table_get_cell_name (table, virt_loc);
+    if (!want_gtkrc)
+        handler_name = gnc_table_get_cell_name (table, virt_loc);
 
     fg_color_handler = gnc_table_model_get_fg_color_handler (table->model,
-                       cell_name);
+                       handler_name);
     if (!fg_color_handler)
         return 0x0;
 
     return fg_color_handler (virt_loc, table->model->handler_user_data);
 }
 
+guint32
+gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
+{
+    return gnc_table_get_fg_color_internal (table, virt_loc, FALSE);
+}
+
+guint32
+gnc_table_get_gtkrc_fg_color (Table *table, VirtualLocation virt_loc)
+{
+    return gnc_table_get_fg_color_internal (table, virt_loc, TRUE);
+}
+
 static guint32
 gnc_table_get_bg_color_internal (Table *table, VirtualLocation virt_loc,
                                  gboolean *hatching,
diff --git a/src/register/register-core/table-allgui.h b/src/register/register-core/table-allgui.h
index 0f7bbd4..5fe7a91 100644
--- a/src/register/register-core/table-allgui.h
+++ b/src/register/register-core/table-allgui.h
@@ -210,6 +210,8 @@ CellIOFlags    gnc_table_get_io_flags (Table *table, VirtualLocation virt_loc);
 
 guint32        gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc);
 
+guint32        gnc_table_get_gtkrc_fg_color (Table *table, VirtualLocation virt_loc);
+
 guint32        gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
                                        gboolean *hatching);
 guint32        gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
diff --git a/src/register/register-gnome/gnucash-grid.c b/src/register/register-gnome/gnucash-grid.c
index d357c8f..3272104 100644
--- a/src/register/register-gnome/gnucash-grid.c
+++ b/src/register/register-gnome/gnucash-grid.c
@@ -605,17 +605,25 @@ draw_cell (GnucashGrid *grid,
     context = pango_layout_get_context (layout);
     font = pango_font_description_copy (pango_context_get_font_description (context));
 
-    argb = gnc_table_get_fg_color (table, virt_loc);
-#ifdef READONLY_LINES_WITH_CHANGED_FG_COLOR
-    // Are we in a read-only row? Then make the foreground color somewhat less black
-    if ((virt_loc.phys_row_offset == (block->style->nrows - 1))
-            && (table->model->dividing_row_upper >= 0)
-            && (virt_loc.vcell_loc.virt_row < table->model->dividing_row_upper))
+    if (grid->sheet->use_theme_colors)
     {
-        argb = inc_intensity_10percent(argb);
+        color_type = gnc_table_get_gtkrc_fg_color (table, virt_loc);
+        fg_color = get_gtkrc_color(grid->sheet, color_type);
     }
+    else
+    {
+        argb = gnc_table_get_fg_color (table, virt_loc);
+#ifdef READONLY_LINES_WITH_CHANGED_FG_COLOR
+        // Are we in a read-only row? Then make the foreground color somewhat less black
+        if ((virt_loc.phys_row_offset == (block->style->nrows - 1))
+                && (table->model->dividing_row_upper >= 0)
+                && (virt_loc.vcell_loc.virt_row < table->model->dividing_row_upper))
+        {
+            argb = inc_intensity_10percent(argb);
+        }
 #endif
-    fg_color = gnucash_color_argb_to_gdk (argb);
+        fg_color = gnucash_color_argb_to_gdk (argb);
+    }
 
     gdk_gc_set_foreground (grid->gc, fg_color);
 
diff --git a/src/register/register-gnome/gnucash-header.c b/src/register/register-gnome/gnucash-header.c
index 7d902d5..dab6873 100644
--- a/src/register/register-gnome/gnucash-header.c
+++ b/src/register/register-gnome/gnucash-header.c
@@ -75,7 +75,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
     VirtualLocation virt_loc;
     VirtualCell *vcell;
     CellDimensions *cd;
-    GdkColor *bg_color;
+    GdkColor *bg_color, *fg_color;
     int xpaint, ypaint;
     const char *text;
     CellBlock *cb;
@@ -94,11 +94,15 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
         color_type = gnc_table_get_gtkrc_bg_color (table, virt_loc,
                      NULL);
         bg_color = get_gtkrc_color(header->sheet, color_type);
+        color_type = gnc_table_get_gtkrc_fg_color (table, virt_loc);
+        fg_color = get_gtkrc_color(header->sheet, color_type);
     }
     else
     {
         argb = gnc_table_get_bg_color (table, virt_loc, NULL);
         bg_color = gnucash_color_argb_to_gdk (argb);
+        argb = gnc_table_get_fg_color (table, virt_loc);
+        fg_color = gnucash_color_argb_to_gdk (argb);
     }
 
     h = style->dimensions->height;
@@ -111,7 +115,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
                         style->dimensions->width, h);
 
     gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
-    gdk_gc_set_foreground (header->gc, &gn_black);
+    gdk_gc_set_foreground (header->gc, fg_color);
 
     gdk_draw_rectangle (drawable, header->gc, FALSE, -x, -y,
                         style->dimensions->width, h);
@@ -121,7 +125,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
 
     gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
     gdk_gc_set_background (header->gc, &gn_white);
-    gdk_gc_set_foreground (header->gc, &gn_black);
+    gdk_gc_set_foreground (header->gc, fg_color);
     /*font = gnucash_register_font;*/
 
     vcell = gnc_table_get_virtual_cell
diff --git a/src/register/register-gnome/gnucash-item-edit.c b/src/register/register-gnome/gnucash-item-edit.c
index 3486ffb..9b34102 100644
--- a/src/register/register-gnome/gnucash-item-edit.c
+++ b/src/register/register-gnome/gnucash-item-edit.c
@@ -196,16 +196,20 @@ gnc_item_edit_draw_info (GncItemEdit *item_edit, int x, int y, TextDrawInfo *inf
                      item_edit->virt_loc,
                      &hatching);
         info->bg_color = get_gtkrc_color(item_edit->sheet, color_type);
+        color_type = gnc_table_get_gtkrc_fg_color (table,
+                     item_edit->virt_loc);
+        info->fg_color = get_gtkrc_color(item_edit->sheet, color_type);
     }
     else
     {
         argb = gnc_table_get_bg_color (table, item_edit->virt_loc,
                                        &hatching);
         info->bg_color = gnucash_color_argb_to_gdk (argb);
+        argb = gnc_table_get_fg_color (table, item_edit->virt_loc);
+        info->fg_color = gnucash_color_argb_to_gdk (argb);
     }
 
     info->hatching = hatching;
-    info->fg_color = &gn_black;
 
     info->bg_color2 = &gn_dark_gray;
     info->fg_color2 = &gn_white;
diff --git a/src/register/register-gnome/gnucash-sheet.c b/src/register/register-gnome/gnucash-sheet.c
index 03f6918..06c639e 100644
--- a/src/register/register-gnome/gnucash-sheet.c
+++ b/src/register/register-gnome/gnucash-sheet.c
@@ -2609,31 +2609,49 @@ get_gtkrc_color (GnucashSheet *sheet,
 {
     GtkWidget *widget = NULL;
     GtkStyle *style;
-    GdkColor *white;
+    GdkColor *white, *black, *red;
     GdkColor *color = NULL;
 
     white = gnucash_color_argb_to_gdk (0xFFFFFF);
+    black = gnucash_color_argb_to_gdk (0x000000);
+    red   = gnucash_color_argb_to_gdk (0xFF0000); /* Hardcoded...*/
     switch (field_type)
     {
     default:
         return white;
 
+    case COLOR_UNKNOWN_BG:
+        return white;
+
+    case COLOR_UNKNOWN_FG:
+        return black;
+
+    case COLOR_NEGATIVE:
+        return red;
+
     case COLOR_HEADER_BG:
+    case COLOR_HEADER_FG:
         widget = sheet->header_color;
         break;
 
     case COLOR_PRIMARY_BG:
     case COLOR_PRIMARY_BG_ACTIVE:
+    case COLOR_PRIMARY_FG:
+    case COLOR_PRIMARY_FG_ACTIVE:
         widget = sheet->primary_color;
         break;
 
     case COLOR_SECONDARY_BG:
     case COLOR_SECONDARY_BG_ACTIVE:
+    case COLOR_SECONDARY_FG:
+    case COLOR_SECONDARY_FG_ACTIVE:
         widget = sheet->secondary_color;
         break;
 
     case COLOR_SPLIT_BG:
     case COLOR_SPLIT_BG_ACTIVE:
+    case COLOR_SPLIT_FG:
+    case COLOR_SPLIT_FG_ACTIVE:
         widget = sheet->split_color;
         break;
     }
@@ -2659,6 +2677,19 @@ get_gtkrc_color (GnucashSheet *sheet,
     case COLOR_SPLIT_BG_ACTIVE:
         color = &style->base[GTK_STATE_SELECTED];
         break;
+
+    case COLOR_HEADER_FG:
+    case COLOR_PRIMARY_FG:
+    case COLOR_SECONDARY_FG:
+    case COLOR_SPLIT_FG:
+        color = &style->text[GTK_STATE_NORMAL];
+        break;
+
+    case COLOR_PRIMARY_FG_ACTIVE:
+    case COLOR_SECONDARY_FG_ACTIVE:
+    case COLOR_SPLIT_FG_ACTIVE:
+        color = &style->text[GTK_STATE_SELECTED];
+        break;
     }
 
     gnucash_color_alloc_gdk(color);

commit 61cd3f1106c4549fcad404aa7eeef463a014a93e
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 30 16:09:01 2014 +0200

    Rename enum elements to make room for foreground equivalents

diff --git a/src/register/ledger-core/split-register-model.c b/src/register/ledger-core/split-register-model.c
index 2e34b4e..af029da 100644
--- a/src/register/ledger-core/split-register-model.c
+++ b/src/register/ledger-core/split-register-model.c
@@ -52,14 +52,14 @@ typedef enum
  */
 static const guint32 reg_colors_default [] =
 {
-    0x000000,     // COLOR_UNKNOWN
-    0x96B183,     // COLOR_HEADER
-    0xBFDEB9,     // COLOR_PRIMARY
-    0xFFEF98,     // COLOR_PRIMARY_ACTIVE
-    0xF6FFDA,     // COLOR_SECONDARY
-    0xFFEF98,     // COLOR_SECONDARY_ACTIVE
-    0xEDE7D3,     // COLOR_SPLIT
-    0xFFEF98,     // COLOR_SPLIT_ACTIVE
+    0xFFFFFF,     // COLOR_UNKNOWN_BG
+    0x96B183,     // COLOR_HEADER_BG
+    0xBFDEB9,     // COLOR_PRIMARY_BG
+    0xFFEF98,     // COLOR_PRIMARY_BG_ACTIVE
+    0xF6FFDA,     // COLOR_SECONDARY_BG
+    0xFFEF98,     // COLOR_SECONDARY_BG_ACTIVE
+    0xEDE7D3,     // COLOR_SPLIT_BG
+    0xFFEF98,     // COLOR_SPLIT_BG_ACTIVE
 };
 
 /* The colors in this array are ordered according to the RegisterColor Enum
@@ -67,14 +67,14 @@ static const guint32 reg_colors_default [] =
  */
 static const guint32 reg_colors_gtkrc [] =
 {
-    COLOR_UNKNOWN,          // COLOR_UNKNOWN
-    COLOR_HEADER,           // COLOR_HEADER
-    COLOR_PRIMARY,          // COLOR_PRIMARY
-    COLOR_PRIMARY_ACTIVE,   // COLOR_PRIMARY_ACTIVE
-    COLOR_SECONDARY,        // COLOR_SECONDARY
-    COLOR_SECONDARY_ACTIVE, // COLOR_SECONDARY_ACTIVE
-    COLOR_SPLIT,            // COLOR_SPLIT
-    COLOR_SPLIT_ACTIVE,     // COLOR_SPLIT_ACTIVE
+    COLOR_UNKNOWN_BG,          // COLOR_UNKNOWN_BG
+    COLOR_HEADER_BG,           // COLOR_HEADER_BG
+    COLOR_PRIMARY_BG,          // COLOR_PRIMARY_BG
+    COLOR_PRIMARY_BG_ACTIVE,   // COLOR_PRIMARY_BG_ACTIVE
+    COLOR_SECONDARY_BG,        // COLOR_SECONDARY_BG
+    COLOR_SECONDARY_BG_ACTIVE, // COLOR_SECONDARY_BG_ACTIVE
+    COLOR_SPLIT_BG,            // COLOR_SPLIT_BG
+    COLOR_SPLIT_BG_ACTIVE,     // COLOR_SPLIT_BG_ACTIVE
 };
 
 /* This static indicates the debugging module that this .o belongs to. */
@@ -608,7 +608,7 @@ gnc_split_register_get_bg_color_internal (VirtualLocation virt_loc,
         return default_color;
 
     if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
-        return color_table[COLOR_HEADER];
+        return color_table[COLOR_HEADER_BG];
 
     vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
     if (!vcell || !vcell->cellblock)
@@ -628,11 +628,11 @@ gnc_split_register_get_bg_color_internal (VirtualLocation virt_loc,
     {
         if (is_current)
             return vcell->start_primary_color ?
-                    color_table[COLOR_PRIMARY_ACTIVE] :
-                    color_table[COLOR_SECONDARY_ACTIVE];
+                    color_table[COLOR_PRIMARY_BG_ACTIVE] :
+                    color_table[COLOR_SECONDARY_BG_ACTIVE];
 
         return vcell->start_primary_color ?
-                color_table[COLOR_PRIMARY] : color_table[COLOR_SECONDARY];
+                color_table[COLOR_PRIMARY_BG] : color_table[COLOR_SECONDARY_BG];
     }
 
     if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
@@ -646,30 +646,30 @@ gnc_split_register_get_bg_color_internal (VirtualLocation virt_loc,
         {
             if (double_alternate_virt)
                 return vcell->start_primary_color ?
-                        color_table[COLOR_PRIMARY_ACTIVE] :
-                        color_table[COLOR_SECONDARY_ACTIVE];
+                        color_table[COLOR_PRIMARY_BG_ACTIVE] :
+                        color_table[COLOR_SECONDARY_BG_ACTIVE];
 
             return (virt_loc.phys_row_offset % 2 == 0) ?
-                    color_table[COLOR_PRIMARY_ACTIVE] :
-                    color_table[COLOR_SECONDARY_ACTIVE];
+                    color_table[COLOR_PRIMARY_BG_ACTIVE] :
+                    color_table[COLOR_SECONDARY_BG_ACTIVE];
         }
 
         if (double_alternate_virt)
             return vcell->start_primary_color ?
-                    color_table[COLOR_PRIMARY] :
-                    color_table[COLOR_SECONDARY];
+                    color_table[COLOR_PRIMARY_BG] :
+                    color_table[COLOR_SECONDARY_BG];
 
         return (virt_loc.phys_row_offset % 2 == 0) ?
-                color_table[COLOR_PRIMARY] :
-                color_table[COLOR_SECONDARY];
+                color_table[COLOR_PRIMARY_BG] :
+                color_table[COLOR_SECONDARY_BG];
     }
 
     if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
     {
         if (is_current)
-            return color_table[COLOR_SPLIT_ACTIVE];
+            return color_table[COLOR_SPLIT_BG_ACTIVE];
 
-        return color_table[COLOR_SPLIT];
+        return color_table[COLOR_SPLIT_BG];
     }
 
     PWARN ("Unexpected cursor: %s\n", cursor_name);
@@ -701,7 +701,7 @@ gnc_split_register_get_gtkrc_bg_color (VirtualLocation virt_loc,
     if (hatching)
         *hatching = FALSE;
 
-    return gnc_split_register_get_bg_color_internal (virt_loc, reg, reg_colors_gtkrc, COLOR_UNKNOWN);
+    return gnc_split_register_get_bg_color_internal (virt_loc, reg, reg_colors_gtkrc, COLOR_UNKNOWN_BG);
 }
 
 static guint32
diff --git a/src/register/ledger-core/split-register-model.h b/src/register/ledger-core/split-register-model.h
index e3ace1b..f26e28c 100644
--- a/src/register/ledger-core/split-register-model.h
+++ b/src/register/ledger-core/split-register-model.h
@@ -30,14 +30,14 @@ TableModel * gnc_template_register_model_new (void);
 
 typedef enum
 {
-    COLOR_UNKNOWN,
-    COLOR_HEADER,
-    COLOR_PRIMARY,
-    COLOR_PRIMARY_ACTIVE,
-    COLOR_SECONDARY,
-    COLOR_SECONDARY_ACTIVE,
-    COLOR_SPLIT,
-    COLOR_SPLIT_ACTIVE,
+    COLOR_UNKNOWN_BG,
+    COLOR_HEADER_BG,
+    COLOR_PRIMARY_BG,
+    COLOR_PRIMARY_BG_ACTIVE,
+    COLOR_SECONDARY_BG,
+    COLOR_SECONDARY_BG_ACTIVE,
+    COLOR_SPLIT_BG,
+    COLOR_SPLIT_BG_ACTIVE,
 } RegisterColor;
 
 #endif
diff --git a/src/register/register-gnome/gnucash-sheet.c b/src/register/register-gnome/gnucash-sheet.c
index 1c17b76..03f6918 100644
--- a/src/register/register-gnome/gnucash-sheet.c
+++ b/src/register/register-gnome/gnucash-sheet.c
@@ -2618,22 +2618,22 @@ get_gtkrc_color (GnucashSheet *sheet,
     default:
         return white;
 
-    case COLOR_HEADER:
+    case COLOR_HEADER_BG:
         widget = sheet->header_color;
         break;
 
-    case COLOR_PRIMARY:
-    case COLOR_PRIMARY_ACTIVE:
+    case COLOR_PRIMARY_BG:
+    case COLOR_PRIMARY_BG_ACTIVE:
         widget = sheet->primary_color;
         break;
 
-    case COLOR_SECONDARY:
-    case COLOR_SECONDARY_ACTIVE:
+    case COLOR_SECONDARY_BG:
+    case COLOR_SECONDARY_BG_ACTIVE:
         widget = sheet->secondary_color;
         break;
 
-    case COLOR_SPLIT:
-    case COLOR_SPLIT_ACTIVE:
+    case COLOR_SPLIT_BG:
+    case COLOR_SPLIT_BG_ACTIVE:
         widget = sheet->split_color;
         break;
     }
@@ -2647,16 +2647,16 @@ get_gtkrc_color (GnucashSheet *sheet,
     default:
         return white;
 
-    case COLOR_HEADER:
-    case COLOR_PRIMARY:
-    case COLOR_SECONDARY:
-    case COLOR_SPLIT:
+    case COLOR_HEADER_BG:
+    case COLOR_PRIMARY_BG:
+    case COLOR_SECONDARY_BG:
+    case COLOR_SPLIT_BG:
         color = &style->base[GTK_STATE_NORMAL];
         break;
 
-    case COLOR_PRIMARY_ACTIVE:
-    case COLOR_SECONDARY_ACTIVE:
-    case COLOR_SPLIT_ACTIVE:
+    case COLOR_PRIMARY_BG_ACTIVE:
+    case COLOR_SECONDARY_BG_ACTIVE:
+    case COLOR_SPLIT_BG_ACTIVE:
         color = &style->base[GTK_STATE_SELECTED];
         break;
     }

commit 904afd8bd517f20897883200feb0b821d0407b8f
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 30 15:51:22 2014 +0200

    Refactor register color functions to reduce duplication

diff --git a/src/register/ledger-core/split-register-model.c b/src/register/ledger-core/split-register-model.c
index 4ff22af..2e34b4e 100644
--- a/src/register/ledger-core/split-register-model.c
+++ b/src/register/ledger-core/split-register-model.c
@@ -40,15 +40,41 @@
 #include "engine-helpers.h"
 
 
-static SplitRegisterColors reg_colors =
-{
-    0x96B183,
-    0xBFDEB9,
-    0xF6FFDA,
-    0xFFEF98,
-    0xFFEF98,
-    0xEDE7D3,
-    0xFFEF98,
+typedef enum
+{
+    COLOR_TABLE_DEFAULT,
+    COLOR_TABLE_GTKRC,
+} SplitRegisterColorTable;
+
+/* Alternative color tables to use for the register.
+ * The colors in this array are ordered according to the RegisterColor Enum
+ * Be careful to respect this order !
+ */
+static const guint32 reg_colors_default [] =
+{
+    0x000000,     // COLOR_UNKNOWN
+    0x96B183,     // COLOR_HEADER
+    0xBFDEB9,     // COLOR_PRIMARY
+    0xFFEF98,     // COLOR_PRIMARY_ACTIVE
+    0xF6FFDA,     // COLOR_SECONDARY
+    0xFFEF98,     // COLOR_SECONDARY_ACTIVE
+    0xEDE7D3,     // COLOR_SPLIT
+    0xFFEF98,     // COLOR_SPLIT_ACTIVE
+};
+
+/* The colors in this array are ordered according to the RegisterColor Enum
+ * Be careful to respect this order !
+ */
+static const guint32 reg_colors_gtkrc [] =
+{
+    COLOR_UNKNOWN,          // COLOR_UNKNOWN
+    COLOR_HEADER,           // COLOR_HEADER
+    COLOR_PRIMARY,          // COLOR_PRIMARY
+    COLOR_PRIMARY_ACTIVE,   // COLOR_PRIMARY_ACTIVE
+    COLOR_SECONDARY,        // COLOR_SECONDARY
+    COLOR_SECONDARY_ACTIVE, // COLOR_SECONDARY_ACTIVE
+    COLOR_SPLIT,            // COLOR_SPLIT
+    COLOR_SPLIT_ACTIVE,     // COLOR_SPLIT_ACTIVE
 };
 
 /* This static indicates the debugging module that this .o belongs to. */
@@ -513,15 +539,15 @@ get_trans_total_balance (SplitRegister *reg, Transaction *trans)
 }
 
 static guint32
-gnc_split_register_get_shares_fg_color (VirtualLocation virt_loc,
-                                        gpointer user_data)
+gnc_split_register_get_fg_color (VirtualLocation virt_loc,
+                                 gpointer user_data)
 {
     SplitRegister *reg = user_data;
     const guint32 black = 0x000000;
     const guint32 red   = 0xff0000;
     const char * cell_name;
     gboolean is_current;
-    gnc_numeric shares;
+    gnc_numeric value;
     Split *split;
 
     if (!use_red_for_negative)
@@ -533,96 +559,64 @@ gnc_split_register_get_shares_fg_color (VirtualLocation virt_loc,
 
     cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
 
-    is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
-                                      virt_loc.vcell_loc);
-
     if (gnc_cell_name_equal (cell_name, TSHRS_CELL))
-        shares = get_trans_total_amount (reg, xaccSplitGetParent (split));
-    else if (is_current)
-        shares = gnc_price_cell_get_value
-                 ((PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
-                         SHRS_CELL));
-    else
-        shares = xaccSplitGetAmount (split);
-
-    if (gnc_numeric_negative_p (shares))
-        return red;
-
-    return black;
-}
-
-static guint32
-gnc_split_register_get_balance_fg_color (VirtualLocation virt_loc,
-        gpointer user_data)
-{
-    SplitRegister *reg = user_data;
-    const guint32 black = 0x000000;
-    const guint32 red   = 0xff0000;
-    const char * cell_name;
-    gnc_numeric balance;
-    Split *split;
-
-    if (!use_red_for_negative)
-        return black;
-
-    split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
-    if (!split)
-        return black;
-
-    cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
-
-    if (gnc_cell_name_equal (cell_name, BALN_CELL))
-        balance = xaccSplitGetBalance (split);
-    else if (gnc_cell_name_equal (cell_name, RBALN_CELL))
-        balance = gnc_split_register_get_rbaln (virt_loc, user_data, TRUE);
-    else
-        balance = get_trans_total_balance (reg, xaccSplitGetParent (split));
-
+        value = get_trans_total_amount (reg, xaccSplitGetParent (split));
+    else if (gnc_cell_name_equal (cell_name, SHRS_CELL))
     {
-        Account *account;
-
-        account = xaccSplitGetAccount (split);
-
-        if (gnc_reverse_balance (account))
-            balance = gnc_numeric_neg (balance);
+        if (virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
+                                      virt_loc.vcell_loc))
+            value = gnc_price_cell_get_value
+                     ((PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
+                             SHRS_CELL));
+        else
+            value = xaccSplitGetAmount (split);
     }
+    else if (gnc_cell_name_equal (cell_name, BALN_CELL))
+        value = xaccSplitGetBalance (split);
+    else if (gnc_cell_name_equal (cell_name, RBALN_CELL))
+        value = gnc_split_register_get_rbaln (virt_loc, user_data, TRUE);
+    else if (gnc_cell_name_equal (cell_name, TBALN_CELL))
+        value = get_trans_total_balance (reg, xaccSplitGetParent (split));
+
+    if ((gnc_cell_name_equal (cell_name, BALN_CELL)) ||
+            (gnc_cell_name_equal (cell_name, RBALN_CELL)) ||
+            (gnc_cell_name_equal (cell_name, TBALN_CELL)))
+        {
+            Account *account = xaccSplitGetAccount (split);
+            if (gnc_reverse_balance (account))
+                value = gnc_numeric_neg (value);
+        }
 
-    if (gnc_numeric_negative_p (balance))
+    if (gnc_numeric_negative_p (value))
         return red;
 
     return black;
 }
 
 static guint32
-gnc_split_register_get_bg_color (VirtualLocation virt_loc,
-                                 gboolean *hatching,
-                                 gpointer user_data)
+gnc_split_register_get_bg_color_internal (VirtualLocation virt_loc,
+                                          SplitRegister *reg,
+                                          const guint32 *color_table,
+                                          guint32 default_color)
 {
-    SplitRegister *reg = user_data;
     const char *cursor_name;
     VirtualCell *vcell;
-    guint32 bg_color;
     gboolean is_current;
     gboolean double_alternate_virt;
 
-    if (hatching)
-        *hatching = FALSE;
-
-    bg_color = 0xffffff; /* white */
-
     if (!reg)
-        return bg_color;
+        return default_color;
 
     if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
-        return reg_colors.header_bg_color;
+        return color_table[COLOR_HEADER];
 
     vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
     if (!vcell || !vcell->cellblock)
-        return bg_color;
+        return default_color;
 
     if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
             (virt_loc.phys_col_offset > vcell->cellblock->stop_col))
-        return bg_color;
+        return default_color;
 
     is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
                                       virt_loc.vcell_loc);
@@ -634,11 +628,11 @@ gnc_split_register_get_bg_color (VirtualLocation virt_loc,
     {
         if (is_current)
             return vcell->start_primary_color ?
-                   reg_colors.primary_active_bg_color :
-                   reg_colors.secondary_active_bg_color;
+                    color_table[COLOR_PRIMARY_ACTIVE] :
+                    color_table[COLOR_SECONDARY_ACTIVE];
 
         return vcell->start_primary_color ?
-               reg_colors.primary_bg_color : reg_colors.secondary_bg_color;
+                color_table[COLOR_PRIMARY] : color_table[COLOR_SECONDARY];
     }
 
     if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
@@ -652,119 +646,62 @@ gnc_split_register_get_bg_color (VirtualLocation virt_loc,
         {
             if (double_alternate_virt)
                 return vcell->start_primary_color ?
-                       reg_colors.primary_active_bg_color :
-                       reg_colors.secondary_active_bg_color;
+                        color_table[COLOR_PRIMARY_ACTIVE] :
+                        color_table[COLOR_SECONDARY_ACTIVE];
 
             return (virt_loc.phys_row_offset % 2 == 0) ?
-                   reg_colors.primary_active_bg_color :
-                   reg_colors.secondary_active_bg_color;
+                    color_table[COLOR_PRIMARY_ACTIVE] :
+                    color_table[COLOR_SECONDARY_ACTIVE];
         }
 
         if (double_alternate_virt)
             return vcell->start_primary_color ?
-                   reg_colors.primary_bg_color :
-                   reg_colors.secondary_bg_color;
+                    color_table[COLOR_PRIMARY] :
+                    color_table[COLOR_SECONDARY];
 
         return (virt_loc.phys_row_offset % 2 == 0) ?
-               reg_colors.primary_bg_color :
-               reg_colors.secondary_bg_color;
+                color_table[COLOR_PRIMARY] :
+                color_table[COLOR_SECONDARY];
     }
 
     if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
     {
         if (is_current)
-            return reg_colors.split_active_bg_color;
+            return color_table[COLOR_SPLIT_ACTIVE];
 
-        return reg_colors.split_bg_color;
+        return color_table[COLOR_SPLIT];
     }
 
     PWARN ("Unexpected cursor: %s\n", cursor_name);
 
-    return bg_color;
+    return default_color;
 }
 
-static RegisterColor
-gnc_split_register_get_gtkrc_bg_color (VirtualLocation virt_loc,
-                                       gboolean *hatching,
-                                       gpointer user_data)
+static guint32
+gnc_split_register_get_bg_color (VirtualLocation virt_loc,
+        gboolean *hatching,
+        gpointer user_data)
 {
     SplitRegister *reg = user_data;
-    const char *cursor_name;
-    VirtualCell *vcell;
-    gboolean is_current;
-    gboolean double_alternate_virt;
-
-    if (!reg)
-        return COLOR_UNKNOWN;
-
-    if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
-        return COLOR_HEADER;
-
-    vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
-    if (!vcell || !vcell->cellblock)
-        return COLOR_UNKNOWN;
-
-    if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
-            (virt_loc.phys_col_offset > vcell->cellblock->stop_col))
-        return COLOR_UNKNOWN;
-
-    is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
-                                      virt_loc.vcell_loc);
-
-    cursor_name = vcell->cellblock->cursor_name;
-
-    if (g_strcmp0 (cursor_name, CURSOR_SINGLE_JOURNAL) == 0 ||
-            g_strcmp0 (cursor_name, CURSOR_SINGLE_LEDGER) == 0)
-    {
-        if (is_current)
-            return vcell->start_primary_color ?
-                   COLOR_PRIMARY_ACTIVE :
-                   COLOR_SECONDARY_ACTIVE;
-
-        return vcell->start_primary_color ?
-               COLOR_PRIMARY : COLOR_SECONDARY;
-    }
-
-    if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
-            g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL_NUM_ACTN) == 0 ||
-            g_strcmp0 (cursor_name, CURSOR_DOUBLE_LEDGER) == 0 ||
-            g_strcmp0 (cursor_name, CURSOR_DOUBLE_LEDGER_NUM_ACTN) == 0)
-    {
-        double_alternate_virt = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
-                                                    GNC_PREF_ALT_COLOR_BY_TRANS);
-        if (is_current)
-        {
-            if (double_alternate_virt)
-                return vcell->start_primary_color ?
-                       COLOR_PRIMARY_ACTIVE :
-                       COLOR_SECONDARY_ACTIVE;
-
-            return (virt_loc.phys_row_offset % 2 == 0) ?
-                   COLOR_PRIMARY_ACTIVE :
-                   COLOR_SECONDARY_ACTIVE;
-        }
 
-        if (double_alternate_virt)
-            return vcell->start_primary_color ?
-                   COLOR_PRIMARY :
-                   COLOR_SECONDARY;
+    if (hatching)
+        *hatching = FALSE;
 
-        return (virt_loc.phys_row_offset % 2 == 0) ?
-               COLOR_PRIMARY :
-               COLOR_SECONDARY;
-    }
+    return gnc_split_register_get_bg_color_internal (virt_loc, reg, reg_colors_default, 0xffffff);
+}
 
-    if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
-    {
-        if (is_current)
-            return COLOR_SPLIT_ACTIVE;
 
-        return COLOR_SPLIT;
-    }
+static RegisterColor
+gnc_split_register_get_gtkrc_bg_color (VirtualLocation virt_loc,
+                                       gboolean *hatching,
+                                       gpointer user_data)
+{
+    SplitRegister *reg = user_data;
 
-    PWARN ("Unexpected cursor: %s\n", cursor_name);
+    if (hatching)
+        *hatching = FALSE;
 
-    return COLOR_UNKNOWN;
+    return gnc_split_register_get_bg_color_internal (virt_loc, reg, reg_colors_gtkrc, COLOR_UNKNOWN);
 }
 
 static guint32
@@ -2660,19 +2597,19 @@ gnc_split_register_model_new (void)
 
 
     gnc_table_model_set_fg_color_handler(
-        model, gnc_split_register_get_shares_fg_color, SHRS_CELL);
+        model, gnc_split_register_get_fg_color, SHRS_CELL);
 
     gnc_table_model_set_fg_color_handler(
-        model, gnc_split_register_get_shares_fg_color, TSHRS_CELL);
+        model, gnc_split_register_get_fg_color, TSHRS_CELL);
 
     gnc_table_model_set_fg_color_handler(
-        model, gnc_split_register_get_balance_fg_color, BALN_CELL);
+        model, gnc_split_register_get_fg_color, BALN_CELL);
 
     gnc_table_model_set_fg_color_handler(
-        model, gnc_split_register_get_balance_fg_color, TBALN_CELL);
+        model, gnc_split_register_get_fg_color, TBALN_CELL);
 
     gnc_table_model_set_fg_color_handler(
-        model, gnc_split_register_get_balance_fg_color, RBALN_CELL);
+        model, gnc_split_register_get_fg_color, RBALN_CELL);
 
 
     gnc_table_model_set_default_bg_color_handler(
diff --git a/src/register/ledger-core/split-register.h b/src/register/ledger-core/split-register.h
index b7ba1b4..5e82a04 100644
--- a/src/register/ledger-core/split-register.h
+++ b/src/register/ledger-core/split-register.h
@@ -238,20 +238,6 @@ typedef enum
     NUM_CURSOR_CLASSES
 } CursorClass;
 
-typedef struct split_register_colors
-{
-    guint32 header_bg_color;
-
-    guint32 primary_bg_color;
-    guint32 secondary_bg_color;
-
-    guint32 primary_active_bg_color;
-    guint32 secondary_active_bg_color;
-
-    guint32 split_bg_color;
-    guint32 split_active_bg_color;
-} SplitRegisterColors;
-
 
 /** @brief A split register created with ::gnc_split_register_new */
 typedef struct split_register SplitRegister;
diff --git a/src/register/register-core/table-allgui.c b/src/register/register-core/table-allgui.c
index 3a9041f..29ec9b0 100644
--- a/src/register/register-core/table-allgui.c
+++ b/src/register/register-core/table-allgui.c
@@ -364,12 +364,13 @@ gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
     return fg_color_handler (virt_loc, table->model->handler_user_data);
 }
 
-guint32
-gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
-                        gboolean *hatching)
+static guint32
+gnc_table_get_bg_color_internal (Table *table, VirtualLocation virt_loc,
+                                 gboolean *hatching,
+                                 gboolean want_gtkrc)
 {
     TableGetBGColorHandler bg_color_handler;
-    const char *cell_name;
+    const char *handler_name = "gtkrc";
 
     if (hatching)
         *hatching = FALSE;
@@ -377,10 +378,11 @@ gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
     if (!table || !table->model)
         return 0xffffff; /* white */
 
-    cell_name = gnc_table_get_cell_name (table, virt_loc);
+    if (!want_gtkrc)
+        handler_name = gnc_table_get_cell_name (table, virt_loc);
 
     bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
-                       cell_name);
+            handler_name);
     if (!bg_color_handler)
         return 0xffffff;
 
@@ -389,24 +391,17 @@ gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
 }
 
 guint32
+gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
+                        gboolean *hatching)
+{
+    return gnc_table_get_bg_color_internal (table, virt_loc, hatching, FALSE);
+}
+
+guint32
 gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
                               gboolean *hatching)
 {
-    TableGetBGColorHandler bg_color_handler;
-
-    if (hatching)
-        *hatching = FALSE;
-
-    if (!table || !table->model)
-        return 0xffffff; /* white */
-
-    bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
-                       "gtkrc");
-    if (!bg_color_handler)
-        return 0xffffff;
-
-    return bg_color_handler (virt_loc, hatching,
-                             table->model->handler_user_data);
+    return gnc_table_get_bg_color_internal (table, virt_loc, hatching, TRUE);
 }
 
 void

commit 0a394c5bba98b54011777668f3f8226b5962af6c
Author: Geert Janssens <janssens-geert at telenet.be>
Date:   Sat Aug 30 12:24:32 2014 +0200

    Bug 711440 - Tab labels have different background colour than containing gui element

diff --git a/src/gnome-utils/gnc-main-window.c b/src/gnome-utils/gnc-main-window.c
index 4ab4257..0635cf7 100644
--- a/src/gnome-utils/gnc-main-window.c
+++ b/src/gnome-utils/gnc-main-window.c
@@ -130,7 +130,6 @@ static void gnc_main_window_destroy (GtkObject *object);
 
 static void gnc_main_window_setup_window (GncMainWindow *window);
 static void gnc_window_main_window_init (GncWindowIface *iface);
-static gboolean main_window_find_tab_event (GncMainWindow *window, GncPluginPage *page, GtkWidget **event_p);
 #ifndef MAC_INTEGRATION
 static void gnc_main_window_update_all_menu_items (void);
 #endif
@@ -2009,31 +2008,11 @@ static void
 gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
         gpointer user_data)
 {
-    GncMainWindow        *window = user_data;
-    GncMainWindowPrivate *priv;
     const gchar          *color_string;
-    GdkColor              tab_color;
-    GtkWidget            *event_box;
 
     ENTER("page %p", page);
-
-    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-
-    /* Get the event box to update the tab */
-    main_window_find_tab_event(window, page, &event_box);
-
     color_string = gnc_plugin_page_get_page_color(page);
-    if (color_string == NULL) color_string = "";
-    if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
-    {
-        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
-        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
-    }
-    else
-    {
-        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
-        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
-    }
+    main_window_update_page_color (page, color_string);
     LEAVE(" ");
 }
 
@@ -2142,7 +2121,7 @@ main_window_find_tab_items (GncMainWindow *window,
                             GtkWidget **entry_p)
 {
     GncMainWindowPrivate *priv;
-    GtkWidget *tab_hbox, *widget, *event_box;
+    GtkWidget *tab_hbox, *widget, *tab_widget;
     GList *children, *tmp;
 
     ENTER("window %p, page %p, label_p %p, entry_p %p",
@@ -2156,10 +2135,17 @@ main_window_find_tab_items (GncMainWindow *window,
         return FALSE;
     }
 
-    event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
+    tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
                                            page->notebook_page);
-
-    tab_hbox = gtk_bin_get_child(GTK_BIN(event_box));
+    if (GTK_IS_EVENT_BOX (tab_widget))
+        tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
+    else if (GTK_IS_HBOX (tab_widget))
+        tab_hbox = tab_widget;
+    else
+    {
+        PWARN ("Unknown widget for tab label %p", tab_widget);
+        return FALSE;
+    }
 
     children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
     for (tmp = children; tmp; tmp = g_list_next(tmp))
@@ -2181,16 +2167,15 @@ main_window_find_tab_items (GncMainWindow *window,
 }
 
 static gboolean
-main_window_find_tab_event (GncMainWindow *window,
-                            GncPluginPage *page,
-                            GtkWidget **event_p)
+main_window_find_tab_widget (GncMainWindow *window,
+                             GncPluginPage *page,
+                             GtkWidget **widget_p)
 {
     GncMainWindowPrivate *priv;
-    GtkWidget *event_box;
 
-    ENTER("window %p, page %p, event %p",
-          window, page, event_p);
-    *event_p = NULL;
+    ENTER("window %p, page %p, widget %p",
+          window, page, widget_p);
+    *widget_p = NULL;
 
     if (!page->notebook_page)
     {
@@ -2199,17 +2184,11 @@ main_window_find_tab_event (GncMainWindow *window,
     }
 
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
-    event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
+    *widget_p = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
                                            page->notebook_page);
-    if (GTK_IS_EVENT_BOX(event_box))
-    {
-        *event_p = event_box;
-        LEAVE("event %p", *event_p);
-        return (TRUE);
-    }
 
-    LEAVE("event %p", *event_p);
-    return (FALSE);
+    LEAVE("widget %p", *widget_p);
+    return TRUE;
 }
 
 void
@@ -2218,7 +2197,7 @@ main_window_update_page_name (GncPluginPage *page,
 {
     GncMainWindow *window;
     GncMainWindowPrivate *priv;
-    GtkWidget *label, *entry, *event_box;
+    GtkWidget *label, *entry;
     gchar *name, *old_page_name, *old_page_long_name;
 
     ENTER(" ");
@@ -2264,14 +2243,15 @@ main_window_update_page_name (GncPluginPage *page,
     {
         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);
 
         gnc_plugin_page_set_page_long_name(page, new_page_long_name);
 
-        if (main_window_find_tab_event(window, page, &event_box))
-            gtk_widget_set_tooltip_text(event_box, new_page_long_name);
+        if (main_window_find_tab_widget(window, page, &tab_widget))
+            gtk_widget_set_tooltip_text(tab_widget, new_page_long_name);
 
         g_free(new_page_long_name);
     }
@@ -2300,44 +2280,55 @@ main_window_update_page_color (GncPluginPage *page,
 {
     GncMainWindow *window;
     GncMainWindowPrivate *priv;
-    GtkWidget *event_box;
+    GtkWidget *tab_widget;
     GdkColor tab_color;
-    gchar *color_string;
+    gchar *color_string = NULL;
+    gboolean want_color = FALSE;
 
     ENTER(" ");
-    if ((color_in == NULL) || (*color_in == '\0'))
-    {
-        LEAVE("no string");
-        return;
-    }
-    color_string = g_strstrip(g_strdup(color_in));
+    if (color_in)
+        color_string = g_strstrip(g_strdup(color_in));
 
-    /* Optimization, if the color hasn't changed, don't update. */
-    if (*color_string == '\0' || 0 == g_strcmp0(color_string, gnc_plugin_page_get_page_color(page)))
-    {
-        g_free(color_string);
-        LEAVE("empty string or color unchanged");
-        return;
-    }
+    if (color_string && *color_string != '\0')
+        want_color = TRUE;
 
     /* Update the plugin */
     window = GNC_MAIN_WINDOW(page->window);
-    gnc_plugin_page_set_page_color(page, color_string);
-
-    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
+    if (want_color)
+        gnc_plugin_page_set_page_color(page, color_string);
+    else
+        gnc_plugin_page_set_page_color(page, NULL);
 
     /* Update the notebook tab */
-    main_window_find_tab_event(window, page, &event_box);
+    main_window_find_tab_widget (window, page, &tab_widget);
+    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 
-    if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
+    if (want_color && gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
     {
-        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
-        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
+        if (!GTK_IS_EVENT_BOX (tab_widget))
+        {
+            GtkWidget *event_box = gtk_event_box_new ();
+            g_object_ref (tab_widget);
+            gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
+                                        page->notebook_page, event_box);
+            gtk_container_add (GTK_CONTAINER(event_box), tab_widget);
+            g_object_unref (tab_widget);
+            tab_widget = event_box;
+        }
+        gtk_widget_modify_bg(tab_widget, GTK_STATE_NORMAL, &tab_color);
+        gtk_widget_modify_bg(tab_widget, GTK_STATE_ACTIVE, &tab_color);
     }
     else
     {
-        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
-        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
+        if (GTK_IS_EVENT_BOX (tab_widget))
+        {
+            GtkWidget *tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
+            g_object_ref (tab_hbox);
+            gtk_container_remove (GTK_CONTAINER(tab_widget), tab_hbox);
+            gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
+                                        page->notebook_page, tab_hbox);
+            g_object_unref (tab_hbox);
+        }
     }
     g_free(color_string);
     LEAVE("done");
@@ -2882,7 +2873,7 @@ gnc_main_window_open_page (GncMainWindow *window,
 {
     GncMainWindowPrivate *priv;
     GtkWidget *tab_hbox;
-    GtkWidget *label, *entry, *event_box;
+    GtkWidget *label, *entry;
     const gchar *icon, *text, *color_string;
     GtkWidget *image;
     GList *tmp;
@@ -2955,27 +2946,10 @@ gnc_main_window_open_page (GncMainWindow *window,
     else
         gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
 
-    event_box = gtk_event_box_new();
-    gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), TRUE);
-    gtk_widget_show(event_box);
-    gtk_container_add(GTK_CONTAINER(event_box), tab_hbox);
-    color_string = gnc_plugin_page_get_page_color(page);
-    if (color_string == NULL) color_string = "";
-    if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
-    {
-        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
-        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
-    }
-    else
-    {
-        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
-        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
-    }
-
     text = gnc_plugin_page_get_page_long_name(page);
     if (text)
     {
-        gtk_widget_set_tooltip_text(event_box, text);
+        gtk_widget_set_tooltip_text(tab_hbox, text);
     }
 
     entry = gtk_entry_new();
@@ -3029,7 +3003,10 @@ gnc_main_window_open_page (GncMainWindow *window,
     /*
      * Now install it all in the window.
      */
-    gnc_main_window_connect(window, page, event_box, label);
+    gnc_main_window_connect(window, page, tab_hbox, label);
+
+    color_string = gnc_plugin_page_get_page_color(page);
+    main_window_update_page_color (page, color_string);
     LEAVE("");
 }
 
diff --git a/src/gnome-utils/gnc-plugin-page.c b/src/gnome-utils/gnc-plugin-page.c
index 86a803b..55b4dc9 100644
--- a/src/gnome-utils/gnc-plugin-page.c
+++ b/src/gnome-utils/gnc-plugin-page.c
@@ -868,7 +868,8 @@ gnc_plugin_page_set_page_color (GncPluginPage *page, const gchar *color)
     priv = GNC_PLUGIN_PAGE_GET_PRIVATE(page);
     if (priv->page_color)
         g_free(priv->page_color);
-    priv->page_color = g_strdup(color);
+    if (color)
+        priv->page_color = g_strdup(color);
 }
 
 



Summary of changes:
 src/business/business-gnome/dialog-payment.c       |  36 +-
 src/business/business-ledger/gncEntryLedger.h      |  11 -
 src/business/business-ledger/gncEntryLedgerModel.c |  94 +++-
 src/engine/Account.c                               |  24 +
 src/engine/Account.h                               |  10 +
 src/engine/Makefile.am                             |   2 +
 src/engine/Scrub.c                                 |   5 +-
 src/engine/Scrub2.c                                |   8 +-
 src/engine/Scrub2.h                                |  26 +-
 src/engine/Scrub3.c                                |   4 +-
 src/engine/Scrub3.h                                |   4 +-
 src/engine/ScrubBusiness.c                         | 333 +++++++++++++
 src/engine/{Scrub3.h => ScrubBusiness.h}           |  62 +--
 src/engine/Split.c                                 |   1 -
 src/engine/Transaction.h                           |   2 +-
 src/engine/gnc-lot.c                               |   3 +
 src/engine/gncOwner.c                              | 552 ++++++++++++++++-----
 src/engine/gncOwner.h                              |  28 ++
 src/gnome-utils/gnc-main-window.c                  | 155 +++---
 src/gnome-utils/gnc-plugin-page.c                  |   3 +-
 src/gnome-utils/gnc-tree-view.h                    |   2 +-
 src/gnome/assistant-acct-period.c                  |   1 -
 src/gnome/dialog-lot-viewer.c                      |  11 +-
 src/gnome/gnc-plugin-page-account-tree.c           |  14 +-
 src/gnome/gnc-plugin-page-register.c               |  13 +
 src/gnome/window-autoclear.c                       |   1 -
 src/quotes/Makefile.am                             |  10 +
 src/register/ledger-core/split-register-model.c    | 302 +++++------
 src/register/ledger-core/split-register-model.h    |  12 -
 src/register/ledger-core/split-register.h          |  16 +-
 src/register/register-core/table-allgui.c          |  59 ++-
 src/register/register-core/table-allgui.h          |  88 ++++
 src/register/register-gnome/gnucash-grid.c         |  24 +-
 src/register/register-gnome/gnucash-header.c       |  10 +-
 src/register/register-gnome/gnucash-item-edit.c    |   6 +-
 src/register/register-gnome/gnucash-sheet.c        |  61 ++-
 36 files changed, 1400 insertions(+), 593 deletions(-)
 create mode 100644 src/engine/ScrubBusiness.c
 copy src/engine/{Scrub3.h => ScrubBusiness.h} (52%)



More information about the gnucash-changes mailing list