[gnome-shell] MessageTray: add right click menu for summary items



commit 868bf5838d5a669efb8799827ab817435306e4cb
Author: Hellyna Ng <hellyna hellyna com>
Date:   Sat Feb 12 03:43:01 2011 +0800

    MessageTray: add right click menu for summary items
    
    This provides straight forward controls for opening the corresponding
    application or removing the summary item.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=617224

 data/theme/gnome-shell.css      |   10 ++-
 js/ui/messageTray.js            |  197 ++++++++++++++++++++++++--------------
 js/ui/notificationDaemon.js     |    2 +-
 js/ui/popupMenu.js              |    8 ++-
 js/ui/telepathyClient.js        |    2 +-
 js/ui/windowAttentionHandler.js |    2 +-
 6 files changed, 142 insertions(+), 79 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index b317e6e..da69a3e 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -977,7 +977,7 @@ StTooltip StLabel {
     padding-bottom: 8px;
 }
 
-.summary-notification-boxpointer {
+.summary-boxpointer {
     -arrow-border-radius: 9px;
     -arrow-background-color: rgba(0,0,0,0.9);
     -arrow-border-width: 2px;
@@ -986,12 +986,18 @@ StTooltip StLabel {
     -arrow-rise: 15px;
 }
 
-.summary-notification-boxpointer #notification {
+.summary-boxpointer #notification {
     border-radius: 9px;
     background: rgba(0,0,0,0) !important;
     padding-bottom: 12px;
 }
 
+.summary-boxpointer #summary-right-click-menu {
+    font-size: 14px;
+    padding-top: 12px;
+    padding-bottom: 12px;
+}
+
 #notification-scrollview {
     max-height: 10em;
 }
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index d15bd62..29d882c 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -15,6 +15,7 @@ const St = imports.gi.St;
 const BoxPointer = imports.ui.boxpointer;
 const GnomeSession = imports.misc.gnomeSession;
 const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
 const Params = imports.misc.params;
 const Tweener = imports.ui.tweener;
 const Util = imports.misc.util;
@@ -853,7 +854,7 @@ Source.prototype = {
 
         this.notification = notification;
 
-        this._notificationClickedId = notification.connect('clicked', Lang.bind(this, this._notificationClicked));
+        this._notificationClickedId = notification.connect('clicked', Lang.bind(this, this.open));
         this._notificationDestroyedId = notification.connect('destroy', Lang.bind(this,
             function () {
                 if (this.notification == notification) {
@@ -881,7 +882,7 @@ Source.prototype = {
     },
 
     // Default implementation is to do nothing, but subclasses can override
-    _notificationClicked: function(notification) {
+    open: function(notification) {
     },
 
     // Default implementation is to destroy this source, but subclasses can override
@@ -917,6 +918,27 @@ SummaryItem.prototype = {
         this._sourceBox.add_actor(this._sourceIcon);
         this._sourceBox.add_actor(this._sourceTitleBin, { expand: true });
         this.actor.child = this._sourceBox;
+        this.rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
+                                                 vertical: true });
+
+        let item;
+
+        item = new PopupMenu.PopupMenuItem(_("Open"));
+        item.connect('activate', Lang.bind(this, function() {
+            source.open();
+            this.emit('right-click-menu-done-displaying');
+        }));
+        this.rightClickMenu.add(item.actor);
+
+        item = new PopupMenu.PopupMenuItem(_("Remove"));
+        item.connect('activate', Lang.bind(this, function() {
+            source.destroy();
+            this.emit('right-click-menu-done-displaying');
+        }));
+        this.rightClickMenu.add(item.actor);
+
+        let focusManager = St.FocusManager.get_for_stage(global.stage);
+        focusManager.add_group(this.rightClickMenu);
     },
 
     // getTitleNaturalWidth, getTitleWidth, and setTitleWidth include
@@ -943,6 +965,7 @@ SummaryItem.prototype = {
         this._sourceTitle.clutter_text.ellipsize = mode;
     }
 };
+Signals.addSignalMethods(SummaryItem.prototype);
 
 function MessageTray() {
     this._init();
@@ -980,17 +1003,19 @@ MessageTray.prototype = {
 
         this._summaryMotionId = 0;
 
-        this._summaryNotificationBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
-                                                                        { reactive: true,
-                                                                          track_hover: true });
-        this._summaryNotificationBoxPointer.actor.style_class = 'summary-notification-boxpointer';
-        this.actor.add_actor(this._summaryNotificationBoxPointer.actor);
-        this._summaryNotificationBoxPointer.actor.lower_bottom();
-        this._summaryNotificationBoxPointer.actor.hide();
+        this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
+                                                           { reactive: true,
+                                                             track_hover: true });
+        this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
+        this.actor.add_actor(this._summaryBoxPointer.actor);
+        this._summaryBoxPointer.actor.lower_bottom();
+        this._summaryBoxPointer.actor.hide();
 
         this._summaryNotification = null;
         this._summaryNotificationClickedId = 0;
+        this._summaryRightClickMenuClickedId = 0;
         this._clickedSummaryItem = null;
+        this._clickedSummaryItemMouseButton = -1;
         this._clickedSummaryItemAllocationChangedId = 0;
         this._expandedSummaryItem = null;
         this._summaryItemTitleWidth = 0;
@@ -1004,7 +1029,7 @@ MessageTray.prototype = {
         this._focusGrabber = new FocusGrabber();
         this._focusGrabber.connect('focus-grabbed', Lang.bind(this,
             function() {
-                if (this._summaryNotification)
+                if (this._summaryBoxPointer.bin.child)
                     this._lock();
             }));
         this._focusGrabber.connect('focus-ungrabbed', Lang.bind(this, this._unlock));
@@ -1027,7 +1052,7 @@ MessageTray.prototype = {
         this._notificationState = State.HIDDEN;
         this._notificationTimeoutId = 0;
         this._notificationExpandedId = 0;
-        this._summaryNotificationState = State.HIDDEN;
+        this._summaryBoxPointerState = State.HIDDEN;
         this._summaryNotificationTimeoutId = 0;
         this._summaryNotificationExpandedId = 0;
         this._overviewVisible = Main.overview.visible;
@@ -1037,7 +1062,7 @@ MessageTray.prototype = {
         Main.chrome.addActor(this.actor, { affectsStruts: false,
                                            visibleInOverview: true });
         Main.chrome.trackActor(this._notificationBin);
-        Main.chrome.trackActor(this._summaryNotificationBoxPointer.actor);
+        Main.chrome.trackActor(this._summaryBoxPointer.actor);
 
         global.gdk_screen.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
 
@@ -1138,9 +1163,9 @@ MessageTray.prototype = {
                 this._onSummaryItemHoverChanged(summaryItem);
             }));
 
-        summaryItem.actor.connect('clicked', Lang.bind(this,
-            function () {
-                this._onSummaryItemClicked(summaryItem);
+        summaryItem.actor.connect('button-press-event', Lang.bind(this,
+            function (actor, event) {
+                this._onSummaryItemClicked(summaryItem, event);
             }));
 
         source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
@@ -1354,11 +1379,16 @@ MessageTray.prototype = {
             this._expandedSummaryItem.setEllipsization(Pango.EllipsizeMode.END);
     },
 
-    _onSummaryItemClicked: function(summaryItem) {
-        if (!this._clickedSummaryItem || this._clickedSummaryItem != summaryItem)
+    _onSummaryItemClicked: function(summaryItem, event) {
+        let clickedButton = event.get_button();
+        if (!this._clickedSummaryItem ||
+            this._clickedSummaryItem != summaryItem ||
+            this._clickedSummaryItemMouseButton != clickedButton) {
             this._clickedSummaryItem = summaryItem;
-        else
+            this._clickedSummaryItemMouseButton = clickedButton;
+        } else {
             this._unsetClickedSummaryItem();
+        }
 
         this._updateState();
     },
@@ -1385,7 +1415,7 @@ MessageTray.prototype = {
             // leaving the tray. The tray is locked when the summary notification is visible anyway, but we
             // should treat the mouse being over the summary notification as the tray being left for collapsing
             // any expanded summary item other than the one related to the notification.
-            if (this._summaryNotificationBoxPointer.bin.hover)
+            if (this._summaryBoxPointer.bin.hover)
                 return;
 
             this._useLongerTrayLeftTimeout = false;
@@ -1543,19 +1573,23 @@ MessageTray.prototype = {
         }
 
         // Summary notification
-        let haveSummaryNotification = this._clickedSummaryItem != null;
-        let summaryNotificationIsMainNotification = (haveSummaryNotification &&
+        let haveClickedSummaryItem = this._clickedSummaryItem != null;
+        let summaryNotificationIsMainNotification = (haveClickedSummaryItem &&
                                                      this._clickedSummaryItem.source.notification == this._notification);
-        let canShowSummaryNotification = this._summaryState == State.SHOWN;
-        let wrongSummaryNotification = (haveSummaryNotification &&
+        let canShowSummaryBoxPointer = this._summaryState == State.SHOWN;
+        let wrongSummaryNotification = (this._clickedSummaryItemMouseButton == 1 &&
                                         this._summaryNotification != this._clickedSummaryItem.source.notification);
-
-        if (this._summaryNotificationState == State.HIDDEN) {
-            if (haveSummaryNotification && !summaryNotificationIsMainNotification && canShowSummaryNotification)
-                this._showSummaryNotification();
-        } else if (this._summaryNotificationState == State.SHOWN) {
-            if (!haveSummaryNotification || !canShowSummaryNotification || wrongSummaryNotification)
-                this._hideSummaryNotification();
+        let wrongSummaryRightClickMenu = (this._clickedSummaryItemMouseButton == 3 &&
+                                          this._summaryBoxPointer.bin.child != this._clickedSummaryItem.rightClickMenu);
+        let wrongSummaryBoxPointer = (haveClickedSummaryItem &&
+                                      (wrongSummaryNotification || wrongSummaryRightClickMenu));
+
+        if (this._summaryBoxPointerState == State.HIDDEN) {
+            if (haveClickedSummaryItem && !summaryNotificationIsMainNotification && canShowSummaryBoxPointer)
+                this._showSummaryBoxPointer();
+        } else if (this._summaryBoxPointerState == State.SHOWN) {
+            if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer)
+                this._hideSummaryBoxPointer();
         }
 
         // Tray itself
@@ -1813,44 +1847,53 @@ MessageTray.prototype = {
         this._expandedSummaryItemTitleWidth = this._summaryItemTitleWidth;
     },
 
-    _showSummaryNotification: function() {
-        this._summaryNotification = this._clickedSummaryItem.source.notification;
-        this._summaryNotificationClickedId = this._summaryNotification.connect('done-displaying',
-                                                                               Lang.bind(this, this._escapeTray));
-        let index = this._notificationQueue.indexOf(this._summaryNotification);
-        if (index != -1)
-            this._notificationQueue.splice(index, 1);
+    _showSummaryBoxPointer: function() {
+        if (this._clickedSummaryItemMouseButton == 1) {
+            let clickedSummaryItemNotification = this._clickedSummaryItem.source.notification;
+            let index = this._notificationQueue.indexOf(clickedSummaryItemNotification);
+            if (index != -1)
+                this._notificationQueue.splice(index, 1);
+
+            this._summaryNotification = clickedSummaryItemNotification;
+            this._summaryNotificationClickedId = this._summaryNotification.connect('done-displaying',
+                                                                                   Lang.bind(this, this._escapeTray));
+            this._summaryBoxPointer.bin.child = this._summaryNotification.actor;
+            if (!this._summaryNotificationExpandedId)
+                this._summaryNotificationExpandedId = this._summaryNotification.connect('expanded',
+                                                                                        Lang.bind(this, this._onSummaryBoxPointerExpanded));
+            this._summaryNotification.expand(false);
+        } else if (this._clickedSummaryItemMouseButton == 3) {
+            this._summaryRightClickMenuClickedId = this._clickedSummaryItem.connect('right-click-menu-done-displaying',
+                                                                                    Lang.bind(this, this._escapeTray));
+            this._summaryBoxPointer.bin.child = this._clickedSummaryItem.rightClickMenu;
+        }
 
-        this._summaryNotificationBoxPointer.bin.child = this._summaryNotification.actor;
-        this._focusGrabber.grabFocus(this._summaryNotification.actor);
+        this._focusGrabber.grabFocus(this._summaryBoxPointer.bin.child);
 
-        if (!this._summaryNotificationExpandedId)
-            this._summaryNotificationExpandedId = this._summaryNotification.connect('expanded', Lang.bind(this, this._onSummaryNotificationExpanded));
-        this._summaryNotification.expand(false);
 
         this._clickedSummaryItemAllocationChangedId =
             this._clickedSummaryItem.actor.connect('allocation-changed',
-                                                   Lang.bind(this, this._adjustNotificationBoxPointerPosition));
+                                                   Lang.bind(this, this._adjustSummaryBoxPointerPosition));
         // _clickedSummaryItem.actor can change absolute postiion without changing allocation
         this._summaryMotionId = this._summary.connect('allocation-changed',
-                                                      Lang.bind(this, this._adjustNotificationBoxPointerPosition));
+                                                      Lang.bind(this, this._adjustSummaryBoxPointerPosition));
 
-        this._summaryNotificationBoxPointer.actor.opacity = 0;
-        this._summaryNotificationBoxPointer.actor.show();
-        this._adjustNotificationBoxPointerPosition();
+        this._summaryBoxPointer.actor.opacity = 0;
+        this._summaryBoxPointer.actor.show();
+        this._adjustSummaryBoxPointerPosition();
 
-        this._summaryNotificationState = State.SHOWING;
-        this._summaryNotificationBoxPointer.show(true, Lang.bind(this, function() {
-            this._summaryNotificationState = State.SHOWN;
+        this._summaryBoxPointerState = State.SHOWING;
+        this._summaryBoxPointer.show(true, Lang.bind(this, function() {
+            this._summaryBoxPointerState = State.SHOWN;
         }));
     },
 
-    _adjustNotificationBoxPointerPosition: function() {
+    _adjustSummaryBoxPointerPosition: function() {
         // The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor
         if (!this._clickedSummaryItem)
             return;
 
-        this._summaryNotificationBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5);
+        this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5);
     },
 
     _unsetClickedSummaryItem: function() {
@@ -1861,14 +1904,20 @@ MessageTray.prototype = {
             this._summaryMotionId = 0;
         }
 
+        if (this._summaryRightClickMenuClickedId) {
+            this._clickedSummaryItem.disconnect(this._summaryRightClickMenuClickedId);
+            this._summaryRightClickMenuClickedId = 0;
+        }
+
         this._clickedSummaryItem = null;
+        this._clickedSummaryItemMouseButton = -1;
     },
 
-    _onSummaryNotificationExpanded: function() {
-        this._adjustNotificationBoxPointerPosition();
+    _onSummaryBoxPointerExpanded: function() {
+        this._adjustSummaryBoxPointerPosition();
     },
 
-    _hideSummaryNotification: function() {
+    _hideSummaryBoxPointer: function() {
         if (this._summaryNotificationExpandedId) {
             this._summaryNotification.disconnect(this._summaryNotificationExpandedId);
             this._summaryNotificationExpandedId = 0;
@@ -1878,23 +1927,25 @@ MessageTray.prototype = {
             this._unsetClickedSummaryItem();
 
         this._focusGrabber.ungrabFocus();
-        this._summaryNotificationState = State.HIDING;
-        this._summaryNotificationBoxPointer.hide(true, Lang.bind(this, this._hideSummaryNotificationCompleted));
-    },
-
-    _hideSummaryNotificationCompleted: function() {
-        this._summaryNotificationState = State.HIDDEN;
-        this._summaryNotificationBoxPointer.bin.child = null;
-        this._summaryNotification.collapseCompleted();
-        this._summaryNotification.disconnect(this._summaryNotificationClickedId);
-        this._summaryNotificationClickedId = 0;
-        let summaryNotification = this._summaryNotification;
-        this._summaryNotification = null;
-        if (summaryNotification.isTransient && !this._reNotifyWithSummaryNotificationAfterHide)
-            summaryNotification.destroy(NotificationDestroyedReason.EXPIRED);
-        if (this._reNotifyWithSummaryNotificationAfterHide) {
-            this._onNotify(summaryNotification.source, summaryNotification);
-            this._reNotifyWithSummaryNotificationAfterHide = false;
+        this._summaryBoxPointerState = State.HIDING;
+        this._summaryBoxPointer.hide(true, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
+    },
+
+    _hideSummaryBoxPointerCompleted: function() {
+        this._summaryBoxPointerState = State.HIDDEN;
+        this._summaryBoxPointer.bin.child = null;
+        if (this._summaryNotification != null) {
+            this._summaryNotification.collapseCompleted();
+            this._summaryNotification.disconnect(this._summaryNotificationClickedId);
+            this._summaryNotificationClickedId = 0;
+            let summaryNotification = this._summaryNotification;
+            this._summaryNotification = null;
+            if (summaryNotification.isTransient && !this._reNotifyWithSummaryNotificationAfterHide)
+                summaryNotification.destroy(NotificationDestroyedReason.EXPIRED);
+            if (this._reNotifyWithSummaryNotificationAfterHide) {
+                this._onNotify(summaryNotification.source, summaryNotification);
+                this._reNotifyWithSummaryNotificationAfterHide = false;
+            }
         }
         if (this._clickedSummaryItem)
             this._updateState();
@@ -1920,7 +1971,7 @@ SystemNotificationSource.prototype = {
                              icon_size: this.ICON_SIZE });
     },
 
-    _notificationClicked: function() {
+    open: function() {
         this.destroy();
     }
 };
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index 2285d58..7953b85 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -473,7 +473,7 @@ Source.prototype = {
         this._isTrayIcon = true;
     },
 
-    _notificationClicked: function(notification) {
+    open: function(notification) {
         this.openApp();
     },
 
diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js
index 6e753fd..e16220c 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -54,8 +54,10 @@ PopupBaseMenuItem.prototype = {
         }
         if (params.reactive && params.hover)
             this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged));
-        if (params.reactive)
+        if (params.reactive) {
             this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
+            this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
+        }
     },
 
     _onStyleChanged: function (actor) {
@@ -81,6 +83,10 @@ PopupBaseMenuItem.prototype = {
         this.setActive(true);
     },
 
+    _onKeyFocusOut: function (actor) {
+        this.setActive(false);
+    },
+
     _onHoverChanged: function (actor) {
         this.setActive(actor.hover);
     },
diff --git a/js/ui/telepathyClient.js b/js/ui/telepathyClient.js
index 5ddaaef..d835d3e 100644
--- a/js/ui/telepathyClient.js
+++ b/js/ui/telepathyClient.js
@@ -173,7 +173,7 @@ Source.prototype = {
         }
     },
 
-    _notificationClicked: function(notification) {
+    open: function(notification) {
         let props = {};
         props[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
         [props[Tp.PROP_CHANNEL_TARGET_HANDLE], props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE]] = this._channel.get_handle();
diff --git a/js/ui/windowAttentionHandler.js b/js/ui/windowAttentionHandler.js
index fd80cd9..560e915 100644
--- a/js/ui/windowAttentionHandler.js
+++ b/js/ui/windowAttentionHandler.js
@@ -101,7 +101,7 @@ Source.prototype = {
         return this._app.create_icon_texture(this.ICON_SIZE);
     },
 
-    _notificationClicked : function(notification) {
+    open : function(notification) {
         Main.activateWindow(this._window);
         this.destroy();
     }



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