[gnome-shell/message-tray] add an explicit message tray Source type



commit ef49ada57510ad386cabe6d09bb8f7e7cb9bf67b
Author: Dan Winship <danw gnome org>
Date:   Tue Dec 8 10:07:15 2009 -0500

    add an explicit message tray Source type
    
    https://bugzilla.gnome.org/show_bug.cgi?id=603546

 js/ui/messageTray.js        |   80 +++++++++++++++++++++++++++++++++++--------
 js/ui/messaging.js          |   22 ++++++------
 js/ui/notificationDaemon.js |   69 ++++++++++++++++++++++++++++++++----
 3 files changed, 137 insertions(+), 34 deletions(-)
---
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index 47d0b1b..39ccc76 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -1,8 +1,10 @@
 /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 
+const Clutter = imports.gi.Clutter;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 const St = imports.gi.St;
+const Signals = imports.signals;
 const Tweener = imports.ui.tweener;
 
 const Main = imports.ui.main;
@@ -68,9 +70,8 @@ Notification.prototype = {
     },
 
     hideComplete: function() {
-        // We don't explicitly destroy the icon, since the caller may
-        // still want it.
-        this._iconBox.child = null;
+        if (this._iconBox.child)
+            this._iconBox.child.destroy();
 
         // Don't hide the notification if we are showing a new one.
         if (this._hideTimeoutId == 0)
@@ -78,6 +79,37 @@ Notification.prototype = {
     }
 };
 
+function Source(id, createIcon) {
+    this._init(id, createIcon);
+}
+
+Source.prototype = {
+    _init: function(id, createIcon) {
+        this.id = id;
+        if (createIcon)
+            this.createIcon = createIcon;
+    },
+
+    // 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);
+    },
+
+    notify: function(text) {
+        Main.notificationPopup.show(this.createIcon(), text);
+    },
+
+    clicked: function() {
+        this.emit('clicked');
+    },
+
+    destroy: function() {
+        this.emit('destroy');
+    }
+};
+Signals.addSignalMethods(Source.prototype);
+
 function MessageTray() {
     this._init();
 }
@@ -105,30 +137,48 @@ MessageTray.prototype = {
         this._tray = new St.BoxLayout({ name: 'message-tray-inner' });
         this.actor.child = this._tray;
         this._tray.expand = true;
+        this._sources = {};
         this._icons = {};
     },
 
-    contains: function(id) {
-        return this._icons.hasOwnProperty(id);
+    contains: function(source) {
+        return this._sources.hasOwnProperty(source.id);
     },
 
-    add: function(id, icon) {
-        if (this.contains(id))
+    add: function(source) {
+        if (this.contains(source)) {
+            log('Trying to re-add source ' + source.id);
             return;
+        }
 
-        let iconBox = new St.Bin();
-        iconBox.child = icon;
+        let iconBox = new St.Bin({ reactive: true });
+        iconBox.child = source.createIcon();
         this._tray.insert_actor(iconBox, 0);
-        this._icons[id] = iconBox;
+        this._icons[source.id] = iconBox;
+        this._sources[source.id] = source;
+
+        iconBox.connect('button-release-event', Lang.bind(this,
+            function () {
+                source.clicked();
+            }));
+
+        source.connect('destroy', Lang.bind(this,
+            function () {
+                this.remove(source);
+            }));
     },
 
-    remove: function(id) {
-        if (!this.contains(id))
+    remove: function(source) {
+        if (!this.contains(source))
             return;
 
-        this._tray.remove_actor(this._icons[id]);
-        this._icons[id].destroy();
-        delete this._icons[id];
+        this._tray.remove_actor(this._icons[source.id]);
+        delete this._icons[source.id];
+        delete this._sources[source.id];
+    },
+
+    getSource: function(id) {
+        return this._sources[id];
     },
 
     _onMessageTrayEntered: function() {
diff --git a/js/ui/messaging.js b/js/ui/messaging.js
index db007d7..8cfc335 100644
--- a/js/ui/messaging.js
+++ b/js/ui/messaging.js
@@ -6,8 +6,7 @@ const Shell = imports.gi.Shell;
 const St = imports.gi.St;
 
 const Main = imports.ui.main;
-
-const AVATAR_SIZE = 24;
+const MessageTray = imports.ui.messageTray;
 
 const TELEPATHY = "org.freedesktop.Telepathy.";
 const CONN = TELEPATHY + "Connection";
@@ -386,7 +385,11 @@ function Source(conn, channelPath, channel_props, targetId) {
 }
 
 Source.prototype = {
+    __proto__:  MessageTray.Source.prototype,
+
     _init: function(conn, channelPath, targetHandle, targetId) {
+        MessageTray.Source.prototype._init.call(this, targetId);
+
         let connName = nameify(conn.getPath());
         this._channel = new Channel(connName, channelPath);
         this._closedId = this._channel.connect('Closed', Lang.bind(this, this._channelClosed));
@@ -403,8 +406,8 @@ Source.prototype = {
         this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages));
     },
 
-    _createIcon: function() {
-        return this._avatars.createAvatar(this._targetHandle, AVATAR_SIZE);
+    createIcon: function(size) {
+        return this._avatars.createAvatar(this._targetHandle, size);
     },
 
     _gotPendingMessages: function(msgs, excp) {
@@ -419,17 +422,14 @@ Source.prototype = {
         log('Channel closed ' + this._targetId);
         this._channel.disconnect(this._closedId);
         this._channelText.disconnect(this._receivedId);
-        Main.messageTray.remove(this._targetId);
+        this.destroy();
     },
 
     _receivedMessage: function(channel, id, timestamp, sender,
                                type, flags, text) {
         log('Received: id ' + id + ', time ' + timestamp + ', sender ' + sender + ', type ' + type + ', flags ' + flags + ': ' + text);
-        let popupAvatar = this._createIcon();
-        Main.notificationPopup.show(popupAvatar, text);
-        if (!Main.messageTray.contains(this._targetId)) {
-            let trayAvatar = this._createIcon();
-            Main.messageTray.add(this._targetId, trayAvatar);
-        }
+        if (!Main.messageTray.contains(this))
+            Main.messageTray.add(this);
+        this.notify(text);
     }
 };
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index 8ae0157..51044f1 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -6,6 +6,9 @@ const Shell = imports.gi.Shell;
 const Mainloop = imports.mainloop;
 
 const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
+
+let nextNotificationId = 1;
 
 const NotificationDaemonIface = {
     name: 'org.freedesktop.Notifications',
@@ -24,7 +27,18 @@ const NotificationDaemonIface = {
               { name: 'GetServerInformation',
                 inSignature: '',
                 outSignature: 'ssss'
-              }]
+              }],
+    signals: [{ name: 'NotificationClosed',
+                inSignature: 'uu' },
+              { name: 'ActionInvoked',
+                inSignature: 'us' }]
+};
+
+const NotificationClosedReason = {
+    EXPIRED: 1,
+    DISMISSED: 2,
+    APP_CLOSED: 3,
+    UNDEFINED: 4
 };
 
 function NotificationDaemon() {
@@ -49,20 +63,52 @@ NotificationDaemon.prototype = {
             });
     },
 
+    _sourceId: function(id) {
+        return 'notification-' + id;
+    },
+
     Notify: function(appName, replacesId, icon, summary, body,
                      actions, hints, timeout) {
-        let iconActor = null;
+        let id, source = null;
 
-        if (icon != '')
-            iconActor = Shell.TextureCache.get_default().load_icon_name(icon, 24);
-        else {
-            // FIXME: load icon data from hints
-        }            
+        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
+        }
 
-        Main.notificationPopup.show(iconActor, summary);
+        if (source == null) {
+            id = nextNotificationId++;
+
+            source = new MessageTray.Source(this._sourceId(id), Lang.bind(this,
+                function (size) {
+                    if (icon != '')
+                        return Shell.TextureCache.get_default().load_icon_name(icon, size);
+                    else {
+                        // FIXME: load icon data from hints
+                        // FIXME: better fallback icon
+                        return Shell.TextureCache.get_default().load_icon_name('gtk-dialog-info', size);
+                    }
+                }));
+            Main.messageTray.add(source);
+
+            source.connect('clicked', Lang.bind(this,
+                function() {
+                    source.destroy();
+                    this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
+                }));
+        }
+
+        source.notify(summary);
+        return id;
     },
 
     CloseNotification: function(id) {
+        let source = Main.messageTray.getSource(this._sourceId(id));
+        if (source)
+            source.destroy();
+        this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
     },
 
     GetCapabilities: function() {
@@ -85,6 +131,13 @@ NotificationDaemon.prototype = {
             '0.1', // FIXME, get this from somewhere
             '1.0'
         ];
+    },
+
+    _emitNotificationClosed: function(id, reason) {
+        DBus.session.emit_signal('/org/freedesktop/Notifications',
+                                 'org.freedesktop.Notifications',
+                                 'NotificationClosed', 'uu',
+                                 [id, reason]);
     }
 };
 



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