[telepathy-glib/master] account: add features and feature functions

Jonny Lamb jonny.lamb at collabora.co.uk
Mon Sep 21 03:00:02 PDT 2009


Signed-off-by: Jonny Lamb <jonny.lamb at collabora.co.uk>
---
 docs/reference/telepathy-glib-sections.txt |    8 +-
 telepathy-glib/account-manager.c           |   17 +-
 telepathy-glib/account.c                   |  327 ++++++++++++++++++++++++----
 telepathy-glib/account.h                   |   17 ++-
 4 files changed, 319 insertions(+), 50 deletions(-)

diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt
index c2ca5af..e7a2ea1 100644
--- a/docs/reference/telepathy-glib-sections.txt
+++ b/docs/reference/telepathy-glib-sections.txt
@@ -3001,7 +3001,6 @@ tp_account_reconnect_async
 tp_account_reconnect_finish
 tp_account_is_enabled
 tp_account_is_valid
-tp_account_is_ready
 tp_account_update_parameters_async
 tp_account_update_parameters_finish
 tp_account_remove_async
@@ -3033,6 +3032,13 @@ tp_account_get_unique_name
 tp_account_get_avatar_async
 tp_account_get_avatar_finish
 <SUBSECTION>
+TP_ACCOUNT_FEATURE_CORE
+tp_account_is_ready
+tp_account_prepare_async
+tp_account_prepare_finish
+tp_account_get_features
+tp_account_set_features
+<SUBSECTION>
 tp_cli_account_callback_for_reconnect
 tp_cli_account_call_reconnect
 tp_cli_account_callback_for_remove
diff --git a/telepathy-glib/account-manager.c b/telepathy-glib/account-manager.c
index a92204d..04ffd1b 100644
--- a/telepathy-glib/account-manager.c
+++ b/telepathy-glib/account-manager.c
@@ -237,7 +237,7 @@ _tp_account_manager_check_ready (TpAccountManager *manager)
     {
       TpAccount *account = TP_ACCOUNT (value);
 
-      if (!tp_account_is_ready (account))
+      if (!tp_account_is_ready (account, TP_ACCOUNT_FEATURE_CORE))
         return;
     }
 
@@ -820,16 +820,16 @@ _tp_account_manager_account_removed_cb (TpAccount *account,
 }
 
 static void
-_tp_account_manager_account_ready_cb (GObject *object,
-    GParamSpec *spec,
+_tp_account_manager_account_ready_cb (GObject *source_object,
+    GAsyncResult *res,
     gpointer user_data)
 {
   TpAccountManager *manager = TP_ACCOUNT_MANAGER (user_data);
   TpAccountManagerPrivate *priv = manager->priv;
-  TpAccount *account = TP_ACCOUNT (object);
+  TpAccount *account = TP_ACCOUNT (source_object);
   GSimpleAsyncResult *result;
 
-  if (!tp_account_is_ready (account))
+  if (!tp_account_prepare_finish (account, res, NULL))
     return;
 
   /* see if there's any pending callbacks for this account */
@@ -910,6 +910,7 @@ tp_account_manager_ensure_account (TpAccountManager *manager,
 {
   TpAccountManagerPrivate *priv = manager->priv;
   TpAccount *account;
+  GQuark fs[] = { TP_ACCOUNT_FEATURE_CORE, 0 };
 
   account = g_hash_table_lookup (priv->accounts, path);
   if (account != NULL)
@@ -918,8 +919,8 @@ tp_account_manager_ensure_account (TpAccountManager *manager,
   account = tp_account_new (tp_proxy_get_dbus_daemon (manager), path, NULL);
   g_hash_table_insert (priv->accounts, g_strdup (path), account);
 
-  g_signal_connect (account, "notify::ready",
-      G_CALLBACK (_tp_account_manager_account_ready_cb), manager);
+  tp_account_prepare_async (account, fs, _tp_account_manager_account_ready_cb,
+      manager);
 
   return account;
 }
@@ -1048,7 +1049,7 @@ tp_account_manager_request_global_presence (TpAccountManager *manager,
     {
       TpAccount *account = TP_ACCOUNT (value);
 
-      if (tp_account_is_ready (account))
+      if (tp_account_is_ready (account, TP_ACCOUNT_FEATURE_CORE))
         tp_account_request_presence_async (account, type, status, message,
             NULL, NULL);
     }
diff --git a/telepathy-glib/account.c b/telepathy-glib/account.c
index c98dc8f..9819bf9 100644
--- a/telepathy-glib/account.c
+++ b/telepathy-glib/account.c
@@ -95,7 +95,6 @@ struct _TpAccountPrivate {
 
   gboolean enabled;
   gboolean valid;
-  gboolean ready;
   gboolean removed;
   /* Timestamp when the connection got connected in seconds since the epoch */
   glong connect_time;
@@ -107,8 +106,23 @@ struct _TpAccountPrivate {
   gchar *display_name;
 
   GHashTable *parameters;
+
+  /* Features. */
+  GList *features;
+  GArray *features_array;
+  GList *callbacks;
 };
 
+typedef struct {
+  GQuark name;
+  gboolean ready;
+} TpAccountFeature;
+
+typedef struct {
+  GSimpleAsyncResult *result;
+  GQuark *features;
+} TpAccountFeatureCallback;
+
 G_DEFINE_TYPE (TpAccount, tp_account, TP_TYPE_PROXY);
 
 /* signals */
@@ -127,7 +141,6 @@ enum {
   PROP_PRESENCE,
   PROP_STATUS,
   PROP_STATUS_MESSAGE,
-  PROP_READY,
   PROP_CONNECTION_STATUS,
   PROP_CONNECTION_STATUS_REASON,
   PROP_CONNECTION,
@@ -153,6 +166,79 @@ tp_account_init (TpAccount *self)
   self->priv->connection_status = TP_CONNECTION_STATUS_DISCONNECTED;
 }
 
+static TpAccountFeature *
+_tp_account_get_feature (TpAccount *self,
+    GQuark feature,
+    gboolean add)
+{
+  TpAccountPrivate *priv = self->priv;
+  GList *l;
+  TpAccountFeature *feat = NULL;
+
+  for (l = priv->features; l != NULL; l = l->next)
+    {
+      feat = l->data;
+
+      if (feat->name == feature)
+        break;
+    }
+
+  if (feat == NULL && add)
+    {
+      GQuark fs[] = { feature, 0 };
+      tp_account_set_features (self, fs);
+
+      /* New feature will be the first element as we use g_list_prepend */
+      feat = priv->features->data;
+    }
+
+  return feat;
+}
+
+static void
+_tp_account_become_ready (TpAccount *self,
+    GQuark feature)
+{
+  TpAccountPrivate *priv = self->priv;
+  TpAccountFeature *f = NULL;
+  GList *l, *remove = NULL;
+
+  f = _tp_account_get_feature (self, feature, TRUE);
+
+  if (f->ready)
+    return;
+
+  f->ready = TRUE;
+
+  for (l = priv->callbacks; l != NULL; l = l->next)
+    {
+      TpAccountFeatureCallback *cb = l->data;
+      gboolean ready = TRUE;
+      guint i;
+
+      for (i = 0; cb->features[i] != 0; i++)
+        {
+          if (!tp_account_is_ready (self, cb->features[i]))
+            {
+              ready = FALSE;
+              break;
+            }
+        }
+
+      if (ready)
+        {
+          remove = g_list_prepend (remove, l);
+          g_simple_async_result_complete (cb->result);
+          g_object_unref (cb->result);
+        }
+    }
+
+  for (l = remove; l != NULL; l = l->next)
+    priv->callbacks = g_list_delete_link (priv->callbacks, l->data);
+
+  g_list_free (remove);
+}
+
 static void
 _tp_account_removed_cb (TpAccount *self,
     gpointer unused G_GNUC_UNUSED,
@@ -480,11 +566,7 @@ _tp_account_update (TpAccount *account,
           parameters);
     }
 
-  if (!priv->ready)
-    {
-      priv->ready = TRUE;
-      g_object_notify (G_OBJECT (account), "ready");
-    }
+  _tp_account_become_ready (account, TP_ACCOUNT_FEATURE_CORE);
 
   if (priv->connection_status != old_s)
     {
@@ -551,7 +633,7 @@ _tp_account_properties_changed (TpAccount *proxy,
 {
   TpAccount *self = TP_ACCOUNT (weak_object);
 
-  if (!self->priv->ready)
+  if (!tp_account_is_ready (self, TP_ACCOUNT_FEATURE_CORE))
     return;
 
   _tp_account_update (self, properties);
@@ -572,6 +654,10 @@ _tp_account_constructed (GObject *object)
 
   g_return_if_fail (tp_proxy_get_dbus_daemon (self) != NULL);
 
+  priv->features = NULL;
+  priv->callbacks = NULL;
+  priv->features_array = g_array_new (TRUE, FALSE, sizeof (GQuark));
+
   sc = tp_cli_account_connect_to_removed (self, _tp_account_removed_cb,
       NULL, NULL, NULL, &error);
 
@@ -625,9 +711,6 @@ _tp_account_get_property (GObject *object,
     case PROP_ENABLED:
       g_value_set_boolean (value, self->priv->enabled);
       break;
-    case PROP_READY:
-      g_value_set_boolean (value, self->priv->ready);
-      break;
     case PROP_PRESENCE:
       g_value_set_uint (value, self->priv->presence);
       break;
@@ -706,6 +789,28 @@ _tp_account_dispose (GObject *object)
 }
 
 static void
+_tp_account_feature_free (gpointer data,
+    gpointer user_data)
+{
+  g_slice_free (TpAccountFeature, data);
+}
+
+static void
+_tp_account_feature_callback_free (gpointer data,
+    gpointer user_data)
+{
+  TpAccountFeatureCallback *cb = data;
+  GError e = { TP_ERRORS, TP_ERROR_NO_ANSWER,
+               "the TpAccount was disposed before the feature(s) became ready" };
+
+  g_simple_async_result_set_from_error (cb->result, &e);
+  g_simple_async_result_complete (cb->result);
+  g_object_unref (cb->result);
+
+  g_slice_free (TpAccountFeatureCallback, data);
+}
+
+static void
 _tp_account_finalize (GObject *object)
 {
   TpAccount *self = TP_ACCOUNT (object);
@@ -723,6 +828,16 @@ _tp_account_finalize (GObject *object)
   g_free (priv->icon_name);
   g_free (priv->display_name);
 
+  g_list_foreach (priv->features, _tp_account_feature_free, NULL);
+  g_list_free (priv->features);
+  priv->features = NULL;
+
+  g_list_foreach (priv->callbacks, _tp_account_feature_callback_free, NULL);
+  g_list_free (priv->callbacks);
+  priv->callbacks = NULL;
+
+  g_array_free (priv->features_array, TRUE);
+
   /* free any data held directly by the object here */
   if (G_OBJECT_CLASS (tp_account_parent_class)->finalize != NULL)
     G_OBJECT_CLASS (tp_account_parent_class)->finalize (object);
@@ -755,18 +870,6 @@ tp_account_class_init (TpAccountClass *klass)
           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
 
   /**
-   * TpAccount:ready:
-   *
-   * Whether this account is ready to be used or not.
-   */
-  g_object_class_install_property (object_class, PROP_READY,
-      g_param_spec_boolean ("ready",
-          "Ready",
-          "Whether this account is ready to be used",
-          FALSE,
-          G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
-
-  /**
    * TpAccount:presence:
    *
    * The account connection's presence type.
@@ -1313,22 +1416,6 @@ tp_account_is_enabled (TpAccount *account)
   return priv->enabled;
 }
 
-/**
- * tp_account_is_ready:
- * @account: a #TpAccount
- *
- * <!-- -->
- *
- * Returns: the same thing as the #TpAccount:ready property
- */
-gboolean
-tp_account_is_ready (TpAccount *account)
-{
-  TpAccountPrivate *priv = account->priv;
-
-  return priv->ready;
-}
-
 static void
 _tp_account_property_set_cb (TpProxy *proxy,
     const GError *error,
@@ -2225,3 +2312,165 @@ tp_account_get_avatar_finish (TpAccount *account,
   return g_simple_async_result_get_op_res_gpointer (
       G_SIMPLE_ASYNC_RESULT (result));
 }
+
+/**
+ * TP_ACCOUNT_FEATURE_CORE:
+ *
+ * Expands to a call to a function that returns a quark for the "core" feature
+ * on a #TpAccount.
+ */
+
+/**
+ * tp_account_is_ready:
+ * @account: a #TpAccount
+ * @feature: a feature which is required
+ * @error: a #GError to fill
+ *
+ * <!-- -->
+ *
+ * Returns: %TRUE whether @feature is ready on @account, otherwise %FALSE
+ */
+gboolean
+tp_account_is_ready (TpAccount *account,
+    GQuark feature)
+{
+  TpAccountFeature *f;
+
+  f = _tp_account_get_feature (account, feature, FALSE);
+
+  if (f == NULL)
+    return FALSE;
+
+  return f->ready;
+}
+
+/**
+ * tp_account_prepare_async:
+ * @account: a #TpAccount
+ * @features: a 0-terminated list of features
+ * @callback: a callback to call when the request is satisfied
+ * @user_data: data to pass to @callback
+ *
+ * Requests an asynchronous preparation of @account with the features specified
+ * by @features. When the operation is finished, @callback will be called. You
+ * can then call tp_account_prepare_finish() to get the result of the
+ * operation.
+ */
+void
+tp_account_prepare_async (TpAccount *account,
+    GQuark* features,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  TpAccountPrivate *priv = account->priv;
+  GSimpleAsyncResult *result;
+  guint i;
+  gboolean already_ready = TRUE;
+
+  result = g_simple_async_result_new (G_OBJECT (account),
+      callback, user_data, tp_account_prepare_finish);
+
+  for (i = 0; features[i] != 0; i++)
+    {
+      TpAccountFeature *f;
+
+      f = _tp_account_get_feature (account, features[i], TRUE);
+
+      if (!f->ready)
+        {
+          already_ready = FALSE;
+          break;
+        }
+    }
+
+  if (already_ready)
+    {
+      g_simple_async_result_complete (result);
+      g_object_unref (result);
+    }
+  else
+    {
+      TpAccountFeatureCallback *cb;
+
+      cb = g_slice_new0 (TpAccountFeatureCallback);
+      cb->result = result;
+      cb->features = features;
+      priv->callbacks = g_list_prepend (priv->callbacks, cb);
+    }
+}
+
+/**
+ * tp_account_prepare_finish:
+ * @account: a #TpAccount
+ * @result: a #GAsyncResult
+ * @error: a #GError to fill
+ *
+ * Finishes an async preparation of the account @account.
+ *
+ * Returns: %TRUE if the preparation was successful, otherwise %FALSE
+ */
+gboolean
+tp_account_prepare_finish (TpAccount *account,
+    GAsyncResult *result,
+    GError **error)
+{
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
+          error) ||
+      !g_simple_async_result_is_valid (result, G_OBJECT (account),
+          tp_account_prepare_finish))
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * tp_account_set_features:
+ * @account: a #TpAccount
+ * @features: a 0-terminated list of features
+ *
+ * Sets additional features on @account. Features cannot be removed from
+ * an object. Features which are already set on @account will be ignored.
+ *
+ * Returns: %TRUE if the set was successful, otherwise %FALSE
+ */
+gboolean
+tp_account_set_features (TpAccount *account,
+    const GQuark* features)
+{
+  TpAccountPrivate *priv = account->priv;
+  guint i;
+
+  for (i = 0; features[i] != 0; i++)
+    {
+      TpAccountFeature *feature;
+      GQuark f = features[i];
+
+      if (_tp_account_get_feature (account, f, FALSE) != NULL)
+        continue;
+
+      feature = g_slice_new0 (TpAccountFeature);
+      feature->name = f;
+      feature->ready = FALSE;
+      /* If _prepend is changed, _get_feature must also be changed.
+       * (see comment there) */
+      priv->features = g_list_prepend (priv->features, feature);
+
+      g_array_append_val (priv->features_array, f);
+    }
+
+  return TRUE;
+}
+
+/**
+ * tp_account_get_features:
+ * @account: a #TpAccount
+ *
+ * <!-- -->
+ *
+ * Returns: a 0-terminated list of features set on @account
+ */
+const GQuark *
+tp_account_get_features (TpAccount *account)
+{
+  return (const GQuark *) account->priv->features_array->data;
+}
diff --git a/telepathy-glib/account.h b/telepathy-glib/account.h
index fad80d6..fcc3d21 100644
--- a/telepathy-glib/account.h
+++ b/telepathy-glib/account.h
@@ -66,6 +66,9 @@ GType tp_account_get_type (void);
   (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TYPE_ACCOUNT, \
                               TpAccountClass))
 
+#define TP_ACCOUNT_FEATURE_CORE \
+  g_quark_from_static_string ("tp-account-feature-core")
+
 TpAccount *tp_account_new (TpDBusDaemon *bus_daemon, const gchar *object_path,
     GError **error);
 
@@ -102,8 +105,6 @@ gboolean tp_account_is_enabled (TpAccount *account);
 
 gboolean tp_account_is_valid (TpAccount *account);
 
-gboolean tp_account_is_ready (TpAccount *account);
-
 void tp_account_update_parameters_async (TpAccount *account,
     GHashTable *parameters, const gchar **unset_parameters,
     GAsyncReadyCallback callback, gpointer user_data);
@@ -187,6 +188,18 @@ void tp_account_get_avatar_async (TpAccount *account,
 const GArray *tp_account_get_avatar_finish (TpAccount *account,
     GAsyncResult *result, GError **error);
 
+gboolean tp_account_is_ready (TpAccount *account, GQuark feature);
+
+void tp_account_prepare_async (TpAccount *account, GQuark* features,
+    GAsyncReadyCallback callback, gpointer user_data);
+
+gboolean tp_account_prepare_finish (TpAccount *account, GAsyncResult *result,
+    GError **error);
+
+gboolean tp_account_set_features (TpAccount *account, const GQuark* features);
+
+const GQuark * tp_account_get_features (TpAccount *account);
+
 G_END_DECLS
 
 #include <telepathy-glib/_gen/tp-cli-account.h>
-- 
1.5.6.5




More information about the telepathy-commits mailing list