Compare commits
	
		
			2 Commits
		
	
	
		
			citadel-45
			...
			155-move-f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					13ddf1eb83 | ||
| 
						 | 
					15ab33a11a | 
@@ -1,12 +1,19 @@
 | 
			
		||||
<node>
 | 
			
		||||
  <interface name="org.gnome.Shell.CalendarServer">
 | 
			
		||||
    <method name="GetEvents">
 | 
			
		||||
      <arg type="x" direction="in" />
 | 
			
		||||
      <arg type="x" direction="in" />
 | 
			
		||||
      <arg type="b" direction="in" />
 | 
			
		||||
      <arg type="a(sssbxxa{sv})" direction="out" />
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
desktop_files = [
 | 
			
		||||
  'org.gnome.Shell.desktop',
 | 
			
		||||
  'org.gnome.Shell.CalendarServer.desktop',
 | 
			
		||||
  'org.gnome.Extensions.desktop',
 | 
			
		||||
]
 | 
			
		||||
service_files = []
 | 
			
		||||
@@ -13,6 +14,7 @@ desktopconf = configuration_data()
 | 
			
		||||
# We substitute in bindir so it works as an autostart
 | 
			
		||||
# file when built in a non-system prefix
 | 
			
		||||
desktopconf.set('bindir', bindir)
 | 
			
		||||
desktopconf.set('libexecdir', libexecdir)
 | 
			
		||||
desktopconf.set('systemd_hidden', have_systemd ? 'true' : 'false')
 | 
			
		||||
 | 
			
		||||
foreach desktop_file : desktop_files
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								data/org.gnome.Shell.CalendarServer.desktop.in.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								data/org.gnome.Shell.CalendarServer.desktop.in.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
[Desktop Entry]
 | 
			
		||||
Type=Application
 | 
			
		||||
Name=Clock Applet
 | 
			
		||||
Icon=appointment-soon
 | 
			
		||||
Exec=@libexecdir@/gnome-shell-calendar-server
 | 
			
		||||
Terminal=false
 | 
			
		||||
Categories=
 | 
			
		||||
OnlyShowIn=GNOME
 | 
			
		||||
NoDisplay=true
 | 
			
		||||
@@ -220,7 +220,9 @@ class DBusEventSource extends EventSourceBase {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
@@ -257,7 +259,7 @@ class DBusEventSource extends EventSourceBase {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _resetCache() {
 | 
			
		||||
        this._events = [];
 | 
			
		||||
        this._events = new Map();
 | 
			
		||||
        this._lastRequestBegin = null;
 | 
			
		||||
        this._lastRequestEnd = null;
 | 
			
		||||
    }
 | 
			
		||||
@@ -273,28 +275,59 @@ class DBusEventSource extends EventSourceBase {
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onChanged() {
 | 
			
		||||
        this._loadEvents(false);
 | 
			
		||||
    }
 | 
			
		||||
    _onEventsAdded(dbusProxy, nameOwner, argArray) {
 | 
			
		||||
        let appointments = argArray[0] || [];
 | 
			
		||||
        let changed = false;
 | 
			
		||||
 | 
			
		||||
    _onEventsReceived(results, _error) {
 | 
			
		||||
        let newEvents = [];
 | 
			
		||||
        let appointments = results[0] || [];
 | 
			
		||||
        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];
 | 
			
		||||
            let summary = a[1];
 | 
			
		||||
            let allDay = a[3];
 | 
			
		||||
            let allDay = a[2];
 | 
			
		||||
            let date = new Date(a[3] * 1000);
 | 
			
		||||
            let end = new Date(a[4] * 1000);
 | 
			
		||||
            let event = new CalendarEvent(id, date, end, summary, allDay);
 | 
			
		||||
            newEvents.push(event);
 | 
			
		||||
        }
 | 
			
		||||
        newEvents.sort((ev1, ev2) => ev1.date.getTime() - ev2.date.getTime());
 | 
			
		||||
            this._events.set(event.id, event);
 | 
			
		||||
 | 
			
		||||
        this._events = newEvents;
 | 
			
		||||
        this._isLoading = false;
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
            changed = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (changed)
 | 
			
		||||
            this.emit('changed');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onEventsRemoved(dbusProxy, nameOwner, argArray) {
 | 
			
		||||
        let ids = argArray[0] || [];
 | 
			
		||||
        let changed = false;
 | 
			
		||||
 | 
			
		||||
        for (let n = 0; n < ids.length; n++) {
 | 
			
		||||
            let id = ids[n];
 | 
			
		||||
 | 
			
		||||
            if (this._events.delete(id))
 | 
			
		||||
                changed = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (changed)
 | 
			
		||||
            this.emit('changed');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onClientDisappeared(dbusProxy, nameOwner, argArray) {
 | 
			
		||||
        let sourceUid = argArray[0] || "";
 | 
			
		||||
        let changed = false;
 | 
			
		||||
        let idsIter = this._events.keys();
 | 
			
		||||
 | 
			
		||||
        sourceUid += '\n';
 | 
			
		||||
 | 
			
		||||
        for (let item = idsIter.next(); !item.done; item = idsIter.next()) {
 | 
			
		||||
            let id = item.value;
 | 
			
		||||
 | 
			
		||||
            if (id.startsWith(sourceUid) &&
 | 
			
		||||
                this._events.delete(id))
 | 
			
		||||
                changed = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (changed)
 | 
			
		||||
            this.emit('changed');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadEvents(forceReload) {
 | 
			
		||||
@@ -303,32 +336,40 @@ class DBusEventSource extends EventSourceBase {
 | 
			
		||||
            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);
 | 
			
		||||
            if (forceReload) {
 | 
			
		||||
                this._events.clear();
 | 
			
		||||
                this.emit('changed');
 | 
			
		||||
            }
 | 
			
		||||
            this._dbusProxy.SetTimeRangeRemote(this._curRequestBegin.getTime() / 1000,
 | 
			
		||||
                                               this._curRequestEnd.getTime() / 1000,
 | 
			
		||||
                                               forceReload,
 | 
			
		||||
                                               Gio.DBusCallFlags.NONE);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestRange(begin, end) {
 | 
			
		||||
        if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
 | 
			
		||||
            this._isLoading = true;
 | 
			
		||||
            this._lastRequestBegin = begin;
 | 
			
		||||
            this._lastRequestEnd = end;
 | 
			
		||||
            this._curRequestBegin = begin;
 | 
			
		||||
            this._curRequestEnd = end;
 | 
			
		||||
            this._loadEvents(false);
 | 
			
		||||
            this._loadEvents(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getEvents(begin, end) {
 | 
			
		||||
    getEvents(begin, end, onlyCheckExistence) {
 | 
			
		||||
        let result = [];
 | 
			
		||||
        for (let n = 0; n < this._events.length; n++) {
 | 
			
		||||
            let event = this._events[n];
 | 
			
		||||
        let eventsIter = this._events.values();
 | 
			
		||||
 | 
			
		||||
            if (_dateIntervalsOverlap(event.date, event.end, begin, end))
 | 
			
		||||
        for (let item = eventsIter.next(); !item.done; item = eventsIter.next()) {
 | 
			
		||||
            let event = item.value;
 | 
			
		||||
 | 
			
		||||
            if (_dateIntervalsOverlap(event.date, event.end, begin, end)) {
 | 
			
		||||
                result.push(event);
 | 
			
		||||
 | 
			
		||||
                if (onlyCheckExistence)
 | 
			
		||||
                    return result;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        result.sort((event1, event2) => {
 | 
			
		||||
            // sort events by end time on ending day
 | 
			
		||||
@@ -343,7 +384,7 @@ class DBusEventSource extends EventSourceBase {
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(day);
 | 
			
		||||
        let dayEnd = _getEndOfDay(day);
 | 
			
		||||
 | 
			
		||||
        let events = this.getEvents(dayBegin, dayEnd);
 | 
			
		||||
        let events = this.getEvents(dayBegin, dayEnd, true);
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
@@ -873,7 +914,7 @@ class EventsSection extends MessageList.MessageListSection {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reloadEvents() {
 | 
			
		||||
        if (this._eventSource.isLoading)
 | 
			
		||||
        if (this._eventSource.isLoading || this._reloading)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._reloading = true;
 | 
			
		||||
@@ -882,10 +923,7 @@ class EventsSection extends MessageList.MessageListSection {
 | 
			
		||||
        let periodEnd = _getEndOfDay(this._date);
 | 
			
		||||
        let events = this._eventSource.getEvents(periodBegin, periodEnd);
 | 
			
		||||
 | 
			
		||||
        let ids = events.map(e => e.id);
 | 
			
		||||
        this._messageById.forEach((message, id) => {
 | 
			
		||||
            if (ids.includes(id))
 | 
			
		||||
                return;
 | 
			
		||||
            this._messageById.delete(id);
 | 
			
		||||
            this.removeMessage(message);
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ cogl_pango_pc = 'mutter-cogl-pango-' + mutter_api_version
 | 
			
		||||
libmutter_pc = 'libmutter-' + mutter_api_version
 | 
			
		||||
 | 
			
		||||
ecal_req = '>= 3.33.1'
 | 
			
		||||
eds_req = '>= 3.17.2'
 | 
			
		||||
eds_req = '>= 3.33.1'
 | 
			
		||||
gcr_req = '>= 3.7.5'
 | 
			
		||||
gio_req = '>= 2.56.0'
 | 
			
		||||
gi_req = '>= 1.49.1'
 | 
			
		||||
 
 | 
			
		||||
@@ -74,6 +74,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
 | 
			
		||||
 
 | 
			
		||||
@@ -43,20 +43,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;
 | 
			
		||||
@@ -69,175 +56,150 @@ 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
 | 
			
		||||
registry_watcher_source_appeared_cb (ESourceRegistryWatcher *watcher,
 | 
			
		||||
                                     ESource *source,
 | 
			
		||||
                                     CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  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
 | 
			
		||||
registry_watcher_source_disappeared_cb (ESourceRegistryWatcher *watcher,
 | 
			
		||||
                                        ESource *source,
 | 
			
		||||
                                        CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  gboolean emit;
 | 
			
		||||
 | 
			
		||||
  g_mutex_lock (&sources->priv->clients_lock);
 | 
			
		||||
 | 
			
		||||
  emit = g_hash_table_remove (sources->priv->clients, source);
 | 
			
		||||
 | 
			
		||||
  g_mutex_unlock (&sources->priv->clients_lock);
 | 
			
		||||
 | 
			
		||||
  if (emit)
 | 
			
		||||
    g_signal_emit (sources, signals[CLIENT_DISAPPEARED], 0, e_source_get_uid (source), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_data_free (ClientData *data)
 | 
			
		||||
{
 | 
			
		||||
  g_clear_signal_handler (&data->backend_died_id, data->client);
 | 
			
		||||
  g_signal_handler_disconnect (data->client, data->backend_died_id);
 | 
			
		||||
  g_object_unref (data->client);
 | 
			
		||||
  g_slice_free (ClientData, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_class_init (CalendarSourcesClass *klass)
 | 
			
		||||
{
 | 
			
		||||
  GObjectClass *gobject_class = (GObjectClass *) klass;
 | 
			
		||||
 | 
			
		||||
  parent_class = g_type_class_peek_parent (klass);
 | 
			
		||||
 | 
			
		||||
  gobject_class->finalize = calendar_sources_finalize;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_init (CalendarSources *sources)
 | 
			
		||||
calendar_sources_constructed (GObject *object)
 | 
			
		||||
{
 | 
			
		||||
  CalendarSources *sources = CALENDAR_SOURCES (object);
 | 
			
		||||
  ESourceRegistry *registry = NULL;
 | 
			
		||||
  GError *error = NULL;
 | 
			
		||||
  GDBusConnection *session_bus;
 | 
			
		||||
  GVariant *result;
 | 
			
		||||
 | 
			
		||||
  sources->priv = calendar_sources_get_instance_private (sources);
 | 
			
		||||
 | 
			
		||||
  /* 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
    }
 | 
			
		||||
  G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object);
 | 
			
		||||
 | 
			
		||||
  registry = e_source_registry_new_sync (NULL, &error);
 | 
			
		||||
  if (error != NULL)
 | 
			
		||||
    {
 | 
			
		||||
      /* Any error is fatal, but we don't want to crash gnome-shell-calendar-server
 | 
			
		||||
         because of e-d-s problems. So just exit here.
 | 
			
		||||
      */
 | 
			
		||||
      g_warning ("Failed to start evolution-source-registry: %s", error->message);
 | 
			
		||||
      exit(EXIT_FAILURE);
 | 
			
		||||
      exit (EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  g_object_unref (session_bus);
 | 
			
		||||
  g_return_if_fail (registry != NULL);
 | 
			
		||||
 | 
			
		||||
  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->registry_watcher = e_source_registry_watcher_new (registry, NULL);
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  g_clear_object (®istry);
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_finalize_source_data (CalendarSources    *sources,
 | 
			
		||||
				       CalendarSourceData *source_data)
 | 
			
		||||
{
 | 
			
		||||
  if (source_data->loaded)
 | 
			
		||||
    {
 | 
			
		||||
      g_hash_table_destroy (source_data->clients);
 | 
			
		||||
      source_data->clients = NULL;
 | 
			
		||||
 | 
			
		||||
      g_clear_handle_id (&source_data->timeout_id, g_source_remove);
 | 
			
		||||
 | 
			
		||||
      source_data->loaded = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
  e_source_registry_watcher_reclaim (sources->priv->registry_watcher);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -245,28 +207,73 @@ calendar_sources_finalize (GObject *object)
 | 
			
		||||
{
 | 
			
		||||
  CalendarSources *sources = CALENDAR_SOURCES (object);
 | 
			
		||||
 | 
			
		||||
  if (sources->priv->registry)
 | 
			
		||||
  if (sources->priv->clients)
 | 
			
		||||
    {
 | 
			
		||||
      g_clear_signal_handler (&sources->priv->source_added_id,
 | 
			
		||||
                              sources->priv->registry);
 | 
			
		||||
      g_clear_signal_handler (&sources->priv->source_changed_id,
 | 
			
		||||
                              sources->priv->registry);
 | 
			
		||||
      g_clear_signal_handler (&sources->priv->source_removed_id,
 | 
			
		||||
                              sources->priv->registry);
 | 
			
		||||
      g_object_unref (sources->priv->registry);
 | 
			
		||||
      g_hash_table_destroy (sources->priv->clients);
 | 
			
		||||
      sources->priv->clients = NULL;
 | 
			
		||||
    }
 | 
			
		||||
  sources->priv->registry = NULL;
 | 
			
		||||
 | 
			
		||||
  calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
 | 
			
		||||
  calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
 | 
			
		||||
  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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  if (G_OBJECT_CLASS (parent_class)->finalize)
 | 
			
		||||
    G_OBJECT_CLASS (parent_class)->finalize (object);
 | 
			
		||||
  g_mutex_clear (&sources->priv->clients_lock);
 | 
			
		||||
 | 
			
		||||
  G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_class_init (CalendarSourcesClass *klass)
 | 
			
		||||
{
 | 
			
		||||
  GObjectClass *gobject_class = (GObjectClass *) klass;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  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 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
@@ -274,85 +281,70 @@ calendar_sources_get (void)
 | 
			
		||||
 | 
			
		||||
  calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL);
 | 
			
		||||
  g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton),
 | 
			
		||||
			     singleton_location);
 | 
			
		||||
                             singleton_location);
 | 
			
		||||
 | 
			
		||||
  return calendar_sources_singleton;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* The clients are just created here but not loaded */
 | 
			
		||||
static void
 | 
			
		||||
create_client_for_source (ESource              *source,
 | 
			
		||||
		          ECalClientSourceType  source_type,
 | 
			
		||||
		          CalendarSourceData   *source_data)
 | 
			
		||||
ESourceRegistry *
 | 
			
		||||
calendar_sources_get_registry (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  ClientData *data;
 | 
			
		||||
  EClient *client;
 | 
			
		||||
  GError *error = NULL;
 | 
			
		||||
 | 
			
		||||
  client = g_hash_table_lookup (source_data->clients, source);
 | 
			
		||||
  g_return_if_fail (client == NULL);
 | 
			
		||||
 | 
			
		||||
  client = e_cal_client_connect_sync (source, source_type, -1, NULL, &error);
 | 
			
		||||
  if (!client)
 | 
			
		||||
    {
 | 
			
		||||
      g_warning ("Could not load source '%s': %s",
 | 
			
		||||
		 e_source_get_uid (source),
 | 
			
		||||
		 error->message);
 | 
			
		||||
      g_clear_error(&error);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  data = g_slice_new0 (ClientData);
 | 
			
		||||
  data->client = E_CAL_CLIENT (client);  /* takes ownership */
 | 
			
		||||
  data->backend_died_id = g_signal_connect (client,
 | 
			
		||||
                                            "backend-died",
 | 
			
		||||
                                            G_CALLBACK (backend_died_cb),
 | 
			
		||||
                                            source_data);
 | 
			
		||||
 | 
			
		||||
  g_hash_table_insert (source_data->clients, g_object_ref (source), data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
debug_dump_ecal_list (GHashTable *clients)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CALENDAR_ENABLE_DEBUG
 | 
			
		||||
  GList *list, *link;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
      dprintf ("  %s %s\n",
 | 
			
		||||
	       e_source_get_uid (source),
 | 
			
		||||
	       e_source_get_display_name (source));
 | 
			
		||||
    }
 | 
			
		||||
  g_list_free (list);
 | 
			
		||||
#endif
 | 
			
		||||
  return e_source_registry_watcher_get_registry (sources->priv->registry_watcher);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_load_esource_list (ESourceRegistry *registry,
 | 
			
		||||
                                    CalendarSourceData *source_data);
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
backend_restart (gpointer data)
 | 
			
		||||
gather_event_clients_cb (gpointer key,
 | 
			
		||||
                         gpointer value,
 | 
			
		||||
                         gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
  CalendarSourceData *source_data = data;
 | 
			
		||||
  ESourceRegistry *registry;
 | 
			
		||||
  GSList **plist = user_data;
 | 
			
		||||
  ClientData *cd = value;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
  if (cd && cd->is_for_events)
 | 
			
		||||
    *plist = g_slist_prepend (*plist, g_object_ref (cd->client));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  source_data->timeout_id = 0;
 | 
			
		||||
    
 | 
			
		||||
  return FALSE;
 | 
			
		||||
GSList *
 | 
			
		||||
calendar_sources_ref_clients (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  GSList *list = NULL;
 | 
			
		||||
 | 
			
		||||
  g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  return list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
calendar_sources_has_clients (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  GHashTableIter iter;
 | 
			
		||||
  gpointer value;
 | 
			
		||||
  gboolean has = FALSE;
 | 
			
		||||
 | 
			
		||||
  g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
 | 
			
		||||
 | 
			
		||||
  g_mutex_lock (&sources->priv->clients_lock);
 | 
			
		||||
 | 
			
		||||
  g_hash_table_iter_init (&iter, sources->priv->clients);
 | 
			
		||||
  while (!has && g_hash_table_iter_next (&iter, NULL, &value))
 | 
			
		||||
   {
 | 
			
		||||
     ClientData *cd = value;
 | 
			
		||||
 | 
			
		||||
     has = cd && cd->is_for_events;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
  g_mutex_unlock (&sources->priv->clients_lock);
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
@@ -360,196 +352,179 @@ 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);
 | 
			
		||||
 | 
			
		||||
  g_clear_handle_id (&source_data->timeout_id, g_source_remove);
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
  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) {
 | 
			
		||||
     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;
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
      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);
 | 
			
		||||
 | 
			
		||||
      extension = e_source_get_extension (source, extension_name);
 | 
			
		||||
      show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
 | 
			
		||||
 | 
			
		||||
      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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct _AsyncContext {
 | 
			
		||||
  gboolean is_for_events;
 | 
			
		||||
  ESource *source;
 | 
			
		||||
  ECalClientSourceType source_type;
 | 
			
		||||
  guint32 wait_for_connected_seconds;
 | 
			
		||||
} AsyncContext;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
 | 
			
		||||
                                             ESource         *source,
 | 
			
		||||
                                             CalendarSources *sources)
 | 
			
		||||
async_context_free (gpointer ptr)
 | 
			
		||||
{
 | 
			
		||||
  if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
 | 
			
		||||
  AsyncContext *ctx = ptr;
 | 
			
		||||
 | 
			
		||||
  if (ctx)
 | 
			
		||||
    {
 | 
			
		||||
      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);
 | 
			
		||||
        }
 | 
			
		||||
      g_clear_object (&ctx->source);
 | 
			
		||||
      g_slice_free (AsyncContext, ctx);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
 | 
			
		||||
                                             ESource         *source,
 | 
			
		||||
                                             CalendarSources *sources)
 | 
			
		||||
calendar_sources_connect_client_thread (GTask *task,
 | 
			
		||||
                                        gpointer source_object,
 | 
			
		||||
                                        gpointer task_data,
 | 
			
		||||
                                        GCancellable *cancellable)
 | 
			
		||||
{
 | 
			
		||||
  if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
 | 
			
		||||
  CalendarSources *sources = source_object;
 | 
			
		||||
  AsyncContext *ctx = task_data;
 | 
			
		||||
  EClient *client;
 | 
			
		||||
  GError *local_error = NULL;
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
    {
 | 
			
		||||
      CalendarSourceData *source_data;
 | 
			
		||||
 | 
			
		||||
      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))
 | 
			
		||||
    {
 | 
			
		||||
      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);
 | 
			
		||||
      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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ensure_appointment_sources (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)
 | 
			
		||||
{
 | 
			
		||||
  if (!sources->priv->appointment_sources.loaded)
 | 
			
		||||
  AsyncContext *ctx;
 | 
			
		||||
  GTask *task;
 | 
			
		||||
 | 
			
		||||
  ctx = g_slice_new0 (AsyncContext);
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  g_task_run_in_thread (task, calendar_sources_connect_client_thread);
 | 
			
		||||
 | 
			
		||||
  g_object_unref (task);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EClient *
 | 
			
		||||
calendar_sources_connect_client_finish (CalendarSources *sources,
 | 
			
		||||
                                        GAsyncResult *result,
 | 
			
		||||
                                        GError **error)
 | 
			
		||||
{
 | 
			
		||||
  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_task_propagate_pointer (G_TASK (result), error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
print_debug (const gchar *format,
 | 
			
		||||
             ...)
 | 
			
		||||
{
 | 
			
		||||
  g_autofree char *s = NULL;
 | 
			
		||||
  g_autofree char *timestamp = NULL;
 | 
			
		||||
  va_list ap;
 | 
			
		||||
  g_autoptr (GDateTime) now = NULL;
 | 
			
		||||
  static volatile gsize once_init_value = 0;
 | 
			
		||||
  static gboolean show_debug = FALSE;
 | 
			
		||||
  static guint pid = 0;
 | 
			
		||||
 | 
			
		||||
  if (g_once_init_enter (&once_init_value))
 | 
			
		||||
    {
 | 
			
		||||
      calendar_sources_load_esource_list (sources->priv->registry,
 | 
			
		||||
                                          &sources->priv->appointment_sources);
 | 
			
		||||
      sources->priv->appointment_sources.loaded = TRUE;
 | 
			
		||||
      show_debug = (g_getenv ("CALENDAR_SERVER_DEBUG") != NULL);
 | 
			
		||||
      pid = getpid ();
 | 
			
		||||
      g_once_init_leave (&once_init_value, 1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ensure_task_sources (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  if (!sources->priv->task_sources.loaded)
 | 
			
		||||
    {
 | 
			
		||||
      calendar_sources_load_esource_list (sources->priv->registry,
 | 
			
		||||
                                          &sources->priv->task_sources);
 | 
			
		||||
      sources->priv->task_sources.loaded = TRUE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GList *
 | 
			
		||||
calendar_sources_get_task_clients (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  GList *list, *link;
 | 
			
		||||
 | 
			
		||||
  g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
 | 
			
		||||
 | 
			
		||||
  ensure_task_sources (sources);
 | 
			
		||||
 | 
			
		||||
  list = g_hash_table_get_values (sources->priv->task_sources.clients);
 | 
			
		||||
 | 
			
		||||
  for (link = list; link != NULL; link = g_list_next (link))
 | 
			
		||||
    link->data = ((ClientData *) link->data)->client;
 | 
			
		||||
 | 
			
		||||
  return list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
calendar_sources_has_sources (CalendarSources *sources)
 | 
			
		||||
{
 | 
			
		||||
  g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
 | 
			
		||||
 | 
			
		||||
  ensure_appointment_sources (sources);
 | 
			
		||||
  ensure_task_sources (sources);
 | 
			
		||||
 | 
			
		||||
  return g_hash_table_size (sources->priv->appointment_sources.clients) > 0 ||
 | 
			
		||||
    g_hash_table_size (sources->priv->task_sources.clients) > 0;
 | 
			
		||||
 | 
			
		||||
  if (!show_debug)
 | 
			
		||||
    goto out;
 | 
			
		||||
 | 
			
		||||
  now = g_date_time_new_now_local ();
 | 
			
		||||
  timestamp = g_date_time_format (now, "%H:%M:%S");
 | 
			
		||||
 | 
			
		||||
  va_start (ap, format);
 | 
			
		||||
  s = g_strdup_vprintf (format, ap);
 | 
			
		||||
  va_end (ap);
 | 
			
		||||
 | 
			
		||||
  g_print ("gnome-shell-calendar-server[%d]: %s.%03d: %s\n",
 | 
			
		||||
           pid, timestamp, g_date_time_get_microsecond (now), s);
 | 
			
		||||
 out:
 | 
			
		||||
  ;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,17 +26,46 @@
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
#define EDS_DISABLE_DEPRECATED
 | 
			
		||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
 | 
			
		||||
#include <libedataserver/libedataserver.h>
 | 
			
		||||
#include <libecal/libecal.h>
 | 
			
		||||
G_GNUC_END_IGNORE_DEPRECATIONS
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
#define CALENDAR_TYPE_SOURCES (calendar_sources_get_type ())
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
/* Set the environment variable CALENDAR_SERVER_DEBUG to show debug */
 | 
			
		||||
void            print_debug                          (const gchar *str,
 | 
			
		||||
                                                      ...) G_GNUC_PRINTF (1, 2);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										767
									
								
								src/calendar-server/reminder-watcher.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										767
									
								
								src/calendar-server/reminder-watcher.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,767 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Red Hat (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>
 | 
			
		||||
 | 
			
		||||
#define HANDLE_LIBICAL_MEMORY
 | 
			
		||||
#define EDS_DISABLE_DEPRECATED
 | 
			
		||||
#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_WITH_PRIVATE (ReminderWatcher, reminder_watcher, E_TYPE_REMINDER_WATCHER)
 | 
			
		||||
 | 
			
		||||
static const gchar *
 | 
			
		||||
reminder_watcher_get_rd_summary (const EReminderData *rd)
 | 
			
		||||
{
 | 
			
		||||
  if (!rd)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  return i_cal_component_get_summary (e_cal_component_get_icalcomponent (e_reminder_data_get_component (rd)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
reminder_watcher_notify_audio (ReminderWatcher *rw,
 | 
			
		||||
                               const EReminderData *rd,
 | 
			
		||||
                               ECalComponentAlarm *alarm)
 | 
			
		||||
{
 | 
			
		||||
#if 0
 | 
			
		||||
  ICalAttach *attach = NULL;
 | 
			
		||||
  GSList *attachments;
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  attachments = e_cal_component_alarm_get_attachments (alarm);
 | 
			
		||||
  if (attachments && !attachments->next)
 | 
			
		||||
    attach = attachments->data;
 | 
			
		||||
 | 
			
		||||
  if (attach && i_cal_attach_get_is_url (attach))
 | 
			
		||||
    {
 | 
			
		||||
      const gchar *url;
 | 
			
		||||
 | 
			
		||||
      url = i_cal_attach_get_url (attach);
 | 
			
		||||
      if (url && *url)
 | 
			
		||||
        {
 | 
			
		||||
          gchar *filename;
 | 
			
		||||
          GError *error = NULL;
 | 
			
		||||
 | 
			
		||||
          filename = g_filename_from_uri (url, NULL, &error);
 | 
			
		||||
 | 
			
		||||
          if (!filename)
 | 
			
		||||
            ean_debug_print ("Audio notify: Failed to convert URI '%s' to filename: %s\n", url, error ? error->message : "Unknown error");
 | 
			
		||||
           else if (g_file_test (filename, G_FILE_TEST_EXISTS))
 | 
			
		||||
            {
 | 
			
		||||
#ifdef HAVE_CANBERRA
 | 
			
		||||
              gint err = ca_context_play (ca_gtk_context_get (), 0,
 | 
			
		||||
                                          CA_PROP_MEDIA_FILENAME, filename,
 | 
			
		||||
                                          NULL);
 | 
			
		||||
 | 
			
		||||
              did_play = !err;
 | 
			
		||||
 | 
			
		||||
              if (err)
 | 
			
		||||
                ean_debug_print ("Audio notify: Cannot play file '%s': %s\n", filename, ca_strerror (err));
 | 
			
		||||
#else
 | 
			
		||||
                ean_debug_print ("Audio notify: Cannot play file '%s': Not compiled with libcanberra\n", filename);
 | 
			
		||||
#endif
 | 
			
		||||
            }
 | 
			
		||||
           else
 | 
			
		||||
            ean_debug_print ("Audio notify: File '%s' does not exist\n", filename);
 | 
			
		||||
 | 
			
		||||
          g_clear_error (&error);
 | 
			
		||||
          g_free (filename);
 | 
			
		||||
        }
 | 
			
		||||
       else
 | 
			
		||||
        ean_debug_print ("Audio notify: Alarm has stored empty URL, fallback to default sound\n");
 | 
			
		||||
    }
 | 
			
		||||
   else if (!attach)
 | 
			
		||||
    ean_debug_print ("Audio notify: Alarm has no attachment, fallback to default sound\n");
 | 
			
		||||
   else
 | 
			
		||||
    ean_debug_print ("Audio notify: Alarm attachment is not a URL to sound file, fallback to default sound\n");
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CANBERRA
 | 
			
		||||
  if (!did_play)
 | 
			
		||||
    {
 | 
			
		||||
      gint err = ca_context_play (ca_gtk_context_get (), 0,
 | 
			
		||||
                                  CA_PROP_EVENT_ID, "alarm-clock-elapsed",
 | 
			
		||||
                                  NULL);
 | 
			
		||||
 | 
			
		||||
      did_play = !err;
 | 
			
		||||
 | 
			
		||||
      if (err)
 | 
			
		||||
        ean_debug_print ("Audio notify: Cannot play event sound: %s\n", ca_strerror (err));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if (!did_play)
 | 
			
		||||
    {
 | 
			
		||||
      GdkDisplay *display;
 | 
			
		||||
 | 
			
		||||
      display = rw->priv->window ? gtk_widget_get_display (rw->priv->window) : NULL;
 | 
			
		||||
 | 
			
		||||
      if (!display)
 | 
			
		||||
        display = gdk_display_get_default ();
 | 
			
		||||
 | 
			
		||||
      if (display)
 | 
			
		||||
        gdk_display_beep (display);
 | 
			
		||||
      else
 | 
			
		||||
        ean_debug_print ("Audio notify: Cannot beep, no display found\n");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  print_debug ("ReminderWatcher::Notify Audio for '%s'", reminder_watcher_get_rd_summary (rd));
 | 
			
		||||
 | 
			
		||||
  return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gchar *
 | 
			
		||||
reminder_watcher_build_notif_id (const EReminderData *rd)
 | 
			
		||||
{
 | 
			
		||||
  GString *string;
 | 
			
		||||
  ECalComponentId *id;
 | 
			
		||||
  ECalComponentAlarmInstance *instance;
 | 
			
		||||
 | 
			
		||||
  g_return_val_if_fail (rd != NULL, NULL);
 | 
			
		||||
 | 
			
		||||
  string = g_string_sized_new (32);
 | 
			
		||||
 | 
			
		||||
  if (e_reminder_data_get_source_uid (rd))
 | 
			
		||||
    {
 | 
			
		||||
      g_string_append (string, e_reminder_data_get_source_uid (rd));
 | 
			
		||||
      g_string_append_c (string, '\n');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  id = e_cal_component_get_id (e_reminder_data_get_component (rd));
 | 
			
		||||
  if (id)
 | 
			
		||||
    {
 | 
			
		||||
      if (e_cal_component_id_get_uid (id))
 | 
			
		||||
        {
 | 
			
		||||
          g_string_append (string, e_cal_component_id_get_uid (id));
 | 
			
		||||
          g_string_append_c (string, '\n');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      if (e_cal_component_id_get_rid (id))
 | 
			
		||||
        {
 | 
			
		||||
          g_string_append (string, e_cal_component_id_get_rid (id));
 | 
			
		||||
          g_string_append_c (string, '\n');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      e_cal_component_id_free (id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  instance = e_reminder_data_get_instance (rd);
 | 
			
		||||
 | 
			
		||||
  g_string_append_printf (string, "%" G_GINT64_FORMAT, (gint64) (instance ? e_cal_component_alarm_instance_get_time (instance) : -1));
 | 
			
		||||
 | 
			
		||||
  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_notification_add_button_with_target (notification, _("Dismiss"), "app.dismiss-reminder", "s", notif_id);
 | 
			
		||||
  g_notification_set_default_action_and_target (notification, "app.dismiss-reminder", "s", notif_id);
 | 
			
		||||
 | 
			
		||||
  g_application_send_notification (rw->priv->application, notif_id, notification);
 | 
			
		||||
 | 
			
		||||
  g_object_unref (notification);
 | 
			
		||||
  g_free (description);
 | 
			
		||||
  g_free (notif_id);
 | 
			
		||||
 | 
			
		||||
  print_debug ("ReminderWatcher::Notify Display for '%s'", reminder_watcher_get_rd_summary (rd));
 | 
			
		||||
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
reminder_watcher_notify_email (ReminderWatcher *rw,
 | 
			
		||||
                               const EReminderData *rd,
 | 
			
		||||
                               ECalComponentAlarm *alarm)
 | 
			
		||||
{
 | 
			
		||||
  print_debug ("ReminderWatcher::Notify Email for '%s'", reminder_watcher_get_rd_summary (rd));
 | 
			
		||||
 | 
			
		||||
  /* 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 = NULL;
 | 
			
		||||
  GSList *attachments;
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  print_debug ("ReminderWatcher::Notify Procedure for '%s'", reminder_watcher_get_rd_summary (rd));
 | 
			
		||||
 | 
			
		||||
  attachments = e_cal_component_alarm_get_attachments (alarm);
 | 
			
		||||
 | 
			
		||||
  if (attachments && !attachments->next)
 | 
			
		||||
    attach = attachments->data;
 | 
			
		||||
 | 
			
		||||
  description = e_cal_component_alarm_get_description (alarm);
 | 
			
		||||
 | 
			
		||||
  /* If the alarm has no attachment, simply display a notification dialog. */
 | 
			
		||||
  if (!attach)
 | 
			
		||||
    goto fallback;
 | 
			
		||||
 | 
			
		||||
  if (!i_cal_attach_get_is_url (attach))
 | 
			
		||||
    goto fallback;
 | 
			
		||||
 | 
			
		||||
  url = i_cal_attach_get_url (attach);
 | 
			
		||||
  g_return_val_if_fail (url != NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
  /* Ask for confirmation before executing the stuff */
 | 
			
		||||
  if (description && e_cal_component_text_get_value (description))
 | 
			
		||||
    cmd = g_strconcat (url, " ", e_cal_component_text_get_value (description), 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);
 | 
			
		||||
 | 
			
		||||
  /* 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;
 | 
			
		||||
  ECalComponentAlarmInstance *instance;
 | 
			
		||||
  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 (e_reminder_data_get_component (rd)) == E_CAL_COMPONENT_TODO)
 | 
			
		||||
    {
 | 
			
		||||
      ICalPropertyStatus status;
 | 
			
		||||
 | 
			
		||||
      status = e_cal_component_get_status (e_reminder_data_get_component (rd));
 | 
			
		||||
 | 
			
		||||
      if (status == I_CAL_STATUS_COMPLETED &&
 | 
			
		||||
          !g_settings_get_boolean (rw->priv->settings, "notify-completed-tasks"))
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  instance = e_reminder_data_get_instance (rd);
 | 
			
		||||
 | 
			
		||||
  alarm = instance ? e_cal_component_get_alarm (e_reminder_data_get_component (rd), e_cal_component_alarm_instance_get_uid (instance)) : NULL;
 | 
			
		||||
  if (!alarm)
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  if (!snoozed && !g_settings_get_boolean (rw->priv->settings, "notify-past-events"))
 | 
			
		||||
    {
 | 
			
		||||
      ECalComponentAlarmTrigger *trigger;
 | 
			
		||||
      time_t offset = 0, event_relative, orig_trigger_day, today;
 | 
			
		||||
 | 
			
		||||
      trigger = e_cal_component_alarm_get_trigger (alarm);
 | 
			
		||||
 | 
			
		||||
      switch (trigger ? e_cal_component_alarm_trigger_get_kind (trigger) : E_CAL_COMPONENT_ALARM_TRIGGER_NONE)
 | 
			
		||||
        {
 | 
			
		||||
          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 = i_cal_duration_as_int (e_cal_component_alarm_trigger_get_duration (trigger));
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
          default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        today = time (NULL);
 | 
			
		||||
        event_relative = e_cal_component_alarm_instance_get_occur_start (instance) - offset;
 | 
			
		||||
 | 
			
		||||
        #define CLAMP_TO_DAY(x) ((x) - ((x) % (60 * 60 * 24)))
 | 
			
		||||
 | 
			
		||||
        event_relative = CLAMP_TO_DAY (event_relative);
 | 
			
		||||
        orig_trigger_day = CLAMP_TO_DAY (e_cal_component_alarm_instance_get_time (instance));
 | 
			
		||||
        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;
 | 
			
		||||
          }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  action = e_cal_component_alarm_get_action (alarm);
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
  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 = reminder_watcher_get_instance_private (rw);
 | 
			
		||||
  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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
reminder_watcher_dismiss_done_cb (GObject *source_object,
 | 
			
		||||
                                  GAsyncResult *result,
 | 
			
		||||
                                  gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
  GError *error = NULL;
 | 
			
		||||
 | 
			
		||||
  if (!e_reminder_watcher_dismiss_finish (E_REMINDER_WATCHER (source_object), result, &error))
 | 
			
		||||
    {
 | 
			
		||||
      if (!g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED))
 | 
			
		||||
        print_debug ("Dismiss: Failed with error: %s", error ? error->message : "Unknown error");
 | 
			
		||||
 | 
			
		||||
      g_clear_error (&error);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
reminder_watcher_dismiss_by_id (EReminderWatcher *reminder_watcher,
 | 
			
		||||
                                const gchar *id)
 | 
			
		||||
{
 | 
			
		||||
  ReminderWatcher *rw;
 | 
			
		||||
  GSList *past, *link;
 | 
			
		||||
 | 
			
		||||
  g_return_if_fail (IS_REMINDER_WATCHER (reminder_watcher));
 | 
			
		||||
  g_return_if_fail (id && *id);
 | 
			
		||||
 | 
			
		||||
  rw = REMINDER_WATCHER (reminder_watcher);
 | 
			
		||||
  past = e_reminder_watcher_dup_past (reminder_watcher);
 | 
			
		||||
 | 
			
		||||
  for (link = past; link; link = g_slist_next (link))
 | 
			
		||||
    {
 | 
			
		||||
      EReminderData *rd = link->data;
 | 
			
		||||
      gchar *rd_id;
 | 
			
		||||
 | 
			
		||||
      rd_id = reminder_watcher_build_notif_id (rd);
 | 
			
		||||
 | 
			
		||||
      if (g_strcmp0 (rd_id, id) == 0)
 | 
			
		||||
        {
 | 
			
		||||
          print_debug ("Dismiss: Going to dismiss '%s'", reminder_watcher_get_rd_summary (rd));
 | 
			
		||||
 | 
			
		||||
          g_application_withdraw_notification (rw->priv->application, id);
 | 
			
		||||
 | 
			
		||||
          e_reminder_watcher_dismiss (reminder_watcher, rd, NULL,
 | 
			
		||||
                                      reminder_watcher_dismiss_done_cb, NULL);
 | 
			
		||||
 | 
			
		||||
          g_free (rd_id);
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      g_free (rd_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  if (!link)
 | 
			
		||||
    print_debug ("Dismiss: Cannot find reminder '%s'", id);
 | 
			
		||||
 | 
			
		||||
  g_slist_free_full (past, e_reminder_data_free);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								src/calendar-server/reminder-watcher.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/calendar-server/reminder-watcher.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Red Hat (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
 | 
			
		||||
 | 
			
		||||
#define HANDLE_LIBICAL_MEMORY
 | 
			
		||||
#define EDS_DISABLE_DEPRECATED
 | 
			
		||||
#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);
 | 
			
		||||
void              reminder_watcher_dismiss_by_id(EReminderWatcher *reminder_watcher,
 | 
			
		||||
                                                 const gchar *id);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* REMINDER_WATCHER_H */
 | 
			
		||||
		Reference in New Issue
	
	Block a user