[gnome-shell-extensions] alternate-tab: more alternative implementations



commit 002b1b5d07affda9c0d03c0c8baa25aa416a63f7
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Mon Aug 1 15:36:45 2011 +0200

    alternate-tab: more alternative implementations
    
    Now alternate-tab shows a dialog on first usage, allowing to choose
    between native behavior (app icons from all worskpaces), app icons
    from current workspace only and window thumbnails only.
    
    Patch written by Joachim Bargsten <dev bargsten org>

 extensions/alternate-tab/Makefile.am               |   11 +
 extensions/alternate-tab/extension.js              |  344 +++++++++++++++++++-
 extensions/alternate-tab/metadata.json.in          |    2 +-
 ...e.shell.extensions.alternate-tab.gschema.xml.in |   19 +
 po/POTFILES.in                                     |    1 +
 5 files changed, 365 insertions(+), 12 deletions(-)
---
diff --git a/extensions/alternate-tab/Makefile.am b/extensions/alternate-tab/Makefile.am
index b8fde76..2e3930e 100644
--- a/extensions/alternate-tab/Makefile.am
+++ b/extensions/alternate-tab/Makefile.am
@@ -1,3 +1,14 @@
 EXTENSION_ID = alternate-tab
 
 include ../../extension.mk
+
+gschemas_in = org.gnome.shell.extensions.alternate-tab.gschema.xml.in
+
+ INTLTOOL_XML_NOMERGE_RULE@
+
+gsettings_SCHEMAS = $(gschemas_in:.xml.in=.xml)
+
+ GSETTINGS_RULES@
+
+CLEANFILES += $(gschemas_in:.xml.in=.valid) $(gsettings_SCHEMAS)
+EXTRA_DIST += $(gschemas_in)
diff --git a/extensions/alternate-tab/extension.js b/extensions/alternate-tab/extension.js
index 0e158c4..b71f17a 100644
--- a/extensions/alternate-tab/extension.js
+++ b/extensions/alternate-tab/extension.js
@@ -1,16 +1,324 @@
 /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 
+/* most of the code is borrowed from
+ * > js/ui/altTab.js <
+ * of the gnome-shell source code
+ */
+
+const AltTab = imports.ui.altTab;
 const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const Gtk = imports.gi.Gtk;
 const Lang = imports.lang;
+const Main = imports.ui.main;
 const Mainloop = imports.mainloop;
-const Shell= imports.gi.Shell;
+const ModalDialog = imports.ui.modalDialog;
+const Shell = imports.gi.Shell;
 const St = imports.gi.St;
-
-const AltTab=imports.ui.altTab;
-const Main = imports.ui.main;
 const Tweener = imports.ui.tweener;
 const WindowManager = imports.ui.windowManager;
 
+const Gettext = imports.gettext.domain('gnome-shell-extensions');
+const _ = Gettext.gettext;
+const N_ = function(e) { return e };
+
+const POPUP_FADE_TIME = 0.1; // seconds
+
+const SETTINGS_SCHEMA = 'org.gnome.shell.extensions.alternate-tab';
+const SETTINGS_BEHAVIOUR_KEY = 'behaviour';
+const SETTINGS_FIRST_TIME_KEY = 'first-time';
+
+const MODES = {
+    native: function() {
+            Main.wm._startAppSwitcher();
+    },
+    all_thumbnails: function() {
+            new AltTabPopup2();
+    },
+    workspace_icons: function() {
+            new AltTabPopupW().show();
+    }
+};
+
+const MESSAGE = N_("This is the first time you use the Alternate Tab extension. \n\
+Please choose your preferred behaviour:\n\
+\n\
+All & Thumbnails:\n\
+    This mode presents all applications from all workspaces in one selection \n\
+    list. Instead of using the application icon of every window, it uses small \n\
+    thumbnails resembling the window itself. \n\
+\n\
+Workspace & Icons:\n\
+    This mode let's you switch between the applications of your current \n\
+    workspace and gives you additionally the option to switch to the last used \n\
+    application of your previous workspace. This is always the last symbol in \n\
+    the list and is segregated by a separator/vertical line if available. \n\
+    Every window is represented by its application icon.  \n\
+\n\
+Native:\n\
+    This mode is the native GNOME 3 behaviour or in other words: Clicking \n\
+    native switches the Alternate Tab extension off. \n\
+");
+
+function AltTabPopupW() {
+    this._init();
+}
+
+AltTabPopupW.prototype = {
+    __proto__ : AltTab.AltTabPopup.prototype,
+
+    show : function(backward, switch_group) {
+        let tracker = Shell.WindowTracker.get_default();
+        let apps = tracker.get_running_apps ('');
+
+        if (!apps.length)
+            return false;
+
+        if (!Main.pushModal(this.actor))
+            return false;
+        this._haveModal = true;
+
+        this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
+        this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
+
+        this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
+        this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
+
+        this._appSwitcher = new WindowSwitcher(apps, this);
+        this.actor.add_actor(this._appSwitcher.actor);
+        this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
+        this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
+
+        this._appIcons = this._appSwitcher.icons;
+
+        // Make the initial selection
+        if (switch_group) {
+            if (backward) {
+                this._select(0, this._appIcons[0].cachedWindows.length - 1);
+            } else {
+                if (this._appIcons[0].cachedWindows.length > 1)
+                    this._select(0, 1);
+                else
+                    this._select(0, 0);
+            }
+        } else if (this._appIcons.length == 1) {
+            this._select(0);
+        } else if (backward) {
+            this._select(this._appIcons.length - 1);
+        } else {
+            this._select(1);
+        }
+
+        // There's a race condition; if the user released Alt before
+        // we got the grab, then we won't be notified. (See
+        // https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
+        // details.) So we check now. (Have to do this after updating
+        // selection.)
+        let [x, y, mods] = global.get_pointer();
+        if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
+            this._finish();
+            return false;
+        }
+
+        this.actor.opacity = 0;
+        this.actor.show();
+        Tweener.addTween(this.actor,
+                         { opacity: 255,
+                           time: POPUP_FADE_TIME,
+                           transition: 'easeOutQuad'
+                         });
+
+        return true;
+    },
+
+
+    _finish : function() {
+        let app = this._appIcons[this._currentApp];
+        Main.activateWindow(app.cachedWindows[0]);
+        this.destroy();
+    }
+
+};
+
+function AppIcon(app, window) {
+    this._init(app, window);
+}
+
+AppIcon.prototype = {
+    __proto__ : AltTab.AppIcon.prototype,
+
+    _init: function(app, window) {
+        this.app = app;
+
+        this.cachedWindows = [];
+        this.cachedWindows.push(window);
+
+        this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
+                                         vertical: true });
+        this.icon = null;
+        this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
+
+        this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
+
+        let title = window.get_title();
+        if (title) {
+            this.label = new St.Label({ text: title });
+            let bin = new St.Bin({ x_align: St.Align.MIDDLE });
+            bin.add_actor(this.label);
+            this.actor.add(bin);
+        }
+        else {
+            this.label = new St.Label({ text: this.app.get_name() });
+            this.actor.add(this.label, { x_fill: false });
+        }
+    }
+};
+
+function WindowSwitcher(apps, altTabPopup) {
+    this._init(apps, altTabPopup);
+}
+
+WindowSwitcher.prototype = {
+    __proto__ : AltTab.AppSwitcher.prototype,
+
+    _init : function(apps, altTabPopup) {
+        AltTab.SwitcherList.prototype._init.call(this, true);
+
+        // Construct the AppIcons, sort by time, add to the popup
+        let activeWorkspace = global.screen.get_active_workspace();
+        let workspaceIcons = [];
+        let otherIcons = [];
+        for (let i = 0; i < apps.length; i++) {
+            // Cache the window list now; we don't handle dynamic changes here,
+            // and we don't want to be continually retrieving it
+            let windows = apps[i].get_windows();
+
+            for(let j = 0; j < windows.length; j++) {
+                let appIcon = new AppIcon(apps[i], windows[j]);
+                if (this._isWindowOnWorkspace(windows[j], activeWorkspace)) {
+                  workspaceIcons.push(appIcon);
+                }
+                else {
+                  otherIcons.push(appIcon);
+                }
+            }
+        }
+
+        workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
+        otherIcons.sort(Lang.bind(this, this._sortAppIcon));
+
+        if(otherIcons.length > 0) {
+            let mostRecentOtherIcon = otherIcons[0];
+            otherIcons = [];
+            otherIcons.push(mostRecentOtherIcon);
+        }
+
+        this.icons = [];
+        this._arrows = [];
+        for (let i = 0; i < workspaceIcons.length; i++)
+            this._addIcon(workspaceIcons[i]);
+        if (workspaceIcons.length > 0 && otherIcons.length > 0)
+            this.addSeparator();
+        for (let i = 0; i < otherIcons.length; i++)
+            this._addIcon(otherIcons[i]);
+
+        this._curApp = -1;
+        this._iconSize = 0;
+        this._altTabPopup = altTabPopup;
+        this._mouseTimeOutId = 0;
+    },
+
+
+    _isWindowOnWorkspace: function(w, workspace) {
+            if (w.get_workspace() == workspace)
+                return true;
+        return false;
+    },
+
+    _sortAppIcon : function(appIcon1, appIcon2) {
+        let t1 = appIcon1.cachedWindows[0].get_user_time();
+        let t2 = appIcon2.cachedWindows[0].get_user_time();
+        if (t2 > t1) return 1;
+        else return -1;
+    }
+};
+
+function AltTabSettingsDialog() {
+    this._init();
+}
+
+AltTabSettingsDialog.prototype = {
+    __proto__: ModalDialog.ModalDialog.prototype,
+
+    _init : function() {
+        ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: null });
+
+        let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
+                                                vertical: false });
+        this.contentLayout.add(mainContentBox,
+                               { x_fill: true,
+                                 y_fill: true });
+
+        let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
+                                            vertical: true });
+        mainContentBox.add(messageBox,
+                           { y_align: St.Align.START });
+
+        this._subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
+                                            text: _("Alt Tab Behaviour") });
+
+        messageBox.add(this._subjectLabel,
+                       { y_fill:  false,
+                         y_align: St.Align.START });
+
+        this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
+                                                text: Gettext.gettext(MESSAGE) });
+
+        messageBox.add(this._descriptionLabel,
+                       { y_fill:  true,
+                         y_align: St.Align.START });
+
+
+        this.setButtons([
+            {
+                label: _("All & Thumbnails"),
+                action: Lang.bind(this, function() {
+                    this.setBehaviour('all_thumbnails');
+                    this.close();
+                })
+            },
+            {
+                label: _("Workspace & Icons"),
+                action: Lang.bind(this, function() {
+                    this.setBehaviour('workspace_icons');
+                    this.close();
+                })
+            },
+            {
+                label: _("Native"),
+                action: Lang.bind(this, function() {
+                    this.setBehaviour('native');
+                    this.close();
+                })
+            },
+            {
+                label: _("Cancel"),
+                action: Lang.bind(this, function() {
+                    this.close();
+                }),
+                key: Clutter.Escape
+            }
+        ]);
+    },
+
+    setBehaviour: function(behaviour) {
+           this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
+           this._settings.set_string(SETTINGS_BEHAVIOUR_KEY, behaviour);
+           this._settings.set_boolean(SETTINGS_FIRST_TIME_KEY, false);
+    }
+};
+
 function AltTabPopup2() {
     this._init();
 }
@@ -41,6 +349,7 @@ AltTabPopup2.prototype = {
 
 	this.show();
         Main.uiGroup.add_actor(this.actor);
+        this._select(0);
     },
 
     show : function(backward) {
@@ -54,9 +363,7 @@ AltTabPopup2.prototype = {
 
 	for (let w = windows.length-1; w >= 0; w--) {
 	    let win = windows[w].get_meta_window();
-	    if (win.window_type == 0) {
 	        normal_windows.push(win);
-	    }
 	}
 	normal_windows.sort(Lang.bind(this, this._sortWindows));
 
@@ -74,8 +381,10 @@ AltTabPopup2.prototype = {
 		        ap1 = new AltTab.AppIcon(apps[i]);
 	        }
 	    }
-	    ap1.cachedWindows = [win];
-	    appIcons.push(ap1); 
+	    if (ap1 != null) {
+              ap1.cachedWindows = [win];
+	      appIcons.push(ap1);
+            }
 	}
 
         if (!windows.length)
@@ -93,13 +402,13 @@ AltTabPopup2.prototype = {
 
         this._appSwitcher = new WindowList(windows);
 	this._appSwitcher._altTabPopup=this;
-	this._appSwitcher.highlight(0,false);
         this.actor.add_actor(this._appSwitcher.actor);
         this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
         this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
 
         this._appIcons = appIcons;
 
+
 	return true
     },
 
@@ -220,8 +529,10 @@ WindowList.prototype = {
 	            }
 	        }
   	    }
+            if (ap1 != null) {
 	    ap1.cachedWindows = [win];
             this._addIcon(ap1);
+            }
 	}
     },
 
@@ -230,8 +541,19 @@ WindowList.prototype = {
     }
 };
 
-function main() {
+function main(metadata) {
+    imports.gettext.bindtextdomain('gnome-shell-extensions', metadata.localedir);
+
     Main.wm.setKeybindingHandler('switch_windows', function() {
-        let alpopup = new AltTabPopup2();
+        let settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
+
+        if(settings.get_boolean(SETTINGS_FIRST_TIME_KEY)) {
+           new AltTabSettingsDialog().open();
+        } else {
+            let behaviour = settings.get_string(SETTINGS_BEHAVIOUR_KEY);
+            if(behaviour in MODES) {
+                MODES[behaviour]();
+            }
+        }
     });
 }
diff --git a/extensions/alternate-tab/metadata.json.in b/extensions/alternate-tab/metadata.json.in
index aae37e2..d811b54 100644
--- a/extensions/alternate-tab/metadata.json.in
+++ b/extensions/alternate-tab/metadata.json.in
@@ -2,7 +2,7 @@
 "uuid": "@uuid@",
 "name": "AlternateTab",
 "description": "A replacement for Alt-Tab, allows to cycle between windows and does not group by application",
-"original-author": "thomas bouffon gmail com",
+"original-authors": [  "jw bargsten org", "thomas bouffon gmail com" ],
 "shell-version": [ "@shell_current@" ],
 "localedir": "@LOCALEDIR@",
 "url": "@url@"
diff --git a/extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in b/extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in
new file mode 100644
index 0000000..7dbbca7
--- /dev/null
+++ b/extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in
@@ -0,0 +1,19 @@
+<schemalist gettext-domain="gnome-shell-extensions">
+  <enum id="org.gnome.shell.extensions.alternate-tab.BehaviourMode">
+    <value value="0" nick="native"/>
+    <value value="1" nick="all_thumbnails"/>
+    <value value="2" nick="workspace_icons"/>
+  </enum>
+  <schema id="org.gnome.shell.extensions.alternate-tab" path="/org/gnome/shell/extensions/alternate-tab/">
+    <key name="behaviour" enum="org.gnome.shell.extensions.alternate-tab.BehaviourMode">
+      <default>'native'</default>
+      <_summary>The alt tab behaviour.</_summary>
+      <_description>Sets the Alt-Tab behaviour. Possible values are: native, all_thumbnails and workspace_icons.</_description>
+    </key>
+    <key type="b" name="first-time">
+      <default>true</default>
+      <_summary>Indicates if Alternate Tab is newly installed</_summary>
+      <_description>Ask the user for a default behaviour if true.</_description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 74678af..d1be54b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,5 +1,6 @@
 extensions/alternative-status-menu/extension.js
 extensions/alternate-tab/extension.js
+extensions/alternate-tab/org.gnome.shell.extensions.alternate-tab.gschema.xml.in
 extensions/auto-move-windows/extension.js
 extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml.in
 extensions/dock/extension.js



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