[evolution-data-server] Bug 620088 - Enable "This and future" recurrence change option
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug 620088 - Enable "This and future" recurrence change option
- Date: Tue, 18 Nov 2014 11:17:05 +0000 (UTC)
commit adf31279ccd604b1ee44ab2693781579a9d6e22f
Author: Milan Crha <mcrha redhat com>
Date: Tue Nov 18 12:15:04 2014 +0100
Bug 620088 - Enable "This and future" recurrence change option
calendar/backends/caldav/e-cal-backend-caldav.c | 121 ++++++++-
calendar/backends/file/e-cal-backend-file.c | 83 ++++--
calendar/libecal/e-cal-util.c | 340 ++++++++++++++++++++---
calendar/libecal/e-cal-util.h | 7 +
4 files changed, 486 insertions(+), 65 deletions(-)
---
diff --git a/calendar/backends/caldav/e-cal-backend-caldav.c b/calendar/backends/caldav/e-cal-backend-caldav.c
index 3add622..92ca0b8 100644
--- a/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -2654,7 +2654,6 @@ caldav_get_backend_property (ECalBackend *backend,
const gchar *extension_name;
caps = g_string_new (
- CAL_STATIC_CAPABILITY_NO_THISANDFUTURE ","
CAL_STATIC_CAPABILITY_NO_THISANDPRIOR ","
CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED);
@@ -4025,8 +4024,8 @@ do_modify_objects (ECalBackendCalDAV *cbdav,
GError **error)
{
ECalComponent *comp;
- icalcomponent *cache_comp;
- gboolean online, did_put = FALSE;
+ icalcomponent *cache_comp, *master_comp;
+ gboolean online, did_put = FALSE, success = TRUE;
ECalComponentId *id;
struct icaltimetype current;
gchar *href = NULL, *etag = NULL;
@@ -4158,6 +4157,112 @@ do_modify_objects (ECalBackendCalDAV *cbdav,
break;
case E_CAL_OBJ_MOD_THIS_AND_PRIOR:
case E_CAL_OBJ_MOD_THIS_AND_FUTURE:
+ master_comp = get_master_comp (cbdav, cache_comp);
+ if (e_cal_component_is_instance (comp) && master_comp) {
+ ECalComponent *mcomp;
+ gboolean processed = FALSE;
+ struct icaltimetype rid, master_dtstart;
+ icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
+ icalcomponent *split_icalcomp;
+ icalproperty *prop;
+
+ rid = icalcomponent_get_recurrenceid (icalcomp);
+ mcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone
(master_comp));
+
+ if (mod == E_CAL_OBJ_MOD_THIS_AND_FUTURE &&
+ e_cal_util_is_first_instance (mcomp, icalcomponent_get_recurrenceid (icalcomp),
resolve_tzid, cbdav)) {
+ icalproperty *prop = icalcomponent_get_first_property (icalcomp,
ICAL_RECURRENCEID_PROPERTY);
+
+ if (prop)
+ icalcomponent_remove_property (icalcomp, prop);
+
+ e_cal_component_rescan (comp);
+
+ /* Then do it like for "mod_all" */
+ cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(e_cal_component_get_icalcomponent (comp)));
+ g_clear_object (&mcomp);
+
+ if (new_components) {
+ /* read the comp from cache again, as some servers can modify it on
put */
+ *new_components = g_slist_prepend (*new_components,
get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid, NULL, comp));
+ }
+ break;
+ }
+
+ prop = icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY);
+ if (prop)
+ icalcomponent_remove_property (icalcomp, prop);
+ e_cal_component_rescan (comp);
+
+ master_dtstart = icalcomponent_get_dtstart (master_comp);
+ if (master_dtstart.zone && master_dtstart.zone != rid.zone)
+ rid = icaltime_convert_to_zone (rid, (icaltimezone *) master_dtstart.zone);
+ split_icalcomp = e_cal_util_split_at_instance (icalcomp, rid, master_dtstart);
+ if (split_icalcomp) {
+ ECalComponent *prev_comp;
+
+ prev_comp = e_cal_component_clone (mcomp);
+
+ rid = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ());
+ e_cal_util_remove_instances (master_comp, rid, mod);
+ e_cal_component_rescan (mcomp);
+
+ if (new_components) {
+ *new_components = g_slist_prepend (*new_components,
+ get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid,
NULL, mcomp));
+ }
+
+ g_clear_object (&prev_comp);
+ }
+
+ processed = TRUE;
+
+ cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(master_comp));
+ if (split_icalcomp) {
+ gchar *new_uid;
+
+ new_uid = e_cal_component_gen_uid ();
+ icalcomponent_set_uid (split_icalcomp, new_uid);
+ g_free (new_uid);
+
+ g_warn_if_fail (e_cal_component_set_icalcomponent (comp, split_icalcomp));
+
+ /* sanitize the component */
+ sanitize_component ((ECalBackend *) cbdav, comp);
+
+ if (online) {
+ CalDAVObject object;
+
+ object.href = ecalcomp_gen_href (comp);
+ object.etag = NULL;
+ object.cdata = pack_cobj (cbdav, split_icalcomp);
+
+ success = caldav_server_put_object (cbdav, &object, split_icalcomp,
cancellable, error);
+ if (success && new_components) {
+ ECalComponent *new_comp;
+
+ /* read the comp from cache again, as some servers can modify
it on put */
+ new_comp = get_ecalcomp_master_from_cache_or_fallback (cbdav,
icalcomponent_get_uid (split_icalcomp), NULL, comp);
+ if (new_comp)
+ e_cal_backend_notify_component_created (E_CAL_BACKEND
(cbdav), new_comp);
+
+ g_clear_object (&new_comp);
+ }
+
+ caldav_object_free (&object, FALSE);
+ } else {
+ /* mark component as out of synch */
+ /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_CREATED); */
+ }
+ }
+
+ g_clear_object (&mcomp);
+
+ if (!processed)
+ cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(e_cal_component_get_icalcomponent (comp)));
+ } else {
+ cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(e_cal_component_get_icalcomponent (comp)));
+ }
break;
}
@@ -4168,7 +4273,7 @@ do_modify_objects (ECalBackendCalDAV *cbdav,
object.etag = etag;
object.cdata = pack_cobj (cbdav, cache_comp);
- did_put = caldav_server_put_object (cbdav, &object, cache_comp, cancellable, error);
+ did_put = success && caldav_server_put_object (cbdav, &object, cache_comp, cancellable,
error);
caldav_object_free (&object, FALSE);
href = NULL;
@@ -4269,6 +4374,14 @@ do_remove_objects (ECalBackendCalDAV *cbdav,
break;
case E_CAL_OBJ_MOD_THIS_AND_PRIOR:
case E_CAL_OBJ_MOD_THIS_AND_FUTURE:
+ if (remove_instance (cbdav, cache_comp, icaltime_from_string (rid), mod, TRUE)) {
+ if (new_components) {
+ icalcomponent *master = get_master_comp (cbdav, cache_comp);
+ if (master) {
+ *new_components = g_slist_prepend (*new_components,
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
+ }
+ }
+ }
break;
}
diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c
index b7479ee..b39505f 100644
--- a/calendar/backends/file/e-cal-backend-file.c
+++ b/calendar/backends/file/e-cal-backend-file.c
@@ -472,10 +472,9 @@ e_cal_backend_file_get_backend_property (ECalBackend *backend,
return g_strjoin (
",",
CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS,
- CAL_STATIC_CAPABILITY_NO_THISANDFUTURE,
+ CAL_STATIC_CAPABILITY_NO_THISANDPRIOR,
CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED,
CAL_STATIC_CAPABILITY_REMOVE_ONLY_THIS,
- CAL_STATIC_CAPABILITY_NO_THISANDPRIOR,
CAL_STATIC_CAPABILITY_BULK_ADDS,
CAL_STATIC_CAPABILITY_BULK_MODIFIES,
CAL_STATIC_CAPABILITY_BULK_REMOVES,
@@ -2414,7 +2413,7 @@ e_cal_backend_file_modify_objects (ECalBackendSync *backend,
gchar *rid = NULL;
gchar *real_rid;
const gchar *comp_uid;
- icalcomponent * icalcomp = l->data;
+ icalcomponent * icalcomp = l->data, *split_icalcomp = NULL;
ECalComponent *comp, *recurrence;
ECalBackendFileObject *obj_data;
@@ -2489,19 +2488,23 @@ e_cal_backend_file_modify_objects (ECalBackendSync *backend,
break;
case E_CAL_OBJ_MOD_THIS_AND_PRIOR:
case E_CAL_OBJ_MOD_THIS_AND_FUTURE:
- if (!rid || !*rid) {
- if (old_components)
- *old_components = g_slist_prepend (*old_components,
obj_data->full_object ? e_cal_component_clone (obj_data->full_object) : NULL);
-
- remove_component (cbfile, comp_uid, obj_data);
-
- /* Add the new object */
- add_component (cbfile, comp, TRUE);
- break;
- }
+ if (!rid || !*rid)
+ goto like_mod_all;
/* remove the component from our data, temporarily */
if (obj_data->full_object) {
+ if (mod == E_CAL_OBJ_MOD_THIS_AND_FUTURE &&
+ e_cal_util_is_first_instance (obj_data->full_object,
icalcomponent_get_recurrenceid (icalcomp), resolve_tzid, priv->icalcomp)) {
+ icalproperty *prop = icalcomponent_get_first_property (icalcomp,
ICAL_RECURRENCEID_PROPERTY);
+
+ if (prop)
+ icalcomponent_remove_property (icalcomp, prop);
+
+ e_cal_component_rescan (comp);
+
+ goto like_mod_all;
+ }
+
icalcomponent_remove_component (
priv->icalcomp,
e_cal_component_get_icalcomponent (obj_data->full_object));
@@ -2536,24 +2539,58 @@ e_cal_backend_file_modify_objects (ECalBackendSync *backend,
* so that it's always before any detached instance we
* might have */
if (obj_data->full_object) {
+ struct icaltimetype rid_struct = icalcomponent_get_recurrenceid (icalcomp),
master_dtstart;
+ icalcomponent *master_icalcomp = e_cal_component_get_icalcomponent
(obj_data->full_object);
+ icalproperty *prop = icalcomponent_get_first_property (icalcomp,
ICAL_RECURRENCEID_PROPERTY);
+
+ if (prop)
+ icalcomponent_remove_property (icalcomp, prop);
+
+ master_dtstart = icalcomponent_get_dtstart (master_icalcomp);
+ if (master_dtstart.zone && master_dtstart.zone != rid_struct.zone)
+ rid_struct = icaltime_convert_to_zone (rid_struct, (icaltimezone *)
master_dtstart.zone);
+ split_icalcomp = e_cal_util_split_at_instance (icalcomp, rid_struct,
master_dtstart);
+ if (split_icalcomp) {
+ ECalComponent *prev_comp;
+ prev_comp = e_cal_component_clone (obj_data->full_object);
+
+ rid_struct = icaltime_convert_to_zone (rid_struct,
icaltimezone_get_utc_timezone ());
+ e_cal_util_remove_instances (e_cal_component_get_icalcomponent
(obj_data->full_object), rid_struct, mod);
+ e_cal_component_rescan (obj_data->full_object);
+
+ e_cal_backend_notify_component_modified (E_CAL_BACKEND (backend),
prev_comp, obj_data->full_object);
+
+ g_clear_object (&prev_comp);
+ }
+
icalcomponent_add_component (
priv->icalcomp,
e_cal_component_get_icalcomponent (obj_data->full_object));
priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
+ } else {
+ struct icaltimetype rid_struct = icalcomponent_get_recurrenceid (icalcomp);
+
+ split_icalcomp = e_cal_util_split_at_instance (icalcomp, rid_struct,
icaltime_null_time ());
}
- /* add the new detached recurrence */
- g_hash_table_insert (
- obj_data->recurrences,
- g_strdup (rid),
- comp);
- icalcomponent_add_component (
- priv->icalcomp,
- e_cal_component_get_icalcomponent (comp));
- priv->comp = g_list_append (priv->comp, comp);
- obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
+ if (split_icalcomp) {
+ gchar *new_uid;
+
+ new_uid = e_cal_component_gen_uid ();
+ icalcomponent_set_uid (split_icalcomp, new_uid);
+ g_free (new_uid);
+
+ g_warn_if_fail (e_cal_component_set_icalcomponent (comp, split_icalcomp));
+
+ /* sanitize the component */
+ sanitize_component (cbfile, comp);
+
+ /* Add the object */
+ add_component (cbfile, comp, TRUE);
+ }
break;
case E_CAL_OBJ_MOD_ALL :
+ like_mod_all:
/* Remove the old version */
if (old_components)
*old_components = g_slist_prepend (*old_components, obj_data->full_object ?
e_cal_component_clone (obj_data->full_object) : NULL);
diff --git a/calendar/libecal/e-cal-util.c b/calendar/libecal/e-cal-util.c
index 7c81dba..0034665 100644
--- a/calendar/libecal/e-cal-util.c
+++ b/calendar/libecal/e-cal-util.c
@@ -1081,26 +1081,18 @@ time_matches_rid (struct icaltimetype itt,
return FALSE;
}
-/**
- * e_cal_util_remove_instances:
- * @icalcomp: A (recurring) #icalcomponent
- * @rid: The base RECURRENCE-ID to remove
- * @mod: How to interpret @rid
- *
- * Removes one or more instances from @comp according to @rid and @mod.
- *
- * FIXME: should probably have a return value indicating whether @icalcomp
- * still has any instances
- **/
-void
-e_cal_util_remove_instances (icalcomponent *icalcomp,
- struct icaltimetype rid,
- ECalObjModType mod)
+static void
+e_cal_util_remove_instances_ex (icalcomponent *icalcomp,
+ struct icaltimetype rid,
+ ECalObjModType mod,
+ gboolean keep_rid,
+ gboolean can_add_exrule)
{
icalproperty *prop;
struct icaltimetype itt, recur;
struct icalrecurrencetype rule;
icalrecur_iterator *iter;
+ GSList *remove_props = NULL, *rrules = NULL, *link;
g_return_if_fail (icalcomp != NULL);
g_return_if_fail (mod != E_CAL_OBJ_MOD_ALL);
@@ -1112,17 +1104,28 @@ e_cal_util_remove_instances (icalcomponent *icalcomp,
struct icaldatetimeperiodtype period;
period = icalproperty_get_rdate (prop);
- if (time_matches_rid (period.time, rid, mod))
- icalcomponent_remove_property (icalcomp, prop);
+ if (time_matches_rid (period.time, rid, mod) && (!keep_rid ||
+ icaltime_compare (itt, rid) != 0))
+ remove_props = g_slist_prepend (remove_props, prop);
}
for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY);
prop;
prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) {
itt = icalproperty_get_exdate (prop);
- if (time_matches_rid (itt, rid, mod))
- icalcomponent_remove_property (icalcomp, prop);
+ if (time_matches_rid (itt, rid, mod) && (!keep_rid ||
+ icaltime_compare (itt, rid) != 0))
+ remove_props = g_slist_prepend (remove_props, prop);
}
+ for (link = remove_props; link; link = g_slist_next (link)) {
+ prop = link->data;
+
+ icalcomponent_remove_property (icalcomp, prop);
+ }
+
+ g_slist_free (remove_props);
+ remove_props = NULL;
+
/* If we're only removing one instance, just add an EXDATE. */
if (mod == E_CAL_OBJ_MOD_THIS) {
prop = icalproperty_new_exdate (rid);
@@ -1135,25 +1138,56 @@ e_cal_util_remove_instances (icalcomponent *icalcomp,
for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY);
prop;
prop = icalcomponent_get_next_property (icalcomp, ICAL_RRULE_PROPERTY)) {
+ rrules = g_slist_prepend (rrules, prop);
+ }
+
+ for (link = rrules; link; link = g_slist_next (link)) {
+ prop = link->data;
rule = icalproperty_get_rrule (prop);
iter = icalrecur_iterator_new (rule, rid);
recur = icalrecur_iterator_next (iter);
if (mod & E_CAL_OBJ_MOD_THIS_AND_FUTURE) {
- /* If there is a recurrence on or after rid,
- * use the UNTIL parameter to truncate the rule
- * at rid.
- */
+ /* Truncate the rule at rid. */
if (!icaltime_is_null_time (recur)) {
- rule.count = 0;
- rule.until = rid;
- icaltime_adjust (&rule.until, 0, 0, 0, -1);
+ /* Use count if it was used */
+ if (rule.count > 0) {
+ gint occurrences_count = 0;
+ icalrecur_iterator *count_iter;
+ struct icaltimetype count_recur;
+
+ count_iter = icalrecur_iterator_new (rule, icalcomponent_get_dtstart
(icalcomp));
+ while (count_recur = icalrecur_iterator_next (count_iter),
!icaltime_is_null_time (count_recur) && occurrences_count < rule.count) {
+ if (icaltime_compare (count_recur, rid) >= 0)
+ break;
+
+ occurrences_count++;
+ }
+
+ icalrecur_iterator_free (count_iter);
+
+ if (keep_rid && icaltime_compare (count_recur, rid) == 0)
+ occurrences_count++;
+
+ /* The caller should make sure that the remove will keep at least one
instance */
+ g_warn_if_fail (occurrences_count > 0);
+
+ rule.count = occurrences_count;
+ } else {
+ if (keep_rid && icaltime_compare (recur, rid) == 0)
+ rule.until = icaltime_add (rid, icalcomponent_get_duration
(icalcomp));
+ else
+ rule.until = rid;
+ icaltime_adjust (&rule.until, 0, 0, 0, -1);
+ }
+
icalproperty_set_rrule (prop, rule);
+ icalproperty_remove_parameter_by_name (prop, "X-EVOLUTION-ENDDATE");
}
} else {
/* (If recur == rid, skip to the next occurrence) */
- if (icaltime_compare (recur, rid) == 0)
+ if (!keep_rid && icaltime_compare (recur, rid) == 0)
recur = icalrecur_iterator_next (iter);
/* If there is a recurrence after rid, add
@@ -1161,21 +1195,251 @@ e_cal_util_remove_instances (icalcomponent *icalcomp,
* Otherwise, just remove the RRULE.
*/
if (!icaltime_is_null_time (recur)) {
- rule.count = 0;
- /* iCalendar says we should just use rid
- * here, but Outlook/Exchange handle
- * UNTIL incorrectly.
- */
- rule.until = icaltime_add (
- rid, icalcomponent_get_duration (icalcomp));
- prop = icalproperty_new_exrule (rule);
- icalcomponent_add_property (icalcomp, prop);
- } else
- icalcomponent_remove_property (icalcomp, prop);
+ if (can_add_exrule) {
+ rule.count = 0;
+ /* iCalendar says we should just use rid
+ * here, but Outlook/Exchange handle
+ * UNTIL incorrectly.
+ */
+ if (keep_rid && icaltime_compare (recur, rid) == 0) {
+ struct icaldurationtype duration = icalcomponent_get_duration
(icalcomp);
+ duration.is_neg = !duration.is_neg;
+ rule.until = icaltime_add (rid, duration);
+ } else
+ rule.until = icaltime_add (rid, icalcomponent_get_duration
(icalcomp));
+ prop = icalproperty_new_exrule (rule);
+ icalcomponent_add_property (icalcomp, prop);
+ }
+ } else {
+ remove_props = g_slist_prepend (remove_props, prop);
+ }
}
icalrecur_iterator_free (iter);
}
+
+ for (link = remove_props; link; link = g_slist_next (link)) {
+ prop = link->data;
+
+ icalcomponent_remove_property (icalcomp, prop);
+ }
+
+ g_slist_free (remove_props);
+ g_slist_free (rrules);
+}
+
+/**
+ * e_cal_util_remove_instances:
+ * @icalcomp: A (recurring) #icalcomponent
+ * @rid: The base RECURRENCE-ID to remove
+ * @mod: How to interpret @rid
+ *
+ * Removes one or more instances from @comp according to @rid and @mod.
+ *
+ * FIXME: should probably have a return value indicating whether @icalcomp
+ * still has any instances
+ **/
+void
+e_cal_util_remove_instances (icalcomponent *icalcomp,
+ struct icaltimetype rid,
+ ECalObjModType mod)
+{
+ g_return_if_fail (icalcomp != NULL);
+ g_return_if_fail (mod != E_CAL_OBJ_MOD_ALL);
+
+ e_cal_util_remove_instances_ex (icalcomp, rid, mod, FALSE, TRUE);
+}
+
+/**
+ * e_cal_util_split_at_instance:
+ * @icalcomp: A (recurring) #icalcomponent
+ * @rid: The base RECURRENCE-ID to remove
+ * @master_dtstart: The DTSTART of the master object
+ *
+ * Splits a recurring @icalcomp into two at time @rid. The returned icalcomponent
+ * is modified @icalcomp which contains recurrences beginning at @rid, inclusive.
+ * The instance identified by @rid should exist. The @master_dtstart can be
+ * a null time, then it is read from the @icalcomp.
+ *
+ * Use e_cal_util_remove_instances() with E_CAL_OBJ_MOD_THIS_AND_FUTURE mode
+ * on the @icalcomp to remove the overlapping interval from it, if needed.
+ *
+ * Returns: the split icalcomponent, or %NULL.
+ *
+ * Since: 3.14
+ **/
+icalcomponent *
+e_cal_util_split_at_instance (icalcomponent *icalcomp,
+ struct icaltimetype rid,
+ struct icaltimetype master_dtstart)
+{
+ icalproperty *prop;
+ struct instance_data instance;
+ struct icaltimetype start, end;
+ struct icaldurationtype duration;
+ GSList *remove_props = NULL, *link;
+
+ g_return_val_if_fail (icalcomp != NULL, NULL);
+ g_return_val_if_fail (!icaltime_is_null_time (rid), NULL);
+
+ /* Make sure this is really recurring */
+ if (!icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY) &&
+ !icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY))
+ return NULL;
+
+ /* Make sure the specified instance really exists */
+ start = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ());
+ end = start;
+ icaltime_adjust (&end, 0, 0, 0, 1);
+
+ instance.start = icaltime_as_timet (start);
+ instance.found = FALSE;
+ icalcomponent_foreach_recurrence (icalcomp, start, end,
+ check_instance, &instance);
+ /* Make the copy */
+ icalcomp = icalcomponent_new_clone (icalcomp);
+
+ e_cal_util_remove_instances_ex (icalcomp, rid, E_CAL_OBJ_MOD_THIS_AND_PRIOR, TRUE, FALSE);
+
+ start = rid;
+ if (icaltime_is_null_time (master_dtstart))
+ master_dtstart = icalcomponent_get_dtstart (icalcomp);
+ duration = icalcomponent_get_duration (icalcomp);
+
+ /* Expect that DTSTART and DTEND are already set when the instance could not be found */
+ if (instance.found) {
+ icalcomponent_set_dtstart (icalcomp, start);
+ /* Update either DURATION or DTEND */
+ if (icaltime_is_null_time (icalcomponent_get_dtend (icalcomp))) {
+ icalcomponent_set_duration (icalcomp, duration);
+ } else {
+ end = start;
+ if (duration.is_neg)
+ icaltime_adjust (&end, -duration.days - 7 * duration.weeks, -duration.hours,
-duration.minutes, -duration.seconds);
+ else
+ icaltime_adjust (&end, duration.days + 7 * duration.weeks, duration.hours,
duration.minutes, duration.seconds);
+ icalcomponent_set_dtend (icalcomp, end);
+ }
+ }
+
+ /* any RRULE with 'count' should be shortened */
+ for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY);
+ prop;
+ prop = icalcomponent_get_next_property (icalcomp, ICAL_RRULE_PROPERTY)) {
+ struct icaltimetype recur;
+ struct icalrecurrencetype rule;
+
+ rule = icalproperty_get_rrule (prop);
+
+ if (rule.count != 0) {
+ gint occurrences_count = 0;
+ icalrecur_iterator *iter;
+
+ iter = icalrecur_iterator_new (rule, master_dtstart);
+ while (recur = icalrecur_iterator_next (iter), !icaltime_is_null_time (recur) &&
occurrences_count < rule.count) {
+ if (icaltime_compare (recur, rid) >= 0)
+ break;
+
+ occurrences_count++;
+ }
+
+ icalrecur_iterator_free (iter);
+
+ if (icaltime_is_null_time (recur)) {
+ remove_props = g_slist_prepend (remove_props, prop);
+ } else {
+ rule.count -= occurrences_count;
+ icalproperty_set_rrule (prop, rule);
+ icalproperty_remove_parameter_by_name (prop, "X-EVOLUTION-ENDDATE");
+ }
+ }
+ }
+
+ for (link = remove_props; link; link = g_slist_next (link)) {
+ prop = link->data;
+
+ icalcomponent_remove_property (icalcomp, prop);
+ }
+
+ g_slist_free (remove_props);
+
+ return icalcomp;
+}
+
+typedef struct {
+ struct icaltimetype rid;
+ gboolean matches;
+} CheckFirstInstanceData;
+
+static gboolean
+check_first_instance_cb (ECalComponent *comp,
+ time_t instance_start,
+ time_t instance_end,
+ gpointer user_data)
+{
+ CheckFirstInstanceData *ifs = user_data;
+ icalcomponent *icalcomp;
+ struct icaltimetype rid;
+
+ g_return_val_if_fail (ifs != NULL, FALSE);
+
+ icalcomp = e_cal_component_get_icalcomponent (comp);
+ if (icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY) != NULL) {
+ rid = icalcomponent_get_recurrenceid (icalcomp);
+ } else {
+ struct icaltimetype dtstart;
+
+ dtstart = icalcomponent_get_dtstart (icalcomp);
+ if (dtstart.zone) {
+ rid = icaltime_from_timet_with_zone (instance_start, dtstart.is_date, dtstart.zone);
+ } else {
+ rid = icaltime_from_timet (instance_start, dtstart.is_date);
+ }
+ }
+
+ ifs->matches = icaltime_compare (ifs->rid, rid) == 0;
+
+ return FALSE;
+}
+
+/**
+ * e_cal_util_is_first_instance:
+ * @comp: an #ECalComponent instance
+ * @rid: a recurrence ID
+ * @tz_cb: (closure tz_cb_data) (scope call): The #ECalRecurResolveTimezoneFn to call
+ * @tz_cb_data: (closure): User data to be passed to the @tz_cb callback
+ *
+ * Returns whether the given @rid is the first instance of
+ * the recurrence defined in the @comp.
+ *
+ * Return: Whether the @rid identifies the first instance of @comp.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_cal_util_is_first_instance (ECalComponent *comp,
+ struct icaltimetype rid,
+ ECalRecurResolveTimezoneFn tz_cb,
+ gpointer tz_cb_data)
+{
+ CheckFirstInstanceData ifs;
+ icalcomponent *icalcomp;
+ time_t start, end;
+
+ g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+ g_return_val_if_fail (!icaltime_is_null_time (rid), FALSE);
+
+ ifs.rid = rid;
+ ifs.matches = FALSE;
+
+ icalcomp = e_cal_component_get_icalcomponent (comp);
+ start = icaltime_as_timet (icalcomponent_get_dtstart (icalcomp)) - 24 * 60 * 60;
+ end = icaltime_as_timet (icalcomponent_get_dtend (icalcomp)) + 24 * 60 * 60;
+
+ e_cal_recur_generate_instances (comp, start, end, check_first_instance_cb, &ifs,
+ tz_cb, tz_cb_data, icaltimezone_get_utc_timezone ());
+
+ return ifs.matches;
}
/**
diff --git a/calendar/libecal/e-cal-util.h b/calendar/libecal/e-cal-util.h
index 3e51498..cad1fe5 100644
--- a/calendar/libecal/e-cal-util.h
+++ b/calendar/libecal/e-cal-util.h
@@ -201,6 +201,13 @@ icalcomponent * e_cal_util_construct_instance (icalcomponent *icalcomp,
void e_cal_util_remove_instances (icalcomponent *icalcomp,
struct icaltimetype rid,
ECalObjModType mod);
+icalcomponent * e_cal_util_split_at_instance (icalcomponent *icalcomp,
+ struct icaltimetype rid,
+ struct icaltimetype master_dtstart);
+gboolean e_cal_util_is_first_instance (ECalComponent *comp,
+ struct icaltimetype rid,
+ ECalRecurResolveTimezoneFn tz_cb,
+ gpointer tz_cb_data);
gchar * e_cal_util_get_system_timezone_location (void);
icaltimezone * e_cal_util_get_system_timezone (void);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]