[folks] Bug 626725 — Add an Individual.personas_changed signal



commit 15947652e0e31dad9bf9061565c5b243b628572c
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Thu Aug 19 17:55:02 2010 +0100

    Bug 626725 â?? Add an Individual.personas_changed signal
    
    Add a personas_changed signal, allowing client code to do various things
    with the lists of added and removed Personas, instead of having to determine
    which Personas were added or removed themselves. Closes: bgo#626725

 folks/individual.vala |  185 +++++++++++++++++++++++++++++++++----------------
 1 files changed, 125 insertions(+), 60 deletions(-)
---
diff --git a/folks/individual.vala b/folks/individual.vala
index d35ad10..d6aef64 100644
--- a/folks/individual.vala
+++ b/folks/individual.vala
@@ -33,12 +33,18 @@ public class Folks.Individual : Object,
     Groups,
     Presence
 {
+  private bool _is_favourite;
+  private string _alias;
   private HashTable<string, bool> _groups;
+  /* These two data structures should store exactly the same set of Personas:
+   * the Personas contained in this Individual. The HashSet is used for fast
+   * lookups, whereas the List is used for iteration. */
   private GLib.List<Persona> _persona_list;
   private HashSet<Persona> _persona_set;
-  private HashSet<PersonaStore> stores;
-  private bool _is_favourite;
-  private string _alias;
+  /* Mapping from PersonaStore -> number of Personas from that store contained
+   * in this Individual. There shouldn't be any entries with a number < 1.
+   * This is used for working out when to disconnect from store signals. */
+  private HashMap<PersonaStore, uint> stores;
 
   /**
    * { inheritDoc}
@@ -157,6 +163,18 @@ public class Folks.Individual : Object,
       set { this._set_personas (value, null); }
     }
 
+  /**
+   * Emitted when one or more { link Persona}s are added to or removed from
+   * the Individual.
+   *
+   * @param added a list of { link Persona}s which have been added
+   * @param removed a list of { link Persona}s which have been removed
+   *
+   * @since 0.1.16
+   */
+  public signal void personas_changed (GLib.List<Persona>? added,
+      GLib.List<Persona>? removed);
+
   private void notify_groups_cb (Object obj, ParamSpec ps)
     {
       this.update_groups ();
@@ -224,17 +242,20 @@ public class Folks.Individual : Object,
   public Individual (GLib.List<Persona>? personas)
     {
       this._persona_set = new HashSet<Persona> (null, null);
-      this.stores = new HashSet<PersonaStore> (null, null);
+      this.stores = new HashMap<PersonaStore, uint> (null, null);
       this.personas = personas;
     }
 
   private void store_removed_cb (PersonaStore store)
     {
+      GLib.List<Persona> removed_personas = null;
       Iterator<Persona> iter = this._persona_set.iterator ();
       while (iter.next ())
         {
           Persona persona = iter.get ();
 
+          removed_personas.prepend (persona);
+
           this._persona_list.remove (persona);
           /* FIXME: bgo#624249 means GLib.List leaks item references.
            * We probably eventually want to transition away from GLib.List
@@ -245,6 +266,9 @@ public class Folks.Individual : Object,
           iter.remove ();
         }
 
+      if (removed_personas != null)
+        this.personas_changed (null, removed_personas);
+
       if (store != null)
         this.stores.remove (store);
 
@@ -264,18 +288,24 @@ public class Folks.Individual : Object,
       Persona? actor,
       Groups.ChangeReason reason)
     {
+      GLib.List<Persona> removed_personas = null;
       removed.foreach ((data) =>
         {
           unowned Persona p = (Persona) data;
 
           if (this._persona_set.remove (p))
             {
+              removed_personas.prepend (p);
+
               this._persona_list.remove (p);
               /* FIXME: bgo#624249 means GLib.List leaks item references */
               g_object_unref (p);
             }
         });
 
+      if (removed_personas != null)
+        this.personas_changed (null, removed_personas);
+
       if (this._persona_set.size < 1)
         {
           this.removed (null);
@@ -545,85 +575,120 @@ public class Folks.Individual : Object,
       return p.is_online ();
     }
 
-  private void _set_personas (GLib.List<Persona>? personas,
-      Individual? replacement_individual)
+  private void connect_to_persona (Persona persona)
     {
-      /* Disconnect from all our previous personas */
-      this._persona_list.foreach ((p) =>
+      persona.notify["alias"].connect (this.notify_alias_cb);
+      persona.notify["avatar"].connect (this.notify_avatar_cb);
+      persona.notify["presence-message"].connect (this.notify_presence_cb);
+      persona.notify["presence-type"].connect (this.notify_presence_cb);
+      persona.notify["is-favourite"].connect (this.notify_is_favourite_cb);
+      persona.notify["groups"].connect (this.notify_groups_cb);
+
+      if (persona is Groups)
         {
-          unowned Persona persona = (Persona) p;
+          ((Groups) persona).group_changed.connect (
+              this.persona_group_changed_cb);
+        }
+    }
 
-          persona.notify["alias"].disconnect (this.notify_alias_cb);
-          persona.notify["avatar"].disconnect (this.notify_avatar_cb);
-          persona.notify["presence-message"].disconnect (
-              this.notify_presence_cb);
-          persona.notify["presence-type"].disconnect (this.notify_presence_cb);
-          persona.notify["is-favourite"].disconnect (
-              this.notify_is_favourite_cb);
-          persona.notify["groups"].disconnect (this.notify_groups_cb);
+  private void disconnect_from_persona (Persona persona)
+    {
+      persona.notify["alias"].disconnect (this.notify_alias_cb);
+      persona.notify["avatar"].disconnect (this.notify_avatar_cb);
+      persona.notify["presence-message"].disconnect (
+          this.notify_presence_cb);
+      persona.notify["presence-type"].disconnect (this.notify_presence_cb);
+      persona.notify["is-favourite"].disconnect (
+          this.notify_is_favourite_cb);
+      persona.notify["groups"].disconnect (this.notify_groups_cb);
+
+      if (persona is Groups)
+        {
+          ((Groups) persona).group_changed.disconnect (
+              this.persona_group_changed_cb);
+        }
+    }
 
-          if (p is Groups)
-            {
-              ((Groups) p).group_changed.disconnect (
-                  this.persona_group_changed_cb);
-            }
+  private void _set_personas (GLib.List<Persona>? persona_list,
+      Individual? replacement_individual)
+    {
+      HashSet<Persona> persona_set = new HashSet<Persona> (null, null);
+      GLib.List<Persona> added = null;
+      GLib.List<Persona> removed = null;
 
-          /* Disconnect from this persona's store */
-          if (this.stores.contains (persona.store))
+      /* Determine which Personas have been added */
+      foreach (Persona p in persona_list)
+        {
+          if (!this._persona_set.contains (p))
             {
-              persona.store.removed.disconnect (this.store_removed_cb);
-              persona.store.personas_changed.disconnect (
-                  this.store_personas_changed_cb);
-              this.stores.remove (persona.store);
-            }
+              added.prepend (p);
 
-          this._persona_set.remove (persona);
-        });
+              this._persona_set.add (p);
+              this.connect_to_persona (p);
 
-      /* Connect to all the new Personas */
-      this._persona_list = new GLib.List<Persona> ();
-      personas.foreach ((p) =>
-        {
-          unowned Persona persona = (Persona) p;
+              /* Increment the Persona count for this PersonaStore */
+              unowned PersonaStore store = p.store;
+              uint num_from_store = this.stores.get (store);
+              if (num_from_store == 0)
+                {
+                  this.stores.set (store, num_from_store + 1);
+                }
+              else
+                {
+                  this.stores.set (store, 1);
 
-          this._persona_list.prepend (persona);
+                  store.removed.connect (this.store_removed_cb);
+                  store.personas_changed.connect (
+                      this.store_personas_changed_cb);
+                }
+            }
 
-          persona.notify["alias"].connect (this.notify_alias_cb);
-          persona.notify["avatar"].connect (this.notify_avatar_cb);
-          persona.notify["presence-message"].connect (this.notify_presence_cb);
-          persona.notify["presence-type"].connect (this.notify_presence_cb);
-          persona.notify["is-favourite"].connect (this.notify_is_favourite_cb);
-          persona.notify["groups"].connect (this.notify_groups_cb);
+          persona_set.add (p);
+        }
 
-          if (p is Groups)
+      /* Determine which Personas have been removed */
+      foreach (Persona p in this._persona_list)
+        {
+          if (!persona_set.contains (p))
             {
-              ((Groups) p).group_changed.connect (
-                  this.persona_group_changed_cb);
-            }
+              removed.prepend (p);
 
-          /* Connect to this persona's store */
-          if (!this.stores.contains (persona.store))
-            {
-              persona.store.removed.connect (this.store_removed_cb);
-              persona.store.personas_changed.connect (
-                  this.store_personas_changed_cb);
-              this.stores.add (persona.store);
+              /* Decrement the Persona count for this PersonaStore */
+              unowned PersonaStore store = p.store;
+              uint num_from_store = this.stores.get (store);
+              if (num_from_store > 1)
+                {
+                  this.stores.set (store, num_from_store - 1);
+                }
+              else
+                {
+                  store.removed.disconnect (this.store_removed_cb);
+                  store.personas_changed.disconnect (
+                      this.store_personas_changed_cb);
+
+                  this.stores.unset (store);
+                }
+
+              this.disconnect_from_persona (p);
+              this._persona_set.remove (p);
             }
+        }
 
-          this._persona_set.add (persona);
-        });
+      /* Update the Persona list. We just copy the list given to us to save
+       * repeated insertions/removals and also to ensure we retain the ordering
+       * of the Personas we were given. */
+      this._persona_list = persona_list.copy ();
 
-      this._persona_list.reverse ();
+      this.personas_changed (added, removed);
 
-      /* If all the personas have been removed, remove the individual */
+      /* If all the Personas have been removed, remove the Individual */
       if (this._persona_set.size < 1)
         {
           this.removed (replacement_individual);
             return;
         }
 
-      /* TODO: base this upon our ID in permanent storage, once we have that
-       */
+      /* TODO: Base this upon our ID in permanent storage, once we have that. */
       if (this.id == null && this._persona_list.data != null)
         this.id = this._persona_list.data.uid;
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]