[california/wip/725787-remove-recurring: 8/8] API development



commit b0f83bacd42f8a2c235d5446ee100e2a7bdb2f01
Author: Jim Nelson <jim yorba org>
Date:   Wed Jul 2 12:44:40 2014 -0700

    API development
    
    Examining Evolution, need to re-think how signals from ClientView are
    handled in relation to how instances are handled internally.

 .../backing-calendar-source-subscription.vala      |    7 +++
 src/backing/backing-calendar-source.vala           |   54 +++++++++++++++++--
 src/backing/eds/backing-eds-calendar-source.vala   |   44 +++++++++++++++-
 src/component/component-date-time.vala             |    6 ++-
 src/host/host-show-event.vala                      |   37 ++++++++++----
 vapi/libecal-1.2.vapi                              |   21 ++++----
 vapi/libecal-1.2/libecal-1.2.metadata              |    4 +-
 7 files changed, 142 insertions(+), 31 deletions(-)
---
diff --git a/src/backing/backing-calendar-source-subscription.vala 
b/src/backing/backing-calendar-source-subscription.vala
index 71216fd..12a1342 100644
--- a/src/backing/backing-calendar-source-subscription.vala
+++ b/src/backing/backing-calendar-source-subscription.vala
@@ -9,6 +9,13 @@ namespace California.Backing {
 /**
  * A subscription to an active timespan of interest of a calendar.
  *
+ * CalendarSourceSubscription generates { link Component.Instance}s of the various events in a
+ * window of time in a calendar.  Note that for recurring events, there is no interface to directly
+ * edit or remove the "original" iCalendar source.  Rather, the caller should update or remove
+ * generated instances of that recurring event with flags to indicate how the original is to be
+ * altered to match these changes.  See { link CalendarSource} for the interface to make these
+ * alterations.
+ *
  * The subscription can notify of calendar event updates and list a complete or partial collections
  * of the same.
  */
diff --git a/src/backing/backing-calendar-source.vala b/src/backing/backing-calendar-source.vala
index 1cd6cde..4286b67 100644
--- a/src/backing/backing-calendar-source.vala
+++ b/src/backing/backing-calendar-source.vala
@@ -9,11 +9,40 @@ namespace California.Backing {
 /**
  * An abstract representation of a backing source of calendar information.
  *
+ * CalendarSource provides information about the calendar and an interface for operating on
+ * { link Component.Instance}s in the calendar.  Use { link CalendarSourceSubscription} to generate
+ * those instances for specific windows of time.
+ *
  * @see Manager
  * @see Source
  */
 
 public abstract class CalendarSource : Source {
+    /**
+     * The affected range of a modification or removal operation.
+     *
+     * Note that zero (0) does ''not'' mean "none", it means { link AffectedInstances.THIS}.  The
+     * additional enums merely expand the scope of the default, which is the supplied instance.
+     */
+    public enum AffectedInstances {
+        /**
+         * Include the supplied { link Component.Instance} in the affected instances.
+         */
+        THIS = 0,
+        /**
+         * Include all prior { link Component.Instance}s in the affected instances.
+         */
+        THIS_AND_PRIOR,
+        /**
+         * Include all future { link Component.Instance}s in the affected instances.
+         */
+        THIS_AND_FUTURE,
+        /**
+         * Indicating all { link Component.Instance}s should be affected.
+         */
+        ALL
+    }
+    
     protected CalendarSource(string id, string title) {
         base (id, title);
     }
@@ -28,9 +57,10 @@ public abstract class CalendarSource : Source {
      * Creates a new { link Component} instance on the backing { link CalendarSource}.
      *
      * Outstanding { link CalendarSourceSubscriptions} will eventually report the generated
-     * instance when it's available.
+     * instance when it's available.  If the supplied instance includes an RRULE (i.e.
+     * { link Component.Instance.rrule}), one or more instances will be generated.
      *
-     * @returns The { link Component.UID}.of the generated instance, if available.
+     * @returns The { link Component.UID}.keyed to all instances, if available.
      */
     public abstract async Component.UID? create_component_async(Component.Instance instance,
         Cancellable? cancellable = null) throws Error;
@@ -45,15 +75,27 @@ public abstract class CalendarSource : Source {
         Cancellable? cancellable = null) throws Error;
     
     /**
-     * Destroys (removes) a { link Component} instance on the backing { link CalendarSource}.
+     * Destroys (removes) all { link Component.Instance}s on the backing { link CalendarSource}
+     * keyed to the supplied { link Component.UID}.
      *
-     * Outstanding { link CalendarSourceSubscriptions} will eventually report the instance as
-     * removed.
+     * Outstanding { link CalendarSourceSubscriptions} will eventually report all affected instances
+     * as removed.
      */
-    public abstract async void remove_component_async(Component.UID uid,
+    public abstract async void remove_all_instances_async(Component.UID uid,
         Cancellable? cancellable = null) throws Error;
     
     /**
+     * Destroys (removes) some or all { link Component.Instance}s on the backing
+     * { link CalendarSource} keyed to the supplied { link Component.UID}, the supplied RID,
+     * and the { link AffectedInstances}.
+     *
+     * Outstanding { link CalendarSourceSubscriptions} will eventually report all affected instances
+     * as removed.
+     */
+    public abstract async void remove_instances_async(Component.UID uid, Component.DateTime rid,
+        AffectedInstances affected, Cancellable? cancellable = null) throws Error;
+    
+    /**
      * Imports a { link Component.iCalendar} into the { link CalendarSource}.
      */
     public abstract async void import_icalendar_async(Component.iCalendar ical, Cancellable? cancellable = 
null)
diff --git a/src/backing/eds/backing-eds-calendar-source.vala 
b/src/backing/eds/backing-eds-calendar-source.vala
index 984f778..292359e 100644
--- a/src/backing/eds/backing-eds-calendar-source.vala
+++ b/src/backing/eds/backing-eds-calendar-source.vala
@@ -118,6 +118,31 @@ internal class EdsCalendarSource : CalendarSource {
         read_only = true;
     }
     
+    // Note that E.CalObjModType.ONLY_THIS is *never* returned ... examining EDS source code,
+    // it appears in e-cal-backend-file.c that ONLY_THIS merely removes the instance but does not
+    // include an EXDATE in the original iCal source ... I don't quite understand the benefit of
+    // this, as this suggests (a) other calendar clients won't learn of the removal and (b) the
+    // instance will be re-generated the next time the user runs an EDS calendar client.  In either
+    // case, ONLY maps to our desired effect by adding an EXDATE to the iCal source.
+    private E.CalObjModType convert_to_obj_mod_type(CalendarSource.AffectedInstances affected) {
+        switch (affected) {
+            case CalendarSource.AffectedInstances.THIS:
+                return E.CalObjModType.THIS;
+            
+            case CalendarSource.AffectedInstances.THIS_AND_FUTURE:
+                return E.CalObjModType.THIS_AND_FUTURE;
+            
+            case CalendarSource.AffectedInstances.THIS_AND_PRIOR:
+                return E.CalObjModType.THIS_AND_PRIOR;
+            
+            case CalendarSource.AffectedInstances.ALL:
+                return E.CalObjModType.ALL;
+            
+            default:
+                assert_not_reached();
+        }
+    }
+    
     private void check_open() throws BackingError {
         if (client == null)
             throw new BackingError.UNAVAILABLE("%s has been removed", to_string());
@@ -155,11 +180,26 @@ internal class EdsCalendarSource : CalendarSource {
         yield client.modify_object(instance.ical_component, E.CalObjModType.THIS, cancellable);
     }
     
-    public override async void remove_component_async(Component.UID uid,
+    public override async void remove_all_instances_async(Component.UID uid,
         Cancellable? cancellable = null) throws Error {
         check_open();
         
-        yield client.remove_object(uid.value, null, E.CalObjModType.THIS, cancellable);
+        yield client.remove_object(uid.value, null, E.CalObjModType.ALL, cancellable);
+    }
+    
+    public override async void remove_instances_async(Component.UID uid, Component.DateTime rid,
+        CalendarSource.AffectedInstances affected, Cancellable? cancellable = null) throws Error {
+        check_open();
+        
+        E.CalObjModType mod_type = convert_to_obj_mod_type(affected);
+        
+        debug("remove_instances_async: UID=%s RID=%s MOD_TYPE=%Xh", uid.value, rid.value, mod_type);
+        
+        // special-case ALL
+        if (mod_type == E.CalObjModType.ALL)
+            yield remove_all_instances_async(uid, cancellable);
+        else
+            yield client.remove_object(uid.value, rid.value, mod_type, cancellable);
     }
     
     public override async void import_icalendar_async(Component.iCalendar ical, Cancellable? cancellable = 
null)
diff --git a/src/component/component-date-time.vala b/src/component/component-date-time.vala
index 685949c..0a64cbc 100644
--- a/src/component/component-date-time.vala
+++ b/src/component/component-date-time.vala
@@ -46,8 +46,10 @@ public class DateTime : BaseObject, Gee.Hashable<DateTime>, Gee.Comparable<DateT
     
     /**
      * Returns the original iCalendar string representing the DATE/DATE-TIME property value.
+     *
+     * This does not include the iCal key string preceding the value, i.e. "DTSTAMP:"
      */
-    public string value_as_ical_string { get; private set; }
+    public string value { get; private set; }
     
     /**
      * The DATE-TIME for the iCal component and property kind.
@@ -119,7 +121,7 @@ public class DateTime : BaseObject, Gee.Hashable<DateTime>, Gee.Comparable<DateT
         }
         
         kind = ical_prop_kind;
-        value_as_ical_string = prop.get_value_as_string();
+        value = prop.get_value_as_string();
     }
     
     /**
diff --git a/src/host/host-show-event.vala b/src/host/host-show-event.vala
index 5cdc044..142a402 100644
--- a/src/host/host-show-event.vala
+++ b/src/host/host-show-event.vala
@@ -83,7 +83,7 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
         
         // If recurring (and so this is a generated instance of the VEVENT, not the VEVENT itself),
         // use a popup menu to ask how to remove this event
-        if (event.is_recurring_generated) {
+        if (event.is_recurring) {
             remove_recurring_menu = new Gtk.Menu();
             
             Gtk.MenuItem remove_all = new Gtk.MenuItem.with_mnemonic(_("Remove _All Events"));
@@ -98,13 +98,18 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
                 _("Remove This and All _Following Events"));
             remove_following.activate.connect(on_remove_recurring_this_and_following);
             remove_recurring_menu.append(remove_following);
+            
+            Gtk.MenuItem remove_prior = new Gtk.MenuItem.with_mnemonic(
+                _("Remove This and All _Prior Events"));
+            remove_prior.activate.connect(on_remove_recurring_this_and_prior);
+            remove_recurring_menu.append(remove_prior);
         }
         
         // don't current support updating or removing recurring events properly; see
         // https://bugzilla.gnome.org/show_bug.cgi?id=725786
         bool read_only = event.calendar_source != null && event.calendar_source.read_only;
         
-        bool updatable = !event.is_recurring_generated && !read_only;
+        bool updatable = !event.is_recurring && !read_only;
         update_button.visible = updatable;
         update_button.no_show_all = updatable;
         
@@ -151,7 +156,7 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
     
     [GtkCallback]
     private void on_remove_button_clicked() {
-        if (event.is_recurring_generated) {
+        if (event.is_recurring) {
             assert(remove_recurring_menu != null);
             
             remove_recurring_menu.popup(null, null, null, 0, Gtk.get_current_event_time());
@@ -160,7 +165,7 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
             return;
         }
         
-        remove_event_async.begin();
+        remove_events_async.begin(null, Backing.CalendarSource.AffectedInstances.ALL);
     }
     
     [GtkCallback]
@@ -173,32 +178,44 @@ public class ShowEvent : Gtk.Grid, Toolkit.Card {
         notify_user_closed();
     }
     
-    private async void remove_event_async() {
+    private async void remove_events_async(Component.DateTime? rid,
+        Backing.CalendarSource.AffectedInstances affected) {
         Gdk.Cursor? cursor = Toolkit.set_busy(this);
         
         Error? remove_err = null;
         try {
-            yield event.calendar_source.remove_component_async(event.uid, null);
+            if (rid == null || affected == Backing.CalendarSource.AffectedInstances.ALL)
+                yield event.calendar_source.remove_all_instances_async(event.uid, null);
+            else
+                yield event.calendar_source.remove_instances_async(event.uid, rid, affected, null);
         } catch (Error err) {
             remove_err = err;
         }
         
         Toolkit.set_unbusy(this, cursor);
         
-        if (remove_err == null)
+        if (remove_err == null) {
             notify_success();
-        else
-            notify_failure(_("Unable to remove event: %s").printf(remove_err.message));
+        } else {
+            notify_failure(ngettext("Unable to remove event: %s", "Unable to remove events: %s",
+                rid == null ? 1 : 2).printf(remove_err.message));
+        }
     }
     
     private void on_remove_recurring_all() {
-        remove_event_async.begin();
+        remove_events_async.begin(null, Backing.CalendarSource.AffectedInstances.ALL);
     }
     
     private void on_remove_recurring_this() {
+        remove_events_async.begin(event.rid, Backing.CalendarSource.AffectedInstances.THIS);
     }
     
     private void on_remove_recurring_this_and_following() {
+        remove_events_async.begin(event.rid, Backing.CalendarSource.AffectedInstances.THIS_AND_FUTURE);
+    }
+    
+    private void on_remove_recurring_this_and_prior() {
+        remove_events_async.begin(event.rid, Backing.CalendarSource.AffectedInstances.THIS_AND_PRIOR);
     }
 }
 
diff --git a/vapi/libecal-1.2.vapi b/vapi/libecal-1.2.vapi
index e414c46..7952301 100644
--- a/vapi/libecal-1.2.vapi
+++ b/vapi/libecal-1.2.vapi
@@ -248,6 +248,17 @@ namespace E {
                public void set_repeat (E.CalComponentAlarmRepeat repeat);
                public void set_trigger (E.CalComponentAlarmTrigger trigger);
        }
+       [CCode (cheader_filename = "libecal/libecal.h", copy_function = "e_cal_component_id_copy", 
free_function = "e_cal_component_free_id")]
+       [Compact]
+       public class CalComponentId {
+               public weak string rid;
+               public weak string uid;
+               [CCode (has_construct_function = false)]
+               public CalComponentId (string uid, string rid);
+               public E.CalComponentId copy ();
+               public bool equal (E.CalComponentId id2);
+               public uint hash ();
+       }
        [CCode (cheader_filename = "libecal/libecal.h")]
        public interface TimezoneCache : GLib.Object {
                public abstract unowned GLib.List list_timezones ();
@@ -301,16 +312,6 @@ namespace E {
                public weak string tzid;
        }
        [CCode (cheader_filename = "libecal/libecal.h")]
-       public struct CalComponentId {
-               public weak string uid;
-               public weak string rid;
-               [CCode (has_construct_function = false)]
-               public CalComponentId (string uid, string rid);
-               public E.CalComponentId copy ();
-               public bool equal (E.CalComponentId id2);
-               public uint hash ();
-       }
-       [CCode (cheader_filename = "libecal/libecal.h")]
        public struct CalComponentOrganizer {
                public weak string value;
                public weak string sentby;
diff --git a/vapi/libecal-1.2/libecal-1.2.metadata b/vapi/libecal-1.2/libecal-1.2.metadata
index 0d82150..951af62 100644
--- a/vapi/libecal-1.2/libecal-1.2.metadata
+++ b/vapi/libecal-1.2/libecal-1.2.metadata
@@ -234,7 +234,9 @@ ECalComponentAttendee is_value_type="1"
 ECalComponentDateTime is_value_type="1" has_destroy_function="0"
 ECalComponentDateTime.value type_name="iCal.icaltimetype*"
 
-ECalComponentId is_value_type="1"
+ECalComponentId is_value_type="0" free_function="e_cal_component_free_id"
+e_cal_component_id_copy transfer_ownership="1"
+
 ECalComponentOrganizer is_value_type="1"
 ECalComponentPeriod is_value_type="1"
 ECalComponentPeriod.start type_name="iCal.icaltimetype"


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