[gcr] gcr: Add GcrFilterCollection



commit fa095ebe11a59c9cda2a49651a0bec821e9fce30
Author: Stef Walter <stefw collabora co uk>
Date:   Wed Nov 9 13:57:20 2011 +0100

    gcr: Add GcrFilterCollection
    
     * Adds a new collection type, which wraps another collection and
       filters out certain objects

 docs/reference/gcr/gcr-sections.txt |   21 ++
 docs/reference/gcr/gcr.interfaces   |    1 +
 docs/reference/gcr/gcr.types        |    1 +
 gcr/Makefile.am                     |    2 +
 gcr/gcr-base.h                      |    1 +
 gcr/gcr-base.symbols                |    5 +
 gcr/gcr-filter-collection.c         |  381 +++++++++++++++++++++++++++++++++++
 gcr/gcr-filter-collection.h         |   76 +++++++
 gcr/tests/Makefile.am               |    1 +
 9 files changed, 489 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index f6ff36b..6d48882 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -298,6 +298,27 @@ GcrSimpleCollectionPrivate
 </SECTION>
 
 <SECTION>
+<FILE>gcr-filter-collection</FILE>
+GcrFilterCollection
+GcrFilterCollectionClass
+GcrFilterCollectionFunc
+gcr_filter_collection_new_with_callback
+gcr_filter_collection_set_callback
+gcr_filter_collection_get_underlying
+gcr_filter_collection_refilter
+<SUBSECTION Standard>
+gcr_filter_collection_get_type
+GCR_FILTER_COLLECTION
+GCR_FILTER_COLLECTION_CLASS
+GCR_FILTER_COLLECTION_GET_CLASS
+GCR_IS_FILTER_COLLECTION
+GCR_IS_FILTER_COLLECTION_CLASS
+GCR_TYPE_FILTER_COLLECTION
+<SUBSECTION Private>
+GcrFilterCollectionPrivate
+</SECTION>
+
+<SECTION>
 <FILE>gcr-union-collection</FILE>
 GcrUnionCollection
 GcrUnionCollectionClass
diff --git a/docs/reference/gcr/gcr.interfaces b/docs/reference/gcr/gcr.interfaces
index a88b4e2..1ef3691 100644
--- a/docs/reference/gcr/gcr.interfaces
+++ b/docs/reference/gcr/gcr.interfaces
@@ -21,6 +21,7 @@ GcrTreeSelector AtkImplementorIface GtkBuildable GtkScrollable
 GtkCellArea GtkCellLayout GtkBuildable
 GtkTreeViewColumn GtkCellLayout GtkBuildable
 GcrCollectionModel GtkTreeModel GtkTreeSortable
+GcrFilterCollection GcrCollection
 GcrKeyRenderer GcrRenderer
 GcrPkcs11Certificate GcrComparableIface GcrCertificate
 GcrSimpleCertificate GcrComparableIface GcrCertificate
diff --git a/docs/reference/gcr/gcr.types b/docs/reference/gcr/gcr.types
index fcd2dd9..4f2ac11 100644
--- a/docs/reference/gcr/gcr.types
+++ b/docs/reference/gcr/gcr.types
@@ -6,6 +6,7 @@ gcr_collection_get_type
 gcr_collection_model_get_type
 gcr_combo_selector_get_type
 gcr_comparable_get_type
+gcr_filter_collection_get_type
 gcr_import_button_get_type
 gcr_importer_get_type
 gcr_key_renderer_get_type
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index f1e6499..1d54fb8 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -21,6 +21,7 @@ HEADER_BASE_FILES = \
 	gcr-comparable.h \
 	gcr-deprecated-base.h \
 	gcr-fingerprint.h \
+	gcr-filter-collection.h \
 	gcr-icons.h \
 	gcr-importer.h \
 	gcr-import-interaction.h \
@@ -100,6 +101,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-collection.c gcr-collection.h \
 	gcr-comparable.c gcr-comparable.h \
 	gcr-debug.c gcr-debug.h \
+	gcr-filter-collection.c gcr-filter-collection.h \
 	gcr-fingerprint.c gcr-fingerprint.h \
 	gcr-gnupg-collection.c gcr-gnupg-collection.h \
 	gcr-gnupg-importer.c gcr-gnupg-importer.h \
diff --git a/gcr/gcr-base.h b/gcr/gcr-base.h
index 69133f1..753c381 100644
--- a/gcr/gcr-base.h
+++ b/gcr/gcr-base.h
@@ -38,6 +38,7 @@
 #include "gcr-certificate-chain.h"
 #include "gcr-deprecated-base.h"
 #include "gcr-enum-types-base.h"
+#include "gcr-filter-collection.h"
 #include "gcr-icons.h"
 #include "gcr-importer.h"
 #include "gcr-library.h"
diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols
index fc777ca..7d0be13 100644
--- a/gcr/gcr-base.symbols
+++ b/gcr/gcr-base.symbols
@@ -53,6 +53,11 @@ gcr_data_error_get_domain
 gcr_data_error_get_type
 gcr_data_format_get_type
 gcr_error_get_domain
+gcr_filter_collection_get_type
+gcr_filter_collection_get_underlying
+gcr_filter_collection_new_with_callback
+gcr_filter_collection_refilter
+gcr_filter_collection_set_callback
 gcr_fingerprint_from_attributes
 gcr_fingerprint_from_certificate_public_key
 gcr_fingerprint_from_subject_public_key_info
diff --git a/gcr/gcr-filter-collection.c b/gcr/gcr-filter-collection.c
new file mode 100644
index 0000000..3a29dfb
--- /dev/null
+++ b/gcr/gcr-filter-collection.c
@@ -0,0 +1,381 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2010 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-collection.h"
+#include "gcr-filter-collection.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gcr-filter-collection
+ * @title: GcrFilterCollection
+ * @short_description: A collection which filters a GcrCollection
+ *
+ * An implementation of #GcrCollection which filters objects from another
+ * underlying collection. Use gcr_filter_collection_new_with_callback()
+ * to create a new filter collection.
+ *
+ * The callback will determine the criteria for whether an object shows through
+ * the filter or not.
+ */
+
+/**
+ * GcrFilterCollection:
+ *
+ * A filter implementation of #GcrCollection.
+ */
+
+/**
+ * GcrFilterCollectionClass:
+ * @parent_class: the parent class
+ *
+ * The class for #GcrFilterCollection.
+ */
+
+/**
+ * GcrFilterCollectionFunc:
+ * @object: object to filter
+ * @user_data: user data passed to the callback
+ *
+ * A function which is called by #GcrFilterCollection in order to determine
+ * whether an object should show through the filter or not.
+ *
+ * Returns: %TRUE if an object should be included in the filtered collection
+ */
+
+enum {
+	PROP_0,
+	PROP_UNDERLYING
+};
+
+struct _GcrFilterCollectionPrivate {
+	GHashTable *items;
+	GcrCollection *underlying;
+	GcrFilterCollectionFunc filter_func;
+	gpointer user_data;
+	GDestroyNotify destroy_func;
+};
+
+static void       gcr_filter_collection_iface       (GcrCollectionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrFilterCollection, gcr_filter_collection, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_COLLECTION, gcr_filter_collection_iface));
+
+static void
+add_object (GcrFilterCollection *self,
+            GObject *object)
+{
+	g_assert (g_hash_table_lookup (self->pv->items, object) == NULL);
+	g_hash_table_insert (self->pv->items, g_object_ref (object), object);
+	gcr_collection_emit_added (GCR_COLLECTION (self), object);
+}
+
+static void
+remove_object (GcrFilterCollection *self,
+               GObject *object)
+{
+	g_object_ref (object);
+	if (!g_hash_table_remove (self->pv->items, object))
+		g_assert_not_reached ();
+	gcr_collection_emit_removed (GCR_COLLECTION (self), object);
+	g_object_unref (object);
+}
+
+static gboolean
+filter_object (GcrFilterCollection *self,
+               GObject *object)
+{
+	gboolean match = TRUE;
+
+	if (self->pv->filter_func)
+		match = (self->pv->filter_func) (object, self->pv->user_data);
+
+	return match;
+}
+
+static void
+on_collection_added (GcrCollection *collection,
+                     GObject *object,
+                     gpointer user_data)
+{
+	GcrFilterCollection *self = GCR_FILTER_COLLECTION (user_data);
+	if (filter_object (self, object))
+		add_object (self, object);
+}
+
+static void
+on_collection_removed (GcrCollection *collection,
+                       GObject *object,
+                       gpointer user_data)
+{
+	GcrFilterCollection *self = GCR_FILTER_COLLECTION (user_data);
+	if (g_hash_table_lookup (self->pv->items, object))
+		remove_object (self, object);
+}
+
+static void
+gcr_filter_collection_init (GcrFilterCollection *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_FILTER_COLLECTION, GcrFilterCollectionPrivate);
+	self->pv->items = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
+}
+
+static void
+gcr_filter_collection_set_property (GObject *obj,
+                                    guint property_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+	GcrFilterCollection *self = GCR_FILTER_COLLECTION (obj);
+
+	switch (property_id) {
+	case PROP_UNDERLYING:
+		g_return_if_fail (self->pv->underlying == NULL);
+		self->pv->underlying = g_value_dup_object (value);
+		g_return_if_fail (self->pv->underlying != NULL);
+		g_signal_connect (self->pv->underlying, "added",
+		                  G_CALLBACK (on_collection_added), self);
+		g_signal_connect (self->pv->underlying, "removed",
+		                  G_CALLBACK (on_collection_removed), self);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_filter_collection_get_property (GObject *obj,
+                                    guint property_id,
+                                    GValue *value,
+                                    GParamSpec *pspec)
+{
+	GcrFilterCollection *self = GCR_FILTER_COLLECTION (obj);
+
+	switch (property_id) {
+	case PROP_UNDERLYING:
+		g_value_set_object (value, gcr_filter_collection_get_underlying (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_filter_collection_finalize (GObject *obj)
+{
+	GcrFilterCollection *self = GCR_FILTER_COLLECTION (obj);
+
+	if (self->pv->underlying) {
+		g_signal_handlers_disconnect_by_func (self->pv->underlying,
+		                                      on_collection_added, self);
+		g_signal_handlers_disconnect_by_func (self->pv->underlying,
+		                                      on_collection_removed, self);
+		g_object_unref (self->pv->underlying);
+	}
+
+	if (self->pv->destroy_func)
+		(self->pv->destroy_func) (self->pv->user_data);
+
+	g_assert (self->pv->items);
+	g_hash_table_destroy (self->pv->items);
+	self->pv->items = NULL;
+
+	G_OBJECT_CLASS (gcr_filter_collection_parent_class)->finalize (obj);
+}
+
+static void
+gcr_filter_collection_class_init (GcrFilterCollectionClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->get_property = gcr_filter_collection_get_property;
+	gobject_class->set_property = gcr_filter_collection_set_property;
+	gobject_class->finalize = gcr_filter_collection_finalize;
+
+	g_type_class_add_private (gobject_class, sizeof (GcrFilterCollectionPrivate));
+
+	g_object_class_install_property (gobject_class, PROP_UNDERLYING,
+	            g_param_spec_object ("underlying", "Underlying", "Underlying collection",
+	                                 GCR_TYPE_COLLECTION, G_PARAM_STATIC_STRINGS |
+	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static guint
+gcr_filter_collection_get_length (GcrCollection *coll)
+{
+	GcrFilterCollection *self = GCR_FILTER_COLLECTION (coll);
+	return g_hash_table_size (self->pv->items);
+}
+
+static GList*
+gcr_filter_collection_get_objects (GcrCollection *coll)
+{
+	GcrFilterCollection *self = GCR_FILTER_COLLECTION (coll);
+	return g_hash_table_get_keys (self->pv->items);
+}
+
+static gboolean
+gcr_filter_collection_contains (GcrCollection *collection,
+                                GObject *object)
+{
+	GcrFilterCollection *self = GCR_FILTER_COLLECTION (collection);
+	return g_hash_table_lookup (self->pv->items, object) ? TRUE : FALSE;
+}
+
+static void
+gcr_filter_collection_iface (GcrCollectionIface *iface)
+{
+	iface->get_length = gcr_filter_collection_get_length;
+	iface->get_objects = gcr_filter_collection_get_objects;
+	iface->contains = gcr_filter_collection_contains;
+}
+
+/**
+ * gcr_filter_collection_new_with_callback:
+ * @underlying: the underlying collection
+ * @callback: (allow-none): function to call for each object
+ * @user_data: data to pass to the callback
+ * @destroy_func: called for user_data when it is no longer needed
+ *
+ * Create a new #GcrFilterCollection.
+ *
+ * The callback should return %TRUE if an object should appear in the
+ * filtered collection.
+ *
+ * If a %NULL callback is set, then all underlynig objects will appear in the
+ * filtered collection.
+ *
+ * Returns: (transfer full) (type Gcr.FilterCollection): a newly allocated
+ *          filtered collection, which should be freed with g_object_unref()
+ */
+GcrCollection *
+gcr_filter_collection_new_with_callback (GcrCollection *underlying,
+                                         GcrFilterCollectionFunc callback,
+                                         gpointer user_data,
+                                         GDestroyNotify destroy_func)
+{
+	GcrCollection *collection;
+
+	collection = g_object_new (GCR_TYPE_FILTER_COLLECTION,
+	                           "underlying", underlying,
+	                           NULL);
+	gcr_filter_collection_set_callback (GCR_FILTER_COLLECTION (collection),
+	                                    callback, user_data, destroy_func);
+
+	return collection;
+}
+
+/**
+ * gcr_filter_collection_set_callback:
+ * @self: a filter collection
+ * @callback: (allow-none): function to call for each object
+ * @user_data: data to pass to the callback
+ * @destroy_func: called for user_data when it is no longer needed
+ *
+ * Set the callback used to filter the objects in the underlying collection.
+ * The callback should return %TRUE if an object should appear in the
+ * filtered collection.
+ *
+ * If a %NULL callback is set, then all underlynig objects will appear in the
+ * filtered collection.
+ *
+ * This will refilter the collection.
+ */
+void
+gcr_filter_collection_set_callback (GcrFilterCollection *self,
+                                    GcrFilterCollectionFunc callback,
+                                    gpointer user_data,
+                                    GDestroyNotify destroy_func)
+{
+	g_return_if_fail (GCR_IS_FILTER_COLLECTION (self));
+
+	if (self->pv->destroy_func)
+		(self->pv->destroy_func) (self->pv->user_data);
+	self->pv->filter_func = callback;
+	self->pv->user_data = user_data;
+	self->pv->destroy_func = destroy_func;
+
+	gcr_filter_collection_refilter (self);
+}
+
+/**
+ * gcr_filter_collection_refilter:
+ * @self: a filter collection
+ *
+ * Refilter all objects in the underlying collection. Call this function if
+ * the filter callback function changes its filtering criteria.
+ */
+void
+gcr_filter_collection_refilter (GcrFilterCollection *self)
+{
+	GList *objects = NULL;
+	GHashTable *snapshot;
+	GHashTableIter iter;
+	GObject *object;
+	gboolean have;
+	gboolean should;
+	GList *l;
+
+	g_return_if_fail (GCR_IS_FILTER_COLLECTION (self));
+
+	snapshot = g_hash_table_new (g_direct_hash, g_direct_equal);
+	g_hash_table_iter_init (&iter, self->pv->items);
+	while (g_hash_table_iter_next (&iter, (gpointer *)&object, NULL))
+		g_hash_table_insert (snapshot, object, object);
+
+	if (self->pv->underlying)
+		objects = gcr_collection_get_objects (self->pv->underlying);
+
+	for (l = objects; l != NULL; l = g_list_next (l)) {
+		have = g_hash_table_remove (snapshot, l->data);
+		should = filter_object (self, l->data);
+		if (have && !should)
+			remove_object (self, l->data);
+		else if (!have && should)
+			add_object (self, l->data);
+	}
+
+	g_hash_table_iter_init (&iter, snapshot);
+	while (g_hash_table_iter_next (&iter, (gpointer *)&object, NULL))
+		remove_object (self, object);
+	g_hash_table_destroy (snapshot);
+
+	g_list_free (objects);
+}
+
+/**
+ * gcr_filter_collection_get_underlying:
+ * @self: a filter collection
+ *
+ * Get the collection that is being filtered by this filter collection.
+ *
+ * Returns: (transfer none): the underlying collection
+ */
+GcrCollection *
+gcr_filter_collection_get_underlying (GcrFilterCollection *self)
+{
+	g_return_val_if_fail (GCR_IS_FILTER_COLLECTION (self), NULL);
+	return self->pv->underlying;
+}
diff --git a/gcr/gcr-filter-collection.h b/gcr/gcr-filter-collection.h
new file mode 100644
index 0000000..8679e79
--- /dev/null
+++ b/gcr/gcr-filter-collection.h
@@ -0,0 +1,76 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __GCR_FILTER_COLLECTION_H__
+#define __GCR_FILTER_COLLECTION_H__
+
+#include "gcr-collection.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_FILTER_COLLECTION               (gcr_filter_collection_get_type ())
+#define GCR_FILTER_COLLECTION(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_FILTER_COLLECTION, GcrFilterCollection))
+#define GCR_FILTER_COLLECTION_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_FILTER_COLLECTION, GcrFilterCollectionClass))
+#define GCR_IS_FILTER_COLLECTION(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_FILTER_COLLECTION))
+#define GCR_IS_FILTER_COLLECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_FILTER_COLLECTION))
+#define GCR_FILTER_COLLECTION_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_FILTER_COLLECTION, GcrFilterCollectionClass))
+
+typedef struct _GcrFilterCollection GcrFilterCollection;
+typedef struct _GcrFilterCollectionClass GcrFilterCollectionClass;
+typedef struct _GcrFilterCollectionPrivate GcrFilterCollectionPrivate;
+
+struct _GcrFilterCollection {
+	GObject parent;
+
+	/*< private >*/
+	GcrFilterCollectionPrivate *pv;
+};
+
+struct _GcrFilterCollectionClass {
+	GObjectClass parent_class;
+};
+
+GType               gcr_filter_collection_get_type                (void);
+
+typedef gboolean    (* GcrFilterCollectionFunc)                   (GObject *object,
+                                                                   gpointer user_data);
+
+GcrCollection *     gcr_filter_collection_new_with_callback       (GcrCollection *underlying,
+                                                                   GcrFilterCollectionFunc callback,
+                                                                   gpointer user_data,
+                                                                   GDestroyNotify destroy_func);
+
+void                gcr_filter_collection_set_callback            (GcrFilterCollection *self,
+                                                                   GcrFilterCollectionFunc callback,
+                                                                   gpointer user_data,
+                                                                   GDestroyNotify destroy_func);
+
+void                gcr_filter_collection_refilter                (GcrFilterCollection *self);
+
+GcrCollection *     gcr_filter_collection_get_underlying          (GcrFilterCollection *self);
+
+G_END_DECLS
+
+#endif /* __GCR_FILTER_COLLECTION_H__ */
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index 5872ff9..ab725a4 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -21,6 +21,7 @@ LDADD = \
 
 TEST_PROGS = \
 	test-util \
+	test-filter-collection \
 	test-secret-exchange \
 	test-simple-certificate \
 	test-certificate \



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]