gnucash master: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Sat Feb 15 10:14:05 EST 2020


Updated	 via  https://github.com/Gnucash/gnucash/commit/efed7094 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/555a467a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3ac60ed2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b23d2445 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7577afe0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f66b7ed2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/907bff34 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f5c0ddd7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/58ddb47f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3be42beb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/18acb423 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/09d3e953 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6e64a378 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/19db1dae (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7cbe367c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/70d8acc7 (commit)
	from  https://github.com/Gnucash/gnucash/commit/50b882aa (commit)



commit efed70941437a7ec84e931a00590cb17f5fde562
Merge: 907bff34c 555a467ab
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Feb 15 23:13:40 2020 +0800

    Merge branch 'maint'

diff --cc gnucash/report/html-document.scm
index b1322387e,ef050a197..a5a4c994b
--- a/gnucash/report/html-document.scm
+++ b/gnucash/report/html-document.scm
@@@ -21,7 -21,8 +21,8 @@@
  ;; Boston, MA  02110-1301,  USA       gnu at gnu.org
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
 -(gnc:module-load "gnucash/html" 0)
 +(use-modules (gnucash html))
+ (use-modules (ice-9 match))
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;  <html-document> class
@@@ -221,69 -221,74 +222,49 @@@
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  (define (gnc:html-document-markup-start doc markup end-tag? . rest)
-   (let ((childinfo (gnc:html-document-fetch-markup-style doc markup))
-         (extra-attrib (and (pair? rest) rest)))
-     ;; now generate the start tag
-     (let ((tag   (gnc:html-markup-style-info-tag childinfo))
-           (attr  (gnc:html-markup-style-info-attributes childinfo)))
- 
-       ;; "" tags mean "show no tag"; #f tags means use default.
-       (cond ((not tag)
-              (set! tag markup))
-             ((and (string? tag) (string=? tag ""))
-              (set! tag #f)))
-       (let* ((retval '())
-              (push (lambda (l) (set! retval (cons l retval))))
-              (add-internal-tag (lambda (tag) (push "<") (push tag) (push ">")))
-              (add-attribute
-               (lambda (key value prior)
-                       (push " ") (push key)
-                       (if value (begin (push "=\"")
-                                        (push value)
-                                        (push "\"")))
-                       #t))
-              (addextraatt
-               (lambda (attr)
-                 (cond ((string? attr) (push " ") (push attr))
-                       (attr (gnc:warn "non-string attribute" attr)))))
-              (build-first-tag
-               (lambda (tag)
-                 (push "<") (push tag)
-                 (if attr (hash-fold add-attribute #f attr))
-                 (if extra-attrib (for-each addextraatt extra-attrib))
-                 (if (not end-tag?)
-                     (push " /")) ;;add closing "/" for no-end elements...
-                 (push ">"))))
-         (if tag
-             (if (list? tag)
-                 (begin
-                   (build-first-tag (car tag))
-                   (for-each add-internal-tag (cdr tag)))
-                 (build-first-tag tag)))
-         retval))))
+   (let* ((childinfo (gnc:html-document-fetch-markup-style doc markup))
+          (extra-attrib (and (pair? rest) rest))
+          (retval '())
+          (tag   (or (gnc:html-markup-style-info-tag childinfo) markup))
 -         (attr  (gnc:html-markup-style-info-attributes childinfo))
 -         (face  (gnc:html-markup-style-info-font-face childinfo))
 -         (size  (gnc:html-markup-style-info-font-size childinfo))
 -         (color (gnc:html-markup-style-info-font-color childinfo)))
++         (attr  (gnc:html-markup-style-info-attributes childinfo)))
+ 
+     (define (push l) (set! retval (cons l retval)))
+     (define (add-internal-tag tag) (push "<") (push tag) (push ">"))
+     (define (add-attribute key value)
+       (push " ") (push key)
+       (when value (push "=\"") (push value) (push "\"")))
+     (define (addextraatt attr)
+       (cond ((string? attr) (push " ") (push attr))
+             (attr (gnc:warn "non-string attribute" attr))))
+     (define (build-first-tag tag)
+       (push "<") (push tag)
+       (if attr (hash-for-each add-attribute attr))
+       (if extra-attrib (for-each addextraatt extra-attrib))
+       (unless end-tag? (push " /")) ;;add closing "/" for no-end elements...
+       (push ">"))
+ 
+     (match tag
+       ("" #f)
+       ((head . tail) (build-first-tag head) (for-each add-internal-tag tail))
+       (_ (build-first-tag tag)))
 -
 -    ;; XXX Font styling should be done through CSS, NOT html code
 -    ;; XXX Also, why is this even here?  'Font' is an html tag just like anything else,
 -    ;;       so why does it have it's own custom pseudo code here?  It should be built
 -    ;;       as a call to this function just like any other tag, passing face/size/color as attributes.
 -    (if (or face size color)
 -        (begin
 -          (issue-deprecation-warning
 -           "this section is unreachable in code")
 -          (push "<font ")
 -          (if face
 -              (begin
 -                (push "face=\"") (push face) (push "\" ")))
 -          (if size
 -              (begin
 -                (push "size=\"") (push size) (push "\" ")))
 -          (if color
 -              (begin
 -                (push "color=\"") (push color) (push "\" ")))
 -          (push ">")))
+     retval))
  
  (define (gnc:html-document-markup-end doc markup)
-   (let ((childinfo (gnc:html-document-fetch-markup-style doc markup)))
+   (let* ((childinfo (gnc:html-document-fetch-markup-style doc markup))
+          (tag (or (gnc:html-markup-style-info-tag childinfo) markup))
+          (retval '()))
+     (define (push l) (set! retval (cons l retval)))
+     (define (addtag t)
+       (push "</")
+       (push t)
+       (push ">\n"))
 -    (when (gnc:html-markup-style-info-closing-font-tag childinfo)
 -      (push "</font>\n"))
      ;; now generate the end tag
-     (let ((tag (gnc:html-markup-style-info-tag childinfo)))
-       ;; "" tags mean "show no tag"; #f tags means use default.
-       (cond ((not tag)
-              (set! tag markup))
-             ((and (string? tag) (string=? tag ""))
-              (set! tag #f)))
-       (let* ((retval '())
-              (push (lambda (l) (set! retval (cons l retval)))))
-         (if tag
-             (let ((addtag (lambda (t)
-                             (push "</")
-                             (push tag)
-                             (push ">\n"))))
-               (cond
-                ((string? tag)
-                 (addtag tag))
-                ((list? tag)
-                 (for-each addtag (reverse tag))))))
-         retval))))
+     ;; "" tags mean "show no tag"; #f tags means use default.)
+     (match tag
+       ("" #f)
+       ((? string?) (addtag tag))
+       ((? list?) (for-each addtag (reverse tag))))
+     retval))
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;  html-document-render-data
diff --cc gnucash/report/reports/standard/view-column.scm
index d73e13178,8d224f1c5..516e6cba5
--- a/gnucash/report/reports/standard/view-column.scm
+++ b/gnucash/report/reports/standard/view-column.scm
@@@ -26,13 -26,19 +26,14 @@@
  ;; edited in a special window.  Every view gets a stylesheet so we
  ;; don't have to worry about that here.
  
 -(define-module (gnucash report view-column))
 +(define-module (gnucash reports standard view-column))
 +(use-modules (gnucash engine))
+ (use-modules (ice-9 match))
  (use-modules (gnucash utilities)) 
 +(use-modules (gnucash core-utils))
  (use-modules (gnucash app-utils))
 -(use-modules (gnucash gnc-module))
 -(use-modules (gnucash gettext))
 -(eval-when
 -      (compile load eval expand)
 -      (load-extension "libgncmod-report-system" "scm_init_sw_report_system_module"))
 -(use-modules (sw_report_system))
 -
 -(gnc:module-load "gnucash/report/report-system" 0)
 -(gnc:module-load "gnucash/html" 0) ;for gnc-build-url
 +(use-modules (gnucash report))
 +(use-modules (gnucash html))
  
  (define (make-options)
    (let* ((options (gnc:new-options))

commit 555a467aba7733e9bf7d6e53cc5b1054c328427a
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Feb 15 18:34:01 2020 +0800

    [new-owner-report] revert highlight trigger to onclick
    
    and use Windows libwebkit1-compatible javascript

diff --git a/gnucash/report/business-reports/new-owner-report.scm b/gnucash/report/business-reports/new-owner-report.scm
index e42b8df78..e39b885e7 100644
--- a/gnucash/report/business-reports/new-owner-report.scm
+++ b/gnucash/report/business-reports/new-owner-report.scm
@@ -63,18 +63,22 @@
 (define javascript "
 <script>
   function getID(cell) { return cell.getAttribute('link-id'); }
-  function mousedown(e) {
-      var id = getID(e.target);
-      var ishighlighted = e.target.classList.contains('highlight');
-      e.preventDefault ();
-      TDs.forEach(TD => TD.classList.remove('highlight'));
+
+  function clicky() {
+      var id = getID(this);
+      var ishighlighted = this.classList.contains('highlight');
+      TDs.forEach (function (item, idx) {
+          item.classList.remove('highlight')});
       if (ishighlighted) return;
-      TDs
-          .filter (TD => getID(TD) == id)
-          .forEach (TD => TD.classList.add('highlight'));}
+      TDs.forEach (function (item, idx) {
+          if (getID(item) == id)
+              item.classList.add('highlight')})}
+
   var TDs = document.getElementsByTagName('td');
-  TDs = [...TDs].filter(getID);
-  TDs.forEach(TD => TD.onmousedown = mousedown);
+  TDs = Array.prototype.slice.call (TDs);
+  TDs = TDs.filter (getID);
+  TDs.forEach(function (item, idx) {
+      item.addEventListener('click', clicky)});
 </script>
 ")
 

commit 3ac60ed2e427816fcbc70b7f757b617510caef45
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Feb 9 18:20:41 2020 +0800

    compact, use (ice-9 match)

diff --git a/gnucash/report/report-system/html-document.scm b/gnucash/report/report-system/html-document.scm
index 3744eb79a..ef050a197 100644
--- a/gnucash/report/report-system/html-document.scm
+++ b/gnucash/report/report-system/html-document.scm
@@ -22,6 +22,7 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (gnc:module-load "gnucash/html" 0)
+(use-modules (ice-9 match))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;  <html-document> class
@@ -220,95 +221,74 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (define (gnc:html-document-markup-start doc markup end-tag? . rest)
-  (let ((childinfo (gnc:html-document-fetch-markup-style doc markup))
-        (extra-attrib (and (pair? rest) rest)))
-    ;; now generate the start tag
-    (let ((tag   (gnc:html-markup-style-info-tag childinfo))
-          (attr  (gnc:html-markup-style-info-attributes childinfo))
-          (face  (gnc:html-markup-style-info-font-face childinfo))
-          (size  (gnc:html-markup-style-info-font-size childinfo))
-          (color (gnc:html-markup-style-info-font-color childinfo)))
-
-      ;; "" tags mean "show no tag"; #f tags means use default.
-      (cond ((not tag)
-             (set! tag markup))
-            ((and (string? tag) (string=? tag ""))
-             (set! tag #f)))
-      (let* ((retval '())
-             (push (lambda (l) (set! retval (cons l retval))))
-             (add-internal-tag (lambda (tag) (push "<") (push tag) (push ">")))
-             (add-attribute
-              (lambda (key value prior)
-                      (push " ") (push key)
-                      (if value (begin (push "=\"")
-                                       (push value)
-                                       (push "\"")))
-                      #t))
-             (addextraatt
-              (lambda (attr)
-                (cond ((string? attr) (push " ") (push attr))
-                      (attr (gnc:warn "non-string attribute" attr)))))
-             (build-first-tag
-              (lambda (tag)
-                (push "<") (push tag)
-                (if attr (hash-fold add-attribute #f attr))
-                (if extra-attrib (for-each addextraatt extra-attrib))
-                (if (not end-tag?)
-                    (push " /")) ;;add closing "/" for no-end elements...
-                (push ">"))))
-        (if tag
-            (if (list? tag)
-                (begin
-                  (build-first-tag (car tag))
-                  (for-each add-internal-tag (cdr tag)))
-                (build-first-tag tag)))
-        ;; XXX Font styling should be done through CSS, NOT html code
-        ;; XXX Also, why is this even here?  'Font' is an html tag just like anything else,
-        ;;       so why does it have it's own custom pseudo code here?  It should be built
-        ;;       as a call to this function just like any other tag, passing face/size/color as attributes.
-        (if (or face size color)
-            (begin
-              (issue-deprecation-warning
-               "this section is unreachable in code")
-              (push "<font ")
-              (if face
-                  (begin
-                    (push "face=\"") (push face) (push "\" ")))
-              (if size
-                  (begin
-                    (push "size=\"") (push size) (push "\" ")))
-              (if color
-                  (begin
-                    (push "color=\"") (push color) (push "\" ")))
-              (push ">")))
-        retval))))
+  (let* ((childinfo (gnc:html-document-fetch-markup-style doc markup))
+         (extra-attrib (and (pair? rest) rest))
+         (retval '())
+         (tag   (or (gnc:html-markup-style-info-tag childinfo) markup))
+         (attr  (gnc:html-markup-style-info-attributes childinfo))
+         (face  (gnc:html-markup-style-info-font-face childinfo))
+         (size  (gnc:html-markup-style-info-font-size childinfo))
+         (color (gnc:html-markup-style-info-font-color childinfo)))
+
+    (define (push l) (set! retval (cons l retval)))
+    (define (add-internal-tag tag) (push "<") (push tag) (push ">"))
+    (define (add-attribute key value)
+      (push " ") (push key)
+      (when value (push "=\"") (push value) (push "\"")))
+    (define (addextraatt attr)
+      (cond ((string? attr) (push " ") (push attr))
+            (attr (gnc:warn "non-string attribute" attr))))
+    (define (build-first-tag tag)
+      (push "<") (push tag)
+      (if attr (hash-for-each add-attribute attr))
+      (if extra-attrib (for-each addextraatt extra-attrib))
+      (unless end-tag? (push " /")) ;;add closing "/" for no-end elements...
+      (push ">"))
+
+    (match tag
+      ("" #f)
+      ((head . tail) (build-first-tag head) (for-each add-internal-tag tail))
+      (_ (build-first-tag tag)))
+
+    ;; XXX Font styling should be done through CSS, NOT html code
+    ;; XXX Also, why is this even here?  'Font' is an html tag just like anything else,
+    ;;       so why does it have it's own custom pseudo code here?  It should be built
+    ;;       as a call to this function just like any other tag, passing face/size/color as attributes.
+    (if (or face size color)
+        (begin
+          (issue-deprecation-warning
+           "this section is unreachable in code")
+          (push "<font ")
+          (if face
+              (begin
+                (push "face=\"") (push face) (push "\" ")))
+          (if size
+              (begin
+                (push "size=\"") (push size) (push "\" ")))
+          (if color
+              (begin
+                (push "color=\"") (push color) (push "\" ")))
+          (push ">")))
+    retval))
 
 (define (gnc:html-document-markup-end doc markup)
-  (let ((childinfo (gnc:html-document-fetch-markup-style doc markup)))
+  (let* ((childinfo (gnc:html-document-fetch-markup-style doc markup))
+         (tag (or (gnc:html-markup-style-info-tag childinfo) markup))
+         (retval '()))
+    (define (push l) (set! retval (cons l retval)))
+    (define (addtag t)
+      (push "</")
+      (push t)
+      (push ">\n"))
+    (when (gnc:html-markup-style-info-closing-font-tag childinfo)
+      (push "</font>\n"))
     ;; now generate the end tag
-    (let ((tag (gnc:html-markup-style-info-tag childinfo))
-          (closing-font-tag
-           (gnc:html-markup-style-info-closing-font-tag childinfo)))
-      ;; "" tags mean "show no tag"; #f tags means use default.
-      (cond ((not tag)
-             (set! tag markup))
-            ((and (string? tag) (string=? tag ""))
-             (set! tag #f)))
-      (let* ((retval '())
-             (push (lambda (l) (set! retval (cons l retval)))))
-        (if closing-font-tag
-            (push "</font>\n"))
-        (if tag
-            (let ((addtag (lambda (t)
-                            (push "</")
-                            (push tag)
-                            (push ">\n"))))
-              (cond
-               ((string? tag)
-                (addtag tag))
-               ((list? tag)
-                (for-each addtag (reverse tag))))))
-        retval))))
+    ;; "" tags mean "show no tag"; #f tags means use default.)
+    (match tag
+      ("" #f)
+      ((? string?) (addtag tag))
+      ((? list?) (for-each addtag (reverse tag))))
+    retval))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;  html-document-render-data
diff --git a/gnucash/report/report-system/trep-engine.scm b/gnucash/report/report-system/trep-engine.scm
index f39a47a7b..0a10504de 100644
--- a/gnucash/report/report-system/trep-engine.scm
+++ b/gnucash/report/report-system/trep-engine.scm
@@ -1939,18 +1939,14 @@ be excluded from periodic reporting.")
   (define BOOK-SPLIT-ACTION
     (qof-book-use-split-action-for-num-field (gnc-get-current-book)))
   (define (is-filter-member split account-list)
-    (let* ((txn (xaccSplitGetParent split))
-           (splitcount (xaccTransCountSplits txn))
-           (is-in-account-list? (lambda (acc) (member acc account-list))))
-      (cond
-       ((= splitcount 2)
-        (is-in-account-list?
-         (xaccSplitGetAccount (xaccSplitGetOtherSplit split))))
-       ((> splitcount 2)
-        (or-map is-in-account-list?
-                (map xaccSplitGetAccount
-                     (delete split (xaccTransGetSplitList txn)))))
-       (else #f))))
+    (define (same-split? s) (equal? s split))
+    (define (from-account? s) (member (xaccSplitGetAccount s) account-list))
+    (let lp ((splits (xaccTransGetSplitList (xaccSplitGetParent split))))
+      (match splits
+        (() #f)
+        (((? same-split?) . rest) (lp rest))
+        (((? from-account?) . _) #t)
+        ((_ . rest) (lp rest)))))
 
   (gnc:report-starting (opt-val gnc:pagename-general gnc:optname-reportname))
 
diff --git a/gnucash/report/utility-reports/view-column.scm b/gnucash/report/utility-reports/view-column.scm
index df68cf689..8d224f1c5 100644
--- a/gnucash/report/utility-reports/view-column.scm
+++ b/gnucash/report/utility-reports/view-column.scm
@@ -27,6 +27,7 @@
 ;; don't have to worry about that here.
 
 (define-module (gnucash report view-column))
+(use-modules (ice-9 match))
 (use-modules (gnucash utilities)) 
 (use-modules (gnucash app-utils))
 (use-modules (gnucash gnc-module))
@@ -86,20 +87,14 @@
 
     ;; make sure each subreport has an option change callback that 
     ;; pings the parent
-    (let loop ((new-reports '())
-               (reports reports))
-      (if (null? reports)
-          (gnc:option-set-value report-opt (reverse new-reports))
-          (let* ((report-info (car reports))
-                 (child (car report-info))
-                 (rowspan (cadr report-info))
-                 (colspan (caddr report-info))
-                 (callback (or (cadddr report-info)
-                               (make-child-options-callback
-                                report (gnc-report-find child)))))
-            (loop (cons (list child rowspan colspan callback)
-                        new-reports)
-                  (cdr reports)))))
+    (let loop ((reports reports) (new-reports '()))
+      (match reports
+        (() (gnc:option-set-value report-opt (reverse new-reports)))
+        (((child rowspan colspan callback) . rest)
+         (let ((callback (or callback
+                             (make-child-options-callback
+                              report (gnc-report-find child)))))
+           (loop rest (cons (list child rowspan colspan callback) new-reports))))))
     
     ;; we really would rather do something smart here with the
     ;; report's cached text if possible.  For the moment, we'll have
@@ -217,17 +212,11 @@
 (define (cleanup-options report)
   (let* ((options (gnc:report-options report))
 	 (report-opt (gnc:lookup-option options "__general" "report-list")))
-    (let loop ((new-reports '())
-               (reports (gnc:option-value report-opt)))
-      (if (null? reports)
-          (gnc:option-set-value report-opt (reverse new-reports))
-          (let* ((report-info (car reports))
-                 (child (car report-info))
-                 (rowspan (cadr report-info))
-                 (colspan (caddr report-info)))
-            (loop (cons (list child rowspan colspan #f)
-                        new-reports)
-                  (cdr reports)))))))
+    (let loop ((reports (gnc:option-value report-opt)) (new-reports '()))
+      (match reports
+        (() (gnc:option-set-value report-opt (reverse new-reports)))
+        (((child rowspan colspan _) . rest)
+         (loop rest (cons (list child rowspan colspan #f) new-reports)))))))
 
 ;; define the view now.
 (gnc:define-report 

commit b23d2445fc6b437b41f11740cdae5dd012f7b9dd
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Feb 13 16:34:25 2020 +0000

    New budgets save state information with no changes
    
    If you create a new budget and do not change any thing when closing the
    budget or quitting with new budget open the state information for that
    budget is saved but the budget is not. To fix this make a change to the
    new budget description to force the save of the new budget.

diff --git a/gnucash/gnome/gnc-plugin-budget.c b/gnucash/gnome/gnc-plugin-budget.c
index 2056afe2a..ff50121e2 100644
--- a/gnucash/gnome/gnc-plugin-budget.c
+++ b/gnucash/gnome/gnc-plugin-budget.c
@@ -149,11 +149,19 @@ gnc_plugin_budget_cmd_new_budget (GtkAction *action,
 {
     GncBudget *budget;
     GncPluginPage *page;
+    gchar *description, *date;
 
     g_return_if_fail (user_data != NULL);
 
     budget = gnc_budget_new (gnc_get_current_book());
     page = gnc_plugin_page_budget_new (budget);
+
+    date = qof_print_date (gnc_time (NULL));
+    description = g_strdup_printf ("%s: %s",  _("Created"), date);
+    gnc_budget_set_description (budget, description);
+    g_free (description);
+    g_free (date);
+
     gnc_main_window_open_page (user_data->window, page);
 }
 

commit 7577afe0a9e6353dce2de2e99e7973dbd8ad2319
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu Feb 13 15:00:54 2020 +0000

    Bug 796911 - Minimum window width to large.
    
    This is down to the amount of information that is displayed on the
    register status bar which can also be influenced by the type of
    register being displayed. To fix this the text labels used have been
    enabled to ellipsize at the end and also the displayed information has
    been added to a tooltip. So for example the minimum app size was
    957x736 and after the changes it can be 610x475.

diff --git a/gnucash/gnome/gnc-split-reg.c b/gnucash/gnome/gnc-split-reg.c
index 599a16705..12dcfe5e5 100644
--- a/gnucash/gnome/gnc-split-reg.c
+++ b/gnucash/gnome/gnc-split-reg.c
@@ -511,10 +511,17 @@ gsr_update_summary_label( GtkWidget *label,
 {
     gnc_numeric amount;
     char string[256];
+    const gchar *label_str = NULL;
+    GtkWidget *text_label, *hbox;
+    gchar *tooltip;
 
     if ( label == NULL )
         return;
 
+    hbox = g_object_get_data (G_OBJECT(label), "text_box");
+    text_label = g_object_get_data (G_OBJECT(label), "text_label");
+    label_str = gtk_label_get_text (GTK_LABEL(text_label));
+
     amount = (*getter)( leader );
 
     if ( reverse )
@@ -534,6 +541,13 @@ gsr_update_summary_label( GtkWidget *label,
 
     gnc_set_label_color( label, amount );
     gtk_label_set_text( GTK_LABEL(label), string );
+
+    if (label_str)
+    {
+        tooltip = g_strdup_printf ("%s %s", label_str, string);
+        gtk_widget_set_tooltip_text (GTK_WIDGET(hbox), tooltip);
+        g_free (tooltip);
+    }
 }
 
 static
@@ -2418,7 +2432,7 @@ GtkWidget*
 add_summary_label (GtkWidget *summarybar, gboolean pack_start, const char *label_str, GtkWidget *extra)
 {
     GtkWidget *hbox;
-    GtkWidget *label;
+    GtkWidget *text_label, *secondary_label;
 
     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
     gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
@@ -2427,18 +2441,21 @@ add_summary_label (GtkWidget *summarybar, gboolean pack_start, const char *label
     else
         gtk_box_pack_end( GTK_BOX(summarybar), hbox, FALSE, FALSE, 5 );
 
-    label = gtk_label_new( label_str );
-    gnc_label_set_alignment(label, 1.0, 0.5 );
-    gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, FALSE, 0 );
+    text_label = gtk_label_new (label_str);
+    gnc_label_set_alignment (text_label, 1.0, 0.5 );
+    gtk_label_set_ellipsize (GTK_LABEL(text_label), PANGO_ELLIPSIZE_END);
+    gtk_box_pack_start (GTK_BOX(hbox), text_label, FALSE, FALSE, 0);
 
-    label = gtk_label_new( "" );
-    gnc_label_set_alignment(label, 1.0, 0.5 );
-    gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, FALSE, 0 );
+    secondary_label = gtk_label_new ( "" );
+    g_object_set_data (G_OBJECT(secondary_label), "text_label", text_label);
+    g_object_set_data (G_OBJECT(secondary_label), "text_box", hbox);
+    gnc_label_set_alignment (secondary_label, 1.0, 0.5 );
+    gtk_box_pack_start (GTK_BOX(hbox), secondary_label, FALSE, FALSE, 0);
 
     if (extra != NULL)
         gtk_box_pack_start( GTK_BOX(hbox), extra, FALSE, FALSE, 0 );
 
-    return label;
+    return secondary_label;
 }
 
 static void

commit f66b7ed275eba56bd8fee1fee9f9e3457351cacb
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Wed Feb 5 14:41:01 2020 +0000

    Follow up to previous commit 94cb965
    
    This commit moves the setting up of the page changed signal callback to
    when the plugin page is inserted and also records the id used. This is
    used to disconnect this callback when the page is moved to a different
    window and also when the page is destroyed.

diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.c
index f7e62c3c4..f6ebdc762 100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.c
@@ -2795,6 +2795,9 @@ gnc_main_window_disconnect (GncMainWindow *window,
     g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
                                          G_CALLBACK(gnc_main_window_button_press_cb), page);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
+
     /* Disconnect the page and summarybar from the window */
     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
     if (priv->current_page == page)
@@ -3712,7 +3715,7 @@ gnc_quartz_shutdown (GtkosxApplication *theApp, gpointer data)
     /* Do Nothing. It's too late. */
 }
 /* Should quit responds to NSApplicationBlockTermination; returning
- * TRUE means "don't terminate", FALSE means "do terminate". 
+ * TRUE means "don't terminate", FALSE means "do terminate".
  */
 static gboolean
 gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window)
@@ -4325,6 +4328,9 @@ gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window)
     tab_widget = gtk_notebook_get_tab_label (notebook, page->notebook_page);
     menu_widget = gtk_notebook_get_menu_label (notebook, page->notebook_page);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
+
     /* Ref the page components, then remove it from its old window */
     g_object_ref(page);
     g_object_ref(tab_widget);
diff --git a/gnucash/gnome-utils/gnc-plugin-page.c b/gnucash/gnome-utils/gnc-plugin-page.c
index d757f3a53..0298d59a0 100644
--- a/gnucash/gnome-utils/gnc-plugin-page.c
+++ b/gnucash/gnome-utils/gnc-plugin-page.c
@@ -58,6 +58,9 @@ static void gnc_plugin_page_get_property (GObject         *object,
         GValue          *value,
         GParamSpec      *pspec);
 
+static void gnc_plugin_page_default_focus (GncPluginPage *plugin_page,
+                                           gboolean on_current_page);
+
 enum
 {
     INSERTED,
@@ -102,6 +105,9 @@ typedef struct _GncPluginPagePrivate
     gchar *page_color;
     gchar *uri;
     gchar *statusbar_text;
+
+    gulong page_changed_id;
+
 } GncPluginPagePrivate;
 
 GNC_DEFINE_TYPE_WITH_CODE(GncPluginPage, gnc_plugin_page, G_TYPE_OBJECT,
@@ -371,6 +377,7 @@ gnc_plugin_page_class_init (GncPluginPageClass *klass)
 
     klass->tab_icon    = NULL;
     klass->plugin_name = NULL;
+    klass->focus_page = gnc_plugin_page_default_focus;
 
     g_object_class_install_property
     (gobject_class,
@@ -506,18 +513,19 @@ static void
 gnc_plugin_page_init (GncPluginPage *page, void *data)
 {
     GncPluginPagePrivate *priv;
-    
+
     GncPluginPageClass *klass = (GncPluginPageClass*)data;
 
     priv = GNC_PLUGIN_PAGE_GET_PRIVATE(page);
     priv->page_name   = NULL;
     priv->page_color  = NULL;
     priv->uri         = NULL;
+    priv->page_changed_id = 0;
 
     page->window      = NULL;
     page->summarybar  = NULL;
 
-    gnc_gobject_tracking_remember(G_OBJECT(page), 
+    gnc_gobject_tracking_remember(G_OBJECT(page),
 		                  G_OBJECT_CLASS(klass));
 }
 
@@ -848,6 +856,96 @@ gnc_plugin_page_set_page_color (GncPluginPage *page, const gchar *color)
 }
 
 
+static void
+gnc_plugin_page_default_focus (GncPluginPage *plugin_page,
+                               gboolean on_current_page)
+{
+    GncPluginPagePrivate *priv;
+
+    if (!on_current_page)
+        return;
+
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE(plugin_page));
+
+    priv = GNC_PLUGIN_PAGE_GET_PRIVATE(plugin_page);
+
+    if (G_LIKELY(GNC_PLUGIN_PAGE_GET_CLASS(plugin_page)->focus_page_function))
+    {
+        // The page changed signal is emitted multiple times so we need
+        // to use an idle_add to change the focus
+        g_idle_remove_by_data (GNC_PLUGIN_PAGE(plugin_page));
+        g_idle_add ((GSourceFunc)(GNC_PLUGIN_PAGE_GET_CLASS(plugin_page)->focus_page_function),
+                     GNC_PLUGIN_PAGE(plugin_page));
+    }
+}
+
+
+/* this is the callback for the plugin "page_changed" signal */
+static void
+gnc_plugin_page_main_window_changed (GtkWindow *window,
+                                     GObject *object,
+                                     gpointer user_data)
+{
+    GncPluginPage *current_plugin_page = GNC_PLUGIN_PAGE(object);
+    GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(user_data);
+    GncPluginPagePrivate *priv;
+    gboolean on_current_page = FALSE;
+
+    // Continue if current_plugin_page is valid
+    if (!current_plugin_page || !GNC_IS_PLUGIN_PAGE(current_plugin_page))
+        return;
+
+    // Continue only if the plugin_page is valid
+    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
+        return;
+
+    priv = GNC_PLUGIN_PAGE_GET_PRIVATE(plugin_page);
+
+    if (current_plugin_page == plugin_page)
+        on_current_page = TRUE;
+
+    (GNC_PLUGIN_PAGE_GET_CLASS(plugin_page)->focus_page)(plugin_page, on_current_page);
+}
+
+/* this is the callback for the plugin "inserted" signal which will setup
+ * the callback for the "page_changed" signal and save a pointer to the
+ * page focus function. */
+void
+gnc_plugin_page_inserted_cb (GncPluginPage *page, gpointer user_data)
+{
+    GncPluginPagePrivate *priv;
+
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE(page));
+
+    priv = GNC_PLUGIN_PAGE_GET_PRIVATE(page);
+
+    priv->page_changed_id = g_signal_connect (G_OBJECT(page->window), "page_changed",
+                                              G_CALLBACK(gnc_plugin_page_main_window_changed),
+                                              page);
+
+    // on initial load try and set the page focus
+    (GNC_PLUGIN_PAGE_GET_CLASS(page)->focus_page)(page, TRUE);
+}
+
+
+/* disconnect the page_changed callback */
+void
+gnc_plugin_page_disconnect_page_changed (GncPluginPage *page)
+{
+    GncPluginPagePrivate *priv;
+
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE(page));
+
+    priv = GNC_PLUGIN_PAGE_GET_PRIVATE(page);
+
+    if (priv->page_changed_id > 0)
+    {
+        g_signal_handler_disconnect (G_OBJECT(page->window), priv->page_changed_id);
+        priv->page_changed_id = 0;
+    }
+}
+
+
 /*  Retrieve the Uniform Resource Identifier for this page. */
 const gchar *
 gnc_plugin_page_get_uri (GncPluginPage *page)
diff --git a/gnucash/gnome-utils/gnc-plugin-page.h b/gnucash/gnome-utils/gnc-plugin-page.h
index fa09f9b24..82416acfe 100644
--- a/gnucash/gnome-utils/gnc-plugin-page.h
+++ b/gnucash/gnome-utils/gnc-plugin-page.h
@@ -156,6 +156,21 @@ typedef struct
      *  @param window The window where the page was added. */
     void (* window_changed) (GncPluginPage *plugin_page, GtkWidget *window);
 
+    /** Perform plugin specific actions to set the focus.
+     *
+     *  @param page The page that was added to a window.
+     *
+     *  @param on_current_pgae Whether this page is the currentone. */
+    void (* focus_page) (GncPluginPage *plugin_page, gboolean on_current_page);
+
+    /** This function performs specific actions to set the focus on a specific
+     *  widget.
+     *
+     *  @param page The page that was added to a window.
+     *
+     *  @param on_current_pgae Whether this page is the currentone. */
+    gboolean (* focus_page_function) (GncPluginPage *plugin_page);
+
     /** This function vector allows page specific actions to occur
      *  when the page name is changed.
      *
@@ -393,12 +408,30 @@ const gchar *gnc_plugin_page_get_page_color (GncPluginPage *page);
  *
  *  @param page The page whose name should be retrieved.
  *
- *  @return The color for this page.  This string is owned by the page and
+ *  @param The color for this page.  This string is owned by the page and
  *  should not be freed by the caller.
  */
 void gnc_plugin_page_set_page_color (GncPluginPage *page, const char *color);
 
 
+/** Set up the page_changed callback for when the current page is changed.
+ *  This will store a pointer to the page focus funtion passed as a parameter
+ *  so that it can be used in setting up the g_idle_add
+ *
+ *  @param page The page the callback is setup for.
+ *
+ *  @param user_data The page focus function
+ */
+void gnc_plugin_page_inserted_cb (GncPluginPage *page, gpointer user_data);
+
+
+/** Disconnect the page_changed_id signal callback.
+ *
+ *  @param page The page whose name should be retrieved.
+ */
+void gnc_plugin_page_disconnect_page_changed (GncPluginPage *page);
+
+
 /** Retrieve the Uniform Resource Identifier for this page.
  *
  *  @param page The page whose URI should be retrieved.
diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.c b/gnucash/gnome/gnc-plugin-page-account-tree.c
index 1f70ad94a..c1d3396bd 100644
--- a/gnucash/gnome/gnc-plugin-page-account-tree.c
+++ b/gnucash/gnome/gnc-plugin-page-account-tree.c
@@ -114,6 +114,7 @@ static void gnc_plugin_page_account_tree_init (GncPluginPageAccountTree *plugin_
 static void gnc_plugin_page_account_tree_finalize (GObject *object);
 static void gnc_plugin_page_account_tree_selected (GObject *object, gpointer user_data);
 
+static gboolean gnc_plugin_page_account_tree_focus_widget (GncPluginPage *plugin_page);
 static GtkWidget *gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page);
 static void gnc_plugin_page_account_tree_destroy_widget (GncPluginPage *plugin_page);
 static void gnc_plugin_page_account_tree_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
@@ -405,6 +406,7 @@ gnc_plugin_page_account_tree_class_init (GncPluginPageAccountTreeClass *klass)
     gnc_plugin_class->destroy_widget  = gnc_plugin_page_account_tree_destroy_widget;
     gnc_plugin_class->save_page       = gnc_plugin_page_account_tree_save_page;
     gnc_plugin_class->recreate_page   = gnc_plugin_page_account_tree_recreate_page;
+    gnc_plugin_class->focus_page_function = gnc_plugin_page_account_tree_focus_widget;
 
     plugin_page_signals[ACCOUNT_SELECTED] =
         g_signal_new ("account_selected",
@@ -573,12 +575,16 @@ gnc_plugin_page_account_tree_get_current_account (GncPluginPageAccountTree *page
     return account;
 }
 
-gboolean
-gnc_plugin_page_account_tree_focus (GncPluginPageAccountTree *page)
+/**
+ * Whenever the current page is changed, if an account page is
+ * the current page, set focus on the tree view.
+ */
+static gboolean
+gnc_plugin_page_account_tree_focus_widget (GncPluginPage *account_plugin_page)
 {
-    if (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page))
+    if (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(account_plugin_page))
     {
-        GncPluginPageAccountTreePrivate *priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
+        GncPluginPageAccountTreePrivate *priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(account_plugin_page);
         GtkTreeView *view = GTK_TREE_VIEW(priv->tree_view);
 
         if (!gtk_widget_is_focus (GTK_WIDGET(view)))
@@ -636,32 +642,11 @@ gnc_plugin_page_account_editing_finished_cb (gpointer various, GncPluginPageRegi
         gtk_action_set_sensitive (action, TRUE);
 }
 
-static void
-gnc_plugin_account_tree_main_window_page_changed (GncMainWindow *window,
-                                                  GncPluginPage *current_plugin_page,
-                                                  GncPluginPage *account_plugin_page)
-{
-    // We continue only if the plugin_page is a valid
-    if (!current_plugin_page || !GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(current_plugin_page)||
-        !account_plugin_page || !GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(account_plugin_page))
-        return;
-
-    if (current_plugin_page == account_plugin_page)
-    {
-        // The page changed signal is emitted multiple times so we need
-        // to use an idle_add to change the focus to the tree view
-        g_idle_remove_by_data (GNC_PLUGIN_PAGE_ACCOUNT_TREE (account_plugin_page));
-        g_idle_add ((GSourceFunc)gnc_plugin_page_account_tree_focus,
-                      GNC_PLUGIN_PAGE_ACCOUNT_TREE (account_plugin_page));
-    }
-}
-
 static GtkWidget *
 gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page)
 {
     GncPluginPageAccountTree *page;
     GncPluginPageAccountTreePrivate *priv;
-    GncMainWindow *window;
     GtkTreeSelection *selection;
     GtkTreeView *tree_view;
     GtkWidget *scrolled_window;
@@ -759,10 +744,9 @@ gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page)
                            gnc_plugin_page_account_tree_summarybar_position_changed,
                            page);
 
-    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(page)->window);
-    g_signal_connect (window, "page_changed",
-                      G_CALLBACK(gnc_plugin_account_tree_main_window_page_changed),
-                      plugin_page);
+    g_signal_connect (G_OBJECT(plugin_page), "inserted",
+                      G_CALLBACK(gnc_plugin_page_inserted_cb),
+                      NULL);
 
     // Read account filter state information from account section
     gnc_tree_view_account_restore_filter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view), &priv->fd,
@@ -798,8 +782,11 @@ gnc_plugin_page_account_tree_destroy_widget (GncPluginPage *plugin_page)
     // Destroy the filter override hash table
     g_hash_table_destroy(priv->fd.filter_override);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
+
     // Remove the page focus idle function if present
-    g_idle_remove_by_data (GNC_PLUGIN_PAGE_ACCOUNT_TREE (plugin_page));
+    g_idle_remove_by_data (plugin_page);
 
     if (priv->widget)
     {
diff --git a/gnucash/gnome/gnc-plugin-page-account-tree.h b/gnucash/gnome/gnc-plugin-page-account-tree.h
index 2dad1e7e4..57b862987 100644
--- a/gnucash/gnome/gnc-plugin-page-account-tree.h
+++ b/gnucash/gnome/gnc-plugin-page-account-tree.h
@@ -95,15 +95,6 @@ GncPluginPage *gnc_plugin_page_account_tree_new  (void);
 Account * gnc_plugin_page_account_tree_get_current_account (GncPluginPageAccountTree *page);
 
 
-/** Given a pointer to an account tree plugin page, set the focus to
- *  the GtkTreeView. This is used in a g_idle_add so return FALSE.
- *
- *  @param page The "account tree" page.
- * 
- *  @return FALSE;
- */
-gboolean gnc_plugin_page_account_tree_focus (GncPluginPageAccountTree *page);
-
 /** Given a pointer to an account, the account tree will open
  *  and the account will be selected (if any).
  *
diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c
index 3449f289d..cd845c23e 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.c
+++ b/gnucash/gnome/gnc-plugin-page-budget.c
@@ -58,6 +58,7 @@
 #include "gnc-tree-view-account.h"
 #include "gnc-ui.h"
 #include "gnc-ui-util.h"
+#include "gnc-window.h"
 #include "option-util.h"
 #include "gnc-main-window.h"
 #include "gnc-component-manager.h"
@@ -85,6 +86,7 @@ static void gnc_plugin_page_budget_finalize (GObject *object);
 
 static GtkWidget *
 gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page);
+static gboolean gnc_plugin_page_budget_focus_widget (GncPluginPage *plugin_page);
 static void gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page);
 static void gnc_plugin_page_budget_save_page (GncPluginPage *plugin_page,
                                               GKeyFile *file,
@@ -299,6 +301,7 @@ gnc_plugin_page_budget_class_init (GncPluginPageBudgetClass *klass)
     gnc_plugin_class->destroy_widget  = gnc_plugin_page_budget_destroy_widget;
     gnc_plugin_class->save_page       = gnc_plugin_page_budget_save_page;
     gnc_plugin_class->recreate_page   = gnc_plugin_page_budget_recreate_page;
+    gnc_plugin_class->focus_page_function = gnc_plugin_page_budget_focus_widget;
 }
 
 
@@ -372,12 +375,16 @@ gnc_plugin_page_budget_close_cb (gpointer user_data)
 }
 
 
-gboolean
-gnc_plugin_page_budget_focus (GncPluginPageBudget *page)
+/**
+ * Whenever the current page is changed, if a budget page is
+ * the current page, set focus on the budget tree view.
+ */
+static gboolean
+gnc_plugin_page_budget_focus_widget (GncPluginPage *budget_plugin_page)
 {
-    if (GNC_IS_PLUGIN_PAGE_BUDGET(page))
+    if (GNC_IS_PLUGIN_PAGE_BUDGET(budget_plugin_page))
     {
-        GncPluginPageBudgetPrivate *priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
+        GncPluginPageBudgetPrivate *priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(budget_plugin_page);
         GncBudgetView *budget_view = priv->budget_view;
         GtkWidget *account_view = gnc_budget_view_get_account_tree_view (budget_view);
 
@@ -423,27 +430,6 @@ gnc_plugin_page_budget_refresh_cb (GHashTable *changes, gpointer user_data)
 }
 
 
-static void
-gnc_plugin_budget_main_window_page_changed (GncMainWindow *window,
-                                            GncPluginPage *current_plugin_page,
-                                            GncPluginPage *budget_plugin_page)
-{
-    // We continue only if the plugin_page is a valid
-    if (!current_plugin_page || !GNC_IS_PLUGIN_PAGE_BUDGET(current_plugin_page) ||
-        !budget_plugin_page || !GNC_IS_PLUGIN_PAGE_BUDGET(budget_plugin_page))
-        return;
-
-    if (current_plugin_page == budget_plugin_page)
-    {
-        // The page changed signal is emitted multiple times so we need
-        // to use an idle_add to change the focus to the tree view
-        g_idle_remove_by_data (GNC_PLUGIN_PAGE_BUDGET(budget_plugin_page));
-        g_idle_add ((GSourceFunc)gnc_plugin_page_budget_focus,
-                      GNC_PLUGIN_PAGE_BUDGET(budget_plugin_page));
-    }
-}
-
-
 /****************************
  * GncPluginPage Functions  *
  ***************************/
@@ -452,7 +438,6 @@ gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page)
 {
     GncPluginPageBudget *page;
     GncPluginPageBudgetPrivate *priv;
-    GncMainWindow *window;
 
     ENTER("page %p", plugin_page);
     page = GNC_PLUGIN_PAGE_BUDGET(plugin_page);
@@ -487,10 +472,9 @@ gnc_plugin_page_budget_create_widget (GncPluginPage *plugin_page)
                                     gnc_budget_get_guid (priv->budget),
                                     QOF_EVENT_DESTROY | QOF_EVENT_MODIFY);
 
-    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(page)->window);
-    g_signal_connect (window, "page_changed",
-                      G_CALLBACK(gnc_plugin_budget_main_window_page_changed),
-                      plugin_page);
+    g_signal_connect (G_OBJECT(plugin_page), "inserted",
+                      G_CALLBACK(gnc_plugin_page_inserted_cb),
+                      NULL);
 
     LEAVE("widget = %p", priv->budget_view);
     return GTK_WIDGET(priv->budget_view);
@@ -505,8 +489,11 @@ gnc_plugin_page_budget_destroy_widget (GncPluginPage *plugin_page)
     ENTER("page %p", plugin_page);
     priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(plugin_page);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
+
     // Remove the page focus idle function if present
-    g_idle_remove_by_data (GNC_PLUGIN_PAGE_BUDGET(plugin_page));
+    g_idle_remove_by_data (plugin_page);
 
     if (priv->budget_view)
     {
diff --git a/gnucash/gnome/gnc-plugin-page-budget.h b/gnucash/gnome/gnc-plugin-page-budget.h
index 9efe5203b..ffec5883f 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.h
+++ b/gnucash/gnome/gnc-plugin-page-budget.h
@@ -70,16 +70,6 @@ GncPluginPage *gnc_plugin_page_budget_new (GncBudget *budget);
 
 void gnc_budget_gui_delete_budget (GncBudget *budget);
 
-/** Given a pointer to a budget plugin page, set the focus to
- *  the Account GtkTreeView. This is used in a g_idle_add so
- *  return FALSE.
- *
- *  @param page The "budget" page.
- *
- *  @return FALSE
- */
-gboolean gnc_plugin_page_budget_focus (GncPluginPageBudget *page);
-
 G_END_DECLS
 
 #endif /* __GNC_PLUGIN_PAGE_BUDGET_H */
diff --git a/gnucash/gnome/gnc-plugin-page-invoice.c b/gnucash/gnome/gnc-plugin-page-invoice.c
index f4fa4c4a7..c0143f370 100644
--- a/gnucash/gnome/gnc-plugin-page-invoice.c
+++ b/gnucash/gnome/gnc-plugin-page-invoice.c
@@ -52,6 +52,7 @@ static void gnc_plugin_page_invoice_init (GncPluginPageInvoice *plugin_page);
 static void gnc_plugin_page_invoice_finalize (GObject *object);
 
 static GtkWidget *gnc_plugin_page_invoice_create_widget (GncPluginPage *plugin_page);
+static gboolean gnc_plugin_page_invoice_focus_widget (GncPluginPage *plugin_page);
 static void gnc_plugin_page_invoice_destroy_widget (GncPluginPage *plugin_page);
 static void gnc_plugin_page_invoice_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
 static GncPluginPage *gnc_plugin_page_invoice_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
@@ -448,6 +449,7 @@ gnc_plugin_page_invoice_class_init (GncPluginPageInvoiceClass *klass)
     gnc_plugin_class->save_page       = gnc_plugin_page_invoice_save_page;
     gnc_plugin_class->recreate_page   = gnc_plugin_page_invoice_recreate_page;
     gnc_plugin_class->window_changed  = gnc_plugin_page_invoice_window_changed;
+    gnc_plugin_class->focus_page_function = gnc_plugin_page_invoice_focus_widget;
 }
 
 static void
@@ -572,56 +574,39 @@ gnc_plugin_page_invoice_update_menus (GncPluginPage *page, gboolean is_posted, g
 }
 
 
-static gboolean
-gnc_plugin_page_invoice_focus (InvoiceWindow *iw)
-{
-    GtkWidget *regWidget = gnc_invoice_get_register(iw);
-    GtkWidget *notes = gnc_invoice_get_notes(iw);
-    GnucashSheet *sheet;
-
-    if (!GNUCASH_IS_REGISTER(regWidget))
-        return FALSE;
-
-    sheet = gnucash_register_get_sheet (GNUCASH_REGISTER(regWidget));
-
-    // Test for the sheet being read only
-    if (!gnucash_sheet_is_read_only (sheet))
-    {
-        if (!gtk_widget_is_focus (GTK_WIDGET(sheet)))
-            gtk_widget_grab_focus (GTK_WIDGET(sheet));
-    }
-    else // set focus to the notes field
-    {
-        if (!gtk_widget_is_focus (GTK_WIDGET(notes)))
-            gtk_widget_grab_focus (GTK_WIDGET(notes));
-    }
-    return FALSE;
-}
-
-
 /**
  * Whenever the current page is changed, if an invoice page is
  * the current page, set focus on the sheet or notes field.
  */
-static void
-gnc_plugin_page_invoice_main_window_page_changed (GncMainWindow *window,
-                                                  GncPluginPage *current_plugin_page,
-                                                  GncPluginPage *invoice_plugin_page)
+static gboolean
+gnc_plugin_page_invoice_focus_widget (GncPluginPage *invoice_plugin_page)
 {
-    // We continue only if the plugin_page is a valid
-    if (!current_plugin_page || !GNC_IS_PLUGIN_PAGE_INVOICE(current_plugin_page) ||
-        !invoice_plugin_page || !GNC_IS_PLUGIN_PAGE_INVOICE(invoice_plugin_page))
-        return;
-
-    if (current_plugin_page == invoice_plugin_page)
+    if (GNC_IS_PLUGIN_PAGE_INVOICE(invoice_plugin_page))
     {
         GncPluginPageInvoicePrivate *priv = GNC_PLUGIN_PAGE_INVOICE_GET_PRIVATE(invoice_plugin_page);
 
-        // The page changed signal is emitted multiple times so we need
-        // to use an idle_add to change the focus to the sheet
-        g_idle_remove_by_data (priv->iw);
-        g_idle_add ((GSourceFunc)gnc_plugin_page_invoice_focus, priv->iw);
+        GtkWidget *regWidget = gnc_invoice_get_register(priv->iw);
+        GtkWidget *notes = gnc_invoice_get_notes(priv->iw);
+        GnucashSheet *sheet;
+
+        if (!GNUCASH_IS_REGISTER(regWidget))
+            return FALSE;
+
+        sheet = gnucash_register_get_sheet (GNUCASH_REGISTER(regWidget));
+
+        // Test for the sheet being read only
+        if (!gnucash_sheet_is_read_only (sheet))
+        {
+            if (!gtk_widget_is_focus (GTK_WIDGET(sheet)))
+                gtk_widget_grab_focus (GTK_WIDGET(sheet));
+        }
+        else // set focus to the notes field
+        {
+            if (!gtk_widget_is_focus (GTK_WIDGET(notes)))
+                gtk_widget_grab_focus (GTK_WIDGET(notes));
+        }
     }
+    return FALSE;
 }
 
 
@@ -633,7 +618,6 @@ gnc_plugin_page_invoice_create_widget (GncPluginPage *plugin_page)
     GncPluginPageInvoice *page;
     GncPluginPageInvoicePrivate *priv;
     GtkWidget *regWidget, *widget;
-    GncMainWindow  *window;
 
     ENTER("page %p", plugin_page);
     page = GNC_PLUGIN_PAGE_INVOICE (plugin_page);
@@ -680,10 +664,9 @@ gnc_plugin_page_invoice_create_widget (GncPluginPage *plugin_page)
                                    gnc_plugin_page_invoice_refresh_cb,
                                    NULL, page);
 
-    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(plugin_page)->window);
-    g_signal_connect(window, "page_changed",
-                     G_CALLBACK(gnc_plugin_page_invoice_main_window_page_changed),
-                     plugin_page);
+    g_signal_connect (G_OBJECT(plugin_page), "inserted",
+                      G_CALLBACK(gnc_plugin_page_inserted_cb),
+                      NULL);
 
     LEAVE("");
     return priv->widget;
@@ -708,8 +691,11 @@ gnc_plugin_page_invoice_destroy_widget (GncPluginPage *plugin_page)
                                  gnc_plugin_page_invoice_summarybar_position_changed,
                                  page);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
+
     // Remove the page focus idle function if present
-    g_idle_remove_by_data (priv->iw);
+    g_idle_remove_by_data (plugin_page);
 
     if (priv->widget == NULL)
     {
diff --git a/gnucash/gnome/gnc-plugin-page-owner-tree.c b/gnucash/gnome/gnc-plugin-page-owner-tree.c
index 1b54a06f6..83c87186b 100644
--- a/gnucash/gnome/gnc-plugin-page-owner-tree.c
+++ b/gnucash/gnome/gnc-plugin-page-owner-tree.c
@@ -58,6 +58,7 @@
 #include "gnc-tree-view-owner.h"
 #include "gnc-ui.h"
 #include "gnc-ui-util.h"
+#include "gnc-window.h"
 #include "guile-mappings.h"
 #include "dialog-lot-viewer.h"
 #include "dialog-object-references.h"
@@ -365,43 +366,25 @@ gnc_plugin_page_owner_tree_new (GncOwnerType owner_type)
     return GNC_PLUGIN_PAGE(plugin_page);
 }
 
-
-static gboolean
-gnc_plugin_page_owner_focus (GtkTreeView *tree_view)
-{
-    if (GTK_IS_TREE_VIEW(tree_view))
-    {
-        if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
-            gtk_widget_grab_focus (GTK_WIDGET(tree_view));
-    }
-    return FALSE;
-}
-
-
 /**
  * Whenever the current page is changed, if an owner page is
- * the current page, set focus on the treeview.
+ * the current page, set focus on the tree view.
  */
-static void
-gnc_plugin_page_owner_main_window_page_changed (GncMainWindow *window,
-                                                GncPluginPage *current_plugin_page,
-                                                GncPluginPage *owner_plugin_page)
+static gboolean
+gnc_plugin_page_owner_focus_widget (GncPluginPage *owner_plugin_page)
 {
-    // We continue only if the plugin_page is a valid
-    if (!current_plugin_page || !GNC_IS_PLUGIN_PAGE_OWNER_TREE(current_plugin_page) ||
-        !owner_plugin_page || !GNC_IS_PLUGIN_PAGE_OWNER_TREE(owner_plugin_page))
-        return;
-
-    if (current_plugin_page == owner_plugin_page)
+    if (GNC_IS_PLUGIN_PAGE_OWNER_TREE(owner_plugin_page))
     {
         GncPluginPageOwnerTreePrivate *priv = GNC_PLUGIN_PAGE_OWNER_TREE_GET_PRIVATE(owner_plugin_page);
+        GtkTreeView *tree_view = priv->tree_view;
 
-        // The page changed signal is emitted multiple times so we need
-        // to use an idle_add to change the focus to the tree view
-        g_idle_remove_by_data (GTK_TREE_VIEW (priv->tree_view));
-        g_idle_add ((GSourceFunc)gnc_plugin_page_owner_focus,
-                      GTK_TREE_VIEW (priv->tree_view));
+        if (GTK_IS_TREE_VIEW(tree_view))
+        {
+            if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
+                gtk_widget_grab_focus (GTK_WIDGET(tree_view));
+        }
     }
+    return FALSE;
 }
 
 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageOwnerTree, gnc_plugin_page_owner_tree, GNC_TYPE_PLUGIN_PAGE)
@@ -422,6 +405,7 @@ gnc_plugin_page_owner_tree_class_init (GncPluginPageOwnerTreeClass *klass)
     gnc_plugin_class->destroy_widget  = gnc_plugin_page_owner_tree_destroy_widget;
     gnc_plugin_class->save_page       = gnc_plugin_page_owner_tree_save_page;
     gnc_plugin_class->recreate_page   = gnc_plugin_page_owner_tree_recreate_page;
+    gnc_plugin_class->focus_page_function = gnc_plugin_page_owner_focus_widget;
 
     plugin_page_signals[OWNER_SELECTED] =
         g_signal_new ("owner_selected",
@@ -571,7 +555,6 @@ gnc_plugin_page_owner_tree_create_widget (GncPluginPage *plugin_page)
 {
     GncPluginPageOwnerTree *page;
     GncPluginPageOwnerTreePrivate *priv;
-    GncMainWindow  *window;
     GtkTreeSelection *selection;
     GtkTreeView *tree_view;
     GtkWidget *scrolled_window;
@@ -686,10 +669,9 @@ gnc_plugin_page_owner_tree_create_widget (GncPluginPage *plugin_page)
     gnc_gui_component_set_session (priv->component_id,
                                    gnc_get_current_session());
 
-    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(plugin_page)->window);
-    g_signal_connect(window, "page_changed",
-                     G_CALLBACK(gnc_plugin_page_owner_main_window_page_changed),
-                     plugin_page);
+    g_signal_connect (G_OBJECT(plugin_page), "inserted",
+                      G_CALLBACK(gnc_plugin_page_inserted_cb),
+                      NULL);
 
     LEAVE("widget = %p", priv->widget);
     return priv->widget;
@@ -705,8 +687,11 @@ gnc_plugin_page_owner_tree_destroy_widget (GncPluginPage *plugin_page)
     page = GNC_PLUGIN_PAGE_OWNER_TREE (plugin_page);
     priv = GNC_PLUGIN_PAGE_OWNER_TREE_GET_PRIVATE(page);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
+
     // Remove the page focus idle function if present
-    g_idle_remove_by_data (GTK_TREE_VIEW (priv->tree_view));
+    g_idle_remove_by_data (plugin_page);
 
     if (priv->widget)
     {
diff --git a/gnucash/gnome/gnc-plugin-page-register.c b/gnucash/gnome/gnc-plugin-page-register.c
index 1d1958698..0c311aea4 100644
--- a/gnucash/gnome/gnc-plugin-page-register.c
+++ b/gnucash/gnome/gnc-plugin-page-register.c
@@ -102,6 +102,8 @@ static void gnc_plugin_page_register_finalize (GObject *object);
 static GtkWidget *gnc_plugin_page_register_create_widget (GncPluginPage *plugin_page);
 static void gnc_plugin_page_register_destroy_widget (GncPluginPage *plugin_page);
 static void gnc_plugin_page_register_window_changed (GncPluginPage *plugin_page, GtkWidget *window);
+static gboolean gnc_plugin_page_register_focus_widget (GncPluginPage *plugin_page);
+static void gnc_plugin_page_register_focus (GncPluginPage *plugin_page, gboolean current_page);
 static void gnc_plugin_page_register_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
 static GncPluginPage *gnc_plugin_page_register_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
 static void gnc_plugin_page_register_update_edit_menu (GncPluginPage *page, gboolean hide);
@@ -750,10 +752,12 @@ gnc_plugin_page_register_class_init (GncPluginPageRegisterClass *klass)
     gnc_plugin_class->create_widget   = gnc_plugin_page_register_create_widget;
     gnc_plugin_class->destroy_widget  = gnc_plugin_page_register_destroy_widget;
     gnc_plugin_class->window_changed  = gnc_plugin_page_register_window_changed;
+    gnc_plugin_class->focus_page      = gnc_plugin_page_register_focus;
     gnc_plugin_class->save_page       = gnc_plugin_page_register_save_page;
     gnc_plugin_class->recreate_page   = gnc_plugin_page_register_recreate_page;
     gnc_plugin_class->update_edit_menu_actions = gnc_plugin_page_register_update_edit_menu;
     gnc_plugin_class->finish_pending  = gnc_plugin_page_register_finish_pending;
+    gnc_plugin_class->focus_page_function = gnc_plugin_page_register_focus_widget;
 
     gnc_ui_register_account_destroy_callback (gppr_account_destroy_cb);
 }
@@ -843,12 +847,16 @@ gnc_plugin_page_register_get_current_txn (GncPluginPageRegister *page)
     return gnc_split_register_get_current_trans(reg);
 }
 
-gboolean
-gnc_plugin_page_register_focus (GncPluginPageRegister *page)
+/**
+ * Whenever the current page is changed, if a register page is
+ * the current page, set focus on the sheet.
+ */
+static gboolean
+gnc_plugin_page_register_focus_widget (GncPluginPage *register_plugin_page)
 {
-    if (GNC_IS_PLUGIN_PAGE_REGISTER(page))
+    if (GNC_IS_PLUGIN_PAGE_REGISTER(register_plugin_page))
     {
-        GNCSplitReg *gsr = gnc_plugin_page_register_get_gsr(GNC_PLUGIN_PAGE(page));
+        GNCSplitReg *gsr = gnc_plugin_page_register_get_gsr(GNC_PLUGIN_PAGE(register_plugin_page));
         gnc_split_reg_focus_on_sheet (gsr);
     }
     return FALSE;
@@ -1145,31 +1153,33 @@ get_filter_default_num_of_days (GNCLedgerDisplayType ledger_type)
         return "0";
 }
 
+/* For setting the focus on a register page, the default gnc_plugin
+ * function for 'focus_page' is overridden so that the page focus
+ * can be condionally set. This is to allow for enabling the setting
+ * of the sheet focus only when the page is the current one.
+ */
 static void
-gnc_plugin_register_main_window_page_changed (GncMainWindow *window,
-                                              GncPluginPage *current_plugin_page,
-                                              GncPluginPage *register_plugin_page)
+gnc_plugin_page_register_focus (GncPluginPage *plugin_page,
+                                gboolean on_current_page)
 {
+    GncPluginPageRegister *page;
     GncPluginPageRegisterPrivate *priv;
     GNCSplitReg *gsr;
 
-    // We continue only if the plugin_page is a valid
-    if (!current_plugin_page || !GNC_IS_PLUGIN_PAGE_REGISTER(current_plugin_page) ||
-        !register_plugin_page || !GNC_IS_PLUGIN_PAGE_REGISTER(register_plugin_page))
-        return;
+    g_return_if_fail (GNC_IS_PLUGIN_PAGE_REGISTER (plugin_page));
 
-    priv = GNC_PLUGIN_PAGE_REGISTER_GET_PRIVATE(register_plugin_page);
-    gsr = gnc_plugin_page_register_get_gsr (GNC_PLUGIN_PAGE(register_plugin_page));
+    page = GNC_PLUGIN_PAGE_REGISTER(plugin_page);
+    priv = GNC_PLUGIN_PAGE_REGISTER_GET_PRIVATE(page);
+
+    gsr = gnc_plugin_page_register_get_gsr (GNC_PLUGIN_PAGE(plugin_page));
 
-    if (current_plugin_page == register_plugin_page)
+    if (on_current_page)
     {
         priv->page_focus = TRUE;
 
-        // The page changed signal is emitted multiple times so we need
-        // to use an idle_add to change the focus to the register
-        g_idle_remove_by_data (GNC_PLUGIN_PAGE_REGISTER (register_plugin_page));
-        g_idle_add ((GSourceFunc)gnc_plugin_page_register_focus,
-                      GNC_PLUGIN_PAGE_REGISTER (register_plugin_page));
+       // Chain up to use parent version of 'focus_page' which will
+       // use an idle_add as the page changed signal is emitted multiple times.
+       GNC_PLUGIN_PAGE_CLASS(parent_class)->focus_page (plugin_page, TRUE);
     }
     else
         priv->page_focus = FALSE;
@@ -1183,7 +1193,6 @@ gnc_plugin_page_register_create_widget (GncPluginPage *plugin_page)
 {
     GncPluginPageRegister *page;
     GncPluginPageRegisterPrivate *priv;
-    GncMainWindow *window;
     GNCLedgerDisplayType ledger_type;
     GncWindow *gnc_window;
     guint numRows;
@@ -1398,10 +1407,9 @@ gnc_plugin_page_register_create_widget (GncPluginPage *plugin_page)
     gnc_split_reg_set_moved_cb
     (priv->gsr, (GFunc)gnc_plugin_page_register_ui_update, page);
 
-    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(plugin_page)->window);
-    g_signal_connect (window, "page_changed",
-                      G_CALLBACK(gnc_plugin_register_main_window_page_changed),
-                      plugin_page);
+    g_signal_connect (G_OBJECT(plugin_page), "inserted",
+                      G_CALLBACK(gnc_plugin_page_inserted_cb),
+                      NULL);
 
     /* DRH - Probably lots of other stuff from regWindowLedger should end up here. */
     LEAVE(" ");
@@ -1427,6 +1435,9 @@ gnc_plugin_page_register_destroy_widget (GncPluginPage *plugin_page)
                                  gnc_plugin_page_register_summarybar_position_changed,
                                  page);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
+
     // Remove the page focus idle function if present
     g_idle_remove_by_data (GNC_PLUGIN_PAGE_REGISTER (plugin_page));
 
@@ -1468,7 +1479,7 @@ gnc_plugin_page_register_destroy_widget (GncPluginPage *plugin_page)
 
 static void
 gnc_plugin_page_register_window_changed (GncPluginPage *plugin_page,
-        GtkWidget *window)
+                                         GtkWidget *window)
 {
     GncPluginPageRegister *page;
     GncPluginPageRegisterPrivate *priv;
diff --git a/gnucash/gnome/gnc-plugin-page-register.h b/gnucash/gnome/gnc-plugin-page-register.h
index 604836f40..43b2b2e2f 100644
--- a/gnucash/gnome/gnc-plugin-page-register.h
+++ b/gnucash/gnome/gnc-plugin-page-register.h
@@ -163,16 +163,6 @@ gnc_plugin_page_register_get_account (GncPluginPageRegister *page);
 Transaction *
 gnc_plugin_page_register_get_current_txn (GncPluginPageRegister *page);
 
-/** Given a pointer to a register plugin page, set the focus to
- *  the sheet. This is used in a g_idle_add so return FALSE.
- *
- *  @param page The "register" page.
- *
- *  @return FALSE
- */
-gboolean
-gnc_plugin_page_register_focus (GncPluginPageRegister *page);
-
 G_END_DECLS
 /** @} */
 /** @} */
diff --git a/gnucash/gnome/gnc-plugin-page-sx-list.c b/gnucash/gnome/gnc-plugin-page-sx-list.c
index 2f6ab431f..7fa88a38f 100644
--- a/gnucash/gnome/gnc-plugin-page-sx-list.c
+++ b/gnucash/gnome/gnc-plugin-page-sx-list.c
@@ -73,6 +73,7 @@
 #include "gnc-tree-view-sx-list.h"
 #include "gnc-ui-util.h"
 #include "gnc-ui.h"
+#include "gnc-window.h"
 
 #undef G_LOG_DOMAIN
 #define G_LOG_DOMAIN "gnc.gui.plugin-page.sx-list"
@@ -187,42 +188,25 @@ gnc_plugin_page_sx_list_new (void)
 }
 
 
-static gboolean
-gnc_plugin_page_sx_list_focus (GtkTreeView *tree_view)
-{
-    if (GTK_IS_TREE_VIEW(tree_view))
-    {
-        if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
-            gtk_widget_grab_focus (GTK_WIDGET(tree_view));
-    }
-    return FALSE;
-}
-
-
 /**
- * Whenever the current page is changed, if a schedule editor page is
- * the current page, set focus on the treeview.
+ * Whenever the current page is changed, if a sx page is
+ * the current page, set focus on the tree view.
  */
-static void
-gnc_plugin_page_sx_list_main_window_page_changed (GncMainWindow *window,
-                                                  GncPluginPage *current_plugin_page,
-                                                  GncPluginPage *sx_plugin_page)
+static gboolean
+gnc_plugin_page_sx_list_focus_widget (GncPluginPage *sx_plugin_page)
 {
-    // We continue only if the plugin_page is a valid
-    if (!current_plugin_page || !GNC_IS_PLUGIN_PAGE_SX_LIST(current_plugin_page) ||
-        !sx_plugin_page || !GNC_IS_PLUGIN_PAGE_SX_LIST(sx_plugin_page))
-        return;
-
-    if (current_plugin_page == sx_plugin_page)
+    if (GNC_IS_PLUGIN_PAGE_SX_LIST(sx_plugin_page))
     {
         GncPluginPageSxListPrivate *priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(sx_plugin_page);
+        GtkTreeView *tree_view = priv->tree_view;
 
-        // The page changed signal is emitted multiple times so we need
-        // to use an idle_add to change the focus to the tree view
-        g_idle_remove_by_data (GTK_TREE_VIEW (priv->tree_view));
-        g_idle_add ((GSourceFunc)gnc_plugin_page_sx_list_focus,
-                      GTK_TREE_VIEW (priv->tree_view));
+        if (GTK_IS_TREE_VIEW(tree_view))
+        {
+            if (!gtk_widget_is_focus (GTK_WIDGET(tree_view)))
+                gtk_widget_grab_focus (GTK_WIDGET(tree_view));
+        }
     }
+    return FALSE;
 }
 
 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageSxList, gnc_plugin_page_sx_list, GNC_TYPE_PLUGIN_PAGE)
@@ -244,6 +228,7 @@ gnc_plugin_page_sx_list_class_init (GncPluginPageSxListClass *klass)
     gnc_plugin_class->destroy_widget  = gnc_plugin_page_sx_list_destroy_widget;
     gnc_plugin_class->save_page       = gnc_plugin_page_sx_list_save_page;
     gnc_plugin_class->recreate_page   = gnc_plugin_page_sx_list_recreate_page;
+    gnc_plugin_class->focus_page_function = gnc_plugin_page_sx_list_focus_widget;
 }
 
 
@@ -371,7 +356,6 @@ gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
 {
     GncPluginPageSxList *page;
     GncPluginPageSxListPrivate *priv;
-    GncMainWindow  *window;
     GtkWidget *widget;
     GtkWidget *vbox;
     GtkWidget *label;
@@ -498,10 +482,9 @@ gnc_plugin_page_sx_list_create_widget (GncPluginPage *plugin_page)
     gnc_gui_component_set_session (priv->gnc_component_id,
                                    gnc_get_current_session());
 
-    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(plugin_page)->window);
-    g_signal_connect(window, "page_changed",
-                     G_CALLBACK(gnc_plugin_page_sx_list_main_window_page_changed),
-                     plugin_page);
+    g_signal_connect (G_OBJECT(plugin_page), "inserted",
+                      G_CALLBACK(gnc_plugin_page_inserted_cb),
+                      NULL);
 
     return priv->widget;
 }
@@ -516,8 +499,11 @@ gnc_plugin_page_sx_list_destroy_widget (GncPluginPage *plugin_page)
     page = GNC_PLUGIN_PAGE_SX_LIST (plugin_page);
     priv = GNC_PLUGIN_PAGE_SX_LIST_GET_PRIVATE(page);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
+
     // Remove the page focus idle function if present
-    g_idle_remove_by_data (GTK_TREE_VIEW (priv->tree_view));
+    g_idle_remove_by_data (plugin_page);
 
     if (priv->widget)
     {
diff --git a/gnucash/report/report-gnome/gnc-plugin-page-report.c b/gnucash/report/report-gnome/gnc-plugin-page-report.c
index 19272f77e..a46d968d4 100644
--- a/gnucash/report/report-gnome/gnc-plugin-page-report.c
+++ b/gnucash/report/report-gnome/gnc-plugin-page-report.c
@@ -233,41 +233,25 @@ gnc_plugin_page_report_set_property( GObject *obj,
 
 }
 
-static gboolean
-gnc_plugin_page_report_focus (GtkWidget *widget)
-{
-    if (GTK_IS_WIDGET(widget))
-    {
-        if (!gtk_widget_is_focus (GTK_WIDGET(widget)))
-            gtk_widget_grab_focus (GTK_WIDGET(widget));
-    }
-    return FALSE;
-}
-
 /**
  * Whenever the current page is changed, if a report page is
  * the current page, set focus on the report.
  */
-static void
-gnc_plugin_page_report_main_window_page_changed (GncMainWindow *window,
-                                                 GncPluginPage *current_plugin_page,
-                                                 GncPluginPage *report_plugin_page)
+static gboolean
+gnc_plugin_page_report_focus_widget (GncPluginPage *report_plugin_page)
 {
-    // We continue only if the plugin_page is a valid
-    if (!current_plugin_page || !GNC_IS_PLUGIN_PAGE_REPORT(current_plugin_page) ||
-        !report_plugin_page || !GNC_IS_PLUGIN_PAGE_REPORT(report_plugin_page))
-        return;
-
-    if (current_plugin_page == report_plugin_page)
+    if (GNC_IS_PLUGIN_PAGE_REPORT(report_plugin_page))
     {
         GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report_plugin_page);
         GtkWidget *widget = gnc_html_get_widget (priv->html);
 
-        // The page changed signal is emitted multiple times so we need
-        // to use an idle_add to change the focus to the webkit widget
-        g_idle_remove_by_data (widget);
-        g_idle_add ((GSourceFunc)gnc_plugin_page_report_focus, widget);
+        if (GTK_IS_WIDGET(widget))
+        {
+            if (!gtk_widget_is_focus (GTK_WIDGET(widget)))
+                gtk_widget_grab_focus (GTK_WIDGET(widget));
+        }
     }
+    return FALSE;
 }
 
 static void
@@ -294,6 +278,7 @@ gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass)
     gnc_plugin_page_class->page_name_changed = gnc_plugin_page_report_name_changed;
     gnc_plugin_page_class->update_edit_menu_actions = gnc_plugin_page_report_update_edit_menu;
     gnc_plugin_page_class->finish_pending   = gnc_plugin_page_report_finish_pending;
+    gnc_plugin_page_class->focus_page_function = gnc_plugin_page_report_focus_widget;
 
     // create the "reportId" property
     g_object_class_install_property( object_class,
@@ -412,7 +397,6 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
 {
     GncPluginPageReport *report;
     GncPluginPageReportPrivate *priv;
-    GncMainWindow  *window;
     GtkWindow *topLvl;
     GtkAction *action;
     URLType type;
@@ -477,10 +461,9 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page )
     g_signal_connect (G_OBJECT(GTK_WIDGET(priv->container)), "realize",
                       G_CALLBACK(gnc_plugin_page_report_realize_uri), page);
 
-    window = GNC_MAIN_WINDOW(GNC_PLUGIN_PAGE(page)->window);
-    g_signal_connect(window, "page_changed",
-                     G_CALLBACK(gnc_plugin_page_report_main_window_page_changed),
-                     page);
+    g_signal_connect (G_OBJECT(page), "inserted",
+                      G_CALLBACK(gnc_plugin_page_inserted_cb),
+                      NULL);
 
     gtk_widget_show_all( GTK_WIDGET(priv->container) );
     LEAVE("container %p", priv->container);
@@ -783,8 +766,11 @@ gnc_plugin_page_report_destroy_widget(GncPluginPage *plugin_page)
 
     widget = gnc_html_get_widget(priv->html);
 
+    // Remove the page_changed signal callback
+    gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
+
     // Remove the page focus idle function if present
-    g_idle_remove_by_data (widget);
+    g_idle_remove_by_data (plugin_page);
 
     if (priv->component_manager_id)
     {

commit 907bff34c3a1e60931f87831926deac15a7a6b1d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Feb 9 11:34:43 2020 +0800

    [gnc-module] clean up deprecation warnings
    
    * use reasonable max-width
    * compact code
    * use (ice-9 match)

diff --git a/bindings/guile/gnc-module.scm b/bindings/guile/gnc-module.scm
index d47cff6f2..9d916fb8e 100644
--- a/bindings/guile/gnc-module.scm
+++ b/bindings/guile/gnc-module.scm
@@ -27,50 +27,55 @@
 
 (define-module (gnucash gnc-module))
 
+(use-modules (ice-9 match))
+
+(define (deprecate . lst)
+  (issue-deprecation-warning (string-concatenate lst)))
+
 (define (no-op-deprecation-warning)
-    (issue-deprecation-warning "* WARNING * Guile wrappers for the gnc module system have been deprecated.")
-    (issue-deprecation-warning "This particular function call is now a no-op. Please use equivalent (use-modules ...) calls instead."))
+  (deprecate "* WARNING * Guile wrappers for the gnc module system have been \
+deprecated. This particular function call is now a no-op. Please use \
+equivalent (use-modules ...) calls instead."))
+
+(define-public gnc:module-system-init no-op-deprecation-warning)
+(define-public gnc:module-system-refresh no-op-deprecation-warning)
+(define-public gnc:module-load-optional no-op-deprecation-warning)
+(define-public gnc:module-unload no-op-deprecation-warning)
 
-(define-public (gnc:module-system-init)
-    (no-op-deprecation-warning))
-(define-public (gnc:module-system-refresh)
-    (no-op-deprecation-warning))
-(define-public (gnc:module-load-optional)
-    (no-op-deprecation-warning))
-(define-public (gnc:module-unload)
-    (no-op-deprecation-warning))
 (define-public (gnc:module-load gnc-mod-name mod-sys-version)
-    (let* ((mod-name-split (string-split gnc-mod-name #\/))
-           (mod-name-str (string-join mod-name-split " "))
-           (scm-mod-name (map string->symbol mod-name-split)))
-        (cond
-            ((string=? gnc-mod-name "gnucash/app-utils")
-             (issue-deprecation-warning "* WARNING * 'gnc:module-load (\"gnucash/app-utils\" 0)' has been deprecated and will be removed in gnucash 5.0.")
-             (issue-deprecation-warning "Use '(use-modules (gnucash engine)(gnucash app-utils))' instead.")
-             (issue-deprecation-warning "Use of the '(gnucash engine)' guile module is optional and depends on whether or not you use functions from this module in your code or not.")
-             (use-modules (gnucash engine)(gnucash app-utils)))
-            ((or
-                (string=? gnc-mod-name "gnucash/tax/de_DE")
-                (string=? gnc-mod-name "gnucash/tax/us"))
-             (set! scm-mod-name (list 'gnucash 'locale (list-ref scm-mod-name 2) 'tax))
-             (set! mod-name-str (string-join (map symbol->string scm-mod-name) " "))
-             (issue-deprecation-warning (string-concatenate (list "* WARNING * '(gnc:module-load \"" gnc-mod-name "\" 0)' has been deprecated.")))
-             (issue-deprecation-warning (string-concatenate (list "Use '(use-modules (" mod-name-str "))' instead.")))
-             (module-use! (current-module) (resolve-interface scm-mod-name)))
-            ((or
-                (string=? gnc-mod-name "gnucash/gnome-utils")
-                (string=? gnc-mod-name "gnucash/html")
-                (string=? gnc-mod-name "gnucash/report/report-system"))
-             (when (string=? gnc-mod-name"gnucash/report/report-system")
-                (set! mod-name-str "gnucash report"))
-                (set! scm-mod-name (list 'gnucash 'report))
-             (issue-deprecation-warning (string-concatenate (list "* WARNING * '(gnc:module-load \"" gnc-mod-name "\" 0)' has been deprecated.")))
-             (issue-deprecation-warning (string-concatenate (list "Use '(use-modules (gnucash engine)(gnucash app-utils)(" mod-name-str "))' instead.")))
-             (issue-deprecation-warning "Use of the '(gnucash engine)' or '(gnucash app-utils)' guile modules is optional and depends on whether or not you use functions from this module in your code or not.")
-             (use-modules (gnucash engine)(gnucash app-utils))
-             (module-use! (current-module) (resolve-interface scm-mod-name)))
-            (else
-             (issue-deprecation-warning (string-concatenate (list "* WARNING * '(gnc:module-load \"" gnc-mod-name "\" 0)' has been deprecated.")))
-             (issue-deprecation-warning (string-concatenate ( list "Use '(use-modules (" mod-name-str "))' instead.")))
-             (issue-deprecation-warning "Additional guile modules may have to be loaded depending on your specific code.")
-             (module-use! (current-module) (resolve-interface scm-mod-name))))))
+  (let* ((mod-name-split (string-split gnc-mod-name #\/))
+         (mod-name-str (string-join mod-name-split " "))
+         (scm-mod-name (map string->symbol mod-name-split)))
+
+    (match gnc-mod-name
+      ("gnucash/app-utils"
+       (deprecate "* WARNING * 'gnc:module-load (\"gnucash/app-utils\" 0)' has \
+been deprecated and will be removed in gnucash 5.0. Use '(use-modules (gnucash \
+engine) (gnucash app-utils))' instead. Use of the '(gnucash engine)' guile \
+module is optional and depends on whether or not you use functions from \
+this module in your code or not.")
+       (use-modules (gnucash engine) (gnucash app-utils)))
+
+      ((or "gnucash/tax/de_DE" "gnucash/tax/us")
+       (set! scm-mod-name `(gnucash locale ,(list-ref scm-mod-name 2) tax))
+       (set! mod-name-str (string-join (map symbol->string scm-mod-name) " "))
+       (deprecate "* WARNING * '(gnc:module-load \"" gnc-mod-name "\" 0)' has \
+been deprecated. Use '(use-modules (" mod-name-str "))' instead.")
+       (module-use! (current-module) (resolve-interface scm-mod-name)))
+
+      ((or "gnucash/gnome-utils" "gnucash/html" "gnucash/report/report-system")
+       (when (string=? gnc-mod-name "gnucash/report/report-system")
+         (set! mod-name-str "gnucash report"))
+       (set! scm-mod-name '(gnucash report))
+       (deprecate "* WARNING * '(gnc:module-load \"" gnc-mod-name "\" 0)' has \
+been deprecated. Use '(use-modules (gnucash engine) (gnucash app-utils) \
+(" mod-name-str "))' instead. Use of the '(gnucash engine)' or \
+'(gnucash app-utils)' guile modules is optional and depends on whether \
+or not you use functions from this module in your code or not.")
+       (use-modules (gnucash engine) (gnucash app-utils))
+       (module-use! (current-module) (resolve-interface scm-mod-name)))
+
+      (_ (deprecate "* WARNING * '(gnc:module-load \"" gnc-mod-name "\" 0)' \
+has been deprecated. Use '(use-modules (" mod-name-str "))' instead. \
+Additional guile modules may have to be loaded depending on your specific code.")
+       (module-use! (current-module) (resolve-interface scm-mod-name))))))

commit f5c0ddd7864d2ddf59cd7fb7db8758bb982f04de
Merge: 50b882aa8 58ddb47f5
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Feb 9 11:30:10 2020 +0800

    Merge branch 'maint'


commit 58ddb47f5696191b33b5fbacf632485d88ab73c7
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Feb 9 11:13:59 2020 +0800

    [new-owner-report] change highlight trigger: onclick to onmousedown
    
    and also disable event propagation; this disables text selection

diff --git a/gnucash/report/business-reports/new-owner-report.scm b/gnucash/report/business-reports/new-owner-report.scm
index ec67270c7..e42b8df78 100644
--- a/gnucash/report/business-reports/new-owner-report.scm
+++ b/gnucash/report/business-reports/new-owner-report.scm
@@ -63,9 +63,10 @@
 (define javascript "
 <script>
   function getID(cell) { return cell.getAttribute('link-id'); }
-  function clicky() {
-      var id = getID(this);
-      var ishighlighted = this.classList.contains('highlight');
+  function mousedown(e) {
+      var id = getID(e.target);
+      var ishighlighted = e.target.classList.contains('highlight');
+      e.preventDefault ();
       TDs.forEach(TD => TD.classList.remove('highlight'));
       if (ishighlighted) return;
       TDs
@@ -73,7 +74,7 @@
           .forEach (TD => TD.classList.add('highlight'));}
   var TDs = document.getElementsByTagName('td');
   TDs = [...TDs].filter(getID);
-  TDs.forEach(TD => TD.onclick = clicky);
+  TDs.forEach(TD => TD.onmousedown = mousedown);
 </script>
 ")
 

commit 3be42bebb859dfad7158cd18b689f9f0807cd557
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Feb 9 09:55:25 2020 +0800

    [test-new-owner-report] refine test to target exact table row

diff --git a/gnucash/report/business-reports/test/test-new-owner-report.scm b/gnucash/report/business-reports/test/test-new-owner-report.scm
index f28676b65..8f292bb2f 100644
--- a/gnucash/report/business-reports/test/test-new-owner-report.scm
+++ b/gnucash/report/business-reports/test/test-new-owner-report.scm
@@ -226,18 +226,12 @@
     (let* ((options (default-testing-options owner-1 (get-acct "AR-USD")))
            (sxml (options->sxml options "new-customer-report basic")))
       (test-equal "line 1"
-        '("Customer History" "Linked Details" "1980-01-13" "1980-01-13"
-          "Invoice" "$11.50" "$11.50" "1980-03-18" "Payment" "inv >90 payment"
-          "$11.50" "pay only $1.50" "$1.50" "$1.50" "Pre-Payment" "Current"
-          "0-30 days" "31-60 days" "61-90 days" "91+ days" "Total" "$20.00"
-          "$0.00" "$0.00" "$0.00" "$0.00" "$11.75" "$31.75")
-        ((sxpath `(// (table 3) // (tr 1) // *text*)) sxml))
+        '("1980-01-13" "1980-01-13" "Invoice" "$11.50" "$11.50" "1980-03-18"
+          "Payment" "inv >90 payment" "$11.50" "pay only $1.50" "$1.50" "$1.50")
+        ((sxpath `(html body (table 3) tbody (tr 1) // *text*)) sxml))
       (test-equal "line 2"
-        '("Date" "Due Date" "Reference" "Type" "Description" "Invoice"
-          "Payment" "Balance" "Date" "Reference" "Type" "Description"
-          "Partial Amount" "Amount" "1980-03-20" "Payment" "inv >90 payment"
-          "pay only $2.00" "$2.00" "$2.00")
-        ((sxpath `(// (table 3) // (tr 2) // *text*)) sxml))
+        '("1980-03-20" "Payment" "inv >90 payment" "pay only $2.00" "$2.00" "$2.00")
+        ((sxpath `(// (table 3) // tbody // (tr 2) // *text*)) sxml))
       (test-equal "line 3"
         '("UNPAID" "$8.00")
         ((sxpath `(// (table 3) // (tr 3) // *text*)) sxml))

commit 18acb42344c2f57ef6c9950e2d9a3a43a2c02983
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Feb 9 10:27:36 2020 +0800

    [new-owner-report] clarify payment-txn processor
    
    use unique varnames

diff --git a/gnucash/report/business-reports/new-owner-report.scm b/gnucash/report/business-reports/new-owner-report.scm
index 87e013ec1..ec67270c7 100644
--- a/gnucash/report/business-reports/new-owner-report.scm
+++ b/gnucash/report/business-reports/new-owner-report.scm
@@ -629,15 +629,15 @@
          (let ((lot (xaccSplitGetLot split)))
            (define (equal-to-split? s) (equal? s split))
            (match (gncInvoiceGetInvoiceFromLot lot)
-             (() (lp rest
-                     (- overpayment (gnc-lot-get-balance lot))
-                     invoices
-                     (let lp ((lot-splits (gnc-lot-get-split-list lot))
-                              (acc opposing-splits))
-                       (match lot-splits
-                         (() acc)
-                         (((? equal-to-split?) . rest) (lp rest acc))
-                         ((lot-split . rest) (lp rest (cons lot-split acc)))))))
+             (() (let lp1 ((lot-splits (gnc-lot-get-split-list lot))
+                           (opposing-splits opposing-splits))
+                   (match lot-splits
+                     (() (lp rest
+                             (- overpayment (gnc-lot-get-balance lot))
+                             invoices
+                             opposing-splits))
+                     (((? equal-to-split?) . tail) (lp1 tail opposing-splits))
+                     ((head . tail) (lp1 tail (cons head opposing-splits))))))
              (inv
               (lp rest
                   overpayment

commit 09d3e953792d5272790710acf31f553800a6ad52
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Feb 9 09:54:25 2020 +0800

    [new-owner-report] if Payment amount is negative, label "Refund"
    
    and add logic to properly handle AP/AR negation rules

diff --git a/gnucash/report/business-reports/new-owner-report.scm b/gnucash/report/business-reports/new-owner-report.scm
index a3b0924d1..87e013ec1 100644
--- a/gnucash/report/business-reports/new-owner-report.scm
+++ b/gnucash/report/business-reports/new-owner-report.scm
@@ -274,12 +274,14 @@
       (let ((inv (gncInvoiceGetInvoiceFromLot (xaccSplitGetLot split))))
         (gnc:make-html-text (invoice->anchor inv)))))))
 
-(define (split->type-str split)
+(define (split->type-str split payable?)
   (let* ((txn (xaccSplitGetParent split))
+         (amt (xaccSplitGetAmount split))
+         (refund? (if payable? (< amt 0) (> amt 0)))
          (invoice (gncInvoiceGetInvoiceFromTxn txn)))
     (cond
      ((txn-is-invoice? txn) (gncInvoiceGetTypeString invoice))
-     ((txn-is-payment? txn) (_ "Payment"))
+     ((txn-is-payment? txn) (if refund? (_ "Refund") (_ "Payment")))
      ((txn-is-link? txn) (_ "Link"))
      (else (_ "Unknown")))))
 
@@ -573,7 +575,7 @@
                         (cons (make-link-data
                                (qof-print-date (xaccTransGetDate lot-txn))
                                (split->reference lot-split)
-                               (split->type-str lot-split)
+                               (split->type-str lot-split payable?)
                                (splits->desc non-document)
                                (gnc:make-html-text (split->anchor lot-split #t))
                                (list->cell
@@ -600,7 +602,7 @@
                          (cons (make-link-data
                                 (qof-print-date (xaccTransGetDate posting-txn))
                                 (split->reference posting-split)
-                                (split->type-str posting-split)
+                                (split->type-str posting-split payable?)
                                 (splits->desc (list posting-split))
                                 (gnc:make-html-text (split->anchor lot-split neg))
                                 (gnc:make-html-text (split->anchor posting-split neg))
@@ -689,7 +691,7 @@
          (make-link-data
           (qof-print-date (xaccTransGetDate (xaccSplitGetParent s)))
           (split->reference s)
-          (split->type-str s)
+          (split->type-str s payable?)
           (splits->desc (list s))
           (gnc:make-html-text (split->anchor s #f))
           (gnc:make-html-text (split->anchor s #f))
@@ -771,7 +773,7 @@
         (add-row
          table odd-row? used-columns date (gncInvoiceGetDateDue invoice)
          (split->reference split)
-         (split->type-str split)
+         (split->type-str split payable?)
          (splits->desc (list split))
          currency (+ total value)
          (and (>= orig-value 0) (amount->anchor split orig-value))
@@ -800,7 +802,7 @@
         (add-row
          table odd-row? used-columns date #f
          (split->reference split)
-         (split->type-str split)
+         (split->type-str split payable?)
          (splits->desc (xaccTransGetAPARAcctSplitList txn #t))
          currency (+ total value)
          (and (>= orig-value 0) (amount->anchor split orig-value))
diff --git a/gnucash/report/business-reports/test/test-new-owner-report.scm b/gnucash/report/business-reports/test/test-new-owner-report.scm
index 30beb02ab..f28676b65 100644
--- a/gnucash/report/business-reports/test/test-new-owner-report.scm
+++ b/gnucash/report/business-reports/test/test-new-owner-report.scm
@@ -274,25 +274,25 @@
         ((sxpath `(// (table 3) // (tr 11) // *text*)) sxml))
 
       ;; tests for refund $120 to partially repay
-      (test-equal "line 12"
-        '("1980-06-28" "Payment" "-$148.25" "1980-06-30" "Payment"
+      (test-equal "line 12 refund $120 to partially repay"
+        '("1980-06-28" "Payment" "-$148.25" "1980-06-30" "Refund"
           "$160.00" "$50.00" "$50.00")
         ((sxpath `(// (table 3) // (tr 12) // *text*)) sxml))
-      (test-equal "line 13"
-        '("1980-06-29" "Payment" "$120.00" "$120.00")
+      (test-equal "line 13 refund $120 to partially repay"
+        '("1980-06-29" "Refund" "$120.00" "$120.00")
         ((sxpath `(// (table 3) // (tr 13) // *text*)) sxml))
-      (test-equal "line 14"
+      (test-equal "line 14 refund $120 to partially repay"
         '("Pre-Payment" "-$10.00")
         ((sxpath `(// (table 3) // (tr 14) // *text*)) sxml))
-      (test-equal "line 15"
-        '("1980-06-29" "Payment" "-$28.25" "1980-06-28" "Payment"
+      (test-equal "line 15 refund $120 to partially repay"
+        '("1980-06-29" "Refund" "-$28.25" "1980-06-28" "Payment"
           "$120.00" "-$120.00" "-$120.00")
         ((sxpath `(// (table 3) // (tr 15) // *text*)) sxml))
-      (test-equal "line 16"
-        '("1980-06-30" "Payment" "$21.75" "1980-06-28" "Payment"
+      (test-equal "line 16 refund $120 to partially repay"
+        '("1980-06-30" "Refund" "$21.75" "1980-06-28" "Payment"
           "$50.00" "-$40.00" "-$40.00")
         ((sxpath `(// (table 3) // (tr 16) // *text*)) sxml))
-      (test-equal "line 17"
+      (test-equal "line 17 refund $120 to partially repay"
         '("Pre-Payment" "-$10.00")
         ((sxpath `(// (table 3) // (tr 17) // *text*)) sxml))
 

commit 6e64a37839ae951d187f349ecf775e4aa32282fa
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Feb 9 09:56:58 2020 +0800

    [new-owner-report] fix comment for non-document accumulator

diff --git a/gnucash/report/business-reports/new-owner-report.scm b/gnucash/report/business-reports/new-owner-report.scm
index 81720a62a..a3b0924d1 100644
--- a/gnucash/report/business-reports/new-owner-report.scm
+++ b/gnucash/report/business-reports/new-owner-report.scm
@@ -607,9 +607,8 @@
                                 (gncInvoiceReturnGUID document))
                                result)))))
 
-               ;; this payment's peer APAR split can't find
-               ;; document. this likely is an old style link txn. RHS
-               ;; show transaction only.
+               ;; this payment's peer split can't find document. this
+               ;; is a regular payment or an old link txn. accumulate.
                (else
                 (lp1 (cdr lot-txn-splits)
                      (cons (car lot-txn-splits) non-document)

commit 19db1daed68a1b9d2478066f10da70b7bdf0f69d
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Feb 8 18:20:23 2020 +0800

    Bug 797419 - equity-statement unrealized-gain calculator uses weighted-average
    
    for consistency. all other unrealized-gain calculators use average-cost.

diff --git a/gnucash/report/standard-reports/equity-statement.scm b/gnucash/report/standard-reports/equity-statement.scm
index 85ad4cc85..2639a8e2e 100644
--- a/gnucash/report/standard-reports/equity-statement.scm
+++ b/gnucash/report/standard-reports/equity-statement.scm
@@ -283,12 +283,12 @@
 	 )
 
     (define (unrealized-gains-at-date book-balance exchange-fn date)
-      (define weighted-fn
-	(gnc:case-exchange-fn 'weighted-average report-commodity date))
+      (define cost-fn
+	(gnc:case-exchange-fn 'average-cost report-commodity date))
       (gnc:monetaries-add
        (gnc:sum-collector-commodity book-balance report-commodity exchange-fn)
        (gnc:monetary-neg
-        (gnc:sum-collector-commodity book-balance report-commodity weighted-fn))))
+        (gnc:sum-collector-commodity book-balance report-commodity cost-fn))))
 
     (define (get-start-balance-fn account)
       (gnc:account-get-comm-balance-at-date account start-date #f))
diff --git a/gnucash/report/standard-reports/test/test-equity-statement.scm b/gnucash/report/standard-reports/test/test-equity-statement.scm
index 610a58c2f..f42c40dd4 100644
--- a/gnucash/report/standard-reports/test/test-equity-statement.scm
+++ b/gnucash/report/standard-reports/test/test-equity-statement.scm
@@ -108,13 +108,13 @@
         (sxml->table-row-col sxml 1 5 #f))
 
       (test-equal "unrealized"
-        '("Unrealized Losses" "$0.25")
+        '("Unrealized Losses" "$1.00")
         (sxml->table-row-col sxml 1 6 #f))
 
       (test-equal "inc/dec in capital"
-        '("Increase in capital" "$3,086.75")
+        '("Increase in capital" "$3,086.00")
         (sxml->table-row-col sxml 1 7 #f))
 
       (test-equal "capital end"
-        '("Capital, 01/01/05" "$3,115.75")
+        '("Capital, 01/01/05" "$3,115.00")
         (sxml->table-row-col sxml 1 8 #f)))))

commit 7cbe367cafe531f295b05bb4a1a07f3e3309ea35
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Feb 8 17:37:43 2020 +0800

    [new-owner-report] LHS invoice->payment LINK/PAYMENT merge
    
    * invoice->payment LINK or PAYMENT txns are handled identically. Merge.
    * reorder definitions in document handler.
    * rename variable 'invoice' to 'document'; invoice was clashing with
      outer variable. This fixes a credit-note negation bug.

diff --git a/gnucash/report/business-reports/new-owner-report.scm b/gnucash/report/business-reports/new-owner-report.scm
index 49ce284bf..81720a62a 100644
--- a/gnucash/report/business-reports/new-owner-report.scm
+++ b/gnucash/report/business-reports/new-owner-report.scm
@@ -581,14 +581,6 @@
                                (gncTransGetGUID lot-txn))
                               result))))
 
-               ;; this payment peer is non-document, accumulate it.
-               ((not (memv (xaccAccountGetType
-                            (xaccSplitGetAccount (car lot-txn-splits)))
-                           (list ACCT-TYPE-RECEIVABLE ACCT-TYPE-PAYABLE)))
-                (lp1 (cdr lot-txn-splits)
-                     (cons (car lot-txn-splits) non-document)
-                     result))
-
                ;; this payment's peer split has same sign as the
                ;; payment split. ignore.
                ((sign-equal? (xaccSplitGetAmount (car lot-txn-splits))
@@ -599,11 +591,10 @@
                ;; reducing split.
                ((lot-split->posting-split (car lot-txn-splits)) =>
                 (lambda (posting-split)
-                  (let ((lot-txn-split (car lot-txn-splits))
-                        (invoice (gncInvoiceGetInvoiceFromTxn
-                                  (xaccSplitGetParent posting-split)))
-                        (neg (not (gncInvoiceGetIsCreditNote invoice)))
-                        (posting-txn (xaccSplitGetParent posting-split)))
+                  (let* ((lot-txn-split (car lot-txn-splits))
+                         (posting-txn (xaccSplitGetParent posting-split))
+                         (document (gncInvoiceGetInvoiceFromTxn posting-txn))
+                         (neg (gncInvoiceGetIsCreditNote document)))
                     (lp1 (cdr lot-txn-splits)
                          non-document
                          (cons (make-link-data
@@ -613,15 +604,13 @@
                                 (splits->desc (list posting-split))
                                 (gnc:make-html-text (split->anchor lot-split neg))
                                 (gnc:make-html-text (split->anchor posting-split neg))
-                                (gncInvoiceReturnGUID invoice))
+                                (gncInvoiceReturnGUID document))
                                result)))))
 
                ;; this payment's peer APAR split can't find
                ;; document. this likely is an old style link txn. RHS
                ;; show transaction only.
                (else
-                (gnc:warn (car lot-txn-splits) " in APAR but can't find "
-                          "owner; is likely an old-style link transaction.")
                 (lp1 (cdr lot-txn-splits)
                      (cons (car lot-txn-splits) non-document)
                      result))))))))))

commit 70d8acc7acf25f4df69afc379260ba9870d9633a
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Feb 8 16:21:20 2020 +0800

    [test-new-owner-report] add tests for "$120 to partially repay"

diff --git a/gnucash/report/business-reports/test/test-new-owner-report.scm b/gnucash/report/business-reports/test/test-new-owner-report.scm
index bbb15543b..30beb02ab 100644
--- a/gnucash/report/business-reports/test/test-new-owner-report.scm
+++ b/gnucash/report/business-reports/test/test-new-owner-report.scm
@@ -160,7 +160,7 @@
         (set-option! options "General" "From"
                      (cons 'absolute (gnc-dmy2time64 1 1 1980)))
         (set-option! options "General" "To"
-                     (cons 'absolute (gnc-dmy2time64 1 7 1980)))
+                     (cons 'absolute (gnc-dmy2time64 1 7 1981)))
         (set-option! options "Display Columns" "Links" 'detailed)
         options))
 
@@ -181,7 +181,7 @@
     (let ((new-cn (add-invoice (gnc-dmy2time64 22 06 1980) -3 "CN")))
       (gncInvoiceSetIsCreditNote new-cn #t))
 
-    ;; inv $11.50, 2 payments
+    ;; inv $28, CN $27, Bank $1
     (let* ((inv (add-invoice (gnc-dmy2time64 24 06 1980) 28 "$28.00"))
            (CN (add-invoice (gnc-dmy2time64 25 06 1980) -27 "$27.00"))
            (inv-lot (gncInvoiceGetPostedLot inv))
@@ -198,6 +198,29 @@
        (list (create-split-for-lot AR -27 inv-lot)
              (create-split-for-lot AR 27 CN-lot))))
 
+    ;; refund $120 to partially repay
+    (let ((lot1 (gnc-lot-new (gnc-get-current-book)))
+          (lot2 (gnc-lot-new (gnc-get-current-book))))
+
+      (gncOwnerAttachToLot owner-1 lot1)
+      (gncOwnerAttachToLot owner-1 lot2)
+
+      (create-multisplit
+       28 06 1980 "payment" TXN-TYPE-PAYMENT
+       (list (create-split-for-lot AR  -120 lot1)
+             (create-split-for-lot AR   -40 lot2)
+             (create-split-for-lot Bank 160 #f)))
+
+      (create-multisplit
+       29 06 1980 "payment" TXN-TYPE-PAYMENT
+       (list (create-split-for-lot AR    120 lot1)
+             (create-split-for-lot Bank -120 #f)))
+
+      (create-multisplit
+       30 06 1980 "payment" TXN-TYPE-PAYMENT
+       (list (create-split-for-lot AR    50 lot2)
+             (create-split-for-lot Bank -50 #f))))
+
     (display "new-owner-report tests:\n")
     (test-begin "new-customer-report")
     (let* ((options (default-testing-options owner-1 (get-acct "AR-USD")))
@@ -206,8 +229,8 @@
         '("Customer History" "Linked Details" "1980-01-13" "1980-01-13"
           "Invoice" "$11.50" "$11.50" "1980-03-18" "Payment" "inv >90 payment"
           "$11.50" "pay only $1.50" "$1.50" "$1.50" "Pre-Payment" "Current"
-          "0-30 days" "31-60 days" "61-90 days" "91+ days" "Total" "$0.00"
-          "$0.00" "-$3.00" "$6.75" "$0.00" "$8.00" "$11.75")
+          "0-30 days" "31-60 days" "61-90 days" "91+ days" "Total" "$20.00"
+          "$0.00" "$0.00" "$0.00" "$0.00" "$11.75" "$31.75")
         ((sxpath `(// (table 3) // (tr 1) // *text*)) sxml))
       (test-equal "line 2"
         '("Date" "Due Date" "Reference" "Type" "Description" "Invoice"
@@ -248,5 +271,30 @@
       (test-equal "line 11"
         '("1980-06-26" "Payment" "$11.75" "1980-06-24" "Invoice" "$1.00"
           "$1.00" "$28.00")
-        ((sxpath `(// (table 3) // (tr 11) // *text*)) sxml)))
+        ((sxpath `(// (table 3) // (tr 11) // *text*)) sxml))
+
+      ;; tests for refund $120 to partially repay
+      (test-equal "line 12"
+        '("1980-06-28" "Payment" "-$148.25" "1980-06-30" "Payment"
+          "$160.00" "$50.00" "$50.00")
+        ((sxpath `(// (table 3) // (tr 12) // *text*)) sxml))
+      (test-equal "line 13"
+        '("1980-06-29" "Payment" "$120.00" "$120.00")
+        ((sxpath `(// (table 3) // (tr 13) // *text*)) sxml))
+      (test-equal "line 14"
+        '("Pre-Payment" "-$10.00")
+        ((sxpath `(// (table 3) // (tr 14) // *text*)) sxml))
+      (test-equal "line 15"
+        '("1980-06-29" "Payment" "-$28.25" "1980-06-28" "Payment"
+          "$120.00" "-$120.00" "-$120.00")
+        ((sxpath `(// (table 3) // (tr 15) // *text*)) sxml))
+      (test-equal "line 16"
+        '("1980-06-30" "Payment" "$21.75" "1980-06-28" "Payment"
+          "$50.00" "-$40.00" "-$40.00")
+        ((sxpath `(// (table 3) // (tr 16) // *text*)) sxml))
+      (test-equal "line 17"
+        '("Pre-Payment" "-$10.00")
+        ((sxpath `(// (table 3) // (tr 17) // *text*)) sxml))
+
+      )
     (test-end "new-customer-report")))



Summary of changes:
 bindings/guile/gnc-module.scm                      |  93 ++++++++++---------
 gnucash/gnome-utils/gnc-main-window.c              |   8 +-
 gnucash/gnome-utils/gnc-plugin-page.c              | 102 ++++++++++++++++++++-
 gnucash/gnome-utils/gnc-plugin-page.h              |  35 ++++++-
 gnucash/gnome/gnc-plugin-budget.c                  |   8 ++
 gnucash/gnome/gnc-plugin-page-account-tree.c       |  47 ++++------
 gnucash/gnome/gnc-plugin-page-account-tree.h       |   9 --
 gnucash/gnome/gnc-plugin-page-budget.c             |  49 ++++------
 gnucash/gnome/gnc-plugin-page-budget.h             |  10 --
 gnucash/gnome/gnc-plugin-page-invoice.c            |  80 +++++++---------
 gnucash/gnome/gnc-plugin-page-owner-tree.c         |  55 ++++-------
 gnucash/gnome/gnc-plugin-page-register.c           |  61 +++++++-----
 gnucash/gnome/gnc-plugin-page-register.h           |  10 --
 gnucash/gnome/gnc-plugin-page-report.c             |  48 ++++------
 gnucash/gnome/gnc-plugin-page-sx-list.c            |  56 +++++------
 gnucash/gnome/gnc-split-reg.c                      |  33 +++++--
 gnucash/report/html-document.scm                   | 101 +++++++++-----------
 .../report/reports/standard/equity-statement.scm   |   6 +-
 .../report/reports/standard/new-owner-report.scm   |  77 ++++++++--------
 .../standard/test/test-equity-statement.scm        |   6 +-
 .../standard/test/test-new-owner-report.scm        |  70 +++++++++++---
 gnucash/report/reports/standard/view-column.scm    |  39 +++-----
 gnucash/report/trep-engine.scm                     |  20 ++--
 23 files changed, 546 insertions(+), 477 deletions(-)



More information about the gnucash-changes mailing list