[gnome-calendar/fix-shifting-events: 1/2] manager: Fix bug where recurring events shift after being edited
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-calendar/fix-shifting-events: 1/2] manager: Fix bug where recurring events shift after being edited
- Date: Fri, 14 Oct 2022 21:12:59 +0000 (UTC)
commit d071c5e2b02859f469657a40391e2c625dd74612
Author: Ray Strode <rstrode redhat com>
Date: Tue Oct 11 15:33:36 2022 -0400
manager: Fix bug where recurring events shift after being edited
Right now if a user edits say event 3 in a set of 5 events, the
whole event set gets shifted over by 3. It's as if the middle
instance event is treated as the new first event.
This is because of a bit of the code that clears the recurrence
id of the event, to get at the "main event", and edit the whole
set. The code retains the start time from the instance, though,
rather than use the start time of the main event it's trying to
update.
This commit queries for the main event from the instance event,
and sets the new main event start time to match the old main
event start time, before doing an update.
src/core/gcal-event.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++
src/core/gcal-event.h | 5 ++
src/core/gcal-manager.c | 20 +++---
3 files changed, 185 insertions(+), 9 deletions(-)
---
diff --git a/src/core/gcal-event.c b/src/core/gcal-event.c
index 646a52f3..88087337 100644
--- a/src/core/gcal-event.c
+++ b/src/core/gcal-event.c
@@ -832,6 +832,175 @@ gcal_event_new_from_event (GcalEvent *self)
return gcal_event_new (self->calendar, component, NULL);
}
+static void
+gcal_event_get_time_offsets (GcalEvent *self,
+ GTimeSpan *start_offset,
+ GTimeSpan *end_offset)
+{
+ GDateTime *start_time = NULL;
+ GDateTime *end_time = NULL;
+ g_autoptr (GDateTime) start_day = NULL;
+ g_autoptr (GDateTime) end_day = NULL;
+
+ start_time = gcal_event_get_date_start (self);
+ end_time = gcal_event_get_date_end (self);
+
+ start_day = g_date_time_new (g_date_time_get_timezone (start_time),
+ g_date_time_get_year (start_time),
+ g_date_time_get_month (start_time),
+ g_date_time_get_day_of_month (start_time),
+ 0, 0, 0);
+ end_day = g_date_time_new (g_date_time_get_timezone (end_time),
+ g_date_time_get_year (end_time),
+ g_date_time_get_month (end_time),
+ g_date_time_get_day_of_month (end_time),
+ 0, 0, 0);
+
+ *start_offset = g_date_time_difference (start_time, start_day);
+ *end_offset = g_date_time_difference (end_time, end_day);
+}
+
+/**
+ * gcal_event_new_main_event_from_instance_event:
+ * @self: a #GcalEvent
+ *
+ * Creates a main @event from an instance event. This is useful
+ * for updating all recurring events at the same time to match
+ * the content of one instance in the set.
+ *
+ * Note, if the instance start time has been changed, the generated
+ * main event will be updated to reflect that new start time as
+ * well.
+ *
+ * Returns: (transfer full)(nullable): a #GcalEvent
+ */
+GcalEvent*
+gcal_event_new_main_event_from_instance_event (GcalEvent *self)
+{
+ GcalEvent *main_event;
+
+ g_autoptr (ECalComponent) new_main_ecal_component = NULL;
+ g_autoptr (ECalComponent) old_main_ecal_component = NULL;
+ ECalComponentDateTime *start_date = NULL;
+ ECalComponentDateTime *end_date = NULL;
+ g_autoptr (GError) error = NULL;
+ ECalClient *client;
+ const char *uid;
+
+ ICalComponent *old_main_ical_component;
+
+ g_return_val_if_fail (GCAL_IS_EVENT (self), NULL);
+
+ if (!e_cal_component_is_instance (self->component))
+ return NULL;
+
+ new_main_ecal_component = e_cal_component_clone (self->component);
+
+ /* The main event shares a uid with the instance event, but has
+ * a null recurrence id
+ */
+ uid = e_cal_component_get_uid (new_main_ecal_component);
+
+ client = gcal_calendar_get_client (self->calendar);
+
+ if (!e_cal_client_get_object_sync (client,
+ uid,
+ NULL,
+ &old_main_ical_component,
+ NULL,
+ &error))
+ {
+ g_warning ("Error finding main event from instance event: %s", error->message);
+ return NULL;
+ }
+
+ old_main_ecal_component = e_cal_component_new_from_icalcomponent (g_steal_pointer
(&old_main_ical_component));
+
+ if (!old_main_ecal_component)
+ return NULL;
+
+ /* Copy the start time and end time from the old main event to the new one
+ * and clear the recurrence id.
+ */
+ start_date = e_cal_component_get_dtstart (old_main_ecal_component);
+ e_cal_component_set_dtstart (new_main_ecal_component, start_date);
+ g_clear_pointer (&start_date, e_cal_component_datetime_free);
+
+ end_date = e_cal_component_get_dtend (old_main_ecal_component);
+
+ if (end_date != NULL)
+ {
+ e_cal_component_set_dtend (new_main_ecal_component, end_date);
+ g_clear_pointer (&end_date, e_cal_component_datetime_free);
+ }
+
+ e_cal_component_set_recurid (new_main_ecal_component, NULL);
+ e_cal_component_commit_sequence (new_main_ecal_component);
+
+ main_event = gcal_event_new (self->calendar, new_main_ecal_component, &error);
+
+ if (!main_event)
+ {
+ g_warning ("Error creating main event from modified instance event: %s", error->message);
+ return NULL;
+ }
+
+ return main_event;
+}
+
+/**
+ * gcal_event_apply_instance:
+ * @self: a #GcalEvent
+ *
+ * Applies the changes from an instance event to its associated
+ * main event.
+ */
+void
+gcal_event_apply_instance (GcalEvent *self,
+ GcalEvent *instance)
+{
+ g_return_if_fail (GCAL_IS_EVENT (self));
+
+ if (e_cal_component_is_instance (self->component))
+ return;
+
+ if (!e_cal_component_is_instance (instance->component))
+ return;
+
+ if (!instance->all_day)
+ {
+ GTimeSpan instance_event_start_offset = 0, main_event_start_offset = 0;
+ GTimeSpan instance_event_end_offset = 0, main_event_end_offset = 0;
+ g_autoptr (GDateTime) main_event_start_date = NULL;
+ g_autoptr (GDateTime) main_event_end_date = NULL;
+
+ gcal_event_set_all_day (self, FALSE);
+
+ gcal_event_get_time_offsets (instance,
+ &instance_event_start_offset,
+ &instance_event_end_offset);
+
+ gcal_event_get_time_offsets (self,
+ &main_event_start_offset,
+ &main_event_end_offset);
+
+ if (instance_event_start_offset != main_event_start_offset)
+ {
+ main_event_start_date = g_date_time_add (self->dt_start,
+ instance_event_start_offset - main_event_start_offset);
+ gcal_event_set_date_start (self, main_event_start_date);
+ }
+
+ if (instance_event_end_offset != main_event_end_offset)
+ {
+ main_event_end_date = g_date_time_add (self->dt_end,
+ instance_event_end_offset - main_event_end_offset);
+
+ gcal_event_set_date_end (self, main_event_end_date);
+ }
+ }
+}
+
/**
* gcal_event_get_all_day:
* @self: a #GcalEvent
diff --git a/src/core/gcal-event.h b/src/core/gcal-event.h
index a5859db9..c124bc55 100644
--- a/src/core/gcal-event.h
+++ b/src/core/gcal-event.h
@@ -55,6 +55,11 @@ GcalEvent* gcal_event_new (GcalCalendar
GcalEvent* gcal_event_new_from_event (GcalEvent *self);
+GcalEvent* gcal_event_new_main_event_from_instance_event (GcalEvent *self);
+
+void gcal_event_apply_instance (GcalEvent *self,
+ GcalEvent *instance);
+
gboolean gcal_event_get_all_day (GcalEvent *self);
void gcal_event_set_all_day (GcalEvent *self,
diff --git a/src/core/gcal-manager.c b/src/core/gcal-manager.c
index c123603a..cf9284d6 100644
--- a/src/core/gcal-manager.c
+++ b/src/core/gcal-manager.c
@@ -1043,24 +1043,26 @@ gcal_manager_update_event (GcalManager *self,
{
ECalComponent *component;
GcalCalendar *calendar;
+ g_autoptr (GcalEvent) main_event = NULL;
GCAL_ENTRY;
g_return_if_fail (GCAL_IS_MANAGER (self));
g_return_if_fail (GCAL_IS_EVENT (event));
+ if (mod == GCAL_RECURRENCE_MOD_ALL)
+ {
+ main_event = gcal_event_new_main_event_from_instance_event (event);
+
+ gcal_event_apply_instance (main_event, event);
+
+ if (main_event != NULL)
+ event = main_event;
+ }
+
calendar = gcal_event_get_calendar (event);
component = gcal_event_get_component (event);
- /*
- * HACK: In Evolution Calendar, a NULL 'rid' is usually associated
- * with an E_CAL_OBJ_MOD_ALL modtype. Here, we are manually setting
- * the rid to NULL when modifying a recurrent event with MOD_ALL
- * modtype.
- */
- if (mod == GCAL_RECURRENCE_MOD_ALL)
- e_cal_component_set_recurid (component, NULL);
-
e_cal_client_modify_object (gcal_calendar_get_client (calendar),
e_cal_component_get_icalcomponent (component),
(ECalObjModType) mod,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]