aboutsummaryrefslogtreecommitdiffstats
path: root/modules/contact-photos/e-contact-photo-source.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/contact-photos/e-contact-photo-source.c')
-rw-r--r--modules/contact-photos/e-contact-photo-source.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/modules/contact-photos/e-contact-photo-source.c b/modules/contact-photos/e-contact-photo-source.c
new file mode 100644
index 0000000000..b290a2168f
--- /dev/null
+++ b/modules/contact-photos/e-contact-photo-source.c
@@ -0,0 +1,432 @@
+/*
+ * e-contact-photo-source.c
+ *
+ * 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 of the License, or (at your option) version 3.
+ *
+ * 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-contact-photo-source.h"
+
+#define E_CONTACT_PHOTO_SOURCE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CONTACT_PHOTO_SOURCE, EContactPhotoSourcePrivate))
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _EContactPhotoSourcePrivate {
+ EClientCache *client_cache;
+ ESource *source;
+};
+
+struct _AsyncContext {
+ gchar *query_string;
+ GInputStream *stream;
+ gint priority;
+};
+
+enum {
+ PROP_0,
+ PROP_CLIENT_CACHE,
+ PROP_SOURCE
+};
+
+/* Forward Declarations */
+static void e_contact_photo_source_interface_init
+ (EPhotoSourceInterface *interface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (
+ EContactPhotoSource,
+ e_contact_photo_source,
+ G_TYPE_OBJECT,
+ 0,
+ G_IMPLEMENT_INTERFACE_DYNAMIC (
+ E_TYPE_PHOTO_SOURCE,
+ e_contact_photo_source_interface_init))
+
+static void
+async_context_free (AsyncContext *async_context)
+{
+ g_free (async_context->query_string);
+ g_clear_object (&async_context->stream);
+
+ g_slice_free (AsyncContext, async_context);
+}
+
+static EContactPhoto *
+contact_photo_source_extract_photo (EContact *contact,
+ gint *out_priority)
+{
+ EContactPhoto *photo;
+
+ photo = e_contact_get (contact, E_CONTACT_PHOTO);
+ *out_priority = G_PRIORITY_HIGH;
+
+ if (photo == NULL) {
+ photo = e_contact_get (contact, E_CONTACT_LOGO);
+ *out_priority = G_PRIORITY_LOW;
+ }
+
+ return photo;
+}
+
+static void
+contact_photo_source_get_photo_thread (GSimpleAsyncResult *simple,
+ GObject *source_object,
+ GCancellable *cancellable)
+{
+ EContactPhotoSource *photo_source;
+ AsyncContext *async_context;
+ EClientCache *client_cache;
+ ESourceRegistry *registry;
+ EClient *client = NULL;
+ ESource *source;
+ GSList *slist = NULL;
+ GSList *slink;
+ GError *error = NULL;
+
+ photo_source = E_CONTACT_PHOTO_SOURCE (source_object);
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ client_cache = e_contact_photo_source_ref_client_cache (photo_source);
+ source = e_contact_photo_source_ref_source (photo_source);
+ registry = e_client_cache_ref_registry (client_cache);
+
+ /* Return no result if the source is disabled. */
+ if (!e_source_registry_check_enabled (registry, source))
+ goto exit;
+
+ client = e_client_cache_get_client_sync (
+ client_cache, source,
+ E_SOURCE_EXTENSION_ADDRESS_BOOK,
+ cancellable, &error);
+
+ /* Sanity check. */
+ g_return_if_fail (
+ ((client != NULL) && (error == NULL)) ||
+ ((client == NULL) && (error != NULL)));
+
+ if (error != NULL) {
+ g_simple_async_result_take_error (simple, error);
+ goto exit;
+ }
+
+ e_book_client_get_contacts_sync (
+ E_BOOK_CLIENT (client),
+ async_context->query_string,
+ &slist, cancellable, &error);
+
+ if (error != NULL) {
+ g_warn_if_fail (slist == NULL);
+ g_simple_async_result_take_error (simple, error);
+ goto exit;
+ }
+
+ /* See if any of the contacts have a photo. */
+ for (slink = slist; slink != NULL; slink = g_slist_next (slink)) {
+ EContact *contact = E_CONTACT (slink->data);
+ GInputStream *stream = NULL;
+ EContactPhoto *photo;
+
+ photo = contact_photo_source_extract_photo (
+ contact, &async_context->priority);
+
+ if (photo == NULL)
+ continue;
+
+ /* Stream takes ownership of the inlined data. */
+ if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
+ stream = g_memory_input_stream_new_from_data (
+ photo->data.inlined.data,
+ photo->data.inlined.length,
+ (GDestroyNotify) g_free);
+ photo->data.inlined.data = NULL;
+ photo->data.inlined.length = 0;
+
+ } else {
+ GFileInputStream *file_stream;
+ GFile *file;
+
+ file = g_file_new_for_uri (photo->data.uri);
+
+ /* Disregard errors and proceed as
+ * though the contact has no photo. */
+
+ /* XXX Return type should have been GInputStream. */
+ file_stream = g_file_read (file, cancellable, NULL);
+ if (file_stream != NULL)
+ stream = G_INPUT_STREAM (file_stream);
+
+ g_object_unref (file);
+ }
+
+ e_contact_photo_free (photo);
+
+ /* Stop on the first input stream. */
+ if (stream != NULL) {
+ async_context->stream = g_object_ref (stream);
+ g_object_unref (stream);
+ break;
+ }
+ }
+
+ g_slist_free_full (slist, (GDestroyNotify) g_object_unref);
+
+exit:
+ g_clear_object (&client_cache);
+ g_clear_object (&registry);
+ g_clear_object (&client);
+ g_clear_object (&source);
+}
+
+static void
+contact_photo_source_set_client_cache (EContactPhotoSource *photo_source,
+ EClientCache *client_cache)
+{
+ g_return_if_fail (E_IS_CLIENT_CACHE (client_cache));
+ g_return_if_fail (photo_source->priv->client_cache == NULL);
+
+ photo_source->priv->client_cache = g_object_ref (client_cache);
+}
+
+static void
+contact_photo_source_set_source (EContactPhotoSource *photo_source,
+ ESource *source)
+{
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (photo_source->priv->source == NULL);
+
+ photo_source->priv->source = g_object_ref (source);
+}
+
+static void
+contact_photo_source_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CLIENT_CACHE:
+ contact_photo_source_set_client_cache (
+ E_CONTACT_PHOTO_SOURCE (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_SOURCE:
+ contact_photo_source_set_source (
+ E_CONTACT_PHOTO_SOURCE (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+contact_photo_source_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CLIENT_CACHE:
+ g_value_take_object (
+ value,
+ e_contact_photo_source_ref_client_cache (
+ E_CONTACT_PHOTO_SOURCE (object)));
+ return;
+
+ case PROP_SOURCE:
+ g_value_take_object (
+ value,
+ e_contact_photo_source_ref_source (
+ E_CONTACT_PHOTO_SOURCE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+contact_photo_source_dispose (GObject *object)
+{
+ EContactPhotoSourcePrivate *priv;
+
+ priv = E_CONTACT_PHOTO_SOURCE_GET_PRIVATE (object);
+
+ g_clear_object (&priv->client_cache);
+ g_clear_object (&priv->source);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_contact_photo_source_parent_class)->dispose (object);
+}
+
+static void
+contact_photo_source_get_photo (EPhotoSource *photo_source,
+ const gchar *email_address,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+ EBookQuery *book_query;
+
+ book_query = e_book_query_field_test (
+ E_CONTACT_EMAIL, E_BOOK_QUERY_IS, email_address);
+
+ async_context = g_slice_new0 (AsyncContext);
+ async_context->query_string = e_book_query_to_string (book_query);
+
+ e_book_query_unref (book_query);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (photo_source), callback,
+ user_data, contact_photo_source_get_photo);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, async_context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, contact_photo_source_get_photo_thread,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (simple);
+}
+
+static gboolean
+contact_photo_source_get_photo_finish (EPhotoSource *photo_source,
+ GAsyncResult *result,
+ GInputStream **out_stream,
+ gint *out_priority,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (photo_source),
+ contact_photo_source_get_photo), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ if (async_context->stream != NULL) {
+ *out_stream = g_object_ref (async_context->stream);
+ if (out_priority != NULL)
+ *out_priority = async_context->priority;
+ } else {
+ *out_stream = NULL;
+ }
+
+ return TRUE;
+}
+
+static void
+e_contact_photo_source_class_init (EContactPhotoSourceClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EContactPhotoSourcePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = contact_photo_source_set_property;
+ object_class->get_property = contact_photo_source_get_property;
+ object_class->dispose = contact_photo_source_dispose;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CLIENT_CACHE,
+ g_param_spec_object (
+ "client-cache",
+ "Client Cache",
+ "Cache of shared EClient instances",
+ E_TYPE_CLIENT_CACHE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SOURCE,
+ g_param_spec_object (
+ "source",
+ "Source",
+ "An address book source",
+ E_TYPE_SOURCE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_contact_photo_source_class_finalize (EContactPhotoSourceClass *class)
+{
+}
+
+static void
+e_contact_photo_source_interface_init (EPhotoSourceInterface *interface)
+{
+ interface->get_photo = contact_photo_source_get_photo;
+ interface->get_photo_finish = contact_photo_source_get_photo_finish;
+}
+
+static void
+e_contact_photo_source_init (EContactPhotoSource *photo_source)
+{
+ photo_source->priv = E_CONTACT_PHOTO_SOURCE_GET_PRIVATE (photo_source);
+}
+
+void
+e_contact_photo_source_type_register (GTypeModule *type_module)
+{
+ /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+ * function, so we have to wrap it with a public function in
+ * order to register types from a separate compilation unit. */
+ e_contact_photo_source_register_type (type_module);
+}
+
+EPhotoSource *
+e_contact_photo_source_new (EClientCache *client_cache,
+ ESource *source)
+{
+ g_return_val_if_fail (E_IS_CLIENT_CACHE (client_cache), NULL);
+ g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+ return g_object_new (
+ E_TYPE_CONTACT_PHOTO_SOURCE,
+ "client-cache", client_cache,
+ "source", source,
+ NULL);
+}
+
+EClientCache *
+e_contact_photo_source_ref_client_cache (EContactPhotoSource *photo_source)
+{
+ g_return_val_if_fail (E_IS_CONTACT_PHOTO_SOURCE (photo_source), NULL);
+
+ return g_object_ref (photo_source->priv->client_cache);
+}
+
+ESource *
+e_contact_photo_source_ref_source (EContactPhotoSource *photo_source)
+{
+ g_return_val_if_fail (E_IS_CONTACT_PHOTO_SOURCE (photo_source), NULL);
+
+ return g_object_ref (photo_source->priv->source);
+}
+