diff options
-rw-r--r-- | calendar/ChangeLog | 15 | ||||
-rw-r--r-- | calendar/cal-util/cal-recur.c | 1 | ||||
-rw-r--r-- | calendar/cal-util/cal-util.c | 174 | ||||
-rw-r--r-- | calendar/cal-util/cal-util.h | 7 | ||||
-rw-r--r-- | calendar/pcs/cal-backend-file.c | 116 |
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, ¤t); - /* 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); |