Blame gcr/gcr-filter-collection.c

Packit b00eeb
/*
Packit b00eeb
 * gnome-keyring
Packit b00eeb
 *
Packit b00eeb
 * Copyright (C) 2010 Stefan Walter
Packit b00eeb
 *
Packit b00eeb
 * This program is free software; you can redistribute it and/or modify
Packit b00eeb
 * it under the terms of the GNU Lesser General Public License as
Packit b00eeb
 * published by the Free Software Foundation; either version 2.1 of
Packit b00eeb
 * the License, or (at your option) any later version.
Packit b00eeb
 *
Packit b00eeb
 * This program is distributed in the hope that it will be useful, but
Packit b00eeb
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b00eeb
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit b00eeb
 * Lesser General Public License for more details.
Packit b00eeb
 *
Packit b00eeb
 * You should have received a copy of the GNU Lesser General Public
Packit b00eeb
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
#include "config.h"
Packit b00eeb
Packit b00eeb
#include "gcr-collection.h"
Packit b00eeb
#include "gcr-filter-collection.h"
Packit b00eeb
Packit b00eeb
#include <string.h>
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * SECTION:gcr-filter-collection
Packit b00eeb
 * @title: GcrFilterCollection
Packit b00eeb
 * @short_description: A collection which filters a GcrCollection
Packit b00eeb
 *
Packit b00eeb
 * An implementation of #GcrCollection which filters objects from another
Packit b00eeb
 * underlying collection. Use gcr_filter_collection_new_with_callback()
Packit b00eeb
 * to create a new filter collection.
Packit b00eeb
 *
Packit b00eeb
 * The callback will determine the criteria for whether an object shows through
Packit b00eeb
 * the filter or not.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * GcrFilterCollection:
Packit b00eeb
 *
Packit b00eeb
 * A filter implementation of #GcrCollection.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * GcrFilterCollectionClass:
Packit b00eeb
 * @parent_class: the parent class
Packit b00eeb
 *
Packit b00eeb
 * The class for #GcrFilterCollection.
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * GcrFilterCollectionFunc:
Packit b00eeb
 * @object: object to filter
Packit b00eeb
 * @user_data: user data passed to the callback
Packit b00eeb
 *
Packit b00eeb
 * A function which is called by #GcrFilterCollection in order to determine
Packit b00eeb
 * whether an object should show through the filter or not.
Packit b00eeb
 *
Packit b00eeb
 * Returns: %TRUE if an object should be included in the filtered collection
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
enum {
Packit b00eeb
	PROP_0,
Packit b00eeb
	PROP_UNDERLYING
Packit b00eeb
};
Packit b00eeb
Packit b00eeb
struct _GcrFilterCollectionPrivate {
Packit b00eeb
	GHashTable *items;
Packit b00eeb
	GcrCollection *underlying;
Packit b00eeb
	GcrFilterCollectionFunc filter_func;
Packit b00eeb
	gpointer user_data;
Packit b00eeb
	GDestroyNotify destroy_func;
Packit b00eeb
};
Packit b00eeb
Packit b00eeb
static void       gcr_filter_collection_iface       (GcrCollectionIface *iface);
Packit b00eeb
Packit b00eeb
G_DEFINE_TYPE_WITH_CODE (GcrFilterCollection, gcr_filter_collection, G_TYPE_OBJECT,
Packit b00eeb
                         G_IMPLEMENT_INTERFACE (GCR_TYPE_COLLECTION, gcr_filter_collection_iface));
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
add_object (GcrFilterCollection *self,
Packit b00eeb
            GObject *object)
Packit b00eeb
{
Packit b00eeb
	g_assert (g_hash_table_lookup (self->pv->items, object) == NULL);
Packit b00eeb
	g_hash_table_insert (self->pv->items, g_object_ref (object), object);
Packit b00eeb
	gcr_collection_emit_added (GCR_COLLECTION (self), object);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
remove_object (GcrFilterCollection *self,
Packit b00eeb
               GObject *object)
Packit b00eeb
{
Packit b00eeb
	g_object_ref (object);
Packit b00eeb
	if (!g_hash_table_remove (self->pv->items, object))
Packit b00eeb
		g_assert_not_reached ();
Packit b00eeb
	gcr_collection_emit_removed (GCR_COLLECTION (self), object);
Packit b00eeb
	g_object_unref (object);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
filter_object (GcrFilterCollection *self,
Packit b00eeb
               GObject *object)
Packit b00eeb
{
Packit b00eeb
	gboolean match = TRUE;
Packit b00eeb
Packit b00eeb
	if (self->pv->filter_func)
Packit b00eeb
		match = (self->pv->filter_func) (object, self->pv->user_data);
Packit b00eeb
Packit b00eeb
	return match;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
on_collection_added (GcrCollection *collection,
Packit b00eeb
                     GObject *object,
Packit b00eeb
                     gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	GcrFilterCollection *self = GCR_FILTER_COLLECTION (user_data);
Packit b00eeb
	if (filter_object (self, object))
Packit b00eeb
		add_object (self, object);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
on_collection_removed (GcrCollection *collection,
Packit b00eeb
                       GObject *object,
Packit b00eeb
                       gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	GcrFilterCollection *self = GCR_FILTER_COLLECTION (user_data);
Packit b00eeb
	if (g_hash_table_lookup (self->pv->items, object))
Packit b00eeb
		remove_object (self, object);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_filter_collection_init (GcrFilterCollection *self)
Packit b00eeb
{
Packit b00eeb
	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_FILTER_COLLECTION, GcrFilterCollectionPrivate);
Packit b00eeb
	self->pv->items = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_filter_collection_set_property (GObject *obj,
Packit b00eeb
                                    guint property_id,
Packit b00eeb
                                    const GValue *value,
Packit b00eeb
                                    GParamSpec *pspec)
Packit b00eeb
{
Packit b00eeb
	GcrFilterCollection *self = GCR_FILTER_COLLECTION (obj);
Packit b00eeb
Packit b00eeb
	switch (property_id) {
Packit b00eeb
	case PROP_UNDERLYING:
Packit b00eeb
		g_return_if_fail (self->pv->underlying == NULL);
Packit b00eeb
		self->pv->underlying = g_value_dup_object (value);
Packit b00eeb
		g_return_if_fail (self->pv->underlying != NULL);
Packit b00eeb
		g_signal_connect (self->pv->underlying, "added",
Packit b00eeb
		                  G_CALLBACK (on_collection_added), self);
Packit b00eeb
		g_signal_connect (self->pv->underlying, "removed",
Packit b00eeb
		                  G_CALLBACK (on_collection_removed), self);
Packit b00eeb
		break;
Packit b00eeb
	default:
Packit b00eeb
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
Packit b00eeb
		break;
Packit b00eeb
	}
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_filter_collection_get_property (GObject *obj,
Packit b00eeb
                                    guint property_id,
Packit b00eeb
                                    GValue *value,
Packit b00eeb
                                    GParamSpec *pspec)
Packit b00eeb
{
Packit b00eeb
	GcrFilterCollection *self = GCR_FILTER_COLLECTION (obj);
Packit b00eeb
Packit b00eeb
	switch (property_id) {
Packit b00eeb
	case PROP_UNDERLYING:
Packit b00eeb
		g_value_set_object (value, gcr_filter_collection_get_underlying (self));
Packit b00eeb
		break;
Packit b00eeb
	default:
Packit b00eeb
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
Packit b00eeb
		break;
Packit b00eeb
	}
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_filter_collection_finalize (GObject *obj)
Packit b00eeb
{
Packit b00eeb
	GcrFilterCollection *self = GCR_FILTER_COLLECTION (obj);
Packit b00eeb
Packit b00eeb
	if (self->pv->underlying) {
Packit b00eeb
		g_signal_handlers_disconnect_by_func (self->pv->underlying,
Packit b00eeb
		                                      on_collection_added, self);
Packit b00eeb
		g_signal_handlers_disconnect_by_func (self->pv->underlying,
Packit b00eeb
		                                      on_collection_removed, self);
Packit b00eeb
		g_object_unref (self->pv->underlying);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (self->pv->destroy_func)
Packit b00eeb
		(self->pv->destroy_func) (self->pv->user_data);
Packit b00eeb
Packit b00eeb
	g_assert (self->pv->items);
Packit b00eeb
	g_hash_table_destroy (self->pv->items);
Packit b00eeb
	self->pv->items = NULL;
Packit b00eeb
Packit b00eeb
	G_OBJECT_CLASS (gcr_filter_collection_parent_class)->finalize (obj);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_filter_collection_class_init (GcrFilterCollectionClass *klass)
Packit b00eeb
{
Packit b00eeb
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit b00eeb
Packit b00eeb
	gobject_class->get_property = gcr_filter_collection_get_property;
Packit b00eeb
	gobject_class->set_property = gcr_filter_collection_set_property;
Packit b00eeb
	gobject_class->finalize = gcr_filter_collection_finalize;
Packit b00eeb
Packit b00eeb
	g_type_class_add_private (gobject_class, sizeof (GcrFilterCollectionPrivate));
Packit b00eeb
Packit b00eeb
	g_object_class_install_property (gobject_class, PROP_UNDERLYING,
Packit b00eeb
	            g_param_spec_object ("underlying", "Underlying", "Underlying collection",
Packit b00eeb
	                                 GCR_TYPE_COLLECTION, G_PARAM_STATIC_STRINGS |
Packit b00eeb
	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static guint
Packit b00eeb
gcr_filter_collection_get_length (GcrCollection *coll)
Packit b00eeb
{
Packit b00eeb
	GcrFilterCollection *self = GCR_FILTER_COLLECTION (coll);
Packit b00eeb
	return g_hash_table_size (self->pv->items);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static GList*
Packit b00eeb
gcr_filter_collection_get_objects (GcrCollection *coll)
Packit b00eeb
{
Packit b00eeb
	GcrFilterCollection *self = GCR_FILTER_COLLECTION (coll);
Packit b00eeb
	return g_hash_table_get_keys (self->pv->items);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
gcr_filter_collection_contains (GcrCollection *collection,
Packit b00eeb
                                GObject *object)
Packit b00eeb
{
Packit b00eeb
	GcrFilterCollection *self = GCR_FILTER_COLLECTION (collection);
Packit b00eeb
	return g_hash_table_lookup (self->pv->items, object) ? TRUE : FALSE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
gcr_filter_collection_iface (GcrCollectionIface *iface)
Packit b00eeb
{
Packit b00eeb
	iface->get_length = gcr_filter_collection_get_length;
Packit b00eeb
	iface->get_objects = gcr_filter_collection_get_objects;
Packit b00eeb
	iface->contains = gcr_filter_collection_contains;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * gcr_filter_collection_new_with_callback:
Packit b00eeb
 * @underlying: the underlying collection
Packit b00eeb
 * @callback: (allow-none): function to call for each object
Packit b00eeb
 * @user_data: data to pass to the callback
Packit b00eeb
 * @destroy_func: called for user_data when it is no longer needed
Packit b00eeb
 *
Packit b00eeb
 * Create a new #GcrFilterCollection.
Packit b00eeb
 *
Packit b00eeb
 * The callback should return %TRUE if an object should appear in the
Packit b00eeb
 * filtered collection.
Packit b00eeb
 *
Packit b00eeb
 * If a %NULL callback is set, then all underlynig objects will appear in the
Packit b00eeb
 * filtered collection.
Packit b00eeb
 *
Packit b00eeb
 * Returns: (transfer full) (type Gcr.FilterCollection): a newly allocated
Packit b00eeb
 *          filtered collection, which should be freed with g_object_unref()
Packit b00eeb
 */
Packit b00eeb
GcrCollection *
Packit b00eeb
gcr_filter_collection_new_with_callback (GcrCollection *underlying,
Packit b00eeb
                                         GcrFilterCollectionFunc callback,
Packit b00eeb
                                         gpointer user_data,
Packit b00eeb
                                         GDestroyNotify destroy_func)
Packit b00eeb
{
Packit b00eeb
	GcrCollection *collection;
Packit b00eeb
Packit b00eeb
	collection = g_object_new (GCR_TYPE_FILTER_COLLECTION,
Packit b00eeb
	                           "underlying", underlying,
Packit b00eeb
	                           NULL);
Packit b00eeb
	gcr_filter_collection_set_callback (GCR_FILTER_COLLECTION (collection),
Packit b00eeb
	                                    callback, user_data, destroy_func);
Packit b00eeb
Packit b00eeb
	return collection;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * gcr_filter_collection_set_callback:
Packit b00eeb
 * @self: a filter collection
Packit b00eeb
 * @callback: (allow-none): function to call for each object
Packit b00eeb
 * @user_data: data to pass to the callback
Packit b00eeb
 * @destroy_func: called for user_data when it is no longer needed
Packit b00eeb
 *
Packit b00eeb
 * Set the callback used to filter the objects in the underlying collection.
Packit b00eeb
 * The callback should return %TRUE if an object should appear in the
Packit b00eeb
 * filtered collection.
Packit b00eeb
 *
Packit b00eeb
 * If a %NULL callback is set, then all underlynig objects will appear in the
Packit b00eeb
 * filtered collection.
Packit b00eeb
 *
Packit b00eeb
 * This will refilter the collection.
Packit b00eeb
 */
Packit b00eeb
void
Packit b00eeb
gcr_filter_collection_set_callback (GcrFilterCollection *self,
Packit b00eeb
                                    GcrFilterCollectionFunc callback,
Packit b00eeb
                                    gpointer user_data,
Packit b00eeb
                                    GDestroyNotify destroy_func)
Packit b00eeb
{
Packit b00eeb
	g_return_if_fail (GCR_IS_FILTER_COLLECTION (self));
Packit b00eeb
Packit b00eeb
	if (self->pv->destroy_func)
Packit b00eeb
		(self->pv->destroy_func) (self->pv->user_data);
Packit b00eeb
	self->pv->filter_func = callback;
Packit b00eeb
	self->pv->user_data = user_data;
Packit b00eeb
	self->pv->destroy_func = destroy_func;
Packit b00eeb
Packit b00eeb
	gcr_filter_collection_refilter (self);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * gcr_filter_collection_refilter:
Packit b00eeb
 * @self: a filter collection
Packit b00eeb
 *
Packit b00eeb
 * Refilter all objects in the underlying collection. Call this function if
Packit b00eeb
 * the filter callback function changes its filtering criteria.
Packit b00eeb
 */
Packit b00eeb
void
Packit b00eeb
gcr_filter_collection_refilter (GcrFilterCollection *self)
Packit b00eeb
{
Packit b00eeb
	GList *objects = NULL;
Packit b00eeb
	GHashTable *snapshot;
Packit b00eeb
	GHashTableIter iter;
Packit b00eeb
	GObject *object;
Packit b00eeb
	gboolean have;
Packit b00eeb
	gboolean should;
Packit b00eeb
	GList *l;
Packit b00eeb
Packit b00eeb
	g_return_if_fail (GCR_IS_FILTER_COLLECTION (self));
Packit b00eeb
Packit b00eeb
	snapshot = g_hash_table_new (g_direct_hash, g_direct_equal);
Packit b00eeb
	g_hash_table_iter_init (&iter, self->pv->items);
Packit b00eeb
	while (g_hash_table_iter_next (&iter, (gpointer *)&object, NULL))
Packit b00eeb
		g_hash_table_insert (snapshot, object, object);
Packit b00eeb
Packit b00eeb
	if (self->pv->underlying)
Packit b00eeb
		objects = gcr_collection_get_objects (self->pv->underlying);
Packit b00eeb
Packit b00eeb
	for (l = objects; l != NULL; l = g_list_next (l)) {
Packit b00eeb
		have = g_hash_table_remove (snapshot, l->data);
Packit b00eeb
		should = filter_object (self, l->data);
Packit b00eeb
		if (have && !should)
Packit b00eeb
			remove_object (self, l->data);
Packit b00eeb
		else if (!have && should)
Packit b00eeb
			add_object (self, l->data);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	g_hash_table_iter_init (&iter, snapshot);
Packit b00eeb
	while (g_hash_table_iter_next (&iter, (gpointer *)&object, NULL))
Packit b00eeb
		remove_object (self, object);
Packit b00eeb
	g_hash_table_destroy (snapshot);
Packit b00eeb
Packit b00eeb
	g_list_free (objects);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
/**
Packit b00eeb
 * gcr_filter_collection_get_underlying:
Packit b00eeb
 * @self: a filter collection
Packit b00eeb
 *
Packit b00eeb
 * Get the collection that is being filtered by this filter collection.
Packit b00eeb
 *
Packit b00eeb
 * Returns: (transfer none): the underlying collection
Packit b00eeb
 */
Packit b00eeb
GcrCollection *
Packit b00eeb
gcr_filter_collection_get_underlying (GcrFilterCollection *self)
Packit b00eeb
{
Packit b00eeb
	g_return_val_if_fail (GCR_IS_FILTER_COLLECTION (self), NULL);
Packit b00eeb
	return self->pv->underlying;
Packit b00eeb
}