[california/wip/727120-webcal] Use CalendarSubscriptionManager to deal with mass subscriptions
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/727120-webcal] Use CalendarSubscriptionManager to deal with mass subscriptions
- Date: Fri, 28 Mar 2014 00:57:58 +0000 (UTC)
commit e15f1da83130113cfacd2f08f1c6bd77e380f594
Author: Jim Nelson <jim yorba org>
Date: Thu Mar 27 17:56:35 2014 -0700
Use CalendarSubscriptionManager to deal with mass subscriptions
This fixes a couple of bugs in handling calendars from coming and
going.
src/Makefile.am | 1 +
.../backing-calendar-source-subscription.vala | 1 +
.../backing-calendar-subscription-manager.vala | 176 ++++++++++++++++++++
src/backing/backing-store.vala | 4 +-
src/backing/eds/backing-eds-store.vala | 4 +-
src/component/component-instance.vala | 2 +-
src/view/month/month-controllable.vala | 46 ++---
7 files changed, 201 insertions(+), 33 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 93d896c..9f3fbca 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,6 +16,7 @@ california_VALASOURCES = \
backing/backing-activator.vala \
backing/backing-calendar-source.vala \
backing/backing-calendar-source-subscription.vala \
+ backing/backing-calendar-subscription-manager.vala \
backing/backing-error.vala \
backing/backing-manager.vala \
backing/backing-source.vala \
diff --git a/src/backing/backing-calendar-source-subscription.vala
b/src/backing/backing-calendar-source-subscription.vala
index 90245d5..b54fea9 100644
--- a/src/backing/backing-calendar-source-subscription.vala
+++ b/src/backing/backing-calendar-source-subscription.vala
@@ -255,6 +255,7 @@ public abstract class CalendarSourceSubscription : BaseObject {
// Use to_array() so no iteration troubles when notify_instance_dropped removes it from
// the multimap
+ debug("Dropping %d instances to %s: unavailable", instances.size, calendar.to_string());
foreach (Component.Instance instance in instances.get_values().to_array())
notify_instance_dropped(instance);
}
diff --git a/src/backing/backing-calendar-subscription-manager.vala
b/src/backing/backing-calendar-subscription-manager.vala
new file mode 100644
index 0000000..c4851ef
--- /dev/null
+++ b/src/backing/backing-calendar-subscription-manager.vala
@@ -0,0 +1,176 @@
+/* Copyright 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 California.Backing {
+
+/**
+ * Subscribe to all { link CalendarSource}s and their { link Component.Instance}s for a specific
+ * span of time.
+ *
+ * This class manages the signals and { link CalendarSourceSubscription}s for all registered
+ * calendars and converts their important events into a set of symmetric signals. It also
+ * automatically subscribes to new calendars (and unsubscribes to dropped ones) notifying of changes
+ * through the same set of signals Callers should simply subscribe to those signals and let them
+ * drive the application.
+ *
+ * The time span { link window} cannot be altered once the object is created.
+ */
+
+public class CalendarSubscriptionManager : BaseObject {
+ /**
+ * The time span for all managed subscriptions.
+ */
+ public Calendar.ExactTimeSpan window { get; private set; }
+
+ /**
+ * Indicates a { link CalendarSource} was added to the manager, either listed when first
+ * created or detected at runtime afterwards.
+ */
+ public signal void calendar_added(Backing.CalendarSource calendar);
+
+ /**
+ * Indicates the { link CalendarSource} was removed from the manager.
+ */
+ public signal void calendar_removed(Backing.CalendarSource calendar);
+
+ /**
+ * Indicates the { link Component.Instance} was generated by one of the managed subscriptions,
+ * either generated (discovered) when first opened or added later.
+ */
+ public signal void instance_added(Component.Instance instance);
+
+ /**
+ * Indicates the { link Component.Instance} was removed by one of the managed subscriptions,
+ * either due to the { link CalendarSource} being made unavailable or removal by the user.
+ */
+ public signal void instance_removed(Component.Instance instance);
+
+ /**
+ * An error was returned when attempting to subscribe to the { link CalendarSource}.
+ */
+ public signal void subscription_error(Backing.CalendarSource calendar, Error err);
+
+ private Gee.ArrayList<Backing.CalendarSourceSubscription> subscriptions = new Gee.ArrayList<
+ Backing.CalendarSourceSubscription>();
+ private Cancellable cancellable = new Cancellable();
+
+ /**
+ * Create a new { link CalendarSubscriptionManager}.
+ *
+ * The { link window} cannot be modified once created.
+ *
+ * Events will not be signalled until { link start} is called.
+ */
+ public CalendarSubscriptionManager(Calendar.ExactTimeSpan window) {
+ this.window = window;
+ }
+
+ ~CalendarSubscriptionManager() {
+ // cancel any outstanding subscription starts
+ cancellable.cancel();
+
+ // drop signals on objects that will persist after this object's destruction
+ foreach (Backing.Store store in Backing.Manager.instance.get_stores()) {
+ store.source_added.disconnect(on_source_added);
+ store.source_removed.disconnect(on_source_removed);
+ }
+ }
+
+ /**
+ * Generate subscriptions and begin firing signals.
+ *
+ * There is no "stop" method. Destroying the object will cancel all subscriptions, although
+ * signals will not be fired at that time.
+ */
+ public void start() {
+ foreach (Backing.Store store in Backing.Manager.instance.get_stores()) {
+ // watch each store for future added sources
+ store.source_added.connect(on_source_added);
+ store.source_removed.connect(on_source_removed);
+
+ foreach (Backing.Source source in store.get_sources_of_type<Backing.CalendarSource>())
+ add_calendar((Backing.CalendarSource) source);
+ }
+ }
+
+ private void on_source_added(Backing.Source source) {
+ Backing.CalendarSource? calendar = source as Backing.CalendarSource;
+ if (calendar != null)
+ add_calendar(calendar);
+ }
+
+ private void add_calendar(Backing.CalendarSource calendar) {
+ // report calendar as added to subscription
+ calendar_added(calendar);
+
+ // start generating instances on this calendar
+ calendar.subscribe_async.begin(window, cancellable, on_subscribed);
+ }
+
+ // Since this might be called after the dtor has finished (cancelling the operation), don't
+ // touch the "this" ref unless the Error is shown not to be a cancellation
+ private void on_subscribed(Object? source, AsyncResult result) {
+ Backing.CalendarSource calendar = (Backing.CalendarSource) source;
+
+ try {
+ Backing.CalendarSourceSubscription subscription = calendar.subscribe_async.end(result);
+
+ // okay to use "this" ref
+ subscriptions.add(subscription);
+
+ subscription.instance_discovered.connect(on_instance_added);
+ subscription.instance_added.connect(on_instance_added);
+ subscription.instance_removed.connect(on_instance_removed);
+ subscription.instance_dropped.connect(on_instance_removed);
+ subscription.start_failed.connect(on_error);
+
+ // this will start signals firing for event changes
+ subscription.start();
+ } catch (Error err) {
+ debug("Unable to subscribe to %s: %s", calendar.to_string(), err.message);
+
+ // only fire -- or even touch "this" -- if not a cancellation
+ if (!(err is IOError.CANCELLED))
+ subscription_error(calendar, err);
+ }
+ }
+
+ private void on_instance_added(Component.Instance instance) {
+ instance_added(instance);
+ }
+
+ private void on_instance_removed(Component.Instance instance) {
+ instance_removed(instance);
+ }
+
+ private void on_error(CalendarSourceSubscription subscription, Error err) {
+ subscription_error(subscription.calendar, err);
+ }
+
+ // Don't need to do much here as all instances are dropped prior to the source being removed
+ private void on_source_removed(Backing.Source source) {
+ Backing.CalendarSource? calendar = source as Backing.CalendarSource;
+ if (calendar == null)
+ return;
+
+ // drop all related subscriptions ... their instances should've been dropped via the
+ // "instance-dropped" signal, so no signal their removal here
+ Gee.Iterator<CalendarSourceSubscription> iter = subscriptions.iterator();
+ while (iter.next()) {
+ if (iter.get().calendar == calendar)
+ iter.remove();
+ }
+
+ calendar_removed(calendar);
+ }
+
+ public override string to_string() {
+ return "%s window=%s".printf(get_class().get_type().name(), window.to_string());
+ }
+}
+
+}
+
diff --git a/src/backing/backing-store.vala b/src/backing/backing-store.vala
index 46bfb29..527b0a2 100644
--- a/src/backing/backing-store.vala
+++ b/src/backing/backing-store.vala
@@ -29,7 +29,7 @@ public abstract class Store : BaseObject {
*
* Also fired in { link open_async} when Sources are discovered.
*/
- public virtual signal void added(Source source) {
+ public virtual signal void source_added(Source source) {
debug("%s: added %s", to_string(), source.to_string());
}
@@ -40,7 +40,7 @@ public abstract class Store : BaseObject {
*
* Also called in { link close_async} when internal refs are being dropped.
*/
- public virtual signal void removed(Source source) {
+ public virtual signal void source_removed(Source source) {
debug("%s: removed %s", to_string(), source.to_string());
}
diff --git a/src/backing/eds/backing-eds-store.vala b/src/backing/eds/backing-eds-store.vala
index 776b3a7..11f72bf 100644
--- a/src/backing/eds/backing-eds-store.vala
+++ b/src/backing/eds/backing-eds-store.vala
@@ -130,7 +130,7 @@ internal class EdsStore : Store, WebCalSubscribable {
sources.set(eds_source, calendar);
- added(calendar);
+ source_added(calendar);
}
// since the registry ref may have been dropped (in close_async), it shouldn't be ref'd here
@@ -140,7 +140,7 @@ internal class EdsStore : Store, WebCalSubscribable {
if (sources.unset(eds_source, out source)) {
assert(source != null);
- removed(source);
+ source_removed(source);
source.set_unavailable();
}
diff --git a/src/component/component-instance.vala b/src/component/component-instance.vala
index f9769d7..8124f90 100644
--- a/src/component/component-instance.vala
+++ b/src/component/component-instance.vala
@@ -39,7 +39,7 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
*
* This will initialize as null if created as a { link blank} Instance.
*/
- public Backing.CalendarSource? calendar_source { get; set; default = null; }
+ public unowned Backing.CalendarSource? calendar_source { get; set; default = null; }
/**
* The date-time stamp of the { link Instance}.
diff --git a/src/view/month/month-controllable.vala b/src/view/month/month-controllable.vala
index 6528522..3707df8 100644
--- a/src/view/month/month-controllable.vala
+++ b/src/view/month/month-controllable.vala
@@ -65,8 +65,7 @@ public class Controllable : Gtk.Grid, View.Controllable {
public Calendar.Date default_date { get; protected set; }
private Gee.HashMap<Calendar.Date, Cell> date_to_cell = new Gee.HashMap<Calendar.Date, Cell>();
- private Gee.ArrayList<Backing.CalendarSourceSubscription> subscriptions = new Gee.ArrayList<
- Backing.CalendarSourceSubscription>();
+ private Backing.CalendarSubscriptionManager? subscriptions = null;
private Gdk.EventType button_press_type = Gdk.EventType.NOTHING;
private Gdk.Point button_press_point = Gdk.Point();
@@ -275,34 +274,25 @@ public class Controllable : Gtk.Grid, View.Controllable {
Calendar.ExactTimeSpan time_window = new Calendar.ExactTimeSpan.from_date_span(window,
Calendar.Timezone.local);
- // clear current subscriptions and generate new subscriptions for new window
- subscriptions.clear();
- foreach (Backing.Store store in Backing.Manager.instance.get_stores()) {
- foreach (Backing.Source source in store.get_sources_of_type<Backing.CalendarSource>()) {
- Backing.CalendarSource calendar = (Backing.CalendarSource) source;
- calendar.notify[Backing.Source.PROP_VISIBLE].connect(queue_draw);
- calendar.subscribe_async.begin(time_window, null, on_subscribed);
- }
- }
+ // create new subscription manager, subscribe to its signals, and let them drive
+ subscriptions = null;
+ subscriptions = new Backing.CalendarSubscriptionManager(time_window);
+ subscriptions.calendar_added.connect(on_calendar_added);
+ subscriptions.calendar_removed.connect(on_calendar_removed);
+ subscriptions.instance_added.connect(on_instance_added);
+ subscriptions.instance_removed.connect(on_instance_removed);
+
+ subscriptions.start();
}
- private void on_subscribed(Object? source, AsyncResult result) {
- Backing.CalendarSource calendar = (Backing.CalendarSource) source;
-
- try {
- Backing.CalendarSourceSubscription subscription = calendar.subscribe_async.end(result);
- subscriptions.add(subscription);
-
- subscription.instance_discovered.connect(on_instance_added);
- subscription.instance_added.connect(on_instance_added);
- subscription.instance_removed.connect(on_instance_removed);
- subscription.instance_dropped.connect(on_instance_removed);
-
- // this will start signals firing for event changes
- subscription.start();
- } catch (Error err) {
- debug("Unable to subscribe to %s: %s", calendar.to_string(), err.message);
- }
+ private void on_calendar_added(Backing.CalendarSource calendar) {
+ calendar.notify[Backing.Source.PROP_VISIBLE].connect(queue_draw);
+ calendar.notify[Backing.Source.PROP_COLOR].connect(queue_draw);
+ }
+
+ private void on_calendar_removed(Backing.CalendarSource calendar) {
+ calendar.notify[Backing.Source.PROP_VISIBLE].disconnect(queue_draw);
+ calendar.notify[Backing.Source.PROP_COLOR].disconnect(queue_draw);
}
private void on_instance_added(Component.Instance instance) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]