- Fixed crashing bug in menu.c
[wmaker-crm.git] / WPrefs.app / KeyboardShortcuts.c
blob76e3eb27d6b64e1f6f806999eb72fffbfbcae6ea
1 /* KeyboardShortcuts.c- keyboard shortcut bindings
2 *
3 * WPrefs - Window Maker Preferences Program
4 *
5 * Copyright (c) 1998-2003 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
24 #include "config.h" /* for HAVE_XCONVERTCASE */
27 #include "WPrefs.h"
28 #include <ctype.h>
30 #include <X11/keysym.h>
33 typedef struct _Panel {
34 WMBox *box;
36 char *sectionName;
38 char *description;
40 CallbackRec callbacks;
42 WMWidget *parent;
44 WMLabel *actL;
45 WMList *actLs;
47 WMFrame *shoF;
48 WMTextField *shoT;
49 WMButton *cleB;
50 WMButton *defB;
52 WMLabel *instructionsL;
54 WMColor *white;
55 WMColor *black;
56 WMColor *gray;
57 WMFont *font;
59 /**/
60 char capturing;
61 char **shortcuts;
62 int actionCount;
63 } _Panel;
67 #define ICON_FILE "keyshortcuts"
70 /* must be in the same order as the corresponding items in actions list */
71 static char *keyOptions[] = {
72 "RootMenuKey",
73 "WindowListKey",
74 "WindowMenuKey",
75 "HideKey",
76 "HideOthersKey",
77 "MiniaturizeKey",
78 "CloseKey",
79 "MaximizeKey",
80 "VMaximizeKey",
81 "HMaximizeKey",
82 "RaiseKey",
83 "LowerKey",
84 "RaiseLowerKey",
85 "ShadeKey",
86 "MoveResizeKey",
87 "SelectKey",
88 "FocusNextKey",
89 "FocusPrevKey",
90 "NextWorkspaceKey",
91 "PrevWorkspaceKey",
92 "NextWorkspaceLayerKey",
93 "PrevWorkspaceLayerKey",
94 "Workspace1Key",
95 "Workspace2Key",
96 "Workspace3Key",
97 "Workspace4Key",
98 "Workspace5Key",
99 "Workspace6Key",
100 "Workspace7Key",
101 "Workspace8Key",
102 "Workspace9Key",
103 "Workspace10Key",
104 "WindowShortcut1Key",
105 "WindowShortcut2Key",
106 "WindowShortcut3Key",
107 "WindowShortcut4Key",
108 "WindowShortcut5Key",
109 "WindowShortcut6Key",
110 "WindowShortcut7Key",
111 "WindowShortcut8Key",
112 "WindowShortcut9Key",
113 "WindowShortcut10Key",
114 "ScreenSwitchKey",
115 "ClipRaiseKey",
116 "ClipLowerKey",
117 #ifndef XKB_MODELOCK
118 "ClipRaiseLowerKey"
119 #else
120 "ClipRaiseLowerKey",
121 "ToggleKbdModeKey"
122 #endif /* XKB_MODELOCK */
127 #ifndef HAVE_XCONVERTCASE
128 /* from Xlib */
130 static void
131 XConvertCase(sym, lower, upper)
132 register KeySym sym;
133 KeySym *lower;
134 KeySym *upper;
136 *lower = sym;
137 *upper = sym;
138 switch(sym >> 8) {
139 case 0: /* Latin 1 */
140 if ((sym >= XK_A) && (sym <= XK_Z))
141 *lower += (XK_a - XK_A);
142 else if ((sym >= XK_a) && (sym <= XK_z))
143 *upper -= (XK_a - XK_A);
144 else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
145 *lower += (XK_agrave - XK_Agrave);
146 else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
147 *upper -= (XK_agrave - XK_Agrave);
148 else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
149 *lower += (XK_oslash - XK_Ooblique);
150 else if ((sym >= XK_oslash) && (sym <= XK_thorn))
151 *upper -= (XK_oslash - XK_Ooblique);
152 break;
153 case 1: /* Latin 2 */
154 /* Assume the KeySym is a legal value (ignore discontinuities) */
155 if (sym == XK_Aogonek)
156 *lower = XK_aogonek;
157 else if (sym >= XK_Lstroke && sym <= XK_Sacute)
158 *lower += (XK_lstroke - XK_Lstroke);
159 else if (sym >= XK_Scaron && sym <= XK_Zacute)
160 *lower += (XK_scaron - XK_Scaron);
161 else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
162 *lower += (XK_zcaron - XK_Zcaron);
163 else if (sym == XK_aogonek)
164 *upper = XK_Aogonek;
165 else if (sym >= XK_lstroke && sym <= XK_sacute)
166 *upper -= (XK_lstroke - XK_Lstroke);
167 else if (sym >= XK_scaron && sym <= XK_zacute)
168 *upper -= (XK_scaron - XK_Scaron);
169 else if (sym >= XK_zcaron && sym <= XK_zabovedot)
170 *upper -= (XK_zcaron - XK_Zcaron);
171 else if (sym >= XK_Racute && sym <= XK_Tcedilla)
172 *lower += (XK_racute - XK_Racute);
173 else if (sym >= XK_racute && sym <= XK_tcedilla)
174 *upper -= (XK_racute - XK_Racute);
175 break;
176 case 2: /* Latin 3 */
177 /* Assume the KeySym is a legal value (ignore discontinuities) */
178 if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
179 *lower += (XK_hstroke - XK_Hstroke);
180 else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
181 *lower += (XK_gbreve - XK_Gbreve);
182 else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
183 *upper -= (XK_hstroke - XK_Hstroke);
184 else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
185 *upper -= (XK_gbreve - XK_Gbreve);
186 else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
187 *lower += (XK_cabovedot - XK_Cabovedot);
188 else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
189 *upper -= (XK_cabovedot - XK_Cabovedot);
190 break;
191 case 3: /* Latin 4 */
192 /* Assume the KeySym is a legal value (ignore discontinuities) */
193 if (sym >= XK_Rcedilla && sym <= XK_Tslash)
194 *lower += (XK_rcedilla - XK_Rcedilla);
195 else if (sym >= XK_rcedilla && sym <= XK_tslash)
196 *upper -= (XK_rcedilla - XK_Rcedilla);
197 else if (sym == XK_ENG)
198 *lower = XK_eng;
199 else if (sym == XK_eng)
200 *upper = XK_ENG;
201 else if (sym >= XK_Amacron && sym <= XK_Umacron)
202 *lower += (XK_amacron - XK_Amacron);
203 else if (sym >= XK_amacron && sym <= XK_umacron)
204 *upper -= (XK_amacron - XK_Amacron);
205 break;
206 case 6: /* Cyrillic */
207 /* Assume the KeySym is a legal value (ignore discontinuities) */
208 if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
209 *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
210 else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
211 *upper += (XK_Serbian_DJE - XK_Serbian_dje);
212 else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
213 *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
214 else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
215 *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
216 break;
217 case 7: /* Greek */
218 /* Assume the KeySym is a legal value (ignore discontinuities) */
219 if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
220 *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
221 else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
222 sym != XK_Greek_iotaaccentdieresis &&
223 sym != XK_Greek_upsilonaccentdieresis)
224 *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
225 else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
226 *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
227 else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega &&
228 sym != XK_Greek_finalsmallsigma)
229 *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
230 break;
231 case 0x14: /* Armenian */
232 if (sym >= XK_Armenian_AYB && sym <= XK_Armenian_fe) {
233 *lower = sym | 1;
234 *upper = sym & ~1;
236 break;
239 #endif
242 static char*
243 captureShortcut(Display *dpy, _Panel *panel)
245 XEvent ev;
246 KeySym ksym, lksym, uksym;
247 char buffer[64];
248 char *key = NULL;
250 while (panel->capturing) {
251 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
252 WMNextEvent(dpy, &ev);
253 if (ev.type==KeyPress && ev.xkey.keycode!=0) {
254 ksym = XKeycodeToKeysym(dpy, ev.xkey.keycode, 0);
255 if (!IsModifierKey(ksym)) {
256 XConvertCase(ksym, &lksym, &uksym);
257 key=XKeysymToString(uksym);
259 panel->capturing = 0;
260 break;
263 WMHandleEvent(&ev);
266 if (!key)
267 return NULL;
269 buffer[0] = 0;
271 if (ev.xkey.state & ControlMask) {
272 strcat(buffer, "Control+");
274 if (ev.xkey.state & ShiftMask) {
275 strcat(buffer, "Shift+");
277 if (ev.xkey.state & Mod1Mask) {
278 strcat(buffer, "Mod1+");
280 if (ev.xkey.state & Mod2Mask) {
281 strcat(buffer, "Mod2+");
283 if (ev.xkey.state & Mod3Mask) {
284 strcat(buffer, "Mod3+");
286 if (ev.xkey.state & Mod4Mask) {
287 strcat(buffer, "Mod4+");
289 if (ev.xkey.state & Mod5Mask) {
290 strcat(buffer, "Mod5+");
292 strcat(buffer, key);
294 return wstrdup(buffer);
298 static void
299 captureClick(WMWidget *w, void *data)
301 _Panel *panel = (_Panel*)data;
302 Display *dpy = WMScreenDisplay(WMWidgetScreen(panel->parent));
303 char *shortcut;
305 if (!panel->capturing) {
306 panel->capturing = 1;
307 WMSetButtonText(w, _("Cancel"));
308 WMSetLabelText(panel->instructionsL, _("Press the desired shortcut key(s) or click Cancel to stop capturing."));
309 XGrabKeyboard(dpy, WMWidgetXID(panel->parent), True, GrabModeAsync,
310 GrabModeAsync, CurrentTime);
311 shortcut = captureShortcut(dpy, panel);
312 if (shortcut) {
313 int row = WMGetListSelectedItemRow(panel->actLs);
315 WMSetTextFieldText(panel->shoT, shortcut);
316 if (row>=0) {
317 if (panel->shortcuts[row])
318 wfree(panel->shortcuts[row]);
319 panel->shortcuts[row] = shortcut;
321 WMRedisplayWidget(panel->actLs);
322 } else {
323 wfree(shortcut);
327 panel->capturing = 0;
328 WMSetButtonText(w, _("Capture"));
329 WMSetLabelText(panel->instructionsL, _("Click Capture to interactively define the shortcut key."));
330 XUngrabKeyboard(dpy, CurrentTime);
335 static void
336 clearShortcut(WMWidget *w, void *data)
338 _Panel *panel = (_Panel*)data;
339 int row = WMGetListSelectedItemRow(panel->actLs);
341 WMSetTextFieldText(panel->shoT, NULL);
343 if (row>=0) {
344 if (panel->shortcuts[row])
345 wfree(panel->shortcuts[row]);
346 panel->shortcuts[row]=NULL;
347 WMRedisplayWidget(panel->actLs);
353 static void
354 typedKeys(void *observerData, WMNotification *notification)
356 _Panel *panel = (_Panel*)observerData;
357 int row = WMGetListSelectedItemRow(panel->actLs);
359 if (row<0)
360 return;
362 if (panel->shortcuts[row])
363 wfree(panel->shortcuts[row]);
364 panel->shortcuts[row] = WMGetTextFieldText(panel->shoT);
365 if (strlen(panel->shortcuts[row])==0) {
366 wfree(panel->shortcuts[row]);
367 panel->shortcuts[row] = NULL;
369 WMRedisplayWidget(panel->actLs);
374 static void
375 listClick(WMWidget *w, void *data)
377 _Panel *panel = (_Panel*)data;
378 int row = WMGetListSelectedItemRow(w);
380 WMSetTextFieldText(panel->shoT, panel->shortcuts[row]);
384 static char*
385 trimstr(char *str)
387 char *p = str;
388 int i;
390 while (isspace(*p)) p++;
391 p = wstrdup(p);
392 i = strlen(p);
393 while (isspace(p[i]) && i>0) {
394 p[i]=0;
395 i--;
398 return p;
402 static void
403 showData(_Panel *panel)
405 char *str;
406 int i;
408 for (i=0; i<panel->actionCount; i++) {
410 str = GetStringForKey(keyOptions[i]);
411 if (panel->shortcuts[i])
412 wfree(panel->shortcuts[i]);
413 if (str)
414 panel->shortcuts[i] = trimstr(str);
415 else
416 panel->shortcuts[i] = NULL;
418 if (panel->shortcuts[i] &&
419 (strcasecmp(panel->shortcuts[i], "none")==0
420 || strlen(panel->shortcuts[i])==0)) {
421 wfree(panel->shortcuts[i]);
422 panel->shortcuts[i] = NULL;
425 WMRedisplayWidget(panel->actLs);
429 static void
430 paintItem(WMList *lPtr, int index, Drawable d, char *text, int state,
431 WMRect *rect)
433 int width, height, x, y;
434 _Panel *panel = (_Panel*)WMGetHangedData(lPtr);
435 WMScreen *scr = WMWidgetScreen(lPtr);
436 Display *dpy = WMScreenDisplay(scr);
437 WMColor *backColor = (state & WLDSSelected) ? panel->white : panel->gray;
439 width = rect->size.width;
440 height = rect->size.height;
441 x = rect->pos.x;
442 y = rect->pos.y;
444 XFillRectangle(dpy, d, WMColorGC(backColor), x, y, width, height);
446 if (panel->shortcuts[index]) {
447 WMPixmap *pix = WMGetSystemPixmap(scr, WSICheckMark);
448 WMSize size = WMGetPixmapSize(pix);
450 WMDrawPixmap(pix, d, x+(20-size.width)/2, (height-size.height)/2+y);
451 WMReleasePixmap(pix);
454 WMDrawString(scr, d, panel->black, panel->font, x+20, y, text, strlen(text));
458 static void
459 createPanel(Panel *p)
461 _Panel *panel = (_Panel*)p;
462 WMScreen *scr = WMWidgetScreen(panel->parent);
463 WMColor *color;
464 WMFont *boldFont;
466 panel->capturing = 0;
468 panel->white = WMWhiteColor(scr);
469 panel->black = WMBlackColor(scr);
470 panel->gray = WMGrayColor(scr);
471 panel->font = WMSystemFontOfSize(scr, 12);
473 panel->box = WMCreateBox(panel->parent);
474 WMSetViewExpandsToParent(WMWidgetView(panel->box), 2, 2, 2, 2);
476 boldFont = WMBoldSystemFontOfSize(scr, 12);
478 /* **************** Actions **************** */
479 panel->actL = WMCreateLabel(panel->box);
480 WMResizeWidget(panel->actL, 280, 20);
481 WMMoveWidget(panel->actL, 20, 10);
482 WMSetLabelFont(panel->actL, boldFont);
483 WMSetLabelText(panel->actL, _("Actions"));
484 WMSetLabelRelief(panel->actL, WRSunken);
485 WMSetLabelTextAlignment(panel->actL, WACenter);
486 color = WMDarkGrayColor(scr);
487 WMSetWidgetBackgroundColor(panel->actL, color);
488 WMReleaseColor(color);
489 WMSetLabelTextColor(panel->actL, panel->white);
491 panel->actLs = WMCreateList(panel->box);
492 WMResizeWidget(panel->actLs, 280, 190);
493 WMMoveWidget(panel->actLs, 20, 32);
494 WMSetListUserDrawProc(panel->actLs, paintItem);
495 WMHangData(panel->actLs, panel);
497 WMAddListItem(panel->actLs, _("Open applications menu"));
498 WMAddListItem(panel->actLs, _("Open window list menu"));
499 WMAddListItem(panel->actLs, _("Open window commands menu"));
500 WMAddListItem(panel->actLs, _("Hide active application"));
501 WMAddListItem(panel->actLs, _("Hide other applications"));
502 WMAddListItem(panel->actLs, _("Miniaturize active window"));
503 WMAddListItem(panel->actLs, _("Close active window"));
504 WMAddListItem(panel->actLs, _("Maximize active window"));
505 WMAddListItem(panel->actLs, _("Maximize active window vertically"));
506 WMAddListItem(panel->actLs, _("Maximize active window horizontally"));
507 WMAddListItem(panel->actLs, _("Raise active window"));
508 WMAddListItem(panel->actLs, _("Lower active window"));
509 WMAddListItem(panel->actLs, _("Raise/Lower window under mouse pointer"));
510 WMAddListItem(panel->actLs, _("Shade active window"));
511 WMAddListItem(panel->actLs, _("Move/Resize active window"));
512 WMAddListItem(panel->actLs, _("Select active window"));
513 WMAddListItem(panel->actLs, _("Focus next window"));
514 WMAddListItem(panel->actLs, _("Focus previous window"));
515 WMAddListItem(panel->actLs, _("Switch to next workspace"));
516 WMAddListItem(panel->actLs, _("Switch to previous workspace"));
517 WMAddListItem(panel->actLs, _("Switch to next ten workspaces"));
518 WMAddListItem(panel->actLs, _("Switch to previous ten workspaces"));
519 WMAddListItem(panel->actLs, _("Switch to workspace 1"));
520 WMAddListItem(panel->actLs, _("Switch to workspace 2"));
521 WMAddListItem(panel->actLs, _("Switch to workspace 3"));
522 WMAddListItem(panel->actLs, _("Switch to workspace 4"));
523 WMAddListItem(panel->actLs, _("Switch to workspace 5"));
524 WMAddListItem(panel->actLs, _("Switch to workspace 6"));
525 WMAddListItem(panel->actLs, _("Switch to workspace 7"));
526 WMAddListItem(panel->actLs, _("Switch to workspace 8"));
527 WMAddListItem(panel->actLs, _("Switch to workspace 9"));
528 WMAddListItem(panel->actLs, _("Switch to workspace 10"));
529 WMAddListItem(panel->actLs, _("Shortcut for window 1"));
530 WMAddListItem(panel->actLs, _("Shortcut for window 2"));
531 WMAddListItem(panel->actLs, _("Shortcut for window 3"));
532 WMAddListItem(panel->actLs, _("Shortcut for window 4"));
533 WMAddListItem(panel->actLs, _("Shortcut for window 5"));
534 WMAddListItem(panel->actLs, _("Shortcut for window 6"));
535 WMAddListItem(panel->actLs, _("Shortcut for window 7"));
536 WMAddListItem(panel->actLs, _("Shortcut for window 8"));
537 WMAddListItem(panel->actLs, _("Shortcut for window 9"));
538 WMAddListItem(panel->actLs, _("Shortcut for window 10"));
539 WMAddListItem(panel->actLs, _("Switch to Next Screen/Monitor"));
540 WMAddListItem(panel->actLs, _("Raise Clip"));
541 WMAddListItem(panel->actLs, _("Lower Clip"));
542 WMAddListItem(panel->actLs, _("Raise/Lower Clip"));
543 #ifdef XKB_MODELOCK
544 WMAddListItem(panel->actLs, _("Toggle keyboard language"));
545 #endif /* XKB_MODELOCK */
547 WMSetListAction(panel->actLs, listClick, panel);
549 panel->actionCount = WMGetListNumberOfRows(panel->actLs);
550 panel->shortcuts = wmalloc(sizeof(char*)*panel->actionCount);
551 memset(panel->shortcuts, 0, sizeof(char*)*panel->actionCount);
553 /***************** Shortcut ****************/
555 panel->shoF = WMCreateFrame(panel->box);
556 WMResizeWidget(panel->shoF, 190, 210);
557 WMMoveWidget(panel->shoF, 315, 10);
558 WMSetFrameTitle(panel->shoF, _("Shortcut"));
560 panel->shoT = WMCreateTextField(panel->shoF);
561 WMResizeWidget(panel->shoT, 160, 20);
562 WMMoveWidget(panel->shoT, 15, 65);
563 WMAddNotificationObserver(typedKeys, panel,
564 WMTextDidChangeNotification, panel->shoT);
566 panel->cleB = WMCreateCommandButton(panel->shoF);
567 WMResizeWidget(panel->cleB, 75, 24);
568 WMMoveWidget(panel->cleB, 15, 95);
569 WMSetButtonText(panel->cleB, _("Clear"));
570 WMSetButtonAction(panel->cleB, clearShortcut, panel);
572 panel->defB = WMCreateCommandButton(panel->shoF);
573 WMResizeWidget(panel->defB, 75, 24);
574 WMMoveWidget(panel->defB, 100, 95);
575 WMSetButtonText(panel->defB, _("Capture"));
576 WMSetButtonAction(panel->defB, captureClick, panel);
578 panel->instructionsL = WMCreateLabel(panel->shoF);
579 WMResizeWidget(panel->instructionsL, 160, 55);
580 WMMoveWidget(panel->instructionsL, 15, 140);
581 WMSetLabelTextAlignment(panel->instructionsL, WACenter);
582 WMSetLabelWraps(panel->instructionsL, True);
583 WMSetLabelText(panel->instructionsL, _("Click Capture to interactively define the shortcut key."));
585 WMMapSubwidgets(panel->shoF);
587 WMReleaseFont(boldFont);
589 WMRealizeWidget(panel->box);
590 WMMapSubwidgets(panel->box);
593 showData(panel);
597 static void
598 storeData(_Panel *panel)
600 int i;
601 char *str;
603 for (i=0; i<panel->actionCount; i++) {
604 str = NULL;
605 if (panel->shortcuts[i]) {
606 str = trimstr(panel->shortcuts[i]);
607 if (strlen(str)==0) {
608 wfree(str);
609 str = NULL;
612 if (str) {
613 SetStringForKey(str, keyOptions[i]);
614 wfree(str);
615 } else {
616 SetStringForKey("None", keyOptions[i]);
623 Panel*
624 InitKeyboardShortcuts(WMScreen *scr, WMWidget *parent)
626 _Panel *panel;
628 panel = wmalloc(sizeof(_Panel));
629 memset(panel, 0, sizeof(_Panel));
631 panel->sectionName = _("Keyboard Shortcut Preferences");
633 panel->description = _("Change the keyboard shortcuts for actions such\n"
634 "as changing workspaces and opening menus.");
636 panel->parent = parent;
638 panel->callbacks.createWidgets = createPanel;
639 panel->callbacks.updateDomain = storeData;
641 AddSection(panel, ICON_FILE);
643 return panel;