[gnome-shell/155-move-functionality-from-evolution-alarm-notify-to-gnome-shell-calendar-server-2] Initial change, there are still things left to be done and test
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/155-move-functionality-from-evolution-alarm-notify-to-gnome-shell-calendar-server-2] Initial change, there are still things left to be done and test
- Date: Thu, 24 May 2018 15:48:30 +0000 (UTC)
commit ef5e7fe233cf6fa70927839eb189f854a539db92
Author: Milan Crha <mcrha redhat com>
Date: Thu May 24 17:49:43 2018 +0200
Initial change, there are still things left to be done and test
js/ui/calendar.js | 103 ++-
meson.build | 4 +-
po/POTFILES.in | 1 +
src/calendar-server/calendar-sources.c | 679 +++++++-------
src/calendar-server/calendar-sources.h | 29 +-
src/calendar-server/gnome-shell-calendar-server.c | 1011 +++++++++++----------
src/calendar-server/meson.build | 4 +-
src/calendar-server/reminder-watcher.c | 648 +++++++++++++
src/calendar-server/reminder-watcher.h | 63 ++
9 files changed, 1657 insertions(+), 885 deletions(-)
---
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
index a46017ad0..199a47222 100644
--- a/js/ui/calendar.js
+++ b/js/ui/calendar.js
@@ -142,8 +142,21 @@ const CalendarServerIface = '<node> \
<arg type="b" direction="in" /> \
<arg type="a(sssbxxa{sv})" direction="out" /> \
</method> \
+<method name="SetTimeRange"> \
+ <arg type="x" name="since" direction="in"/> \
+ <arg type="x" name="until" direction="in"/> \
+ <arg type="b" name="force_reload" direction="in"/> \
+</method> \
+<signal name="EventsAdded"> \
+ <arg type="a(ssbxxa{sv})" name="events" direction="out"/> \
+</signal> \
+<signal name="EventsRemoved"> \
+ <arg type="as" name="ids" direction="out"/> \
+</signal> \
+<signal name="ClientDisappeared"> \
+ <arg type="s" name="source_uid" direction="out"/> \
+</signal> \
<property name="HasCalendars" type="b" access="read" /> \
-<signal name="Changed" /> \
</interface> \
</node>';
@@ -215,7 +228,9 @@ var DBusEventSource = new Lang.Class({
}
}
- this._dbusProxy.connectSignal('Changed', this._onChanged.bind(this));
+ this._dbusProxy.connectSignal('EventsAdded', this._onEventsAdded.bind(this));
+ this._dbusProxy.connectSignal('EventsRemoved', this._onEventsRemoved.bind(this));
+ this._dbusProxy.connectSignal('ClientDisappeared', this._onClientDisappeared.bind(this));
this._dbusProxy.connect('notify::g-name-owner', () => {
if (this._dbusProxy.g_name_owner)
@@ -264,30 +279,67 @@ var DBusEventSource = new Lang.Class({
this.emit('changed');
},
- _onChanged() {
- this._loadEvents(false);
- },
-
- _onEventsReceived(results, error) {
- let newEvents = [];
- let appointments = results ? results[0] : null;
+ _onEventsAdded(in_appts) {
+ let appointments = in_appts ? in_appts[0] : null;
if (appointments != null) {
+ let changed = false;
+
for (let n = 0; n < appointments.length; n++) {
let a = appointments[n];
- let date = new Date(a[4] * 1000);
- let end = new Date(a[5] * 1000);
let id = a[0];
+
+ if (this._ignoredEvents.has(id))
+ continue;
+
+ let date = new Date(a[3] * 1000);
+ let end = new Date(a[4] * 1000);
let summary = a[1];
- let allDay = a[3];
+ let allDay = a[2];
let event = new CalendarEvent(id, date, end, summary, allDay);
- newEvents.push(event);
+ this._events.set(event.id, event);
+ changed = true;
}
- newEvents.sort((ev1, ev2) => ev1.date.getTime() - ev2.date.getTime());
+
+ if (changed)
+ this.emit('changed');
}
+ },
- this._events = newEvents;
- this.isLoading = false;
- this.emit('changed');
+ _onEventsRemoved(in_ids) {
+ let ids = in_ids ? in_ids[0] : null;
+ if (ids != null) {
+ let changed = false;
+
+ for (let n = 0; n < ids.length; n++) {
+ let id = ids[n];
+
+ if (!this._ignoredEvents.has(id) &&
+ this._events.delete(id))
+ changed = true;
+ }
+
+ if (changed)
+ this.emit('changed');
+ }
+ },
+
+ _onClientDisappeared(source_uid) {
+ let ids = this._events.keys();
+ if (ids != null) {
+ let changed = false;
+
+ source_uid = source_uid + '\n';
+ for (let n = 0; n < ids.length; n++) {
+ let id = ids[n];
+
+ if (id.startsWith(source_uid) &&
+ this._events.delete(id))
+ changed = true;
+ }
+
+ if (changed)
+ this.emit('changed');
+ }
},
_loadEvents(forceReload) {
@@ -296,11 +348,10 @@ var DBusEventSource = new Lang.Class({
return;
if (this._curRequestBegin && this._curRequestEnd){
- this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
- this._curRequestEnd.getTime() / 1000,
- forceReload,
- this._onEventsReceived.bind(this),
- Gio.DBusCallFlags.NONE);
+ this._dbusProxy.SetTimeRangeRemote(this._curRequestBegin.getTime() / 1000,
+ this._curRequestEnd.getTime() / 1000,
+ forceReload,
+ Gio.DBusCallFlags.NONE);
}
},
@@ -316,7 +367,6 @@ var DBusEventSource = new Lang.Class({
requestRange(begin, end) {
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
- this.isLoading = true;
this._lastRequestBegin = begin;
this._lastRequestEnd = end;
this._curRequestBegin = begin;
@@ -327,8 +377,9 @@ var DBusEventSource = new Lang.Class({
getEvents(begin, end) {
let result = [];
- for(let n = 0; n < this._events.length; n++) {
- let event = this._events[n];
+ let events = this._events.values;
+ for(let n = 0; n < events.length; n++) {
+ let event = events[n];
if (this._ignoredEvents.has(event.id))
continue;
@@ -870,7 +921,7 @@ var EventsSection = new Lang.Class({
},
_reloadEvents() {
- if (this._eventSource.isLoading)
+ if (this._eventSource.isLoading || this._reloading)
return;
this._reloading = true;
diff --git a/meson.build b/meson.build
index 5601c96ca..bc634bd4b 100644
--- a/meson.build
+++ b/meson.build
@@ -14,8 +14,8 @@ cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version
libmutter_pc = 'libmutter-' + mutter_api_version
croco_req = '>= 0.6.8'
-ecal_req = '>= 3.5.3'
-eds_req = '>= 3.17.2'
+ecal_req = '>= 3.29.3'
+eds_req = '>= 3.29.3'
gcr_req = '>= 3.7.5'
gdesktop_req = '>= 3.7.90'
gio_req = '>= 2.56.0'
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 02805f402..51c0ae1ac 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -69,6 +69,7 @@ js/ui/windowAttentionHandler.js
js/ui/windowManager.js
js/ui/windowMenu.js
src/calendar-server/evolution-calendar.desktop.in
+src/calendar-server/reminder-watcher.c
src/main.c
src/shell-app.c
src/shell-app-system.c
diff --git a/src/calendar-server/calendar-sources.c b/src/calendar-server/calendar-sources.c
index 628f89b07..5e3d7f535 100644
--- a/src/calendar-server/calendar-sources.c
+++ b/src/calendar-server/calendar-sources.c
@@ -48,20 +48,7 @@ struct _ClientData
{
ECalClient *client;
gulong backend_died_id;
-};
-
-struct _CalendarSourceData
-{
- ECalClientSourceType source_type;
- CalendarSources *sources;
- guint changed_signal;
-
- /* ESource -> EClient */
- GHashTable *clients;
-
- guint timeout_id;
-
- guint loaded : 1;
+ gboolean is_for_events; /* Because this can hold other clients too (for EReminderWatcher) */
};
typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate;
@@ -74,118 +61,117 @@ struct _CalendarSources
struct _CalendarSourcesPrivate
{
- ESourceRegistry *registry;
- gulong source_added_id;
- gulong source_changed_id;
- gulong source_removed_id;
+ ESourceRegistryWatcher *registry_watcher;
+ gulong filter_id;
+ gulong appeared_id;
+ gulong disappeared_id;
- CalendarSourceData appointment_sources;
- CalendarSourceData task_sources;
+ GMutex clients_lock;
+ GHashTable *clients; /* ESource -> ClientData */
};
G_DEFINE_TYPE_WITH_PRIVATE (CalendarSources, calendar_sources, G_TYPE_OBJECT)
-static void calendar_sources_finalize (GObject *object);
-
-static void backend_died_cb (EClient *client, CalendarSourceData *source_data);
-static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
- ESource *source,
- CalendarSources *sources);
-static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
- ESource *source,
- CalendarSources *sources);
-
enum
{
- APPOINTMENT_SOURCES_CHANGED,
- TASK_SOURCES_CHANGED,
+ CLIENT_APPEARED,
+ CLIENT_DISAPPEARED,
LAST_SIGNAL
};
static guint signals [LAST_SIGNAL] = { 0, };
-static GObjectClass *parent_class = NULL;
-static CalendarSources *calendar_sources_singleton = NULL;
+static void
+calendar_sources_client_connected_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (source_object);
+ ESource *source = user_data;
+ EClient *client;
+ GError *error = NULL;
+
+ /* The calendar_sources_connect_client_sync() already stored the 'client'
+ * into the priv->clients */
+ client = calendar_sources_connect_client_finish (sources, result, &error);
+ if (error)
+ {
+ g_warning ("Could not load source '%s': %s",
+ e_source_get_uid (source),
+ error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ g_signal_emit (sources, signals[CLIENT_APPEARED], 0, client, NULL);
+ }
+
+ g_clear_object (&client);
+ g_clear_object (&source);
+}
+
+static gboolean
+registry_watcher_filter_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
+{
+ return e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) &&
+ e_source_selectable_get_selected (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR));
+}
static void
-client_data_free (ClientData *data)
+registry_watcher_source_appeared_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
{
- g_signal_handler_disconnect (data->client, data->backend_died_id);
- g_object_unref (data->client);
- g_slice_free (ClientData, data);
+ ECalClientSourceType source_type;
+
+ 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;
+ else
+ g_return_if_reached ();
+
+ calendar_sources_connect_client (sources, TRUE, source, source_type, 30, NULL,
calendar_sources_client_connected_cb, g_object_ref (source));
}
static void
-calendar_sources_class_init (CalendarSourcesClass *klass)
+registry_watcher_source_disappeared_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
{
- GObjectClass *gobject_class = (GObjectClass *) klass;
+ gboolean emit;
- parent_class = g_type_class_peek_parent (klass);
+ g_mutex_lock (&sources->priv->clients_lock);
- gobject_class->finalize = calendar_sources_finalize;
+ emit = g_hash_table_remove (sources->priv->clients, source);
- signals [APPOINTMENT_SOURCES_CHANGED] =
- g_signal_new ("appointment-sources-changed",
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL,
- NULL,
- NULL,
- G_TYPE_NONE,
- 0);
+ g_mutex_unlock (&sources->priv->clients_lock);
- signals [TASK_SOURCES_CHANGED] =
- g_signal_new ("task-sources-changed",
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL,
- NULL,
- NULL,
- G_TYPE_NONE,
- 0);
+ if (emit)
+ g_signal_emit (sources, signals[CLIENT_DISAPPEARED], 0, e_source_get_uid (source), NULL);
}
static void
-calendar_sources_init (CalendarSources *sources)
+client_data_free (ClientData *data)
{
- GError *error = NULL;
- GDBusConnection *session_bus;
- GVariant *result;
+ g_signal_handler_disconnect (data->client, data->backend_died_id);
+ g_object_unref (data->client);
+ g_slice_free (ClientData, data);
+}
- sources->priv = calendar_sources_get_instance_private (sources);
+static void
+calendar_sources_constructed (GObject *object)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (object);
+ ESourceRegistry *registry = NULL;
+ GError *error = NULL;
- /* WORKAROUND: the hardcoded timeout for e_source_registry_new_sync()
- (and other library calls that eventually call g_dbus_proxy_new[_sync]())
- is 25 seconds. This has been shown to be too small for
- evolution-source-registry in certain cases (slow disk, concurrent IO,
- many configured sources), so we first ensure that the service
- starts with a manual call and a higher timeout.
-
- HACK: every time the DBus API is bumped in e-d-s we need
- to update this!
- */
- session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
- if (session_bus == NULL)
- {
- g_error ("Failed to connect to the session bus: %s", error->message);
- }
+ G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object);
- result = g_dbus_connection_call_sync (session_bus, "org.freedesktop.DBus",
- "/", "org.freedesktop.DBus",
- "StartServiceByName",
- g_variant_new ("(su)",
- "org.gnome.evolution.dataserver.Sources5",
- 0),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- 60 * 1000,
- NULL, &error);
- if (result != NULL)
- {
- g_variant_unref (result);
- sources->priv->registry = e_source_registry_new_sync (NULL, &error);
- }
+ registry = e_source_registry_new_sync (NULL, &error);
if (error != NULL)
{
@@ -196,86 +182,104 @@ calendar_sources_init (CalendarSources *sources)
exit(EXIT_FAILURE);
}
- g_object_unref (session_bus);
-
- sources->priv->source_added_id = g_signal_connect (sources->priv->registry,
- "source-added",
- G_CALLBACK
(calendar_sources_registry_source_changed_cb),
- sources);
- sources->priv->source_changed_id = g_signal_connect (sources->priv->registry,
- "source-changed",
- G_CALLBACK
(calendar_sources_registry_source_changed_cb),
- sources);
- sources->priv->source_removed_id = g_signal_connect (sources->priv->registry,
- "source-removed",
- G_CALLBACK
(calendar_sources_registry_source_removed_cb),
- sources);
-
- sources->priv->appointment_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
- sources->priv->appointment_sources.sources = sources;
- sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
- sources->priv->appointment_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
- (GEqualFunc) e_source_equal,
- (GDestroyNotify) g_object_unref,
- (GDestroyNotify)
client_data_free);
- sources->priv->appointment_sources.timeout_id = 0;
-
- sources->priv->task_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
- sources->priv->task_sources.sources = sources;
- sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
- sources->priv->task_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
- (GEqualFunc) e_source_equal,
- (GDestroyNotify) g_object_unref,
- (GDestroyNotify) client_data_free);
- sources->priv->task_sources.timeout_id = 0;
+ g_return_if_fail (registry != NULL);
+
+ sources->priv->registry_watcher = e_source_registry_watcher_new (registry, NULL);
+
+ g_clear_object (®istry);
+
+ sources->priv->clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
+ (GEqualFunc) e_source_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) client_data_free);
+ sources->priv->filter_id = g_signal_connect (sources->priv->registry_watcher,
+ "filter",
+ G_CALLBACK (registry_watcher_filter_cb),
+ sources);
+ sources->priv->appeared_id = g_signal_connect (sources->priv->registry_watcher,
+ "appeared",
+ G_CALLBACK (registry_watcher_source_appeared_cb),
+ sources);
+ sources->priv->disappeared_id = g_signal_connect (sources->priv->registry_watcher,
+ "disappeared",
+ G_CALLBACK (registry_watcher_source_disappeared_cb),
+ sources);
+
+ e_source_registry_watcher_reclaim (sources->priv->registry_watcher);
}
static void
-calendar_sources_finalize_source_data (CalendarSources *sources,
- CalendarSourceData *source_data)
+calendar_sources_finalize (GObject *object)
{
- if (source_data->loaded)
- {
- g_hash_table_destroy (source_data->clients);
- source_data->clients = NULL;
+ CalendarSources *sources = CALENDAR_SOURCES (object);
- if (source_data->timeout_id != 0)
- {
- g_source_remove (source_data->timeout_id);
- source_data->timeout_id = 0;
- }
+ if (sources->priv->clients)
+ {
+ g_hash_table_destroy (sources->priv->clients);
+ sources->priv->clients = NULL;
+ }
- source_data->loaded = FALSE;
+ if (sources->priv->registry_watcher)
+ {
+ g_signal_handler_disconnect (sources->priv->registry_watcher,
+ sources->priv->filter_id);
+ g_signal_handler_disconnect (sources->priv->registry_watcher,
+ sources->priv->appeared_id);
+ g_signal_handler_disconnect (sources->priv->registry_watcher,
+ sources->priv->disappeared_id);
+ g_clear_object (&sources->priv->registry_watcher);
}
+
+ g_mutex_clear (&sources->priv->clients_lock);
+
+ G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object);
}
static void
-calendar_sources_finalize (GObject *object)
+calendar_sources_class_init (CalendarSourcesClass *klass)
{
- CalendarSources *sources = CALENDAR_SOURCES (object);
+ GObjectClass *gobject_class = (GObjectClass *) klass;
- if (sources->priv->registry)
- {
- g_signal_handler_disconnect (sources->priv->registry,
- sources->priv->source_added_id);
- g_signal_handler_disconnect (sources->priv->registry,
- sources->priv->source_changed_id);
- g_signal_handler_disconnect (sources->priv->registry,
- sources->priv->source_removed_id);
- g_object_unref (sources->priv->registry);
- }
- sources->priv->registry = NULL;
+ gobject_class->constructed = calendar_sources_constructed;
+ gobject_class->finalize = calendar_sources_finalize;
+
+ signals [CLIENT_APPEARED] =
+ g_signal_new ("client-appeared",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ E_TYPE_CAL_CLIENT);
- calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
- calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
+ signals [CLIENT_DISAPPEARED] =
+ g_signal_new ("client-disappeared",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING); /* ESource::uid of the disappeared client */
+}
- if (G_OBJECT_CLASS (parent_class)->finalize)
- G_OBJECT_CLASS (parent_class)->finalize (object);
+static void
+calendar_sources_init (CalendarSources *sources)
+{
+ sources->priv = calendar_sources_get_instance_private (sources);
+
+ g_mutex_init (&sources->priv->clients_lock);
}
CalendarSources *
calendar_sources_get (void)
{
+ static CalendarSources *calendar_sources_singleton = NULL;
gpointer singleton_location = &calendar_sources_singleton;
if (calendar_sources_singleton)
@@ -288,80 +292,65 @@ calendar_sources_get (void)
return calendar_sources_singleton;
}
-/* The clients are just created here but not loaded */
+ESourceRegistry *
+calendar_sources_get_registry (CalendarSources *sources)
+{
+ return e_source_registry_watcher_get_registry (sources->priv->registry_watcher);
+}
+
static void
-create_client_for_source (ESource *source,
- ECalClientSourceType source_type,
- CalendarSourceData *source_data)
+gather_event_clients_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
{
- ClientData *data;
- ECalClient *client;
- GError *error = NULL;
+ GSList **plist = user_data;
+ ClientData *cd = value;
- client = g_hash_table_lookup (source_data->clients, source);
- g_return_if_fail (client == NULL);
+ if (cd && cd->is_for_events)
+ *plist = g_slist_prepend (*plist, g_object_ref (cd->client));
+}
- client = e_cal_client_new (source, source_type, &error);
- if (!client)
- {
- g_warning ("Could not load source '%s': %s",
- e_source_get_uid (source),
- error->message);
- g_clear_error(&error);
- return;
- }
+GSList *
+calendar_sources_ref_clients (CalendarSources *sources)
+{
+ GSList *list = NULL;
+
+ g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
- data = g_slice_new0 (ClientData);
- data->client = client; /* takes ownership */
- data->backend_died_id = g_signal_connect (client,
- "backend-died",
- G_CALLBACK (backend_died_cb),
- source_data);
+ g_mutex_lock (&sources->priv->clients_lock);
+ g_hash_table_foreach (sources->priv->clients, gather_event_clients_cb, &list);
+ g_mutex_unlock (&sources->priv->clients_lock);
- g_hash_table_insert (source_data->clients, g_object_ref (source), data);
+ return list;
}
-static inline void
-debug_dump_ecal_list (GHashTable *clients)
+gboolean
+calendar_sources_has_clients (CalendarSources *sources)
{
-#ifdef CALENDAR_ENABLE_DEBUG
- GList *list, *link;
+ GHashTableIter iter;
+ gpointer value;
+ gboolean has = FALSE;
- dprintf ("Loaded clients:\n");
- list = g_hash_table_get_keys (clients);
- for (link = list; link != NULL; link = g_list_next (link))
- {
- ESource *source = E_SOURCE (link->data);
+ g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
- dprintf (" %s %s\n",
- e_source_get_uid (source),
- e_source_get_display_name (source));
- }
- g_list_free (list);
-#endif
-}
+ g_mutex_lock (&sources->priv->clients_lock);
-static void
-calendar_sources_load_esource_list (ESourceRegistry *registry,
- CalendarSourceData *source_data);
+ g_hash_table_iter_init (&iter, sources->priv->clients);
+ while (!has && g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ ClientData *cd = value;
-static gboolean
-backend_restart (gpointer data)
-{
- CalendarSourceData *source_data = data;
- ESourceRegistry *registry;
+ has = cd && cd->is_for_events;
+ }
- registry = source_data->sources->priv->registry;
- calendar_sources_load_esource_list (registry, source_data);
- g_signal_emit (source_data->sources, source_data->changed_signal, 0);
+ g_mutex_unlock (&sources->priv->clients_lock);
- source_data->timeout_id = 0;
-
- return FALSE;
+ return has;
}
static void
-backend_died_cb (EClient *client, CalendarSourceData *source_data)
+backend_died_cb (EClient *client,
+ CalendarSources *sources)
{
ESource *source;
const char *display_name;
@@ -369,200 +358,144 @@ backend_died_cb (EClient *client, CalendarSourceData *source_data)
source = e_client_get_source (client);
display_name = e_source_get_display_name (source);
g_warning ("The calendar backend for '%s' has crashed.", display_name);
- g_hash_table_remove (source_data->clients, source);
-
- if (source_data->timeout_id != 0)
- {
- g_source_remove (source_data->timeout_id);
- source_data->timeout_id = 0;
- }
- source_data->timeout_id = g_timeout_add_seconds (2, backend_restart,
- source_data);
- g_source_set_name_by_id (source_data->timeout_id, "[gnome-shell] backend_restart");
+ g_mutex_lock (&sources->priv->clients_lock);
+ g_hash_table_remove (sources->priv->clients, source);
+ g_mutex_unlock (&sources->priv->clients_lock);
}
-static void
-calendar_sources_load_esource_list (ESourceRegistry *registry,
- CalendarSourceData *source_data)
+EClient *
+calendar_sources_connect_client_sync (CalendarSources *sources,
+ gboolean is_for_events,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GError **error)
{
- GList *list, *link;
- const gchar *extension_name;
+ EClient *client = NULL;
+ ClientData *client_data;
+
+ g_mutex_lock (&sources->priv->clients_lock);
+ client_data = g_hash_table_lookup (sources->priv->clients, source);
+ if (client_data) {
+ if (is_for_events)
+ client_data->is_for_events = TRUE;
+ client = E_CLIENT (g_object_ref (client_data->client));
+ }
+ g_mutex_unlock (&sources->priv->clients_lock);
+
+ if (client)
+ return client;
+
+ client = e_cal_client_connect_sync (source, source_type, wait_for_connected_seconds, cancellable, error);
+ if (!client)
+ return NULL;
- switch (source_data->source_type)
+ g_mutex_lock (&sources->priv->clients_lock);
+ client_data = g_hash_table_lookup (sources->priv->clients, source);
+ if (client_data)
{
- case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
- extension_name = E_SOURCE_EXTENSION_CALENDAR;
- break;
- case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
- extension_name = E_SOURCE_EXTENSION_TASK_LIST;
- break;
- default:
- g_return_if_reached ();
+ if (is_for_events)
+ client_data->is_for_events = TRUE;
+ g_clear_object (&client);
+ client = E_CLIENT (g_object_ref (client_data->client));
}
-
- list = e_source_registry_list_sources (registry, extension_name);
-
- for (link = list; link != NULL; link = g_list_next (link))
+ else
{
- ESource *source = E_SOURCE (link->data);
- ESourceSelectable *extension;
- gboolean show_source;
-
- extension = e_source_get_extension (source, extension_name);
- show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
+ client_data = g_slice_new0 (ClientData);
+ client_data->client = E_CAL_CLIENT (g_object_ref (client));
+ client_data->is_for_events = is_for_events;
+ client_data->backend_died_id = g_signal_connect (client,
+ "backend-died",
+ G_CALLBACK (backend_died_cb),
+ sources);
- if (show_source)
- create_client_for_source (source, source_data->source_type, source_data);
+ g_hash_table_insert (sources->priv->clients, g_object_ref (source), client_data);
}
+ g_mutex_unlock (&sources->priv->clients_lock);
- debug_dump_ecal_list (source_data->clients);
-
- g_list_free_full (list, g_object_unref);
+ return client;
}
-static void
-calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
- ESource *source,
- CalendarSources *sources)
-{
- if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
- {
- CalendarSourceData *source_data;
- ESourceSelectable *extension;
- gboolean have_client;
- gboolean show_source;
-
- source_data = &sources->priv->appointment_sources;
- extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
- have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
- show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
-
- if (!show_source && have_client)
- {
- g_hash_table_remove (source_data->clients, source);
- g_signal_emit (sources, source_data->changed_signal, 0);
- }
- if (show_source && !have_client)
- {
- create_client_for_source (source, source_data->source_type, source_data);
- g_signal_emit (sources, source_data->changed_signal, 0);
- }
- }
-
- if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
- {
- CalendarSourceData *source_data;
- ESourceSelectable *extension;
- gboolean have_client;
- gboolean show_source;
-
- source_data = &sources->priv->task_sources;
- extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
- have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
- show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
-
- if (!show_source && have_client)
- {
- g_hash_table_remove (source_data->clients, source);
- g_signal_emit (sources, source_data->changed_signal, 0);
- }
- if (show_source && !have_client)
- {
- create_client_for_source (source, source_data->source_type, source_data);
- g_signal_emit (sources, source_data->changed_signal, 0);
- }
- }
-}
+typedef struct _AsyncContext {
+ gboolean is_for_events;
+ ESource *source;
+ ECalClientSourceType source_type;
+ guint32 wait_for_connected_seconds;
+} AsyncContext;
static void
-calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
- ESource *source,
- CalendarSources *sources)
+async_context_free (gpointer ptr)
{
- if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
- {
- CalendarSourceData *source_data;
+ AsyncContext *ctx = ptr;
- source_data = &sources->priv->appointment_sources;
- g_hash_table_remove (source_data->clients, source);
- g_signal_emit (sources, source_data->changed_signal, 0);
- }
-
- if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+ if (ctx)
{
- CalendarSourceData *source_data;
-
- source_data = &sources->priv->task_sources;
- g_hash_table_remove (source_data->clients, source);
- g_signal_emit (sources, source_data->changed_signal, 0);
+ g_clear_object (&ctx->source);
+ g_free (ctx);
}
}
static void
-ensure_appointment_sources (CalendarSources *sources)
+calendar_sources_connect_client_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
{
- if (!sources->priv->appointment_sources.loaded)
- {
- calendar_sources_load_esource_list (sources->priv->registry,
- &sources->priv->appointment_sources);
- sources->priv->appointment_sources.loaded = TRUE;
- }
-}
-
-GList *
-calendar_sources_get_appointment_clients (CalendarSources *sources)
-{
- GList *list, *link;
-
- g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
-
- ensure_appointment_sources (sources);
-
- list = g_hash_table_get_values (sources->priv->appointment_sources.clients);
-
- for (link = list; link != NULL; link = g_list_next (link))
- link->data = ((ClientData *) link->data)->client;
-
- return list;
-}
+ CalendarSources *sources = source_object;
+ AsyncContext *ctx = task_data;
+ EClient *client;
+ GError *local_error = NULL;
-static void
-ensure_task_sources (CalendarSources *sources)
-{
- if (!sources->priv->task_sources.loaded)
+ client = calendar_sources_connect_client_sync (sources, ctx->is_for_events, ctx->source, ctx->source_type,
+ ctx->wait_for_connected_seconds, cancellable, &local_error);
+ if (!client)
{
- calendar_sources_load_esource_list (sources->priv->registry,
- &sources->priv->task_sources);
- sources->priv->task_sources.loaded = TRUE;
+ if (local_error)
+ g_task_return_error (task, local_error);
+ else
+ g_task_return_pointer (task, NULL, NULL);
+ } else {
+ g_task_return_pointer (task, client, g_object_unref);
}
}
-GList *
-calendar_sources_get_task_clients (CalendarSources *sources)
+void
+calendar_sources_connect_client (CalendarSources *sources,
+ gboolean is_for_events,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GList *list, *link;
+ AsyncContext *ctx;
+ GTask *task;
- g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
+ ctx = g_new0 (AsyncContext, 1);
+ ctx->is_for_events = is_for_events;
+ ctx->source = g_object_ref (source);
+ ctx->source_type = source_type;
+ ctx->wait_for_connected_seconds = wait_for_connected_seconds;
- ensure_task_sources (sources);
+ task = g_task_new (sources, cancellable, callback, user_data);
+ g_task_set_source_tag (task, calendar_sources_connect_client);
+ g_task_set_task_data (task, ctx, async_context_free);
- list = g_hash_table_get_values (sources->priv->task_sources.clients);
+ g_task_run_in_thread (task, calendar_sources_connect_client_thread);
- for (link = list; link != NULL; link = g_list_next (link))
- link->data = ((ClientData *) link->data)->client;
-
- return list;
+ g_object_unref (task);
}
-gboolean
-calendar_sources_has_sources (CalendarSources *sources)
+EClient *
+calendar_sources_connect_client_finish (CalendarSources *sources,
+ GAsyncResult *result,
+ GError **error)
{
- g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
-
- ensure_appointment_sources (sources);
- ensure_task_sources (sources);
+ g_return_val_if_fail (g_task_is_valid (result, sources), NULL);
+ g_return_val_if_fail (g_async_result_is_tagged (result, calendar_sources_connect_client), NULL);
- return g_hash_table_size (sources->priv->appointment_sources.clients) > 0 ||
- g_hash_table_size (sources->priv->task_sources.clients) > 0;
+ return g_task_propagate_pointer (G_TASK (result), error);
}
diff --git a/src/calendar-server/calendar-sources.h b/src/calendar-server/calendar-sources.h
index d11850fff..c0f6badce 100644
--- a/src/calendar-server/calendar-sources.h
+++ b/src/calendar-server/calendar-sources.h
@@ -25,6 +25,8 @@
#define __CALENDAR_SOURCES_H__
#include <glib-object.h>
+#include <libedataserver/libedataserver.h>
+#include <libecal/libecal.h>
G_BEGIN_DECLS
@@ -32,11 +34,30 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (CalendarSources, calendar_sources,
CALENDAR, SOURCES, GObject)
-CalendarSources *calendar_sources_get (void);
-GList *calendar_sources_get_appointment_clients (CalendarSources *sources);
-GList *calendar_sources_get_task_clients (CalendarSources *sources);
+CalendarSources *calendar_sources_get (void);
+ESourceRegistry *calendar_sources_get_registry (CalendarSources *sources);
+GSList *calendar_sources_ref_clients (CalendarSources *sources);
+gboolean calendar_sources_has_clients (CalendarSources *sources);
-gboolean calendar_sources_has_sources (CalendarSources *sources);
+EClient *calendar_sources_connect_client_sync(CalendarSources *sources,
+ gboolean is_for_events,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GError **error);
+void calendar_sources_connect_client (CalendarSources *sources,
+ gboolean is_for_events,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+EClient *calendar_sources_connect_client_finish
+ (CalendarSources *sources,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
diff --git a/src/calendar-server/gnome-shell-calendar-server.c
b/src/calendar-server/gnome-shell-calendar-server.c
index 9b76fb073..d1e889746 100644
--- a/src/calendar-server/gnome-shell-calendar-server.c
+++ b/src/calendar-server/gnome-shell-calendar-server.c
@@ -37,6 +37,7 @@
#include <libecal/libecal.h>
#include "calendar-sources.h"
+#include "reminder-watcher.h"
/* Set the environment variable CALENDAR_SERVER_DEBUG to show debug */
static void print_debug (const gchar *str, ...);
@@ -46,13 +47,20 @@ static void print_debug (const gchar *str, ...);
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.gnome.Shell.CalendarServer'>"
- " <method name='GetEvents'>"
+ " <method name='SetTimeRange'>"
" <arg type='x' name='since' direction='in'/>"
" <arg type='x' name='until' direction='in'/>"
" <arg type='b' name='force_reload' direction='in'/>"
- " <arg type='a(sssbxxa{sv})' name='events' direction='out'/>"
" </method>"
- " <signal name='Changed'/>"
+ " <signal name='EventsAdded'>" /* It's for both added and modified */
+ " <arg type='a(ssbxxa{sv})' name='events' direction='out'/>"
+ " </signal>"
+ " <signal name='EventsRemoved'>"
+ " <arg type='as' name='ids' direction='out'/>"
+ " </signal>"
+ " <signal name='ClientDisappeared'>"
+ " <arg type='s' name='source_uid' direction='out'/>"
+ " </signal>"
" <property name='Since' type='x' access='read'/>"
" <property name='Until' type='x' access='read'/>"
" <property name='HasCalendars' type='b' access='read'/>"
@@ -72,35 +80,40 @@ static App *_global_app = NULL;
/* ---------------------------------------------------------------------------------------------------- */
+/* While the UID is usually enough to identify an event,
+ * only the triple of (source,UID,RID) is fully unambiguous;
+ * neither may contain '\n', so we can safely use it to
+ * create a unique ID from the triple
+ */
+static gchar *
+create_event_id (const gchar *source_uid,
+ const gchar *comp_uid,
+ const gchar *comp_rid)
+{
+ return g_strconcat (
+ source_uid ? source_uid : "",
+ "\n",
+ comp_uid ? comp_uid : "",
+ "\n",
+ comp_rid ? comp_rid : "",
+ NULL);
+}
+
typedef struct
{
- char *rid;
- time_t start_time;
- time_t end_time;
-} CalendarOccurrence;
+ ECalClient *client;
+ GSList **pappointments; /* CalendarAppointment * */
+} CollectAppointmentsData;
typedef struct
{
- char *uid;
- char *source_id;
- char *backend_name;
- char *summary;
- char *description;
- char *color_string;
+ gchar *id;
+ gchar *summary;
time_t start_time;
time_t end_time;
guint is_all_day : 1;
-
- /* Only used internally */
- GSList *occurrences;
} CalendarAppointment;
-typedef struct
-{
- ECalClient *client;
- GHashTable *appointments;
-} CollectAppointmentsData;
-
static time_t
get_time_from_property (icalcomponent *ical,
icalproperty_kind prop_kind,
@@ -129,36 +142,6 @@ get_time_from_property (icalcomponent *ical,
return icaltime_as_timet_with_zone (ical_time, timezone);
}
-static char *
-get_ical_uid (icalcomponent *ical)
-{
- return g_strdup (icalcomponent_get_uid (ical));
-}
-
-static char *
-get_ical_summary (icalcomponent *ical)
-{
- icalproperty *prop;
-
- prop = icalcomponent_get_first_property (ical, ICAL_SUMMARY_PROPERTY);
- if (!prop)
- return NULL;
-
- return g_strdup (icalproperty_get_summary (prop));
-}
-
-static char *
-get_ical_description (icalcomponent *ical)
-{
- icalproperty *prop;
-
- prop = icalcomponent_get_first_property (ical, ICAL_DESCRIPTION_PROPERTY);
- if (!prop)
- return NULL;
-
- return g_strdup (icalproperty_get_description (prop));
-}
-
static inline time_t
get_ical_start_time (icalcomponent *ical,
icaltimezone *default_zone)
@@ -232,207 +215,68 @@ get_ical_completed_time (icalcomponent *ical,
default_zone);
}
-static char *
-get_source_color (ECalClient *esource)
-{
- ESource *source;
- ECalClientSourceType source_type;
- ESourceSelectable *extension;
- const gchar *extension_name;
-
- g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
-
- source = e_client_get_source (E_CLIENT (esource));
- source_type = e_cal_client_get_source_type (esource);
-
- switch (source_type)
- {
- case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
- extension_name = E_SOURCE_EXTENSION_CALENDAR;
- break;
- case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
- extension_name = E_SOURCE_EXTENSION_TASK_LIST;
- break;
- default:
- g_return_val_if_reached (NULL);
- }
-
- extension = e_source_get_extension (source, extension_name);
-
- return e_source_selectable_dup_color (extension);
-}
-
-static gchar *
-get_source_backend_name (ECalClient *esource)
+static CalendarAppointment *
+calendar_appointment_new (ECalClient *cal,
+ ECalComponent *comp)
{
- ESource *source;
- ECalClientSourceType source_type;
- ESourceBackend *extension;
- const gchar *extension_name;
+ CalendarAppointment *appt;
+ icaltimezone *default_zone;
+ icalcomponent *ical;
+ ECalComponentId *id;
- g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
+ default_zone = e_cal_client_get_default_timezone (cal);
+ ical = e_cal_component_get_icalcomponent (comp);
+ id = e_cal_component_get_id (comp);
- source = e_client_get_source (E_CLIENT (esource));
- source_type = e_cal_client_get_source_type (esource);
+ appt = g_new0 (CalendarAppointment, 1);
- switch (source_type)
- {
- case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
- extension_name = E_SOURCE_EXTENSION_CALENDAR;
- break;
- case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
- extension_name = E_SOURCE_EXTENSION_TASK_LIST;
- break;
- default:
- g_return_val_if_reached (NULL);
- }
+ appt->id = create_event_id (e_source_get_uid (e_client_get_source (E_CLIENT (cal))),
+ id ? id->uid : NULL,
+ id ? id->rid : NULL);
+ appt->summary = g_strdup (icalcomponent_get_summary (ical));
+ appt->start_time = get_ical_start_time (ical, default_zone);
+ appt->end_time = get_ical_end_time (ical, default_zone);
+ appt->is_all_day = get_ical_is_all_day (ical,
+ appt->start_time,
+ default_zone);
- extension = e_source_get_extension (source, extension_name);
+ if (id)
+ e_cal_component_free_id (id);
- return e_source_backend_dup_backend_name (extension);
+ return appt;
}
-static inline int
-null_safe_strcmp (const char *a,
- const char *b)
-{
- return (!a && !b) ? 0 : (a && !b) || (!a && b) ? 1 : strcmp (a, b);
-}
-
-static inline gboolean
-calendar_appointment_equal (CalendarAppointment *a,
- CalendarAppointment *b)
+static void
+calendar_appointment_free (gpointer ptr)
{
- GSList *la, *lb;
+ CalendarAppointment *appt = ptr;
- if (g_slist_length (a->occurrences) != g_slist_length (b->occurrences))
- return FALSE;
-
- for (la = a->occurrences, lb = b->occurrences; la && lb; la = la->next, lb = lb->next)
+ if (appt)
{
- CalendarOccurrence *oa = la->data;
- CalendarOccurrence *ob = lb->data;
-
- if (oa->start_time != ob->start_time ||
- oa->end_time != ob->end_time ||
- null_safe_strcmp (oa->rid, ob->rid) != 0)
- return FALSE;
+ g_free (appt->id);
+ g_free (appt->summary);
+ g_free (appt);
}
-
- return
- null_safe_strcmp (a->uid, b->uid) == 0 &&
- null_safe_strcmp (a->source_id, b->source_id) == 0 &&
- null_safe_strcmp (a->backend_name, b->backend_name) == 0 &&
- null_safe_strcmp (a->summary, b->summary) == 0 &&
- null_safe_strcmp (a->description, b->description) == 0 &&
- null_safe_strcmp (a->color_string, b->color_string) == 0 &&
- a->start_time == b->start_time &&
- a->end_time == b->end_time &&
- a->is_all_day == b->is_all_day;
-}
-
-static void
-calendar_appointment_free (CalendarAppointment *appointment)
-{
- GSList *l;
-
- for (l = appointment->occurrences; l; l = l->next)
- g_free (((CalendarOccurrence *)l->data)->rid);
- g_slist_free_full (appointment->occurrences, g_free);
- appointment->occurrences = NULL;
-
- g_free (appointment->uid);
- appointment->uid = NULL;
-
- g_free (appointment->source_id);
- appointment->source_id = NULL;
-
- g_free (appointment->backend_name);
- appointment->backend_name = NULL;
-
- g_free (appointment->summary);
- appointment->summary = NULL;
-
- g_free (appointment->description);
- appointment->description = NULL;
-
- g_free (appointment->color_string);
- appointment->color_string = NULL;
-
- appointment->start_time = 0;
- appointment->is_all_day = FALSE;
-}
-
-static void
-calendar_appointment_init (CalendarAppointment *appointment,
- icalcomponent *ical,
- ECalClient *cal)
-{
- icaltimezone *default_zone;
- const char *source_id;
-
- source_id = e_source_get_uid (e_client_get_source (E_CLIENT (cal)));
- default_zone = e_cal_client_get_default_timezone (cal);
-
- appointment->uid = get_ical_uid (ical);
- appointment->source_id = g_strdup (source_id);
- appointment->backend_name = get_source_backend_name (cal);
- appointment->summary = get_ical_summary (ical);
- appointment->description = get_ical_description (ical);
- appointment->color_string = get_source_color (cal);
- appointment->start_time = get_ical_start_time (ical, default_zone);
- appointment->end_time = get_ical_end_time (ical, default_zone);
- appointment->is_all_day = get_ical_is_all_day (ical,
- appointment->start_time,
- default_zone);
-}
-
-static CalendarAppointment *
-calendar_appointment_new (icalcomponent *ical,
- ECalClient *cal)
-{
- CalendarAppointment *appointment;
-
- appointment = g_new0 (CalendarAppointment, 1);
-
- calendar_appointment_init (appointment, ical, cal);
- return appointment;
}
static gboolean
generate_instances_cb (ECalComponent *comp,
time_t start,
time_t end,
- gpointer data)
+ gpointer user_data)
{
- ECalClient *cal = ((CollectAppointmentsData *)data)->client;
- GHashTable *appointments = ((CollectAppointmentsData *)data)->appointments;
+ CollectAppointmentsData *data = user_data;
CalendarAppointment *appointment;
- CalendarOccurrence *occurrence;
- const char *uid;
-
- e_cal_component_get_uid (comp, &uid);
- appointment = g_hash_table_lookup (appointments, uid);
- if (appointment == NULL)
- {
- icalcomponent *ical = e_cal_component_get_icalcomponent (comp);
-
- appointment = calendar_appointment_new (ical, cal);
- g_hash_table_insert (appointments, g_strdup (uid), appointment);
- }
+ appointment = calendar_appointment_new (data->client, comp);
+ appointment->start_time = start;
+ appointment->end_time = end;
- occurrence = g_new0 (CalendarOccurrence, 1);
- occurrence->start_time = start;
- occurrence->end_time = end;
- occurrence->rid = e_cal_component_get_recurid_as_string (comp);
-
- appointment->occurrences = g_slist_append (appointment->occurrences, occurrence);
+ *(data->pappointments) = g_slist_prepend (*(data->pappointments), appointment);
return TRUE;
}
-
/* ---------------------------------------------------------------------------------------------------- */
struct _App
@@ -445,18 +289,19 @@ struct _App
icaltimezone *zone;
CalendarSources *sources;
- gulong sources_signal_id;
+ gulong client_appeared_signal_id;
+ gulong client_disappeared_signal_id;
- /* hash from uid to CalendarAppointment objects */
- GHashTable *appointments;
+ EReminderWatcher *reminder_watcher;
gchar *timezone_location;
- guint changed_timeout_id;
-
- gboolean cache_invalid;
+ GSList *notify_appointments; /* CalendarAppointment *, for EventsAdded */
+ GSList *notify_ids; /* gchar *, for EventsRemoved */
+ guint events_added_timeout_id;
+ guint events_removed_timeout_id;
- GList *live_views;
+ GSList *live_views;
};
static void
@@ -482,64 +327,196 @@ app_update_timezone (App *app)
}
static gboolean
-on_app_schedule_changed_cb (gpointer user_data)
+on_app_schedule_events_added_cb (gpointer user_data)
{
App *app = user_data;
- print_debug ("Emitting changed");
+ GVariantBuilder builder, extras_builder;
+ GSList *events, *link;
+
+ if (g_source_is_destroyed (g_main_current_source ()))
+ return FALSE;
+
+ events = g_slist_reverse (app->notify_appointments);
+ app->notify_appointments = NULL;
+ app->events_added_timeout_id = 0;
+
+ print_debug ("Emitting EventsAdded with %d events", g_slist_length (events));
+
+ if (!events)
+ return FALSE;
+
+ /* The a{sv} is used as an escape hatch in case we want to provide more
+ * information in the future without breaking ABI
+ */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssbxxa{sv})"));
+ for (link = events; link; link = g_slist_next (link))
+ {
+ CalendarAppointment *appt = link->data;
+ time_t start_time = appt->start_time;
+ time_t end_time = appt->end_time;
+
+ if ((start_time >= app->since &&
+ start_time < app->until) ||
+ (start_time <= app->since &&
+ (end_time - 1) > app->since))
+ {
+ g_variant_builder_init (&extras_builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder,
+ "(ssbxxa{sv})",
+ appt->id,
+ appt->summary != NULL ? appt->summary : "",
+ (gboolean) appt->is_all_day,
+ (gint64) start_time,
+ (gint64) end_time,
+ extras_builder);
+ g_variant_builder_clear (&extras_builder);
+ }
+ }
+
g_dbus_connection_emit_signal (app->connection,
NULL, /* destination_bus_name */
"/org/gnome/Shell/CalendarServer",
"org.gnome.Shell.CalendarServer",
- "Changed",
- NULL, /* no params */
+ "EventsAdded",
+ g_variant_new ("(a(ssbxxa{sv}))", &builder),
NULL);
- app->changed_timeout_id = 0;
+ g_variant_builder_clear (&builder);
+
+ g_slist_free_full (events, calendar_appointment_free);
+
return FALSE;
}
static void
-app_schedule_changed (App *app)
+app_schedule_events_added (App *app)
{
- print_debug ("Scheduling changed");
- if (app->changed_timeout_id == 0)
+ print_debug ("Scheduling EventsAdded");
+ if (app->events_added_timeout_id == 0)
{
- app->changed_timeout_id = g_timeout_add (2000,
- on_app_schedule_changed_cb,
- app);
- g_source_set_name_by_id (app->changed_timeout_id, "[gnome-shell] on_app_schedule_changed_cb");
+ app->events_added_timeout_id = g_timeout_add_seconds (2,
+ on_app_schedule_events_added_cb,
+ app);
+ g_source_set_name_by_id (app->events_added_timeout_id, "[gnome-shell]
on_app_schedule_events_added_cb");
}
}
+static gboolean
+on_app_schedule_events_removed_cb (gpointer user_data)
+{
+ App *app = user_data;
+ GVariantBuilder builder;
+ GSList *ids, *link;
+
+ if (g_source_is_destroyed (g_main_current_source ()))
+ return FALSE;
+
+ ids = app->notify_ids;
+ app->notify_ids = NULL;
+ app->events_removed_timeout_id = 0;
+
+ print_debug ("Emitting EventsRemoved with %d ids", g_slist_length (ids));
+
+ if (!ids)
+ return FALSE;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+ for (link = ids; link; link = g_slist_next (link))
+ {
+ const gchar *id = link->data;
+
+ g_variant_builder_add (&builder, "s", id);
+ }
+
+ g_dbus_connection_emit_signal (app->connection,
+ NULL, /* destination_bus_name */
+ "/org/gnome/Shell/CalendarServer",
+ "org.gnome.Shell.CalendarServer",
+ "EventsRemoved",
+ g_variant_new ("(as)", &builder),
+ NULL);
+ g_variant_builder_clear (&builder);
+
+ g_slist_free_full (ids, g_free);
+
+ return FALSE;
+}
+
static void
-invalidate_cache (App *app)
+app_schedule_events_removed (App *app)
{
- app->cache_invalid = TRUE;
+ print_debug ("Scheduling EventsRemoved");
+ if (app->events_removed_timeout_id == 0)
+ {
+ app->events_removed_timeout_id = g_timeout_add_seconds (2,
+ on_app_schedule_events_removed_cb,
+ app);
+ g_source_set_name_by_id (app->events_removed_timeout_id, "[gnome-shell]
on_app_schedule_events_removed_cb");
+ }
}
static void
-on_objects_added (ECalClientView *view,
- GSList *objects,
- gpointer user_data)
+app_process_added_modified_objects (App *app,
+ ECalClientView *view,
+ GSList *objects) /* icalcomponent * */
{
- App *app = user_data;
- GSList *l;
+ ECalClient *cal_client;
+ GSList *link;
+ gboolean expand_recurrences;
- print_debug ("%s for calendar", G_STRFUNC);
+ cal_client = e_cal_client_view_ref_client (view);
+ expand_recurrences = e_cal_client_get_source_type (cal_client) == E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
- for (l = objects; l != NULL; l = l->next)
+ for (link = objects; link; link = g_slist_next (link))
{
- icalcomponent *ical = l->data;
- const char *uid;
+ ECalComponent *comp;
+ icalcomponent *icomp = link->data;
- uid = icalcomponent_get_uid (ical);
+ if (!icomp || !icalcomponent_get_uid (icomp))
+ continue;
- if (g_hash_table_lookup (app->appointments, uid) == NULL)
+ if (expand_recurrences &&
+ !e_cal_util_component_is_instance (icomp) &&
+ e_cal_util_component_has_recurrences (icomp))
{
- /* new appointment we don't know about => changed signal */
- invalidate_cache (app);
- app_schedule_changed (app);
+ CollectAppointmentsData data;
+
+ data.client = cal_client;
+ data.pappointments = &app->notify_appointments;
+
+ e_cal_client_generate_instances_for_object_sync (cal_client, icomp, app->since, app->until,
+ generate_instances_cb, &data);
+ }
+ else
+ {
+ comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icomp));
+ if (!comp)
+ continue;
+
+ app->notify_appointments = g_slist_prepend (app->notify_appointments,
+ calendar_appointment_new (cal_client, comp));
+ g_object_unref (comp);
}
}
+
+ g_clear_object (&cal_client);
+
+ if (app->notify_appointments)
+ app_schedule_events_added (app);
+}
+
+static void
+on_objects_added (ECalClientView *view,
+ GSList *objects,
+ gpointer user_data)
+{
+ App *app = user_data;
+ ECalClient *client;
+
+ client = e_cal_client_view_ref_client (view);
+ print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (objects), e_source_get_uid
(e_client_get_source (E_CLIENT (client))));
+ g_clear_object (&client);
+
+ app_process_added_modified_objects (app, view, objects);
}
static void
@@ -548,9 +525,13 @@ on_objects_modified (ECalClientView *view,
gpointer user_data)
{
App *app = user_data;
- print_debug ("%s for calendar", G_STRFUNC);
- invalidate_cache (app);
- app_schedule_changed (app);
+ ECalClient *client;
+
+ client = e_cal_client_view_ref_client (view);
+ print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (objects), e_source_get_uid
(e_client_get_source (E_CLIENT (client))));
+ g_clear_object (&client);
+
+ app_process_added_modified_objects (app, view, objects);
}
static void
@@ -559,41 +540,55 @@ on_objects_removed (ECalClientView *view,
gpointer user_data)
{
App *app = user_data;
- print_debug ("%s for calendar", G_STRFUNC);
- invalidate_cache (app);
- app_schedule_changed (app);
+ ECalClient *client;
+ GSList *link;
+ const gchar *source_uid;
+
+ client = e_cal_client_view_ref_client (view);
+ source_uid = e_source_get_uid (e_client_get_source (E_CLIENT (client)));
+
+ print_debug ("%s (%d) for calendar '%s'", G_STRFUNC, g_slist_length (uids), source_uid);
+
+ for (link = uids; link; link = g_slist_next (link))
+ {
+ ECalComponentId *id = link->data;
+
+ if (!id)
+ continue;
+
+ app->notify_ids = g_slist_prepend (app->notify_ids, create_event_id (source_uid, id->uid, id->rid));
+ }
+
+ g_clear_object (&client);
+
+ if (app->notify_ids)
+ app_schedule_events_removed (app);
}
-static void
-app_load_events (App *app)
+static gboolean
+app_has_calendars (App *app)
+{
+ return app->live_views != NULL;
+}
+
+static ECalClientView *
+app_start_view (App *app,
+ ECalClient *cal_client)
{
- GList *clients;
- GList *l;
- GList *ll;
gchar *since_iso8601;
gchar *until_iso8601;
gchar *query;
const char *tz_location;
+ ECalClientView *view = NULL;
+ GError *error = NULL;
- /* out with the old */
- g_hash_table_remove_all (app->appointments);
- /* nuke existing views */
- for (ll = app->live_views; ll != NULL; ll = ll->next)
- {
- ECalClientView *view = E_CAL_CLIENT_VIEW (ll->data);
- g_signal_handlers_disconnect_by_func (view, on_objects_added, app);
- g_signal_handlers_disconnect_by_func (view, on_objects_modified, app);
- g_signal_handlers_disconnect_by_func (view, on_objects_removed, app);
- e_cal_client_view_stop (view, NULL);
- g_object_unref (view);
- }
- g_list_free (app->live_views);
- app->live_views = NULL;
+ if (app->since <= 0 || app->since >= app->until)
+ return NULL;
if (!app->since || !app->until)
{
print_debug ("Skipping load of events, no time interval set yet");
- return;
+ return NULL;
}
/* timezone could have changed */
app_update_timezone (app);
@@ -602,9 +597,10 @@ app_load_events (App *app)
until_iso8601 = isodate_from_time_t (app->until);
tz_location = icaltimezone_get_location (app->zone);
- print_debug ("Loading events since %s until %s",
+ print_debug ("Loading events since %s until %s for calendar '%s'",
since_iso8601,
- until_iso8601);
+ until_iso8601,
+ e_source_get_uid (e_client_get_source (E_CLIENT (cal_client))));
query = g_strdup_printf ("occur-in-time-range? (make-time \"%s\") "
"(make-time \"%s\") \"%s\"",
@@ -612,122 +608,211 @@ app_load_events (App *app)
until_iso8601,
tz_location);
- clients = calendar_sources_get_appointment_clients (app->sources);
- for (l = clients; l != NULL; l = l->next)
+ e_cal_client_set_default_timezone (cal_client, app->zone);
+
+ if (!e_cal_client_get_view_sync (cal_client, query, &view, NULL /* cancellable */, &error))
+ {
+ g_warning ("Error setting up live-query '%s' on calendar: %s\n", query, error ? error->message :
"Unknown error");
+ g_clear_error (&error);
+ view = NULL;
+ }
+ else
+ {
+ g_signal_connect (view,
+ "objects-added",
+ G_CALLBACK (on_objects_added),
+ app);
+ g_signal_connect (view,
+ "objects-modified",
+ G_CALLBACK (on_objects_modified),
+ app);
+ g_signal_connect (view,
+ "objects-removed",
+ G_CALLBACK (on_objects_removed),
+ app);
+ e_cal_client_view_start (view, NULL);
+ }
+
+ g_free (since_iso8601);
+ g_free (until_iso8601);
+ g_free (query);
+
+ return view;
+}
+
+static void
+app_stop_view (App *app,
+ ECalClientView *view)
+{
+ e_cal_client_view_stop (view, NULL);
+
+ g_signal_handlers_disconnect_by_func (view, on_objects_added, app);
+ g_signal_handlers_disconnect_by_func (view, on_objects_modified, app);
+ g_signal_handlers_disconnect_by_func (view, on_objects_removed, app);
+}
+
+static void
+app_update_views (App *app)
+{
+ GSList *link, *clients;
+
+ for (link = app->live_views; link; link = g_slist_next (link))
{
- ECalClient *cal = E_CAL_CLIENT (l->data);
- GError *error;
+ app_stop_view (app, link->data);
+ }
+
+ g_slist_free_full (app->live_views, g_object_unref);
+ app->live_views = NULL;
+
+ clients = calendar_sources_ref_clients (app->sources);
+
+ for (link = clients; link; link = g_slist_next (link))
+ {
+ ECalClient *cal_client = link->data;
ECalClientView *view;
- CollectAppointmentsData data;
- e_cal_client_set_default_timezone (cal, app->zone);
+ if (!cal_client)
+ continue;
- error = NULL;
- if (!e_client_open_sync (E_CLIENT (cal), TRUE, NULL, &error))
- {
- ESource *source = e_client_get_source (E_CLIENT (cal));
- g_warning ("Error opening calendar %s: %s\n",
- e_source_get_uid (source), error->message);
- g_error_free (error);
- continue;
- }
+ view = app_start_view (app, cal_client);
+ if (view)
+ app->live_views = g_slist_prepend (app->live_views, view);
+ }
- data.client = cal;
- data.appointments = app->appointments;
- e_cal_client_generate_instances_sync (cal,
- app->since,
- app->until,
- generate_instances_cb,
- &data);
-
- error = NULL;
- if (!e_cal_client_get_view_sync (cal,
- query,
- &view,
- NULL, /* cancellable */
- &error))
- {
- g_warning ("Error setting up live-query on calendar: %s\n", error->message);
- g_error_free (error);
- }
- else
+ g_slist_free_full (clients, g_object_unref);
+}
+
+static void
+app_notify_has_calendars (App *app)
+{
+ GVariantBuilder dict_builder;
+
+ g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&dict_builder, "{sv}", "HasCalendars",
+ g_variant_new_boolean (app_has_calendars (app)));
+
+ g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
+ NULL,
+ "/org/gnome/Shell/CalendarServer",
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ g_variant_new ("(sa{sv}as)",
+ "org.gnome.Shell.CalendarServer",
+ &dict_builder,
+ NULL),
+ NULL);
+ g_variant_builder_clear (&dict_builder);
+}
+
+static void
+on_client_appeared_cb (CalendarSources *sources,
+ ECalClient *client,
+ gpointer user_data)
+{
+ App *app = user_data;
+ ECalClientView *view;
+ GSList *link;
+ const gchar *source_uid;
+
+ source_uid = e_source_get_uid (e_client_get_source (E_CLIENT (client)));
+
+ print_debug ("Client appeared '%s'", source_uid);
+
+ for (link = app->live_views; link; link = g_slist_next (link))
+ {
+ ECalClientView *view = link->data;
+ ECalClient *cal_client;
+ ESource *source;
+
+ cal_client = e_cal_client_view_ref_client (view);
+ source = e_client_get_source (E_CLIENT (cal_client));
+
+ if (g_strcmp0 (source_uid, e_source_get_uid (source)) == 0)
{
- g_signal_connect (view,
- "objects-added",
- G_CALLBACK (on_objects_added),
- app);
- g_signal_connect (view,
- "objects-modified",
- G_CALLBACK (on_objects_modified),
- app);
- g_signal_connect (view,
- "objects-removed",
- G_CALLBACK (on_objects_removed),
- app);
- e_cal_client_view_start (view, NULL);
- app->live_views = g_list_prepend (app->live_views, view);
+ g_clear_object (&cal_client);
+ return;
}
+
+ g_clear_object (&cal_client);
}
- g_list_free (clients);
- g_free (since_iso8601);
- g_free (until_iso8601);
- g_free (query);
- app->cache_invalid = FALSE;
-}
-static gboolean
-app_has_calendars (App *app)
-{
- return calendar_sources_has_sources (app->sources);
+ view = app_start_view (app, client);
+
+ if (view)
+ {
+ app->live_views = g_slist_prepend (app->live_views, view);
+
+ /* It's the first view, notify that it has calendars now */
+ if (!g_slist_next (app->live_views))
+ app_notify_has_calendars (app);
+ }
}
static void
-on_appointment_sources_changed (CalendarSources *sources,
- gpointer user_data)
+on_client_disappeared_cb (CalendarSources *sources,
+ const gchar *source_uid,
+ gpointer user_data)
{
App *app = user_data;
+ GSList *link;
+
+ print_debug ("Client disappeared '%s'", source_uid);
+
+ for (link = app->live_views; link; link = g_slist_next (link))
+ {
+ ECalClientView *view = link->data;
+ ECalClient *cal_client;
+ ESource *source;
+
+ cal_client = e_cal_client_view_ref_client (view);
+ source = e_client_get_source (E_CLIENT (cal_client));
+
+ if (g_strcmp0 (source_uid, e_source_get_uid (source)) == 0)
+ {
+ g_clear_object (&cal_client);
+ app_stop_view (app, view);
+ app->live_views = g_slist_remove (app->live_views, view);
+ g_object_unref (view);
+
+ print_debug ("Emitting ClientDisappeared for '%s'", source_uid);
+
+ g_dbus_connection_emit_signal (app->connection,
+ NULL, /* destination_bus_name */
+ "/org/gnome/Shell/CalendarServer",
+ "org.gnome.Shell.CalendarServer",
+ "ClientDisappeared",
+ g_variant_new ("(s)", source_uid),
+ NULL);
- print_debug ("Sources changed\n");
- app_load_events (app);
-
- /* Notify the HasCalendars property */
- {
- GVariantBuilder dict_builder;
-
- g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sv}"));
- g_variant_builder_add (&dict_builder, "{sv}", "HasCalendars",
- g_variant_new_boolean (app_has_calendars (app)));
-
- g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
- NULL,
- "/org/gnome/Shell/CalendarServer",
- "org.freedesktop.DBus.Properties",
- "PropertiesChanged",
- g_variant_new ("(sa{sv}as)",
- "org.gnome.Shell.CalendarServer",
- &dict_builder,
- NULL),
- NULL);
- }
+ /* It was the last view, notify that it doesn't have calendars now */
+ if (!app->live_views)
+ app_notify_has_calendars (app);
+
+ break;
+ }
+
+ g_clear_object (&cal_client);
+ }
}
static App *
-app_new (GDBusConnection *connection)
+app_new (GApplication *application,
+ GDBusConnection *connection)
{
App *app;
app = g_new0 (App, 1);
app->connection = g_object_ref (connection);
app->sources = calendar_sources_get ();
- app->sources_signal_id = g_signal_connect (app->sources,
- "appointment-sources-changed",
- G_CALLBACK (on_appointment_sources_changed),
- app);
-
- app->appointments = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) calendar_appointment_free);
+ app->reminder_watcher = reminder_watcher_new (application, calendar_sources_get_registry (app->sources));
+ app->client_appeared_signal_id = g_signal_connect (app->sources,
+ "client-appeared",
+ G_CALLBACK (on_client_appeared_cb),
+ app);
+ app->client_disappeared_signal_id = g_signal_connect (app->sources,
+ "client-disappeared",
+ G_CALLBACK (on_client_disappeared_cb),
+ app);
app_update_timezone (app);
@@ -737,30 +822,36 @@ app_new (GDBusConnection *connection)
static void
app_free (App *app)
{
- GList *ll;
- for (ll = app->live_views; ll != NULL; ll = ll->next)
+ GSList *ll;
+
+ if (app->events_added_timeout_id != 0)
+ g_source_remove (app->events_added_timeout_id);
+
+ if (app->events_removed_timeout_id != 0)
+ g_source_remove (app->events_removed_timeout_id);
+
+ for (ll = app->live_views; ll != NULL; ll = g_slist_next (ll))
{
ECalClientView *view = E_CAL_CLIENT_VIEW (ll->data);
- g_signal_handlers_disconnect_by_func (view, on_objects_added, app);
- g_signal_handlers_disconnect_by_func (view, on_objects_modified, app);
- g_signal_handlers_disconnect_by_func (view, on_objects_removed, app);
- e_cal_client_view_stop (view, NULL);
- g_object_unref (view);
+
+ app_stop_view (app, view);
}
- g_list_free (app->live_views);
+
+ g_signal_handler_disconnect (app->sources,
+ app->client_appeared_signal_id);
+ g_signal_handler_disconnect (app->sources,
+ app->client_disappeared_signal_id);
g_free (app->timezone_location);
- g_hash_table_unref (app->appointments);
+ g_slist_free_full (app->live_views, g_object_unref);
+ g_slist_free_full (app->notify_appointments, calendar_appointment_free);
+ g_slist_free_full (app->notify_ids, g_free);
g_object_unref (app->connection);
- g_signal_handler_disconnect (app->sources,
- app->sources_signal_id);
+ g_object_unref (app->reminder_watcher);
g_object_unref (app->sources);
- if (app->changed_timeout_id != 0)
- g_source_remove (app->changed_timeout_id);
-
g_free (app);
}
@@ -778,15 +869,12 @@ handle_method_call (GDBusConnection *connection,
{
App *app = user_data;
- if (g_strcmp0 (method_name, "GetEvents") == 0)
+ if (g_strcmp0 (method_name, "SetTimeRange") == 0)
{
- GVariantBuilder builder;
- GHashTableIter hash_iter;
- CalendarAppointment *a;
gint64 since;
gint64 until;
- gboolean force_reload;
- gboolean window_changed;
+ gboolean force_reload = FALSE;
+ gboolean window_changed = FALSE;
g_variant_get (parameters,
"(xxb)",
@@ -802,13 +890,12 @@ handle_method_call (GDBusConnection *connection,
goto out;
}
- print_debug ("Handling GetEvents (since=%" G_GINT64_FORMAT ", until=%" G_GINT64_FORMAT ",
force_reload=%s)",
+ print_debug ("Handling SetTimeRange (since=%" G_GINT64_FORMAT ", until=%" G_GINT64_FORMAT ",
force_reload=%s)",
since,
until,
force_reload ? "true" : "false");
- window_changed = FALSE;
- if (!(app->until == until && app->since == since))
+ if (app->until != until || app->since != since)
{
GVariantBuilder *builder;
GVariantBuilder *invalidated_builder;
@@ -833,61 +920,15 @@ handle_method_call (GDBusConnection *connection,
builder,
invalidated_builder),
NULL); /* GError** */
- }
- /* reload events if necessary */
- if (window_changed || force_reload || app->cache_invalid)
- {
- app_load_events (app);
+ g_variant_builder_unref (builder);
+ g_variant_builder_unref (invalidated_builder);
}
- /* The a{sv} is used as an escape hatch in case we want to provide more
- * information in the future without breaking ABI
- */
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssbxxa{sv})"));
- g_hash_table_iter_init (&hash_iter, app->appointments);
- while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &a))
- {
- GVariantBuilder extras_builder;
- GSList *l;
-
- for (l = a->occurrences; l; l = l->next)
- {
- CalendarOccurrence *o = l->data;
- time_t start_time = o->start_time;
- time_t end_time = o->end_time;
-
- if ((start_time >= app->since &&
- start_time < app->until) ||
- (start_time <= app->since &&
- (end_time - 1) > app->since))
- {
- /* While the UID is usually enough to identify an event,
- * only the triple of (source,UID,RID) is fully unambiguous;
- * neither may contain '\n', so we can safely use it to
- * create a unique ID from the triple
- */
- char *id = g_strdup_printf ("%s\n%s\n%s",
- a->source_id,
- a->uid,
- o->rid ? o->rid : "");
-
- g_variant_builder_init (&extras_builder, G_VARIANT_TYPE ("a{sv}"));
- g_variant_builder_add (&builder,
- "(sssbxxa{sv})",
- id,
- a->summary != NULL ? a->summary : "",
- a->description != NULL ? a->description : "",
- (gboolean) a->is_all_day,
- (gint64) start_time,
- (gint64) end_time,
- extras_builder);
- g_free (id);
- }
- }
- }
- g_dbus_method_invocation_return_value (invocation,
- g_variant_new ("(a(sssbxxa{sv}))", &builder));
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ if (window_changed || force_reload)
+ app_update_views (app);
}
else
{
@@ -942,12 +983,11 @@ on_bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- GError *error;
+ GApplication *application = user_data;
guint registration_id;
+ GError *error = NULL;
- _global_app = app_new (connection);
-
- error = NULL;
+ _global_app = app_new (application, connection);
registration_id = g_dbus_connection_register_object (connection,
"/org/gnome/Shell/CalendarServer",
introspection_data->interfaces[0],
@@ -957,16 +997,16 @@ on_bus_acquired (GDBusConnection *connection,
&error);
if (registration_id == 0)
{
- g_printerr ("Error exporting object: %s (%s %d)",
+ g_printerr ("Error exporting object: %s (%s %d)\n",
error->message,
g_quark_to_string (error->domain),
error->code);
g_error_free (error);
- _exit (1);
+ g_application_quit (application);
+ return;
}
print_debug ("Connected to the session bus");
-
}
static void
@@ -974,11 +1014,11 @@ on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- GMainLoop *main_loop = user_data;
+ GApplication *application = user_data;
g_print ("gnome-shell-calendar-server[%d]: Lost (or failed to acquire) the name " BUS_NAME " - exiting\n",
(gint) getpid ());
- g_main_loop_quit (main_loop);
+ g_application_quit (application);
}
static void
@@ -994,13 +1034,13 @@ stdin_channel_io_func (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
- GMainLoop *main_loop = data;
+ GApplication *application = data;
if (condition & G_IO_HUP)
{
g_debug ("gnome-shell-calendar-server[%d]: Got HUP on stdin - exiting\n",
(gint) getpid ());
- g_main_loop_quit (main_loop);
+ g_application_quit (application);
}
else
{
@@ -1015,38 +1055,41 @@ main (int argc,
{
GError *error;
GOptionContext *opt_context;
- GMainLoop *main_loop;
gint ret;
guint name_owner_id;
GIOChannel *stdin_channel;
+ GApplication *application;
ret = 1;
opt_context = NULL;
name_owner_id = 0;
stdin_channel = NULL;
+ error = NULL;
introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
g_assert (introspection_data != NULL);
opt_context = g_option_context_new ("gnome-shell calendar server");
g_option_context_add_main_entries (opt_context, opt_entries, NULL);
- error = NULL;
if (!g_option_context_parse (opt_context, &argc, &argv, &error))
{
- g_printerr ("Error parsing options: %s", error->message);
+ g_printerr ("Error parsing options: %s\n", error->message);
g_error_free (error);
goto out;
}
- main_loop = g_main_loop_new (NULL, FALSE);
+ application = g_application_new (BUS_NAME, G_APPLICATION_NON_UNIQUE);
+
+ g_signal_connect (application, "activate",
+ G_CALLBACK (g_application_hold), NULL);
stdin_channel = g_io_channel_unix_new (STDIN_FILENO);
g_io_add_watch_full (stdin_channel,
G_PRIORITY_DEFAULT,
G_IO_HUP,
stdin_channel_io_func,
- g_main_loop_ref (main_loop),
- (GDestroyNotify) g_main_loop_unref);
+ application,
+ (GDestroyNotify) NULL);
name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
BUS_NAME,
@@ -1055,14 +1098,22 @@ main (int argc,
on_bus_acquired,
on_name_acquired,
on_name_lost,
- g_main_loop_ref (main_loop),
- (GDestroyNotify) g_main_loop_unref);
+ application,
+ (GDestroyNotify) NULL);
- g_main_loop_run (main_loop);
+ if (g_application_register (application, NULL, &error))
+ {
+ print_debug ("Registered application");
- g_main_loop_unref (main_loop);
+ ret = g_application_run (application, argc, argv);
- ret = 0;
+ print_debug ("Quit application");
+ }
+ else
+ {
+ g_printerr ("Failed to register application: %s\n", error->message);
+ g_clear_error (&error);
+ }
out:
if (stdin_channel != NULL)
@@ -1073,6 +1124,8 @@ main (int argc,
g_bus_unown_name (name_owner_id);
if (opt_context != NULL)
g_option_context_free (opt_context);
+ g_clear_object (&application);
+
return ret;
}
diff --git a/src/calendar-server/meson.build b/src/calendar-server/meson.build
index a7393fce5..d6aa9be26 100644
--- a/src/calendar-server/meson.build
+++ b/src/calendar-server/meson.build
@@ -2,7 +2,9 @@ calendar_sources = [
'gnome-shell-calendar-server.c',
'calendar-debug.h',
'calendar-sources.c',
- 'calendar-sources.h'
+ 'calendar-sources.h',
+ 'reminder-watcher.c',
+ 'reminder-watcher.h'
]
calendar_server = executable('gnome-shell-calendar-server', calendar_sources,
diff --git a/src/calendar-server/reminder-watcher.c b/src/calendar-server/reminder-watcher.c
new file mode 100644
index 000000000..8ac1b64e3
--- /dev/null
+++ b/src/calendar-server/reminder-watcher.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <libecal/libecal.h>
+
+#include "calendar-sources.h"
+#include "reminder-watcher.h"
+
+struct _ReminderWatcherPrivate {
+ GApplication *application; /* not referenced */
+ CalendarSources *sources;
+ GSettings *settings;
+
+ GMutex dismiss_lock;
+ GSList *dismiss; /* EReminderData * */
+ GThread *dismiss_thread; /* not referenced, only to know whether it's scheduled */
+};
+
+G_DEFINE_TYPE (ReminderWatcher, reminder_watcher, E_TYPE_REMINDER_WATCHER)
+
+static gboolean
+reminder_watcher_notify_audio (ReminderWatcher *rw,
+ const EReminderData *rd,
+ ECalComponentAlarm *alarm)
+{
+#if 0
+ icalattach *attach = NULL;
+ gboolean did_play = FALSE;
+
+ g_return_val_if_fail (rw != NULL, FALSE);
+ g_return_val_if_fail (rd != NULL, FALSE);
+ g_return_val_if_fail (alarm != NULL, FALSE);
+
+ e_cal_component_alarm_get_attach (alarm, &attach);
+
+ if (attach && icalattach_get_is_url (attach))
+ {
+ const gchar *url;
+
+ url = icalattach_get_url (attach);
+ if (url && *url)
+ {
+ gchar *filename;
+ GError *error = NULL;
+
+ filename = g_filename_from_uri (url, NULL, &error);
+
+ if (error != NULL)
+ {
+ g_warning ("%s: Failed to convert URI to filename: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+ else if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
+ {
+#ifdef HAVE_CANBERRA
+ did_play = ca_context_play (ca_gtk_context_get (), 0,
+ CA_PROP_MEDIA_FILENAME, filename,
+ NULL) == 0;
+#endif
+ }
+
+ g_free (filename);
+ }
+ }
+
+ if (!did_play)
+ gdk_beep ();
+
+ if (attach)
+ icalattach_unref (attach);
+
+#endif
+
+ return FALSE;
+}
+
+static gchar *
+reminder_watcher_build_notif_id (const EReminderData *rd)
+{
+ GString *string;
+ ECalComponentId *id;
+
+ g_return_val_if_fail (rd != NULL, NULL);
+
+ string = g_string_sized_new (32);
+
+ if (rd->source_uid)
+ {
+ g_string_append (string, rd->source_uid);
+ g_string_append (string, "\n");
+ }
+
+ id = e_cal_component_get_id (rd->component);
+ if (id)
+ {
+ if (id->uid)
+ {
+ g_string_append (string, id->uid);
+ g_string_append (string, "\n");
+ }
+
+ if (id->rid)
+ {
+ g_string_append (string, id->rid);
+ g_string_append (string, "\n");
+ }
+
+ e_cal_component_free_id (id);
+ }
+
+ g_string_append_printf (string, "%" G_GINT64_FORMAT, rd->instance.trigger);
+
+ return g_string_free (string, FALSE);
+}
+
+static gboolean
+reminder_watcher_notify_display (ReminderWatcher *rw,
+ const EReminderData *rd,
+ ECalComponentAlarm *alarm)
+{
+ GNotification *notification;
+#if 0
+ GtkIconInfo *icon_info;
+#endif
+ gchar *description, *notif_id;
+
+ g_return_val_if_fail (rw != NULL, FALSE);
+ g_return_val_if_fail (rd != NULL, FALSE);
+ g_return_val_if_fail (alarm != NULL, FALSE);
+
+ notif_id = reminder_watcher_build_notif_id (rd);
+ description = e_reminder_watcher_describe_data (E_REMINDER_WATCHER (rw), rd,
E_REMINDER_WATCHER_DESCRIBE_FLAG_NONE);
+
+ notification = g_notification_new (_("Reminders"));
+ g_notification_set_body (notification, description);
+
+#if 0
+ icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (), "appointment-soon",
GTK_ICON_SIZE_DIALOG, 0);
+ if (icon_info)
+ {
+ const gchar *filename;
+
+ filename = gtk_icon_info_get_filename (icon_info);
+ if (filename && *filename)
+ {
+ GFile *file;
+ GIcon *icon;
+
+ file = g_file_new_for_path (filename);
+ icon = g_file_icon_new (file);
+
+ if (icon)
+ {
+ g_notification_set_icon (notification, icon);
+ g_object_unref (icon);
+ }
+
+ g_object_unref (file);
+ }
+
+ gtk_icon_info_free (icon_info);
+ }
+#endif
+
+ g_application_send_notification (rw->priv->application, notif_id, notification);
+
+ g_object_unref (notification);
+ g_free (description);
+ g_free (notif_id);
+
+ return TRUE;
+}
+
+static gboolean
+reminder_watcher_notify_email (ReminderWatcher *rw,
+ const EReminderData *rd,
+ ECalComponentAlarm *alarm)
+{
+ /* Nothing to do here */
+ return FALSE;
+}
+
+static gboolean
+reminder_watcher_is_blessed_program (GSettings *settings,
+ const gchar *url)
+{
+ gchar **list;
+ gint ii;
+ gboolean found = FALSE;
+
+ g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (url != NULL, FALSE);
+
+ list = g_settings_get_strv (settings, "notify-programs");
+
+ for (ii = 0; list && list[ii] && !found; ii++)
+ {
+ found = g_strcmp0 (list[ii], url) == 0;
+ }
+
+ g_strfreev (list);
+
+ return found;
+}
+
+#if 0
+static void
+reminder_watcher_save_blessed_program (GSettings *settings,
+ const gchar *url)
+{
+ gchar **list;
+ gint ii;
+ GPtrArray *array;
+
+ g_return_if_fail (G_IS_SETTINGS (settings));
+ g_return_if_fail (url != NULL);
+
+ array = g_ptr_array_new ();
+
+ list = g_settings_get_strv (settings, "notify-programs");
+
+ for (ii = 0; list && list[ii]; ii++)
+ {
+ if (g_strcmp0 (url, list[ii]) != 0)
+ g_ptr_array_add (array, list[ii]);
+ }
+
+ g_ptr_array_add (array, (gpointer) url);
+ g_ptr_array_add (array, NULL);
+
+ g_settings_set_strv (settings, "notify-programs", (const gchar * const *) array->pdata);
+
+ g_ptr_array_free (array, TRUE);
+ g_strfreev (list);
+}
+#endif
+
+static gboolean
+reminder_watcher_can_procedure (ReminderWatcher *rw,
+ const gchar *cmd,
+ const gchar *url)
+{
+#if 0
+ GtkWidget *container;
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *checkbox;
+ gchar *str;
+ gint response;
+#endif
+
+ if (reminder_watcher_is_blessed_program (rw->priv->settings, url))
+ return TRUE;
+
+#if 0
+ dialog = gtk_dialog_new_with_buttons (
+ _("Warning"), GTK_WINDOW (rw->priv->window), 0,
+ _("_No"), GTK_RESPONSE_CANCEL,
+ _("_Yes"), GTK_RESPONSE_OK,
+ NULL);
+
+ str = g_strdup_printf (
+ _("A calendar reminder is about to trigger. "
+ "This reminder is configured to run the following program:\n\n"
+ " %s\n\n"
+ "Are you sure you want to run this program?"),
+ cmd);
+ label = gtk_label_new (str);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_widget_show (label);
+
+ container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_box_pack_start (GTK_BOX (container), label, TRUE, TRUE, 4);
+ g_free (str);
+
+ checkbox = gtk_check_button_new_with_label (_("Do not ask me about this program again"));
+ gtk_widget_show (checkbox);
+ gtk_box_pack_start (GTK_BOX (container), checkbox, TRUE, TRUE, 4);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response == GTK_RESPONSE_OK &&
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox)))
+ {
+ reminder_watcher_save_blessed_program (rw->priv->settings, url);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return response == GTK_RESPONSE_OK;
+#endif
+
+ return FALSE;
+}
+
+static gboolean
+reminder_watcher_notify_procedure (ReminderWatcher *rw,
+ const EReminderData *rd,
+ ECalComponentAlarm *alarm)
+{
+ ECalComponentText description;
+ icalattach *attach;
+ const gchar *url;
+ gchar *cmd;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (rw != NULL, FALSE);
+ g_return_val_if_fail (rd != NULL, FALSE);
+ g_return_val_if_fail (alarm != NULL, FALSE);
+
+ e_cal_component_alarm_get_attach (alarm, &attach);
+ e_cal_component_alarm_get_description (alarm, &description);
+
+ /* If the alarm has no attachment, simply display a notification dialog. */
+ if (!attach)
+ goto fallback;
+
+ if (!icalattach_get_is_url (attach))
+ {
+ icalattach_unref (attach);
+ goto fallback;
+ }
+
+ url = icalattach_get_url (attach);
+ g_return_val_if_fail (url != NULL, FALSE);
+
+ /* Ask for confirmation before executing the stuff */
+ if (description.value)
+ cmd = g_strconcat (url, " ", description.value, NULL);
+ else
+ cmd = (gchar *) url;
+
+ if (reminder_watcher_can_procedure (rw, cmd, url))
+ result = g_spawn_command_line_async (cmd, NULL);
+
+ if (cmd != (gchar *) url)
+ g_free (cmd);
+
+ icalattach_unref (attach);
+
+ /* Fall back to display notification if we got an error */
+ if (!result)
+ goto fallback;
+
+ return FALSE;
+
+ fallback:
+ return reminder_watcher_notify_display (rw, rd, alarm);
+}
+
+/* Returns %TRUE to keep it, %FALSE to dismiss it */
+static gboolean
+reminders_process_one (ReminderWatcher *rw,
+ const EReminderData *rd,
+ gboolean snoozed)
+{
+ ECalComponentAlarm *alarm;
+ ECalComponentAlarmAction action;
+ gboolean keep_in_reminders = FALSE;
+
+ g_return_val_if_fail (rw != NULL, FALSE);
+ g_return_val_if_fail (rd != NULL, FALSE);
+
+ if (e_cal_component_get_vtype (rd->component) == E_CAL_COMPONENT_TODO)
+ {
+ icalproperty_status status = ICAL_STATUS_NONE;
+
+ e_cal_component_get_status (rd->component, &status);
+
+ if (status == ICAL_STATUS_COMPLETED &&
+ !g_settings_get_boolean (rw->priv->settings, "notify-completed-tasks"))
+ {
+ return FALSE;
+ }
+ }
+
+ alarm = e_cal_component_get_alarm (rd->component, rd->instance.auid);
+ if (!alarm)
+ return FALSE;
+
+ if (!snoozed && !g_settings_get_boolean (rw->priv->settings, "notify-past-events"))
+ {
+ ECalComponentAlarmTrigger trigger;
+ ECalComponentAlarmRepeat repeat;
+ time_t offset = 0, event_relative, orig_trigger_day, today;
+
+ e_cal_component_alarm_get_trigger (alarm, &trigger);
+ e_cal_component_alarm_get_repeat (alarm, &repeat);
+
+ switch (trigger.type)
+ {
+ case E_CAL_COMPONENT_ALARM_TRIGGER_NONE:
+ case E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE:
+ break;
+
+ case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START:
+ case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END:
+ offset = icaldurationtype_as_int (trigger.u.rel_duration);
+ break;
+
+ default:
+ break;
+ }
+
+ today = time (NULL);
+ event_relative = rd->instance.occur_start - offset;
+
+ #define CLAMP_TO_DAY(x) ((x) - ((x) % (60 * 60 * 24)))
+
+ event_relative = CLAMP_TO_DAY (event_relative);
+ orig_trigger_day = CLAMP_TO_DAY (rd->instance.trigger);
+ today = CLAMP_TO_DAY (today);
+
+ #undef CLAMP_TO_DAY
+
+ if (event_relative < today && orig_trigger_day < today)
+ {
+ e_cal_component_alarm_free (alarm);
+ return FALSE;
+ }
+ }
+
+ e_cal_component_alarm_get_action (alarm, &action);
+
+ switch (action)
+ {
+ case E_CAL_COMPONENT_ALARM_AUDIO:
+ keep_in_reminders = reminder_watcher_notify_audio (rw, rd, alarm);
+ break;
+
+ case E_CAL_COMPONENT_ALARM_DISPLAY:
+ keep_in_reminders = reminder_watcher_notify_display (rw, rd, alarm);
+ break;
+
+ case E_CAL_COMPONENT_ALARM_EMAIL:
+ keep_in_reminders = reminder_watcher_notify_email (rw, rd, alarm);
+ break;
+
+ case E_CAL_COMPONENT_ALARM_PROCEDURE:
+ keep_in_reminders = reminder_watcher_notify_procedure (rw, rd, alarm);
+ break;
+
+ case E_CAL_COMPONENT_ALARM_NONE:
+ case E_CAL_COMPONENT_ALARM_UNKNOWN:
+ break;
+ }
+
+ e_cal_component_alarm_free (alarm);
+
+ return keep_in_reminders;
+}
+
+static gpointer
+reminders_dismiss_thread (gpointer user_data)
+{
+ ReminderWatcher *rw = user_data;
+ EReminderWatcher *watcher;
+ GSList *dismiss, *link;
+
+ g_return_val_if_fail (IS_REMINDER_WATCHER (rw), NULL);
+
+ g_mutex_lock (&rw->priv->dismiss_lock);
+ dismiss = rw->priv->dismiss;
+ rw->priv->dismiss = NULL;
+ rw->priv->dismiss_thread = NULL;
+ g_mutex_unlock (&rw->priv->dismiss_lock);
+
+ watcher = E_REMINDER_WATCHER (rw);
+ if (watcher)
+ {
+ for (link = dismiss; link; link = g_slist_next (link))
+ {
+ EReminderData *rd = link->data;
+
+ if (rd)
+ {
+ /* Silently ignore any errors here */
+ e_reminder_watcher_dismiss_sync (watcher, rd, NULL, NULL);
+ }
+ }
+ }
+
+ g_slist_free_full (dismiss, e_reminder_data_free);
+ g_clear_object (&rw);
+
+ return NULL;
+}
+
+static void
+reminders_triggered_cb (EReminderWatcher *watcher,
+ const GSList *reminders, /* EReminderData * */
+ gboolean snoozed,
+ gpointer user_data)
+{
+ ReminderWatcher *rw = REMINDER_WATCHER (watcher);
+ GSList *link;
+
+ g_return_if_fail (IS_REMINDER_WATCHER (rw));
+
+ g_mutex_lock (&rw->priv->dismiss_lock);
+
+ for (link = (GSList *) reminders; link; link = g_slist_next (link))
+ {
+ const EReminderData *rd = link->data;
+
+ if (rd && !reminders_process_one (rw, rd, snoozed))
+ {
+ rw->priv->dismiss = g_slist_prepend (rw->priv->dismiss, e_reminder_data_copy (rd));
+ }
+ }
+
+ if (rw->priv->dismiss && !rw->priv->dismiss_thread)
+ {
+ rw->priv->dismiss_thread = g_thread_new (NULL, reminders_dismiss_thread, g_object_ref (rw));
+ g_warn_if_fail (rw->priv->dismiss_thread != NULL);
+ if (rw->priv->dismiss_thread)
+ g_thread_unref (rw->priv->dismiss_thread);
+ }
+
+ g_mutex_unlock (&rw->priv->dismiss_lock);
+}
+
+static EClient *
+reminder_watcher_cal_client_connect_sync (EReminderWatcher *watcher,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ReminderWatcher *reminder_watcher = REMINDER_WATCHER (watcher);
+
+ return calendar_sources_connect_client_sync (reminder_watcher->priv->sources, FALSE, source, source_type,
+ wait_for_connected_seconds, cancellable, error);
+}
+
+static void
+reminder_watcher_cal_client_connect (EReminderWatcher *watcher,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ReminderWatcher *reminder_watcher = REMINDER_WATCHER (watcher);
+
+ calendar_sources_connect_client (reminder_watcher->priv->sources, FALSE, source, source_type,
+ wait_for_connected_seconds, cancellable, callback, user_data);
+}
+
+static EClient *
+reminder_watcher_cal_client_connect_finish (EReminderWatcher *watcher,
+ GAsyncResult *result,
+ GError **error)
+{
+ ReminderWatcher *reminder_watcher = REMINDER_WATCHER (watcher);
+
+ return calendar_sources_connect_client_finish (reminder_watcher->priv->sources, result, error);
+}
+
+static void
+reminder_watcher_constructed (GObject *object)
+{
+ ReminderWatcher *rw = REMINDER_WATCHER (object);
+
+ G_OBJECT_CLASS (reminder_watcher_parent_class)->constructed (object);
+
+ rw->priv->sources = calendar_sources_get ();
+
+ g_signal_connect (rw, "triggered",
+ G_CALLBACK (reminders_triggered_cb), NULL);
+}
+
+static void
+reminder_watcher_finalize (GObject *object)
+{
+ ReminderWatcher *rw = REMINDER_WATCHER (object);
+
+ g_clear_object (&rw->priv->sources);
+ g_clear_object (&rw->priv->settings);
+ g_mutex_clear (&rw->priv->dismiss_lock);
+
+ G_OBJECT_CLASS (reminder_watcher_parent_class)->finalize (object);
+}
+
+static void
+reminder_watcher_class_init (ReminderWatcherClass *klass)
+{
+ GObjectClass *object_class;
+ EReminderWatcherClass *watcher_class;
+
+ g_type_class_add_private (klass, sizeof (ReminderWatcherPrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->constructed = reminder_watcher_constructed;
+ object_class->finalize = reminder_watcher_finalize;
+
+ watcher_class = E_REMINDER_WATCHER_CLASS (klass);
+ watcher_class->cal_client_connect_sync = reminder_watcher_cal_client_connect_sync;
+ watcher_class->cal_client_connect = reminder_watcher_cal_client_connect;
+ watcher_class->cal_client_connect_finish = reminder_watcher_cal_client_connect_finish;
+}
+
+static void
+reminder_watcher_init (ReminderWatcher *rw)
+{
+ rw->priv = G_TYPE_INSTANCE_GET_PRIVATE (rw, TYPE_REMINDER_WATCHER, ReminderWatcherPrivate);
+ rw->priv->settings = g_settings_new ("org.gnome.evolution-data-server.calendar");
+
+ g_mutex_init (&rw->priv->dismiss_lock);
+}
+
+EReminderWatcher *
+reminder_watcher_new (GApplication *application,
+ ESourceRegistry *registry)
+{
+ ReminderWatcher *rw;
+
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+ rw = g_object_new (TYPE_REMINDER_WATCHER,
+ "registry", registry,
+ NULL);
+
+ rw->priv->application = application;
+
+ return E_REMINDER_WATCHER (rw);
+}
diff --git a/src/calendar-server/reminder-watcher.h b/src/calendar-server/reminder-watcher.h
new file mode 100644
index 000000000..9d63aa69d
--- /dev/null
+++ b/src/calendar-server/reminder-watcher.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef REMINDER_WATCHER_H
+#define REMINDER_WATCHER_H
+
+#include <libecal/libecal.h>
+
+/* Standard GObject macros */
+#define TYPE_REMINDER_WATCHER \
+ (reminder_watcher_get_type ())
+#define REMINDER_WATCHER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), TYPE_REMINDER_WATCHER, ReminderWatcher))
+#define REMINDER_WATCHER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), TYPE_REMINDER_WATCHER, ReminderWatcherClass))
+#define IS_REMINDER_WATCHER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), TYPE_REMINDER_WATCHER))
+#define IS_REMINDER_WATCHER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), TYPE_REMINDER_WATCHER))
+#define REMINDER_WATCHER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), TYPE_REMINDER_WATCHER, ReminderWatcherClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ReminderWatcher ReminderWatcher;
+typedef struct _ReminderWatcherClass ReminderWatcherClass;
+typedef struct _ReminderWatcherPrivate ReminderWatcherPrivate;
+
+struct _ReminderWatcher {
+ EReminderWatcher parent;
+ ReminderWatcherPrivate *priv;
+};
+
+struct _ReminderWatcherClass {
+ EReminderWatcherClass parent_class;
+};
+
+GType reminder_watcher_get_type (void) G_GNUC_CONST;
+EReminderWatcher *reminder_watcher_new (GApplication *application,
+ ESourceRegistry *registry);
+
+G_END_DECLS
+
+#endif /* REMINDER_WATCHER_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]