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



commit cbf2cbac610fa771fd7efcc47c96fd1f8280a9fa
Author: Dan Winship <danw gnome org>
Date:   Mon Aug 9 12:33:34 2010 -0400

    [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

 js/ui/main.js                 |    3 +
 js/ui/notificationDaemon.js   |   87 +++++++++++++++++++++++++++++---------
 js/ui/panel.js                |   92 +++++++++++-----------------------------
 js/ui/statusIconDispatcher.js |   54 ++++++++++++++++++++++++
 src/gnome-shell-plugin.c      |   15 +++++++
 5 files changed, 164 insertions(+), 87 deletions(-)
---
diff --git a/js/ui/main.js b/js/ui/main.js
index a94ef93..a0287c3 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -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();
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index 2165aea..899de97 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -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,21 +258,12 @@ 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();
-                        }));
-                    source.connect('destroy', Lang.bind(this,
-                        function() {
-                            delete this._sources[pid];
-                            delete this._senderToPid[sender];
-                        }));
-
-                    this._sources[pid] = source;
-                    Main.messageTray.add(source);
-                }
+                if (!source)
+                    source = this._newSource(appName, pid);
+                source.connect('destroy', Lang.bind(this,
+                    function() {
+                        delete this._senderToPid[sender];
+                    }));
 
                 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);
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 4c3206c..057e0e1 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -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,56 +862,39 @@ 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;
-            let children = this._trayBox.get_children();
-            let i;
-            // Walk children backwards, until we find one that isn't
-            // well-known, or one where we should follow
-            for (i = children.length - 1; i >= 0; i--) {
-                let rolePosition = children[i]._rolePosition;
-                if (!rolePosition || position > rolePosition) {
-                    this._trayBox.insert_actor(icon, i + 1);
-                    break;
-                }
-            }
-            if (i == -1) {
-                // If we didn't find a position, we must be first
-                this._trayBox.insert_actor(icon, 0);
+        if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
+            // This icon is legacy, and replaced by a Shell version
+            // Hide it
+            return;
+        }
+        // Figure out the index in our well-known order for this icon
+        let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
+        icon._rolePosition = position;
+        let children = this._trayBox.get_children();
+        let i;
+        // Walk children backwards, until we find one that isn't
+        // well-known, or one where we should follow
+        for (i = children.length - 1; i >= 0; i--) {
+            let rolePosition = children[i]._rolePosition;
+            if (!rolePosition || position > rolePosition) {
+                this._trayBox.insert_actor(icon, i + 1);
+                break;
             }
         }
+        if (i == -1) {
+            // 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) {
diff --git a/js/ui/statusIconDispatcher.js b/js/ui/statusIconDispatcher.js
new file mode 100644
index 0000000..85d7ffe
--- /dev/null
+++ b/js/ui/statusIconDispatcher.js
@@ -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);
diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c
index 227644f..91626f9 100644
--- a/src/gnome-shell-plugin.c
+++ b/src/gnome-shell-plugin.c
@@ -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;
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]