gtk2/gtk/tests/gtktreemodelrefcount.c
Benjamin Otte 6df7562570 tests: Remove deleted rows from cache
Otherwise the GNode pointer address that we use for indexing might get
reused for a new node and then we don't start out with a pristine
refcount.
2012-03-19 02:26:16 +01:00

314 lines
9.2 KiB
C

/* gtktreemodelrefcount.c
* Copyright (C) 2011 Kristian Rietveld <kris@gtk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtktreemodelrefcount.h"
/* The purpose of this GtkTreeModel is to keep record of the reference count
* of each node. The reference count does not effect the functioning of
* the model in any way. Because this model is a subclass of GtkTreeStore,
* the GtkTreeStore API should be used to add to and remove nodes from
* this model. We depend on the iter format of GtkTreeStore, which means
* that this model needs to be revised in case the iter format of
* GtkTreeStore is modified. Currently, we make use of the fact that
* the value stored in the user_data field is unique for each node.
*/
struct _GtkTreeModelRefCountPrivate
{
GHashTable *node_hash;
};
typedef struct
{
int ref_count;
}
NodeInfo;
static void gtk_tree_model_ref_count_tree_model_init (GtkTreeModelIface *iface);
static void gtk_tree_model_ref_count_finalize (GObject *object);
static NodeInfo *node_info_new (void);
static void node_info_free (NodeInfo *info);
/* GtkTreeModel interface */
static void gtk_tree_model_ref_count_ref_node (GtkTreeModel *model,
GtkTreeIter *iter);
static void gtk_tree_model_ref_count_unref_node (GtkTreeModel *model,
GtkTreeIter *iter);
G_DEFINE_TYPE_WITH_CODE (GtkTreeModelRefCount, gtk_tree_model_ref_count, GTK_TYPE_TREE_STORE,
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
gtk_tree_model_ref_count_tree_model_init))
static void
row_removed (GtkTreeModelRefCount *ref_model,
GtkTreePath *path)
{
GHashTableIter iter;
GtkTreeIter tree_iter;
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ref_model), &tree_iter))
{
g_hash_table_remove_all (ref_model->priv->node_hash);
return;
}
g_hash_table_iter_init (&iter, ref_model->priv->node_hash);
while (g_hash_table_iter_next (&iter, &tree_iter.user_data, NULL))
{
if (!gtk_tree_store_iter_is_valid (GTK_TREE_STORE (ref_model), &tree_iter))
g_hash_table_iter_remove (&iter);
}
}
static void
gtk_tree_model_ref_count_init (GtkTreeModelRefCount *ref_model)
{
ref_model->priv = G_TYPE_INSTANCE_GET_PRIVATE (ref_model,
GTK_TYPE_TREE_MODEL_REF_COUNT,
GtkTreeModelRefCountPrivate);
ref_model->priv->node_hash = g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
(GDestroyNotify)node_info_free);
g_signal_connect (ref_model, "row-deleted", G_CALLBACK (row_removed), NULL);
}
static void
gtk_tree_model_ref_count_class_init (GtkTreeModelRefCountClass *ref_model_class)
{
GObjectClass *object_class;
object_class = (GObjectClass *) ref_model_class;
object_class->finalize = gtk_tree_model_ref_count_finalize;
g_type_class_add_private (object_class, sizeof (GtkTreeModelRefCountPrivate));
}
static void
gtk_tree_model_ref_count_tree_model_init (GtkTreeModelIface *iface)
{
iface->ref_node = gtk_tree_model_ref_count_ref_node;
iface->unref_node = gtk_tree_model_ref_count_unref_node;
}
static void
gtk_tree_model_ref_count_finalize (GObject *object)
{
GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (object);
if (ref_model->priv->node_hash)
{
g_hash_table_destroy (ref_model->priv->node_hash);
ref_model->priv->node_hash = NULL;
}
G_OBJECT_CLASS (gtk_tree_model_ref_count_parent_class)->finalize (object);
}
static NodeInfo *
node_info_new (void)
{
NodeInfo *info = g_slice_new (NodeInfo);
info->ref_count = 0;
return info;
}
static void
node_info_free (NodeInfo *info)
{
g_slice_free (NodeInfo, info);
}
static void
gtk_tree_model_ref_count_ref_node (GtkTreeModel *model,
GtkTreeIter *iter)
{
NodeInfo *info;
GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (model);
info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data);
if (!info)
{
info = node_info_new ();
g_hash_table_insert (ref_model->priv->node_hash, iter->user_data, info);
}
info->ref_count++;
}
static void
gtk_tree_model_ref_count_unref_node (GtkTreeModel *model,
GtkTreeIter *iter)
{
NodeInfo *info;
GtkTreeModelRefCount *ref_model = GTK_TREE_MODEL_REF_COUNT (model);
info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data);
g_assert (info != NULL);
g_assert (info->ref_count > 0);
info->ref_count--;
}
GtkTreeModel *
gtk_tree_model_ref_count_new (void)
{
GtkTreeModel *retval;
retval = g_object_new (gtk_tree_model_ref_count_get_type (), NULL);
return retval;
}
static void
dump_iter (GtkTreeModelRefCount *ref_model,
GtkTreeIter *iter)
{
gchar *path_str;
NodeInfo *info;
GtkTreePath *path;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (ref_model), iter);
path_str = gtk_tree_path_to_string (path);
gtk_tree_path_free (path);
info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data);
if (!info)
g_print ("%-16s ref_count=0\n", path_str);
else
g_print ("%-16s ref_count=%d\n", path_str, info->ref_count);
g_free (path_str);
}
static void
gtk_tree_model_ref_count_dump_recurse (GtkTreeModelRefCount *ref_model,
GtkTreeIter *iter)
{
do
{
GtkTreeIter child;
dump_iter (ref_model, iter);
if (gtk_tree_model_iter_children (GTK_TREE_MODEL (ref_model),
&child, iter))
gtk_tree_model_ref_count_dump_recurse (ref_model, &child);
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ref_model), iter));
}
void
gtk_tree_model_ref_count_dump (GtkTreeModelRefCount *ref_model)
{
GtkTreeIter iter;
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ref_model), &iter))
return;
gtk_tree_model_ref_count_dump_recurse (ref_model, &iter);
}
static gboolean
check_iter (GtkTreeModelRefCount *ref_model,
GtkTreeIter *iter,
gint expected_ref_count,
gboolean may_assert)
{
NodeInfo *info;
if (may_assert)
g_assert (gtk_tree_store_iter_is_valid (GTK_TREE_STORE (ref_model), iter));
info = g_hash_table_lookup (ref_model->priv->node_hash, iter->user_data);
if (!info)
{
if (expected_ref_count == 0)
return TRUE;
else
{
if (may_assert)
g_error ("Expected ref count %d, but node has never been referenced.\n", expected_ref_count);
return FALSE;
}
}
if (may_assert)
{
if (expected_ref_count == 0)
g_assert_cmpint (expected_ref_count, ==, info->ref_count);
else
g_assert_cmpint (expected_ref_count, <=, info->ref_count);
}
return expected_ref_count == info->ref_count;
}
gboolean
gtk_tree_model_ref_count_check_level (GtkTreeModelRefCount *ref_model,
GtkTreeIter *parent,
gint expected_ref_count,
gboolean recurse,
gboolean may_assert)
{
GtkTreeIter iter;
if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (ref_model),
&iter, parent))
return TRUE;
do
{
if (!check_iter (ref_model, &iter, expected_ref_count, may_assert))
return FALSE;
if (recurse &&
gtk_tree_model_iter_has_child (GTK_TREE_MODEL (ref_model), &iter))
{
if (!gtk_tree_model_ref_count_check_level (ref_model, &iter,
expected_ref_count,
recurse, may_assert))
return FALSE;
}
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ref_model), &iter));
return TRUE;
}
gboolean
gtk_tree_model_ref_count_check_node (GtkTreeModelRefCount *ref_model,
GtkTreeIter *iter,
gint expected_ref_count,
gboolean may_assert)
{
return check_iter (ref_model, iter, expected_ref_count, may_assert);
}