[libsocialweb] contacts: add SwContactView and SwContact
- From: Alban Crequy <albanc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsocialweb] contacts: add SwContactView and SwContact
- Date: Thu, 31 Mar 2011 15:35:31 +0000 (UTC)
commit 0b4d4657d30439499a6bc301162afd6296d9cb2f
Author: Alban Crequy <alban crequy collabora co uk>
Date: Wed Mar 23 13:25:13 2011 +0000
contacts: add SwContactView and SwContact
libsocialweb/Makefile.am | 4 +
libsocialweb/sw-contact-view.c | 832 ++++++++++++++++++++++++++++++++++++++++
libsocialweb/sw-contact-view.h | 71 ++++
libsocialweb/sw-contact.c | 493 ++++++++++++++++++++++++
libsocialweb/sw-contact.h | 105 +++++
libsocialweb/sw-debug.c | 1 +
libsocialweb/sw-debug.h | 19 +-
libsocialweb/sw-types.h | 2 +
8 files changed, 1518 insertions(+), 9 deletions(-)
---
diff --git a/libsocialweb/Makefile.am b/libsocialweb/Makefile.am
index 526fc47..284543a 100644
--- a/libsocialweb/Makefile.am
+++ b/libsocialweb/Makefile.am
@@ -21,6 +21,8 @@ libsocialweb_la_LIBADD = $(DBUS_GLIB_LIBS) $(SOUP_LIBS) $(SOUP_GNOME_LIBS) \
libsocialweb_la_SOURCES = sw-types.h \
sw-debug.c sw-debug.h \
sw-core.c sw-core.h \
+ sw-contact.c sw-contact.h \
+ sw-contact-view.c sw-contact-view.h \
sw-item.c sw-item.h \
sw-item-view.c sw-item-view.h \
sw-item-stream.c sw-item-stream.h \
@@ -41,12 +43,14 @@ public_headers = \
sw-types.h \
sw-service.h \
sw-online.h \
+ sw-contact-view.h \
sw-item-view.h \
sw-item-stream.h \
sw-debug.h \
sw-web.h \
sw-set.h \
sw-cache.h \
+ sw-contact.h \
sw-item.h \
sw-module.h \
sw-utils.h \
diff --git a/libsocialweb/sw-contact-view.c b/libsocialweb/sw-contact-view.c
new file mode 100644
index 0000000..0091d92
--- /dev/null
+++ b/libsocialweb/sw-contact-view.c
@@ -0,0 +1,832 @@
+/*
+ * libsocialweb - social data store
+ * Copyright (C) 2008 - 2009 Intel Corporation.
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * Author: Rob Bradford <rob linux intel com>
+ * Alban Crequy <alban crequy collabora co uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "sw-debug.h"
+#include "sw-contact-view.h"
+#include "sw-contact-view-ginterface.h"
+
+#include <libsocialweb/sw-utils.h>
+#include <libsocialweb/sw-core.h>
+
+static void sw_contact_view_iface_init (gpointer g_iface, gpointer iface_data);
+G_DEFINE_TYPE_WITH_CODE (SwContactView, sw_contact_view, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SW_TYPE_CONTACT_VIEW_IFACE,
+ sw_contact_view_iface_init));
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), SW_TYPE_CONTACT_VIEW, SwContactViewPrivate))
+
+typedef struct _SwContactViewPrivate SwContactViewPrivate;
+
+struct _SwContactViewPrivate {
+ SwService *service;
+ gchar *object_path;
+ SwSet *current_contacts_set;
+ SwSet *pending_contacts_set;
+
+ /* timeout used for coalescing multiple delayed ready additions */
+ guint pending_timeout_id;
+
+ /* timeout used for ratelimiting checking for changed contacts */
+ guint refresh_timeout_id;
+
+ GHashTable *uid_to_contacts;
+
+ GList *changed_contacts;
+};
+
+enum
+{
+ PROP_0,
+ PROP_SERVICE,
+ PROP_OBJECT_PATH
+};
+
+static void sw_contact_view_add_contacts (SwContactView *contact_view,
+ GList *contacts);
+static void sw_contact_view_update_contacts (SwContactView *contact_view,
+ GList *contacts);
+static void sw_contact_view_remove_contacts (SwContactView *contact_view,
+ GList *contacts);
+
+static void
+sw_contact_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (object);
+
+ switch (property_id) {
+ case PROP_SERVICE:
+ g_value_set_object (value, priv->service);
+ break;
+ case PROP_OBJECT_PATH:
+ g_value_set_string (value, priv->object_path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+sw_contact_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (object);
+
+ switch (property_id) {
+ case PROP_SERVICE:
+ priv->service = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+sw_contact_view_dispose (GObject *object)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (object);
+
+ if (priv->service)
+ {
+ g_object_unref (priv->service);
+ priv->service = NULL;
+ }
+
+ if (priv->current_contacts_set)
+ {
+ sw_set_unref (priv->current_contacts_set);
+ priv->current_contacts_set = NULL;
+ }
+
+ if (priv->pending_contacts_set)
+ {
+ sw_set_unref (priv->pending_contacts_set);
+ priv->pending_contacts_set = NULL;
+ }
+
+ if (priv->uid_to_contacts)
+ {
+ g_hash_table_unref (priv->uid_to_contacts);
+ priv->uid_to_contacts = NULL;
+ }
+
+ if (priv->pending_timeout_id)
+ {
+ g_source_remove (priv->pending_timeout_id);
+ priv->pending_timeout_id = 0;
+ }
+
+ if (priv->refresh_timeout_id)
+ {
+ g_source_remove (priv->refresh_timeout_id);
+ priv->refresh_timeout_id = 0;
+ }
+
+ G_OBJECT_CLASS (sw_contact_view_parent_class)->dispose (object);
+}
+
+static void
+sw_contact_view_finalize (GObject *object)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (object);
+
+ g_free (priv->object_path);
+
+ G_OBJECT_CLASS (sw_contact_view_parent_class)->finalize (object);
+}
+
+static gchar *
+_make_object_path (SwContactView *contact_view)
+{
+ gchar *path;
+ static gint count = 0;
+
+ path = g_strdup_printf ("/com/meego/libsocialweb/View%d",
+ count);
+
+ count++;
+
+ return path;
+}
+
+static void
+sw_contact_view_constructed (GObject *object)
+{
+ SwContactView *contact_view = SW_CONTACT_VIEW (object);
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+ SwCore *core;
+
+ core = sw_core_dup_singleton ();
+
+ priv->object_path = _make_object_path (contact_view);
+ dbus_g_connection_register_g_object (sw_core_get_connection (core),
+ priv->object_path,
+ G_OBJECT (contact_view));
+ g_object_unref (core);
+ /* The only reference should be the one on the bus */
+
+ if (G_OBJECT_CLASS (sw_contact_view_parent_class)->constructed)
+ G_OBJECT_CLASS (sw_contact_view_parent_class)->constructed (object);
+}
+
+/* Default implementation for close */
+static void
+sw_contact_view_default_close (SwContactView *contact_view)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+ SwCore *core;
+
+ SW_DEBUG (VIEWS, "%s called on %s", G_STRFUNC, priv->object_path);
+
+ core = sw_core_dup_singleton ();
+ dbus_g_connection_unregister_g_object (sw_core_get_connection (core),
+ G_OBJECT (contact_view));
+ g_object_unref (core);
+
+ /* Object is no longer needed */
+ g_object_unref (contact_view);
+}
+
+static void
+sw_contact_view_class_init (SwContactViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (SwContactViewPrivate));
+
+ object_class->get_property = sw_contact_view_get_property;
+ object_class->set_property = sw_contact_view_set_property;
+ object_class->dispose = sw_contact_view_dispose;
+ object_class->finalize = sw_contact_view_finalize;
+ object_class->constructed = sw_contact_view_constructed;
+
+ klass->close = sw_contact_view_default_close;
+
+ pspec = g_param_spec_object ("service",
+ "service",
+ "The service this view is using",
+ SW_TYPE_SERVICE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_SERVICE, pspec);
+
+ pspec = g_param_spec_string ("object-path",
+ "Object path",
+ "The object path of this view",
+ NULL,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_OBJECT_PATH, pspec);
+}
+
+static void
+sw_contact_view_init (SwContactView *self)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (self);
+
+ priv->current_contacts_set = sw_contact_set_new ();
+ priv->pending_contacts_set = sw_contact_set_new ();
+
+ priv->uid_to_contacts = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+}
+
+/* DBUS interface to class vfunc bindings */
+
+static void
+sw_contact_view_start (SwContactViewIface *iface,
+ DBusGMethodInvocation *context)
+{
+ SwContactView *contact_view = SW_CONTACT_VIEW (iface);
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ SW_DEBUG (VIEWS, "%s called on %s", G_STRFUNC, priv->object_path);
+
+ if (SW_CONTACT_VIEW_GET_CLASS (iface)->start)
+ SW_CONTACT_VIEW_GET_CLASS (iface)->start (contact_view);
+
+ sw_contact_view_iface_return_from_start (context);
+}
+
+static void
+sw_contact_view_refresh (SwContactViewIface *iface,
+ DBusGMethodInvocation *context)
+{
+ SwContactView *contact_view = SW_CONTACT_VIEW (iface);
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ SW_DEBUG (VIEWS, "%s called on %s", G_STRFUNC, priv->object_path);
+
+ if (SW_CONTACT_VIEW_GET_CLASS (iface)->refresh)
+ SW_CONTACT_VIEW_GET_CLASS (iface)->refresh (contact_view);
+
+ sw_contact_view_iface_return_from_refresh (context);
+}
+
+static void
+sw_contact_view_stop (SwContactViewIface *iface,
+ DBusGMethodInvocation *context)
+{
+ SwContactView *contact_view = SW_CONTACT_VIEW (iface);
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ SW_DEBUG (VIEWS, "%s called on %s", G_STRFUNC, priv->object_path);
+
+ if (SW_CONTACT_VIEW_GET_CLASS (iface)->stop)
+ SW_CONTACT_VIEW_GET_CLASS (iface)->stop (contact_view);
+
+ sw_contact_view_iface_return_from_stop (context);
+}
+
+static void
+sw_contact_view_close (SwContactViewIface *iface,
+ DBusGMethodInvocation *context)
+{
+ SwContactView *contact_view = SW_CONTACT_VIEW (iface);
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ SW_DEBUG (VIEWS, "%s called on %s", G_STRFUNC, priv->object_path);
+
+ if (SW_CONTACT_VIEW_GET_CLASS (iface)->close)
+ SW_CONTACT_VIEW_GET_CLASS (iface)->close (contact_view);
+
+ sw_contact_view_iface_return_from_close (context);
+}
+
+static void
+sw_contact_view_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ SwContactViewIfaceClass *klass = (SwContactViewIfaceClass*)g_iface;
+ sw_contact_view_iface_implement_start (klass, sw_contact_view_start);
+ sw_contact_view_iface_implement_refresh (klass, sw_contact_view_refresh);
+ sw_contact_view_iface_implement_stop (klass, sw_contact_view_stop);
+ sw_contact_view_iface_implement_close (klass, sw_contact_view_close);
+}
+
+static gboolean
+_handle_ready_pending_cb (gpointer data)
+{
+ SwContactView *contact_view = SW_CONTACT_VIEW (data);
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+ GList *contacts_to_send = NULL;
+ GList *pending_contacts, *l;
+
+ SW_DEBUG (VIEWS, "Delayed ready timeout fired");
+
+ /* FIXME: Reword this to avoid unnecessary list creation ? */
+ pending_contacts = sw_set_as_list (priv->pending_contacts_set);
+
+ for (l = pending_contacts; l; l = l->next)
+ {
+ SwContact *contact = SW_CONTACT (l->data);
+
+ if (sw_contact_get_ready (contact))
+ {
+ contacts_to_send = g_list_prepend (contacts_to_send, contact);
+ sw_set_remove (priv->pending_contacts_set, (GObject *)contact);
+ }
+ }
+
+ sw_contact_view_add_contacts (contact_view, contacts_to_send);
+
+ g_list_free (pending_contacts);
+
+ priv->pending_timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+_contact_ready_weak_notify_cb (gpointer data,
+ GObject *dead_object);
+
+static void
+_contact_ready_notify_cb (SwContact *contact,
+ GParamSpec *pspec,
+ SwContactView *contact_view)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ if (sw_contact_get_ready (contact)) {
+ SW_DEBUG (VIEWS, "Contact became ready: %s.",
+ sw_contact_get (contact, "id"));
+ g_signal_handlers_disconnect_by_func (contact,
+ _contact_ready_notify_cb,
+ contact_view);
+ g_object_weak_unref ((GObject *)contact_view,
+ _contact_ready_weak_notify_cb,
+ contact);
+
+ if (!priv->pending_timeout_id)
+ {
+ SW_DEBUG (VIEWS, "Setting up timeout");
+ priv->pending_timeout_id = g_timeout_add_seconds (1,
+ _handle_ready_pending_cb,
+ contact_view);
+ } else {
+ SW_DEBUG (VIEWS, "Timeout already set up.");
+ }
+ }
+}
+
+static void
+_contact_ready_weak_notify_cb (gpointer data,
+ GObject *dead_object)
+{
+ g_signal_handlers_disconnect_by_func (data,
+ _contact_ready_notify_cb,
+ dead_object);
+}
+
+static void
+_setup_ready_handler (SwContact *contact,
+ SwContactView *contact_view)
+{
+ g_signal_connect (contact,
+ "notify::ready",
+ (GCallback)_contact_ready_notify_cb,
+ contact_view);
+ g_object_weak_ref ((GObject *)contact_view,
+ _contact_ready_weak_notify_cb,
+ contact);
+}
+
+static gboolean
+_contact_changed_timeout_cb (gpointer data)
+{
+ SwContactView *contact_view = SW_CONTACT_VIEW (data);
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ sw_contact_view_update_contacts (contact_view, priv->changed_contacts);
+ g_list_foreach (priv->changed_contacts, (GFunc)g_object_unref, NULL);
+ g_list_free (priv->changed_contacts);
+ priv->changed_contacts = NULL;
+
+ priv->refresh_timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+_contact_changed_cb (SwContact *contact,
+ SwContactView *contact_view)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ /* We only care if the contact is ready. If it's not then we don't want to be
+ * emitting changed but instead it will be added through the readiness
+ * tracking.
+ */
+ if (!sw_contact_get_ready (contact))
+ return;
+
+ if (!g_list_find (priv->changed_contacts, contact))
+ priv->changed_contacts = g_list_append (priv->changed_contacts, contact);
+
+ if (!priv->refresh_timeout_id)
+ {
+ SW_DEBUG (VIEWS, "Contact changed, Setting up timeout");
+
+ priv->refresh_timeout_id = g_timeout_add_seconds (10,
+ _contact_changed_timeout_cb,
+ contact_view);
+ }
+}
+
+static void
+_contact_changed_weak_notify_cb (gpointer data,
+ GObject *dead_object)
+{
+ SwContact *contact = (SwContact *)data;
+
+ g_signal_handlers_disconnect_by_func (contact,
+ _contact_changed_cb,
+ dead_object);
+ g_object_unref (contact);
+}
+
+static void
+_setup_changed_handler (SwContact *contact,
+ SwContactView *contact_view)
+{
+ g_signal_connect (contact,
+ "changed",
+ (GCallback)_contact_changed_cb,
+ contact_view);
+ g_object_weak_ref ((GObject *)contact_view,
+ _contact_changed_weak_notify_cb,
+ g_object_ref (contact));
+}
+
+/**
+ * sw_contact_view_add_contacts
+ * @contact_view: A #SwContactView
+ * @contacts: A list of #SwContact objects
+ *
+ * Add the contacts supplied in the list from the #SwContactView. In many
+ * cases what you actually want is sw_contact_view_remove_from_set() or
+ * sw_contact_view_set_from_set(). This will cause signal emissions over the
+ * bus.
+ *
+ * This is used in the implementation of sw_contact_view_remove_from_set()
+ */
+static void
+sw_contact_view_add_contacts (SwContactView *contact_view,
+ GList *contacts)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+ GValueArray *value_array;
+ GPtrArray *contacts_ptr_array;
+ GList *l;
+
+ contacts_ptr_array = g_ptr_array_new_with_free_func
+ ((GDestroyNotify)g_value_array_free);
+
+ for (l = contacts; l; l = l->next)
+ {
+ SwContact *contact = SW_CONTACT (l->data);
+
+ if (sw_contact_get_ready (contact))
+ {
+ SW_DEBUG (VIEWS, "Contact ready: %s",
+ sw_contact_get (contact, "id"));
+ value_array = _sw_contact_to_value_array (contact);
+ g_ptr_array_add (contacts_ptr_array, value_array);
+ } else {
+ SW_DEBUG (VIEWS, "Contact not ready, setting up handler: %s",
+ sw_contact_get (contact, "id"));
+ _setup_ready_handler (contact, contact_view);
+ sw_set_add (priv->pending_contacts_set, (GObject *)contact);
+ }
+
+ _setup_changed_handler (contact, contact_view);
+ }
+
+ SW_DEBUG (VIEWS, "Number of contacts to be added: %d", contacts_ptr_array->len);
+
+ if (contacts_ptr_array->len > 0)
+ sw_contact_view_iface_emit_contacts_added (contact_view,
+ contacts_ptr_array);
+
+ g_ptr_array_free (contacts_ptr_array, TRUE);
+}
+
+/**
+ * sw_contact_view_update_contacts
+ * @contact_view: A #SwContactView
+ * @contacts: A list of #SwContact objects that need updating
+ *
+ * Update the contacts supplied in the list in the #SwContactView. This is
+ * will cause signal emissions over the bus.
+ */
+static void
+sw_contact_view_update_contacts (SwContactView *contact_view,
+ GList *contacts)
+{
+ GValueArray *value_array;
+ GPtrArray *contacts_ptr_array;
+ GList *l;
+
+ contacts_ptr_array = g_ptr_array_new_with_free_func
+ ((GDestroyNotify)g_value_array_free);
+
+ for (l = contacts; l; l = l->next)
+ {
+ SwContact *contact = SW_CONTACT (l->data);
+
+ /*
+ * Contact must be ready and also not in the pending contacts set; we need to
+ * check this to prevent ContactsChanged coming before ContactsAdded
+ */
+ if (sw_contact_get_ready (contact))
+ {
+ value_array = _sw_contact_to_value_array (contact);
+ g_ptr_array_add (contacts_ptr_array, value_array);
+ }
+ }
+
+ SW_DEBUG (VIEWS, "Number of contacts to be changed: %d",
+ contacts_ptr_array->len);
+
+ if (contacts_ptr_array->len > 0)
+ sw_contact_view_iface_emit_contacts_changed (contact_view,
+ contacts_ptr_array);
+
+ g_ptr_array_free (contacts_ptr_array, TRUE);
+}
+
+/**
+ * sw_contact_view_remove_contacts
+ * @contact_view: A #SwContactView
+ * @contacts: A list of #SwContact objects
+ *
+ * Remove the contacts supplied in the list from the #SwContactView. In many
+ * cases what you actually want is sw_contact_view_remove_from_set() or
+ * sw_contact_view_set_from_set(). This will cause signal emissions over the
+ * bus.
+ *
+ * This is used in the implementation of sw_contact_view_remove_from_set()
+ *
+ */
+static void
+sw_contact_view_remove_contacts (SwContactView *contact_view,
+ GList *contacts)
+{
+ GValueArray *value_array;
+ GPtrArray *contacts_ptr_array;
+ GList *l;
+ SwContact *contact;
+
+ contacts_ptr_array = g_ptr_array_new_with_free_func
+ ((GDestroyNotify)g_value_array_free);
+
+ for (l = contacts; l; l = l->next)
+ {
+ int values_type = 0;
+ contact = SW_CONTACT (l->data);
+
+ value_array = g_value_array_new (2);
+
+ value_array = g_value_array_append (value_array, NULL);
+ g_value_init (g_value_array_get_nth (value_array, 0), G_TYPE_STRING);
+ g_value_set_string (g_value_array_get_nth (value_array, 0),
+ sw_service_get_name (sw_contact_get_service (contact)));
+
+ value_array = g_value_array_append (value_array, NULL);
+ g_value_init (g_value_array_get_nth (value_array, 1), G_TYPE_STRING);
+ g_value_set_string (g_value_array_get_nth (value_array, 1),
+ sw_contact_get (contact, "id"));
+
+ g_object_get (contact, "values-type", &values_type, NULL);
+ g_ptr_array_add (contacts_ptr_array, value_array);
+ }
+
+ if (contacts_ptr_array->len > 0)
+ sw_contact_view_iface_emit_contacts_removed (contact_view,
+ contacts_ptr_array);
+
+ g_ptr_array_free (contacts_ptr_array, TRUE);
+}
+
+/**
+ * sw_contact_view_get_object_path
+ * @contact_view: A #SwContactView
+ *
+ * Since #SwContactView is responsible for constructing the object path and
+ * registering the object on the bus. This function is necessary for
+ * #SwCore to be able to return the object path as the result of a
+ * function to open a view.
+ *
+ * Returns: A string providing the object path.
+ */
+const gchar *
+sw_contact_view_get_object_path (SwContactView *contact_view)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ return priv->object_path;
+}
+
+/**
+ * sw_contact_view_get_service
+ * @contact_view: A #SwContactView
+ *
+ * Returns: The #SwService that #SwContactView is for
+ */
+SwService *
+sw_contact_view_get_service (SwContactView *contact_view)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+
+ return priv->service;
+}
+
+/* TODO: Export this function ? */
+/**
+ * sw_contact_view_add_from_set
+ * @contact_view: A #SwContactView
+ * @set: A #SwSet
+ *
+ * Add the contacts that are in the supplied set to the view.
+ *
+ * This is used in the implementation of sw_contact_view_set_from_set()
+ */
+void
+sw_contact_view_add_from_set (SwContactView *contact_view,
+ SwSet *set)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+ GList *contacts;
+ GList *l;
+
+ sw_set_add_from (priv->current_contacts_set, set);
+ contacts = sw_set_as_list (set);
+
+ for (l = contacts; l; l = l->next)
+ {
+ SwContact *contact = (SwContact *)l->data;
+
+ g_hash_table_replace (priv->uid_to_contacts,
+ g_strdup (sw_contact_get (contact, "id")),
+ g_object_ref (contact));
+ }
+
+ sw_contact_view_add_contacts (contact_view, contacts);
+ g_list_free (contacts);
+}
+
+/* TODO: Export this function ? */
+/**
+ * sw_contact_view_remove_from_set
+ * @contact_view: A #SwContactView
+ * @set: A #SwSet
+ *
+ * Remove the contacts that are in the supplied set from the view.
+ *
+ * This is used in the implementation of sw_contact_view_set_from_set()
+ */
+void
+sw_contact_view_remove_from_set (SwContactView *contact_view,
+ SwSet *set)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+ GList *contacts;
+ GList *l;
+
+ sw_set_remove_from (priv->current_contacts_set, set);
+
+ contacts = sw_set_as_list (set);
+
+ for (l = contacts; l; l = l->next)
+ {
+ SwContact *contact = (SwContact *)l->data;
+
+ g_hash_table_remove (priv->uid_to_contacts,
+ sw_contact_get (contact, "id"));
+ }
+
+ sw_contact_view_remove_contacts (contact_view, contacts);
+ g_list_free (contacts);
+}
+
+/**
+ * sw_contact_view_update_existing
+ * @contact_view: A #SwContactView
+ * @set: A #SwSet
+ *
+ * Replaces contacts in the internal set for the #SwContactView with the version
+ * from #SwSet if and only if they are sw_contact_equal() says that they are
+ * unequal. This prevents sending excessive contacts changed signals.
+ */
+static void
+sw_contact_view_update_existing (SwContactView *contact_view,
+ SwSet *set)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+ GList *contacts;
+ SwContact *new_contact;
+ SwContact *old_contact;
+ GList *l;
+ GList *contacts_to_send = NULL;
+
+ contacts = sw_set_as_list (set);
+
+ for (l = contacts; l; l = l->next)
+ {
+ new_contact = (SwContact *)l->data;
+ old_contact = g_hash_table_lookup (priv->uid_to_contacts,
+ sw_contact_get (new_contact, "id"));
+
+ /* This is just a new contact so we won't find it */
+ if (!old_contact)
+ continue;
+
+ if (!sw_contact_equal (new_contact, old_contact))
+ {
+ g_hash_table_replace (priv->uid_to_contacts,
+ g_strdup (sw_contact_get (new_contact, "id")),
+ new_contact);
+ /*
+ * This works because sw_set_add uses g_hash_table_replace behind the
+ * scenes
+ */
+ sw_set_add (priv->current_contacts_set, (GObject *)new_contact);
+ contacts_to_send = g_list_append (contacts_to_send, g_object_ref (new_contact));
+ }
+ }
+
+ sw_contact_view_update_contacts (contact_view, contacts_to_send);
+
+ g_list_free (contacts);
+}
+
+/**
+ * sw_contact_view_set_from_set
+ * @contact_view: A #SwContactView
+ * @set: A #SwSet
+ *
+ * Updates what the view contains based on the given #SwSet. Removed
+ * signals will be fired for any contacts that were in the view but that are not
+ * present in the supplied set. Conversely any contacts that are new will cause
+ * signals to be fired indicating their addition.
+ *
+ * This implemented by maintaining a set inside the #SwContactView
+ */
+void
+sw_contact_view_set_from_set (SwContactView *contact_view,
+ SwSet *set)
+{
+ SwContactViewPrivate *priv = GET_PRIVATE (contact_view);
+ SwSet *added_contacts, *removed_contacts;
+
+ if (sw_set_is_empty (priv->current_contacts_set))
+ {
+ sw_contact_view_add_from_set (contact_view, set);
+ } else {
+ removed_contacts = sw_set_difference (priv->current_contacts_set, set);
+ added_contacts = sw_set_difference (set, priv->current_contacts_set);
+
+ if (!sw_set_is_empty (removed_contacts))
+ sw_contact_view_remove_from_set (contact_view, removed_contacts);
+
+ /*
+ * Replace contacts that exist in the new set that are also present in the
+ * original set iff they're not equal
+ *
+ * This function will also cause the ContactsChanged signal to be fired with
+ * the contacts that have changed.
+ */
+ sw_contact_view_update_existing (contact_view, set);
+
+ if (!sw_set_is_empty (added_contacts))
+ sw_contact_view_add_from_set (contact_view, added_contacts);
+
+ sw_set_unref (removed_contacts);
+ sw_set_unref (added_contacts);
+ }
+}
diff --git a/libsocialweb/sw-contact-view.h b/libsocialweb/sw-contact-view.h
new file mode 100644
index 0000000..d5aeade
--- /dev/null
+++ b/libsocialweb/sw-contact-view.h
@@ -0,0 +1,71 @@
+/*
+ * libsocialweb - social data store
+ * Copyright (C) 2008 - 2009 Intel Corporation.
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * Author: Rob Bradford <rob linux intel com>
+ * Alban Crequy <alban crequy collabora co uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef _SW_CONTACT_VIEW
+#define _SW_CONTACT_VIEW
+
+#include <glib-object.h>
+
+#include <libsocialweb/sw-contact.h>
+
+G_BEGIN_DECLS
+
+#define SW_TYPE_CONTACT_VIEW sw_contact_view_get_type()
+
+#define SW_CONTACT_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SW_TYPE_CONTACT_VIEW, SwContactView))
+
+#define SW_CONTACT_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), SW_TYPE_CONTACT_VIEW, SwContactViewClass))
+
+#define SW_IS_CONTACT_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SW_TYPE_CONTACT_VIEW))
+
+#define SW_IS_CONTACT_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), SW_TYPE_CONTACT_VIEW))
+
+#define SW_CONTACT_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), SW_TYPE_CONTACT_VIEW, SwContactViewClass))
+
+typedef struct {
+ GObject parent;
+} SwContactView;
+
+typedef struct {
+ GObjectClass parent_class;
+ void (*start) (SwContactView *contact_view);
+ void (*refresh) (SwContactView *contact_view);
+ void (*stop) (SwContactView *contact_view);
+ void (*close) (SwContactView *contact_view);
+} SwContactViewClass;
+
+GType sw_contact_view_get_type (void);
+
+void sw_contact_view_set_from_set (SwContactView *contact_view,
+ SwSet *set);
+
+const gchar *sw_contact_view_get_object_path (SwContactView *contact_view);
+SwService *sw_contact_view_get_service (SwContactView *contact_view);
+
+G_END_DECLS
+
+#endif /* _SW_CONTACT_VIEW */
+
diff --git a/libsocialweb/sw-contact.c b/libsocialweb/sw-contact.c
new file mode 100644
index 0000000..061de25
--- /dev/null
+++ b/libsocialweb/sw-contact.c
@@ -0,0 +1,493 @@
+/*
+ * libsocialweb - social data store
+ * Copyright (C) 2008 - 2009 Intel Corporation.
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <libsocialweb/sw-utils.h>
+#include <libsocialweb/sw-web.h>
+#include "sw-contact.h"
+#include "sw-debug.h"
+
+G_DEFINE_TYPE (SwContact, sw_contact, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), SW_TYPE_CONTACT, SwContactPrivate))
+
+struct _SwContactPrivate {
+ /* TODO: fix lifecycle */
+ SwService *service;
+ /* Contact: hash (key: string) -> (GStrv value)
+ */
+ GHashTable *hash;
+ time_t cached_date;
+ time_t mtime;
+ gint remaining_fetches;
+};
+
+enum
+{
+ PROP_0,
+ PROP_READY,
+};
+
+enum
+{
+ CHANGED_SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static void
+sw_contact_dispose (GObject *object)
+{
+ SwContact *contact = SW_CONTACT (object);
+ SwContactPrivate *priv = contact->priv;
+
+ if (priv->hash) {
+ g_hash_table_unref (priv->hash);
+ priv->hash = NULL;
+ }
+
+ G_OBJECT_CLASS (sw_contact_parent_class)->dispose (object);
+}
+
+static void
+sw_contact_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SwContact *contact = SW_CONTACT (object);
+
+ switch (property_id)
+ {
+ case PROP_READY:
+ g_value_set_boolean (value, sw_contact_get_ready (contact));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+sw_contact_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+sw_contact_class_init (SwContactClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (SwContactPrivate));
+
+ object_class->dispose = sw_contact_dispose;
+ object_class->get_property = sw_contact_get_property;
+ object_class->set_property = sw_contact_set_property;
+
+ pspec = g_param_spec_boolean ("ready",
+ "ready",
+ "Whether contact is ready to set out",
+ FALSE,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_READY, pspec);
+
+ signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+ SW_TYPE_CONTACT,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SwContactClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+sw_contact_init (SwContact *self)
+{
+ self->priv = GET_PRIVATE (self);
+ self->priv->hash = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) g_strfreev);
+
+}
+
+SwContact*
+sw_contact_new (void)
+{
+ return g_object_new (SW_TYPE_CONTACT, NULL);
+}
+
+void
+sw_contact_set_service (SwContact *contact, SwService *service)
+{
+ g_return_if_fail (SW_IS_CONTACT (contact));
+ g_return_if_fail (SW_IS_SERVICE (service));
+
+ /* TODO: weak reference? Remember to update dispose() */
+ contact->priv->service = service;
+}
+
+SwService *
+sw_contact_get_service (SwContact *contact)
+{
+ g_return_val_if_fail (SW_IS_CONTACT (contact), NULL);
+
+ return contact->priv->service;
+}
+
+void
+sw_contact_put (SwContact *contact, const char *key, const char *value)
+{
+ g_return_if_fail (SW_IS_CONTACT (contact));
+ g_return_if_fail (key);
+
+ GStrv str_array;
+ GStrv new_str_array;
+ str_array = g_hash_table_lookup (contact->priv->hash,
+ (gpointer)g_intern_string (key));
+ if (str_array == NULL) {
+ new_str_array = g_new0 (gchar *, 2);
+ new_str_array[0] = g_strdup (value);
+ } else {
+ int i;
+ int len = g_strv_length (str_array);
+ new_str_array = g_new0 (gchar *, len + 2);
+ for (i = 0 ; i < len ; i++)
+ new_str_array[i] = g_strdup (str_array[i]);
+ new_str_array[len] = g_strdup (value);
+ }
+ g_hash_table_insert (contact->priv->hash,
+ (gpointer)g_intern_string (key),
+ new_str_array);
+
+ sw_contact_touch (contact);
+}
+
+void
+sw_contact_take (SwContact *contact, const char *key, char *value)
+{
+ g_return_if_fail (SW_IS_CONTACT (contact));
+ g_return_if_fail (key);
+
+ GStrv str_array;
+ GStrv new_str_array;
+ str_array = g_hash_table_lookup (contact->priv->hash,
+ (gpointer)g_intern_string (key));
+ if (str_array == NULL) {
+ new_str_array = g_new0 (gchar *, 2);
+ new_str_array[0] = value;
+ } else {
+ int i;
+ int len = g_strv_length (str_array);
+ new_str_array = g_new0 (gchar *, len + 2);
+ for (i = 0 ; i < len ; i++)
+ new_str_array[i] = g_strdup (str_array[i]);
+ new_str_array[len] = value;
+ }
+ g_hash_table_insert (contact->priv->hash,
+ (gpointer)g_intern_string (key),
+ new_str_array);
+
+ sw_contact_touch (contact);
+}
+
+const char *
+sw_contact_get (const SwContact *contact, const char *key)
+{
+ g_return_val_if_fail (SW_IS_CONTACT (contact), NULL);
+ g_return_val_if_fail (key, NULL);
+
+ GStrv str_array = g_hash_table_lookup (contact->priv->hash,
+ g_intern_string (key));
+ if (!str_array)
+ return NULL;
+ return str_array[0];
+}
+
+static const GStrv
+sw_contact_get_all (const SwContact *contact, const char *key)
+{
+ g_return_val_if_fail (SW_IS_CONTACT (contact), NULL);
+ g_return_val_if_fail (key, NULL);
+
+ return g_hash_table_lookup (contact->priv->hash,
+ g_intern_string (key));
+}
+
+static void
+cache_date (SwContact *contact)
+{
+ const char *s;
+
+ if (contact->priv->cached_date)
+ return;
+
+ s = sw_contact_get (contact, "date");
+ if (!s)
+ return;
+
+ contact->priv->cached_date = sw_time_t_from_string (s);
+}
+
+void
+sw_contact_dump (SwContact *contact)
+{
+ GHashTableIter iter;
+ const char *key;
+ gpointer value;
+
+ g_return_if_fail (SW_IS_CONTACT (contact));
+
+ g_printerr ("SwContact %p\n", contact);
+ g_hash_table_iter_init (&iter, contact->priv->hash);
+ while (g_hash_table_iter_next (&iter,
+ (gpointer)&key,
+ &value)) {
+ gchar *concat = g_strjoinv (",", (GStrv) value);
+ g_printerr (" %s=%s\n", key, concat);
+ g_free (concat);
+ }
+}
+
+static guint
+contact_hash (gconstpointer key)
+{
+ const SwContact *contact = key;
+ return g_str_hash (sw_contact_get (contact, "id"));
+}
+
+gboolean
+contact_equal (gconstpointer a, gconstpointer b)
+{
+ const SwContact *contact_a = a;
+ const SwContact *contact_b = b;
+
+ return g_str_equal (sw_contact_get (contact_a, "id"),
+ sw_contact_get (contact_b, "id"));
+}
+
+SwSet *
+sw_contact_set_new (void)
+{
+ return sw_set_new_full (contact_hash, contact_equal);
+}
+
+GHashTable *
+sw_contact_peek_hash (SwContact *contact)
+{
+ g_return_val_if_fail (SW_IS_CONTACT (contact), NULL);
+
+ return contact->priv->hash;
+}
+
+gboolean
+sw_contact_get_ready (SwContact *contact)
+{
+ return (contact->priv->remaining_fetches == 0);
+}
+
+void
+sw_contact_push_pending (SwContact *contact)
+{
+ g_atomic_int_inc (&(contact->priv->remaining_fetches));
+}
+
+void
+sw_contact_pop_pending (SwContact *contact)
+{
+ if (g_atomic_int_dec_and_test (&(contact->priv->remaining_fetches))) {
+ SW_DEBUG (CONTACT, "All outstanding fetches completed. Signalling ready: %s",
+ sw_contact_get (contact, "id"));
+ g_object_notify (G_OBJECT (contact), "ready");
+ }
+
+ sw_contact_touch (contact);
+}
+
+
+typedef struct {
+ SwContact *contact;
+ const gchar *key;
+ gboolean delays_ready;
+} RequestImageFetchClosure;
+
+static void
+_image_download_cb (const char *url,
+ char *file,
+ RequestImageFetchClosure *closure)
+{
+ SW_DEBUG (CONTACT, "Image fetched: %s to %s", url, file);
+ sw_contact_take (closure->contact,
+ closure->key,
+ file);
+
+ if (closure->delays_ready)
+ sw_contact_pop_pending (closure->contact);
+
+ g_object_unref (closure->contact);
+ g_slice_free (RequestImageFetchClosure, closure);
+}
+
+void
+sw_contact_request_image_fetch (SwContact *contact,
+ gboolean delays_ready,
+ const gchar *key,
+ const gchar *url)
+{
+ RequestImageFetchClosure *closure;
+
+ /* If this URL fetch should delay the contact being considered ready, or
+ * whether the contact is useful without this key.
+ */
+ if (delays_ready)
+ sw_contact_push_pending (contact);
+
+ closure = g_slice_new0 (RequestImageFetchClosure);
+
+ closure->key = g_intern_string (key);
+ closure->contact = g_object_ref (contact);
+ closure->delays_ready = delays_ready;
+
+ SW_DEBUG (CONTACT, "Scheduling fetch for %s on: %s",
+ url,
+ sw_contact_get (closure->contact, "id"));
+ sw_web_download_image_async (url,
+ (ImageDownloadCallback)_image_download_cb,
+ closure);
+}
+
+/*
+ * Construct a GValueArray from a SwContact. We use this to construct the
+ * data types that the wonderful dbus-glib needs to emit the signal
+ */
+GValueArray *
+_sw_contact_to_value_array (SwContact *contact)
+{
+ GValueArray *value_array;
+ time_t time;
+
+ time = sw_time_t_from_string (sw_contact_get (contact, "date"));
+
+ value_array = g_value_array_new (4);
+
+ value_array = g_value_array_append (value_array, NULL);
+ g_value_init (g_value_array_get_nth (value_array, 0), G_TYPE_STRING);
+ g_value_set_string (g_value_array_get_nth (value_array, 0),
+ sw_service_get_name (sw_contact_get_service (contact)));
+
+ value_array = g_value_array_append (value_array, NULL);
+ g_value_init (g_value_array_get_nth (value_array, 1), G_TYPE_STRING);
+ g_value_set_string (g_value_array_get_nth (value_array, 1),
+ sw_contact_get (contact, "id"));
+
+ value_array = g_value_array_append (value_array, NULL);
+ g_value_init (g_value_array_get_nth (value_array, 2), G_TYPE_INT64);
+ g_value_set_int64 (g_value_array_get_nth (value_array, 2),
+ time);
+
+ value_array = g_value_array_append (value_array, NULL);
+ g_value_init (g_value_array_get_nth (value_array, 3),
+ dbus_g_type_get_map ("GHashTable",
+ G_TYPE_STRING,
+ G_TYPE_STRV));
+
+ g_value_set_boxed (g_value_array_get_nth (value_array, 3),
+ sw_contact_peek_hash (contact));
+
+ return value_array;
+}
+
+void
+sw_contact_touch (SwContact *contact)
+{
+ contact->priv->mtime = time (NULL);
+
+ g_signal_emit (contact, signals[CHANGED_SIGNAL], 0);
+}
+
+time_t
+sw_contact_get_mtime (SwContact *contact)
+{
+ return contact->priv->mtime;
+}
+
+/* Intentionally don't compare the mtime */
+gboolean
+sw_contact_equal (SwContact *a,
+ SwContact *b)
+{
+ SwContactPrivate *priv_a = GET_PRIVATE (a);
+ SwContactPrivate *priv_b = GET_PRIVATE (b);
+ GHashTable *hash_a = priv_a->hash;
+ GHashTable *hash_b = priv_b->hash;
+ GHashTableIter iter_a;
+ gpointer key_a, value_a;
+ guint size_a, size_b;
+
+ if (priv_a->service != priv_b->service)
+ return FALSE;
+
+ if (priv_a->remaining_fetches != priv_b->remaining_fetches)
+ return FALSE;
+
+ size_a = g_hash_table_size (hash_a);
+ size_b = g_hash_table_size (hash_b);
+
+ if (sw_contact_get (a, "cached"))
+ size_a--;
+
+ if (sw_contact_get (b, "cached"))
+ size_b--;
+
+ if (size_a != size_b)
+ return FALSE;
+
+ g_hash_table_iter_init (&iter_a, hash_a);
+
+ while (g_hash_table_iter_next (&iter_a, &key_a, &value_a))
+ {
+ if (g_str_equal (key_a, "cached"))
+ continue;
+
+ GStrv value_b;
+ int i;
+ value_b = sw_contact_get_all (b, key_a);
+ if (g_strv_length (value_a) != g_strv_length (value_b))
+ return FALSE;
+
+ for (i = 0 ; i < g_strv_length (value_a) ; i++) {
+ if (!g_str_equal (((GStrv)value_a)[i], value_b[i]))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
diff --git a/libsocialweb/sw-contact.h b/libsocialweb/sw-contact.h
new file mode 100644
index 0000000..ea5cde7
--- /dev/null
+++ b/libsocialweb/sw-contact.h
@@ -0,0 +1,105 @@
+/*
+ * libsocialweb - social data store
+ * Copyright (C) 2008 - 2009 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _SW_CONTACT
+#define _SW_CONTACT
+
+#include <glib-object.h>
+#include <libsocialweb/sw-types.h>
+#include <libsocialweb/sw-service.h>
+#include <libsocialweb/sw-set.h>
+
+G_BEGIN_DECLS
+
+#define SW_TYPE_CONTACT sw_contact_get_type()
+
+#define SW_CONTACT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), SW_TYPE_CONTACT, SwContact))
+
+#define SW_CONTACT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), SW_TYPE_CONTACT, SwContactClass))
+
+#define SW_IS_CONTACT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SW_TYPE_CONTACT))
+
+#define SW_IS_CONTACT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), SW_TYPE_CONTACT))
+
+#define SW_CONTACT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), SW_TYPE_CONTACT, SwContactClass))
+
+typedef struct _SwContactPrivate SwContactPrivate;
+
+struct _SwContact {
+ GObject parent;
+ SwContactPrivate *priv;
+};
+
+typedef struct {
+ GObjectClass parent_class;
+ void (*changed)(SwContact *contact);
+} SwContactClass;
+
+GType sw_contact_get_type (void);
+
+SwContact* sw_contact_new (void);
+
+void sw_contact_set_service (SwContact *contact, SwService *service);
+
+SwService * sw_contact_get_service (SwContact *contact);
+
+void sw_contact_put (SwContact *contact,
+ const char *key,
+ const char *value);
+
+void sw_contact_take (SwContact *contact,
+ const char *key,
+ char *value);
+
+void sw_contact_request_image_fetch (SwContact *contact,
+ gboolean delays_ready,
+ const gchar *key,
+ const gchar *url);
+
+const char * sw_contact_get (const SwContact *contact, const char *key);
+
+void sw_contact_dump (SwContact *contact);
+
+GHashTable *sw_contact_peek_hash (SwContact *contact);
+
+gboolean sw_contact_get_ready (SwContact *contact);
+
+void sw_contact_push_pending (SwContact *contact);
+void sw_contact_pop_pending (SwContact *contact);
+
+void sw_contact_touch (SwContact *contact);
+time_t sw_contact_get_mtime (SwContact *contact);
+
+
+gboolean sw_contact_equal (SwContact *a,
+ SwContact *b);
+
+/* Convenience function */
+SwSet *sw_contact_set_new (void);
+
+/* Useful for emitting the signals */
+GValueArray *_sw_contact_to_value_array (SwContact *contact);
+
+G_END_DECLS
+
+#endif /* _SW_CONTACT */
diff --git a/libsocialweb/sw-debug.c b/libsocialweb/sw-debug.c
index 365ac46..cf2e57f 100644
--- a/libsocialweb/sw-debug.c
+++ b/libsocialweb/sw-debug.c
@@ -32,6 +32,7 @@ sw_debug_init (const char *string)
{ "views", SW_DEBUG_VIEWS },
{ "online", SW_DEBUG_ONLINE },
{ "item", SW_DEBUG_ITEM },
+ { "contact", SW_DEBUG_CONTACT },
{ "twitter", SW_DEBUG_TWITTER },
{ "lastfm", SW_DEBUG_LASTFM },
{ "core", SW_DEBUG_CORE },
diff --git a/libsocialweb/sw-debug.h b/libsocialweb/sw-debug.h
index 7e5eedf..68c37aa 100644
--- a/libsocialweb/sw-debug.h
+++ b/libsocialweb/sw-debug.h
@@ -23,15 +23,16 @@ typedef enum {
SW_DEBUG_VIEWS = 1 << 1,
SW_DEBUG_ONLINE = 1 << 2,
SW_DEBUG_ITEM = 1 << 3,
- SW_DEBUG_TWITTER = 1 << 4,
- SW_DEBUG_LASTFM = 1 << 5,
- SW_DEBUG_CORE = 1 << 6,
- SW_DEBUG_VIMEO = 1 << 7,
- SW_DEBUG_FLICKR = 1 << 8,
- SW_DEBUG_SMUGMUG = 1 << 9,
- SW_DEBUG_PHOTOBUCKET = 1 << 10,
- SW_DEBUG_FACEBOOK = 1 << 11,
- SW_DEBUG_CLIENT_MONITOR = 1 << 12
+ SW_DEBUG_CONTACT = 1 << 4,
+ SW_DEBUG_TWITTER = 1 << 5,
+ SW_DEBUG_LASTFM = 1 << 6,
+ SW_DEBUG_CORE = 1 << 7,
+ SW_DEBUG_VIMEO = 1 << 8,
+ SW_DEBUG_FLICKR = 1 << 9,
+ SW_DEBUG_SMUGMUG = 1 << 10,
+ SW_DEBUG_PHOTOBUCKET = 1 << 11,
+ SW_DEBUG_FACEBOOK = 1 << 12,
+ SW_DEBUG_CLIENT_MONITOR = 1 << 13
} SwDebugFlags;
extern guint sw_debug_flags;
diff --git a/libsocialweb/sw-types.h b/libsocialweb/sw-types.h
index 38fbcc0..cb52c41 100644
--- a/libsocialweb/sw-types.h
+++ b/libsocialweb/sw-types.h
@@ -29,6 +29,8 @@ typedef struct _SwSet SwSet;
typedef struct _SwItem SwItem;
+typedef struct _SwContact SwContact;
+
typedef struct _SwCore SwCore;
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]