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

Jonny Lamb jonny.lamb at collabora.co.uk
Mon Sep 21 05:35:37 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           |  369 +++++++++++++++++++++++-----
 telepathy-glib/account-manager.h           |   19 ++-
 telepathy-glib/account.c                   |    3 +-
 4 files changed, 330 insertions(+), 69 deletions(-)

diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt
index e7a2ea1..9097d2b 100644
--- a/docs/reference/telepathy-glib-sections.txt
+++ b/docs/reference/telepathy-glib-sections.txt
@@ -3081,9 +3081,15 @@ tp_account_manager_get_account_for_connection
 tp_account_manager_get_accounts
 tp_account_manager_get_global_presence
 tp_account_manager_get_requested_global_presence
-tp_account_manager_is_ready
 tp_account_manager_request_global_presence
 <SUBSECTION>
+TP_ACCOUNT_MANAGER_FEATURE_CORE
+tp_account_manager_is_ready
+tp_account_manager_prepare_async
+tp_account_manager_prepare_finish
+tp_account_manager_set_features
+tp_account_manager_get_features
+<SUBSECTION>
 tp_cli_account_manager_callback_for_create_account
 tp_cli_account_manager_call_create_account
 tp_cli_account_manager_signal_callback_account_removed
diff --git a/telepathy-glib/account-manager.c b/telepathy-glib/account-manager.c
index 04ffd1b..42a0e95 100644
--- a/telepathy-glib/account-manager.c
+++ b/telepathy-glib/account-manager.c
@@ -65,7 +65,6 @@ struct _TpAccountManagerPrivate {
   /* (owned) object path -> (reffed) TpAccount */
   GHashTable *accounts;
   gboolean dispose_run;
-  gboolean ready;
 
   /* global presence */
   TpAccount *global_account;
@@ -81,8 +80,23 @@ struct _TpAccountManagerPrivate {
   gchar *requested_status_message;
 
   GHashTable *create_results;
+
+  /* Features */
+  GList *features;
+  GList *callbacks;
+  GArray *features_array;
 };
 
+typedef struct {
+  GQuark name;
+  gboolean ready;
+} TpAccountManagerFeature;
+
+typedef struct {
+  GSimpleAsyncResult *result;
+  GQuark *features;
+} TpAccountManagerFeatureCallback;
+
 #define MC5_BUS_NAME "org.freedesktop.Telepathy.MissionControl5"
 
 enum {
@@ -97,14 +111,83 @@ enum {
   LAST_SIGNAL
 };
 
-enum {
-  PROP_READY = 1,
-};
-
 static guint signals[LAST_SIGNAL];
 
 G_DEFINE_TYPE (TpAccountManager, tp_account_manager, TP_TYPE_PROXY);
 
+static TpAccountManagerFeature *
+_tp_account_manager_get_feature (TpAccountManager *self,
+    GQuark feature,
+    gboolean add)
+{
+  TpAccountManagerPrivate *priv = self->priv;
+  GList *l;
+  TpAccountManagerFeature *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_manager_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_manager_become_ready (TpAccountManager *self,
+    GQuark feature)
+{
+  TpAccountManagerPrivate *priv = self->priv;
+  TpAccountManagerFeature *f = NULL;
+  GList *l, *remove = NULL;
+
+  f = _tp_account_manager_get_feature (self, feature, TRUE);
+
+  if (f->ready)
+    return;
+
+  f->ready = TRUE;
+
+  for (l = priv->callbacks; l != NULL; l = l->next)
+    {
+      TpAccountManagerFeatureCallback *cb = l->data;
+      gboolean ready = TRUE;
+      guint i;
+
+      for (i = 0; cb->features[i] != 0; i++)
+        {
+          if (!tp_account_manager_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_manager_init (TpAccountManager *self)
 {
@@ -223,13 +306,13 @@ _tp_account_manager_ensure_all_accounts (TpAccountManager *manager,
 }
 
 static void
-_tp_account_manager_check_ready (TpAccountManager *manager)
+_tp_account_manager_check_core_ready (TpAccountManager *manager)
 {
   TpAccountManagerPrivate *priv = manager->priv;
   GHashTableIter iter;
   gpointer value;
 
-  if (priv->ready)
+  if (tp_account_manager_is_ready (manager, TP_ACCOUNT_MANAGER_FEATURE_CORE))
     return;
 
   g_hash_table_iter_init (&iter, priv->accounts);
@@ -250,8 +333,7 @@ _tp_account_manager_check_ready (TpAccountManager *manager)
           priv->requested_status_message);
     }
 
-  priv->ready = TRUE;
-  g_object_notify (G_OBJECT (manager), "ready");
+  _tp_account_manager_become_ready (manager, TP_ACCOUNT_MANAGER_FEATURE_CORE);
 }
 
 static void
@@ -276,7 +358,7 @@ _tp_account_manager_got_all_cb (TpProxy *proxy,
   if (accounts != NULL)
     _tp_account_manager_ensure_all_accounts (manager, accounts);
 
-  _tp_account_manager_check_ready (manager);
+  _tp_account_manager_check_core_ready (manager);
 }
 
 static void
@@ -300,12 +382,17 @@ _tp_account_manager_constructed (GObject *object)
   TpAccountManager *self = TP_ACCOUNT_MANAGER (object);
   void (*chain_up) (GObject *) =
     ((GObjectClass *) tp_account_manager_parent_class)->constructed;
+  TpAccountManagerPrivate *priv = self->priv;
 
   if (chain_up != NULL)
     chain_up (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));
+
   tp_dbus_daemon_watch_name_owner (tp_proxy_get_dbus_daemon (self),
       TP_ACCOUNT_MANAGER_BUS_NAME, _tp_account_manager_name_owner_cb,
       self, NULL);
@@ -321,6 +408,28 @@ _tp_account_manager_constructed (GObject *object)
 }
 
 static void
+_tp_account_manager_feature_free (gpointer data,
+    gpointer user_data)
+{
+  g_slice_free (TpAccountManagerFeature, data);
+}
+
+static void
+_tp_account_manager_feature_callback_free (gpointer data,
+    gpointer user_data)
+{
+  TpAccountManagerFeatureCallback *cb = data;
+  GError e = { TP_ERRORS, TP_ERROR_NO_ANSWER,
+               "the TpAccountManager 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 (TpAccountManagerFeatureCallback, data);
+}
+
+static void
 _tp_account_manager_finalize (GObject *object)
 {
   TpAccountManager *manager = TP_ACCOUNT_MANAGER (object);
@@ -335,6 +444,17 @@ _tp_account_manager_finalize (GObject *object)
   g_free (priv->requested_status);
   g_free (priv->requested_status_message);
 
+  g_list_foreach (priv->features, _tp_account_manager_feature_free, NULL);
+  g_list_free (priv->features);
+  priv->features = NULL;
+
+  g_list_foreach (priv->callbacks, _tp_account_manager_feature_callback_free,
+      NULL);
+  g_list_free (priv->callbacks);
+  priv->callbacks = NULL;
+
+  g_array_free (priv->features_array, TRUE);
+
   G_OBJECT_CLASS (tp_account_manager_parent_class)->finalize (object);
 }
 
@@ -372,25 +492,6 @@ _tp_account_manager_dispose (GObject *object)
 }
 
 static void
-_tp_account_manager_get_property (GObject *object,
-    guint prop_id,
-    GValue *value,
-    GParamSpec *pspec)
-{
-  TpAccountManager *self = TP_ACCOUNT_MANAGER (object);
-
-  switch (prop_id)
-    {
-    case PROP_READY:
-      g_value_set_boolean (value, self->priv->ready);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
 tp_account_manager_class_init (TpAccountManagerClass *klass)
 {
   TpProxyClass *proxy_class = (TpProxyClass *) klass;
@@ -401,27 +502,11 @@ tp_account_manager_class_init (TpAccountManagerClass *klass)
   object_class->constructed = _tp_account_manager_constructed;
   object_class->finalize = _tp_account_manager_finalize;
   object_class->dispose = _tp_account_manager_dispose;
-  object_class->get_property = _tp_account_manager_get_property;
 
   proxy_class->interface = TP_IFACE_QUARK_ACCOUNT_MANAGER;
   tp_account_manager_init_known_interfaces ();
 
   /**
-   * TpAccountManager:ready:
-   *
-   * Initially FALSE; changes to TRUE when every account in the manager is
-   * individually ready.
-   *
-   * Since: 0.7.UNRELEASED
-   */
-  g_object_class_install_property (object_class, PROP_READY,
-      g_param_spec_boolean ("ready",
-          "Ready",
-          "Whether the initial state dump from the account manager is finished",
-          FALSE,
-          G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
-
-  /**
    * TpAccountManager::account-created:
    * @manager: a #TpAccountManager
    * @account: a #TpAccount
@@ -862,7 +947,7 @@ _tp_account_manager_account_ready_cb (GObject *source_object,
   g_signal_connect (account, "removed",
       G_CALLBACK (_tp_account_manager_account_removed_cb), manager);
 
-  _tp_account_manager_check_ready (manager);
+  _tp_account_manager_check_core_ready (manager);
 }
 
 /**
@@ -926,25 +1011,6 @@ tp_account_manager_ensure_account (TpAccountManager *manager,
 }
 
 /**
- * tp_account_manager_is_ready:
- * @manager: a #TpAccountManager
- *
- * <!-- -->
- *
- * Returns: %TRUE if the @manager and all its accounts are ready, otherwise
- *          %FALSE
- *
- * Since: 0.7.UNRELEASED
- */
-gboolean
-tp_account_manager_is_ready (TpAccountManager *manager)
-{
-  TpAccountManagerPrivate *priv = manager->priv;
-
-  return priv->ready;
-}
-
-/**
  * tp_account_manager_get_account_for_connection:
  * @manager: a #TpAccountManager
  * @connection: a #TpConnection
@@ -1226,3 +1292,176 @@ tp_account_manager_create_account_finish (TpAccountManager *manager,
   return retval;
 }
 
+/**
+ * TP_ACCOUNT_MANAGER_FEATURE_CORE:
+ *
+ * Expands to a call to a function that returns a quark for the "core" feature
+ * on a #TpAccountManager.
+ *
+ * Since: 0.7.UNRELEASED
+ */
+
+/**
+ * tp_account_manager_is_ready:
+ * @manager: a #TpAccountManager
+ * @feature: a feature which is required
+ * @error: a #GError to fill
+ *
+ * <!-- -->
+ *
+ * Returns: %TRUE whether @feature is ready on @manager, otherwise %FALSE
+ *
+ * Since: 0.7.UNRELEASED
+ */
+gboolean
+tp_account_manager_is_ready (TpAccountManager *manager,
+    GQuark feature)
+{
+  TpAccountManagerFeature *f;
+
+  f = _tp_account_manager_get_feature (manager, feature, FALSE);
+
+  if (f == NULL)
+    return FALSE;
+
+  return f->ready;
+}
+
+/**
+ * tp_account_manager_prepare_async:
+ * @manager: a #TpAccountManager
+ * @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 @manager with the features specified
+ * by @features. When the operation is finished, @callback will be called. You
+ * can then call tp_account_manager_prepare_finish() to get the result of the
+ * operation.
+ *
+ * Since: 0.7.UNRELEASED
+ */
+void
+tp_account_manager_prepare_async (TpAccountManager *manager,
+    GQuark* features,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  TpAccountManagerPrivate *priv = manager->priv;
+  GSimpleAsyncResult *result;
+  guint i;
+  gboolean already_ready = TRUE;
+
+  result = g_simple_async_result_new (G_OBJECT (manager),
+      callback, user_data, tp_account_manager_prepare_finish);
+
+  for (i = 0; features[i] != 0; i++)
+    {
+      TpAccountManagerFeature *f;
+
+      f = _tp_account_manager_get_feature (manager, features[i], TRUE);
+
+      if (!f->ready)
+        {
+          already_ready = FALSE;
+          break;
+        }
+    }
+
+  if (already_ready)
+    {
+      g_simple_async_result_complete (result);
+      g_object_unref (result);
+    }
+  else
+    {
+      TpAccountManagerFeatureCallback *cb;
+
+      cb = g_slice_new0 (TpAccountManagerFeatureCallback);
+      cb->result = result;
+      cb->features = features;
+      priv->callbacks = g_list_prepend (priv->callbacks, cb);
+    }
+}
+
+/**
+ * tp_account_manager_prepare_finish:
+ * @manager: a #TpAccountManager
+ * @result: a #GAsyncResult
+ * @error: a #GError to fill
+ *
+ * Finishes an async preparation of the account manager @manager.
+ *
+ * Returns: %TRUE if the preparation was successful, otherwise %FALSE
+ *
+ * Since: 0.7.UNRELEASED
+ */
+gboolean
+tp_account_manager_prepare_finish (TpAccountManager *manager,
+    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 (manager),
+          tp_account_manager_prepare_finish))
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * tp_account_manager_set_features:
+ * @manager: a #TpAccountManager
+ * @features: a 0-terminated list of features
+ *
+ * Sets additional features on @manager. Features cannot be removed from
+ * an object. Features which are already set on @manager will be ignored.
+ *
+ * Returns: %TRUE if the set was successful, otherwise %FALSE
+ *
+ * Since: 0.7.UNRELEASED
+ */
+gboolean
+tp_account_manager_set_features (TpAccountManager *manager,
+    const GQuark* features)
+{
+  TpAccountManagerPrivate *priv = manager->priv;
+  guint i;
+
+  for (i = 0; features[i] != 0; i++)
+    {
+      TpAccountManagerFeature *feature;
+      GQuark f = features[i];
+
+      if (_tp_account_manager_get_feature (manager, f, FALSE) != NULL)
+        continue;
+
+      feature = g_slice_new0 (TpAccountManagerFeature);
+      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_manager_get_features:
+ * @manager: a #TpAccountManager
+ *
+ * <!-- -->
+ *
+ * Returns: a 0-terminated list of features set on @manager
+ *
+ * Since: 0.7.UNRELEASED
+ */
+const GQuark *
+tp_account_manager_get_features (TpAccountManager *manager)
+{
+  return (const GQuark *) manager->priv->features_array->data;
+}
diff --git a/telepathy-glib/account-manager.h b/telepathy-glib/account-manager.h
index 0a3d24d..a5eae80 100644
--- a/telepathy-glib/account-manager.h
+++ b/telepathy-glib/account-manager.h
@@ -64,14 +64,15 @@ GType tp_account_manager_get_type (void);
   (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TYPE_ACCOUNT_MANAGER, \
                               TpAccountManagerClass))
 
+#define TP_ACCOUNT_MANAGER_FEATURE_CORE \
+  g_quark_from_static_string ("tp-account-manager-feature-core")
+
 TpAccountManager *tp_account_manager_new (TpDBusDaemon *bus_daemon);
 
 TpAccountManager *tp_account_manager_dup (void);
 
 void tp_account_manager_init_known_interfaces (void);
 
-gboolean tp_account_manager_is_ready (TpAccountManager *manager);
-
 TpAccount *tp_account_manager_get_account_for_connection (
     TpAccountManager *manager, TpConnection *connection);
 
@@ -100,6 +101,20 @@ void tp_account_manager_create_account_async (TpAccountManager *manager,
 TpAccount * tp_account_manager_create_account_finish (
     TpAccountManager *manager, GAsyncResult *result, GError **error);
 
+gboolean tp_account_manager_is_ready (TpAccountManager *account,
+    GQuark feature);
+
+void tp_account_manager_prepare_async (TpAccountManager *account,
+    GQuark* features, GAsyncReadyCallback callback, gpointer user_data);
+
+gboolean tp_account_manager_prepare_finish (TpAccountManager *account,
+    GAsyncResult *result, GError **error);
+
+gboolean tp_account_manager_set_features (TpAccountManager *account,
+    const GQuark* features);
+
+const GQuark * tp_account_manager_get_features (TpAccountManager *account);
+
 G_END_DECLS
 
 #include <telepathy-glib/_gen/tp-cli-account-manager.h>
diff --git a/telepathy-glib/account.c b/telepathy-glib/account.c
index 9819bf9..8e940b5 100644
--- a/telepathy-glib/account.c
+++ b/telepathy-glib/account.c
@@ -1524,7 +1524,8 @@ tp_account_set_enabled_async (TpAccount *account,
     {
       account_manager = tp_account_manager_dup ();
 
-      if (tp_account_manager_is_ready (account_manager))
+      if (tp_account_manager_is_ready (account_manager,
+              TP_ACCOUNT_MANAGER_FEATURE_CORE))
         {
           _tp_account_set_presence_from_global (account_manager, account);
           g_object_unref (account_manager);
-- 
1.5.6.5




More information about the telepathy-commits mailing list