diff options
author | Milan Crha <mcrha@redhat.com> | 2018-05-11 12:50:54 +0200 |
---|---|---|
committer | Milan Crha <mcrha@redhat.com> | 2018-05-11 12:55:00 +0200 |
commit | 8ba0b06b72e96604db51be8c032afd33fcabe400 (patch) | |
tree | 2639235d3044811bc4d9177fc161bbc2ec42b8a3 /src/calendar | |
parent | 2be90d75c054de32697381c5a55fcf971da6c6f6 (diff) | |
download | evolution-data-server-8ba0b06b72e96604db51be8c032afd33fcabe400.tar.gz |
Move evolution-alarm-notify to evolution-data-server
Apart of the move itself, it also contains a UI change of the notification
dialog, the same as the changed way of dealing with the reminders:
a) reminders persist between sessions, until they are dismissed
b) snoozed reminders also persist between sessions.
Diffstat (limited to 'src/calendar')
-rw-r--r-- | src/calendar/libecal/e-cal-util.c | 72 | ||||
-rw-r--r-- | src/calendar/libecal/e-cal-util.h | 2 | ||||
-rw-r--r-- | src/calendar/libecal/e-reminder-watcher.c | 563 | ||||
-rw-r--r-- | src/calendar/libecal/e-reminder-watcher.h | 36 |
4 files changed, 614 insertions, 59 deletions
diff --git a/src/calendar/libecal/e-cal-util.c b/src/calendar/libecal/e-cal-util.c index 8c45dab49..c59082892 100644 --- a/src/calendar/libecal/e-cal-util.c +++ b/src/calendar/libecal/e-cal-util.c @@ -744,6 +744,78 @@ e_cal_util_priority_from_string (const gchar *string) return priority; } +/** + * e_cal_util_seconds_to_string: + * @seconds: actual time, in seconds + * + * Converts time, in seconds, into a string representation readable by humans + * and localized into the current locale. This can be used to convert event + * duration to string or similar use cases. + * + * Free the returned string with g_free(), when no longer needed. + * + * Returns: (transfer full): a newly allocated string with localized description + * of the given time in seconds. + * + * Since: 3.30 + **/ +gchar * +e_cal_util_seconds_to_string (gint64 seconds) +{ + gchar *times[6], *text; + gint ii; + + ii = 0; + if (seconds >= 7 * 24 * 3600) { + gint weeks; + + weeks = seconds / (7 * 24 * 3600); + seconds %= (7 * 24 * 3600); + + times[ii++] = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d week", "%d weeks", weeks), weeks); + } + + if (seconds >= 24 * 3600) { + gint days; + + days = seconds / (24 * 3600); + seconds %= (24 * 3600); + + times[ii++] = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d day", "%d days", days), days); + } + + if (seconds >= 3600) { + gint hours; + + hours = seconds / 3600; + seconds %= 3600; + + times[ii++] = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d hour", "%d hours", hours), hours); + } + + if (seconds >= 60) { + gint minutes; + + minutes = seconds / 60; + seconds %= 60; + + times[ii++] = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d minute", "%d minutes", minutes), minutes); + } + + if (seconds != 0) { + /* Translators: here, "second" is the time division (like "minute"), not the ordinal number (like "third") */ + times[ii++] = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%d second", "%d seconds", seconds), (gint) seconds); + } + + times[ii] = NULL; + text = g_strjoinv (" ", times); + while (ii > 0) { + g_free (times[--ii]); + } + + return text; +} + /* callback for icalcomponent_foreach_tzid */ typedef struct { icalcomponent *vcal_comp; diff --git a/src/calendar/libecal/e-cal-util.h b/src/calendar/libecal/e-cal-util.h index d43f1bf71..e96661433 100644 --- a/src/calendar/libecal/e-cal-util.h +++ b/src/calendar/libecal/e-cal-util.h @@ -81,6 +81,8 @@ gint e_cal_util_generate_alarms_for_list const gchar * e_cal_util_priority_to_string (gint priority); gint e_cal_util_priority_from_string (const gchar *string); +gchar * e_cal_util_seconds_to_string (gint64 seconds); + void e_cal_util_add_timezones_from_component (icalcomponent *vcal_comp, icalcomponent *icalcomp); diff --git a/src/calendar/libecal/e-reminder-watcher.c b/src/calendar/libecal/e-reminder-watcher.c index 56c70c516..c8eeed7e3 100644 --- a/src/calendar/libecal/e-reminder-watcher.c +++ b/src/calendar/libecal/e-reminder-watcher.c @@ -32,11 +32,14 @@ #include "evolution-data-server-config.h" #include <string.h> +#include <glib/gi18n-lib.h> #include "libedataserver/libedataserver.h" #include "e-cal-client.h" +#include "e-cal-system-timezone.h" #include "e-cal-time-util.h" +#include "e-cal-util.h" #include "e-reminder-watcher.h" @@ -53,6 +56,7 @@ struct _EReminderWatcherPrivate { ESourceRegistryWatcher *registry_watcher; GCancellable *cancellable; GSettings *settings; + gboolean timers_enabled; gulong past_changed_handler_id; gulong snoozed_changed_handler_id; guint expected_past_changes; @@ -77,12 +81,13 @@ struct _EReminderWatcherPrivate { enum { PROP_0, PROP_REGISTRY, - PROP_DEFAULT_ZONE + PROP_DEFAULT_ZONE, + PROP_TIMERS_ENABLED }; enum { + FORMAT_TIME, TRIGGERED, - REMOVED, CHANGED, LAST_SIGNAL }; @@ -1028,7 +1033,7 @@ e_reminder_watcher_calc_next_midnight (EReminderWatcher *watcher) e_reminder_watcher_debug_print ("Required correction of the day end, now at %s\n", e_reminder_watcher_timet_as_string ((gint64) midnight)); } - if (watcher->priv->next_midnight != midnight) { + if (watcher->priv->next_midnight != midnight && watcher->priv->timers_enabled) { GSList *link; e_reminder_watcher_debug_print ("Next midnight at %s\n", e_reminder_watcher_timet_as_string ((gint64) midnight)); @@ -1084,6 +1089,26 @@ e_reminder_watcher_schedule_timer_impl (EReminderWatcher *watcher, g_rec_mutex_unlock (&watcher->priv->lock); } +static void +e_reminder_watcher_format_time_impl (EReminderWatcher *watcher, + const EReminderData *rd, + struct icaltimetype *itt, + gchar **inout_buffer, + gint buffer_size) +{ + struct tm tm; + + g_return_if_fail (E_IS_REMINDER_WATCHER (watcher)); + g_return_if_fail (rd != NULL); + g_return_if_fail (itt != NULL); + g_return_if_fail (inout_buffer != NULL); + g_return_if_fail (*inout_buffer != NULL); + g_return_if_fail (buffer_size > 0); + + tm = icaltimetype_to_tm (itt); + e_time_format_date_and_time (&tm, FALSE, FALSE, FALSE, *inout_buffer, buffer_size); +} + static GSList * /* EReminderData * */ e_reminder_watcher_reminders_from_key (EReminderWatcher *watcher, const gchar *key) @@ -1191,6 +1216,7 @@ typedef struct _EmitSignalData { EReminderWatcher *watcher; guint signal_id; GSList *reminders; /* EReminderData * */ + gboolean is_snoozed; /* only for the triggered signal */ } EmitSignalData; static void @@ -1213,7 +1239,10 @@ e_reminder_watcher_emit_signal_idle_cb (gpointer user_data) g_return_val_if_fail (esd != NULL, FALSE); g_return_val_if_fail (E_IS_REMINDER_WATCHER (esd->watcher), FALSE); - g_signal_emit (esd->watcher, esd->signal_id, 0, esd->reminders, NULL); + if (esd->signal_id == signals[TRIGGERED]) + g_signal_emit (esd->watcher, esd->signal_id, 0, esd->reminders, esd->is_snoozed, NULL); + else + g_signal_emit (esd->watcher, esd->signal_id, 0, esd->reminders, NULL); return FALSE; } @@ -1221,7 +1250,8 @@ e_reminder_watcher_emit_signal_idle_cb (gpointer user_data) static void e_reminder_watcher_emit_signal_idle_multiple (EReminderWatcher *watcher, guint signal_id, - const GSList *reminders) /* EReminderData * */ + const GSList *reminders, /* EReminderData * */ + gboolean is_snoozed) { EmitSignalData *esd; @@ -1229,6 +1259,7 @@ e_reminder_watcher_emit_signal_idle_multiple (EReminderWatcher *watcher, esd->watcher = g_object_ref (watcher); esd->signal_id = signal_id; esd->reminders = g_slist_copy_deep ((GSList *) reminders, (GCopyFunc) e_reminder_data_copy, NULL); + esd->is_snoozed = is_snoozed; g_idle_add_full (G_PRIORITY_HIGH_IDLE, e_reminder_watcher_emit_signal_idle_cb, esd, emit_signal_data_free); } @@ -1243,7 +1274,7 @@ e_reminder_watcher_emit_signal_idle (EReminderWatcher *watcher, if (rd) reminders = g_slist_prepend (NULL, e_reminder_data_copy (rd)); - e_reminder_watcher_emit_signal_idle_multiple (watcher, signal_id, reminders); + e_reminder_watcher_emit_signal_idle_multiple (watcher, signal_id, reminders, FALSE); g_slist_free_full (reminders, e_reminder_data_free); } @@ -1299,8 +1330,6 @@ e_reminder_watcher_remove_from_past (EReminderWatcher *watcher, e_reminder_watcher_save_past (watcher, reminders); - e_reminder_watcher_emit_signal_idle (watcher, signals[REMOVED], found); - e_reminder_watcher_debug_print ("Removed reminder from past for '%s' from %s at %s\n", icalcomponent_get_summary (e_cal_component_get_icalcomponent (found->component)), found->source_uid, @@ -1351,7 +1380,8 @@ e_reminder_watcher_remove_from_snoozed (EReminderWatcher *watcher, static ECalClient * e_reminder_watcher_ref_client (EReminderWatcher *watcher, - const gchar *source_uid) + const gchar *source_uid, + GCancellable *cancellable) { ECalClient *client = NULL; GSList *link; @@ -1372,7 +1402,47 @@ e_reminder_watcher_ref_client (EReminderWatcher *watcher, } } - g_rec_mutex_unlock (&watcher->priv->lock); + if (!client && cancellable) { + ESourceRegistry *registry; + ESource *source; + + registry = g_object_ref (watcher->priv->registry); + + g_rec_mutex_unlock (&watcher->priv->lock); + + source = e_source_registry_ref_source (registry, source_uid); + if (source) { + ECalClientSourceType source_type = E_CAL_CLIENT_SOURCE_TYPE_LAST; + + if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) + source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS; + else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) + source_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS; + else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) + source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS; + + if (source_type != E_CAL_CLIENT_SOURCE_TYPE_LAST) { + EClient *tmp_client; + GError *local_error = NULL; + + tmp_client = e_cal_client_connect_sync (source, source_type, 30, cancellable, &local_error); + if (tmp_client) + client = E_CAL_CLIENT (tmp_client); + + if (!client) { + e_reminder_watcher_debug_print ("Failed to connect client '%s': %s\n", source_uid, local_error ? local_error->message : "Unknown error"); + g_clear_error (&local_error); + } else if (tmp_client) { + client = E_CAL_CLIENT (tmp_client); + } + } + } + + g_clear_object (&source); + g_clear_object (®istry); + } else { + g_rec_mutex_unlock (&watcher->priv->lock); + } return client; } @@ -1437,6 +1507,11 @@ e_reminder_watcher_maybe_schedule_next_trigger (EReminderWatcher *watcher, { g_rec_mutex_lock (&watcher->priv->lock); + if (!watcher->priv->timers_enabled) { + g_rec_mutex_unlock (&watcher->priv->lock); + return; + } + e_reminder_watcher_calc_next_midnight (watcher); if (watcher->priv->snoozed && watcher->priv->snoozed->data) { @@ -1629,7 +1704,9 @@ e_reminder_watcher_client_connect_cb (GObject *source_object, e_reminder_watcher_debug_print ("Connected client: %s (%s)\n", e_source_get_uid (source), e_source_get_display_name (source)); watcher->priv->clients = g_slist_prepend (watcher->priv->clients, cd); - client_data_start_view (cd, watcher->priv->next_midnight, watcher->priv->cancellable); + + if (watcher->priv->timers_enabled) + client_data_start_view (cd, watcher->priv->next_midnight, watcher->priv->cancellable); } g_rec_mutex_unlock (&watcher->priv->lock); @@ -1657,7 +1734,8 @@ e_reminder_watcher_source_appeared_cb (EReminderWatcher *watcher, return; } - e_cal_client_connect (source, source_type, 30, watcher->priv->cancellable, e_reminder_watcher_client_connect_cb, watcher); + if (watcher->priv->timers_enabled) + e_cal_client_connect (source, source_type, 30, watcher->priv->cancellable, e_reminder_watcher_client_connect_cb, watcher); g_rec_mutex_unlock (&watcher->priv->lock); } @@ -1766,6 +1844,12 @@ e_reminder_watcher_set_property (GObject *object, E_REMINDER_WATCHER (object), g_value_get_boxed (value)); return; + + case PROP_TIMERS_ENABLED: + e_reminder_watcher_set_timers_enabled ( + E_REMINDER_WATCHER (object), + g_value_get_boolean (value)); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -1791,6 +1875,13 @@ e_reminder_watcher_get_property (GObject *object, e_reminder_watcher_dup_default_zone ( E_REMINDER_WATCHER (object))); return; + + case PROP_TIMERS_ENABLED: + g_value_set_boolean ( + value, + e_reminder_watcher_get_timers_enabled ( + E_REMINDER_WATCHER (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -1894,6 +1985,7 @@ e_reminder_watcher_class_init (EReminderWatcherClass *klass) object_class->finalize = e_reminder_watcher_finalize; klass->schedule_timer = e_reminder_watcher_schedule_timer_impl; + klass->format_time = e_reminder_watcher_format_time_impl; /** * EReminderWatcher:registry: @@ -1934,47 +2026,76 @@ e_reminder_watcher_class_init (EReminderWatcherClass *klass) G_PARAM_STATIC_STRINGS)); /** - * EReminderWatcher::triggered: + * EReminderWatcher:timers-enabled: + * + * Whether timers are enabled for the #EReminderWatcher. See + * e_reminder_watcher_set_timers_enabled() for more information + * what it means. + * + * Default: %TRUE + * + * Since: 3.30 + **/ + g_object_class_install_property ( + object_class, + PROP_TIMERS_ENABLED, + g_param_spec_boolean ( + "timers-enabled", + "Timers Enabled", + "Whether can schedule timers", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * EReminderWatcher::format-time: * @watcher: an #EReminderWatcher - * @reminders: (element-type EReminderData): a #GSList of #EReminderData + * @rd: an #EReminderData + * @itt: a pointer to struct icaltimetype + * @inout_buffer: (caller allocates) (inout): a pointer to a buffer to fill with formatted @itt + * @buffer_size: size of inout_buffer * - * Signal is emitted when any reminder is either overdue or triggered. + * Formats time @itt to a string and writes it to @inout_buffer, which can hold + * up to @buffer_size bytes. The first character of @inout_buffer is the nul-byte + * when nothing wrote to it yet. * * Since: 3.30 **/ - signals[TRIGGERED] = g_signal_new ( - "triggered", + signals[FORMAT_TIME] = g_signal_new ( + "format-time", G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EReminderWatcherClass, triggered), + G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EReminderWatcherClass, format_time), NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 1, - G_TYPE_POINTER); + G_TYPE_NONE, 4, + G_TYPE_POINTER, + G_TYPE_POINTER, + G_TYPE_POINTER, + G_TYPE_INT); /** - * EReminderWatcher::removed: + * EReminderWatcher::triggered: * @watcher: an #EReminderWatcher * @reminders: (element-type EReminderData): a #GSList of #EReminderData + * @snoozed: %TRUE, when the @reminders had been snoozed, %FALSE otherwise * - * Signal is emitted when any reminder is removed from the past reminders. - * It's also followed by an EReminderWatcher::changed signal. This is used - * when it's known which reminders had been removed from the list of past - * reminders. It's not used when there's a notification from GSettings. + * Signal is emitted when any reminder is either overdue or triggered. * * Since: 3.30 **/ - signals[REMOVED] = g_signal_new ( - "removed", + signals[TRIGGERED] = g_signal_new ( + "triggered", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EReminderWatcherClass, removed), + G_STRUCT_OFFSET (EReminderWatcherClass, triggered), NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 1, - G_TYPE_POINTER); + G_TYPE_NONE, 2, + G_TYPE_POINTER, + G_TYPE_BOOLEAN); /** * EReminderWatcher::changed: @@ -2001,11 +2122,24 @@ e_reminder_watcher_class_init (EReminderWatcherClass *klass) static void e_reminder_watcher_init (EReminderWatcher *watcher) { + icaltimezone *zone = NULL; + gchar *location; + + location = e_cal_system_timezone_get_location (); + if (location) { + zone = icaltimezone_get_builtin_timezone (location); + g_free (location); + } + + if (!zone) + zone = icaltimezone_get_utc_timezone (); + watcher->priv = G_TYPE_INSTANCE_GET_PRIVATE (watcher, E_TYPE_REMINDER_WATCHER, EReminderWatcherPrivate); watcher->priv->cancellable = g_cancellable_new (); watcher->priv->settings = g_settings_new ("org.gnome.evolution-data-server.calendar"); watcher->priv->scheduled = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, e_reminder_watcher_free_rd_slist); - watcher->priv->default_zone = icaltimezone_get_utc_timezone (); + watcher->priv->default_zone = icaltimezone_copy (zone); + watcher->priv->timers_enabled = TRUE; g_rec_mutex_init (&watcher->priv->lock); } @@ -2048,6 +2182,26 @@ e_reminder_watcher_get_registry (EReminderWatcher *watcher) } /** + * e_reminders_widget_ref_opened_client: + * @watcher: an #EReminderWatcher + * @source_uid: an #ESource UID of the calendar to return + * + * Returns: (nullable) (transfer full): a referenced #ECalClient for the @source_uid, + * if any such is opened; %NULL otherwise. + * + * Since: 3.30 + **/ +ECalClient * +e_reminder_watcher_ref_opened_client (EReminderWatcher *watcher, + const gchar *source_uid) +{ + g_return_val_if_fail (E_IS_REMINDER_WATCHER (watcher), NULL); + g_return_val_if_fail (source_uid != NULL, NULL); + + return e_reminder_watcher_ref_client (watcher, source_uid, NULL); +} + +/** * e_reminder_watcher_set_default_zone: * @watcher: an #EReminderWatcher * @zone: (nullable): an icaltimezone or #EReminderWatcherZone structure @@ -2112,6 +2266,279 @@ e_reminder_watcher_dup_default_zone (EReminderWatcher *watcher) return zone; } +/** + * e_reminder_watcher_get_timers_enabled: + * @watcher: an #EReminderWatcher + * + * Returns: whether timers are enabled for the @watcher. See + * e_reminder_watcher_set_timers_enabled() for more information + * what it means. + * + * Since: 3.30 + **/ +gboolean +e_reminder_watcher_get_timers_enabled (EReminderWatcher *watcher) +{ + gboolean enabled; + + g_return_val_if_fail (E_IS_REMINDER_WATCHER (watcher), FALSE); + + g_rec_mutex_lock (&watcher->priv->lock); + + enabled = watcher->priv->timers_enabled; + + g_rec_mutex_unlock (&watcher->priv->lock); + + return enabled; +} + +/** + * e_reminder_watcher_set_timers_enabled: + * @watcher: an #EReminderWatcher + * @enable: a value to set + * + * The @watcher can be used both for scheduling the timers for the reminders + * and respond to them through the "triggered" signal, or only to listen for + * changes on the past reminders. The default is to have timers enabled, thus + * to response to scheduled reminders. Disabling the timers also means there + * will be less resources needed by the @watcher. + * + * Since: 3.30 + **/ +void +e_reminder_watcher_set_timers_enabled (EReminderWatcher *watcher, + gboolean enabled) +{ + g_return_if_fail (E_IS_REMINDER_WATCHER (watcher)); + + g_rec_mutex_lock (&watcher->priv->lock); + + if (!enabled == !watcher->priv->timers_enabled) { + g_rec_mutex_unlock (&watcher->priv->lock); + return; + } + + watcher->priv->timers_enabled = enabled; + + if (watcher->priv->timers_enabled && + !watcher->priv->construct_idle_id) { + e_source_registry_watcher_reclaim (watcher->priv->registry_watcher); + e_reminder_watcher_maybe_schedule_next_trigger (watcher, 0); + } + + g_rec_mutex_unlock (&watcher->priv->lock); + + g_object_notify (G_OBJECT (watcher), "timers-enabled"); +} + +static gchar * +e_reminder_watcher_get_alarm_summary (EReminderWatcher *watcher, + const EReminderData *rd) +{ + ECalComponentText summary_text, alarm_text; + ECalComponentAlarm *alarm; + gchar *alarm_summary; + + g_return_val_if_fail (watcher != NULL, NULL); + g_return_val_if_fail (rd != NULL, NULL); + + summary_text.value = NULL; + alarm_text.value = NULL; + + e_cal_component_get_summary (rd->component, &summary_text); + + alarm = e_cal_component_get_alarm (rd->component, rd->instance.auid); + if (alarm) { + ECalClient *client; + + client = e_reminder_watcher_ref_opened_client (watcher, rd->source_uid); + + if (client && e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_ALARM_DESCRIPTION)) { + e_cal_component_alarm_get_description (alarm, &alarm_text); + if (!alarm_text.value || !*alarm_text.value) + alarm_text.value = NULL; + } + + g_clear_object (&client); + } + + if (alarm_text.value && summary_text.value && + e_util_utf8_strcasecmp (alarm_text.value, summary_text.value) == 0) + alarm_text.value = NULL; + + if (summary_text.value && *summary_text.value && + alarm_text.value && *alarm_text.value) + alarm_summary = g_strconcat (summary_text.value, "\n", alarm_text.value, NULL); + else if (summary_text.value && *summary_text.value) + alarm_summary = g_strdup (summary_text.value); + else if (alarm_text.value && *alarm_text.value) + alarm_summary = g_strdup (alarm_text.value); + else + alarm_summary = NULL; + + if (alarm) + e_cal_component_alarm_free (alarm); + + return alarm_summary; +} + +/** + * e_reminder_watcher_describe_data: + * @watcher: an #EReminderWatcher + * @rd: an #EReminderData + * @flags: bit-or of #EReminderWatcherDescribeFlags + * + * Returns a new string with a text description of the @rd. The text format + * can be influenced with @flags. + * + * Free the returned string with g_free(), when no longer needed. + * + * Returns: (transfer full): a new string with a text description of the @rd. + * + * Since: 3.30 + **/ +gchar * +e_reminder_watcher_describe_data (EReminderWatcher *watcher, + const EReminderData *rd, + guint32 flags) +{ + icalcomponent *icalcomp; + gchar *description = NULL; + gboolean use_markup; + + g_return_val_if_fail (E_IS_REMINDER_WATCHER (watcher), NULL); + g_return_val_if_fail (rd != NULL, NULL); + + use_markup = (flags & E_REMINDER_WATCHER_DESCRIBE_FLAG_MARKUP) != 0; + + icalcomp = e_cal_component_get_icalcomponent (rd->component); + if (icalcomp) { + gchar *summary; + const gchar *location; + gchar *timediff = NULL, *tmp; + gchar timestr[255]; + GString *markup; + + timestr[0] = 0; + markup = g_string_sized_new (256); + summary = e_reminder_watcher_get_alarm_summary (watcher, rd); + location = icalcomponent_get_location (icalcomp); + + if (rd->instance.occur_start > 0) { + gchar *timestrptr = timestr; + icaltimezone *zone; + struct icaltimetype itt; + gboolean is_date = FALSE; + + if (rd->instance.occur_end > rd->instance.occur_start) { + timediff = e_cal_util_seconds_to_string (rd->instance.occur_end - rd->instance.occur_start); + } + + zone = e_reminder_watcher_dup_default_zone (watcher); + if (zone && (!icaltimezone_get_location (zone) || g_strcmp0 (icaltimezone_get_location (zone), "UTC") == 0)) { + icaltimezone_free (zone, 1); + zone = NULL; + } + + itt = icalcomponent_get_dtstart (icalcomp); + if (icaltime_is_valid_time (itt) && !icaltime_is_null_time (itt)) + is_date = itt.is_date; + + itt = icaltime_from_timet_with_zone (rd->instance.occur_start, is_date, zone); + + g_signal_emit (watcher, signals[FORMAT_TIME], 0, rd, &itt, ×trptr, 254, NULL); + + if (!*timestr) + e_reminder_watcher_format_time_impl (watcher, rd, &itt, ×trptr, 254); + + if (zone) + icaltimezone_free (zone, 1); + } + + if (!summary || !*summary) { + g_free (summary); + summary = g_strdup (_( "No Summary")); + } + + if (use_markup) { + tmp = g_markup_printf_escaped ("<b>%s</b>", summary); + g_string_append (markup, tmp); + g_free (tmp); + } else { + g_string_append (markup, summary); + } + g_string_append_c (markup, '\n'); + + if (*timestr) { + /* Translators: The first %s is replaced with the time string, + the second %s with a duration, and the third %s with an event location, + making it something like: "24.1.2018 10:30 (30 minutes) Meeting room A1" */ + #define FMT_TIME_TIME_LOCATION C_("overdue", "%s (%s) %s") + + /* Translators: The first %s is replaced with the time string, + the second %s with a duration, making is something like: + "24.1.2018 10:30 (30 minutes)" */ + #define FMT_TIME_TIME C_("overdue", "%s (%s)") + + /* Translators: The first %s is replaced with the time string, + the second %s with an event location, making it something like: + "24.1.2018 10:30 Meeting room A1" */ + #define FMT_TIME_LOCATION C_("overdue", "%s %s") + + if (timediff && *timediff) { + if (location && *location) { + if (use_markup) + tmp = g_markup_printf_escaped (FMT_TIME_TIME_LOCATION, timestr, timediff, location); + else + tmp = g_strdup_printf (FMT_TIME_TIME_LOCATION, timestr, timediff, location); + } else { + if (use_markup) + tmp = g_markup_printf_escaped (FMT_TIME_TIME, timestr, timediff); + else + tmp = g_strdup_printf (FMT_TIME_TIME, timestr, timediff); + } + } else if (location && *location) { + if (use_markup) + tmp = g_markup_printf_escaped (FMT_TIME_LOCATION, timestr, location); + else + tmp = g_strdup_printf (FMT_TIME_LOCATION, timestr, location); + } else { + if (use_markup) + tmp = g_markup_escape_text (timestr, -1); + else + tmp = g_strdup (timestr); + } + + if (use_markup) + g_string_append (markup, "<small>"); + g_string_append (markup, tmp); + if (use_markup) + g_string_append (markup, "</small>"); + + g_free (tmp); + } else if (location && *location) { + if (use_markup) { + tmp = g_markup_printf_escaped ("%s", location); + + g_string_append (markup, "<small>"); + g_string_append (markup, tmp); + g_string_append (markup, "</small>"); + + g_free (tmp); + } else { + g_string_append (markup, location); + } + } + + description = g_string_free (markup, FALSE); + + g_free (timediff); + g_free (summary); + } + + return description; +} + typedef struct _ForeachTriggerData { gint64 current_time; GSList *triggered; /* EReminderData * */ @@ -2183,7 +2610,7 @@ void e_reminder_watcher_timer_elapsed (EReminderWatcher *watcher) { ForeachTriggerData ftd; - GSList *snoozed, *link; + GSList *snoozed, *link, *triggered_snoozed = NULL; gboolean changed = FALSE; g_return_if_fail (E_IS_REMINDER_WATCHER (watcher)); @@ -2230,13 +2657,13 @@ e_reminder_watcher_timer_elapsed (EReminderWatcher *watcher) changed = e_reminder_watcher_remove_from_snoozed (watcher, rd, FALSE) || changed; - ftd.triggered = g_slist_prepend (ftd.triggered, rd); + triggered_snoozed = g_slist_prepend (triggered_snoozed, rd); } } g_slist_free_full (snoozed, e_reminder_data_free); - if (ftd.triggered) { + if (ftd.triggered || triggered_snoozed) { GHashTable *last_notifies; GHashTableIter iter; GSList *past; @@ -2266,6 +2693,26 @@ e_reminder_watcher_timer_elapsed (EReminderWatcher *watcher) } } + for (link = triggered_snoozed; link; link = g_slist_next (link)) { + EReminderData *rd = e_reminder_data_copy (link->data); + + if (rd) { + if (e_reminder_watcher_add (&past, rd, TRUE, FALSE)) { + time_t *ptrigger; + + ptrigger = g_hash_table_lookup (last_notifies, rd->source_uid); + if (ptrigger) { + if (*ptrigger < rd->instance.trigger) + *ptrigger = rd->instance.trigger; + } else { + ptrigger = g_new0 (time_t, 1); + *ptrigger = rd->instance.trigger; + g_hash_table_insert (last_notifies, rd->source_uid, ptrigger); + } + } + } + } + e_reminder_watcher_save_past (watcher, past); g_hash_table_iter_init (&iter, last_notifies); @@ -2274,7 +2721,7 @@ e_reminder_watcher_timer_elapsed (EReminderWatcher *watcher) const time_t *ptrigger = value; if (source_uid && ptrigger) { - ECalClient *client = e_reminder_watcher_ref_client (watcher, source_uid); + ECalClient *client = e_reminder_watcher_ref_client (watcher, source_uid, NULL); if (client) { client_set_last_notification_time (client, *ptrigger); @@ -2291,10 +2738,16 @@ e_reminder_watcher_timer_elapsed (EReminderWatcher *watcher) if (changed) e_reminder_watcher_save_snoozed (watcher); - if (ftd.triggered) { - e_reminder_watcher_emit_signal_idle_multiple (watcher, signals[TRIGGERED], ftd.triggered); + if (ftd.triggered || triggered_snoozed) { + if (triggered_snoozed) + e_reminder_watcher_emit_signal_idle_multiple (watcher, signals[TRIGGERED], triggered_snoozed, TRUE); + + if (ftd.triggered) + e_reminder_watcher_emit_signal_idle_multiple (watcher, signals[TRIGGERED], ftd.triggered, FALSE); + e_reminder_watcher_emit_signal_idle (watcher, signals[CHANGED], NULL); + g_slist_free_full (triggered_snoozed, e_reminder_data_free); g_slist_free_full (ftd.triggered, e_reminder_data_free); } @@ -2375,9 +2828,8 @@ e_reminder_watcher_dup_snoozed (EReminderWatcher *watcher) * * Snoozes @rd until @until, which is an absolute time when the @rd * should be retriggered. This moves the @rd from the list of past - * reminders into the list of snoozed reminders and invokes the "removed" - * signal when the @rd was in the past reminders. It also invokes - * the "changed" signal. + * reminders into the list of snoozed reminders and invokes the "changed" + * signal. * * Since: 3.30 **/ @@ -2522,9 +2974,9 @@ e_reminder_watcher_dismiss_one_sync (ECalClient *client, success = e_cal_client_discard_alarm_sync (client, id->uid, id->rid, rd->instance.auid, cancellable, &local_error); - e_reminder_watcher_debug_print ("Discard alarm for '%s' from %s %s%s%s%s\n", + e_reminder_watcher_debug_print ("Discard alarm for '%s' from %s (uid:%s rid:%s auid:%s) %s%s%s%s\n", icalcomponent_get_summary (e_cal_component_get_icalcomponent (rd->component)), - rd->source_uid, + rd->source_uid, id->uid, id->rid ? id->rid : "null", rd->instance.auid, success ? "succeeded" : "failed", (!success || local_error) ? " (" : "", local_error ? local_error->message : success ? "" : "Unknown error", @@ -2583,7 +3035,7 @@ e_reminder_watcher_dismiss_sync (EReminderWatcher *watcher, changed = e_reminder_watcher_remove_from_snoozed (watcher, rd_copy, TRUE) || changed; if (changed) - client = e_reminder_watcher_ref_client (watcher, rd_copy->source_uid); + client = e_reminder_watcher_ref_client (watcher, rd_copy->source_uid, cancellable ? cancellable : watcher->priv->cancellable); e_reminder_watcher_maybe_schedule_next_trigger (watcher, 0); @@ -2699,36 +3151,36 @@ e_reminder_watcher_dismiss_all_sync (EReminderWatcher *watcher, GCancellable *cancellable, GError **error) { - GSList *reminders, *link, *dismissed = NULL; + GHashTable *clients; /* gchar *source_uid ~> ECalClient * */ + GSList *reminders, *link; gboolean success = TRUE, changed = FALSE; g_return_val_if_fail (E_IS_REMINDER_WATCHER (watcher), FALSE); g_rec_mutex_lock (&watcher->priv->lock); + clients = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); reminders = e_reminder_watcher_dup_past (watcher); for (link = reminders; link; link = g_slist_next (link)) { EReminderData *rd = link->data; ECalClient *client; - client = e_reminder_watcher_ref_client (watcher, rd->source_uid); + client = g_hash_table_lookup (clients, rd->source_uid); + if (!client) { + client = e_reminder_watcher_ref_client (watcher, rd->source_uid, cancellable ? cancellable : watcher->priv->cancellable); + if (client) { + g_hash_table_insert (clients, g_strdup (rd->source_uid), client); + } + } + if (client) { success = e_reminder_watcher_dismiss_one_sync (client, rd, cancellable, error); - g_object_unref (client); /* To keep the failed discard in the saved list. */ if (!success) break; } - - dismissed = g_slist_prepend (dismissed, rd); - link->data = NULL; - } - - if (dismissed) { - e_reminder_watcher_emit_signal_idle_multiple (watcher, signals[REMOVED], dismissed); - g_slist_free_full (dismissed, e_reminder_data_free); } if (link != reminders && reminders) { @@ -2737,6 +3189,7 @@ e_reminder_watcher_dismiss_all_sync (EReminderWatcher *watcher, } g_slist_free_full (reminders, e_reminder_data_free); + g_hash_table_destroy (clients); g_rec_mutex_unlock (&watcher->priv->lock); diff --git a/src/calendar/libecal/e-reminder-watcher.h b/src/calendar/libecal/e-reminder-watcher.h index 96a830ee0..3cb6a33e5 100644 --- a/src/calendar/libecal/e-reminder-watcher.h +++ b/src/calendar/libecal/e-reminder-watcher.h @@ -23,6 +23,7 @@ #define E_REMINDER_WATCHER_H #include <libedataserver/libedataserver.h> +#include <libecal/e-cal-client.h> /* Standard GObject macros */ #define E_TYPE_REMINDER_WATCHER \ @@ -71,7 +72,7 @@ void e_reminder_data_free (gpointer rd); /* EReminderData * */ /** * EReminderWatcherZone: * - * A libical's icaltimezone encapsulated as a GByxed type. + * A libical's icaltimezone encapsulated as a GBoxed type. * It can be retyped into icaltimezone directly. * * Since: 3.30 @@ -88,6 +89,21 @@ typedef struct _EReminderWatcherClass EReminderWatcherClass; typedef struct _EReminderWatcherPrivate EReminderWatcherPrivate; /** + * EReminderWatcherDescribeFlags: + * @E_REMINDER_WATCHER_DESCRIBE_FLAG_NONE: None flags + * @E_REMINDER_WATCHER_DESCRIBE_FLAG_MARKUP: Returned description will contain + * also markup. Without it it'll be a plain text. + * + * Flags modifying behaviour of e_reminder_watcher_describe_data(). + * + * Since: 3.30 + **/ +typedef enum { /*< flags >*/ + E_REMINDER_WATCHER_DESCRIBE_FLAG_NONE = 0, + E_REMINDER_WATCHER_DESCRIBE_FLAG_MARKUP = (1 << 1) +} EReminderWatcherDescribeFlags; + +/** * EReminderWatcher: * * Contains only private data that should be read and manipulated using the @@ -107,10 +123,14 @@ struct _EReminderWatcherClass { /* Virtual methods and signals */ void (* schedule_timer) (EReminderWatcher *watcher, gint64 at_time); + void (* format_time) (EReminderWatcher *watcher, + const EReminderData *rd, + struct icaltimetype *itt, + gchar **inout_buffer, + gint buffer_size); void (* triggered) (EReminderWatcher *watcher, - const GSList *reminders); /* EReminderData * */ - void (* removed) (EReminderWatcher *watcher, - const GSList *reminders); /* EReminderData * */ + const GSList *reminders, /* EReminderData * */ + gboolean snoozed); void (* changed) (EReminderWatcher *watcher); /* Padding for future expansion */ @@ -122,9 +142,17 @@ EReminderWatcher * e_reminder_watcher_new (ESourceRegistry *registry); ESourceRegistry * e_reminder_watcher_get_registry (EReminderWatcher *watcher); +ECalClient * e_reminder_watcher_ref_opened_client (EReminderWatcher *watcher, + const gchar *source_uid); void e_reminder_watcher_set_default_zone (EReminderWatcher *watcher, const icaltimezone *zone); icaltimezone * e_reminder_watcher_dup_default_zone (EReminderWatcher *watcher); +gboolean e_reminder_watcher_get_timers_enabled (EReminderWatcher *watcher); +void e_reminder_watcher_set_timers_enabled (EReminderWatcher *watcher, + gboolean enabled); +gchar * e_reminder_watcher_describe_data (EReminderWatcher *watcher, + const EReminderData *rd, + guint32 flags); /* bit-or of EReminderWatcherDescribeFlags */ void e_reminder_watcher_timer_elapsed (EReminderWatcher *watcher); GSList * e_reminder_watcher_dup_past (EReminderWatcher *watcher); /* EReminderData * */ GSList * e_reminder_watcher_dup_snoozed (EReminderWatcher *watcher); /* EReminderData * */ |