[folks] Bug 657282 — Add an IndividualAggregator.individuals_changed_detailed signal
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [folks] Bug 657282 — Add an IndividualAggregator.individuals_changed_detailed signal
- Date: Sun, 4 Sep 2011 16:36:09 +0000 (UTC)
commit 0dd7dc1aaa2e3b647f9e62d4a4960df60fd97220
Author: Philip Withnall <philip tecnocode co uk>
Date: Wed Aug 24 22:38:16 2011 +0100
Bug 657282 â Add an IndividualAggregator.individuals_changed_detailed signal
This supersedes IndividualAggregator.individuals_changed, providing more
information about the relationships between the added and removed individuals.
Closes: bgo#657282
NEWS | 3 +
folks/individual-aggregator.vala | 222 ++++++++++++++++++++++++++------------
2 files changed, 156 insertions(+), 69 deletions(-)
---
diff --git a/NEWS b/NEWS
index 27fa05a..9479500 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,7 @@ Bugs fixed:
* Bug 656184 â Add is-quiescent property
* Bug 657971 â Need BirthdayDetails support in eds backend
* Bug 657789 â Don't claim uneditable eds fields as writable
+* Bug 657282 â Add an IndividualAggregator.individuals_changed_detailed signal
API changes:
* Add PersonaStore:always-writeable-properties property
@@ -20,6 +21,8 @@ API changes:
* Add IndividualAggregator:is-quiescent, Backend:is-quiescent and
PersonaStore:is-quiescent
* Add PersonaDetail.GROUPS and PersonaDetail.INVALID
+* Add IndividualAggregator.individuals_changed_detailed and deprecate
+ IndividualAggregator.individuals_changed (but not remove or break it)
Overview of changes from libfolks 0.6.0 to libfolks 0.6.1
=========================================================
diff --git a/folks/individual-aggregator.vala b/folks/individual-aggregator.vala
index 01efcab..7b946fe 100644
--- a/folks/individual-aggregator.vala
+++ b/folks/individual-aggregator.vala
@@ -176,6 +176,11 @@ public class Folks.IndividualAggregator : Object
* Emitted when one or more { link Individual}s are added to or removed from
* the aggregator.
*
+ * If more information about the relationships between { link Individual}s
+ * which have been linked and unlinked is needed, consider connecting to
+ * { link IndividualAggregator.individuals_changed_detailed} instead, which is
+ * emitted at the same time as this signal.
+ *
* This will not be emitted until after { link IndividualAggregator.prepare}
* has been called.
*
@@ -187,25 +192,67 @@ public class Folks.IndividualAggregator : Object
*
* @since 0.5.1
*/
+ [Deprecated (since = "UNRELEASED",
+ replacement = "IndividualAggregator.individuals_changed_detailed")]
public signal void individuals_changed (Set<Individual> added,
Set<Individual> removed,
string? message,
Persona? actor,
GroupDetails.ChangeReason reason);
+ /**
+ * Emitted when one or more { link Individual}s are added to or removed from
+ * the aggregator.
+ *
+ * This is emitted at the same time as
+ * { link IndividualAggregator.individuals_changed}, but includes more
+ * information about the relationships between { link Individual}s which have
+ * been linked and unlinked.
+ *
+ * Individuals which have been linked will be listed in the multi-map as
+ * mappings from the old individuals to the single new individual which
+ * replaces them (i.e. each of the old individuals will map to the same new
+ * individual). This new individual is the one which will be specified as the
+ * `replacement_individual` in the { link Individual.removed} signal for the
+ * old individuals.
+ *
+ * Individuals which have been unlinked will be listed in the multi-map as
+ * a mapping from the unlinked individual to a set of one or more individuals
+ * which replace it.
+ *
+ * Individuals which have been added will be listed in the multi-map as a
+ * mapping from `null` to the set of added individuals. If `null` doesn't
+ * map to anything, no individuals have been added to the aggregator.
+ *
+ * Individuals which have been removed will be listed in the multi-map as
+ * mappings from the removed individual to `null`.
+ *
+ * This will not be emitted until after { link IndividualAggregator.prepare}
+ * has been called.
+ *
+ * @param added a mapping of old { link Individual}s to new
+ * { link Individual}s for the individuals which have changed in the
+ * aggregator
+ *
+ * @since UNRELEASED
+ */
+ public signal void individuals_changed_detailed (
+ MultiMap<Individual?, Individual?> changes);
+
/* FIXME: make this a singleton? */
/**
* Create a new IndividualAggregator.
*
* Clients should connect to the
- * { link IndividualAggregator.individuals_changed} signal, then call
- * { link IndividualAggregator.prepare} to load the backends and start
+ * { link IndividualAggregator.individuals_changed} signal (or the
+ * { link IndividualAggregator.individuals_changed_detailed} signal), then
+ * call { link IndividualAggregator.prepare} to load the backends and start
* aggregating individuals.
*
* An example of how to set up an IndividualAggregator:
* {{{
* IndividualAggregator agg = new IndividualAggregator ();
- * agg.individuals_changed.connect (individuals_changed_cb);
+ * agg.individuals_changed_detailed.connect (individuals_changed_cb);
* agg.prepare ();
* }}}
*/
@@ -374,9 +421,10 @@ public class Folks.IndividualAggregator : Object
*
* This loads all the available backends and prepares them for use by the
* IndividualAggregator. This should be called //after// connecting to the
- * { link IndividualAggregator.individuals_changed} signal, or a race
- * condition could occur, with the signal being emitted before your code has
- * connected to them, and { link Individual}s getting "lost" as a result.
+ * { link IndividualAggregator.individuals_changed} signal (or
+ * { link IndividualAggregator.individuals_changed_detailed} signal), or a
+ * race condition could occur, with the signal being emitted before your code
+ * has connected to them, and { link Individual}s getting "lost" as a result.
*
* This function is guaranteed to be idempotent (since version 0.3.0).
*
@@ -621,15 +669,18 @@ public class Folks.IndividualAggregator : Object
* read-only. */
private void _emit_individuals_changed (Set<Individual>? added,
Set<Individual>? removed,
+ MultiMap<Individual?, Individual?>? changes,
string? message = null,
Persona? actor = null,
GroupDetails.ChangeReason reason = GroupDetails.ChangeReason.NONE)
{
var _added = added;
var _removed = removed;
+ var _changes = changes;
if ((added == null || added.size == 0) &&
- (removed == null || removed.size == 0))
+ (removed == null || removed.size == 0) &&
+ (changes == null || changes.size == 0))
{
/* Don't bother emitting it if nothing's changed */
return;
@@ -642,9 +693,14 @@ public class Folks.IndividualAggregator : Object
{
_removed = new HashSet<Individual> ();
}
+ else if (changes == null)
+ {
+ _changes = new HashMultiMap<Individual?, Individual?> ();
+ }
this.individuals_changed (_added.read_only_view, _removed.read_only_view,
message, actor, reason);
+ this.individuals_changed_detailed (changes);
}
private void _connect_to_individual (Individual individual)
@@ -659,24 +715,9 @@ public class Folks.IndividualAggregator : Object
individual.removed.disconnect (this._individual_removed_cb);
}
- private void _add_personas (Set<Persona> added,
- ref HashSet<Individual> added_individuals,
- ref HashMap<Individual, Individual> replaced_individuals,
- ref Individual user)
+ private void _add_personas (Set<Persona> added, ref Individual user,
+ ref HashMultiMap<Individual?, Individual?> individuals_changes)
{
- /* Set of individuals which have been added as a result of the new
- * personas. These will be returned in added_individuals, but have to be
- * cached first so that we can ensure that we don't return any given
- * individual in both added_individuals _and_ replaced_individuals. This
- * can happen in the case that several of the added personas are linked
- * together to form one final individual. In that case, a succession of
- * newly linked individuals will be produced (one for each iteration of
- * the loop over the added personas); only the *last one* of which should
- * make its way into added_individuals. The rest should not even make
- * their way into replaced_individuals, as they've existed only within the
- * confines of this function call. */
- HashSet<Individual> almost_added_individuals = new HashSet<Individual> ();
-
foreach (var persona in added)
{
PersonaStoreTrust trust_level = persona.store.trust_level;
@@ -837,33 +878,55 @@ public class Folks.IndividualAggregator : Object
}
}
- /* Remove the old Individuals. This has to be done here, as we need
- * the final_individual. */
foreach (var i in candidate_inds)
{
- /* If the replaced individual was marked to be added to the
- * aggregator, unmark it. */
- if (almost_added_individuals.contains (i) == true)
- almost_added_individuals.remove (i);
- else
- replaced_individuals.set (i, final_individual);
+ /* Transitively update the individuals_changes. We have to do this
+ * in two stages as we can't modify individuals_changes while
+ * iterating over it. */
+ var transitive_updates = new HashSet<Individual> ();
+
+ foreach (var k in individuals_changes.get_keys ())
+ {
+ if (i in individuals_changes.get (k))
+ {
+ transitive_updates.add (k);
+ }
+ }
+
+ foreach (var k in transitive_updates)
+ {
+ assert (individuals_changes.remove (k, i) == true);
+
+ /* If we're saying the final_individual is replacing some of
+ * these candidate individuals, we don't also want to say that
+ * it's been added (by also emitting a mapping from
+ * null â final_individual). */
+ if (k != null)
+ {
+ individuals_changes.set (k, final_individual);
+ }
+ }
+
+ /* If there were no transitive changes to make, it's because this
+ * candidate individual existed before this call to
+ * _add_personas(), so it's safe to say it's being replaced by
+ * the final_individual. */
+ if (transitive_updates.size == 0)
+ {
+ individuals_changes.set (i, final_individual);
+ }
+ }
+
+ /* If there were no candidate individuals, mark the final_individual
+ * as added. */
+ if (candidate_inds.size == 0)
+ {
+ individuals_changes.set (null, final_individual);
}
/* If the final Individual is the user, set them as such. */
if (final_individual.is_user == true)
user = final_individual;
-
- /* Mark the final individual for addition later */
- almost_added_individuals.add (final_individual);
- }
-
- /* Add the set of final individuals which weren't later replaced to the
- * aggregator. */
- foreach (var i in almost_added_individuals)
- {
- /* Add the new Individual to the aggregator */
- added_individuals.add (i);
- this._connect_to_individual (i);
}
}
@@ -907,11 +970,11 @@ public class Folks.IndividualAggregator : Object
Persona? actor,
GroupDetails.ChangeReason reason)
{
- var added_individuals = new HashSet<Individual> ();
var removed_individuals = new HashSet<Individual> ();
- var replaced_individuals = new HashMap<Individual, Individual> ();
+ var individuals_changes = new HashMultiMap<Individual?, Individual?> ();
var relinked_personas = new HashSet<Persona> ();
var removed_personas = new HashSet<Persona> (direct_hash, direct_equal);
+ var replaced_individuals = new HashMap<Individual, Individual> ();
/* We store the value of this.user locally and only update it at the end
* of the function to prevent spamming notifications of changes to the
@@ -934,7 +997,10 @@ public class Folks.IndividualAggregator : Object
* removed will be re-linked into other Individuals). */
var ind = this._link_map.lookup (persona.iid);
if (ind != null)
- removed_individuals.add (ind);
+ {
+ removed_individuals.add (ind);
+ individuals_changes.set (ind, null);
+ }
/* Remove the Persona's links from the link map */
this._remove_persona_from_link_map (persona);
@@ -986,8 +1052,7 @@ public class Folks.IndividualAggregator : Object
if (added.size > 0)
{
- this._add_personas (added, ref added_individuals,
- ref replaced_individuals, ref user);
+ this._add_personas (added, ref user, ref individuals_changes);
}
debug ("Relinking Personas:");
@@ -997,30 +1062,46 @@ public class Folks.IndividualAggregator : Object
persona.is_user ? "yes" : "no", persona.iid);
}
- this._add_personas (relinked_personas, ref added_individuals,
- ref replaced_individuals, ref user);
-
- /* Signal the removal of the replaced_individuals at the same time as the
- * removed_individuals. (The only difference between replaced individuals
- * and removed ones is that replaced individuals specify a replacement
- * when they emit their Individual:removed signal. */
- if (replaced_individuals != null)
- {
- MapIterator<Individual, Individual> iter =
- replaced_individuals.map_iterator ();
- while (iter.next () == true)
- removed_individuals.add (iter.get_key ());
- }
+ this._add_personas (relinked_personas, ref user, ref individuals_changes);
/* Notify of changes to this.user */
this.user = user;
/* Signal the addition of new individuals and removal of old ones to the
* aggregator */
- if (added_individuals.size > 0 || removed_individuals.size > 0)
+ if (individuals_changes.size > 0)
{
+ var added_individuals = new HashSet<Individual> ();
+
+ /* Extract the deprecated added and removed sets from
+ * individuals_changes, to be used in the individuals_changed
+ * signal. */
+ foreach (var old_ind in individuals_changes.get_keys ())
+ {
+ foreach (var new_ind in individuals_changes.get (old_ind))
+ {
+ assert (old_ind != null || new_ind != null);
+
+ if (old_ind != null)
+ {
+ removed_individuals.add (old_ind);
+ }
+
+ if (new_ind != null)
+ {
+ added_individuals.add (new_ind);
+ this._connect_to_individual (new_ind);
+ }
+
+ if (old_ind != null && new_ind != null)
+ {
+ replaced_individuals.set (old_ind, new_ind);
+ }
+ }
+ }
+
this._emit_individuals_changed (added_individuals,
- removed_individuals);
+ removed_individuals, individuals_changes);
}
/* Signal the replacement of various Individuals as a consequence of
@@ -1093,9 +1174,6 @@ public class Folks.IndividualAggregator : Object
if (this._individuals.get (i.id) != i)
return;
- var individuals = new HashSet<Individual> ();
- individuals.add (i);
-
if (replacement != null)
{
debug ("Individual '%s' removed (replaced by '%s')", i.id,
@@ -1109,7 +1187,13 @@ public class Folks.IndividualAggregator : Object
/* If the individual has 0 personas, we've already signaled removal */
if (i.personas.size > 0)
{
- this._emit_individuals_changed (null, individuals);
+ var changes = new HashMultiMap<Individual?, Individual?> ();
+ var individuals = new HashSet<Individual> ();
+
+ individuals.add (i);
+ changes.set (i, replacement);
+
+ this._emit_individuals_changed (null, individuals, changes);
}
this._disconnect_from_individual (i);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]