summaryrefslogtreecommitdiff
path: root/src/core/Tracker.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/Tracker.vala')
-rw-r--r--src/core/Tracker.vala216
1 files changed, 216 insertions, 0 deletions
diff --git a/src/core/Tracker.vala b/src/core/Tracker.vala
new file mode 100644
index 0000000..e72992b
--- /dev/null
+++ b/src/core/Tracker.vala
@@ -0,0 +1,216 @@
+/* Copyright 2011-2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+namespace Core {
+
+// A TrackerAccumulator is called by Tracker indicating when a DataObject should be included or
+// unincluded in its accumulated data. All methods return true if their data has changed,
+// indicating that the Tracker's "updated" signal should be fired.
+public interface TrackerAccumulator : Object {
+ public abstract bool include(DataObject object);
+
+ public abstract bool uninclude(DataObject object);
+
+ public abstract bool altered(DataObject object, Alteration alteration);
+}
+
+// A Tracker monitors a DataCollection and reports to an installed TrackerAccumulator when objects
+// are available and unavailable. This simplifies connecting to the DataCollection manually to
+// monitoring availability (or subclassing for similar reasons, which may not always be available).
+public class Tracker {
+ protected delegate bool IncludeUnincludeObject(DataObject object);
+
+ private DataCollection collection;
+ private Gee.Collection<DataObject>? initial;
+ private TrackerAccumulator? acc = null;
+
+ public virtual signal void updated() {
+ }
+
+ public Tracker(DataCollection collection, Gee.Collection<DataObject>? initial = null) {
+ this.collection = collection;
+ this.initial = initial;
+ }
+
+ ~Tracker() {
+ if (acc != null) {
+ collection.items_added.disconnect(on_items_added);
+ collection.items_removed.disconnect(on_items_removed);
+ collection.items_altered.disconnect(on_items_altered);
+ }
+ }
+
+ public void start(TrackerAccumulator acc) {
+ // can only be started once
+ assert(this.acc == null);
+
+ this.acc = acc;
+
+ collection.items_added.connect(on_items_added);
+ collection.items_removed.connect(on_items_removed);
+ collection.items_altered.connect(on_items_altered);
+
+ if (initial != null && initial.size > 0)
+ on_items_added(initial);
+ else if (initial == null)
+ on_items_added(collection.get_all());
+
+ initial = null;
+ }
+
+ public DataCollection get_collection() {
+ return collection;
+ }
+
+ private void on_items_added(Gee.Iterable<DataObject> added) {
+ include_uninclude(added, acc.include);
+ }
+
+ private void on_items_removed(Gee.Iterable<DataObject> removed) {
+ include_uninclude(removed, acc.uninclude);
+ }
+
+ // Subclasses can use this as a utility method.
+ protected void include_uninclude(Gee.Iterable<DataObject> objects, IncludeUnincludeObject cb) {
+ bool fire_updated = false;
+ foreach (DataObject object in objects)
+ fire_updated = cb(object) || fire_updated;
+
+ if (fire_updated)
+ updated();
+ }
+
+ private void on_items_altered(Gee.Map<DataObject, Alteration> map) {
+ bool fire_updated = false;
+ foreach (DataObject object in map.keys)
+ fire_updated = acc.altered(object, map.get(object)) || fire_updated;
+
+ if (fire_updated)
+ updated();
+ }
+}
+
+// A ViewTracker is Tracker designed for ViewCollections. It uses an internal mux to route
+// Tracker's calls to three TrackerAccumulators: all (all objects in the ViewCollection), selected
+// (only for selected objects) and visible (only for items not hidden or filtered out).
+public class ViewTracker : Tracker {
+ private class Mux : Object, TrackerAccumulator {
+ public TrackerAccumulator? all;
+ public TrackerAccumulator? visible;
+ public TrackerAccumulator? selected;
+
+ public Mux(TrackerAccumulator? all, TrackerAccumulator? visible, TrackerAccumulator? selected) {
+ this.all = all;
+ this.visible = visible;
+ this.selected = selected;
+ }
+
+ public bool include(DataObject object) {
+ DataView view = (DataView) object;
+
+ bool fire_updated = false;
+
+ if (all != null)
+ fire_updated = all.include(view) || fire_updated;
+
+ if (visible != null && view.is_visible())
+ fire_updated = visible.include(view) || fire_updated;
+
+ if (selected != null && view.is_selected())
+ fire_updated = selected.include(view) || fire_updated;
+
+ return fire_updated;
+ }
+
+ public bool uninclude(DataObject object) {
+ DataView view = (DataView) object;
+
+ bool fire_updated = false;
+
+ if (all != null)
+ fire_updated = all.uninclude(view) || fire_updated;
+
+ if (visible != null && view.is_visible())
+ fire_updated = visible.uninclude(view) || fire_updated;
+
+ if (selected != null && view.is_selected())
+ fire_updated = selected.uninclude(view) || fire_updated;
+
+ return fire_updated;
+ }
+
+ public bool altered(DataObject object, Alteration alteration) {
+ DataView view = (DataView) object;
+
+ bool fire_updated = false;
+
+ if (all != null)
+ fire_updated = all.altered(view, alteration) || fire_updated;
+
+ if (visible != null && view.is_visible())
+ fire_updated = visible.altered(view, alteration) || fire_updated;
+
+ if (selected != null && view.is_selected())
+ fire_updated = selected.altered(view, alteration) || fire_updated;
+
+ return fire_updated;
+ }
+ }
+
+ private Mux? mux = null;
+
+ public ViewTracker(ViewCollection collection) {
+ base (collection, collection.get_all_unfiltered());
+ }
+
+ ~ViewTracker() {
+ if (mux != null) {
+ ViewCollection? collection = get_collection() as ViewCollection;
+ assert(collection != null);
+ collection.items_shown.disconnect(on_items_shown);
+ collection.items_hidden.disconnect(on_items_hidden);
+ collection.items_selected.disconnect(on_items_selected);
+ collection.items_unselected.disconnect(on_items_unselected);
+ }
+ }
+
+ public new void start(TrackerAccumulator? all, TrackerAccumulator? visible, TrackerAccumulator? selected) {
+ assert(mux == null);
+
+ mux = new Mux(all, visible, selected);
+
+ ViewCollection? collection = get_collection() as ViewCollection;
+ assert(collection != null);
+ collection.items_shown.connect(on_items_shown);
+ collection.items_hidden.connect(on_items_hidden);
+ collection.items_selected.connect(on_items_selected);
+ collection.items_unselected.connect(on_items_unselected);
+
+ base.start(mux);
+ }
+
+ private void on_items_shown(Gee.Collection<DataView> shown) {
+ if (mux.visible != null)
+ include_uninclude(shown, mux.visible.include);
+ }
+
+ private void on_items_hidden(Gee.Collection<DataView> hidden) {
+ if (mux.visible != null)
+ include_uninclude(hidden, mux.visible.uninclude);
+ }
+
+ private void on_items_selected(Gee.Iterable<DataView> selected) {
+ if (mux.selected != null)
+ include_uninclude(selected, mux.selected.include);
+ }
+
+ private void on_items_unselected(Gee.Iterable<DataView> unselected) {
+ if (mux.selected != null)
+ include_uninclude(unselected, mux.selected.uninclude);
+ }
+}
+
+}