Quoth Pierre M,

In Ubuntu i would do
Alt+Ctrl+Shift+arrow
to move a window accross workspaces.

  The attached patches do that and more.

Move the active window to the next or previous workspace. Move it to the workspace you were last on. Move it to the next or previous workspace layer[1].

Bind shortcuts in WPrefs. Have the shortcuts show up against the corresponding options in window menus (new).

Make shortcuts for switching to workspaces show up in the workspace menu too.

A new shortcut and workspace menu entry to switch to the last used workspace.

  Code reuse and consistency stuff.

Hopefully the commit messages will adequately explain what's going on.

[1] Does anyone actually use workspace layers?
From b4c9cf9ec50e09fa6a96059a15c327c599169624 Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Wed, 27 Mar 2013 11:58:47 +0000
Subject: [PATCH 1/7] Added GetShortcutKey().

The function getShortcutString() was defined statically in winmenu.c.

Replace it with the new function GetShortcutKey() which calls the
existing function GetShortcutString() so it can be used elsewhere and
cut down on code duplication.

A result of this change is that shortcuts are now labelled consistently.
Previously the format was different in generated menus, which used, for
example, M1 to refer to Mod1 whereas window menus used the full string
Mod1.  Now both use the shorter form.

One could argue that the new function name is more consistent, as now
GetShortcutString() takes a char * argument and GetShortcutKey() takes a
WShortcutKey argument.  That argument assumes that the original
intention behind the name of GetShortcutString() was not to hint that it
returns a String...
---
 src/funcs.h   |  2 ++
 src/misc.c    | 21 +++++++++++++++++++++
 src/winmenu.c | 38 ++++++++++----------------------------
 3 files changed, 33 insertions(+), 28 deletions(-)

diff --git a/src/funcs.h b/src/funcs.h
index a7df6cb..0d063b0 100644
--- a/src/funcs.h
+++ b/src/funcs.h
@@ -26,6 +26,7 @@
 
 #include "window.h"
 #include "defaults.h"
+#include "keybind.h"
 
 typedef void (WCallBack)(void *cdata);
 typedef void (WDeathHandler)(pid_t pid, unsigned int status, void *cdata);
@@ -67,6 +68,7 @@ char * ExpandOptions(WScreen *scr, char *cmdline);
 char * ShrinkString(WMFont *font, char *string, int width);
 char * FindImage(char *paths, char *file);
 char * GetShortcutString(char *text);
+char * GetShortcutKey(WShortKey key);
 char * EscapeWM_CLASS(char *name, char *class);
 
 Bool IsDoubleClick(WScreen *scr, XEvent *event);
diff --git a/src/misc.c b/src/misc.c
index 9922ea2..39f8503 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -33,6 +33,8 @@
 #include <math.h>
 #include <time.h>
 
+#include <X11/XKBlib.h>
+
 #include <WINGs/WUtil.h>
 #include <wraster.h>
 
@@ -771,6 +773,25 @@ char *GetShortcutString(char *text)
        return buffer;
 }
 
+char *GetShortcutKey(WShortKey key)
+{
+       char *tmp = NULL;
+       char *k = XKeysymToString(XkbKeycodeToKeysym(dpy, key.keycode, 0, 0));
+       if (!k) return NULL;
+
+       char **m = wPreferences.modifier_labels;
+       if (key.modifier & ControlMask) tmp = wstrappend(tmp, m[1] ? m[1] : 
"Ctrl+");
+       if (key.modifier & ShiftMask)   tmp = wstrappend(tmp, m[0] ? m[0] : 
"Shift+");
+       if (key.modifier & Mod1Mask)    tmp = wstrappend(tmp, m[2] ? m[2] : 
"Mod1+");
+       if (key.modifier & Mod2Mask)    tmp = wstrappend(tmp, m[3] ? m[3] : 
"Mod2+");
+       if (key.modifier & Mod3Mask)    tmp = wstrappend(tmp, m[4] ? m[4] : 
"Mod3+");
+       if (key.modifier & Mod4Mask)    tmp = wstrappend(tmp, m[5] ? m[5] : 
"Mod4+");
+       if (key.modifier & Mod5Mask)    tmp = wstrappend(tmp, m[6] ? m[6] : 
"Mod5+");
+       tmp = wstrappend(tmp, k);
+
+       return GetShortcutString(tmp);
+}
+
 char *EscapeWM_CLASS(char *name, char *class)
 {
        char *ret;
diff --git a/src/winmenu.c b/src/winmenu.c
index 7bba1a1..983f7e8 100644
--- a/src/winmenu.c
+++ b/src/winmenu.c
@@ -38,6 +38,7 @@
 #include "client.h"
 #include "application.h"
 #include "keybind.h"
+#include "funcs.h"
 #include "framewin.h"
 #include "workspace.h"
 #include "winspector.h"
@@ -244,25 +245,6 @@ static void updateWorkspaceMenu(WMenu * menu)
                wMenuRealize(menu);
 }
 
-static char *getShortcutString(WShortKey key)
-{
-       char *tmp = NULL;
-       char *k = XKeysymToString(XkbKeycodeToKeysym(dpy, key.keycode, 0, 0));
-       if (!k) return NULL;
-
-       char **m = wPreferences.modifier_labels;
-       if (key.modifier & ControlMask) tmp = wstrappend(tmp, m[1] ? m[1] : 
"Ctrl+");
-       if (key.modifier & ShiftMask)   tmp = wstrappend(tmp, m[0] ? m[0] : 
"Shift+");
-       if (key.modifier & Mod1Mask)    tmp = wstrappend(tmp, m[2] ? m[2] : 
"Mod1+");
-       if (key.modifier & Mod2Mask)    tmp = wstrappend(tmp, m[3] ? m[3] : 
"Mod2+");
-       if (key.modifier & Mod3Mask)    tmp = wstrappend(tmp, m[4] ? m[4] : 
"Mod3+");
-       if (key.modifier & Mod4Mask)    tmp = wstrappend(tmp, m[5] ? m[5] : 
"Mod4+");
-       if (key.modifier & Mod5Mask)    tmp = wstrappend(tmp, m[6] ? m[6] : 
"Mod5+");
-       tmp = wstrappend(tmp, k);
-
-       return tmp;
-}
-
 static void updateMakeShortcutMenu(WMenu * menu, WWindow * wwin)
 {
        WMenu *smenu = menu->cascades[menu->entries[MC_SHORTCUT]->cascade];
@@ -304,7 +286,7 @@ static void updateMakeShortcutMenu(WMenu * menu, WWindow * 
wwin)
                kcode = wKeyBindings[WKBD_WINDOW1 + shortcutNo].keycode;
 
                if (kcode) {
-                       if ((tmp = getShortcutString(wKeyBindings[WKBD_WINDOW1 
+ shortcutNo]))
+                       if ((tmp = GetShortcutKey(wKeyBindings[WKBD_WINDOW1 + 
shortcutNo]))
                            && (!entry->rtext || strcmp(tmp, entry->rtext) != 
0)) {
                                if (entry->rtext)
                                        wfree(entry->rtext);
@@ -428,22 +410,22 @@ static WMenu *createWindowMenu(WScreen * scr)
         * this file.
         */
        entry = wMenuAddCallback(menu, _("Maximize"), execMenuCommand, NULL);
-       entry->rtext = getShortcutString(wKeyBindings[WKBD_MAXIMIZE]);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_MAXIMIZE]);
 
        entry = wMenuAddCallback(menu, _("Miniaturize"), execMenuCommand, NULL);
-       entry->rtext = getShortcutString(wKeyBindings[WKBD_MINIATURIZE]);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_MINIATURIZE]);
 
        entry = wMenuAddCallback(menu, _("Shade"), execMenuCommand, NULL);
-       entry->rtext = getShortcutString(wKeyBindings[WKBD_SHADE]);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_SHADE]);
 
        entry = wMenuAddCallback(menu, _("Hide"), execMenuCommand, NULL);
-       entry->rtext = getShortcutString(wKeyBindings[WKBD_HIDE]);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_HIDE]);
 
        entry = wMenuAddCallback(menu, _("Resize/Move"), execMenuCommand, NULL);
-       entry->rtext = getShortcutString(wKeyBindings[WKBD_MOVERESIZE]);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_MOVERESIZE]);
 
        entry = wMenuAddCallback(menu, _("Select"), execMenuCommand, NULL);
-       entry->rtext = getShortcutString(wKeyBindings[WKBD_SELECT]);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_SELECT]);
 
        entry = wMenuAddCallback(menu, _("Move To"), NULL, NULL);
        scr->workspace_submenu = makeWorkspaceMenu(scr);
@@ -461,10 +443,10 @@ static WMenu *createWindowMenu(WScreen * scr)
         */
 
        entry = wMenuAddCallback(menu, _("Launch"), execMenuCommand, NULL);
-       entry->rtext = getShortcutString(wKeyBindings[WKBD_RELAUNCH]);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_RELAUNCH]);
 
        entry = wMenuAddCallback(menu, _("Close"), execMenuCommand, NULL);
-       entry->rtext = getShortcutString(wKeyBindings[WKBD_CLOSE]);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_CLOSE]);
 
        entry = wMenuAddCallback(menu, _("Kill"), execMenuCommand, NULL);
 
-- 
1.8.1.4

From ce9647cc38a334927682f68ea79ba046edc59aa0 Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Wed, 27 Mar 2013 13:05:03 +0000
Subject: [PATCH 2/7] Eliminate Workspaces menu magic numbers.

A number of functions relating to the Workspaces menu assume that the
menu contains at least three entries: New, Destroy Last and Workspace1.
Because it is assumed that index 1 contains the Destroy Last entry and
index 2 contains the first workspace entry there are several occurrences
throughout workspace.c of magic numbers relating to those indices.

Replace the magic numbers with some #defines, making the code more
readable and allowing the menu to be expanded later.
---
 src/workspace.c | 39 ++++++++++++++++++++++-----------------
 1 file changed, 22 insertions(+), 17 deletions(-)

diff --git a/src/workspace.c b/src/workspace.c
index 491452b..438fe55 100644
--- a/src/workspace.c
+++ b/src/workspace.c
@@ -49,6 +49,11 @@
 #include "wmspec.h"
 #include "xinerama.h"
 
+#define MC_NEW          0
+#define MC_DESTROY_LAST 1
+/* index of the first workspace menu entry */
+#define MC_WORKSPACE1   2
+
 #define MAX_SHORTCUT_LENGTH 32
 #define WORKSPACE_NAME_DISPLAY_PADDING 32
 
@@ -660,16 +665,16 @@ void wWorkspaceRename(WScreen * scr, int workspace, char 
*name)
        scr->workspaces[workspace]->name = wstrdup(buf);
 
        if (scr->clip_ws_menu) {
-               if (strcmp(scr->clip_ws_menu->entries[workspace + 2]->text, 
buf) != 0) {
-                       wfree(scr->clip_ws_menu->entries[workspace + 2]->text);
-                       scr->clip_ws_menu->entries[workspace + 2]->text = 
wstrdup(buf);
+               if (strcmp(scr->clip_ws_menu->entries[workspace + 
MC_WORKSPACE1]->text, buf) != 0) {
+                       wfree(scr->clip_ws_menu->entries[workspace + 
MC_WORKSPACE1]->text);
+                       scr->clip_ws_menu->entries[workspace + 
MC_WORKSPACE1]->text = wstrdup(buf);
                        wMenuRealize(scr->clip_ws_menu);
                }
        }
        if (scr->workspace_menu) {
-               if (strcmp(scr->workspace_menu->entries[workspace + 2]->text, 
buf) != 0) {
-                       wfree(scr->workspace_menu->entries[workspace + 
2]->text);
-                       scr->workspace_menu->entries[workspace + 2]->text = 
wstrdup(buf);
+               if (strcmp(scr->workspace_menu->entries[workspace + 
MC_WORKSPACE1]->text, buf) != 0) {
+                       wfree(scr->workspace_menu->entries[workspace + 
MC_WORKSPACE1]->text);
+                       scr->workspace_menu->entries[workspace + 
MC_WORKSPACE1]->text = wstrdup(buf);
                        wMenuRealize(scr->workspace_menu);
                }
        }
@@ -719,10 +724,10 @@ void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
        if (!menu)
                return;
 
-       if (menu->entry_no < scr->workspace_count + 2) {
+       if (menu->entry_no < scr->workspace_count + MC_WORKSPACE1) {
                /* new workspace(s) added */
-               i = scr->workspace_count - (menu->entry_no - 2);
-               ws = menu->entry_no - 2;
+               i = scr->workspace_count - (menu->entry_no - MC_WORKSPACE1);
+               ws = menu->entry_no - MC_WORKSPACE1;
                while (i > 0) {
                        wstrlcpy(title, scr->workspaces[ws]->name, 
MAX_WORKSPACENAME_WIDTH);
 
@@ -733,24 +738,24 @@ void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
                        i--;
                        ws++;
                }
-       } else if (menu->entry_no > scr->workspace_count + 2) {
+       } else if (menu->entry_no > scr->workspace_count + MC_WORKSPACE1) {
                /* removed workspace(s) */
-               for (i = menu->entry_no - 1; i >= scr->workspace_count + 2; 
i--) {
+               for (i = menu->entry_no - 1; i >= scr->workspace_count + 
MC_WORKSPACE1; i--) {
                        wMenuRemoveItem(menu, i);
                }
        }
        wMenuRealize(menu);
 
        for (i = 0; i < scr->workspace_count; i++) {
-               menu->entries[i + 2]->flags.indicator_on = 0;
+               menu->entries[i + MC_WORKSPACE1]->flags.indicator_on = 0;
        }
-       menu->entries[scr->current_workspace + 2]->flags.indicator_on = 1;
+       menu->entries[scr->current_workspace + 
MC_WORKSPACE1]->flags.indicator_on = 1;
 
        /* don't let user destroy current workspace */
        if (scr->current_workspace == scr->workspace_count - 1) {
-               wMenuSetEnabled(menu, 1, False);
+               wMenuSetEnabled(menu, MC_DESTROY_LAST, False);
        } else {
-               wMenuSetEnabled(menu, 1, True);
+               wMenuSetEnabled(menu, MC_DESTROY_LAST, True);
        }
 
        tmp = menu->frame->top_width + 5;
@@ -818,8 +823,8 @@ void wWorkspaceRestoreState(WScreen *scr)
                        wWorkspaceNew(scr);
 
                if (scr->workspace_menu) {
-                       wfree(scr->workspace_menu->entries[i + 2]->text);
-                       scr->workspace_menu->entries[i + 2]->text = 
wstrdup(WMGetFromPLString(pstr));
+                       wfree(scr->workspace_menu->entries[i + 
MC_WORKSPACE1]->text);
+                       scr->workspace_menu->entries[i + MC_WORKSPACE1]->text = 
wstrdup(WMGetFromPLString(pstr));
                        scr->workspace_menu->flags.realized = 0;
                }
 
-- 
1.8.1.4

From 4bc547ded7b8f7b4ab1b8c0811ece270c8e76671 Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Wed, 27 Mar 2013 22:50:50 +0000
Subject: [PATCH 3/7] Show shortcuts for workspace switching menu entries.

When showing a Workspaces menu entry for switching to a particular
workspace, also show the shortcut if one is bound for switching to that
workspace.
---
 src/defaults.c  | 13 +++++++++++++
 src/workspace.c |  9 ++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/src/defaults.c b/src/defaults.c
index 51de3f1..5b57dd9 100644
--- a/src/defaults.c
+++ b/src/defaults.c
@@ -184,6 +184,8 @@ static int setCursor();
 #define REFRESH_ICON_TITLE_COLOR (1<<13)
 #define REFRESH_ICON_TITLE_BACK (1<<14)
 
+#define REFRESH_WORKSPACE_MENU (1<<15)
+
 #define REFRESH_FRAME_BORDER REFRESH_MENU_FONT|REFRESH_WINDOW_FONT
 
 static WOptionEnumeration seFocusModes[] = {
@@ -1129,6 +1131,13 @@ void wReadDefaults(WScreen * scr, WMPropList * new_dict)
                }
                if (needs_refresh & REFRESH_ICON_TILE)
                        WMPostNotificationName(WNIconTileSettingsChanged, NULL, 
NULL);
+
+               if (needs_refresh & REFRESH_WORKSPACE_MENU) {
+                       if (scr->workspace_menu)
+                               wWorkspaceMenuUpdate(scr, scr->workspace_menu);
+                       if (scr->clip_ws_menu)
+                               wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
+               }
        }
 }
 
@@ -2840,6 +2849,10 @@ static int setKeyGrab(WScreen * scr, WDefaultEntry * 
entry, WShortKey * shortcut
                wwin = wwin->prev;
        }
 
+       /* do we need to update window menus? */
+       if (widx >= WKBD_WORKSPACE1 && widx <= WKBD_WORKSPACE10)
+               return REFRESH_WORKSPACE_MENU;
+
        return 0;
 }
 
diff --git a/src/workspace.c b/src/workspace.c
index 438fe55..6d49498 100644
--- a/src/workspace.c
+++ b/src/workspace.c
@@ -61,6 +61,7 @@ extern int ignore_wks_change;
 extern WPreferences wPreferences;
 extern XContext wVEdgeContext;
 extern void ProcessPendingEvents();
+extern WShortKey wKeyBindings[WKBD_LAST];
 
 static WMPropList *dWorkspaces = NULL;
 static WMPropList *dClip, *dName;
@@ -744,12 +745,18 @@ void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
                        wMenuRemoveItem(menu, i);
                }
        }
-       wMenuRealize(menu);
 
        for (i = 0; i < scr->workspace_count; i++) {
+               /* workspace shortcut labels */
+               if (i / 10 == scr->current_workspace / 10)
+                       menu->entries[i + MC_WORKSPACE1]->rtext = 
GetShortcutKey(wKeyBindings[WKBD_WORKSPACE1 + (i % 10)]);
+               else
+                       menu->entries[i + MC_WORKSPACE1]->rtext = NULL;
+
                menu->entries[i + MC_WORKSPACE1]->flags.indicator_on = 0;
        }
        menu->entries[scr->current_workspace + 
MC_WORKSPACE1]->flags.indicator_on = 1;
+       wMenuRealize(menu);
 
        /* don't let user destroy current workspace */
        if (scr->current_workspace == scr->workspace_count - 1) {
-- 
1.8.1.4

From 2097de96075d511dca2b046e68e2481946b12eaa Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Tue, 26 Mar 2013 23:12:31 +0000
Subject: [PATCH 4/7] Added shortcut to switch to last used workspace.

Added new LastWorkspaceKey shortcut and Workspaces menu entry to switch back to
the last used workspace.
---
 src/defaults.c  |  4 ++++
 src/event.c     |  3 +++
 src/keybind.h   |  1 +
 src/screen.h    |  1 +
 src/startup.c   |  1 +
 src/workspace.c | 21 ++++++++++++++++++++-
 6 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/src/defaults.c b/src/defaults.c
index 5b57dd9..0a8fc0f 100644
--- a/src/defaults.c
+++ b/src/defaults.c
@@ -610,6 +610,8 @@ WDefaultEntry optionList[] = {
            NULL, getKeybind, setKeyGrab, NULL, NULL},
        {"PrevWorkspaceKey", "None", (void *)WKBD_PREVWORKSPACE,
            NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"LastWorkspaceKey", "None", (void *)WKBD_LASTWORKSPACE,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
        {"NextWorkspaceLayerKey", "None", (void *)WKBD_NEXTWSLAYER,
            NULL, getKeybind, setKeyGrab, NULL, NULL},
        {"PrevWorkspaceLayerKey", "None", (void *)WKBD_PREVWSLAYER,
@@ -2852,6 +2854,8 @@ static int setKeyGrab(WScreen * scr, WDefaultEntry * 
entry, WShortKey * shortcut
        /* do we need to update window menus? */
        if (widx >= WKBD_WORKSPACE1 && widx <= WKBD_WORKSPACE10)
                return REFRESH_WORKSPACE_MENU;
+       if (widx == WKBD_LASTWORKSPACE)
+               return REFRESH_WORKSPACE_MENU;
 
        return 0;
 }
diff --git a/src/event.c b/src/event.c
index ace2a3c..0f4b53d 100644
--- a/src/event.c
+++ b/src/event.c
@@ -1554,6 +1554,9 @@ static void handleKeyPress(XEvent * event)
        case WKBD_PREVWORKSPACE:
                wWorkspaceRelativeChange(scr, -1);
                break;
+       case WKBD_LASTWORKSPACE:
+               wWorkspaceChange(scr, scr->last_workspace);
+               break;
 
        case WKBD_WINDOW1:
        case WKBD_WINDOW2:
diff --git a/src/keybind.h b/src/keybind.h
index 99137d8..f4ec95c 100644
--- a/src/keybind.h
+++ b/src/keybind.h
@@ -71,6 +71,7 @@ enum {
        WKBD_WORKSPACE10,
        WKBD_NEXTWORKSPACE,
        WKBD_PREVWORKSPACE,
+       WKBD_LASTWORKSPACE,
        WKBD_NEXTWSLAYER,
        WKBD_PREVWSLAYER,
 
diff --git a/src/screen.h b/src/screen.h
index 15fd4f8..b2c2f42 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -120,6 +120,7 @@ typedef struct _WScreen {
     struct WWorkspace **workspaces;    /* workspace array */
 
     int current_workspace;            /* current workspace number */
+    int last_workspace;                       /* last used workspace number */
 
 
     WReservedArea *reservedAreas;      /* used to build totalUsableArea */
diff --git a/src/startup.c b/src/startup.c
index 00e32ef..93b5fe7 100644
--- a/src/startup.c
+++ b/src/startup.c
@@ -911,6 +911,7 @@ static void manageAllWindows(WScreen * scr, int 
crashRecovery)
                WMNextEvent(dpy, &ev);
                WMHandleEvent(&ev);
        }
+       scr->last_workspace = 0;
        wWorkspaceForceChange(scr, 0);
        if (!wPreferences.flags.noclip)
                wDockShowIcons(scr->workspaces[scr->current_workspace]->clip);
diff --git a/src/workspace.c b/src/workspace.c
index 6d49498..2f94427 100644
--- a/src/workspace.c
+++ b/src/workspace.c
@@ -51,8 +51,9 @@
 
 #define MC_NEW          0
 #define MC_DESTROY_LAST 1
+#define MC_LAST_USED    2
 /* index of the first workspace menu entry */
-#define MC_WORKSPACE1   2
+#define MC_WORKSPACE1   3
 
 #define MAX_SHORTCUT_LENGTH 32
 #define WORKSPACE_NAME_DISPLAY_PADDING 32
@@ -193,6 +194,8 @@ Bool wWorkspaceDelete(WScreen * scr, int workspace)
 
        if (scr->current_workspace >= scr->workspace_count)
                wWorkspaceChange(scr, scr->workspace_count - 1);
+       if (scr->last_workspace >= scr->workspace_count)
+               scr->last_workspace = 0;
 
        return True;
 }
@@ -480,6 +483,7 @@ void wWorkspaceForceChange(WScreen * scr, int workspace)
 
        wClipUpdateForWorkspaceChange(scr, workspace);
 
+       scr->last_workspace = scr->current_workspace;
        scr->current_workspace = workspace;
 
        wWorkspaceMenuUpdate(scr, scr->workspace_menu);
@@ -626,6 +630,11 @@ static void switchWSCommand(WMenu * menu, WMenuEntry * 
entry)
        wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
 }
 
+static void lastWSCommand(WMenu * menu, WMenuEntry * entry)
+{
+       wWorkspaceChange(menu->frame->screen_ptr, 
menu->frame->screen_ptr->last_workspace);
+}
+
 static void deleteWSCommand(WMenu * menu, WMenuEntry * entry)
 {
        wWorkspaceDelete(menu->frame->screen_ptr, 
menu->frame->screen_ptr->workspace_count - 1);
@@ -698,6 +707,7 @@ static void onMenuEntryEdited(WMenu * menu, WMenuEntry * 
entry)
 WMenu *wWorkspaceMenuMake(WScreen * scr, Bool titled)
 {
        WMenu *wsmenu;
+       WMenuEntry *entry;
 
        wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
        if (!wsmenu) {
@@ -711,6 +721,9 @@ WMenu *wWorkspaceMenuMake(WScreen * scr, Bool titled)
        wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
        wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
 
+       entry = wMenuAddCallback(wsmenu, _("Last Used"), lastWSCommand, NULL);
+       entry->rtext = GetShortcutKey(wKeyBindings[WKBD_LASTWORKSPACE]);
+
        return wsmenu;
 }
 
@@ -765,6 +778,12 @@ void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
                wMenuSetEnabled(menu, MC_DESTROY_LAST, True);
        }
 
+       /* back to last workspace */
+       if (scr->workspace_count && scr->last_workspace != 
scr->current_workspace)
+               wMenuSetEnabled(menu, MC_LAST_USED, True);
+       else
+               wMenuSetEnabled(menu, MC_LAST_USED, False);
+
        tmp = menu->frame->top_width + 5;
        /* if menu got unreachable, bring it to a visible place */
        if (menu->frame_x < tmp - (int)menu->frame->core->width)
-- 
1.8.1.4

From f31e485cb5aee069dbe8269a48d5eefcd2dfe8e0 Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Tue, 26 Mar 2013 23:12:46 +0000
Subject: [PATCH 5/7] Allow WPrefs to configure the last used workspace key.

Added LastWorkspaceKey to the keyboard shortcut panel of WPrefs.
---
 WPrefs.app/KeyboardShortcuts.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/WPrefs.app/KeyboardShortcuts.c b/WPrefs.app/KeyboardShortcuts.c
index 00c5e3e..f0eb2d6 100644
--- a/WPrefs.app/KeyboardShortcuts.c
+++ b/WPrefs.app/KeyboardShortcuts.c
@@ -91,6 +91,7 @@ static char *keyOptions[] = {
        "GroupPrevKey",
        "NextWorkspaceKey",
        "PrevWorkspaceKey",
+       "LastWorkspaceKey",
        "NextWorkspaceLayerKey",
        "PrevWorkspaceLayerKey",
        "Workspace1Key",
@@ -479,6 +480,7 @@ static void createPanel(Panel * p)
        WMAddListItem(panel->actLs, _("Focus previous group window"));
        WMAddListItem(panel->actLs, _("Switch to next workspace"));
        WMAddListItem(panel->actLs, _("Switch to previous workspace"));
+       WMAddListItem(panel->actLs, _("Switch to last used workspace"));
        WMAddListItem(panel->actLs, _("Switch to next ten workspaces"));
        WMAddListItem(panel->actLs, _("Switch to previous ten workspaces"));
        WMAddListItem(panel->actLs, _("Switch to workspace 1"));
-- 
1.8.1.4

From caac5c490740380bf2709ba9cecaed60856b1af3 Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Wed, 27 Mar 2013 22:46:00 +0000
Subject: [PATCH 6/7] Shortcuts for moving windows between workspaces.

Added new keyboard shortcuts for moving windows between workspaces.

MoveToWorkspace1Key moves the active window directly to workspace 1.
Similarly for MoveToWorkspace2Key through MoveToWorkspace10Key.

MoveToNextWorkspaceKey moves the window to the next workspace,
MoveToPrevWorkspaceKey moves the window to the previous workspace.
Both keys respect the ws_advance and ws_cycle preferences.

MoveToNextWorkspaceLayerKey moves the window ten workspaces "forward" if
possible.
MoveToPrevWorkspaceLayerKey moves the window ten workspaces "back" if
possible.
---
 src/defaults.c | 36 ++++++++++++++++++++++++++++++++++++
 src/event.c    | 40 ++++++++++++++++++++++++++++++++++++++++
 src/keybind.h  | 17 +++++++++++++++++
 src/window.c   | 24 ++++++++++++++++++++++++
 src/window.h   |  1 +
 src/winmenu.c  | 17 ++++++++++++++---
 6 files changed, 132 insertions(+), 3 deletions(-)

diff --git a/src/defaults.c b/src/defaults.c
index 0a8fc0f..6c23064 100644
--- a/src/defaults.c
+++ b/src/defaults.c
@@ -636,6 +636,36 @@ WDefaultEntry optionList[] = {
            NULL, getKeybind, setKeyGrab, NULL, NULL},
        {"Workspace10Key", "None", (void *)WKBD_WORKSPACE10,
            NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace1Key", "None", (void *)WKBD_MOVE_WORKSPACE1,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace2Key", "None", (void *)WKBD_MOVE_WORKSPACE2,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace3Key", "None", (void *)WKBD_MOVE_WORKSPACE3,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace4Key", "None", (void *)WKBD_MOVE_WORKSPACE4,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace5Key", "None", (void *)WKBD_MOVE_WORKSPACE5,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace6Key", "None", (void *)WKBD_MOVE_WORKSPACE6,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace7Key", "None", (void *)WKBD_MOVE_WORKSPACE7,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace8Key", "None", (void *)WKBD_MOVE_WORKSPACE8,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace9Key", "None", (void *)WKBD_MOVE_WORKSPACE9,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToWorkspace10Key", "None", (void *)WKBD_MOVE_WORKSPACE10,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToNextWorkspaceKey", "None", (void *)WKBD_MOVE_NEXTWORKSPACE,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToPrevWorkspaceKey", "None", (void *)WKBD_MOVE_PREVWORKSPACE,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToLastWorkspaceKey", "None", (void *)WKBD_MOVE_LASTWORKSPACE,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToNextWorkspaceLayerKey", "None", (void *)WKBD_MOVE_NEXTWSLAYER,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
+       {"MoveToPrevWorkspaceLayerKey", "None", (void *)WKBD_MOVE_PREVWSLAYER,
+           NULL, getKeybind, setKeyGrab, NULL, NULL},
        {"WindowShortcut1Key", "None", (void *)WKBD_WINDOW1,
            NULL, getKeybind, setKeyGrab, NULL, NULL},
        {"WindowShortcut2Key", "None", (void *)WKBD_WINDOW2,
@@ -1139,6 +1169,10 @@ void wReadDefaults(WScreen * scr, WMPropList * new_dict)
                                wWorkspaceMenuUpdate(scr, scr->workspace_menu);
                        if (scr->clip_ws_menu)
                                wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
+                       if (scr->workspace_submenu)
+                               scr->workspace_submenu->flags.realized = 0;
+                       if (scr->clip_submenu)
+                               scr->clip_submenu->flags.realized = 0;
                }
        }
 }
@@ -2856,6 +2890,8 @@ static int setKeyGrab(WScreen * scr, WDefaultEntry * 
entry, WShortKey * shortcut
                return REFRESH_WORKSPACE_MENU;
        if (widx == WKBD_LASTWORKSPACE)
                return REFRESH_WORKSPACE_MENU;
+       if (widx >= WKBD_MOVE_WORKSPACE1 && widx <= WKBD_MOVE_WORKSPACE10)
+               return REFRESH_WORKSPACE_MENU;
 
        return 0;
 }
diff --git a/src/event.c b/src/event.c
index 0f4b53d..2da0988 100644
--- a/src/event.c
+++ b/src/event.c
@@ -1558,6 +1558,46 @@ static void handleKeyPress(XEvent * event)
                wWorkspaceChange(scr, scr->last_workspace);
                break;
 
+       case WKBD_MOVE_WORKSPACE1 ... WKBD_MOVE_WORKSPACE10:
+               widx = command - WKBD_MOVE_WORKSPACE1;
+               i = (scr->current_workspace / 10) * 10 + widx;
+               if (wwin && (wPreferences.ws_advance || i < 
scr->workspace_count))
+                       wWindowChangeWorkspace(wwin, i);
+               break;
+
+       case WKBD_MOVE_NEXTWORKSPACE:
+               if (wwin)
+                       wWindowChangeWorkspaceRelative(wwin, 1);
+               break;
+       case WKBD_MOVE_PREVWORKSPACE:
+               if (wwin)
+                       wWindowChangeWorkspaceRelative(wwin, -1);
+               break;
+       case WKBD_MOVE_LASTWORKSPACE:
+               if (wwin)
+                       wWindowChangeWorkspace(wwin, scr->last_workspace);
+               break;
+
+       case WKBD_MOVE_NEXTWSLAYER:
+       case WKBD_MOVE_PREVWSLAYER:
+               {
+                       if (wwin) {
+                               int row, column;
+
+                               row = scr->current_workspace / 10;
+                               column = scr->current_workspace % 10;
+
+                               if (command == WKBD_MOVE_NEXTWSLAYER) {
+                                       if ((row + 1) * 10 < 
scr->workspace_count)
+                                               wWindowChangeWorkspace(wwin, 
column + (row + 1) * 10);
+                               } else {
+                                       if (row > 0)
+                                               wWindowChangeWorkspace(wwin, 
column + (row - 1) * 10);
+                               }
+                       }
+               }
+               break;
+
        case WKBD_WINDOW1:
        case WKBD_WINDOW2:
        case WKBD_WINDOW3:
diff --git a/src/keybind.h b/src/keybind.h
index f4ec95c..4437950 100644
--- a/src/keybind.h
+++ b/src/keybind.h
@@ -75,6 +75,23 @@ enum {
        WKBD_NEXTWSLAYER,
        WKBD_PREVWSLAYER,
 
+       /* move to workspace */
+       WKBD_MOVE_WORKSPACE1,
+       WKBD_MOVE_WORKSPACE2,
+       WKBD_MOVE_WORKSPACE3,
+       WKBD_MOVE_WORKSPACE4,
+       WKBD_MOVE_WORKSPACE5,
+       WKBD_MOVE_WORKSPACE6,
+       WKBD_MOVE_WORKSPACE7,
+       WKBD_MOVE_WORKSPACE8,
+       WKBD_MOVE_WORKSPACE9,
+       WKBD_MOVE_WORKSPACE10,
+       WKBD_MOVE_NEXTWORKSPACE,
+       WKBD_MOVE_PREVWORKSPACE,
+       WKBD_MOVE_LASTWORKSPACE,
+       WKBD_MOVE_NEXTWSLAYER,
+       WKBD_MOVE_PREVWSLAYER,
+
        /* window shortcuts */
        WKBD_WINDOW1,
        WKBD_WINDOW2,
diff --git a/src/window.c b/src/window.c
index a070b0b..affafcf 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1917,6 +1917,30 @@ void wWindowChangeWorkspace(WWindow *wwin, int workspace)
                wWindowUnmap(wwin);
 }
 
+void wWindowChangeWorkspaceRelative(WWindow *wwin, int amount)
+{
+       WScreen *scr = wwin->screen_ptr;
+       int w = scr->current_workspace + amount;
+
+       if (amount < 0) {
+               if (w >= 0) {
+                       wWindowChangeWorkspace(wwin, w);
+               } else if (wPreferences.ws_cycle) {
+                       wWindowChangeWorkspace(wwin, scr->workspace_count + w);
+               }
+       } else if (amount > 0) {
+               if (w < scr->workspace_count) {
+                       wWindowChangeWorkspace(wwin, w);
+               } else if (wPreferences.ws_advance) {
+                       int workspace = WMIN(w, MAX_WORKSPACES - 1);
+                       wWorkspaceMake(scr, workspace);
+                       wWindowChangeWorkspace(wwin, workspace);
+               } else if (wPreferences.ws_cycle) {
+                       wWindowChangeWorkspace(wwin, w % scr->workspace_count);
+               }
+       }
+}
+
 void wWindowSynthConfigureNotify(WWindow *wwin)
 {
        XEvent sevent;
diff --git a/src/window.h b/src/window.h
index c4148a3..2e24f49 100644
--- a/src/window.h
+++ b/src/window.h
@@ -364,6 +364,7 @@ void wWindowUpdateButtonImages(WWindow *wwin);
 void wWindowSaveState(WWindow *wwin);
 
 void wWindowChangeWorkspace(WWindow *wwin, int workspace);
+void wWindowChangeWorkspaceRelative(WWindow *wwin, int amount);
 
 void wWindowSetKeyGrabs(WWindow *wwin);
 
diff --git a/src/winmenu.c b/src/winmenu.c
index 983f7e8..7d49d2b 100644
--- a/src/winmenu.c
+++ b/src/winmenu.c
@@ -220,25 +220,36 @@ static void updateWorkspaceMenu(WMenu * menu)
 {
        WScreen *scr = menu->frame->screen_ptr;
        char title[MAX_WORKSPACENAME_WIDTH + 1];
+       WMenuEntry *entry;
        int i;
 
        for (i = 0; i < scr->workspace_count; i++) {
                if (i < menu->entry_no) {
-                       if (strcmp(menu->entries[i]->text, 
scr->workspaces[i]->name) != 0) {
-                               wfree(menu->entries[i]->text);
+
+                       entry = menu->entries[i];
+                       if (strcmp(entry->text, scr->workspaces[i]->name) != 0) 
{
+                               wfree(entry->text);
                                strncpy(title, scr->workspaces[i]->name, 
MAX_WORKSPACENAME_WIDTH);
                                title[MAX_WORKSPACENAME_WIDTH] = 0;
                                menu->entries[i]->text = wstrdup(title);
+                               menu->entries[i]->rtext = 
GetShortcutKey(wKeyBindings[WKBD_MOVE_WORKSPACE1 + i]);
                                menu->flags.realized = 0;
                        }
                } else {
                        strncpy(title, scr->workspaces[i]->name, 
MAX_WORKSPACENAME_WIDTH);
                        title[MAX_WORKSPACENAME_WIDTH] = 0;
 
-                       wMenuAddCallback(menu, title, switchWSCommand, NULL);
+                       entry = wMenuAddCallback(menu, title, switchWSCommand, 
NULL);
+                       entry->rtext = 
GetShortcutKey(wKeyBindings[WKBD_MOVE_WORKSPACE1 + i]);
 
                        menu->flags.realized = 0;
                }
+
+               /* workspace shortcut labels */
+               if (i / 10 == scr->current_workspace / 10)
+                       entry->rtext = 
GetShortcutKey(wKeyBindings[WKBD_MOVE_WORKSPACE1 + (i % 10)]);
+               else
+                       entry->rtext = NULL;
        }
 
        if (!menu->flags.realized)
-- 
1.8.1.4

From ef605c4a2aedc93ba074121296bf0cc0229f00d3 Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Wed, 27 Mar 2013 21:10:01 +0000
Subject: [PATCH 7/7] Allow WPrefs to configure workspace switching keys.

Added MoveToWorkspace*Key, MoveToNextWorkspaceKey,
MoveToPrevWorkspaceKey, MoveToLastWorkspaceKey,
MoveToNextWorkspaceLayerKey and MoveToPrevWorkspaceLayerKey to the
keyboard shortcut panel of WPrefs.
---
 WPrefs.app/KeyboardShortcuts.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/WPrefs.app/KeyboardShortcuts.c b/WPrefs.app/KeyboardShortcuts.c
index f0eb2d6..61a49a1 100644
--- a/WPrefs.app/KeyboardShortcuts.c
+++ b/WPrefs.app/KeyboardShortcuts.c
@@ -104,6 +104,21 @@ static char *keyOptions[] = {
        "Workspace8Key",
        "Workspace9Key",
        "Workspace10Key",
+       "MoveToNextWorkspaceKey",
+       "MoveToPrevWorkspaceKey",
+       "MoveToLastWorkspaceKey",
+       "MoveToNextWorkspaceLayerKey",
+       "MoveToPrevWorkspaceLayerKey",
+       "MoveToWorkspace1Key",
+       "MoveToWorkspace2Key",
+       "MoveToWorkspace3Key",
+       "MoveToWorkspace4Key",
+       "MoveToWorkspace5Key",
+       "MoveToWorkspace6Key",
+       "MoveToWorkspace7Key",
+       "MoveToWorkspace8Key",
+       "MoveToWorkspace9Key",
+       "MoveToWorkspace10Key",
        "WindowShortcut1Key",
        "WindowShortcut2Key",
        "WindowShortcut3Key",
@@ -493,6 +508,21 @@ static void createPanel(Panel * p)
        WMAddListItem(panel->actLs, _("Switch to workspace 8"));
        WMAddListItem(panel->actLs, _("Switch to workspace 9"));
        WMAddListItem(panel->actLs, _("Switch to workspace 10"));
+       WMAddListItem(panel->actLs, _("Move window to next workspace"));
+       WMAddListItem(panel->actLs, _("Move window to previous workspace"));
+       WMAddListItem(panel->actLs, _("Move window to last used workspace"));
+       WMAddListItem(panel->actLs, _("Move window to next ten workspaces"));
+       WMAddListItem(panel->actLs, _("Move window to previous ten 
workspaces"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 1"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 2"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 3"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 4"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 5"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 6"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 7"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 8"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 9"));
+       WMAddListItem(panel->actLs, _("Move window to workspace 10"));
        WMAddListItem(panel->actLs, _("Shortcut for window 1"));
        WMAddListItem(panel->actLs, _("Shortcut for window 2"));
        WMAddListItem(panel->actLs, _("Shortcut for window 3"));
-- 
1.8.1.4

From 32a3de28aafd800f81adc86eb30c29d16c2888d1 Mon Sep 17 00:00:00 2001
From: Iain Patterson <w...@iain.cx>
Date: Thu, 28 Mar 2013 17:55:47 +0000
Subject: [PATCH 8/8] Document new workspace shortcuts.

Describe the LastWorkspaceKey and MoveToWorkspace*Key shortcuts in the
NEWS file.
---
 NEWS | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/NEWS b/NEWS
index 45f37d5..f771101 100644
--- a/NEWS
+++ b/NEWS
@@ -45,6 +45,15 @@ $ wdwrite WindowMaker FrameBorderWidth 1
 $ wdwrite WindowMaker FrameBorderColor black
 $ wdwrite WindowMaker FrameSelectedBorderColor white
 
+Keyboard shortcuts to move windows between workspaces
+-----------------------------------------------------
+
+You can now bind keyboard shortcuts - or use the window dropdown menus -
+to move windows to other workspaces.  You can either move a window directly to
+a particular workspace or to the "next" or "previous" workspace.
+
+The new shortcuts can be configured in WPrefs.
+
 
 --- 0.95.2
 
-- 
1.8.1.4

Reply via email to