From 1214fc8fc351f1861add2b6adbc6b1334bced1f7 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 17 Aug 2010 18:55:12 +0100 Subject: Aggregate group expansion/contraction in EmpathyIndividualView The idle handler for expanding/contracting groups was getting scheduled many hundreds of times more than necessary when initialising the contact list. This aggregates expansion/contraction of group rows into a single idle handler call which expands or contracts a number of rows at once. --- libempathy-gtk/empathy-individual-view.c | 132 ++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 36 deletions(-) (limited to 'libempathy-gtk') diff --git a/libempathy-gtk/empathy-individual-view.c b/libempathy-gtk/empathy-individual-view.c index e3d5fa301..1c073fd60 100644 --- a/libempathy-gtk/empathy-individual-view.c +++ b/libempathy-gtk/empathy-individual-view.c @@ -74,6 +74,10 @@ typedef struct GtkTreeModelFilter *filter; GtkWidget *search_widget; + + guint expand_groups_idle_handler; + /* owned string (group name) -> bool (whether to expand/contract) */ + GHashTable *expand_groups; } EmpathyIndividualViewPriv; typedef struct @@ -1298,41 +1302,72 @@ individual_view_search_show_cb (EmpathyLiveSearch *search, individual_view_row_expand_or_collapse_cb, GINT_TO_POINTER (TRUE)); } -typedef struct { - EmpathyIndividualView *view; - GtkTreeRowReference *row_ref; - gboolean expand; -} ExpandData; - static gboolean -individual_view_expand_idle_cb (gpointer user_data) +expand_idle_foreach_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyIndividualView *self) { - ExpandData *data = user_data; - GtkTreePath *path; + EmpathyIndividualViewPriv *priv; + gboolean is_group; + gpointer should_expand; + gchar *name; - path = gtk_tree_row_reference_get_path (data->row_ref); - if (path == NULL) - goto done; + /* We only want groups */ + if (gtk_tree_path_get_depth (path) > 1) + return FALSE; + + gtk_tree_model_get (model, iter, + EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, &is_group, + EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name, + -1); - g_signal_handlers_block_by_func (data->view, - individual_view_row_expand_or_collapse_cb, - GINT_TO_POINTER (data->expand)); + if (is_group == FALSE) + { + g_free (name); + return FALSE; + } - if (data->expand) - gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view), path, FALSE); - else - gtk_tree_view_collapse_row (GTK_TREE_VIEW (data->view), path); + priv = GET_PRIV (self); - gtk_tree_path_free (path); + if (g_hash_table_lookup_extended (priv->expand_groups, name, NULL, + &should_expand) == TRUE) + { + if (GPOINTER_TO_INT (should_expand) == TRUE) + gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE); + else + gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path); - g_signal_handlers_unblock_by_func (data->view, - individual_view_row_expand_or_collapse_cb, - GINT_TO_POINTER (data->expand)); + g_hash_table_remove (priv->expand_groups, name); + } -done: - g_object_unref (data->view); - gtk_tree_row_reference_free (data->row_ref); - g_slice_free (ExpandData, data); + g_free (name); + + return FALSE; +} + +static gboolean +individual_view_expand_idle_cb (EmpathyIndividualView *self) +{ + EmpathyIndividualViewPriv *priv = GET_PRIV (self); + + DEBUG ("individual_view_expand_idle_cb"); + + g_signal_handlers_block_by_func (self, + individual_view_row_expand_or_collapse_cb, GINT_TO_POINTER (TRUE)); + g_signal_handlers_block_by_func (self, + individual_view_row_expand_or_collapse_cb, GINT_TO_POINTER (FALSE)); + + gtk_tree_model_foreach (GTK_TREE_MODEL (priv->filter), + (GtkTreeModelForeachFunc) expand_idle_foreach_cb, self); + + g_signal_handlers_unblock_by_func (self, + individual_view_row_expand_or_collapse_cb, GINT_TO_POINTER (FALSE)); + g_signal_handlers_unblock_by_func (self, + individual_view_row_expand_or_collapse_cb, GINT_TO_POINTER (TRUE)); + + g_object_unref (self); + priv->expand_groups_idle_handler = 0; return FALSE; } @@ -1344,9 +1379,9 @@ individual_view_row_has_child_toggled_cb (GtkTreeModel *model, EmpathyIndividualView *view) { EmpathyIndividualViewPriv *priv = GET_PRIV (view); - gboolean is_group = FALSE; + gboolean should_expand, is_group = FALSE; gchar *name = NULL; - ExpandData *data; + gpointer will_expand; gtk_tree_model_get (model, iter, EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, &is_group, @@ -1359,19 +1394,30 @@ individual_view_row_has_child_toggled_cb (GtkTreeModel *model, return; } - data = g_slice_new0 (ExpandData); - data->view = g_object_ref (view); - data->row_ref = gtk_tree_row_reference_new (model, path); - data->expand = - (priv->view_features & + should_expand = (priv->view_features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_SAVE) == 0 || (priv->search_widget != NULL && gtk_widget_get_visible (priv->search_widget)) || empathy_contact_group_get_expanded (name); /* FIXME: It doesn't work to call gtk_tree_view_expand_row () from within - * gtk_tree_model_filter_refilter () */ - g_idle_add (individual_view_expand_idle_cb, data); + * gtk_tree_model_filter_refilter (). We add the rows to expand/contract to + * a hash table, and expand or contract them as appropriate all at once in + * an idle handler which iterates over all the group rows. */ + if (g_hash_table_lookup_extended (priv->expand_groups, name, NULL, + &will_expand) == FALSE && + GPOINTER_TO_INT (will_expand) != should_expand) + { + g_hash_table_insert (priv->expand_groups, g_strdup (name), + GINT_TO_POINTER (should_expand)); + + if (priv->expand_groups_idle_handler == 0) + { + priv->expand_groups_idle_handler = + g_idle_add ((GSourceFunc) individual_view_expand_idle_cb, + g_object_ref (view)); + } + } g_free (name); } @@ -1743,6 +1789,16 @@ individual_view_dispose (GObject *object) G_OBJECT_CLASS (empathy_individual_view_parent_class)->dispose (object); } +static void +individual_view_finalize (GObject *object) +{ + EmpathyIndividualViewPriv *priv = GET_PRIV (object); + + g_hash_table_destroy (priv->expand_groups); + + G_OBJECT_CLASS (empathy_individual_view_parent_class)->finalize (object); +} + static void individual_view_get_property (GObject *object, guint param_id, @@ -1812,6 +1868,7 @@ empathy_individual_view_class_init (EmpathyIndividualViewClass *klass) object_class->constructed = individual_view_constructed; object_class->dispose = individual_view_dispose; + object_class->finalize = individual_view_finalize; object_class->get_property = individual_view_get_property; object_class->set_property = individual_view_set_property; @@ -1877,6 +1934,9 @@ empathy_individual_view_init (EmpathyIndividualView *view) /* Get saved group states. */ empathy_contact_groups_get_all (); + priv->expand_groups = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, NULL); + gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), empathy_individual_store_row_separator_func, NULL, NULL); -- cgit v1.2.3