[gnome-shell] Associate sources with applications



commit 77ed621c741e5e1488e9e4842d6843465c7f5a42
Author: Marina Zhurakhinskaya <marinaz redhat com>
Date:   Mon Feb 22 17:19:32 2010 -0500

    Associate sources with applications
    
    Use the "appName" parameter in notifications to identify the source
    rather than the id - use the latter to enable update and removal of
    individual notifications as laid out in the desktop notification spec.
    
    This is a rebase of the patch by Florian Müllner.

 js/ui/messageTray.js        |   69 +++++++++++++++++++++++++++++++++++++-----
 js/ui/notificationDaemon.js |   56 ++++++++++++++++++++++++----------
 2 files changed, 99 insertions(+), 26 deletions(-)
---
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index 62011a2..a1f6509 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -36,6 +36,7 @@ function _cleanMarkup(text) {
 }
 
 // Notification:
+// @id: the notification's id
 // @source: the notification's Source
 // @title: the title
 // @banner: the banner text
@@ -55,15 +56,21 @@ function _cleanMarkup(text) {
 // of the notification (as with addBody()) when the banner is expanded.
 // In this case, if @banner is too long to fit in the single-line mode,
 // the notification will be made expandable automatically.
-function Notification(source, title, banner, bannerBody) {
-    this._init(source, title, banner, bannerBody);
+function Notification(id, source, title, banner, bannerBody) {
+    this._init(id, source, title, banner, bannerBody);
 }
 
 Notification.prototype = {
-    _init: function(source, title, banner, bannerBody) {
+    _init: function(id, source, title, banner, bannerBody) {
+        this.id = id;
         this.source = source;
         this._bannerBody = bannerBody;
 
+        source.connect('clicked', Lang.bind(this,
+            function() {
+                this.emit('dismissed');
+            }));
+
         this.actor = new St.Table({ name: 'notification' });
         this.update(title, banner, true);
     },
@@ -303,6 +310,10 @@ Notification.prototype = {
                            time: ANIMATION_TIME,
                            transition: "easeOutQuad" });
         return true;
+    },
+
+    destroy: function() {
+        this.emit('destroy');
     }
 };
 Signals.addSignalMethods(Notification.prototype);
@@ -380,6 +391,7 @@ MessageTray.prototype = {
         this._notificationState = State.HIDDEN;
         this._notificationTimeoutId = 0;
         this._overviewVisible = false;
+        this._notificationRemoved = false;
 
         this.actor.show();
         Main.chrome.addActor(this.actor, { affectsStruts: false,
@@ -441,11 +453,11 @@ MessageTray.prototype = {
 
         source.connect('destroy', Lang.bind(this,
             function () {
-                this.remove(source);
+                this.removeSource(source);
             }));
     },
 
-    remove: function(source) {
+    removeSource: function(source) {
         if (!this.contains(source))
             return;
 
@@ -458,7 +470,10 @@ MessageTray.prototype = {
         this._notificationQueue = newNotificationQueue;
 
         this._summary.remove_actor(this._icons[source.id]);
-        this._summaryNeedsToBeShown = true;
+        if (this._summary.get_children().length > 0)
+            this._summaryNeedsToBeShown = true;
+        else
+            this._summaryNeedsToBeShown = false;
         delete this._icons[source.id];
         delete this._sources[source.id];
 
@@ -467,16 +482,50 @@ MessageTray.prototype = {
                 Mainloop.source_remove(this._notificationTimeoutId);
                 this._notificationTimeoutId = 0;
             }
+            this._notificationRemoved = true;
             this._updateState();
         }
     },
 
+    removeNotification: function(notification) {
+        if (this._notification == notification && (this._notificationState == State.SHOWN || this._notificationState == State.SHOWING)) {
+            if (this._notificationTimeoutId) {
+                Mainloop.source_remove(this._notificationTimeoutId);
+                this._notificationTimeoutId = 0;
+            }
+            this._notificationRemoved = true;
+            this._updateState();
+            return;
+        }
+
+        let index = this._notificationQueue.indexOf(notification);
+        if (index != -1)
+            this._notificationQueue.splice(index, 1);
+    },
+
     getSource: function(id) {
         return this._sources[id];
     },
 
+    _getNotification: function(id, source) {
+        if (this._notification && this._notification.id == id)
+            return this._notification;
+
+        for (let i = 0; i < this._notificationQueue.length; i++) {
+            if (this._notificationQueue[i].id == id && this._notificationQueue[i].source == source)
+                return this._notificationQueue[i];
+        }
+
+        return null;
+    },
+
     _onNotify: function(source, notification) {
-        this._notificationQueue.push(notification);
+        if (this._getNotification(notification.id, source) == null) {
+            notification.connect('destroy',
+                                 Lang.bind(this, this.removeNotification));
+            this._notificationQueue.push(notification);
+        }
+
         this._updateState();
     },
 
@@ -524,9 +573,9 @@ MessageTray.prototype = {
     _updateState: function() {
         // Notifications
         let notificationsPending = this._notificationQueue.length > 0;
-        let notificationPinned = this._pointerInTray && !this._pointerInSummary;
+        let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
         let notificationExpanded = this._notificationBin.y < 0;
-        let notificationExpired = this._notificationTimeoutId == 0 && !this._pointerInTray;
+        let notificationExpired = (this._notificationTimeoutId == 0 && !this._pointerInTray) || this._notificationRemoved;
 
         if (this._notificationState == State.HIDDEN) {
             if (notificationsPending)
@@ -541,6 +590,7 @@ MessageTray.prototype = {
         // Summary
         let summarySummoned = this._pointerInSummary || this._overviewVisible;
         let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned;
+
         let notificationsVisible = (this._notificationState == State.SHOWING ||
                                     this._notificationState == State.SHOWN);
         let notificationsDone = !notificationsVisible && !notificationsPending;
@@ -638,6 +688,7 @@ MessageTray.prototype = {
     },
 
     _hideNotification: function() {
+        this._notificationRemoved = false;
         this._notification.popIn();
 
         this._tween(this._notificationBin, "_notificationState", State.HIDDEN,
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index 6e53f1a..deb7352 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -96,6 +96,8 @@ NotificationDaemon.prototype = {
                                   DBus.MANY_INSTANCES,
                                   Lang.bind(this, this._acquiredName),
                                   Lang.bind(this, this._lostName));
+
+        this._currentNotifications = {};
     },
 
     _acquiredName: function() {
@@ -124,30 +126,23 @@ NotificationDaemon.prototype = {
     },
 
     _sourceId: function(id) {
-        return 'notification-' + id;
+        return 'source-' + id;
     },
 
     Notify: function(appName, replacesId, icon, summary, body,
                      actions, hints, timeout) {
-        let id, source = null;
-
-        if (replacesId != 0) {
-            id = replacesId;
-            source = Main.messageTray.getSource(this._sourceId(id));
-            // source may be null if the current source was destroyed
-            // right as the client sent the new notification
-        }
+        let source = Main.messageTray.getSource(this._sourceId(appName));
+        let id = null;
 
+        // Source may be null if we have never received a notification from
+        // this app or if all notifications from this app have been acknowledged.
         if (source == null) {
-            id = nextNotificationId++;
-
-            source = new Source(this._sourceId(id), icon, hints);
+            source = new Source(this._sourceId(appName), icon, hints);
             Main.messageTray.add(source);
 
             source.connect('clicked', Lang.bind(this,
                 function() {
                     source.destroy();
-                    this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
                 }));
 
             let sender = DBus.getCurrentMessageContext().sender;
@@ -156,6 +151,8 @@ NotificationDaemon.prototype = {
                 let app = Shell.WindowTracker.get_default().get_app_from_pid(result);
                 source.setApp(app);
             });
+        } else {
+            source.update(icon, hints);
         }
 
         summary = GLib.markup_escape_text(summary, -1);
@@ -169,7 +166,27 @@ NotificationDaemon.prototype = {
             }
         }
 
-        let notification = new MessageTray.Notification(source, summary, body, true);
+        let notification;
+        if (replacesId != 0) {
+            id = replacesId;
+            notification = this._currentNotifications[id];
+        }
+
+        if (notification == null) {
+            id = nextNotificationId++;
+            notification = new MessageTray.Notification(id, source, summary, body, true);
+            this._currentNotifications[id] = notification;
+            notification.connect('dismissed', Lang.bind(this,
+                function(n) {
+                    n.destroy();
+                    this._emitNotificationClosed(n.id, NotificationClosedReason.DISMISSED);
+                }));
+        } else {
+            // passing in true as the last parameter will clear out extra actors,
+            // such as actions
+            notification.update(summary, body, true);
+        }
+
         if (actions.length) {
             for (let i = 0; i < actions.length - 1; i += 2)
                 notification.addAction(actions[i], actions[i + 1]);
@@ -181,9 +198,9 @@ NotificationDaemon.prototype = {
     },
 
     CloseNotification: function(id) {
-        let source = Main.messageTray.getSource(this._sourceId(id));
-        if (source)
-            source.destroy();
+        let notification = this._currentNotifications[id];
+        if (notification)
+            notification.destroy();
         this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
     },
 
@@ -215,6 +232,7 @@ NotificationDaemon.prototype = {
     },
 
     _emitNotificationClosed: function(id, reason) {
+        delete this._currentNotifications[id];
         DBus.session.emit_signal('/org/freedesktop/Notifications',
                                  'org.freedesktop.Notifications',
                                  'NotificationClosed', 'uu',
@@ -241,6 +259,10 @@ Source.prototype = {
     _init: function(sourceId, icon, hints) {
         MessageTray.Source.prototype._init.call(this, sourceId);
 
+        this.update(icon, hints);
+    },
+
+    update: function(icon, hints) {
         hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
 
         this._icon = icon;



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