Blame SOURCES/0001-window-list-Add-context-menu.patch

47f2c7
From 1155edadcd514ce053beab5e258df13fd2a79dd1 Mon Sep 17 00:00:00 2001
47f2c7
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
47f2c7
Date: Mon, 27 May 2013 23:41:56 +0200
47f2c7
Subject: [PATCH 1/2] window-list: Add context menu
47f2c7
47f2c7
gnome-panel's window list had context menus on buttons, that gave
47f2c7
easy access to common operations like close, minimize and maximize.
47f2c7
Add something similar to the window-list.
47f2c7
47f2c7
https://bugzilla.gnome.org/show_bug.cgi?id=699251
47f2c7
---
47f2c7
 extensions/window-list/extension.js | 255 ++++++++++++++++++++++++++++++++----
47f2c7
 1 file changed, 232 insertions(+), 23 deletions(-)
47f2c7
47f2c7
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
47f2c7
index 1c46791..7a9d392 100644
47f2c7
--- a/extensions/window-list/extension.js
47f2c7
+++ b/extensions/window-list/extension.js
47f2c7
@@ -37,6 +37,85 @@ function _minimizeOrActivateWindow(window) {
47f2c7
             window.activate(global.get_current_time());
47f2c7
 }
47f2c7
 
47f2c7
+function _openMenu(menu) {
47f2c7
+    menu.open();
47f2c7
+
47f2c7
+    let event = Clutter.get_current_event();
47f2c7
+    if (event && event.type() == Clutter.EventType.KEY_RELEASE)
47f2c7
+        menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
47f2c7
+}
47f2c7
+
47f2c7
+
47f2c7
+const WindowContextMenu = new Lang.Class({
47f2c7
+    Name: 'WindowContextMenu',
47f2c7
+    Extends: PopupMenu.PopupMenu,
47f2c7
+
47f2c7
+    _init: function(source, metaWindow) {
47f2c7
+        this.parent(source, 0.5, St.Side.BOTTOM);
47f2c7
+
47f2c7
+        this._metaWindow = metaWindow;
47f2c7
+
47f2c7
+        this._minimizeItem = new PopupMenu.PopupMenuItem('');
47f2c7
+        this._minimizeItem.connect('activate', Lang.bind(this, function() {
47f2c7
+            if (this._metaWindow.minimized)
47f2c7
+                this._metaWindow.unminimize();
47f2c7
+            else
47f2c7
+                this._metaWindow.minimize();
47f2c7
+        }));
47f2c7
+        this.addMenuItem(this._minimizeItem);
47f2c7
+
47f2c7
+        this._notifyMinimizedId =
47f2c7
+            this._metaWindow.connect('notify::minimized',
47f2c7
+                                     Lang.bind(this, this._updateMinimizeItem));
47f2c7
+        this._updateMinimizeItem();
47f2c7
+
47f2c7
+        this._maximizeItem = new PopupMenu.PopupMenuItem('');
47f2c7
+        this._maximizeItem.connect('activate', Lang.bind(this, function() {
47f2c7
+            if (this._metaWindow.maximized_vertically &&
47f2c7
+                this._metaWindow.maximized_horizontally)
47f2c7
+                this._metaWindow.unmaximize(Meta.MaximizeFlags.HORIZONTAL |
47f2c7
+                                            Meta.MaximizeFlags.VERTICAL);
47f2c7
+            else
47f2c7
+                this._metaWindow.maximize(Meta.MaximizeFlags.HORIZONTAL |
47f2c7
+                                          Meta.MaximizeFlags.VERTICAL);
47f2c7
+        }));
47f2c7
+        this.addMenuItem(this._maximizeItem);
47f2c7
+
47f2c7
+        this._notifyMaximizedHId =
47f2c7
+            this._metaWindow.connect('notify::maximized-horizontally',
47f2c7
+                                     Lang.bind(this, this._updateMaximizeItem));
47f2c7
+        this._notifyMaximizedVId =
47f2c7
+            this._metaWindow.connect('notify::maximized-vertically',
47f2c7
+                                     Lang.bind(this, this._updateMaximizeItem));
47f2c7
+        this._updateMaximizeItem();
47f2c7
+
47f2c7
+        let item = new PopupMenu.PopupMenuItem(_("Close"));
47f2c7
+        item.connect('activate', Lang.bind(this, function() {
47f2c7
+            this._metaWindow.delete(global.get_current_time());
47f2c7
+        }));
47f2c7
+        this.addMenuItem(item);
47f2c7
+
47f2c7
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
47f2c7
+    },
47f2c7
+
47f2c7
+    _updateMinimizeItem: function() {
47f2c7
+        this._minimizeItem.label.text = this._metaWindow.minimized ? _("Unminimize")
47f2c7
+                                                                   : _("Minimize");
47f2c7
+    },
47f2c7
+
47f2c7
+    _updateMaximizeItem: function() {
47f2c7
+        let maximized = this._metaWindow.maximized_vertically &&
47f2c7
+                        this._metaWindow.maximized_horizontally;
47f2c7
+        this._maximizeItem.label.text = maximized ? _("Unmaximize")
47f2c7
+                                                  : _("Maximize");
47f2c7
+    },
47f2c7
+
47f2c7
+    _onDestroy: function() {
47f2c7
+        this._metaWindow.disconnect(this._notifyMinimizedId);
47f2c7
+        this._metaWindow.disconnect(this._notifyMaximizedHId);
47f2c7
+        this._metaWindow.disconnect(this._notifyMaximizedVId);
47f2c7
+    }
47f2c7
+});
47f2c7
 
47f2c7
 const WindowTitle = new Lang.Class({
47f2c7
     Name: 'WindowTitle',
47f2c7
@@ -99,13 +178,22 @@ const WindowButton = new Lang.Class({
47f2c7
         this.actor = new St.Button({ style_class: 'window-button',
47f2c7
                                      x_fill: true,
47f2c7
                                      can_focus: true,
47f2c7
+                                     button_mask: St.ButtonMask.ONE |
47f2c7
+                                                  St.ButtonMask.THREE,
47f2c7
                                      child: this._windowTitle.actor });
47f2c7
         this.actor._delegate = this;
47f2c7
 
47f2c7
+        this._menuManager = new PopupMenu.PopupMenuManager(this);
47f2c7
+        this._contextMenu = new WindowContextMenu(this.actor, this.metaWindow);
47f2c7
+        this._contextMenu.actor.hide();
47f2c7
+        this._menuManager.addMenu(this._contextMenu);
47f2c7
+        Main.uiGroup.add_actor(this._contextMenu.actor);
47f2c7
+
47f2c7
         this.actor.connect('allocation-changed',
47f2c7
                            Lang.bind(this, this._updateIconGeometry));
47f2c7
         this.actor.connect('clicked', Lang.bind(this, this._onClicked));
47f2c7
         this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
47f2c7
+        this.actor.connect('popup-menu', Lang.bind(this, this._onPopupMenu));
47f2c7
 
47f2c7
         this._switchWorkspaceId =
47f2c7
             global.window_manager.connect('switch-workspace',
47f2c7
@@ -118,8 +206,22 @@ const WindowButton = new Lang.Class({
47f2c7
         this._updateStyle();
47f2c7
     },
47f2c7
 
47f2c7
-    _onClicked: function() {
47f2c7
-        _minimizeOrActivateWindow(this.metaWindow);
47f2c7
+    _onClicked: function(actor, button) {
47f2c7
+        if (this._contextMenu.isOpen) {
47f2c7
+            this._contextMenu.close();
47f2c7
+            return;
47f2c7
+        }
47f2c7
+
47f2c7
+        if (button == 1)
47f2c7
+            _minimizeOrActivateWindow(this.metaWindow);
47f2c7
+        else
47f2c7
+            _openMenu(this._contextMenu);
47f2c7
+    },
47f2c7
+
47f2c7
+    _onPopupMenu: function(actor) {
47f2c7
+        if (this._contextMenu.isOpen)
47f2c7
+            return;
47f2c7
+        _openMenu(this._contextMenu);
47f2c7
     },
47f2c7
 
47f2c7
     _updateStyle: function() {
47f2c7
@@ -151,10 +253,82 @@ const WindowButton = new Lang.Class({
47f2c7
     _onDestroy: function() {
47f2c7
         global.window_manager.disconnect(this._switchWorkspaceId);
47f2c7
         global.display.disconnect(this._notifyFocusId);
47f2c7
+        this._contextMenu.actor.destroy();
47f2c7
     }
47f2c7
 });
47f2c7
 
47f2c7
 
47f2c7
+const AppContextMenu = new Lang.Class({
47f2c7
+    Name: 'AppContextMenu',
47f2c7
+    Extends: PopupMenu.PopupMenu,
47f2c7
+
47f2c7
+    _init: function(source, app) {
47f2c7
+        this.parent(source, 0.5, St.Side.BOTTOM);
47f2c7
+
47f2c7
+        this._app = app;
47f2c7
+
47f2c7
+        this._minimizeItem = new PopupMenu.PopupMenuItem(_("Minimize all"));
47f2c7
+        this._minimizeItem.connect('activate', Lang.bind(this, function() {
47f2c7
+            this._app.get_windows().forEach(function(w) {
47f2c7
+                w.minimize();
47f2c7
+            });
47f2c7
+        }));
47f2c7
+        this.addMenuItem(this._minimizeItem);
47f2c7
+
47f2c7
+        this._unminimizeItem = new PopupMenu.PopupMenuItem(_("Unminimize all"));
47f2c7
+        this._unminimizeItem.connect('activate', Lang.bind(this, function() {
47f2c7
+            this._app.get_windows().forEach(function(w) {
47f2c7
+                w.unminimize();
47f2c7
+            });
47f2c7
+        }));
47f2c7
+        this.addMenuItem(this._unminimizeItem);
47f2c7
+
47f2c7
+        this._maximizeItem = new PopupMenu.PopupMenuItem(_("Maximize all"));
47f2c7
+        this._maximizeItem.connect('activate', Lang.bind(this, function() {
47f2c7
+            this._app.get_windows().forEach(function(w) {
47f2c7
+                w.maximize(Meta.MaximizeFlags.HORIZONTAL |
47f2c7
+                           Meta.MaximizeFlags.VERTICAL);
47f2c7
+            });
47f2c7
+        }));
47f2c7
+        this.addMenuItem(this._maximizeItem);
47f2c7
+
47f2c7
+        this._unmaximizeItem = new PopupMenu.PopupMenuItem(_("Unmaximize all"));
47f2c7
+        this._unmaximizeItem.connect('activate', Lang.bind(this, function() {
47f2c7
+            this._app.get_windows().forEach(function(w) {
47f2c7
+                w.unmaximize(Meta.MaximizeFlags.HORIZONTAL |
47f2c7
+                             Meta.MaximizeFlags.VERTICAL);
47f2c7
+            });
47f2c7
+        }));
47f2c7
+        this.addMenuItem(this._unmaximizeItem);
47f2c7
+
47f2c7
+        let item = new PopupMenu.PopupMenuItem(_("Close all"));
47f2c7
+        item.connect('activate', Lang.bind(this, function() {
47f2c7
+            this._app.get_windows().forEach(function(w) {
47f2c7
+                w.delete(global.get_current_time());
47f2c7
+            });
47f2c7
+        }));
47f2c7
+        this.addMenuItem(item);
47f2c7
+    },
47f2c7
+
47f2c7
+    open: function(animate) {
47f2c7
+        let windows = this._app.get_windows();
47f2c7
+        this._minimizeItem.actor.visible = windows.some(function(w) {
47f2c7
+            return !w.minimized;
47f2c7
+        });
47f2c7
+        this._unminimizeItem.actor.visible = windows.some(function(w) {
47f2c7
+            return w.minimized;
47f2c7
+        });
47f2c7
+        this._maximizeItem.actor.visible = windows.some(function(w) {
47f2c7
+            return !(w.maximized_horizontally && w.maximized_vertically);
47f2c7
+        });
47f2c7
+        this._unmaximizeItem.actor.visible = windows.some(function(w) {
47f2c7
+            return w.maximized_horizontally && w.maximized_vertically;
47f2c7
+        });
47f2c7
+
47f2c7
+        this.parent(animate);
47f2c7
+    }
47f2c7
+});
47f2c7
+
47f2c7
 const AppButton = new Lang.Class({
47f2c7
     Name: 'AppButton',
47f2c7
 
47f2c7
@@ -165,6 +339,8 @@ const AppButton = new Lang.Class({
47f2c7
         this.actor = new St.Button({ style_class: 'window-button',
47f2c7
                                      x_fill: true,
47f2c7
                                      can_focus: true,
47f2c7
+                                     button_mask: St.ButtonMask.ONE |
47f2c7
+                                                  St.ButtonMask.THREE,
47f2c7
                                      child: stack });
47f2c7
         this.actor._delegate = this;
47f2c7
 
47f2c7
@@ -190,6 +366,12 @@ const AppButton = new Lang.Class({
47f2c7
         this._menuManager.addMenu(this._menu);
47f2c7
         Main.uiGroup.add_actor(this._menu.actor);
47f2c7
 
47f2c7
+        this._contextMenuManager = new PopupMenu.PopupMenuManager(this);
47f2c7
+        this._appContextMenu = new AppContextMenu(this.actor, this.app);
47f2c7
+        this._appContextMenu.actor.hide();
47f2c7
+        this._contextMenuManager.addMenu(this._appContextMenu);
47f2c7
+        Main.uiGroup.add_actor(this._appContextMenu.actor);
47f2c7
+
47f2c7
         this._textureCache = St.TextureCache.get_default();
47f2c7
         this._iconThemeChangedId =
47f2c7
             this._textureCache.connect('icon-theme-changed', Lang.bind(this,
47f2c7
@@ -198,6 +380,7 @@ const AppButton = new Lang.Class({
47f2c7
                 }));
47f2c7
         this.actor.connect('clicked', Lang.bind(this, this._onClicked));
47f2c7
         this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
47f2c7
+        this.actor.connect('popup-menu', Lang.bind(this, this._onPopupMenu));
47f2c7
 
47f2c7
         this._switchWorkspaceId =
47f2c7
             global.window_manager.connect('switch-workspace',
47f2c7
@@ -258,43 +441,69 @@ const AppButton = new Lang.Class({
47f2c7
                 this.metaWindow = windows[0];
47f2c7
                 this._windowTitle = new WindowTitle(this.metaWindow);
47f2c7
                 this._singleWindowTitle.child = this._windowTitle.actor;
47f2c7
+                this._windowContextMenu = new WindowContextMenu(this.actor, this.metaWindow);
47f2c7
+                Main.uiGroup.add_actor(this._windowContextMenu.actor);
47f2c7
+                this._windowContextMenu.actor.hide();
47f2c7
+                this._contextMenuManager.addMenu(this._windowContextMenu);
47f2c7
             }
47f2c7
+            this._contextMenu = this._windowContextMenu;
47f2c7
         } else {
47f2c7
             if (this._windowTitle) {
47f2c7
                 this.metaWindow = null;
47f2c7
                 this._singleWindowTitle.child = null;
47f2c7
                 this._windowTitle = null;
47f2c7
+                this._windowContextMenu.actor.destroy();
47f2c7
+                this._windowContextMenu = null;
47f2c7
             }
47f2c7
+            this._contextMenu = this._appContextMenu;
47f2c7
         }
47f2c7
+
47f2c7
     },
47f2c7
 
47f2c7
-    _onClicked: function() {
47f2c7
-        if (this._menu.isOpen) {
47f2c7
+    _onClicked: function(actor, button) {
47f2c7
+        let menuWasOpen = this._menu.isOpen;
47f2c7
+        if (menuWasOpen)
47f2c7
             this._menu.close();
47f2c7
-            return;
47f2c7
-        }
47f2c7
 
47f2c7
-        let windows = this._getWindowList();
47f2c7
-        if (windows.length == 1) {
47f2c7
-            _minimizeOrActivateWindow(windows[0]);
47f2c7
-        } else {
47f2c7
-            this._menu.removeAll();
47f2c7
-
47f2c7
-            for (let i = 0; i < windows.length; i++) {
47f2c7
-                let windowTitle = new WindowTitle(windows[i]);
47f2c7
-                let item = new PopupMenu.PopupBaseMenuItem();
47f2c7
-                item.addActor(windowTitle.actor);
47f2c7
-                item._window = windows[i];
47f2c7
-                this._menu.addMenuItem(item);
47f2c7
-            }
47f2c7
-            this._menu.open();
47f2c7
+        let contextMenuWasOpen = this._contextMenu.isOpen;
47f2c7
+        if (contextMenuWasOpen)
47f2c7
+            this._contextMenu.close();
47f2c7
+
47f2c7
+        if (button == 1) {
47f2c7
+            if (menuWasOpen)
47f2c7
+                return;
47f2c7
 
47f2c7
-            let event = Clutter.get_current_event();
47f2c7
-            if (event && event.type() == Clutter.EventType.KEY_RELEASE)
47f2c7
-                this._menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
47f2c7
+            let windows = this._getWindowList();
47f2c7
+            if (windows.length == 1) {
47f2c7
+                if (contextMenuWasOpen)
47f2c7
+                    return;
47f2c7
+                _minimizeOrActivateWindow(windows[0]);
47f2c7
+            } else {
47f2c7
+                this._menu.removeAll();
47f2c7
+
47f2c7
+                for (let i = 0; i < windows.length; i++) {
47f2c7
+                    let windowTitle = new WindowTitle(windows[i]);
47f2c7
+                    let item = new PopupMenu.PopupBaseMenuItem();
3614ae
+                    item.addActor(windowTitle.actor);
47f2c7
+                    item._window = windows[i];
47f2c7
+                    this._menu.addMenuItem(item);
47f2c7
+                }
47f2c7
+                _openMenu(this._menu);
47f2c7
+            }
47f2c7
+        } else {
47f2c7
+            if (contextMenuWasOpen)
47f2c7
+                return;
47f2c7
+            _openMenu(this._contextMenu);
47f2c7
         }
47f2c7
     },
47f2c7
 
47f2c7
+    _onPopupMenu: function(actor) {
47f2c7
+        if (this._menu.isOpen || this._contextMenu.isOpen)
47f2c7
+            return;
47f2c7
+        _openMenu(this._contextMenu);
47f2c7
+    },
47f2c7
+
47f2c7
+
47f2c7
     _onMenuActivate: function(menu, child) {
47f2c7
         child._window.activate(global.get_current_time());
47f2c7
     },
47f2c7
-- 
47f2c7
1.8.3.1
47f2c7