]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-individual-manager.c
Merge branch 'sasl'
[empathy.git] / libempathy / empathy-individual-manager.c
index cb3a3975ecda0bbc86c8f4ebdabd49333b29f133..82884e4d1f6e26003e5d5739afcf4261cc4556a0 100644 (file)
@@ -34,8 +34,6 @@
 #include <extensions/extensions.h>
 
 #include "empathy-individual-manager.h"
-#include "empathy-contact-manager.h"
-#include "empathy-contact-list.h"
 #include "empathy-marshal.h"
 #include "empathy-utils.h"
 
 
 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIndividualManager)
 
-/* This class doesn't store or ref any of the individuals, since they're already
- * stored and referenced in the aggregator.
+/* This class only stores and refs Individuals who contain an EmpathyContact.
  *
  * This class merely forwards along signals from the aggregator and individuals
  * and wraps aggregator functions for other client code. */
 typedef struct
 {
   FolksIndividualAggregator *aggregator;
-  EmpathyContactManager *contact_manager;
+  GHashTable *individuals; /* Individual.id -> Individual */
 } EmpathyIndividualManagerPriv;
 
 enum
@@ -91,6 +88,76 @@ individual_notify_is_favourite_cb (FolksIndividual *individual,
       is_favourite);
 }
 
+static void
+add_individual (EmpathyIndividualManager *self, FolksIndividual *individual)
+{
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+
+  g_hash_table_insert (priv->individuals,
+      (gpointer) folks_individual_get_id (individual),
+      g_object_ref (individual));
+
+  g_signal_connect (individual, "group-changed",
+      G_CALLBACK (individual_group_changed_cb), self);
+  g_signal_connect (individual, "notify::is-favourite",
+      G_CALLBACK (individual_notify_is_favourite_cb), self);
+}
+
+static void
+remove_individual (EmpathyIndividualManager *self, FolksIndividual *individual)
+{
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+
+  g_signal_handlers_disconnect_by_func (individual,
+      individual_group_changed_cb, self);
+  g_signal_handlers_disconnect_by_func (individual,
+      individual_notify_is_favourite_cb, self);
+
+  g_hash_table_remove (priv->individuals, folks_individual_get_id (individual));
+}
+
+/* This is emitted for *all* individuals in the individual aggregator (not
+ * just the ones we keep a reference to), to allow for the case where a new
+ * individual doesn't contain an EmpathyContact, but later has a persona added
+ * which does. */
+static void
+individual_notify_personas_cb (FolksIndividual *individual,
+    GParamSpec *pspec,
+    EmpathyIndividualManager *self)
+{
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+
+  const gchar *id = folks_individual_get_id (individual);
+  gboolean has_contact = empathy_folks_individual_contains_contact (individual);
+  gboolean had_contact = (g_hash_table_lookup (priv->individuals,
+      id) != NULL) ? TRUE : FALSE;
+
+  if (had_contact == TRUE && has_contact == FALSE)
+    {
+      GList *removed = NULL;
+
+      /* The Individual has lost its EmpathyContact */
+      removed = g_list_prepend (removed, individual);
+      g_signal_emit (self, signals[MEMBERS_CHANGED], 0, NULL, NULL, removed,
+          TP_CHANNEL_GROUP_CHANGE_REASON_NONE /* FIXME */);
+      g_list_free (removed);
+
+      remove_individual (self, individual);
+    }
+  else if (had_contact == FALSE && has_contact == TRUE)
+    {
+      GList *added = NULL;
+
+      /* The Individual has gained its first EmpathyContact */
+      add_individual (self, individual);
+
+      added = g_list_prepend (added, individual);
+      g_signal_emit (self, signals[MEMBERS_CHANGED], 0, NULL, added, NULL,
+          TP_CHANNEL_GROUP_CHANGE_REASON_NONE /* FIXME */);
+      g_list_free (added);
+    }
+}
+
 static void
 aggregator_individuals_changed_cb (FolksIndividualAggregator *aggregator,
     GList *added,
@@ -100,28 +167,50 @@ aggregator_individuals_changed_cb (FolksIndividualAggregator *aggregator,
     guint reason,
     EmpathyIndividualManager *self)
 {
-  GList *l;
+  EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
+  GList *l, *added_filtered = NULL;
 
-  for (l = added; l; l = l->next)
+  /* Handle the removals first, as one of the added Individuals might have the
+   * same ID as one of the removed Individuals (due to linking). */
+  for (l = removed; l; l = l->next)
     {
-      g_signal_connect (l->data, "group-changed",
-          G_CALLBACK (individual_group_changed_cb), self);
-      g_signal_connect (l->data, "notify::is-favourite",
-          G_CALLBACK (individual_notify_is_favourite_cb), self);
+      FolksIndividual *ind = FOLKS_INDIVIDUAL (l->data);
+
+      g_signal_handlers_disconnect_by_func (ind,
+          individual_notify_personas_cb, self);
+
+      if (g_hash_table_lookup (priv->individuals,
+          folks_individual_get_id (ind)) != NULL)
+        remove_individual (self, ind);
     }
 
-  for (l = removed; l; l = l->next)
+  /* Filter the individuals for ones which contain EmpathyContacts */
+  for (l = added; l; l = l->next)
     {
-      g_signal_handlers_disconnect_by_func (l->data,
-          individual_group_changed_cb, self);
-      g_signal_handlers_disconnect_by_func (l->data,
-          individual_notify_is_favourite_cb, self);
+      FolksIndividual *ind = FOLKS_INDIVIDUAL (l->data);
+
+      g_signal_connect (ind, "notify::personas",
+          G_CALLBACK (individual_notify_personas_cb), self);
+
+      if (empathy_folks_individual_contains_contact (ind) == TRUE)
+        {
+          add_individual (self, ind);
+          added_filtered = g_list_prepend (added_filtered, ind);
+        }
     }
 
+  /* Bail if we have no individuals left */
+  if (added_filtered == NULL && removed == NULL)
+    return;
+
+  added_filtered = g_list_reverse (added_filtered);
+
   g_signal_emit (self, signals[MEMBERS_CHANGED], 0, message,
-      added, removed,
-      tp_chanel_group_change_reason_from_folks_groups_change_reason (reason),
+      added_filtered, removed,
+      tp_channel_group_change_reason_from_folks_groups_change_reason (reason),
       TRUE);
+
+  g_list_free (added_filtered);
 }
 
 static void
@@ -129,7 +218,7 @@ individual_manager_dispose (GObject *object)
 {
   EmpathyIndividualManagerPriv *priv = GET_PRIV (object);
 
-  tp_clear_object (&priv->contact_manager);
+  g_hash_table_destroy (priv->individuals);
   tp_clear_object (&priv->aggregator);
 
   G_OBJECT_CLASS (empathy_individual_manager_parent_class)->dispose (object);
@@ -223,11 +312,13 @@ empathy_individual_manager_init (EmpathyIndividualManager *self)
       EMPATHY_TYPE_INDIVIDUAL_MANAGER, EmpathyIndividualManagerPriv);
 
   self->priv = priv;
-  priv->contact_manager = empathy_contact_manager_dup_singleton ();
+  priv->individuals = g_hash_table_new_full (g_str_hash, g_str_equal,
+      NULL, g_object_unref);
 
   priv->aggregator = folks_individual_aggregator_new ();
   g_signal_connect (priv->aggregator, "individuals-changed",
       G_CALLBACK (aggregator_individuals_changed_cb), self);
+  folks_individual_aggregator_prepare (priv->aggregator, NULL, NULL);
 }
 
 EmpathyIndividualManager *
@@ -240,12 +331,10 @@ GList *
 empathy_individual_manager_get_members (EmpathyIndividualManager *self)
 {
   EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
-  GHashTable *individuals;
 
   g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (self), NULL);
 
-  individuals = folks_individual_aggregator_get_individuals (priv->aggregator);
-  return individuals ? g_hash_table_get_values (individuals) : NULL;
+  return g_hash_table_get_values (priv->individuals);
 }
 
 FolksIndividual *
@@ -253,15 +342,10 @@ empathy_individual_manager_lookup_member (EmpathyIndividualManager *self,
     const gchar *id)
 {
   EmpathyIndividualManagerPriv *priv = GET_PRIV (self);
-  GHashTable *individuals;
 
   g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (self), NULL);
 
-  individuals = folks_individual_aggregator_get_individuals (priv->aggregator);
-  if (individuals != NULL)
-    return g_hash_table_lookup (individuals, id);
-
-  return NULL;
+  return g_hash_table_lookup (priv->individuals, id);
 }
 
 static void
@@ -282,12 +366,16 @@ aggregator_add_persona_from_details_cb (GObject *source,
       g_clear_error (&error);
     }
 
-  /* Set the contact's persona */
-  empathy_contact_set_persona (contact, persona);
+  /* The persona can be NULL even if there wasn't an error, if the persona was
+   * already in the contact list */
+  if (persona != NULL)
+    {
+      /* Set the contact's persona */
+      empathy_contact_set_persona (contact, persona);
+      g_object_unref (persona);
+    }
 
-  /* We can unref the contact now */
   g_object_unref (contact);
-  g_object_unref (persona);
 }
 
 void
@@ -325,6 +413,23 @@ empathy_individual_manager_add_from_contact (EmpathyIndividualManager *self,
   g_hash_table_destroy (details);
 }
 
+static void
+aggregator_remove_individual_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  FolksIndividualAggregator *aggregator = FOLKS_INDIVIDUAL_AGGREGATOR (source);
+  GError *error = NULL;
+
+  folks_individual_aggregator_remove_individual_finish (
+      aggregator, result, &error);
+  if (error != NULL)
+    {
+      g_warning ("failed to remove individual: %s", error->message);
+      g_clear_error (&error);
+    }
+}
+
 /**
  * Removes the inner contact from the server (and thus the Individual). Not
  * meant for de-shelling inner personas from an Individual.
@@ -343,9 +448,26 @@ empathy_individual_manager_remove (EmpathyIndividualManager *self,
 
   DEBUG ("removing individual %s (%s)",
       folks_individual_get_id (individual),
-      folks_individual_get_alias (individual));
+      folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
 
-  folks_individual_aggregator_remove_individual (priv->aggregator, individual);
+  folks_individual_aggregator_remove_individual (priv->aggregator, individual,
+      aggregator_remove_individual_cb, self);
+}
+
+static void
+groups_change_group_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  FolksGroupable *groupable = FOLKS_GROUPABLE (source);
+  GError *error = NULL;
+
+  folks_groupable_change_group_finish (groupable, result, &error);
+  if (error != NULL)
+    {
+      g_warning ("failed to change group: %s", error->message);
+      g_clear_error (&error);
+    }
 }
 
 static void
@@ -353,7 +475,8 @@ remove_group_cb (const gchar *id,
     FolksIndividual *individual,
     const gchar *group)
 {
-  folks_groups_change_group (FOLKS_GROUPS (individual), group, FALSE);
+  folks_groupable_change_group (FOLKS_GROUPABLE (individual), group, FALSE,
+      groups_change_group_cb, NULL);
 }
 
 void
@@ -361,7 +484,6 @@ empathy_individual_manager_remove_group (EmpathyIndividualManager *manager,
     const gchar *group)
 {
   EmpathyIndividualManagerPriv *priv;
-  GHashTable *individuals;
 
   g_return_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (manager));
   g_return_if_fail (group != NULL);
@@ -371,37 +493,74 @@ empathy_individual_manager_remove_group (EmpathyIndividualManager *manager,
   DEBUG ("removing group %s", group);
 
   /* Remove every individual from the group */
-  individuals = folks_individual_aggregator_get_individuals (priv->aggregator);
-  g_hash_table_foreach (individuals, (GHFunc) remove_group_cb,
+  g_hash_table_foreach (priv->individuals, (GHFunc) remove_group_cb,
       (gpointer) group);
 }
 
-EmpathyIndividualManagerFlags
-empathy_individual_manager_get_flags_for_connection (
-    EmpathyIndividualManager *self,
-    TpConnection *connection)
+static void
+link_personas_cb (FolksIndividualAggregator *aggregator,
+    GAsyncResult *async_result,
+    gpointer user_data)
+{
+  GError *error = NULL;
+
+  folks_individual_aggregator_link_personas_finish (aggregator, async_result,
+      &error);
+
+  if (error != NULL)
+    {
+      g_warning ("Failed to link personas: %s", error->message);
+      g_clear_error (&error);
+    }
+}
+
+void
+empathy_individual_manager_link_personas (EmpathyIndividualManager *self,
+    GList *personas)
 {
   EmpathyIndividualManagerPriv *priv;
-  EmpathyContactListFlags list_flags;
-  EmpathyIndividualManagerFlags flags;
 
-  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (self),
-      EMPATHY_INDIVIDUAL_MANAGER_NO_FLAGS);
+  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (self));
+  g_return_if_fail (personas != NULL);
 
   priv = GET_PRIV (self);
 
-  list_flags = empathy_contact_manager_get_flags_for_connection (
-    priv->contact_manager, connection);
+  DEBUG ("Linking %u personas", g_list_length (personas));
+
+  folks_individual_aggregator_link_personas (priv->aggregator, personas,
+      (GAsyncReadyCallback) link_personas_cb, NULL);
+}
+
+static void
+unlink_individual_cb (FolksIndividualAggregator *aggregator,
+    GAsyncResult *async_result,
+    gpointer user_data)
+{
+  GError *error = NULL;
+
+  folks_individual_aggregator_unlink_individual_finish (aggregator,
+      async_result, &error);
+
+  if (error != NULL)
+    {
+      g_warning ("Failed to unlink individual: %s", error->message);
+      g_clear_error (&error);
+    }
+}
+
+void
+empathy_individual_manager_unlink_individual (EmpathyIndividualManager *self,
+    FolksIndividual *individual)
+{
+  EmpathyIndividualManagerPriv *priv;
+
+  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (self));
+  g_return_if_fail (FOLKS_IS_INDIVIDUAL (individual));
+
+  priv = GET_PRIV (self);
 
-  flags = EMPATHY_INDIVIDUAL_MANAGER_NO_FLAGS;
-  if (list_flags & EMPATHY_CONTACT_LIST_CAN_ADD)
-    flags |= EMPATHY_INDIVIDUAL_MANAGER_CAN_ADD;
-  if (list_flags & EMPATHY_CONTACT_LIST_CAN_REMOVE)
-    flags |= EMPATHY_INDIVIDUAL_MANAGER_CAN_REMOVE;
-  if (list_flags & EMPATHY_CONTACT_LIST_CAN_ALIAS)
-    flags |= EMPATHY_INDIVIDUAL_MANAGER_CAN_ALIAS;
-  if (list_flags & EMPATHY_CONTACT_LIST_CAN_GROUP)
-    flags |= EMPATHY_INDIVIDUAL_MANAGER_CAN_GROUP;
+  DEBUG ("Unlinking individual '%s'", folks_individual_get_id (individual));
 
-  return flags;
+  folks_individual_aggregator_unlink_individual (priv->aggregator, individual,
+      (GAsyncReadyCallback) unlink_individual_cb, NULL);
 }