aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--calendar/ChangeLog15
-rw-r--r--calendar/cal-util/cal-recur.c1
-rw-r--r--calendar/cal-util/cal-util.c174
-rw-r--r--calendar/cal-util/cal-util.h7
-rw-r--r--calendar/pcs/cal-backend-file.c116
5 files changed, 297 insertions, 16 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog
index 83c0f3b274..accca887e4 100644
--- a/calendar/ChangeLog
+++ b/calendar/ChangeLog
@@ -1,3 +1,18 @@
+2003-10-24 Rodrigo Moya <rodrigo@ximian.com>
+
+ * cal-util/cal-util.[ch] (cal_util_construct_instance,
+ cal_util_remove_instances): new functions for individual
+ instances management.
+
+ * pcs/cal-backend-file.c (cal_backend_file_get_object): if we
+ dont have a recurrence in our hash table, generate one for the
+ specified recurrence ID.
+ (match_recurrence_sexp): new function to match recurrences on
+ regular expresessions.
+ (match_object_sexp): call match_recurrence_sexp() for all recurrences.
+ (cal_backend_file_modify_object): handle mod_types.
+ (cal_backend_file_remove_object): handle mod_types.
+
2003-10-24 JP Rosevear <jpr@ximian.com>
* gui/gnome-cal.h: update protos
diff --git a/calendar/cal-util/cal-recur.c b/calendar/cal-util/cal-recur.c
index 160a253730..6decd3eae2 100644
--- a/calendar/cal-util/cal-recur.c
+++ b/calendar/cal-util/cal-recur.c
@@ -4018,3 +4018,4 @@ const char *cal_recur_nth[31] = {
N_("30th"),
N_("31st")
};
+
diff --git a/calendar/cal-util/cal-util.c b/calendar/cal-util/cal-util.c
index f1c32610c1..f6ad0d4007 100644
--- a/calendar/cal-util/cal-util.c
+++ b/calendar/cal-util/cal-util.c
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <string.h>
#include <glib.h>
+#include <glib/gstrfuncs.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
#include "cal-util.h"
@@ -750,3 +751,176 @@ cal_util_event_dates_match (icalcomponent *icalcomp1, icalcomponent *icalcomp2)
return TRUE;
}
+
+/* Individual instances management */
+
+struct instance_data {
+ time_t start;
+ gboolean found;
+};
+
+static void
+check_instance (icalcomponent *comp, struct icaltime_span span, void *data)
+{
+ struct instance_data *instance = data;
+
+ if (span.start == instance->start)
+ instance->found = TRUE;
+}
+
+/**
+ * cal_util_construct_instance:
+ * @icalcomp: a recurring #icalcomponent
+ * @rid: the RECURRENCE-ID to construct a component for
+ *
+ * This checks that @rid indicates a valid recurrence of @icalcomp, and
+ * if so, generates a copy of @comp containing a RECURRENCE-ID of @rid.
+ *
+ * Return value: the instance, or %NULL
+ **/
+icalcomponent *
+cal_util_construct_instance (icalcomponent *icalcomp,
+ struct icaltimetype rid)
+{
+ struct instance_data instance;
+ struct icaltimetype start, end;
+
+ g_return_val_if_fail (icalcomp != NULL, 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 */
+ /* FIXME: does the libical recurrence code work correctly now? */
+ 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);
+ if (!instance.found)
+ return NULL;
+
+ /* Make the instance */
+ icalcomp = icalcomponent_new_clone (icalcomp);
+ icalcomponent_set_recurrenceid (icalcomp, rid);
+
+ return icalcomp;
+}
+
+static inline gboolean
+time_matches_rid (struct icaltimetype itt, struct icaltimetype rid, CalObjModType mod)
+{
+ int compare;
+
+ compare = icaltime_compare (itt, rid);
+ if (compare == 0)
+ return TRUE;
+ else if (compare < 0 && (mod & CALOBJ_MOD_THISANDPRIOR))
+ return TRUE;
+ else if (compare > 0 && (mod & CALOBJ_MOD_THISANDFUTURE))
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * 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 or not
+ * @icalcomp still has any instances
+ **/
+void
+cal_util_remove_instances (icalcomponent *icalcomp,
+ struct icaltimetype rid,
+ CalObjModType mod)
+{
+ icalproperty *prop;
+ struct icaltimetype itt, recur;
+ struct icalrecurrencetype rule;
+ icalrecur_iterator *iter;
+ struct instance_data instance;
+
+ g_return_if_fail (icalcomp != NULL);
+ g_return_if_fail (mod != CALOBJ_MOD_ALL);
+
+ /* First remove RDATEs and EXDATEs in the indicated range. */
+ for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RDATE_PROPERTY);
+ prop;
+ prop = icalcomponent_get_next_property (icalcomp, ICAL_RDATE_PROPERTY)) {
+ struct icaldatetimeperiodtype period;
+
+ period = icalproperty_get_rdate (prop);
+ if (time_matches_rid (period.time, rid, mod))
+ icalcomponent_remove_property (icalcomp, 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 we're only removing one instance, just add an EXDATE. */
+ if (mod == CALOBJ_MOD_THIS) {
+ prop = icalproperty_new_exdate (rid);
+ icalcomponent_add_property (icalcomp, prop);
+ return;
+ }
+
+ /* Otherwise, iterate through RRULEs */
+ /* FIXME: this may generate duplicate EXRULEs */
+ for (prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY);
+ prop;
+ prop = icalcomponent_get_next_property (icalcomp, ICAL_RRULE_PROPERTY)) {
+ rule = icalproperty_get_rrule (prop);
+
+ iter = icalrecur_iterator_new (rule, rid);
+ recur = icalrecur_iterator_next (iter);
+
+ if (mod & CALOBJ_MOD_THISANDFUTURE) {
+ /* If there is a recurrence on or after rid,
+ * use the UNTIL parameter to 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);
+ icalproperty_set_rrule (prop, rule);
+ }
+ } else {
+ /* (If recur == rid, skip to the next occurrence) */
+ if (icaltime_compare (recur, rid) == 0)
+ recur = icalrecur_iterator_next (iter);
+
+ /* If there is a recurrence after rid, add
+ * an EXRULE to block instances up to rid.
+ * 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);
+ }
+
+ icalrecur_iterator_free (iter);
+ }
+}
diff --git a/calendar/cal-util/cal-util.h b/calendar/cal-util/cal-util.h
index 336fabd6b4..48bce483f8 100644
--- a/calendar/cal-util/cal-util.h
+++ b/calendar/cal-util/cal-util.h
@@ -122,6 +122,13 @@ gboolean cal_util_event_dates_match (icalcomponent *icalcomp1, icalcomponent *ic
#define CAL_STATIC_CAPABILITY_REMOVE_ALARMS "remove-alarms"
#define CAL_STATIC_CAPABILITY_SAVE_SCHEDULES "save-schedules"
+/* Recurrent events. Management for instances */
+icalcomponent *cal_util_construct_instance (icalcomponent *icalcomp,
+ struct icaltimetype rid);
+void cal_util_remove_instances (icalcomponent *icalcomp,
+ struct icaltimetype rid,
+ CalObjModType mod);
+
G_END_DECLS
#endif
diff --git a/calendar/pcs/cal-backend-file.c b/calendar/pcs/cal-backend-file.c
index f1f5fa1d27..11f611f9e5 100644
--- a/calendar/pcs/cal-backend-file.c
+++ b/calendar/pcs/cal-backend-file.c
@@ -829,6 +829,7 @@ cal_backend_file_get_object (CalBackendSync *backend, Cal *cal, const char *uid,
CalBackendFilePrivate *priv;
CalBackendFileObject *obj_data;
CalComponent *comp = NULL;
+ gboolean free_comp = FALSE;
cbfile = CAL_BACKEND_FILE (backend);
priv = cbfile->priv;
@@ -844,6 +845,19 @@ cal_backend_file_get_object (CalBackendSync *backend, Cal *cal, const char *uid,
if (rid && *rid) {
comp = g_hash_table_lookup (obj_data->recurrences, rid);
if (!comp) {
+ icalcomponent *icalcomp;
+ struct icaltimetype itt;
+
+ itt = icaltime_from_string (rid);
+ icalcomp = cal_util_construct_instance (
+ cal_component_get_icalcomponent (obj_data->full_object),
+ itt);
+ if (!icalcomp)
+ return GNOME_Evolution_Calendar_ObjectNotFound;
+
+ comp = cal_component_new ();
+ free_comp = TRUE;
+ cal_component_set_icalcomponent (comp, icalcomp);
}
} else
comp = obj_data->full_object;
@@ -853,6 +867,9 @@ cal_backend_file_get_object (CalBackendSync *backend, Cal *cal, const char *uid,
*object = cal_component_get_as_string (comp);
+ if (free_comp)
+ g_object_unref (comp);
+
return GNOME_Evolution_Calendar_Success;
}
@@ -915,7 +932,8 @@ cal_backend_file_add_timezone (CalBackendSync *backend, Cal *cal, const char *tz
zone = icaltimezone_new ();
icaltimezone_set_component (zone, tz_comp);
- if (!icalcomponent_get_timezone (priv->icalcomp, icaltimezone_get_tzid (zone))) {
+ if (!icalcomponent_get_timezone (priv->icalcomp,
+ icaltimezone_get_tzid (zone))) {
icalcomponent_add_component (priv->icalcomp, tz_comp);
mark_dirty (cbfile);
}
@@ -960,6 +978,19 @@ typedef struct {
} MatchObjectData;
static void
+match_recurrence_sexp (gpointer key, gpointer value, gpointer data)
+{
+ CalComponent *comp = value;
+ MatchObjectData *match_data = data;
+
+ if ((!match_data->search_needed) ||
+ (cal_backend_object_sexp_match_comp (match_data->obj_sexp, comp, match_data->backend))) {
+ match_data->obj_list = g_list_append (match_data->obj_list,
+ cal_component_get_as_string (comp));
+ }
+}
+
+static void
match_object_sexp (gpointer key, gpointer value, gpointer data)
{
CalBackendFileObject *obj_data = value;
@@ -969,6 +1000,11 @@ match_object_sexp (gpointer key, gpointer value, gpointer data)
(cal_backend_object_sexp_match_comp (match_data->obj_sexp, obj_data->full_object, match_data->backend))) {
match_data->obj_list = g_list_append (match_data->obj_list,
cal_component_get_as_string (obj_data->full_object));
+
+ /* match also recurrences */
+ g_hash_table_foreach (obj_data->recurrences,
+ (GHFunc) match_recurrence_sexp,
+ match_data);
}
}
@@ -1378,7 +1414,8 @@ cal_backend_file_modify_object (CalBackendSync *backend, Cal *cal, const char *c
icalcomponent *icalcomp;
icalcomponent_kind kind;
const char *comp_uid;
- CalComponent *comp, *old_comp;
+ CalComponent *comp;
+ CalBackendFileObject *obj_data;
struct icaltimetype current;
cbfile = CAL_BACKEND_FILE (backend);
@@ -1402,7 +1439,7 @@ cal_backend_file_modify_object (CalBackendSync *backend, Cal *cal, const char *c
comp_uid = icalcomponent_get_uid (icalcomp);
/* Get the object from our cache */
- if (!(old_comp = lookup_component (cbfile, comp_uid))) {
+ if (!(obj_data = g_hash_table_lookup (priv->comp_uid_hash, comp_uid))) {
icalcomponent_free (icalcomp);
return GNOME_Evolution_Calendar_ObjectNotFound;
}
@@ -1415,13 +1452,17 @@ cal_backend_file_modify_object (CalBackendSync *backend, Cal *cal, const char *c
current = icaltime_from_timet (time (NULL), 0);
cal_component_set_last_modified (comp, &current);
- /* FIXME we need to handle mod types here */
-
- /* Remove the old version */
- remove_component (cbfile, old_comp);
+ /* handle mod_type */
+ if (cal_component_is_instance (comp) ||
+ mod != CALOBJ_MOD_ALL) {
+ /* FIXME */
+ } else {
+ /* Remove the old version */
+ remove_component (cbfile, obj_data->full_object);
- /* Add the object */
- add_component (cbfile, comp, TRUE);
+ /* Add the object */
+ add_component (cbfile, comp, TRUE);
+ }
mark_dirty (cbfile);
@@ -1433,12 +1474,16 @@ cal_backend_file_modify_object (CalBackendSync *backend, Cal *cal, const char *c
/* Remove_object handler for the file backend */
static CalBackendSyncStatus
-cal_backend_file_remove_object (CalBackendSync *backend, Cal *cal, const char *uid, const char *rid,
+cal_backend_file_remove_object (CalBackendSync *backend, Cal *cal,
+ const char *uid, const char *rid,
CalObjModType mod, char **object)
{
CalBackendFile *cbfile;
CalBackendFilePrivate *priv;
+ CalBackendFileObject *obj_data;
CalComponent *comp;
+ char *hash_rid;
+ GSList *categories;
cbfile = CAL_BACKEND_FILE (backend);
priv = cbfile->priv;
@@ -1446,14 +1491,53 @@ cal_backend_file_remove_object (CalBackendSync *backend, Cal *cal, const char *u
g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
- /* FIXME we need to handle mod types here */
-
- comp = lookup_component (cbfile, uid);
- if (!comp)
+ obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
+ if (!obj_data)
return GNOME_Evolution_Calendar_ObjectNotFound;
- *object = cal_component_get_as_string (comp);
- remove_component (cbfile, comp);
+ if (rid && *rid) {
+ if (g_hash_table_lookup_extended (obj_data->recurrences, rid,
+ &hash_rid, &comp)) {
+ /* remove the component from our data */
+ icalcomponent_remove_component (priv->icalcomp,
+ cal_component_get_icalcomponent (comp));
+ priv->comp = g_list_remove (priv->comp, comp);
+ g_hash_table_remove (obj_data->recurrences, rid);
+
+ /* update the set of categories */
+ cal_component_get_categories_list (comp, &categories);
+ cal_backend_unref_categories (CAL_BACKEND (cbfile), categories);
+ cal_component_free_categories_list (categories);
+
+ /* free memory */
+ g_free (hash_rid);
+ g_object_unref (comp);
+
+ mark_dirty (cbfile);
+
+ return GNOME_Evolution_Calendar_Success;
+ }
+ }
+
+ comp = obj_data->full_object;
+
+ if (mod != CALOBJ_MOD_ALL) {
+ *object = cal_component_get_as_string (comp);
+ remove_component (cbfile, comp);
+ } else {
+ /* remove the component from our data, temporarily */
+ icalcomponent_remove_component (priv->icalcomp,
+ cal_component_get_icalcomponent (comp));
+ priv->comp = g_list_remove (priv->comp, comp);
+
+ cal_util_remove_instances (cal_component_get_icalcomponent (comp),
+ icaltime_from_string (rid), mod);
+
+ /* add the modified object to the beginning of the list,
+ so that it's always before any detached instance we
+ might have */
+ priv->comp = g_list_prepend (priv->comp, comp);
+ }
mark_dirty (cbfile);