1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-20 12:58:08 +01:00
Files
wmaker/WPrefs.app/KeyboardShortcuts.c
Iain Patterson 18408fff93 Mac OS X-style window cycling.
For those not familiar with the way Macs cycle windows, the Command-Tab
sequence (Alt-Tab elsewhere) switches between DIFFERENT application windows
and Command-Grave (key above tab) switches between windows owned by the
SAME application as is currently focused.  So if you had three Safari and
two Finder windows open, and Safari had focus, Command-Tab would switch to
Finder; Command-Tab would switch back to Safari; Command-Grave would switch
to a different Safari window etc.

This patch implements "something like" the above by only populating the
switchpanel with windows matching the currently-focused WWindow's wm_class
when the new cycling mode is activated.  In practice this means you can
switch to The Next XTerm or The Next Firefox Window using this method.

The configuration names for these new shortcuts are GroupNext and
GroupPrev.  The patch tells WPrefs.app about them.  Of course switching to
The Next Window is still possible with the (unchanged) FocusNext and
FocusPrev keys.
2009-09-14 16:51:26 +02:00

623 lines
18 KiB
C

/* KeyboardShortcuts.c- keyboard shortcut bindings
*
* WPrefs - Window Maker Preferences Program
*
* Copyright (c) 1998-2003 Alfredo K. Kojima
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include "config.h" /* for HAVE_XCONVERTCASE */
#include "WPrefs.h"
#include <ctype.h>
#include <X11/keysym.h>
typedef struct _Panel {
WMBox *box;
char *sectionName;
char *description;
CallbackRec callbacks;
WMWidget *parent;
WMLabel *actL;
WMList *actLs;
WMFrame *shoF;
WMTextField *shoT;
WMButton *cleB;
WMButton *defB;
WMLabel *instructionsL;
WMColor *white;
WMColor *black;
WMColor *gray;
WMFont *font;
/**/ char capturing;
char **shortcuts;
int actionCount;
} _Panel;
#define ICON_FILE "keyshortcuts"
/* must be in the same order as the corresponding items in actions list */
static char *keyOptions[] = {
"RootMenuKey",
"WindowListKey",
"WindowMenuKey",
"HideKey",
"HideOthersKey",
"MiniaturizeKey",
"CloseKey",
"MaximizeKey",
"VMaximizeKey",
"HMaximizeKey",
"LHMaximizeKey",
"RHMaximizeKey",
"MaximusKey",
"RaiseKey",
"LowerKey",
"RaiseLowerKey",
"ShadeKey",
"MoveResizeKey",
"SelectKey",
"FocusNextKey",
"FocusPrevKey",
"GroupNextKey",
"GroupPrevKey",
"NextWorkspaceKey",
"PrevWorkspaceKey",
"NextWorkspaceLayerKey",
"PrevWorkspaceLayerKey",
"Workspace1Key",
"Workspace2Key",
"Workspace3Key",
"Workspace4Key",
"Workspace5Key",
"Workspace6Key",
"Workspace7Key",
"Workspace8Key",
"Workspace9Key",
"Workspace10Key",
"WindowShortcut1Key",
"WindowShortcut2Key",
"WindowShortcut3Key",
"WindowShortcut4Key",
"WindowShortcut5Key",
"WindowShortcut6Key",
"WindowShortcut7Key",
"WindowShortcut8Key",
"WindowShortcut9Key",
"WindowShortcut10Key",
"ScreenSwitchKey",
#ifdef VIRTUAL_DESKTOP
"VirtualEdgeLeftKey",
"VirtualEdgeRightKey",
"VirtualEdgeUpKey",
"VirtualEdgeDownKey",
#endif
"ClipRaiseKey",
"ClipLowerKey",
#ifndef XKB_MODELOCK
"ClipRaiseLowerKey"
#else
"ClipRaiseLowerKey",
"ToggleKbdModeKey"
#endif /* XKB_MODELOCK */
};
#ifndef HAVE_XCONVERTCASE
/* from Xlib */
static void XConvertCase(register KeySym sym, KeySym * lower, KeySym * upper)
{
*lower = sym;
*upper = sym;
switch (sym >> 8) {
case 0: /* Latin 1 */
if ((sym >= XK_A) && (sym <= XK_Z))
*lower += (XK_a - XK_A);
else if ((sym >= XK_a) && (sym <= XK_z))
*upper -= (XK_a - XK_A);
else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
*lower += (XK_agrave - XK_Agrave);
else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
*upper -= (XK_agrave - XK_Agrave);
else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
*lower += (XK_oslash - XK_Ooblique);
else if ((sym >= XK_oslash) && (sym <= XK_thorn))
*upper -= (XK_oslash - XK_Ooblique);
break;
case 1: /* Latin 2 */
/* Assume the KeySym is a legal value (ignore discontinuities) */
if (sym == XK_Aogonek)
*lower = XK_aogonek;
else if (sym >= XK_Lstroke && sym <= XK_Sacute)
*lower += (XK_lstroke - XK_Lstroke);
else if (sym >= XK_Scaron && sym <= XK_Zacute)
*lower += (XK_scaron - XK_Scaron);
else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
*lower += (XK_zcaron - XK_Zcaron);
else if (sym == XK_aogonek)
*upper = XK_Aogonek;
else if (sym >= XK_lstroke && sym <= XK_sacute)
*upper -= (XK_lstroke - XK_Lstroke);
else if (sym >= XK_scaron && sym <= XK_zacute)
*upper -= (XK_scaron - XK_Scaron);
else if (sym >= XK_zcaron && sym <= XK_zabovedot)
*upper -= (XK_zcaron - XK_Zcaron);
else if (sym >= XK_Racute && sym <= XK_Tcedilla)
*lower += (XK_racute - XK_Racute);
else if (sym >= XK_racute && sym <= XK_tcedilla)
*upper -= (XK_racute - XK_Racute);
break;
case 2: /* Latin 3 */
/* Assume the KeySym is a legal value (ignore discontinuities) */
if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
*lower += (XK_hstroke - XK_Hstroke);
else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
*lower += (XK_gbreve - XK_Gbreve);
else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
*upper -= (XK_hstroke - XK_Hstroke);
else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
*upper -= (XK_gbreve - XK_Gbreve);
else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
*lower += (XK_cabovedot - XK_Cabovedot);
else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
*upper -= (XK_cabovedot - XK_Cabovedot);
break;
case 3: /* Latin 4 */
/* Assume the KeySym is a legal value (ignore discontinuities) */
if (sym >= XK_Rcedilla && sym <= XK_Tslash)
*lower += (XK_rcedilla - XK_Rcedilla);
else if (sym >= XK_rcedilla && sym <= XK_tslash)
*upper -= (XK_rcedilla - XK_Rcedilla);
else if (sym == XK_ENG)
*lower = XK_eng;
else if (sym == XK_eng)
*upper = XK_ENG;
else if (sym >= XK_Amacron && sym <= XK_Umacron)
*lower += (XK_amacron - XK_Amacron);
else if (sym >= XK_amacron && sym <= XK_umacron)
*upper -= (XK_amacron - XK_Amacron);
break;
case 6: /* Cyrillic */
/* Assume the KeySym is a legal value (ignore discontinuities) */
if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
*lower -= (XK_Serbian_DJE - XK_Serbian_dje);
else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
*upper += (XK_Serbian_DJE - XK_Serbian_dje);
else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
*lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
*upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
break;
case 7: /* Greek */
/* Assume the KeySym is a legal value (ignore discontinuities) */
if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
*lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
sym != XK_Greek_iotaaccentdieresis && sym != XK_Greek_upsilonaccentdieresis)
*upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
*lower += (XK_Greek_alpha - XK_Greek_ALPHA);
else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega && sym != XK_Greek_finalsmallsigma)
*upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
break;
case 0x14: /* Armenian */
if (sym >= XK_Armenian_AYB && sym <= XK_Armenian_fe) {
*lower = sym | 1;
*upper = sym & ~1;
}
break;
}
}
#endif
static char *captureShortcut(Display * dpy, _Panel * panel)
{
XEvent ev;
KeySym ksym, lksym, uksym;
char buffer[64];
char *key = NULL;
while (panel->capturing) {
XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
WMNextEvent(dpy, &ev);
if (ev.type == KeyPress && ev.xkey.keycode != 0) {
ksym = XKeycodeToKeysym(dpy, ev.xkey.keycode, 0);
if (!IsModifierKey(ksym)) {
XConvertCase(ksym, &lksym, &uksym);
key = XKeysymToString(uksym);
panel->capturing = 0;
break;
}
}
WMHandleEvent(&ev);
}
if (!key)
return NULL;
buffer[0] = 0;
if (ev.xkey.state & ControlMask) {
strcat(buffer, "Control+");
}
if (ev.xkey.state & ShiftMask) {
strcat(buffer, "Shift+");
}
if (ev.xkey.state & Mod1Mask) {
strcat(buffer, "Mod1+");
}
if (ev.xkey.state & Mod2Mask) {
strcat(buffer, "Mod2+");
}
if (ev.xkey.state & Mod3Mask) {
strcat(buffer, "Mod3+");
}
if (ev.xkey.state & Mod4Mask) {
strcat(buffer, "Mod4+");
}
if (ev.xkey.state & Mod5Mask) {
strcat(buffer, "Mod5+");
}
strcat(buffer, key);
return wstrdup(buffer);
}
static void captureClick(WMWidget * w, void *data)
{
_Panel *panel = (_Panel *) data;
Display *dpy = WMScreenDisplay(WMWidgetScreen(panel->parent));
char *shortcut;
if (!panel->capturing) {
panel->capturing = 1;
WMSetButtonText(w, _("Cancel"));
WMSetLabelText(panel->instructionsL,
_("Press the desired shortcut key(s) or click Cancel to stop capturing."));
XGrabKeyboard(dpy, WMWidgetXID(panel->parent), True, GrabModeAsync, GrabModeAsync, CurrentTime);
shortcut = captureShortcut(dpy, panel);
if (shortcut) {
int row = WMGetListSelectedItemRow(panel->actLs);
WMSetTextFieldText(panel->shoT, shortcut);
if (row >= 0) {
if (panel->shortcuts[row])
wfree(panel->shortcuts[row]);
panel->shortcuts[row] = shortcut;
WMRedisplayWidget(panel->actLs);
} else {
wfree(shortcut);
}
}
}
panel->capturing = 0;
WMSetButtonText(w, _("Capture"));
WMSetLabelText(panel->instructionsL, _("Click on Capture to interactively define the shortcut key."));
XUngrabKeyboard(dpy, CurrentTime);
}
static void clearShortcut(WMWidget * w, void *data)
{
_Panel *panel = (_Panel *) data;
int row = WMGetListSelectedItemRow(panel->actLs);
WMSetTextFieldText(panel->shoT, NULL);
if (row >= 0) {
if (panel->shortcuts[row])
wfree(panel->shortcuts[row]);
panel->shortcuts[row] = NULL;
WMRedisplayWidget(panel->actLs);
}
}
static void typedKeys(void *observerData, WMNotification * notification)
{
_Panel *panel = (_Panel *) observerData;
int row = WMGetListSelectedItemRow(panel->actLs);
if (row < 0)
return;
if (panel->shortcuts[row])
wfree(panel->shortcuts[row]);
panel->shortcuts[row] = WMGetTextFieldText(panel->shoT);
if (strlen(panel->shortcuts[row]) == 0) {
wfree(panel->shortcuts[row]);
panel->shortcuts[row] = NULL;
}
WMRedisplayWidget(panel->actLs);
}
static void listClick(WMWidget * w, void *data)
{
_Panel *panel = (_Panel *) data;
int row = WMGetListSelectedItemRow(w);
WMSetTextFieldText(panel->shoT, panel->shortcuts[row]);
}
static char *trimstr(char *str)
{
char *p = str;
int i;
while (isspace(*p))
p++;
p = wstrdup(p);
i = strlen(p);
while (isspace(p[i]) && i > 0) {
p[i] = 0;
i--;
}
return p;
}
static void showData(_Panel * panel)
{
char *str;
int i;
for (i = 0; i < panel->actionCount; i++) {
str = GetStringForKey(keyOptions[i]);
if (panel->shortcuts[i])
wfree(panel->shortcuts[i]);
if (str)
panel->shortcuts[i] = trimstr(str);
else
panel->shortcuts[i] = NULL;
if (panel->shortcuts[i] &&
(strcasecmp(panel->shortcuts[i], "none") == 0 || strlen(panel->shortcuts[i]) == 0)) {
wfree(panel->shortcuts[i]);
panel->shortcuts[i] = NULL;
}
}
WMRedisplayWidget(panel->actLs);
}
static void paintItem(WMList * lPtr, int index, Drawable d, char *text, int state, WMRect * rect)
{
int width, height, x, y;
_Panel *panel = (_Panel *) WMGetHangedData(lPtr);
WMScreen *scr = WMWidgetScreen(lPtr);
Display *dpy = WMScreenDisplay(scr);
WMColor *backColor = (state & WLDSSelected) ? panel->white : panel->gray;
width = rect->size.width;
height = rect->size.height;
x = rect->pos.x;
y = rect->pos.y;
XFillRectangle(dpy, d, WMColorGC(backColor), x, y, width, height);
if (panel->shortcuts[index]) {
WMPixmap *pix = WMGetSystemPixmap(scr, WSICheckMark);
WMSize size = WMGetPixmapSize(pix);
WMDrawPixmap(pix, d, x + (20 - size.width) / 2, (height - size.height) / 2 + y);
WMReleasePixmap(pix);
}
WMDrawString(scr, d, panel->black, panel->font, x + 20, y, text, strlen(text));
}
static void createPanel(Panel * p)
{
_Panel *panel = (_Panel *) p;
WMScreen *scr = WMWidgetScreen(panel->parent);
WMColor *color;
WMFont *boldFont;
panel->capturing = 0;
panel->white = WMWhiteColor(scr);
panel->black = WMBlackColor(scr);
panel->gray = WMGrayColor(scr);
panel->font = WMSystemFontOfSize(scr, 12);
panel->box = WMCreateBox(panel->parent);
WMSetViewExpandsToParent(WMWidgetView(panel->box), 2, 2, 2, 2);
boldFont = WMBoldSystemFontOfSize(scr, 12);
/* **************** Actions **************** */
panel->actL = WMCreateLabel(panel->box);
WMResizeWidget(panel->actL, 280, 20);
WMMoveWidget(panel->actL, 20, 10);
WMSetLabelFont(panel->actL, boldFont);
WMSetLabelText(panel->actL, _("Actions"));
WMSetLabelRelief(panel->actL, WRSunken);
WMSetLabelTextAlignment(panel->actL, WACenter);
color = WMDarkGrayColor(scr);
WMSetWidgetBackgroundColor(panel->actL, color);
WMReleaseColor(color);
WMSetLabelTextColor(panel->actL, panel->white);
panel->actLs = WMCreateList(panel->box);
WMResizeWidget(panel->actLs, 280, 190);
WMMoveWidget(panel->actLs, 20, 32);
WMSetListUserDrawProc(panel->actLs, paintItem);
WMHangData(panel->actLs, panel);
WMAddListItem(panel->actLs, _("Open applications menu"));
WMAddListItem(panel->actLs, _("Open window list menu"));
WMAddListItem(panel->actLs, _("Open window commands menu"));
WMAddListItem(panel->actLs, _("Hide active application"));
WMAddListItem(panel->actLs, _("Hide other applications"));
WMAddListItem(panel->actLs, _("Miniaturize active window"));
WMAddListItem(panel->actLs, _("Close active window"));
WMAddListItem(panel->actLs, _("Maximize active window"));
WMAddListItem(panel->actLs, _("Maximize active window vertically"));
WMAddListItem(panel->actLs, _("Maximize active window horizontally"));
WMAddListItem(panel->actLs, _("Maximize active window left half"));
WMAddListItem(panel->actLs, _("Maximize active window right half"));
WMAddListItem(panel->actLs, _("Maximus: Tiled maximization "));
WMAddListItem(panel->actLs, _("Raise active window"));
WMAddListItem(panel->actLs, _("Lower active window"));
WMAddListItem(panel->actLs, _("Raise/Lower window under mouse pointer"));
WMAddListItem(panel->actLs, _("Shade active window"));
WMAddListItem(panel->actLs, _("Move/Resize active window"));
WMAddListItem(panel->actLs, _("Select active window"));
WMAddListItem(panel->actLs, _("Focus next window"));
WMAddListItem(panel->actLs, _("Focus previous window"));
WMAddListItem(panel->actLs, _("Focus next group window"));
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 next ten workspaces"));
WMAddListItem(panel->actLs, _("Switch to previous ten workspaces"));
WMAddListItem(panel->actLs, _("Switch to workspace 1"));
WMAddListItem(panel->actLs, _("Switch to workspace 2"));
WMAddListItem(panel->actLs, _("Switch to workspace 3"));
WMAddListItem(panel->actLs, _("Switch to workspace 4"));
WMAddListItem(panel->actLs, _("Switch to workspace 5"));
WMAddListItem(panel->actLs, _("Switch to workspace 6"));
WMAddListItem(panel->actLs, _("Switch to workspace 7"));
WMAddListItem(panel->actLs, _("Switch to workspace 8"));
WMAddListItem(panel->actLs, _("Switch to workspace 9"));
WMAddListItem(panel->actLs, _("Switch to workspace 10"));
WMAddListItem(panel->actLs, _("Shortcut for window 1"));
WMAddListItem(panel->actLs, _("Shortcut for window 2"));
WMAddListItem(panel->actLs, _("Shortcut for window 3"));
WMAddListItem(panel->actLs, _("Shortcut for window 4"));
WMAddListItem(panel->actLs, _("Shortcut for window 5"));
WMAddListItem(panel->actLs, _("Shortcut for window 6"));
WMAddListItem(panel->actLs, _("Shortcut for window 7"));
WMAddListItem(panel->actLs, _("Shortcut for window 8"));
WMAddListItem(panel->actLs, _("Shortcut for window 9"));
WMAddListItem(panel->actLs, _("Shortcut for window 10"));
WMAddListItem(panel->actLs, _("Switch to Next Screen/Monitor"));
#ifdef VIRTUAL_DESKTOP
WMAddListItem(panel->actLs, _("Move VirtualDesktop to next left edge"));
WMAddListItem(panel->actLs, _("Move VirtualDesktop to next right edge"));
WMAddListItem(panel->actLs, _("Move VirtualDesktop to next top edge"));
WMAddListItem(panel->actLs, _("Move VirtualDesktop to next bottom edge"));
#endif
WMAddListItem(panel->actLs, _("Raise Clip"));
WMAddListItem(panel->actLs, _("Lower Clip"));
WMAddListItem(panel->actLs, _("Raise/Lower Clip"));
#ifdef XKB_MODELOCK
WMAddListItem(panel->actLs, _("Toggle keyboard language"));
#endif /* XKB_MODELOCK */
WMSetListAction(panel->actLs, listClick, panel);
panel->actionCount = WMGetListNumberOfRows(panel->actLs);
panel->shortcuts = wmalloc(sizeof(char *) * panel->actionCount);
memset(panel->shortcuts, 0, sizeof(char *) * panel->actionCount);
/***************** Shortcut ****************/
panel->shoF = WMCreateFrame(panel->box);
WMResizeWidget(panel->shoF, 190, 210);
WMMoveWidget(panel->shoF, 315, 10);
WMSetFrameTitle(panel->shoF, _("Shortcut"));
panel->shoT = WMCreateTextField(panel->shoF);
WMResizeWidget(panel->shoT, 160, 20);
WMMoveWidget(panel->shoT, 15, 65);
WMAddNotificationObserver(typedKeys, panel, WMTextDidChangeNotification, panel->shoT);
panel->cleB = WMCreateCommandButton(panel->shoF);
WMResizeWidget(panel->cleB, 75, 24);
WMMoveWidget(panel->cleB, 15, 95);
WMSetButtonText(panel->cleB, _("Clear"));
WMSetButtonAction(panel->cleB, clearShortcut, panel);
panel->defB = WMCreateCommandButton(panel->shoF);
WMResizeWidget(panel->defB, 75, 24);
WMMoveWidget(panel->defB, 100, 95);
WMSetButtonText(panel->defB, _("Capture"));
WMSetButtonAction(panel->defB, captureClick, panel);
panel->instructionsL = WMCreateLabel(panel->shoF);
WMResizeWidget(panel->instructionsL, 160, 55);
WMMoveWidget(panel->instructionsL, 15, 140);
WMSetLabelTextAlignment(panel->instructionsL, WACenter);
WMSetLabelWraps(panel->instructionsL, True);
WMSetLabelText(panel->instructionsL, _("Click on Capture to interactively define the shortcut key."));
WMMapSubwidgets(panel->shoF);
WMReleaseFont(boldFont);
WMRealizeWidget(panel->box);
WMMapSubwidgets(panel->box);
showData(panel);
}
static void storeData(_Panel * panel)
{
int i;
char *str;
for (i = 0; i < panel->actionCount; i++) {
str = NULL;
if (panel->shortcuts[i]) {
str = trimstr(panel->shortcuts[i]);
if (strlen(str) == 0) {
wfree(str);
str = NULL;
}
}
if (str) {
SetStringForKey(str, keyOptions[i]);
wfree(str);
} else {
SetStringForKey("None", keyOptions[i]);
}
}
}
Panel *InitKeyboardShortcuts(WMScreen * scr, WMWidget * parent)
{
_Panel *panel;
panel = wmalloc(sizeof(_Panel));
memset(panel, 0, sizeof(_Panel));
panel->sectionName = _("Keyboard Shortcut Preferences");
panel->description = _("Change the keyboard shortcuts for actions such\n"
"as changing workspaces and opening menus.");
panel->parent = parent;
panel->callbacks.createWidgets = createPanel;
panel->callbacks.updateDomain = storeData;
AddSection(panel, ICON_FILE);
return panel;
}