[gnome-shell] [MessageTray] Clean up source-vs-notification icon handling



commit 6f57f072147e075c458ed7e9831016aa3f92caac
Author: Dan Winship <danw gnome org>
Date:   Thu Aug 5 13:09:27 2010 -0400

    [MessageTray] Clean up source-vs-notification icon handling
    
    A Source needs exactly one summary icon (which in the case of a
    trayicon-based source won't even be just an image), but possibly many
    notification icons, which may vary for successive notifications
    (particularly in the case of NotificationDaemon notifications). So
    differentiate these cases in the API.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=627303

 js/ui/messageTray.js            |   50 +++++++++++++++------
 js/ui/notificationDaemon.js     |   91 +++++++++++++++++++--------------------
 js/ui/telepathyClient.js        |    7 ++-
 js/ui/windowAttentionHandler.js |    5 +-
 4 files changed, 89 insertions(+), 64 deletions(-)
---
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index 1391aac..1138a0b 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -19,7 +19,6 @@ const SUMMARY_TIMEOUT = 1;
 
 const HIDE_TIMEOUT = 0.2;
 
-const ICON_SIZE = 24;
 const BUTTON_ICON_SIZE = 36;
 
 const MAX_SOURCE_TITLE_WIDTH = 180;
@@ -46,10 +45,14 @@ function _cleanMarkup(text) {
 // @banner: the banner text
 // @params: optional additional params
 //
-// Creates a notification. In banner mode, it will show
-// @source's icon, @title (in bold) and @banner, all on a single line
+// Creates a notification. In banner mode, it will show an
+// icon, @title (in bold) and @banner, all on a single line
 // (with @banner ellipsized if necessary).
 //
+// By default, the icon shown is created by calling
+// source.createNotificationIcon(). However, you can override this
+// by passing an 'icon' parameter in @params.
+//
 // Additional notification details can be added, in which case the
 // notification can be expanded by moving the pointer into it. In
 // expanded mode, the banner text disappears, and there can be one or
@@ -150,6 +153,7 @@ Notification.prototype = {
     update: function(title, banner, params) {
         params = Params.parse(params, { bannerBody: this._bannerBody,
                                         body: null,
+                                        icon: null,
                                         clear: false });
 
         if (this._icon)
@@ -167,7 +171,7 @@ Notification.prototype = {
 
         this._bannerBody = params.bannerBody;
 
-        this._icon = this.source.createIcon(ICON_SIZE);
+        this._icon = params.icon || this.source.createNotificationIcon();
         this.actor.add(this._icon, { row: 0,
                                      col: 0,
                                      x_expand: false,
@@ -560,21 +564,30 @@ Notification.prototype = {
 };
 Signals.addSignalMethods(Notification.prototype);
 
-function Source(title, createIcon) {
-    this._init(title, createIcon);
+function Source(title) {
+    this._init(title);
 }
 
 Source.prototype = {
-    _init: function(title, createIcon) {
+    ICON_SIZE: 24,
+
+    _init: function(title) {
         this.title = title;
-        if (createIcon)
-            this.createIcon = createIcon;
+        this._iconBin = new St.Bin({ width: this.ICON_SIZE,
+                                     height: this.ICON_SIZE });
+    },
+
+    // Called to create a new icon actor (of size this.ICON_SIZE).
+    // Must be overridden by the subclass if you do not pass icons
+    // explicitly to the Notification() constructor.
+    createNotificationIcon: function() {
+        throw new Error('no implementation of createNotificationIcon in ' + this);
     },
 
-    // This can be overridden by a subclass, or by the createIcon
-    // parameter to _init()
-    createIcon: function(size) {
-        throw new Error('no implementation of createIcon in ' + this);
+    // Unlike createNotificationIcon, this always returns the same actor;
+    // there is only one summary icon actor for a Source.
+    getSummaryIcon: function() {
+        return this._iconBin;
     },
 
     notify: function(notification) {
@@ -600,6 +613,15 @@ Source.prototype = {
 
     destroy: function() {
         this.emit('destroy');
+    },
+
+    //// Protected methods ////
+
+    // The subclass must call this at least once to set the summary icon.
+    _setSummaryIcon: function(icon) {
+        if (this._iconBin.child)
+            this._iconBin.child.destroy();
+        this._iconBin.child = icon;
     }
 };
 Signals.addSignalMethods(Source.prototype);
@@ -626,7 +648,7 @@ SummaryItem.prototype = {
         this._sourceBox.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
         this._sourceBox.connect('allocate', Lang.bind(this, this._allocate));
 
-        this._sourceIcon = source.createIcon(ICON_SIZE);
+        this._sourceIcon = source.getSummaryIcon();
         this._sourceTitleBin = new St.Bin({ y_align: St.Align.MIDDLE, x_fill: true });
         this._sourceTitle = new St.Label({ style_class: 'source-title',
                                            text: source.title });
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index f4a3441..3081f87 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -141,6 +141,37 @@ NotificationDaemon.prototype = {
         }
     },
 
+    _iconForNotificationData: function(icon, hints, size) {
+        let textureCache = St.TextureCache.get_default();
+
+        if (icon) {
+            if (icon.substr(0, 7) == 'file://')
+                return textureCache.load_uri_async(icon, size, size);
+            else if (icon[0] == '/') {
+                let uri = GLib.filename_to_uri(icon, null);
+                return textureCache.load_uri_async(uri, size, size);
+            } else
+                return textureCache.load_icon_name(icon, size);
+        } else if (hints.icon_data) {
+            let [width, height, rowStride, hasAlpha,
+                 bitsPerSample, nChannels, data] = hints.icon_data;
+            return textureCache.load_from_raw(data, data.length, hasAlpha,
+                                              width, height, rowStride, size);
+        } else {
+            let stockIcon;
+            switch (hints.urgency) {
+                case Urgency.LOW:
+                case Urgency.NORMAL:
+                    stockIcon = 'gtk-dialog-info';
+                    break;
+                case Urgency.CRITICAL:
+                    stockIcon = 'gtk-dialog-error';
+                    break;
+            }
+            return textureCache.load_icon_name(stockIcon, size);
+        }
+    },
+
     Notify: function(appName, replacesId, icon, summary, body,
                      actions, hints, timeout) {
         let source = this._sources[appName];
@@ -164,7 +195,7 @@ NotificationDaemon.prototype = {
         // been acknowledged.
         if (source == null) {
             let title = appNameMap[appName] || appName;
-            source = new Source(title, icon, hints);
+            source = new Source(title);
             Main.messageTray.add(source);
             this._sources[appName] = source;
 
@@ -184,8 +215,6 @@ NotificationDaemon.prototype = {
                 if (app)
                     source.setApp(app);
             });
-        } else {
-            source.update(icon, hints);
         }
 
         summary = GLib.markup_escape_text(summary, -1);
@@ -205,10 +234,12 @@ NotificationDaemon.prototype = {
             notification = this._currentNotifications[id];
         }
 
+        let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
         if (notification == null) {
             id = nextNotificationId++;
             notification = new MessageTray.Notification(source, summary, body,
-                                                        { bannerBody: true });
+                                                        { bannerBody: true,
+                                                          icon: iconActor });
             this._currentNotifications[id] = notification;
             notification.connect('dismissed', Lang.bind(this,
                 function(n) {
@@ -220,7 +251,8 @@ NotificationDaemon.prototype = {
                 }));
             notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
         } else {
-            notification.update(summary, body, { clear: true });
+            notification.update(summary, body, { icon: iconActor,
+                                                 clear: true });
         }
 
         if (actions.length) {
@@ -230,7 +262,8 @@ NotificationDaemon.prototype = {
 
         notification.setUrgent(hints.urgency == Urgency.CRITICAL);
 
-        source.notify(notification);
+        let sourceIconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
+        source.notify(sourceIconActor, notification);
         return id;
     },
 
@@ -300,57 +333,23 @@ NotificationDaemon.prototype = {
 
 DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
 
-function Source(title, icon, hints) {
-    this._init(title, icon, hints);
+function Source(title) {
+    this._init(title);
 }
 
 Source.prototype = {
     __proto__:  MessageTray.Source.prototype,
 
-    _init: function(title, icon, hints) {
+    _init: function(title) {
         MessageTray.Source.prototype._init.call(this, title);
 
         this.app = null;
         this._openAppRequested = false;
-
-        this.update(icon, hints);
-    },
-
-    update: function(icon, hints) {
-        this._icon = icon;
-        this._iconData = hints.icon_data;
-        this._urgency = hints.urgency;
     },
 
-    createIcon: function(size) {
-        let textureCache = St.TextureCache.get_default();
-
-        if (this._icon) {
-            if (this._icon.substr(0, 7) == 'file://')
-                return textureCache.load_uri_async(this._icon, size, size);
-            else if (this._icon[0] == '/') {
-                let uri = GLib.filename_to_uri(this._icon, null);
-                return textureCache.load_uri_async(uri, size, size);
-            } else
-                return textureCache.load_icon_name(this._icon, size);
-        } else if (this._iconData) {
-            let [width, height, rowStride, hasAlpha,
-                 bitsPerSample, nChannels, data] = this._iconData;
-            return textureCache.load_from_raw(data, data.length, hasAlpha,
-                                              width, height, rowStride, size);
-        } else {
-            let stockIcon;
-            switch (this._urgency) {
-                case Urgency.LOW:
-                case Urgency.NORMAL:
-                    stockIcon = 'gtk-dialog-info';
-                    break;
-                case Urgency.CRITICAL:
-                    stockIcon = 'gtk-dialog-error';
-                    break;
-            }
-            return textureCache.load_icon_name(stockIcon, size);
-        }
+    notify: function(icon, notification) {
+        this._setSummaryIcon(icon);
+        MessageTray.Source.prototype.notify.call(this, notification);
     },
 
     clicked: function() {
diff --git a/js/ui/telepathyClient.js b/js/ui/telepathyClient.js
index 49504fa..c9d5b24 100644
--- a/js/ui/telepathyClient.js
+++ b/js/ui/telepathyClient.js
@@ -475,10 +475,13 @@ Source.prototype = {
         this._receivedId = this._channelText.connect('Received', Lang.bind(this, this._messageReceived));
 
         this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages));
+
+        this._setSummaryIcon(this.createNotificationIcon());
     },
 
-    createIcon: function(size) {
-        return contactManager.createAvatar(this._conn, this._targetHandle, size);
+    createNotificationIcon: function() {
+        return contactManager.createAvatar(this._conn, this._targetHandle,
+                                           this.ICON_SIZE);
     },
 
     clicked: function() {
diff --git a/js/ui/windowAttentionHandler.js b/js/ui/windowAttentionHandler.js
index 79a86db..85a16e7 100644
--- a/js/ui/windowAttentionHandler.js
+++ b/js/ui/windowAttentionHandler.js
@@ -94,10 +94,11 @@ Source.prototype = {
         MessageTray.Source.prototype._init.call(this, app.get_name());
         this._window = window;
         this._app = app;
+        this._setSummaryIcon(this.createNotificationIcon());
     },
 
-    createIcon : function(size) {
-        return this._app.create_icon_texture(size);
+    createNotificationIcon : function() {
+        return this._app.create_icon_texture(this.ICON_SIZE);
     },
 
     clicked : function() {



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