diff options
author | Philip Withnall <philip.withnall@collabora.co.uk> | 2010-08-19 17:55:02 +0100 |
---|---|---|
committer | Philip Withnall <philip.withnall@collabora.co.uk> | 2010-08-26 19:18:49 +0100 |
commit | 15947652e0e31dad9bf9061565c5b243b628572c (patch) | |
tree | dfde56b7a378e9cc1c19821b0c2bd50a9d65ac17 | |
parent | 8cebb98c04b20eaf0efd04b68d92f2a1a0dd789d (diff) |
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
-rw-r--r-- | 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; |