From f49b6690529d90cc31a67f77faf269921d28f35d Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Sat, 17 Jun 2023 20:19:29 +0100 Subject: [PATCH] Bug 798564 - GnuCash is slow when there are a lot of open registers When loading the book on startup, GnuCash takes a long time. This is partly because gnc_restore_all_state() is loading data for every register page and I have 35+ registers open. Register loads should be deferred on tabs that are not visible until those tabs are focused, like what already happens for restored open reports. Every time a change to a transaction is saved by navigating away from it, the UI freezes for almost a second. This is because gnc_split_register_move_cursor() calls gnc_gui_refresh_internal() which refreshes all the registers. Register refreshes should be deferred on tabs that are not visible until those tabs are focused again. Use focus information to suppress the last part of the refresh_handler() if the ledger isn't currently visible. When it becomes visible again, run the deferred refresh step. Don't immediately load or refresh the ledger (this will happen the first time it is focused instead). --- gnucash/gnome/gnc-plugin-page-register.c | 3 +- .../register/ledger-core/gnc-ledger-display.c | 38 +++++++++++++++++-- .../register/ledger-core/gnc-ledger-display.h | 3 ++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/gnucash/gnome/gnc-plugin-page-register.c b/gnucash/gnome/gnc-plugin-page-register.c index 5e045ee16a..7b6ab22111 100644 --- a/gnucash/gnome/gnc-plugin-page-register.c +++ b/gnucash/gnome/gnc-plugin-page-register.c @@ -1176,6 +1176,8 @@ gnc_plugin_page_register_focus (GncPluginPage* plugin_page, // set the sheet focus setting gnc_split_reg_set_sheet_focus (gsr, priv->page_focus); + + gnc_ledger_display_set_focus (priv->ledger, priv->page_focus); } static GtkWidget* @@ -1742,7 +1744,6 @@ gnc_plugin_page_register_recreate_page (GtkWidget* window, /* enable the refresh */ priv->enable_refresh = TRUE; - gnc_ledger_display_refresh (priv->ledger); LEAVE (" "); return page; } diff --git a/gnucash/register/ledger-core/gnc-ledger-display.c b/gnucash/register/ledger-core/gnc-ledger-display.c index 3b603d7230..bf0f48a183 100644 --- a/gnucash/register/ledger-core/gnc-ledger-display.c +++ b/gnucash/register/ledger-core/gnc-ledger-display.c @@ -66,6 +66,8 @@ struct gnc_ledger_display gboolean loading; gboolean use_double_line_default; + gboolean visible; /* focus */ + gboolean needs_refresh; GNCLedgerDisplayDestroy destroy; GNCLedgerDisplayGetParent get_parent; @@ -617,7 +619,15 @@ refresh_handler (GHashTable* changes, gpointer user_data) } } - gnc_ledger_display_refresh (ld); + if (ld->visible) + { + DEBUG ("immediate refresh because ledger is visible"); + gnc_ledger_display_refresh (ld); + } + else + { + ld->needs_refresh = TRUE; + } LEAVE (" "); } @@ -808,6 +818,8 @@ gnc_ledger_display_internal (Account* lead_account, Query* q, ld->query = NULL; ld->ld_type = ld_type; ld->loading = FALSE; + ld->visible = FALSE; + ld->needs_refresh = TRUE; ld->destroy = NULL; ld->get_parent = NULL; ld->user_data = NULL; @@ -837,10 +849,13 @@ gnc_ledger_display_internal (Account* lead_account, Query* q, gnc_split_register_set_data (ld->reg, ld, gnc_ledger_display_parent); - /* We don't need to recreate the query, so skip that refresh step by - * bypassing gnc_ledger_display_refresh(). + /* Must call this before gnc_table_realize_gui() gets called or all the + * combo boxes will be empty. Use an empty list of splits instad of running + * the query when we're not in focus yet. */ - gnc_ledger_display_refresh_internal (ld); + ld->loading = TRUE; + gnc_split_register_load (ld->reg, NULL, gnc_ledger_display_leader (ld)); + ld->loading = FALSE; return ld; } @@ -894,6 +909,7 @@ gnc_ledger_display_refresh_internal (GNCLedgerDisplay* ld) gnc_split_register_load (ld->reg, splits, gnc_ledger_display_leader (ld)); + ld->needs_refresh = FALSE; ld->loading = FALSE; } @@ -943,6 +959,20 @@ gnc_ledger_display_refresh (GNCLedgerDisplay* ld) LEAVE (" "); } +void gnc_ledger_display_set_focus (GNCLedgerDisplay* ld, gboolean focus) +{ + if (!ld) + return; + + ld->visible = focus; + + if (ld->visible && ld->needs_refresh) + { + DEBUG ("deferred refresh because ledger is now visible"); + gnc_ledger_display_refresh (ld); + } +} + void gnc_ledger_display_refresh_by_split_register (SplitRegister* reg) { diff --git a/gnucash/register/ledger-core/gnc-ledger-display.h b/gnucash/register/ledger-core/gnc-ledger-display.h index fea5078940..6f5faefdfe 100644 --- a/gnucash/register/ledger-core/gnc-ledger-display.h +++ b/gnucash/register/ledger-core/gnc-ledger-display.h @@ -121,6 +121,9 @@ GNCLedgerDisplay* gnc_ledger_display_find_by_query (Query* q); void gnc_ledger_display_refresh (GNCLedgerDisplay* ledger_display); void gnc_ledger_display_refresh_by_split_register (SplitRegister* reg); +/** Mark the ledger as being in focus (refresh immediately) or not. */ +void gnc_ledger_display_set_focus (GNCLedgerDisplay* ld, gboolean focus); + /** close the window */ void gnc_ledger_display_close (GNCLedgerDisplay* ledger_display);