[evolution-data-server] Introduce ESourceRegistryWatcher
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Introduce ESourceRegistryWatcher
- Date: Fri, 9 Jun 2017 10:52:32 +0000 (UTC)
commit 11a2fdf3b3902f616e2b319fe4ed7147b93f8760
Author: Milan Crha <mcrha redhat com>
Date: Fri Jun 9 12:47:13 2017 +0200
Introduce ESourceRegistryWatcher
It can be tricky to watch for changes in ESourceRegistry, especially
when the user wants to know only about a specific subset of ESources-s.
This new object makes things significantly easier and reliable, without
a need for code duplication. It is currently used in the 'Birthdays & Anniversaries'
calendar, but it can be reused elsewhere too (like in the evolution-alarm-notify).
.../evolution-data-server-docs.sgml.in | 1 +
.../backends/contacts/e-cal-backend-contacts.c | 107 ++--
src/libedataserver/CMakeLists.txt | 2 +
src/libedataserver/e-source-registry-watcher.c | 571 ++++++++++++++++++++
src/libedataserver/e-source-registry-watcher.h | 89 +++
src/libedataserver/libedataserver.h | 1 +
6 files changed, 719 insertions(+), 52 deletions(-)
---
diff --git a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
index 0b981ba..79e3c97 100644
--- a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
+++ b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
@@ -234,6 +234,7 @@
<xi:include href="xml/e-sexp.xml"/>
<xi:include href="xml/e-soup-session.xml"/>
<xi:include href="xml/e-soup-ssl-trust.xml"/>
+ <xi:include href="xml/e-source-registry-watcher.xml"/>
<xi:include href="xml/e-time-utils.xml"/>
<xi:include href="xml/e-uid.xml"/>
<xi:include href="xml/e-webdav-discover.xml"/>
diff --git a/src/calendar/backends/contacts/e-cal-backend-contacts.c
b/src/calendar/backends/contacts/e-cal-backend-contacts.c
index d2b5708..d203695 100644
--- a/src/calendar/backends/contacts/e-cal-backend-contacts.c
+++ b/src/calendar/backends/contacts/e-cal-backend-contacts.c
@@ -68,6 +68,8 @@ struct _ECalBackendContactsPrivate {
gboolean alarm_enabled;
gint alarm_interval;
CalUnits alarm_units;
+
+ ESourceRegistryWatcher *registry_watcher;
};
typedef struct _BookRecord {
@@ -526,65 +528,54 @@ contact_record_cb (gpointer key,
}
}
-static void
-source_added_cb (ESourceRegistry *registry,
- ESource *source,
- ECalBackendContacts *cbc)
+static gboolean
+ecb_contacts_watcher_filter_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ gpointer user_data)
{
ESourceContacts *extension;
- const gchar *extension_name;
-
- /* We're only interested in address books. */
- extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
- if (!e_source_has_extension (source, extension_name))
- return;
- extension_name = E_SOURCE_EXTENSION_CONTACTS_BACKEND;
- extension = e_source_get_extension (source, extension_name);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
- if (extension == NULL)
- return;
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CONTACTS_BACKEND);
- if (e_source_contacts_get_include_me (extension))
- create_book_record (cbc, source);
+ return extension && e_source_contacts_get_include_me (extension);
}
static void
-source_removed_cb (ESourceRegistry *registry,
- ESource *source,
- ECalBackendContacts *cbc)
+ecb_contacts_watcher_appeared_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ gpointer user_data)
{
- cal_backend_contacts_remove_book_record (cbc, source);
+ ECalBackendContacts *cbcontacts = user_data;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_CAL_BACKEND_CONTACTS (cbcontacts));
+
+ create_book_record (cbcontacts, source);
}
-static gboolean
-cal_backend_contacts_load_sources (gpointer user_data)
+static void
+ecb_contacts_watcher_disappeared_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ gpointer user_data)
{
- ESourceRegistry *registry;
- ECalBackend *backend;
- GList *list, *link;
- const gchar *extension_name;
+ ECalBackendContacts *cbcontacts = user_data;
- backend = E_CAL_BACKEND (user_data);
- registry = e_cal_backend_get_registry (backend);
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_CAL_BACKEND_CONTACTS (cbcontacts));
- /* Query all address book sources from the registry. */
+ cal_backend_contacts_remove_book_record (cbcontacts, source);
+}
- extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
- list = e_source_registry_list_sources (registry, extension_name);
- for (link = list; link != NULL; link = g_list_next (link))
- source_added_cb (
- registry, E_SOURCE (link->data),
- E_CAL_BACKEND_CONTACTS (backend));
- g_list_free_full (list, (GDestroyNotify) g_object_unref);
+static gboolean
+cal_backend_contacts_load_sources (gpointer user_data)
+{
+ ECalBackendContacts *cbcontacts = user_data;
- g_signal_connect (
- registry, "source-added",
- G_CALLBACK (source_added_cb), backend);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CONTACTS (cbcontacts), FALSE);
- g_signal_connect (
- registry, "source-removed",
- G_CALLBACK (source_removed_cb), backend);
+ e_source_registry_watcher_reclaim (cbcontacts->priv->registry_watcher);
return FALSE;
}
@@ -1326,10 +1317,9 @@ e_cal_backend_contacts_finalize (GObject *object)
static void
e_cal_backend_contacts_dispose (GObject *object)
{
- ESourceRegistry *registry;
+ ECalBackendContacts *cbcontacts = E_CAL_BACKEND_CONTACTS (object);
- registry = e_cal_backend_get_registry (E_CAL_BACKEND (object));
- g_signal_handlers_disconnect_by_data (registry, object);
+ g_clear_object (&cbcontacts->priv->registry_watcher);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_cal_backend_contacts_parent_class)->dispose (object);
@@ -1338,16 +1328,29 @@ e_cal_backend_contacts_dispose (GObject *object)
static void
e_cal_backend_contacts_constructed (GObject *object)
{
- /* Load address book sources from an idle callback
- * to avoid deadlocking e_data_factory_ref_backend(). */
- g_idle_add_full (
- G_PRIORITY_DEFAULT_IDLE,
- cal_backend_contacts_load_sources,
- g_object_ref (object),
- (GDestroyNotify) g_object_unref);
+ ECalBackendContacts *cbcontacts = E_CAL_BACKEND_CONTACTS (object);
+ ESourceRegistry *registry;
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_cal_backend_contacts_parent_class)->constructed (object);
+
+ registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbcontacts));
+
+ cbcontacts->priv->registry_watcher = e_source_registry_watcher_new (registry,
E_SOURCE_EXTENSION_ADDRESS_BOOK);
+
+ g_signal_connect (cbcontacts->priv->registry_watcher, "filter",
+ G_CALLBACK (ecb_contacts_watcher_filter_cb), cbcontacts);
+
+ g_signal_connect (cbcontacts->priv->registry_watcher, "appeared",
+ G_CALLBACK (ecb_contacts_watcher_appeared_cb), cbcontacts);
+
+ g_signal_connect (cbcontacts->priv->registry_watcher, "disappeared",
+ G_CALLBACK (ecb_contacts_watcher_disappeared_cb), cbcontacts);
+
+ /* Load address book sources from an idle callback
+ * to avoid deadlocking e_data_factory_ref_backend(). */
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, cal_backend_contacts_load_sources,
+ g_object_ref (object), g_object_unref);
}
/* Object initialization function for the contacts backend */
diff --git a/src/libedataserver/CMakeLists.txt b/src/libedataserver/CMakeLists.txt
index fc523ea..d562e84 100644
--- a/src/libedataserver/CMakeLists.txt
+++ b/src/libedataserver/CMakeLists.txt
@@ -103,6 +103,7 @@ set(SOURCES
e-source-proxy.c
e-source-refresh.c
e-source-registry.c
+ e-source-registry-watcher.c
e-source-resource.c
e-source-revision-guards.c
e-source-security.c
@@ -186,6 +187,7 @@ set(HEADERS
e-source-proxy.h
e-source-refresh.h
e-source-registry.h
+ e-source-registry-watcher.h
e-source-resource.h
e-source-revision-guards.h
e-source-security.h
diff --git a/src/libedataserver/e-source-registry-watcher.c b/src/libedataserver/e-source-registry-watcher.c
new file mode 100644
index 0000000..2413077
--- /dev/null
+++ b/src/libedataserver/e-source-registry-watcher.c
@@ -0,0 +1,571 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: e-source-registry-watcher
+ * @include: libedataserver/libedataserver.h
+ * @short_description: Watch changes in #ESource-s
+ *
+ * #ESourceRegistryWatcher watches for changes in an #ESourceRegistry
+ * and notifies about newly added and enabled #ESource instances, the same
+ * as about removed or disabled. The amount of notifications can be filtered
+ * with #ESourceRegistryWatcher::filter signal.
+ *
+ * The watcher listens only for changes, thus it is not pre-populated after
+ * its creation. That's because the owner usually wants to subscribe to
+ * the #ESourceRegistryWatcher::filter, #ESourceRegistryWatcher::appeared
+ * and #ESourceRegistryWatcher::disappeared signals. The owner should
+ * call e_source_registry_watcher_reclaim() when it has all the needed
+ * signal handlers connected.
+ **/
+
+#include "evolution-data-server-config.h"
+
+#include "e-source-registry.h"
+#include "e-source.h"
+#include "e-source-collection.h"
+
+#include "e-source-registry-watcher.h"
+
+struct _ESourceRegistryWatcherPrivate {
+ ESourceRegistry *registry;
+ gchar *extension_name;
+
+ GHashTable *known_uids; /* gchar * UID ~> ESource */
+
+ GRecMutex lock;
+
+ gulong added_id;
+ gulong enabled_id;
+ gulong disabled_id;
+ gulong removed_id;
+ gulong changed_id;
+};
+
+G_DEFINE_TYPE (ESourceRegistryWatcher, e_source_registry_watcher, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_EXTENSION_NAME,
+ PROP_REGISTRY
+};
+
+enum {
+ FILTER,
+ APPEARED,
+ DISAPPEARED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static gboolean
+source_registry_watcher_try_remove (ESourceRegistryWatcher *watcher,
+ ESource *source)
+{
+ gboolean removed;
+
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ g_rec_mutex_lock (&watcher->priv->lock);
+ removed = g_hash_table_remove (watcher->priv->known_uids, e_source_get_uid (source));
+ g_rec_mutex_unlock (&watcher->priv->lock);
+
+ if (removed)
+ g_signal_emit (watcher, signals[DISAPPEARED], 0, source);
+
+ return removed;
+}
+
+static gboolean
+source_registry_watcher_try_add (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ gboolean with_remove_check,
+ gboolean skip_appeared_emit)
+{
+ gboolean can_include = TRUE;
+ gboolean added = FALSE;
+ gchar *uid;
+
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ uid = e_source_dup_uid (source);
+ if (!uid)
+ return FALSE;
+
+ g_signal_emit (watcher, signals[FILTER], 0, source, &can_include);
+
+ if (!can_include) {
+ if (with_remove_check)
+ source_registry_watcher_try_remove (watcher, source);
+
+ g_free (uid);
+ return FALSE;
+ }
+
+ g_rec_mutex_lock (&watcher->priv->lock);
+
+ if (!g_hash_table_contains (watcher->priv->known_uids, uid)) {
+ g_hash_table_insert (watcher->priv->known_uids, uid, g_object_ref (source));
+ added = TRUE;
+ } else {
+ g_free (uid);
+ }
+
+ g_rec_mutex_unlock (&watcher->priv->lock);
+
+ if (added && !skip_appeared_emit)
+ g_signal_emit (watcher, signals[APPEARED], 0, source);
+
+ return added;
+}
+
+static void
+source_registry_watcher_reclaim_internal (ESourceRegistryWatcher *watcher,
+ gboolean merge_like)
+{
+ GHashTable *old_known_uids = NULL;
+ GList *enabled, *link;
+
+ g_return_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher));
+
+ g_rec_mutex_lock (&watcher->priv->lock);
+ if (merge_like) {
+ old_known_uids = watcher->priv->known_uids;
+ watcher->priv->known_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
g_object_unref);
+ } else {
+ g_hash_table_remove_all (watcher->priv->known_uids);
+ }
+
+ enabled = e_source_registry_list_enabled (watcher->priv->registry, watcher->priv->extension_name);
+ for (link = enabled; link; link = g_list_next (link)) {
+ ESource *source = link->data;
+ gboolean skip_appeared_emit;
+
+ skip_appeared_emit = old_known_uids && g_hash_table_contains (old_known_uids,
e_source_get_uid (source));
+
+ if (source_registry_watcher_try_add (watcher, source, FALSE, skip_appeared_emit) &&
old_known_uids)
+ g_hash_table_remove (old_known_uids, e_source_get_uid (source));
+ }
+ g_list_free_full (enabled, g_object_unref);
+
+ g_rec_mutex_unlock (&watcher->priv->lock);
+
+ if (old_known_uids) {
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, old_known_uids);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ ESource *source = value;
+
+ g_signal_emit (watcher, signals[DISAPPEARED], 0, source);
+ }
+
+ g_hash_table_destroy (old_known_uids);
+ }
+}
+
+static void
+source_registry_watcher_source_added_or_enabled_cb (ESourceRegistry *registry,
+ ESource *source,
+ gpointer user_data)
+{
+ ESourceRegistryWatcher *watcher = user_data;
+
+ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher));
+
+ if (e_source_registry_check_enabled (registry, source))
+ source_registry_watcher_try_add (watcher, source, TRUE, FALSE);
+}
+
+static void
+source_registry_watcher_source_removed_or_disabled_cb (ESourceRegistry *registry,
+ ESource *source,
+ gpointer user_data)
+{
+ ESourceRegistryWatcher *watcher = user_data;
+
+ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher));
+
+ source_registry_watcher_try_remove (watcher, source);
+}
+
+static void
+source_registry_watcher_source_changed_cb (ESourceRegistry *registry,
+ ESource *source,
+ gpointer user_data)
+{
+ ESourceRegistryWatcher *watcher = user_data;
+
+ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher));
+
+ if (!watcher->priv->extension_name ||
+ e_source_has_extension (source, watcher->priv->extension_name)) {
+ if (e_source_registry_check_enabled (registry, source))
+ source_registry_watcher_try_add (watcher, source, TRUE, FALSE);
+ else
+ source_registry_watcher_try_remove (watcher, source);
+ }
+}
+
+static void
+source_registry_watcher_set_registry (ESourceRegistryWatcher *watcher,
+ ESourceRegistry *registry)
+{
+ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+ g_return_if_fail (watcher->priv->registry == NULL);
+
+ watcher->priv->registry = g_object_ref (registry);
+}
+
+static void
+source_registry_watcher_set_extension_name (ESourceRegistryWatcher *watcher,
+ const gchar *extension_name)
+{
+ if (g_strcmp0 (watcher->priv->extension_name, extension_name) != 0) {
+ g_free (watcher->priv->extension_name);
+ watcher->priv->extension_name = g_strdup (extension_name);
+ }
+}
+
+static void
+source_registry_watcher_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EXTENSION_NAME:
+ source_registry_watcher_set_extension_name (
+ E_SOURCE_REGISTRY_WATCHER (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_REGISTRY:
+ source_registry_watcher_set_registry (
+ E_SOURCE_REGISTRY_WATCHER (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_registry_watcher_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EXTENSION_NAME:
+ g_value_set_string (
+ value,
+ e_source_registry_watcher_get_extension_name (
+ E_SOURCE_REGISTRY_WATCHER (object)));
+ return;
+
+ case PROP_REGISTRY:
+ g_value_set_object (
+ value,
+ e_source_registry_watcher_get_registry (
+ E_SOURCE_REGISTRY_WATCHER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_registry_watcher_constructed (GObject *object)
+{
+ ESourceRegistryWatcher *watcher = E_SOURCE_REGISTRY_WATCHER (object);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_source_registry_watcher_parent_class)->constructed (object);
+
+ g_return_if_fail (watcher->priv->registry != NULL);
+
+ watcher->priv->added_id = g_signal_connect (watcher->priv->registry, "source-added",
+ G_CALLBACK (source_registry_watcher_source_added_or_enabled_cb), watcher);
+
+ watcher->priv->enabled_id = g_signal_connect (watcher->priv->registry, "source-enabled",
+ G_CALLBACK (source_registry_watcher_source_added_or_enabled_cb), watcher);
+
+ watcher->priv->disabled_id = g_signal_connect (watcher->priv->registry, "source-disabled",
+ G_CALLBACK (source_registry_watcher_source_removed_or_disabled_cb), watcher);
+
+ watcher->priv->removed_id = g_signal_connect (watcher->priv->registry, "source-removed",
+ G_CALLBACK (source_registry_watcher_source_removed_or_disabled_cb), watcher);
+
+ watcher->priv->changed_id = g_signal_connect (watcher->priv->registry, "source-changed",
+ G_CALLBACK (source_registry_watcher_source_changed_cb), watcher);
+}
+
+static void
+source_registry_watcher_dispose (GObject *object)
+{
+ ESourceRegistryWatcher *watcher = E_SOURCE_REGISTRY_WATCHER (object);
+
+#define unset_handler(x) G_STMT_START { \
+ if (x) { \
+ g_signal_handler_disconnect (watcher->priv->registry, x); \
+ x = 0; \
+ } } G_STMT_END
+
+ unset_handler (watcher->priv->added_id);
+ unset_handler (watcher->priv->enabled_id);
+ unset_handler (watcher->priv->disabled_id);
+ unset_handler (watcher->priv->removed_id);
+ unset_handler (watcher->priv->changed_id);
+
+#undef unset_handler
+
+ g_clear_object (&watcher->priv->registry);
+
+ g_hash_table_remove_all (watcher->priv->known_uids);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_source_registry_watcher_parent_class)->dispose (object);
+}
+
+static void
+source_registry_watcher_finalize (GObject *object)
+{
+ ESourceRegistryWatcher *watcher = E_SOURCE_REGISTRY_WATCHER (object);
+
+ g_hash_table_destroy (watcher->priv->known_uids);
+ g_free (watcher->priv->extension_name);
+ g_rec_mutex_clear (&watcher->priv->lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_source_registry_watcher_parent_class)->finalize (object);
+}
+
+static void
+e_source_registry_watcher_class_init (ESourceRegistryWatcherClass *klass)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (klass, sizeof (ESourceRegistryWatcherPrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = source_registry_watcher_set_property;
+ object_class->get_property = source_registry_watcher_get_property;
+ object_class->constructed = source_registry_watcher_constructed;
+ object_class->dispose = source_registry_watcher_dispose;
+ object_class->finalize = source_registry_watcher_finalize;
+
+ /**
+ * ESourceRegistryWatcher:extension-name:
+ *
+ * Optional extension name, to consider sources with only.
+ * It can be %NULL, to check for all sources. This is
+ * a complementary filter to #ESourceRegistryWatcher::filter
+ * signal.
+ *
+ * Since: 3.26
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_EXTENSION_NAME,
+ g_param_spec_string (
+ "extension-name",
+ "ExtensionName",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * ESourceRegistryWatcher:registry:
+ *
+ * The #ESourceRegistry manages #ESource instances.
+ *
+ * Since: 3.26
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_REGISTRY,
+ g_param_spec_object (
+ "registry",
+ "Registry",
+ "Data source registry",
+ E_TYPE_SOURCE_REGISTRY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * ESourceRegistryWatcher::filter:
+ * @watcher: the #ESourceRegistryWatcher that received the signal
+ * @source: the #ESource to filter
+ *
+ * A filter signal which verifies whether the @source can be considered
+ * for inclusion in the watcher or not. If none is set then all the sources
+ * are included.
+ *
+ * Returns: %TRUE, when the @source can be included, %FALSE otherwise.
+ *
+ * Since: 3.26
+ **/
+ signals[FILTER] = g_signal_new (
+ "filter",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (ESourceRegistryWatcherClass, filter),
+ NULL, NULL, NULL,
+ G_TYPE_BOOLEAN, 1,
+ E_TYPE_SOURCE);
+
+ /**
+ * ESourceRegistryWatcher::appeared:
+ * @watcher: the #ESourceRegistryWatcher that received the signal
+ * @source: the #ESource which appeared
+ *
+ * A signal emitted when the @source is enabled or added and it had been
+ * considered for inclusion with the @ESourceRegistryWatcher::filter signal.
+ *
+ * Since: 3.26
+ **/
+ signals[APPEARED] = g_signal_new (
+ "appeared",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ESourceRegistryWatcherClass, appeared),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ E_TYPE_SOURCE);
+
+ /**
+ * ESourceRegistryWatcher::disappeared:
+ * @watcher: the #ESourceRegistryWatcher that received the signal
+ * @source: the #ESource which disappeared
+ *
+ * A signal emitted when the @source is disabled or removed and it had been
+ * considered for inclusion with the @ESourceRegistryWatcher::filter signal
+ * earlier.
+ *
+ * Since: 3.26
+ **/
+ signals[DISAPPEARED] = g_signal_new (
+ "disappeared",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ESourceRegistryWatcherClass, disappeared),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ E_TYPE_SOURCE);
+}
+
+static void
+e_source_registry_watcher_init (ESourceRegistryWatcher *watcher)
+{
+ watcher->priv = G_TYPE_INSTANCE_GET_PRIVATE (watcher, E_TYPE_SOURCE_REGISTRY_WATCHER,
ESourceRegistryWatcherPrivate);
+
+ g_rec_mutex_init (&watcher->priv->lock);
+
+ watcher->priv->known_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+}
+
+/**
+ * e_source_registry_watcher_new:
+ * @registry: an #ESourceRegistry
+ * @extension_name: (nullable): optional extension name to filter sources with, or %NULL
+ *
+ * Creates a new #ESourceRegistryWatcher instance.
+ *
+ * The @extension_name can be used as a complementary filter
+ * to #ESourceRegistryWatcher::filter signal.
+ *
+ * Returns: (transfer full): an #ESourceRegistryWatcher
+ *
+ * Since: 3.26
+ **/
+ESourceRegistryWatcher *
+e_source_registry_watcher_new (ESourceRegistry *registry,
+ const gchar *extension_name)
+{
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+ return g_object_new (E_TYPE_SOURCE_REGISTRY_WATCHER,
+ "registry", registry,
+ "extension-name", extension_name,
+ NULL);
+}
+
+/**
+ * e_source_registry_watcher_get_registry:
+ * @watcher: an #ESourceRegistryWatcher
+ *
+ * Returns the #ESourceRegistry passed to e_source_registry_watcher_new().
+ *
+ * Returns: (transfer none): an #ESourceRegistry
+ *
+ * Since: 3.26
+ **/
+ESourceRegistry *
+e_source_registry_watcher_get_registry (ESourceRegistryWatcher *watcher)
+{
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher), NULL);
+
+ return watcher->priv->registry;
+}
+
+/**
+ * e_source_registry_watcher_get_extension_name:
+ * @watcher: an #ESourceRegistryWatcher
+ *
+ * Returns: (nullable): The extension name passed to e_source_registry_watcher_new().
+ *
+ * Since: 3.26
+ **/
+const gchar *
+e_source_registry_watcher_get_extension_name (ESourceRegistryWatcher *watcher)
+{
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher), NULL);
+
+ return watcher->priv->extension_name;
+}
+
+/**
+ * e_source_registry_watcher_reclaim:
+ * @watcher: an #ESourceRegistryWatcher
+ *
+ * Reclaims all available sources satisfying the #ESourceRegistryWatcher::filter
+ * signal. It doesn't notify about disappeared sources, it notifies only
+ * on those appeared.
+ *
+ * Since: 3.26
+ **/
+void
+e_source_registry_watcher_reclaim (ESourceRegistryWatcher *watcher)
+{
+ g_return_if_fail (E_IS_SOURCE_REGISTRY_WATCHER (watcher));
+
+ source_registry_watcher_reclaim_internal (watcher, FALSE);
+}
diff --git a/src/libedataserver/e-source-registry-watcher.h b/src/libedataserver/e-source-registry-watcher.h
new file mode 100644
index 0000000..61951b7
--- /dev/null
+++ b/src/libedataserver/e-source-registry-watcher.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_SOURCE_REGISTRY_WATCHER_H
+#define E_SOURCE_REGISTRY_WATCHER_H
+
+#include <libedataserver/e-source-registry.h>
+#include <libedataserver/e-source.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_REGISTRY_WATCHER \
+ (e_source_registry_watcher_get_type ())
+#define E_SOURCE_REGISTRY_WATCHER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SOURCE_REGISTRY_WATCHER, ESourceRegistryWatcher))
+#define E_SOURCE_REGISTRY_WATCHER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SOURCE_REGISTRY_WATCHER, ESourceRegistryWatcherClass))
+#define E_IS_SOURCE_REGISTRY_WATCHER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SOURCE_REGISTRY_WATCHER))
+#define E_IS_SOURCE_REGISTRY_WATCHER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SOURCE_REGISTRY_WATCHER))
+#define E_SOURCE_REGISTRY_WATCHER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SOURCE_REGISTRY_WATCHER, ESourceRegistryWatcherClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceRegistryWatcher ESourceRegistryWatcher;
+typedef struct _ESourceRegistryWatcherClass ESourceRegistryWatcherClass;
+typedef struct _ESourceRegistryWatcherPrivate ESourceRegistryWatcherPrivate;
+
+/**
+ * ESourceRegistryWatcher:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _ESourceRegistryWatcher {
+ /*< private >*/
+ GObject parent;
+ ESourceRegistryWatcherPrivate *priv;
+};
+
+struct _ESourceRegistryWatcherClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ gboolean (* filter) (ESourceRegistryWatcher *watcher,
+ ESource *source);
+ void (* appeared) (ESourceRegistryWatcher *watcher,
+ ESource *source);
+ void (* disappeared) (ESourceRegistryWatcher *watcher,
+ ESource *source);
+};
+
+GType e_source_registry_watcher_get_type (void) G_GNUC_CONST;
+ESourceRegistryWatcher *
+ e_source_registry_watcher_new (ESourceRegistry *registry,
+ const gchar *extension_name);
+ESourceRegistry *
+ e_source_registry_watcher_get_registry (ESourceRegistryWatcher *watcher);
+const gchar * e_source_registry_watcher_get_extension_name
+ (ESourceRegistryWatcher *watcher);
+void e_source_registry_watcher_reclaim (ESourceRegistryWatcher *watcher);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_REGISTRY_WATCHER_H */
diff --git a/src/libedataserver/libedataserver.h b/src/libedataserver/libedataserver.h
index b11f8f8..fd95ddd 100644
--- a/src/libedataserver/libedataserver.h
+++ b/src/libedataserver/libedataserver.h
@@ -78,6 +78,7 @@
#include <libedataserver/e-source-proxy.h>
#include <libedataserver/e-source-refresh.h>
#include <libedataserver/e-source-registry.h>
+#include <libedataserver/e-source-registry-watcher.h>
#include <libedataserver/e-source-resource.h>
#include <libedataserver/e-source-revision-guards.h>
#include <libedataserver/e-source-security.h>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]