[EMailLabelListStore] Avoid circular dependency in a tag cache
The store has its own cache of tags for quicker lookup, but this cache used GtkTreeRowReference-s, which reference the model, thus it made circular dependency and the store was never freed. Using place GtkTreeIter pointer in the cache works too, one only needs to do more often manual updates of it.
This commit is contained in:
@ -44,7 +44,7 @@ enum {
|
||||
static guint signals[LAST_SIGNAL];
|
||||
|
||||
struct _EMailLabelListStorePrivate {
|
||||
GHashTable *tag_index;
|
||||
GHashTable *tag_index; /* gchar *tag_name ~> GtkTreeIter * */
|
||||
GSettings *mail_settings;
|
||||
guint idle_changed_id;
|
||||
};
|
||||
@ -109,6 +109,29 @@ mail_label_list_store_encode_label (const gchar *label_name,
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
mail_label_list_store_fill_tag_index (EMailLabelListStore *store)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
g_hash_table_remove_all (store->priv->tag_index);
|
||||
|
||||
model = GTK_TREE_MODEL (store);
|
||||
if (!gtk_tree_model_get_iter_first (model, &iter))
|
||||
return;
|
||||
|
||||
do {
|
||||
gchar *tag;
|
||||
|
||||
tag = e_mail_label_list_store_get_tag (store, &iter);
|
||||
if (!tag)
|
||||
continue;
|
||||
|
||||
g_hash_table_insert (store->priv->tag_index, tag, gtk_tree_iter_copy (&iter));
|
||||
} while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
|
||||
static void
|
||||
mail_label_list_store_ensure_defaults (EMailLabelListStore *store)
|
||||
{
|
||||
@ -266,6 +289,8 @@ labels_model_changed_idle_cb (gpointer user_data)
|
||||
store->priv->mail_settings,
|
||||
labels_settings_changed_cb, store);
|
||||
|
||||
mail_label_list_store_fill_tag_index (store);
|
||||
|
||||
g_signal_emit (store, signals[CHANGED], 0);
|
||||
|
||||
return FALSE;
|
||||
@ -276,6 +301,8 @@ labels_model_changed_cb (EMailLabelListStore *store)
|
||||
{
|
||||
g_return_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store));
|
||||
|
||||
mail_label_list_store_fill_tag_index (store);
|
||||
|
||||
/* do the actual save and signal emission on idle,
|
||||
* to accumulate as many changes as possible */
|
||||
if (!store->priv->idle_changed_id)
|
||||
@ -327,6 +354,8 @@ labels_settings_changed_cb (GSettings *settings,
|
||||
if (g_hash_table_size (changed_labels) == 0) {
|
||||
g_hash_table_destroy (changed_labels);
|
||||
g_strfreev (strv);
|
||||
|
||||
mail_label_list_store_fill_tag_index (store);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -349,6 +378,8 @@ labels_settings_changed_cb (GSettings *settings,
|
||||
|
||||
g_signal_handlers_unblock_by_func (
|
||||
store, labels_model_changed_cb, store);
|
||||
|
||||
mail_label_list_store_fill_tag_index (store);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -386,32 +417,6 @@ mail_label_list_store_constructed (GObject *object)
|
||||
G_OBJECT_CLASS (e_mail_label_list_store_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
mail_label_list_store_row_inserted (GtkTreeModel *model,
|
||||
GtkTreePath *path,
|
||||
GtkTreeIter *iter)
|
||||
{
|
||||
EMailLabelListStore *store;
|
||||
GtkTreeRowReference *reference;
|
||||
GHashTable *tag_index;
|
||||
gchar *tag;
|
||||
|
||||
store = E_MAIL_LABEL_LIST_STORE (model);
|
||||
tag = e_mail_label_list_store_get_tag (store, iter);
|
||||
g_return_if_fail (tag != NULL);
|
||||
|
||||
/* Hash table takes ownership of both tag and reference. */
|
||||
tag_index = store->priv->tag_index;
|
||||
reference = gtk_tree_row_reference_new (model, path);
|
||||
g_hash_table_insert (tag_index, tag, reference);
|
||||
|
||||
/* We don't need to do anything special for row deletion.
|
||||
* The reference will automatically become invalid (that's
|
||||
* why we're storing references and not iterators or paths),
|
||||
* so garbage collection is not important. We'll do it
|
||||
* lazily. */
|
||||
}
|
||||
|
||||
static void
|
||||
e_mail_label_list_store_class_init (EMailLabelListStoreClass *class)
|
||||
{
|
||||
@ -439,7 +444,6 @@ e_mail_label_list_store_class_init (EMailLabelListStoreClass *class)
|
||||
static void
|
||||
e_mail_label_list_store_interface_init (GtkTreeModelIface *iface)
|
||||
{
|
||||
iface->row_inserted = mail_label_list_store_row_inserted;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -451,7 +455,7 @@ e_mail_label_list_store_init (EMailLabelListStore *store)
|
||||
tag_index = g_hash_table_new_full (
|
||||
g_str_hash, g_str_equal,
|
||||
(GDestroyNotify) g_free,
|
||||
(GDestroyNotify) gtk_tree_row_reference_free);
|
||||
(GDestroyNotify) gtk_tree_iter_free);
|
||||
|
||||
store->priv = E_MAIL_LABEL_LIST_STORE_GET_PRIVATE (store);
|
||||
store->priv->tag_index = tag_index;
|
||||
@ -658,31 +662,18 @@ e_mail_label_list_store_lookup (EMailLabelListStore *store,
|
||||
const gchar *tag,
|
||||
GtkTreeIter *iter)
|
||||
{
|
||||
GtkTreeRowReference *reference;
|
||||
GHashTable *tag_index;
|
||||
GtkTreeModel *model;
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter *stored_iter;
|
||||
|
||||
g_return_val_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store), FALSE);
|
||||
g_return_val_if_fail (tag != NULL, FALSE);
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
|
||||
tag_index = store->priv->tag_index;
|
||||
reference = g_hash_table_lookup (tag_index, tag);
|
||||
stored_iter = g_hash_table_lookup (store->priv->tag_index, tag);
|
||||
|
||||
if (reference == NULL)
|
||||
if (!stored_iter)
|
||||
return FALSE;
|
||||
|
||||
if (!gtk_tree_row_reference_valid (reference)) {
|
||||
/* Garbage collect the dead reference. */
|
||||
g_hash_table_remove (tag_index, tag);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
model = gtk_tree_row_reference_get_model (reference);
|
||||
path = gtk_tree_row_reference_get_path (reference);
|
||||
gtk_tree_model_get_iter (model, iter, path);
|
||||
gtk_tree_path_free (path);
|
||||
*iter = *stored_iter;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user