diff options
author | Victor Kareh <[email protected]> | 2025-07-28 11:28:15 -0400 |
---|---|---|
committer | Oz Tiram <[email protected]> | 2025-08-03 15:39:12 +0200 |
commit | bf07a6614e91646ecea248db003a576c3d85ad95 (patch) | |
tree | 9da40a2e058bbe6d4043902f9bb2fe0ff3cd7cbc /applets/clock/calendar-client.c | |
parent | 3818bb2847b16ecc21320f0208211da4bd883b30 (diff) | |
download | mate-panel-bf07a6614e91646ecea248db003a576c3d85ad95.tar.bz2 mate-panel-bf07a6614e91646ecea248db003a576c3d85ad95.tar.xz |
clock: Fix timezone handling
We remove a bunch of functions, since they are already handled by
libecal, and add correction to times based on the difference between the
event's own timezone and our current local timezone.
Diffstat (limited to 'applets/clock/calendar-client.c')
-rw-r--r-- | applets/clock/calendar-client.c | 468 |
1 files changed, 174 insertions, 294 deletions
diff --git a/applets/clock/calendar-client.c b/applets/clock/calendar-client.c index 0da62316..8bd9a2f3 100644 --- a/applets/clock/calendar-client.c +++ b/applets/clock/calendar-client.c @@ -33,6 +33,7 @@ #include <libecal/libecal.h> #include "calendar-sources.h" +#include "system-timezone.h" #endif #undef CALENDAR_ENABLE_DEBUG @@ -201,7 +202,28 @@ calendar_client_class_init (CalendarClientClass *klass) static gchar * calendar_client_config_get_timezone (GSettings *calendar_settings) { - return g_settings_get_string (calendar_settings, "timezone"); + gchar *timezone = NULL; + + if (calendar_settings == NULL) + return NULL; + + /* Check if we can list the keys to see if timezone exists */ + gchar **keys = g_settings_list_keys (calendar_settings); + gboolean has_timezone = FALSE; + + for (gint i = 0; keys[i] != NULL; i++) { + if (g_strcmp0 (keys[i], "timezone") == 0) { + has_timezone = TRUE; + break; + } + } + g_strfreev (keys); + + if (has_timezone) { + timezone = g_settings_get_string (calendar_settings, "timezone"); + } + + return timezone; } static ICalTimezone * @@ -896,82 +918,6 @@ calendar_appointment_init (CalendarAppointment *appointment, default_zone); } -static ICalTimezone * -resolve_timezone_id (const char *tzid, - gpointer user_data, - GCancellable *cancellable, - GError **error) -{ - ECalClient *client; - ICalTimezone *retval; - - client = E_CAL_CLIENT (user_data); - retval = i_cal_timezone_get_builtin_timezone_from_tzid (tzid); - - if (!retval) - e_cal_client_get_timezone_sync (client, tzid, &retval, NULL, NULL); - - return retval; -} - -static gboolean -calendar_appointment_collect_occurrence (ICalComponent *component, - ICalTime *occurrence_start, - ICalTime *occurrence_end, - gpointer user_data, - GCancellable *cancellable, - GError **error) -{ - time_t start; - time_t end; - CalendarOccurrence *occurrence; - GSList **collect_loc = user_data; - - start = i_cal_time_as_timet (occurrence_start); - end = i_cal_time_as_timet (occurrence_end); - - occurrence = g_new0 (CalendarOccurrence, 1); - occurrence->start_time = start; - occurrence->end_time = end; - - *collect_loc = g_slist_prepend (*collect_loc, occurrence); - - return TRUE; -} - -static void -calendar_appointment_generate_ocurrences (CalendarAppointment *appointment, - ICalComponent *component, - ECalClient *source, - time_t start, - time_t end, - ICalTimezone *default_zone) -{ - ICalTime *start_time; - ICalTime *end_time; - - g_assert (appointment->occurrences == NULL); - - start_time = i_cal_time_new_from_timet_with_zone (start, FALSE, NULL); - end_time = i_cal_time_new_from_timet_with_zone (end, FALSE, NULL); - - e_cal_recur_generate_instances_sync (component, - start_time, - end_time, - calendar_appointment_collect_occurrence, - &appointment->occurrences, - resolve_timezone_id, - source, - default_zone, - NULL, - NULL); - - g_object_unref (start_time); - g_object_unref (end_time); - - appointment->occurrences = g_slist_reverse (appointment->occurrences); -} - static inline gboolean calendar_task_equal (CalendarTask *a, CalendarTask *b) @@ -1177,25 +1123,6 @@ calendar_event_equal (CalendarEvent *a, return FALSE; } -static void -calendar_event_generate_ocurrences (CalendarEvent *event, - ICalComponent *component, - ECalClient *source, - time_t start, - time_t end, - ICalTimezone *default_zone) -{ - if (event->type != CALENDAR_EVENT_APPOINTMENT) - return; - - calendar_appointment_generate_ocurrences (CALENDAR_APPOINTMENT (event), - component, - source, - start, - end, - default_zone); -} - static inline void calendar_event_debug_dump (CalendarEvent *event) { @@ -1287,180 +1214,6 @@ goddamn_this_is_crack (CalendarClientSource *source, } static void -calendar_client_handle_query_completed (CalendarClientSource *source, - GError *error, - ECalClientView *view) -{ - CalendarClientQuery *query; - - query = goddamn_this_is_crack (source, view, NULL); - - if (error != NULL) - { - g_warning ("Calendar query failed: %s", error->message); - calendar_client_stop_query (source->client, source, query); - return; - } - - g_assert (source->query_in_progress != FALSE); - g_assert (query == &source->in_progress_query); - - calendar_client_query_finalize (&source->completed_query); - - source->completed_query = source->in_progress_query; - source->query_completed = TRUE; - - source->query_in_progress = FALSE; - source->in_progress_query.view = NULL; - source->in_progress_query.events = NULL; - - g_signal_emit (source->client, source->changed_signal_id, 0); -} - -static void -calendar_client_handle_query_result (CalendarClientSource *source, - GList *objects, - ECalClientView *view) -{ - CalendarClientQuery *query; - CalendarClient *client; - gboolean emit_signal; - gboolean events_changed; - GList *l; - time_t month_begin; - time_t month_end; - - client = source->client; - - query = goddamn_this_is_crack (source, view, &emit_signal); - - month_begin = make_time_for_day_begin (1, - client->priv->month, - client->priv->year); - - month_end = make_time_for_day_begin (1, - client->priv->month + 1, - client->priv->year); - - events_changed = FALSE; - for (l = objects; l; l = l->next) - { - CalendarEvent *event; - CalendarEvent *old_event; - ICalComponent *component = l->data; - char *uid; - - event = calendar_event_new (component, source, client->priv->zone); - if (!event) - continue; - - calendar_event_generate_ocurrences (event, - component, - source->source, - month_begin, - month_end, - client->priv->zone); - - uid = calendar_event_get_uid (event); - - old_event = g_hash_table_lookup (query->events, uid); - - if (!calendar_event_equal (event, old_event)) - { - calendar_event_debug_dump (event); - - g_hash_table_replace (query->events, uid, event); - - events_changed = TRUE; - } - else - { - calendar_event_free (event); - g_free (uid); - } - } - - if (emit_signal && events_changed) - { - g_signal_emit (source->client, source->changed_signal_id, 0); - } -} - -static gboolean -check_object_remove (gpointer key, - gpointer value, - gpointer data) -{ - char *uid; - size_t len; - - uid = data; - len = strlen (uid); - - if (len <= strlen (key) && strncmp (uid, key, len) == 0) - { - calendar_event_debug_dump (value); - - return TRUE; - } - - return FALSE; -} - -static void -calendar_client_handle_objects_removed (CalendarClientSource *source, - GList *ids, - ECalClientView *view) -{ - CalendarClientQuery *query; - gboolean emit_signal; - gboolean events_changed; - GList *l; - - query = goddamn_this_is_crack (source, view, &emit_signal); - - events_changed = FALSE; - for (l = ids; l; l = l->next) - { - CalendarEvent *event; - ECalComponentId *id; - const char *uid; - const char *rid; - char *key; - - id = l->data; - uid = e_cal_component_id_get_uid (id); - rid = e_cal_component_id_get_rid (id); - key = g_strdup_printf ("%s%s", uid, rid ? rid : ""); - - if (!rid || !*rid) - { - guint size = g_hash_table_size (query->events); - - g_hash_table_foreach_remove (query->events, check_object_remove, (gpointer) uid); - - if (size != g_hash_table_size (query->events)) - events_changed = TRUE; - } - else if ((event = g_hash_table_lookup (query->events, key))) - { - calendar_event_debug_dump (event); - - g_assert (g_hash_table_remove (query->events, key)); - - events_changed = TRUE; - } - - g_free (key); - } - - if (emit_signal && events_changed) - { - g_signal_emit (source->client, source->changed_signal_id, 0); - } -} - -static void calendar_client_query_finalize (CalendarClientQuery *query) { if (query->view) @@ -1495,49 +1248,176 @@ calendar_client_stop_query (CalendarClient *client, calendar_client_query_finalize (query); } +typedef struct { + CalendarClient *client; + CalendarClientSource *source; + time_t start_time; + time_t end_time; + gboolean events_changed; + ICalTimezone *system_timezone; +} InstanceGenerationData; + +static gboolean +calendar_client_instance_cb (ICalComponent *icomp, + ICalTime *instance_start, + ICalTime *instance_end, + gpointer user_data, + GCancellable *cancellable, + GError **error) +{ + InstanceGenerationData *data = user_data; + CalendarEvent *event; + CalendarEvent *old_event; + char *uid; + time_t start_time_t, end_time_t; + + /* Convert instance times from their timezone to local time */ + ICalTimezone *event_tz = i_cal_time_get_timezone(instance_start); + + ICalTime *local_start = i_cal_time_clone(instance_start); + ICalTime *local_end = i_cal_time_clone(instance_end); + + /* Convert to local timezone */ + if (event_tz && data->system_timezone && event_tz != data->system_timezone) { + i_cal_time_convert_timezone(local_start, event_tz, data->system_timezone); + i_cal_time_convert_timezone(local_end, event_tz, data->system_timezone); + } + + start_time_t = i_cal_time_as_timet (local_start); + end_time_t = i_cal_time_as_timet (local_end); + + g_object_unref(local_start); + g_object_unref(local_end); + + /* Create event from the component */ + event = calendar_event_new (icomp, data->source, data->client->priv->zone); + if (!event) + return TRUE; + + /* Override the times with the instance times (already converted to local timezone) */ + if (event->type == CALENDAR_EVENT_APPOINTMENT) { + CALENDAR_APPOINTMENT (event)->start_time = start_time_t; + CALENDAR_APPOINTMENT (event)->end_time = end_time_t; + + /* Create a single occurrence for this instance */ + CalendarOccurrence *occurrence = g_new0 (CalendarOccurrence, 1); + occurrence->start_time = start_time_t; + occurrence->end_time = end_time_t; + CALENDAR_APPOINTMENT (event)->occurrences = g_slist_prepend (NULL, occurrence); + } + + uid = calendar_event_get_uid (event); + old_event = g_hash_table_lookup (data->source->in_progress_query.events, uid); + + if (!calendar_event_equal (event, old_event)) { + calendar_event_debug_dump (event); + g_hash_table_replace (data->source->in_progress_query.events, uid, event); + data->events_changed = TRUE; + } else { + calendar_event_free (event); + g_free (uid); + } + + return TRUE; +} + static void calendar_client_start_query (CalendarClient *client, CalendarClientSource *source, const char *query) { - ECalClientView *view = NULL; + time_t month_begin, month_end; + GSList *objects = NULL; GError *error = NULL; + InstanceGenerationData instance_data; - if (!e_cal_client_get_view_sync (source->source, query, &view, NULL, &error)) - { - g_warning ("Error preparing the query: '%s': %s", - query, error->message); + /* Validate that client is properly initialized */ + if (client->priv->month == G_MAXUINT || client->priv->year == G_MAXUINT) { + return; + } - g_error_free (error); - return; - } + /* Calculate time range */ + month_begin = make_time_for_day_begin (1, client->priv->month, client->priv->year); - g_assert (view != NULL); + /* Handle year rollover when month is December (11) */ + if (client->priv->month == 11) { /* December */ + month_end = make_time_for_day_begin (1, 0, client->priv->year + 1); /* January next year */ + } else { + month_end = make_time_for_day_begin (1, client->priv->month + 1, client->priv->year); + } + + /* Validate time range */ + if (month_begin == -1 || month_end == -1) { + g_warning ("Invalid time range: month_begin=%ld, month_end=%ld", (long)month_begin, (long)month_end); + calendar_client_stop_query (client, source, &source->in_progress_query); + return; + } if (source->query_in_progress) calendar_client_stop_query (client, source, &source->in_progress_query); source->query_in_progress = TRUE; - source->in_progress_query.view = view; + source->in_progress_query.view = NULL; /* No view needed for instance generation */ source->in_progress_query.events = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) calendar_event_free); - g_signal_connect_swapped (view, "objects-added", - G_CALLBACK (calendar_client_handle_query_result), - source); - g_signal_connect_swapped (view, "objects-modified", - G_CALLBACK (calendar_client_handle_query_result), - source); - g_signal_connect_swapped (view, "objects-removed", - G_CALLBACK (calendar_client_handle_objects_removed), - source); - g_signal_connect_swapped (view, "complete", - G_CALLBACK (calendar_client_handle_query_completed), - source); - - e_cal_client_view_start (view, NULL); + /* Get all objects for the month using query */ + if (!e_cal_client_get_object_list_sync (source->source, query, &objects, NULL, &error)) { + g_warning ("Error getting calendar objects: %s", error->message); + g_error_free (error); + calendar_client_stop_query (client, source, &source->in_progress_query); + return; + } + + /* Get system timezone once for all instances */ + SystemTimezone *systz = system_timezone_new(); + const char *system_tz_name = system_timezone_get(systz); + ICalTimezone *system_timezone = i_cal_timezone_get_builtin_timezone(system_tz_name); + g_object_unref(systz); + + /* Set up instance generation data */ + instance_data.client = client; + instance_data.source = source; + instance_data.start_time = month_begin; + instance_data.end_time = month_end; + instance_data.events_changed = FALSE; + instance_data.system_timezone = system_timezone; + + /* Generate instances for each object with automatic timezone conversion */ + for (GSList *l = objects; l; l = l->next) { + ICalComponent *component = l->data; + + /* Some instances of recurring events may yield negative months - I think these are safe to skip */ + if (month_begin < 0 || month_end < 0) { + continue; + } + + e_cal_client_generate_instances_for_object_sync (source->source, + component, + month_begin, + month_end, + NULL, /* cancellable */ + calendar_client_instance_cb, + &instance_data); + } + + g_slist_free_full (objects, g_object_unref); + + /* Query is now completed */ + calendar_client_query_finalize (&source->completed_query); + source->completed_query = source->in_progress_query; + source->query_completed = TRUE; + + source->query_in_progress = FALSE; + source->in_progress_query.view = NULL; + source->in_progress_query.events = NULL; + + /* Emit signal to capture changed events */ + if (instance_data.events_changed) { + g_signal_emit (source->client, source->changed_signal_id, 0); + } } static void |