[california] Deal with additions/removals of calendars dynamically



commit 84bb02273f7cce3cc48f505de8a4dbe09d685c66
Author: Jim Nelson <jim yorba org>
Date:   Fri Apr 4 16:14:59 2014 -0700

    Deal with additions/removals of calendars dynamically
    
    This also improves the Calendar Manager to use the ListBoxModel,
    which is far cleaner than mucking with GtkWidgets to get to the
    backing data objects.

 src/backing/backing-calendar-source.vala         |    4 +-
 src/backing/backing-manager.vala                 |   37 ++++++++++++++++++---
 src/backing/backing-source.vala                  |   30 ++++++++++++++++-
 src/backing/eds/backing-eds-calendar-source.vala |    2 +-
 src/host/host-create-update-event.vala           |    2 +
 src/manager/manager-calendar-list.vala           |   38 ++++++++++++++++------
 6 files changed, 92 insertions(+), 21 deletions(-)
---
diff --git a/src/backing/backing-calendar-source.vala b/src/backing/backing-calendar-source.vala
index 67ddd64..7f97a9b 100644
--- a/src/backing/backing-calendar-source.vala
+++ b/src/backing/backing-calendar-source.vala
@@ -14,8 +14,8 @@ namespace California.Backing {
  */
 
 public abstract class CalendarSource : Source {
-    protected CalendarSource(string title) {
-        base (title);
+    protected CalendarSource(string id, string title) {
+        base (id, title);
     }
     
     /**
diff --git a/src/backing/backing-manager.vala b/src/backing/backing-manager.vala
index 68b38d6..d099370 100644
--- a/src/backing/backing-manager.vala
+++ b/src/backing/backing-manager.vala
@@ -24,6 +24,20 @@ public class Manager : BaseObject {
      */
     public signal void open_store_failed(Store store, Error err);
     
+    /**
+     * Fired when a { link Store} adds a new { link Source}.
+     *
+     * @see Source.source_added
+     */
+    public signal void source_added(Store store, Source source);
+    
+    /**
+     * Fired when a { link Store} removes a { link Source}.
+     *
+     * @see Source.source_removed
+     */
+    public signal void source_removed(Store store, Source source);
+    
     private Manager() {
     }
     
@@ -57,12 +71,18 @@ public class Manager : BaseObject {
     public async int open_async(Cancellable? cancellable) throws Error {
         int count = 0;
         foreach (Store store in stores) {
+            store.source_added.connect(on_source_added);
+            store.source_removed.connect(on_source_removed);
+            
             try {
                 yield store.open_async(cancellable);
                 assert(store.is_open);
                 
                 count++;
             } catch (Error err) {
+                store.source_added.disconnect(on_source_added);
+                store.source_removed.disconnect(on_source_removed);
+                
                 // treat cancelled as cancelled
                 if (err is IOError.CANCELLED)
                     throw err;
@@ -85,6 +105,9 @@ public class Manager : BaseObject {
      */
     public async void close_async(Cancellable? cancellable) throws Error {
         foreach (Store store in stores) {
+            store.source_added.disconnect(on_source_added);
+            store.source_removed.disconnect(on_source_removed);
+            
             try {
                 if (store.is_open) {
                     yield store.close_async(cancellable);
@@ -102,6 +125,14 @@ public class Manager : BaseObject {
         is_open = false;
     }
     
+    private void on_source_added(Store store, Source source) {
+        source_added(store, source);
+    }
+    
+    private void on_source_removed(Store store, Source source) {
+        source_removed(store, source);
+    }
+    
     /**
      * Returns a read-only list of all available { link Store}s.
      *
@@ -128,8 +159,6 @@ public class Manager : BaseObject {
     /**
      * Returns a list of all available { link Source}s of a particular type.
      *
-     * The list will be sorted by the Sources title in lexiographic order.
-     *
      * Must only be called while the { link Manager} is open.
      *
      * @see Store.get_sources_of_type
@@ -139,10 +168,6 @@ public class Manager : BaseObject {
         foreach (Store store in stores)
             sources.add_all(store.get_sources_of_type<G>());
         
-        sources.sort((a, b) => {
-            return String.stricmp(((Source) a).title, ((Source) b).title);
-        });
-        
         return sources;
     }
     
diff --git a/src/backing/backing-source.vala b/src/backing/backing-source.vala
index 80c9498..c45625f 100644
--- a/src/backing/backing-source.vala
+++ b/src/backing/backing-source.vala
@@ -16,13 +16,20 @@ namespace California.Backing {
  * @see Manager
  */
 
-public abstract class Source : BaseObject {
+public abstract class Source : BaseObject, Gee.Comparable<Source> {
     public const string PROP_IS_AVAILABLE = "is-available";
     public const string PROP_TITLE = "title";
     public const string PROP_VISIBLE = "visible";
     public const string PROP_COLOR = "color";
     
     /**
+     * A unique identifier for the { link Source}.
+     *
+     * This value is persisted by the Source's { link Backing.Store}.
+     */
+    public string id { get; private set; }
+    
+    /**
      * True if the { link Source} is unavailable for use due to being removed from it's
      * { link Backing.Store}.
      *
@@ -56,7 +63,8 @@ public abstract class Source : BaseObject {
      */
     public string color { get; set; }
     
-    protected Source(string title) {
+    protected Source(string id, string title) {
+        this.id = id;
         this.title = title;
     }
     
@@ -98,6 +106,24 @@ public abstract class Source : BaseObject {
         color = Gfx.rgb_to_uint8_rgb_string(Gfx.rgba_to_rgb(rgba));
     }
     
+    /**
+     * The natural comparator for { link Source}s.
+     *
+     * The natural comparator uses the { link title} (compared case-insensitively) then the
+     * { link id} to stabilize the sort.
+     */
+    public virtual int compare_to(Source other) {
+        if (this == other)
+            return 0;
+        
+        int compare = String.stricmp(title, other.title);
+        if (compare != 0)
+            return compare;
+        
+        // use the Source's id to stabilize the sort
+        return strcmp(id, other.id);
+    }
+    
     public override string to_string() {
         return title;
     }
diff --git a/src/backing/eds/backing-eds-calendar-source.vala 
b/src/backing/eds/backing-eds-calendar-source.vala
index a772325..35a2966 100644
--- a/src/backing/eds/backing-eds-calendar-source.vala
+++ b/src/backing/eds/backing-eds-calendar-source.vala
@@ -20,7 +20,7 @@ internal class EdsCalendarSource : CalendarSource {
     private Cancellable? source_write_cancellable = null;
     
     public EdsCalendarSource(E.Source eds_source, E.SourceCalendar eds_calendar) {
-        base (eds_source.display_name);
+        base (eds_source.uid, eds_source.display_name);
         
         this.eds_source = eds_source;
         this.eds_calendar = eds_calendar;
diff --git a/src/host/host-create-update-event.vala b/src/host/host-create-update-event.vala
index 7e8530a..4e4d3e0 100644
--- a/src/host/host-create-update-event.vala
+++ b/src/host/host-create-update-event.vala
@@ -169,6 +169,8 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
         
         // initialize available calendars
         calendar_sources = Backing.Manager.instance.get_sources_of_type<Backing.CalendarSource>();
+        calendar_sources.sort();
+        
         index = 0;
         int calendar_source_index = 0;
         Gee.Iterator<Backing.Source> iter = calendar_sources.iterator();
diff --git a/src/manager/manager-calendar-list.vala b/src/manager/manager-calendar-list.vala
index 5d1ce98..e7ccb33 100644
--- a/src/manager/manager-calendar-list.vala
+++ b/src/manager/manager-calendar-list.vala
@@ -25,16 +25,27 @@ public class CalendarList : Gtk.Grid, Toolkit.Card {
     [GtkChild]
     private Gtk.ListBox calendar_list_box;
     
+    private Toolkit.ListBoxModel<Backing.CalendarSource> model;
+    
     public CalendarList() {
+        model = new Toolkit.ListBoxModel<Backing.CalendarSource>(calendar_list_box, model_presentation);
+        
         // if already open, initialize now
         if (Backing.Manager.instance.is_open)
             init();
         
+        // use Manager's signals to add and remove from model
+        Backing.Manager.instance.source_added.connect(on_source_added_to_manager);
+        Backing.Manager.instance.source_removed.connect(on_source_removed_from_manager);
+        
         // otherwise, initialize when it does open
         Backing.Manager.instance.notify[Backing.Manager.PROP_IS_OPEN].connect(on_manager_opened_closed);
     }
     
     ~CalendarList() {
+        Backing.Manager.instance.source_added.disconnect(on_source_added_to_manager);
+        Backing.Manager.instance.source_removed.disconnect(on_source_removed_from_manager);
+        
         Backing.Manager.instance.notify[Backing.Manager.PROP_IS_OPEN].disconnect(on_manager_opened_closed);
     }
     
@@ -45,23 +56,30 @@ public class CalendarList : Gtk.Grid, Toolkit.Card {
         if (Backing.Manager.instance.is_open)
             init();
         else
-            clear();
+            model.clear();
     }
     
     private void init() {
         assert(Backing.Manager.instance.is_open);
         
-        foreach (Backing.CalendarSource source in
-            Backing.Manager.instance.get_sources_of_type<Backing.CalendarSource>()) {
-            calendar_list_box.add(new CalendarListItem(source));
-        }
+        model.clear();
+        model.add_many(Backing.Manager.instance.get_sources_of_type<Backing.CalendarSource>());
+    }
+    
+    private Gtk.Widget model_presentation(Backing.CalendarSource calendar) {
+        return new CalendarListItem(calendar);
+    }
+    
+    private void on_source_added_to_manager(Backing.Store store, Backing.Source source) {
+        Backing.CalendarSource? calendar = source as Backing.CalendarSource;
+        if (calendar != null)
+            model.add(calendar);
     }
     
-    private void clear() {
-        foreach (unowned Gtk.Widget child in calendar_list_box.get_children()) {
-            if (child is CalendarListItem)
-                calendar_list_box.remove(child);
-        };
+    private void on_source_removed_from_manager(Backing.Store store, Backing.Source source) {
+        Backing.CalendarSource? calendar = source as Backing.CalendarSource;
+        if (calendar != null)
+            model.remove(calendar);
     }
     
     [GtkCallback]


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