/* 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 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; }