[gnome-shell] NotificationDaemon: add support for transient notifications



commit 22f4aabadf464b262876c9108ff0b52e3d875552
Author: Marina Zhurakhinskaya <marinaz redhat com>
Date:   Wed Dec 15 16:30:50 2010 -0500

    NotificationDaemon: add support for transient notifications
    
    Transient notifications are removed after being shown. If the summary
    is being shown while they appear, they are represented in it by a new
    source icon.
    
    We always create a new source for new transient notifications to
    ensure that they don't replace the latest persistent notification
    associated with the source. Because we generally don't want any
    new or resident notifications to be replaced by others, associating
    multiple notifications with a source is the next thing we will
    implement.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=633412

 js/ui/messageTray.js        |   26 ++++++++++++++-
 js/ui/notificationDaemon.js |   78 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 83 insertions(+), 21 deletions(-)
---
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index 50a2f5f..08ad5c2 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -227,6 +227,8 @@ Notification.prototype = {
         this.source = source;
         this.urgent = false;
         this.resident = false;
+        // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
+        this.isTransient = false;
         this.expanded = false;
         this._useActionIcons = false;
         this._customContent = false;
@@ -497,6 +499,10 @@ Notification.prototype = {
         this.resident = resident;
     },
 
+    setTransient: function(isTransient) {
+        this.isTransient = isTransient;
+    },
+
     setUseActionIcons: function(useIcons) {
         this._useActionIcons = useIcons;
     },
@@ -808,6 +814,11 @@ Source.prototype = {
         this.title = title;
         this._iconBin = new St.Bin({ width: this.ICON_SIZE,
                                      height: this.ICON_SIZE });
+        this.isTransient = false;
+    },
+
+    setTransient: function(isTransient) {
+        this.isTransient = isTransient;
     },
 
     // Called to create a new icon actor (of size this.ICON_SIZE).
@@ -1068,7 +1079,15 @@ MessageTray.prototype = {
         }
 
         this._summaryItems.push(summaryItem);
-        this._newSummaryItems.push(summaryItem);
+
+        // We keep this._newSummaryItems to track any new sources that were added to the
+        // summary and show the summary with them to the user for a short period of time
+        // after notifications are done showing. However, we don't want that to happen for
+        // transient sources, which are removed after the notification is shown, but are
+        // not removed fast enough because of the callbacks to avoid the summary popping up.
+        // So we just don't add transient sources to this._newSummaryItems .
+        if (!source.isTransient)
+            this._newSummaryItems.push(summaryItem);
 
         source.connect('notify', Lang.bind(this, this._onNotify));
 
@@ -1601,7 +1620,10 @@ MessageTray.prototype = {
         this._notification.collapseCompleted();
         this._notification.disconnect(this._notificationClickedId);
         this._notificationClickedId = 0;
+        let notification = this._notification;
         this._notification = null;
+        if (notification.isTransient)
+            notification.destroy();
     },
 
     _expandNotification: function(autoExpanding) {
@@ -1750,6 +1772,8 @@ MessageTray.prototype = {
         this._summaryNotificationClickedId = 0;
         let summaryNotification = this._summaryNotification;
         this._summaryNotification = null;
+        if (summaryNotification.isTransient && !this._reNotifyWithSummaryNotificationAfterHide)
+            summaryNotification.destroy();
         if (this._reNotifyWithSummaryNotificationAfterHide) {
             this._onNotify(summaryNotification.source, summaryNotification);
             this._reNotifyWithSummaryNotificationAfterHide = false;
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index 58f6783..0b2833d 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -175,14 +175,43 @@ NotificationDaemon.prototype = {
         }
     },
 
-    _newSource: function(title, pid) {
+    // Returns the source associated with ndata.notification if it is set.
+    // Otherwise, returns the source associated with the pid if one is
+    // stored in this._sources and the notification is not transient.
+    // Otherwise, creates a new source as long as pid is provided.
+    //
+    // Either a pid or ndata.notification is needed to retrieve or
+    // create a source.
+    _getSource: function(title, pid, ndata) {
+        if (!pid && !(ndata && ndata.notification))
+            return null;
+
+        // We use notification's source for the notifications we still have
+        // around that are getting replaced because we don't keep sources
+        // for transient notifications in this._sources, but we still want
+        // the notification associated with them to get replaced correctly.
+        if (ndata && ndata.notification)
+            return ndata.notification.source;
+
+        let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
+
+        // We don't want to override a persistent notification
+        // with a transient one from the same sender, so we
+        // always create a new source object for new transient notifications
+        // and never add it to this._sources .
+        if (!isForTransientNotification && this._sources[pid])
+            return this._sources[pid];
+
         let source = new Source(title, pid);
-        this._sources[pid] = source;
+        source.setTransient(isForTransientNotification);
 
-        source.connect('destroy', Lang.bind(this,
-            function() {
-                delete this._sources[pid];
-            }));
+        if (!isForTransientNotification) {
+            this._sources[pid] = source;
+            source.connect('destroy', Lang.bind(this,
+                function() {
+                    delete this._sources[pid];
+                }));
+        }
 
         Main.messageTray.add(source);
         return source;
@@ -234,7 +263,8 @@ NotificationDaemon.prototype = {
 
         let sender = DBus.getCurrentMessageContext().sender;
         let pid = this._senderToPid[sender];
-        let source = pid ? this._sources[pid] : null;
+
+        let source = this._getSource(appName, pid, ndata);
 
         if (source) {
             this._notifyForSource(source, ndata);
@@ -255,16 +285,23 @@ NotificationDaemon.prototype = {
                 if (!ndata)
                     return;
 
-                this._senderToPid[sender] = pid;
-                source = this._sources[pid];
-
-                if (!source)
-                    source = this._newSource(appName, pid);
-                source.connect('destroy', Lang.bind(this,
-                    function() {
-                        delete this._senderToPid[sender];
-                    }));
-
+                source = this._getSource(appName, pid, ndata);
+
+                // We only store sender-pid entries for persistent sources.
+                // Removing the entries once the source is destroyed
+                // would result in the entries associated with transient
+                // sources removed once the notification is shown anyway.
+                // However, keeping these pairs would mean that we would
+                // possibly remove an entry associated with a persistent
+                // source when a transient source for the same sender is
+                // distroyed.
+                if (!source.isTransient) {
+                    this._senderToPid[sender] = pid;
+                    source.connect('destroy', Lang.bind(this,
+                        function() {
+                            delete this._senderToPid[sender];
+                        }));
+                }
                 this._notifyForSource(source, ndata);
             }));
 
@@ -309,6 +346,9 @@ NotificationDaemon.prototype = {
 
         notification.setUrgent(hints.urgency == Urgency.CRITICAL);
         notification.setResident(hints.resident == true);
+        // 'transient' is a reserved keyword in JS, so we have to retrieve the value
+        // of the 'transient' hint with hints['transient'] rather than hints.transient
+        notification.setTransient(hints['transient'] == true);
 
         let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
         source.notify(notification, sourceIconActor);
@@ -378,9 +418,7 @@ NotificationDaemon.prototype = {
     },
 
     _onTrayIconAdded: function(o, icon) {
-        let source = this._sources[icon.pid];
-        if (!source)
-            source = this._newSource(icon.title || icon.wm_class || _("Unknown"), icon.pid);
+        let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
         source.setTrayIcon(icon);
     },
 



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