windowmaker-wl/WPrefs.app/KeyboardShortcuts.c

588 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "WPrefs.h"
#include <ctype.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;
Bool capturing;
char **shortcuts;
int actionCount;
} _Panel;
#define ICON_FILE "keyshortcuts"
/*
* List of user definable shortcut keys
* First parameter is the internal keyword known by WMaker
* Second is the text displayed to the user
*/
static struct keyOption {
const char *key;
const char *title;
} keyOptions[] = {
{ "RootMenuKey", N_("Open applications menu") },
{ "WindowListKey", N_("Open window list menu") },
{ "WindowMenuKey", N_("Open window commands menu") },
{ "HideKey", N_("Hide active application") },
{ "HideOthersKey", N_("Hide other applications") },
{ "MiniaturizeKey", N_("Miniaturize active window") },
{ "MinimizeAllKey", N_("Miniaturize all windows") },
{ "CloseKey", N_("Close active window") },
{ "MaximizeKey", N_("Maximize active window") },
{ "VMaximizeKey", N_("Maximize active window vertically") },
{ "HMaximizeKey", N_("Maximize active window horizontally") },
{ "LHMaximizeKey", N_("Maximize active window left half") },
{ "RHMaximizeKey", N_("Maximize active window right half") },
{ "THMaximizeKey", N_("Maximize active window top half") },
{ "BHMaximizeKey", N_("Maximize active window bottom half") },
{ "TLCMaximizeKey", N_("Maximize active window top left corner") },
{ "TRCMaximizeKey", N_("Maximize active window top right corner") },
{ "BLCMaximizeKey", N_("Maximize active window bottom left corner") },
{ "BRCMaximizeKey", N_("Maximize active window bottom right corner") },
{ "MaximusKey", N_("Tile active window") },
{ "CenterKey", N_("Center active window") },
{ "KeepOnTopKey", N_("Toggle window on top status") },
{ "KeepAtBottomKey",N_("Toggle window at bottom status") },
{ "OmnipresentKey", N_("Toggle window omnipresent status") },
{ "RaiseKey", N_("Raise active window") },
{ "LowerKey", N_("Lower active window") },
{ "RaiseLowerKey", N_("Raise/Lower window under mouse pointer") },
{ "ShadeKey", N_("Shade active window") },
{ "MoveResizeKey", N_("Move/Resize active window") },
{ "SelectKey", N_("Select active window") },
{ "FocusNextKey", N_("Focus next window") },
{ "FocusPrevKey", N_("Focus previous window") },
{ "GroupNextKey", N_("Focus next group window") },
{ "GroupPrevKey", N_("Focus previous group window") },
/* Workspace Related */
{ "WorkspaceMapKey", N_("Open workspace pager") },
{ "NextWorkspaceKey", N_("Switch to next workspace") },
{ "PrevWorkspaceKey", N_("Switch to previous workspace") },
{ "LastWorkspaceKey", N_("Switch to last used workspace") },
{ "NextWorkspaceLayerKey", N_("Switch to next ten workspaces") },
{ "PrevWorkspaceLayerKey", N_("Switch to previous ten workspaces") },
{ "Workspace1Key", N_("Switch to workspace 1") },
{ "Workspace2Key", N_("Switch to workspace 2") },
{ "Workspace3Key", N_("Switch to workspace 3") },
{ "Workspace4Key", N_("Switch to workspace 4") },
{ "Workspace5Key", N_("Switch to workspace 5") },
{ "Workspace6Key", N_("Switch to workspace 6") },
{ "Workspace7Key", N_("Switch to workspace 7") },
{ "Workspace8Key", N_("Switch to workspace 8") },
{ "Workspace9Key", N_("Switch to workspace 9") },
{ "Workspace10Key", N_("Switch to workspace 10") },
{ "MoveToNextWorkspaceKey", N_("Move window to next workspace") },
{ "MoveToPrevWorkspaceKey", N_("Move window to previous workspace") },
{ "MoveToLastWorkspaceKey", N_("Move window to last used workspace") },
{ "MoveToNextWorkspaceLayerKey", N_("Move window to next ten workspaces") },
{ "MoveToPrevWorkspaceLayerKey", N_("Move window to previous ten workspaces") },
{ "MoveToWorkspace1Key", N_("Move window to workspace 1") },
{ "MoveToWorkspace2Key", N_("Move window to workspace 2") },
{ "MoveToWorkspace3Key", N_("Move window to workspace 3") },
{ "MoveToWorkspace4Key", N_("Move window to workspace 4") },
{ "MoveToWorkspace5Key", N_("Move window to workspace 5") },
{ "MoveToWorkspace6Key", N_("Move window to workspace 6") },
{ "MoveToWorkspace7Key", N_("Move window to workspace 7") },
{ "MoveToWorkspace8Key", N_("Move window to workspace 8") },
{ "MoveToWorkspace9Key", N_("Move window to workspace 9") },
{ "MoveToWorkspace10Key", N_("Move window to workspace 10") },
/* Window Selection */
{ "WindowShortcut1Key", N_("Shortcut for window 1") },
{ "WindowShortcut2Key", N_("Shortcut for window 2") },
{ "WindowShortcut3Key", N_("Shortcut for window 3") },
{ "WindowShortcut4Key", N_("Shortcut for window 4") },
{ "WindowShortcut5Key", N_("Shortcut for window 5") },
{ "WindowShortcut6Key", N_("Shortcut for window 6") },
{ "WindowShortcut7Key", N_("Shortcut for window 7") },
{ "WindowShortcut8Key", N_("Shortcut for window 8") },
{ "WindowShortcut9Key", N_("Shortcut for window 9") },
{ "WindowShortcut10Key", N_("Shortcut for window 10") },
/* Head Selection */
{ "MoveTo12to6Head", N_("Move to right/bottom/left/top head") },
{ "MoveTo6to12Head", N_("Move to left/top/right/bottom head") },
/* Misc. */
{ "WindowRelaunchKey", N_("Launch new instance of application") },
{ "ScreenSwitchKey", N_("Switch to next screen/monitor") },
{ "RunKey", N_("Run application") },
{ "ExitKey", N_("Exit Window Maker") },
{ "DockRaiseLowerKey", N_("Raise/Lower Dock") },
{ "ClipRaiseLowerKey", N_("Raise/Lower Clip") },
{ "ScreenCaptureKey", N_("Capture the entire screen") },
{ "WindowCaptureKey", N_("Capture a window") },
{ "PartialCaptureKey", N_("Capture a portion of the screen") }
#ifdef XKB_MODELOCK
,{ "ToggleKbdModeKey", N_("Toggle keyboard language") }
#endif /* XKB_MODELOCK */
};
static int NumLockMask(WMScreen *scr)
{
int i, j;
static int mask_table[8] = {
WM_MOD_SHIFT, WM_MOD_LOCK, WM_MOD_CONTROL, WM_MOD_MOD1,
WM_MOD_MOD2, WM_MOD_MOD3, WM_MOD_MOD4, WM_MOD_MOD5
};
unsigned int numlock_keycode = scr->eventBackend->keysym_to_keycode(
scr->eventBackendCtx, WMK_Num_Lock);
if (numlock_keycode == 0)
return 0;
#define NL_MAX_PER_MOD 16
WMNativeKeysym keysyms[8 * NL_MAX_PER_MOD];
int mkpm = scr->eventBackend->get_modifier_keysyms(scr->eventBackendCtx,
keysyms, NL_MAX_PER_MOD);
if (mkpm == 0)
return 0;
for (j = 0; j < 8; j++) {
for (i = 0; i < NL_MAX_PER_MOD; i++) {
WMNativeKeysym ks = keysyms[j * NL_MAX_PER_MOD + i];
if (ks == WMK_Num_Lock)
return mask_table[j];
}
}
#undef NL_MAX_PER_MOD
return 0;
}
char *capture_shortcut(WMScreen *scr, Bool *capturing, Bool convert_case)
{
WMEvent ev;
WMNativeKeysym ksym, lksym, uksym;
char buffer[64];
const char *key = NULL;
unsigned int numlock_mask;
while (*capturing) {
scr->eventBackend->allow_events(scr->eventBackendCtx);
WMNextEvent(scr, &ev);
if (ev.type == WME_KEY_PRESS && ev.u.key.keycode != 0) {
numlock_mask = NumLockMask(scr);
/* conditional mask check to get numeric keypad keys */
ksym = scr->eventBackend->keycode_to_keysym(
scr->eventBackendCtx, ev.u.key.keycode,
(ev.u.key.state & numlock_mask) ? 1 : 0);
if (!WM_IsModifierKey(ksym)) {
if (convert_case) {
scr->eventBackend->keysym_convert_case(
scr->eventBackendCtx, ksym, &lksym, &uksym);
key = scr->eventBackend->keysym_to_string(
scr->eventBackendCtx, uksym);
} else {
key = scr->eventBackend->keysym_to_string(
scr->eventBackendCtx, ksym);
}
*capturing = 0;
break;
}
}
WMHandleEvent(&ev);
}
if (!key)
return NULL;
buffer[0] = 0;
if (ev.u.key.state & WM_MOD_CONTROL)
strcat(buffer, "Control+");
if (ev.u.key.state & WM_MOD_SHIFT)
strcat(buffer, "Shift+");
if ((numlock_mask != WM_MOD_MOD1) && (ev.u.key.state & WM_MOD_MOD1))
strcat(buffer, "Mod1+");
if ((numlock_mask != WM_MOD_MOD2) && (ev.u.key.state & WM_MOD_MOD2))
strcat(buffer, "Mod2+");
if ((numlock_mask != WM_MOD_MOD3) && (ev.u.key.state & WM_MOD_MOD3))
strcat(buffer, "Mod3+");
if ((numlock_mask != WM_MOD_MOD4) && (ev.u.key.state & WM_MOD_MOD4))
strcat(buffer, "Mod4+");
if ((numlock_mask != WM_MOD_MOD5) && (ev.u.key.state & WM_MOD_MOD5))
strcat(buffer, "Mod5+");
wstrlcat(buffer, key, sizeof(buffer));
return wstrdup(buffer);
}
/*
* check if the keystr entered is already set to another action
* if found it returns the position in the keyOptions
*/
static int isKeySet(_Panel *panel, char *keystr)
{
int i;
char *str;
for (i = 0; i < panel->actionCount; i++) {
str = NULL;
if (panel->shortcuts[i]) {
str = wtrimspace(panel->shortcuts[i]);
if (strlen(str) == 0) {
wfree(str);
str = NULL;
}
}
if (str) {
if (strcmp(keystr, str) == 0) {
wfree(str);
return i;
}
wfree(str);
}
}
return -1;
}
static void captureClick(WMWidget * w, void *data)
{
_Panel *panel = (_Panel *) data;
WMScreen *scr = 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."));
scr->eventBackend->grab_keyboard(scr->eventBackendCtx, WMWidgetXID(panel->parent));
shortcut = capture_shortcut(scr, &panel->capturing, 1);
if (shortcut) {
int key_idx = -1;
int row = WMGetListSelectedItemRow(panel->actLs);
key_idx = isKeySet(panel, shortcut);
if (key_idx >= 0 && (key_idx != row)) {
char *msg;
msg = wstrconcat(_("Key shortcut already in use by the "), _(keyOptions[key_idx].title));
WMRunAlertPanel(WMWidgetScreen(w), GetWindow(),
_("Error"),
msg,
_("OK"), NULL, NULL);
wfree(msg);
wfree(shortcut);
} else {
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."));
scr->eventBackend->ungrab_keyboard(scr->eventBackendCtx);
}
static void clearShortcut(WMWidget * w, void *data)
{
_Panel *panel = (_Panel *) data;
int row = WMGetListSelectedItemRow(panel->actLs);
/* Parameter not used, but tell the compiler that it is ok */
(void) w;
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);
/* Parameter not used, but tell the compiler that it is ok */
(void) notification;
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 void showData(_Panel * panel)
{
char *str;
int i;
for (i = 0; i < panel->actionCount; i++) {
str = GetStringForKey(keyOptions[i].key);
if (panel->shortcuts[i])
wfree(panel->shortcuts[i]);
if (str)
panel->shortcuts[i] = wtrimspace(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, WMDrawable d, char *text, int state, WMRect * rect)
{
int width, height, x, y;
_Panel *panel = (_Panel *) WMGetHangedData(lPtr);
WMScreen *scr = WMWidgetScreen(lPtr);
WMColor *backColor = (state & WLDSSelected) ? panel->white : panel->gray;
width = rect->size.width;
height = rect->size.height;
x = rect->pos.x;
y = rect->pos.y;
scr->renderBackend->fill_rect(scr->renderBackendCtx, d, 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 int cmpKeyOptions(const void *v1, const void *v2)
{
int rc;
const struct keyOption *opt1 = (struct keyOption *)v1;
const struct keyOption *opt2 = (struct keyOption *)v2;
if ((rc = strncmp(opt1->title, opt2->title, 20)) < 0)
return -1;
else if (rc > 0)
return 1;
return 0;
}
static void createPanel(Panel * p)
{
_Panel *panel = (_Panel *) p;
WMScreen *scr = WMWidgetScreen(panel->parent);
WMColor *color;
WMFont *boldFont;
int i;
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, 314, 20);
WMMoveWidget(panel->actL, 9, 9);
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, 314, 191);
WMMoveWidget(panel->actLs, 9, 31);
WMSetListUserDrawProc(panel->actLs, paintItem);
WMHangData(panel->actLs, panel);
qsort(keyOptions, wlengthof(keyOptions), sizeof(keyOptions[0]), cmpKeyOptions);
for (i = 0; i < wlengthof(keyOptions); i++) {
WMAddListItem(panel->actLs, _(keyOptions[i].title));
}
WMSetListAction(panel->actLs, listClick, panel);
panel->actionCount = WMGetListNumberOfRows(panel->actLs);
panel->shortcuts = wmalloc(sizeof(char *) * panel->actionCount);
/***************** Shortcut ****************/
panel->shoF = WMCreateFrame(panel->box);
WMResizeWidget(panel->shoF, 178, 214);
WMMoveWidget(panel->shoF, 333, 8);
WMSetFrameTitle(panel->shoF, _("Shortcut"));
panel->shoT = WMCreateTextField(panel->shoF);
WMResizeWidget(panel->shoT, 160, 20);
WMMoveWidget(panel->shoT, 9, 65);
WMAddNotificationObserver(typedKeys, panel, WMTextDidChangeNotification, panel->shoT);
panel->cleB = WMCreateCommandButton(panel->shoF);
WMResizeWidget(panel->cleB, 75, 24);
WMMoveWidget(panel->cleB, 9, 95);
WMSetButtonText(panel->cleB, _("Clear"));
WMSetButtonAction(panel->cleB, clearShortcut, panel);
panel->defB = WMCreateCommandButton(panel->shoF);
WMResizeWidget(panel->defB, 75, 24);
WMMoveWidget(panel->defB, 94, 95);
WMSetButtonText(panel->defB, _("Capture"));
WMSetButtonAction(panel->defB, captureClick, panel);
panel->instructionsL = WMCreateLabel(panel->shoF);
WMResizeWidget(panel->instructionsL, 160, 55);
WMMoveWidget(panel->instructionsL, 9, 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 = wtrimspace(panel->shortcuts[i]);
if (strlen(str) == 0) {
wfree(str);
str = NULL;
}
}
if (str) {
SetStringForKey(str, keyOptions[i].key);
wfree(str);
} else {
SetStringForKey("None", keyOptions[i].key);
}
}
}
Panel *InitKeyboardShortcuts(WMWidget *parent)
{
_Panel *panel;
panel = wmalloc(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;
}