[StatusIconDispatcher] Move non-system-tray icons to message tray

New StatusIconDispatcher dispatches icons to the system or message tray.

Based on a patch from Matt Novenstern
https://bugzilla.gnome.org/show_bug.cgi?id=608869
This commit is contained in:
Dan Winship 2010-08-09 12:33:34 -04:00
parent 82e6f9c8d6
commit cbf2cbac61
5 changed files with 164 additions and 87 deletions

View File

@ -34,6 +34,7 @@ const ShellDBus = imports.ui.shellDBus;
const TelepathyClient = imports.ui.telepathyClient;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
@ -55,6 +56,7 @@ let modalCount = 0;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
let statusIconDispatcher = null;
let _errorLogStack = [];
let _startDate;
@ -125,6 +127,7 @@ function start() {
overview = new Overview.Overview();
chrome = new Chrome.Chrome();
magnifier = new Magnifier.Magnifier();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();

View File

@ -104,6 +104,9 @@ NotificationDaemon.prototype = {
this._notifications = {};
this._busProxy = new Bus();
Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Shell.WindowTracker.get_default().connect('notify::focus-app',
Lang.bind(this, this._onFocusAppChanged));
Main.overview.connect('hidden',
@ -166,6 +169,23 @@ NotificationDaemon.prototype = {
}
},
_newSource: function(title, pid) {
let source = new Source(title, pid);
this._sources[pid] = source;
source.connect('clicked', Lang.bind(this,
function() {
source.destroy();
}));
source.connect('destroy', Lang.bind(this,
function() {
delete this._sources[pid];
}));
Main.messageTray.add(source);
return source;
},
Notify: function(appName, replacesId, icon, summary, body,
actions, hints, timeout) {
let id;
@ -238,22 +258,13 @@ NotificationDaemon.prototype = {
this._senderToPid[sender] = pid;
source = this._sources[pid];
if (!source) {
source = new Source(appName, pid);
source.connect('clicked', Lang.bind(this,
function() {
source.destroy();
}));
if (!source)
source = this._newSource(appName, pid);
source.connect('destroy', Lang.bind(this,
function() {
delete this._sources[pid];
delete this._senderToPid[sender];
}));
this._sources[pid] = source;
Main.messageTray.add(source);
}
this._notifyForSource(source, ndata);
}));
@ -291,7 +302,7 @@ NotificationDaemon.prototype = {
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
let sourceIconActor = source.app ? null : this._iconForNotificationData(icon, hints, source.ICON_SIZE);
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
source.notify(notification, sourceIconActor);
},
@ -359,6 +370,19 @@ NotificationDaemon.prototype = {
'org.freedesktop.Notifications',
'ActionInvoked', 'us',
[id, action]);
},
_onTrayIconAdded: function(o, icon) {
let source = this._sources[icon.pid];
if (!source)
source = this._newSource(icon.title, icon.pid);
source.setTrayIcon(icon);
},
_onTrayIconRemoved: function(o, icon) {
let source = this._sources[icon.pid];
if (source)
source.destroy();
}
};
@ -374,19 +398,40 @@ Source.prototype = {
_init: function(title, pid) {
MessageTray.Source.prototype._init.call(this, title);
this.app = Shell.WindowTracker.get_default().get_app_from_pid(pid);
if (this.app) {
this._pid = pid;
this._setApp();
if (this.app)
this.title = this.app.get_name();
this._setSummaryIcon(this.app.create_icon_texture(this.ICON_SIZE));
}
else
this.useNotificationIcon = true;
},
notify: function(notification, icon) {
if (icon)
if (!this.app)
this._setApp();
if (!this.app && icon)
this._setSummaryIcon(icon);
MessageTray.Source.prototype.notify.call(this, notification);
},
_setApp: function() {
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
if (!this.app)
return;
// Only override the icon if we were previously using
// notification-based icons (ie, not a trayicon)
if (this.useNotificationIcon) {
this.useNotificationIcon = false;
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
}
},
setTrayIcon: function(icon) {
this._setSummaryIcon(icon);
this.useNotificationIcon = false;
},
clicked: function() {
this.openApp();
MessageTray.Source.prototype.clicked.call(this);

View File

@ -31,12 +31,6 @@ const SPINNER_UPDATE_TIMEOUT = 130;
const SPINNER_SPEED = 0.02;
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'bluetooth-applet': 'bluetooth',
'gnome-volume-control-applet': 'volume',
'nm-applet': 'network',
'gnome-power-manager': 'battery'
};
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator
};
@ -799,14 +793,6 @@ Panel.prototype = {
/* right */
// Yet-another-Ubuntu-workaround - we have to kill their
// app-indicators, so that applications fall back to normal
// status icons
// http://bugzilla.gnome.org/show_bug.cgi=id=621382
let p = new Shell.Process({ args: ['pkill', '-f',
'^([^ ]*/)?indicator-application-service$']});
p.run();
// System status applets live in statusBox, while legacy tray icons
// live in trayBox
// The trayBox is hidden when there are no tray icons.
@ -831,17 +817,8 @@ Panel.prototype = {
this._menus.addMenu(indicator.menu);
}
this._traymanager = new Shell.TrayManager();
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._traymanager.connect('tray-icon-removed',
Lang.bind(this, function(o, icon) {
trayBox.remove_actor(icon);
if (trayBox.get_children().length == 0)
trayBox.hide();
this._recomputeTraySize();
}));
this._traymanager.manage_stage(global.stage);
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
this._statusmenu = new StatusMenu.StatusMenuButton();
this._menus.addMenu(this._statusmenu.menu);
@ -885,22 +862,14 @@ Panel.prototype = {
});
},
_onTrayIconAdded: function(o, icon) {
let wmClass = icon.wm_class.toLowerCase();
_onTrayIconAdded: function(o, icon, role) {
icon.height = PANEL_ICON_SIZE;
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
if (!role) {
// Unknown icons go first in undefined order
this._trayBox.insert_actor(icon, 0);
} else {
if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
icon._role = role;
// Figure out the index in our well-known order for this icon
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
icon._rolePosition = position;
@ -919,22 +888,13 @@ Panel.prototype = {
// If we didn't find a position, we must be first
this._trayBox.insert_actor(icon, 0);
}
}
// Make sure the trayBox is shown.
this._trayBox.show();
this._recomputeTraySize();
},
// By default, tray icons have a spacing of TRAY_SPACING. However this
// starts to fail if we have too many as can sadly happen; just jump down
// to a spacing of 8 if we're over 6.
// http://bugzilla.gnome.org/show_bug.cgi?id=590495
_recomputeTraySize: function () {
if (this._trayBox.get_children().length > 6)
this._trayBox.add_style_pseudo_class('compact');
else
this._trayBox.remove_style_pseudo_class('compact');
_onTrayIconRemoved: function(o, icon) {
this._trayBox.remove_actor(icon);
},
_addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {

View File

@ -0,0 +1,54 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'bluetooth-applet': 'bluetooth',
'gnome-volume-control-applet': 'volume',
'nm-applet': 'network',
'gnome-power-manager': 'battery'
};
function StatusIconDispatcher() {
this._init();
}
StatusIconDispatcher.prototype = {
_init: function() {
this._traymanager = new Shell.TrayManager();
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._traymanager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
this._traymanager.manage_stage(global.stage);
// Yet-another-Ubuntu-workaround - we have to kill their
// app-indicators, so that applications fall back to normal
// status icons
// http://bugzilla.gnome.org/show_bug.cgi=id=621382
let p = new Shell.Process({ args: ['pkill', '-f', '^([^ ]*/)?indicator-application-service$']});
p.run();
},
_onTrayIconAdded: function(o, icon) {
let wmClass = icon.wm_class.toLowerCase();
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
if (role)
this.emit('status-icon-added', icon, role);
else
this.emit('message-icon-added', icon);
},
_onTrayIconRemoved: function(o, icon) {
let wmClass = icon.wm_class.toLowerCase();
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
if (role)
this.emit('status-icon-removed', icon);
else
this.emit('message-icon-removed', icon);
}
};
Signals.addSignalMethods(StatusIconDispatcher.prototype);

View File

@ -507,6 +507,21 @@ gnome_shell_plugin_xevent_filter (MutterPlugin *plugin,
}
#endif
/* When the pointer leaves the stage to enter a child of the stage
* (like a notification icon), we don't want to produce Clutter leave
* events. But Clutter treats all leave events identically, so we
* need hide the detail = NotifyInferior events from it.
*
* Since Clutter doesn't see any event at all, this does mean that
* it won't produce an enter event on a Clutter actor that surrounds
* the child (unless it gets a MotionNotify before the Enter event).
* Other weirdness is likely also possible.
*/
if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify)
&& xev->xcrossing.detail == NotifyInferior
&& xev->xcrossing.window == clutter_x11_get_stage_window (CLUTTER_STAGE (clutter_stage_get_default ())))
return TRUE;
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
}