mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Bug 797453 - Chart of Accounts is slow to update / redraw
The chart of Accounts tree view gets redrawn often and gets the data from the model via gnc_tree_model_account_get_value(). As such the numeric values are constantly being recalculated on every draw which is not ideal. This change caches all the string values to a GHashTable for retrieval which should be quicker than before. Changes to an account will clear the cache for that account and its parents so values can be updated. Changes to the preference negative colour clears the whole cache.
This commit is contained in:
parent
e83938fdc2
commit
2ff91cf4e1
@ -97,6 +97,9 @@ typedef struct GncTreeModelAccountPrivate
|
||||
Account *root;
|
||||
gint event_handler_id;
|
||||
const gchar *negative_color;
|
||||
|
||||
GHashTable *account_values_hash;
|
||||
|
||||
} GncTreeModelAccountPrivate;
|
||||
|
||||
#define GNC_TREE_MODEL_ACCOUNT_GET_PRIVATE(o) \
|
||||
@ -134,9 +137,16 @@ gnc_tree_model_account_update_color (gpointer gsettings, gchar *key, gpointer us
|
||||
g_return_if_fail(GNC_IS_TREE_MODEL_ACCOUNT(user_data));
|
||||
model = user_data;
|
||||
priv = GNC_TREE_MODEL_ACCOUNT_GET_PRIVATE(model);
|
||||
|
||||
// destroy/recreate the cached acount value hash to force update
|
||||
g_hash_table_destroy (priv->account_values_hash);
|
||||
priv->account_values_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, g_free);
|
||||
|
||||
use_red = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
|
||||
priv->negative_color = use_red ? get_negative_color () : NULL;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* g_object required functions */
|
||||
/************************************************************/
|
||||
@ -182,6 +192,10 @@ gnc_tree_model_account_init (GncTreeModelAccount *model)
|
||||
priv->root = NULL;
|
||||
priv->negative_color = red ? get_negative_color () : NULL;
|
||||
|
||||
// create the account values cache hash
|
||||
priv->account_values_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, g_free);
|
||||
|
||||
gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED,
|
||||
gnc_tree_model_account_update_color,
|
||||
model);
|
||||
@ -230,6 +244,9 @@ gnc_tree_model_account_dispose (GObject *object)
|
||||
priv->event_handler_id = 0;
|
||||
}
|
||||
|
||||
// destroy the cached acount values
|
||||
g_hash_table_destroy (priv->account_values_hash);
|
||||
|
||||
gnc_prefs_remove_cb_by_func(GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED,
|
||||
gnc_tree_model_account_update_color,
|
||||
model);
|
||||
@ -561,6 +578,131 @@ gnc_tree_model_account_compute_period_balance(GncTreeModelAccount *model,
|
||||
return g_strdup(xaccPrintAmount(b3, gnc_account_print_info(acct, TRUE)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
row_changed_foreach_func (GtkTreeModel *model, GtkTreePath *path,
|
||||
GtkTreeIter *iter, gpointer user_data)
|
||||
{
|
||||
gtk_tree_model_row_changed (model, path, iter);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gnc_tree_model_account_clear_cache (GncTreeModelAccount *model)
|
||||
{
|
||||
if (model)
|
||||
{
|
||||
GncTreeModelAccountPrivate *priv = GNC_TREE_MODEL_ACCOUNT_GET_PRIVATE(model);
|
||||
|
||||
// destroy the cached acount values and recreate
|
||||
g_hash_table_destroy (priv->account_values_hash);
|
||||
priv->account_values_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, g_free);
|
||||
|
||||
gtk_tree_model_foreach (GTK_TREE_MODEL(model), row_changed_foreach_func, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clear_account_cached_values (GncTreeModelAccount *model, GHashTable *hash, Account *account)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
|
||||
|
||||
if (!account)
|
||||
return;
|
||||
|
||||
// make sure tree view sees the change
|
||||
if (gnc_tree_model_account_get_iter_from_account (model, account, &iter))
|
||||
{
|
||||
GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
|
||||
|
||||
gtk_tree_model_row_changed (GTK_TREE_MODEL(model), path, &iter);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
|
||||
|
||||
// loop over the columns and remove any found
|
||||
for (gint col = 0; col <= GNC_TREE_MODEL_ACCOUNT_NUM_COLUMNS; col++)
|
||||
{
|
||||
gchar *key = g_strdup_printf ("%s,%d", acct_guid_str, col);
|
||||
|
||||
g_hash_table_remove (hash, key);
|
||||
g_free (key);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_tree_model_account_clear_cached_values (GncTreeModelAccount *model, Account *account)
|
||||
{
|
||||
GncTreeModelAccountPrivate *priv = GNC_TREE_MODEL_ACCOUNT_GET_PRIVATE(model);
|
||||
Account *parent;
|
||||
|
||||
// no hash table or account, return
|
||||
if ((!priv->account_values_hash) || (!account))
|
||||
return;
|
||||
|
||||
clear_account_cached_values (model, priv->account_values_hash, account);
|
||||
parent = gnc_account_get_parent (account);
|
||||
|
||||
// clear also all parent accounts, this will update any balances/totals
|
||||
while (parent)
|
||||
{
|
||||
clear_account_cached_values (model, priv->account_values_hash, parent);
|
||||
parent = gnc_account_get_parent (parent);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnc_tree_model_account_get_cached_value (GncTreeModelAccount *model, Account *account,
|
||||
gint column, gchar **cached_string)
|
||||
{
|
||||
GncTreeModelAccountPrivate *priv = GNC_TREE_MODEL_ACCOUNT_GET_PRIVATE(model);
|
||||
gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
|
||||
gchar *key = NULL;
|
||||
gpointer value;
|
||||
gboolean found;
|
||||
|
||||
if ((!priv->account_values_hash) || (!account))
|
||||
return FALSE;
|
||||
|
||||
guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
|
||||
key = g_strdup_printf ("%s,%d", acct_guid_str, column);
|
||||
|
||||
found = g_hash_table_lookup_extended (priv->account_values_hash, key,
|
||||
NULL, &value);
|
||||
|
||||
if (found)
|
||||
*cached_string = g_strdup (value);
|
||||
|
||||
g_free (key);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_tree_model_account_set_cached_value (GncTreeModelAccount *model, Account *account,
|
||||
gint column, GValue *value)
|
||||
{
|
||||
GncTreeModelAccountPrivate *priv = GNC_TREE_MODEL_ACCOUNT_GET_PRIVATE(model);
|
||||
|
||||
if ((!priv->account_values_hash) || (!account))
|
||||
return;
|
||||
|
||||
// only interested in string values
|
||||
if (G_VALUE_HOLDS_STRING(value))
|
||||
{
|
||||
gchar acct_guid_str[GUID_ENCODING_LENGTH + 1];
|
||||
const gchar *str = g_value_get_string (value);
|
||||
gchar *key = NULL;
|
||||
|
||||
guid_to_string_buff (xaccAccountGetGUID (account), acct_guid_str);
|
||||
key = g_strdup_printf ("%s,%d", acct_guid_str, column);
|
||||
|
||||
g_hash_table_insert (priv->account_values_hash, key, g_strdup (str));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
|
||||
GtkTreeIter *iter,
|
||||
@ -572,6 +714,8 @@ gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
|
||||
Account *account;
|
||||
gboolean negative; /* used to set "deficit style" also known as red numbers */
|
||||
gchar *string;
|
||||
gchar *cached_string = NULL;
|
||||
|
||||
time64 last_date;
|
||||
|
||||
g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model));
|
||||
@ -585,6 +729,14 @@ gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
|
||||
account = (Account *) iter->user_data;
|
||||
priv = GNC_TREE_MODEL_ACCOUNT_GET_PRIVATE(model);
|
||||
|
||||
// lets see if the value is in the cache
|
||||
if (gnc_tree_model_account_get_cached_value (model, account, column, &cached_string))
|
||||
{
|
||||
g_value_init (value, G_TYPE_STRING);
|
||||
g_value_take_string (value, cached_string);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (column)
|
||||
{
|
||||
case GNC_TREE_MODEL_ACCOUNT_COL_NAME:
|
||||
@ -797,6 +949,10 @@ gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
// save the value to the account values cache
|
||||
gnc_tree_model_account_set_cached_value (model, account, column, value);
|
||||
|
||||
LEAVE(" ");
|
||||
}
|
||||
|
||||
@ -1272,6 +1428,10 @@ gnc_tree_model_account_event_handler (QofInstance *entity,
|
||||
priv = GNC_TREE_MODEL_ACCOUNT_GET_PRIVATE(model);
|
||||
|
||||
account = GNC_ACCOUNT(entity);
|
||||
|
||||
/* clear the cached model values for account */
|
||||
gnc_tree_model_account_clear_cached_values (model, account);
|
||||
|
||||
if (gnc_account_get_book(account) != priv->book)
|
||||
{
|
||||
LEAVE("not in this book");
|
||||
|
@ -122,6 +122,11 @@ typedef struct
|
||||
*/
|
||||
GType gnc_tree_model_account_get_type (void);
|
||||
|
||||
/** Clear the tree model account cached values.
|
||||
*
|
||||
* @param model A pointer to the account tree model.
|
||||
*/
|
||||
void gnc_tree_model_account_clear_cache (GncTreeModelAccount *model);
|
||||
|
||||
/** @name Account Tree Model Constructors
|
||||
@{ */
|
||||
|
@ -1076,6 +1076,17 @@ gnc_tree_view_account_count_children (GncTreeViewAccount *view,
|
||||
return num_children;
|
||||
}
|
||||
|
||||
void
|
||||
gnc_tree_view_account_clear_model_cache (GncTreeViewAccount *view)
|
||||
{
|
||||
GtkTreeModel *model, *f_model, *s_model;
|
||||
|
||||
s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
|
||||
f_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(s_model));
|
||||
model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(f_model));
|
||||
|
||||
gnc_tree_model_account_clear_cache (GNC_TREE_MODEL_ACCOUNT(model));
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* Account Tree View Filter Functions */
|
||||
|
@ -342,6 +342,13 @@ void gnc_tree_view_account_refilter (GncTreeViewAccount *view);
|
||||
gint gnc_tree_view_account_count_children (GncTreeViewAccount *view,
|
||||
Account *account);
|
||||
|
||||
/** This function clears the tree model account cache so the values will
|
||||
* be updated/refreshed.
|
||||
*
|
||||
* @param view A pointer to an account tree view.
|
||||
*
|
||||
*/
|
||||
void gnc_tree_view_account_clear_model_cache (GncTreeViewAccount *view);
|
||||
|
||||
|
||||
/** This function returns the account associated with the specified
|
||||
|
@ -205,6 +205,7 @@ gnc_prices_dialog_remove_clicked (GtkWidget *widget, gpointer data)
|
||||
g_list_foreach(price_list, (GFunc)remove_helper, pdb_dialog->price_db);
|
||||
}
|
||||
g_list_free(price_list);
|
||||
gnc_gui_refresh_all ();
|
||||
LEAVE(" ");
|
||||
}
|
||||
|
||||
@ -504,6 +505,7 @@ gnc_prices_dialog_remove_old_clicked (GtkWidget *widget, gpointer data)
|
||||
}
|
||||
g_list_free (comm_list);
|
||||
}
|
||||
gnc_gui_refresh_all ();
|
||||
gtk_widget_destroy (pdb_dialog->remove_dialog);
|
||||
LEAVE(" ");
|
||||
}
|
||||
|
@ -602,6 +602,8 @@ gnc_plugin_page_account_refresh_cb (GHashTable *changes, gpointer user_data)
|
||||
return;
|
||||
|
||||
priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
|
||||
|
||||
gnc_tree_view_account_clear_model_cache (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
|
||||
gtk_widget_queue_draw(priv->widget);
|
||||
}
|
||||
|
||||
@ -1671,6 +1673,8 @@ gnc_plugin_page_account_tree_cmd_refresh (GtkAction *action,
|
||||
g_return_if_fail(GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page));
|
||||
|
||||
priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
|
||||
|
||||
gnc_tree_view_account_clear_model_cache (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
|
||||
gtk_widget_queue_draw (priv->widget);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user