Compare commits

..

2 Commits

Author SHA1 Message Date
Milan Crha
13ddf1eb83 calendar-server: Add 'Dismiss' button and application action
- adds 'Dismiss' button to the notification
- introduces org.gnome.Shell.CalendarServer.desktop.in.in to benefit from GNotification API

Related to https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1064
2020-03-03 15:58:13 +01:00
Milan Crha
15ab33a11a Move functionality from evolution-alarm-notify to gnome-shell-calendar-server
Closes https://gitlab.gnome.org/GNOME/gnome-shell/issues/155
2020-03-03 12:00:27 +01:00
28 changed files with 4940 additions and 3388 deletions

21
NEWS
View File

@@ -1,24 +1,3 @@
3.36.0
======
* Fix off-by-1900 error in date conversions [Florian; !1061]
* Fix crash on startup with topIcons* extension enabled [Florian; #2308]
* Don't require gsd-xsettings for X11 support on wayland [Olivier; !1065]
* Fix ibus support in Xorg session [Carlos; #1690]
* Improve Extensions D-Bus API [Florian; !1074]
* Allow session modes to specify alternative resource name [Marco; !1063]
* Fix link to location settings in aggregate menu [Sebastian; #2316]
* Fix illegible app folder titles with light theme [ub; !1059]
* Really fix visual glitch in sliders [Jonas; #1569]
Contributors:
Marco Trevisan (Treviño), Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
Sebastian Keller, Florian Müllner, ub
Translators:
Aman Alam [pa], Goran Vidović [hr], Aurimas Černius [lt],
Milo Casagrande [it], Daniel Korostil [uk], sicklylife [ja],
Marek Černocký [cs], Nathan Follens [nl]
3.35.92
=======
* Plug a memory leak [Jonas D.; !1015]

View File

@@ -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>

View File

@@ -199,37 +199,14 @@
<!--
LaunchExtensionPrefs:
Deprecated for OpenExtensionPrefs
@uuid: The UUID of the extension
Launch preferences of an extension.
-->
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<!--
OpenExtensionPrefs:
@uuid: The UUID of the extension
@parent_window: Identifier for the application window
@options: Vardict with further options
Opens the prefs dialog of extension @uuid.
The following @options are recognized:
<variablelist>
<varlistentry>
<term>modal b</term>
<listitem>
<para>Whether the prefs window should be modal, default: false</para>
</listitem>
</varlistentry>
</variablelist>
-->
<method name="OpenExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="parent_window"/>
<arg type="a{sv}" direction="in" name="options"/>
</method>
<!--
CheckForUpdates:
Update all extensions for which updates are available
@@ -257,11 +234,5 @@
-->
<property name="ShellVersion" type="s" access="read"/>
<!--
UserExtensionsEnabled:
Whether user extensions are enabled
-->
<property name="UserExtensionsEnabled" type="b" access="readwrite"/>
</interface>
</node>

View File

@@ -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

View 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

View File

@@ -60,7 +60,7 @@ $app_grid_fg_color: #fff;
& .folder-name-entry { width: 300px }
/* FIXME: this is to keep the label in sync with the entry */
& .folder-name-label { padding: 5px 7px; color: $osd_fg_color; }
& .folder-name-label { padding: 5px 7px }
& .edit-folder-button {
@extend %button;

View File

@@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported getIBusManager */
const { Gio, GLib, IBus, Meta } = imports.gi;
const { Gio, GLib, IBus } = imports.gi;
const Signals = imports.signals;
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
@@ -55,7 +55,7 @@ var IBusManager = class {
this._ibus.set_watch_ibus_signal(true);
this._ibus.connect('global-engine-changed', this._engineChanged.bind(this));
this._spawn(Meta.is_wayland_compositor() ? [] : ['--xim']);
this._spawn();
}
_spawn(extraArgs = []) {

View File

@@ -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);
});

View File

@@ -361,8 +361,7 @@ function reloadThemeResource() {
if (_themeResource)
_themeResource._unregister();
_themeResource = Gio.Resource.load('%s/%s'.format(global.datadir,
sessionMode.themeResourceName));
_themeResource = Gio.Resource.load('%s/gnome-shell-theme.gresource'.format(global.datadir));
_themeResource._register();
}

View File

@@ -15,7 +15,6 @@ const _modes = {
'restrictive': {
parentMode: null,
stylesheetName: 'gnome-shell.css',
themeResourceName: 'gnome-shell-theme.gresource',
hasOverview: false,
showCalendarEvents: false,
allowSettings: false,

View File

@@ -255,17 +255,6 @@ var GnomeShellExtensions = class {
constructor() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellExtensionsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._userExtensionsEnabled = this.UserExtensionsEnabled;
global.settings.connect('changed::disable-user-extensions', () => {
if (this._userExtensionsEnabled === this.UserExtensionsEnabled)
return;
this._userExtensionsEnabled = this.UserExtensionsEnabled;
this._dbusImpl.emit_property_changed('UserExtensionsEnabled',
new GLib.Variant('b', this._userExtensionsEnabled));
});
Main.extensionManager.connect('extension-state-changed',
this._extensionStateChanged.bind(this));
}
@@ -312,10 +301,6 @@ var GnomeShellExtensions = class {
}
LaunchExtensionPrefs(uuid) {
this.OpenExtensionPrefs(uuid, '', {});
}
OpenExtensionPrefs(uuid, _parentWindow, _options) {
let appSys = Shell.AppSystem.get_default();
let app = appSys.lookup_app('org.gnome.Extensions.desktop');
let info = app.get_app_info();
@@ -340,14 +325,6 @@ var GnomeShellExtensions = class {
return Config.PACKAGE_VERSION;
}
get UserExtensionsEnabled() {
return !global.settings.get_boolean('disable-user-extensions');
}
set UserExtensionsEnabled(enable) {
global.settings.set_boolean('disable-user-extensions', !enable);
}
_extensionStateChanged(_, newState) {
let state = ExtensionUtils.serializeExtension(newState);
this._dbusImpl.emit_signal('ExtensionStateChanged',

View File

@@ -43,10 +43,10 @@ var Slider = GObject.registerClass({
let [hasHandleColor, handleBorderColor] =
themeNode.lookup_color('-slider-handle-border-color', false);
const ceiledHandleRadius = Math.ceil(handleRadius + handleBorderWidth);
const handleX = ceiledHandleRadius +
(width - 2 * ceiledHandleRadius) * this._value / this._maxValue;
const handleY = height / 2;
let wholeHandleRadius = Math.ceil(handleRadius);
let handleX = wholeHandleRadius +
(width - 2 * wholeHandleRadius) * this._value / this._maxValue;
let handleY = height / 2;
let color = themeNode.get_foreground_color();
Clutter.cairo_set_source_color(cr, color);

View File

@@ -64,7 +64,7 @@ class Indicator extends PanelMenu.SystemIndicator {
this._item.label.text = _("Location Enabled");
this._onOffAction = this._item.menu.addAction(_("Disable"), this._onOnOffAction.bind(this));
this._item.menu.addSettingsAction(_('Privacy Settings'), 'gnome-location-panel.desktop');
this._item.menu.addSettingsAction(_("Privacy Settings"), 'gnome-privacy-panel.desktop');
this.menu.addMenuItem(this._item);

View File

@@ -903,8 +903,7 @@ var WindowManager = class {
/* Leave this watchdog timeout so don't block indefinitely here */
let timeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
Gio.DBus.session.unwatch_name(watchId);
log('Warning: Failed to start gsd-xsettings');
task.return_boolean(true);
task.return_boolean(false);
timeoutId = 0;
return GLib.SOURCE_REMOVE;
});

View File

@@ -1,5 +1,5 @@
project('gnome-shell', 'c',
version: '3.36.0',
version: '3.35.92',
meson_version: '>= 0.47.0',
license: 'GPLv2+'
)
@@ -19,18 +19,18 @@ 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'
gjs_req = '>= 1.63.2'
gtk_req = '>= 3.15.0'
mutter_req = '>= 3.36.0'
mutter_req = '>= 3.35.92'
polkit_req = '>= 0.100'
schemas_req = '>= 3.33.1'
startup_req = '>= 0.11'
ibus_req = '>= 1.5.2'
gnome_desktop_req = '>= 3.35.90'
gnome_desktop_req = '>= 3.34.2'
bt_req = '>= 3.9.0'
gst_req = '>= 0.11.92'

View File

@@ -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

1629
po/cs.po

File diff suppressed because it is too large Load Diff

View File

@@ -16,8 +16,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
"POT-Creation-Date: 2020-02-29 16:06+0000\n"
"PO-Revision-Date: 2020-03-05 06:30+0900\n"
"POT-Creation-Date: 2020-02-27 21:57+0000\n"
"PO-Revision-Date: 2020-02-29 20:28+0900\n"
"Last-Translator: sicklylife <translation@sicklylife.jp>\n"
"Language-Team: Japanese <gnome-translation@gnome.gr.jp>\n"
"Language: ja\n"
@@ -1037,7 +1037,7 @@ msgstr "イベントなし"
#: js/ui/calendar.js:1153
msgid "Do Not Disturb"
msgstr "通知ポップアップを表示しない"
msgstr "通知ポップアップしない"
#: js/ui/calendar.js:1167
msgid "Clear"

1684
po/nl.po

File diff suppressed because it is too large Load Diff

1946
po/uk.po

File diff suppressed because it is too large Load Diff

View File

@@ -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 (&registry);
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:
;
}

View File

@@ -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

View File

@@ -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,

View 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);
}

View 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 */

View File

@@ -29,7 +29,6 @@ struct _ShellTrayManagerPrivate {
ClutterColor bg_color;
GHashTable *icons;
StWidget *theme_widget;
};
typedef struct {
@@ -59,8 +58,6 @@ static guint shell_tray_manager_signals [LAST_SIGNAL] = { 0 };
static const ClutterColor default_color = { 0x00, 0x00, 0x00, 0xff };
static void shell_tray_manager_release_resources (ShellTrayManager *manager);
static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
static void na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
@@ -128,7 +125,16 @@ shell_tray_manager_init (ShellTrayManager *manager)
{
manager->priv = shell_tray_manager_get_instance_private (manager);
manager->priv->na_manager = na_tray_manager_new ();
manager->priv->icons = g_hash_table_new_full (NULL, NULL,
NULL, free_tray_icon);
manager->priv->bg_color = default_color;
g_signal_connect (manager->priv->na_manager, "tray-icon-added",
G_CALLBACK (na_tray_icon_added), manager);
g_signal_connect (manager->priv->na_manager, "tray-icon-removed",
G_CALLBACK (na_tray_icon_removed), manager);
}
static void
@@ -136,7 +142,8 @@ shell_tray_manager_finalize (GObject *object)
{
ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
shell_tray_manager_release_resources (manager);
g_object_unref (manager->priv->na_manager);
g_hash_table_destroy (manager->priv->icons);
G_OBJECT_CLASS (shell_tray_manager_parent_class)->finalize (object);
}
@@ -186,30 +193,6 @@ shell_tray_manager_new (void)
return g_object_new (SHELL_TYPE_TRAY_MANAGER, NULL);
}
static void
shell_tray_manager_ensure_resources (ShellTrayManager *manager)
{
if (manager->priv->na_manager != NULL)
return;
manager->priv->icons = g_hash_table_new_full (NULL, NULL,
NULL, free_tray_icon);
manager->priv->na_manager = na_tray_manager_new ();
g_signal_connect (manager->priv->na_manager, "tray-icon-added",
G_CALLBACK (na_tray_icon_added), manager);
g_signal_connect (manager->priv->na_manager, "tray-icon-removed",
G_CALLBACK (na_tray_icon_removed), manager);
}
static void
shell_tray_manager_release_resources (ShellTrayManager *manager)
{
g_clear_object (&manager->priv->na_manager);
g_clear_pointer (&manager->priv->icons, g_hash_table_destroy);
}
static void
shell_tray_manager_style_changed (StWidget *theme_widget,
gpointer user_data)
@@ -218,9 +201,6 @@ shell_tray_manager_style_changed (StWidget *theme_widget,
StThemeNode *theme_node;
StIconColors *icon_colors;
if (manager->priv->na_manager == NULL)
return;
theme_node = st_widget_get_theme_node (theme_widget);
icon_colors = st_theme_node_get_icon_colors (theme_node);
na_tray_manager_set_colors (manager->priv->na_manager,
@@ -228,30 +208,11 @@ shell_tray_manager_style_changed (StWidget *theme_widget,
&icon_colors->error, &icon_colors->success);
}
static void
shell_tray_manager_manage_screen_internal (ShellTrayManager *manager)
{
shell_tray_manager_ensure_resources (manager);
na_tray_manager_manage_screen (manager->priv->na_manager);
}
void
shell_tray_manager_manage_screen (ShellTrayManager *manager,
StWidget *theme_widget)
{
MetaDisplay *display = shell_global_get_display (shell_global_get ());
g_set_weak_pointer (&manager->priv->theme_widget, theme_widget);
if (meta_display_get_x11_display (display) != NULL)
shell_tray_manager_manage_screen_internal (manager);
g_signal_connect_object (display, "x11-display-setup",
G_CALLBACK (shell_tray_manager_manage_screen_internal),
manager, G_CONNECT_SWAPPED);
g_signal_connect_object (display, "x11-display-closing",
G_CALLBACK (shell_tray_manager_release_resources),
manager, G_CONNECT_SWAPPED);
na_tray_manager_manage_screen (manager->priv->na_manager);
g_signal_connect_object (theme_widget, "style-changed",
G_CALLBACK (shell_tray_manager_style_changed),
@@ -259,24 +220,6 @@ shell_tray_manager_manage_screen (ShellTrayManager *manager,
shell_tray_manager_style_changed (theme_widget, manager);
}
void
shell_tray_manager_unmanage_screen (ShellTrayManager *manager)
{
MetaDisplay *display = shell_global_get_display (shell_global_get ());
g_signal_handlers_disconnect_by_data (display, manager);
if (manager->priv->theme_widget != NULL)
{
g_signal_handlers_disconnect_by_func (manager->priv->theme_widget,
G_CALLBACK (shell_tray_manager_style_changed),
manager);
}
g_set_weak_pointer (&manager->priv->theme_widget, NULL);
shell_tray_manager_release_resources (manager);
}
static void
shell_tray_manager_child_on_realize (GtkWidget *widget,
ShellTrayManagerChild *child)

View File

@@ -15,7 +15,6 @@ G_DECLARE_FINAL_TYPE (ShellTrayManager, shell_tray_manager,
ShellTrayManager *shell_tray_manager_new (void);
void shell_tray_manager_manage_screen (ShellTrayManager *manager,
StWidget *theme_widget);
void shell_tray_manager_unmanage_screen (ShellTrayManager *manager);
G_END_DECLS