WPrefs: Added const attribute to statically stored strings
[wmaker-crm.git] / WPrefs.app / KeyboardShortcuts.c
blobef4865683898c7f2b3077ac02354cbcfefa5f48e
1 /* KeyboardShortcuts.c- keyboard shortcut bindings
3 * WPrefs - Window Maker Preferences Program
5 * Copyright (c) 1998-2003 Alfredo K. Kojima
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 along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "config.h" /* for HAVE_XCONVERTCASE */
24 #include "WPrefs.h"
25 #include <ctype.h>
27 #include <X11/keysym.h>
28 #include <X11/XKBlib.h>
30 typedef struct _Panel {
31 WMBox *box;
33 char *sectionName;
35 char *description;
37 CallbackRec callbacks;
39 WMWidget *parent;
41 WMLabel *actL;
42 WMList *actLs;
44 WMFrame *shoF;
45 WMTextField *shoT;
46 WMButton *cleB;
47 WMButton *defB;
49 WMLabel *instructionsL;
51 WMColor *white;
52 WMColor *black;
53 WMColor *gray;
54 WMFont *font;
56 Bool capturing;
57 char **shortcuts;
58 int actionCount;
59 } _Panel;
61 #define ICON_FILE "keyshortcuts"
64 * List of user definable shortcut keys
65 * First parameter is the internal keyword known by WMaker
66 * Second is the text displayed to the user
68 static const struct {
69 const char *key;
71 * Fixme: this string should be 'const', but 'WMAddListItem'
72 * do not allow us to do so
74 char *title;
75 } keyOptions[] = {
76 { "RootMenuKey", N_("Open applications menu") },
77 { "WindowListKey", N_("Open window list menu") },
78 { "WindowMenuKey", N_("Open window commands menu") },
79 { "HideKey", N_("Hide active application") },
80 { "HideOthersKey", N_("Hide other applications") },
81 { "MiniaturizeKey", N_("Miniaturize active window") },
82 { "MinimizeAllKey", N_("Miniaturize all windows") },
83 { "CloseKey", N_("Close active window") },
84 { "MaximizeKey", N_("Maximize active window") },
85 { "VMaximizeKey", N_("Maximize active window vertically") },
86 { "HMaximizeKey", N_("Maximize active window horizontally") },
87 { "LHMaximizeKey", N_("Maximize active window left half") },
88 { "RHMaximizeKey", N_("Maximize active window right half") },
89 { "MaximusKey", N_("Maximus: Tiled maximization ") },
90 { "RaiseKey", N_("Raise active window") },
91 { "LowerKey", N_("Lower active window") },
92 { "RaiseLowerKey", N_("Raise/Lower window under mouse pointer") },
93 { "ShadeKey", N_("Shade active window") },
94 { "MoveResizeKey", N_("Move/Resize active window") },
95 { "SelectKey", N_("Select active window") },
96 { "FocusNextKey", N_("Focus next window") },
97 { "FocusPrevKey", N_("Focus previous window") },
98 { "GroupNextKey", N_("Focus next group window") },
99 { "GroupPrevKey", N_("Focus previous group window") },
101 /* Workspace Related */
102 { "NextWorkspaceKey", N_("Switch to next workspace") },
103 { "PrevWorkspaceKey", N_("Switch to previous workspace") },
104 { "LastWorkspaceKey", N_("Switch to last used workspace") },
105 { "NextWorkspaceLayerKey", N_("Switch to next ten workspaces") },
106 { "PrevWorkspaceLayerKey", N_("Switch to previous ten workspaces") },
107 { "Workspace1Key", N_("Switch to workspace 1") },
108 { "Workspace2Key", N_("Switch to workspace 2") },
109 { "Workspace3Key", N_("Switch to workspace 3") },
110 { "Workspace4Key", N_("Switch to workspace 4") },
111 { "Workspace5Key", N_("Switch to workspace 5") },
112 { "Workspace6Key", N_("Switch to workspace 6") },
113 { "Workspace7Key", N_("Switch to workspace 7") },
114 { "Workspace8Key", N_("Switch to workspace 8") },
115 { "Workspace9Key", N_("Switch to workspace 9") },
116 { "Workspace10Key", N_("Switch to workspace 10") },
117 { "MoveToNextWorkspaceKey", N_("Move window to next workspace") },
118 { "MoveToPrevWorkspaceKey", N_("Move window to previous workspace") },
119 { "MoveToLastWorkspaceKey", N_("Move window to last used workspace") },
120 { "MoveToNextWorkspaceLayerKey", N_("Move window to next ten workspaces") },
121 { "MoveToPrevWorkspaceLayerKey", N_("Move window to previous ten workspaces") },
122 { "MoveToWorkspace1Key", N_("Move window to workspace 1") },
123 { "MoveToWorkspace2Key", N_("Move window to workspace 2") },
124 { "MoveToWorkspace3Key", N_("Move window to workspace 3") },
125 { "MoveToWorkspace4Key", N_("Move window to workspace 4") },
126 { "MoveToWorkspace5Key", N_("Move window to workspace 5") },
127 { "MoveToWorkspace6Key", N_("Move window to workspace 6") },
128 { "MoveToWorkspace7Key", N_("Move window to workspace 7") },
129 { "MoveToWorkspace8Key", N_("Move window to workspace 8") },
130 { "MoveToWorkspace9Key", N_("Move window to workspace 9") },
131 { "MoveToWorkspace10Key", N_("Move window to workspace 10") },
133 /* Window Selection */
134 { "WindowShortcut1Key", N_("Shortcut for window 1") },
135 { "WindowShortcut2Key", N_("Shortcut for window 2") },
136 { "WindowShortcut3Key", N_("Shortcut for window 3") },
137 { "WindowShortcut4Key", N_("Shortcut for window 4") },
138 { "WindowShortcut5Key", N_("Shortcut for window 5") },
139 { "WindowShortcut6Key", N_("Shortcut for window 6") },
140 { "WindowShortcut7Key", N_("Shortcut for window 7") },
141 { "WindowShortcut8Key", N_("Shortcut for window 8") },
142 { "WindowShortcut9Key", N_("Shortcut for window 9") },
143 { "WindowShortcut10Key", N_("Shortcut for window 10") },
145 /* Misc. */
146 { "WindowRelaunchKey", N_("Launch new instance of application") },
147 { "ScreenSwitchKey", N_("Switch to Next Screen/Monitor") },
148 { "DockRaiseLowerKey", N_("Raise/Lower Dock") },
149 { "ClipRaiseLowerKey", N_("Raise/Lower Clip") }
150 #ifdef XKB_MODELOCK
151 ,{ "ToggleKbdModeKey", N_("Toggle keyboard language") }
152 #endif /* XKB_MODELOCK */
155 #ifndef HAVE_XCONVERTCASE
156 /* from Xlib */
158 static void XConvertCase(register KeySym sym, KeySym * lower, KeySym * upper)
160 *lower = sym;
161 *upper = sym;
162 switch (sym >> 8) {
163 case 0: /* Latin 1 */
164 if ((sym >= XK_A) && (sym <= XK_Z))
165 *lower += (XK_a - XK_A);
166 else if ((sym >= XK_a) && (sym <= XK_z))
167 *upper -= (XK_a - XK_A);
168 else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
169 *lower += (XK_agrave - XK_Agrave);
170 else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
171 *upper -= (XK_agrave - XK_Agrave);
172 else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
173 *lower += (XK_oslash - XK_Ooblique);
174 else if ((sym >= XK_oslash) && (sym <= XK_thorn))
175 *upper -= (XK_oslash - XK_Ooblique);
176 break;
177 case 1: /* Latin 2 */
178 /* Assume the KeySym is a legal value (ignore discontinuities) */
179 if (sym == XK_Aogonek)
180 *lower = XK_aogonek;
181 else if (sym >= XK_Lstroke && sym <= XK_Sacute)
182 *lower += (XK_lstroke - XK_Lstroke);
183 else if (sym >= XK_Scaron && sym <= XK_Zacute)
184 *lower += (XK_scaron - XK_Scaron);
185 else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
186 *lower += (XK_zcaron - XK_Zcaron);
187 else if (sym == XK_aogonek)
188 *upper = XK_Aogonek;
189 else if (sym >= XK_lstroke && sym <= XK_sacute)
190 *upper -= (XK_lstroke - XK_Lstroke);
191 else if (sym >= XK_scaron && sym <= XK_zacute)
192 *upper -= (XK_scaron - XK_Scaron);
193 else if (sym >= XK_zcaron && sym <= XK_zabovedot)
194 *upper -= (XK_zcaron - XK_Zcaron);
195 else if (sym >= XK_Racute && sym <= XK_Tcedilla)
196 *lower += (XK_racute - XK_Racute);
197 else if (sym >= XK_racute && sym <= XK_tcedilla)
198 *upper -= (XK_racute - XK_Racute);
199 break;
200 case 2: /* Latin 3 */
201 /* Assume the KeySym is a legal value (ignore discontinuities) */
202 if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
203 *lower += (XK_hstroke - XK_Hstroke);
204 else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
205 *lower += (XK_gbreve - XK_Gbreve);
206 else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
207 *upper -= (XK_hstroke - XK_Hstroke);
208 else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
209 *upper -= (XK_gbreve - XK_Gbreve);
210 else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
211 *lower += (XK_cabovedot - XK_Cabovedot);
212 else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
213 *upper -= (XK_cabovedot - XK_Cabovedot);
214 break;
215 case 3: /* Latin 4 */
216 /* Assume the KeySym is a legal value (ignore discontinuities) */
217 if (sym >= XK_Rcedilla && sym <= XK_Tslash)
218 *lower += (XK_rcedilla - XK_Rcedilla);
219 else if (sym >= XK_rcedilla && sym <= XK_tslash)
220 *upper -= (XK_rcedilla - XK_Rcedilla);
221 else if (sym == XK_ENG)
222 *lower = XK_eng;
223 else if (sym == XK_eng)
224 *upper = XK_ENG;
225 else if (sym >= XK_Amacron && sym <= XK_Umacron)
226 *lower += (XK_amacron - XK_Amacron);
227 else if (sym >= XK_amacron && sym <= XK_umacron)
228 *upper -= (XK_amacron - XK_Amacron);
229 break;
230 case 6: /* Cyrillic */
231 /* Assume the KeySym is a legal value (ignore discontinuities) */
232 if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
233 *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
234 else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
235 *upper += (XK_Serbian_DJE - XK_Serbian_dje);
236 else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
237 *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
238 else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
239 *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
240 break;
241 case 7: /* Greek */
242 /* Assume the KeySym is a legal value (ignore discontinuities) */
243 if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
244 *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
245 else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
246 sym != XK_Greek_iotaaccentdieresis && sym != XK_Greek_upsilonaccentdieresis)
247 *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
248 else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
249 *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
250 else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega && sym != XK_Greek_finalsmallsigma)
251 *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
252 break;
253 case 0x14: /* Armenian */
254 if (sym >= XK_Armenian_AYB && sym <= XK_Armenian_fe) {
255 *lower = sym | 1;
256 *upper = sym & ~1;
258 break;
261 #endif
263 char *capture_shortcut(Display *dpy, Bool *capturing, Bool convert_case)
265 XEvent ev;
266 KeySym ksym, lksym, uksym;
267 char buffer[64];
268 char *key = NULL;
270 while (*capturing) {
271 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
272 WMNextEvent(dpy, &ev);
273 if (ev.type == KeyPress && ev.xkey.keycode != 0) {
274 ksym = XkbKeycodeToKeysym(dpy, ev.xkey.keycode, 0, 0);
275 if (!IsModifierKey(ksym)) {
276 if (convert_case) {
277 XConvertCase(ksym, &lksym, &uksym);
278 key = XKeysymToString(uksym);
279 } else {
280 key = XKeysymToString(ksym);
283 *capturing = 0;
284 break;
287 WMHandleEvent(&ev);
290 if (!key)
291 return NULL;
293 buffer[0] = 0;
295 if (ev.xkey.state & ControlMask) {
296 strcat(buffer, "Control+");
298 if (ev.xkey.state & ShiftMask) {
299 strcat(buffer, "Shift+");
301 if (ev.xkey.state & Mod1Mask) {
302 strcat(buffer, "Mod1+");
304 if (ev.xkey.state & Mod2Mask) {
305 strcat(buffer, "Mod2+");
307 if (ev.xkey.state & Mod3Mask) {
308 strcat(buffer, "Mod3+");
310 if (ev.xkey.state & Mod4Mask) {
311 strcat(buffer, "Mod4+");
313 if (ev.xkey.state & Mod5Mask) {
314 strcat(buffer, "Mod5+");
316 strcat(buffer, key);
318 return wstrdup(buffer);
321 static void captureClick(WMWidget * w, void *data)
323 _Panel *panel = (_Panel *) data;
324 Display *dpy = WMScreenDisplay(WMWidgetScreen(panel->parent));
325 char *shortcut;
327 if (!panel->capturing) {
328 panel->capturing = 1;
329 WMSetButtonText(w, _("Cancel"));
330 WMSetLabelText(panel->instructionsL,
331 _("Press the desired shortcut key(s) or click Cancel to stop capturing."));
332 XGrabKeyboard(dpy, WMWidgetXID(panel->parent), True, GrabModeAsync, GrabModeAsync, CurrentTime);
333 shortcut = capture_shortcut(dpy, &panel->capturing, 1);
334 if (shortcut) {
335 int row = WMGetListSelectedItemRow(panel->actLs);
337 WMSetTextFieldText(panel->shoT, shortcut);
338 if (row >= 0) {
339 if (panel->shortcuts[row])
340 wfree(panel->shortcuts[row]);
341 panel->shortcuts[row] = shortcut;
343 WMRedisplayWidget(panel->actLs);
344 } else {
345 wfree(shortcut);
349 panel->capturing = 0;
350 WMSetButtonText(w, _("Capture"));
351 WMSetLabelText(panel->instructionsL, _("Click on Capture to interactively define the shortcut key."));
352 XUngrabKeyboard(dpy, CurrentTime);
355 static void clearShortcut(WMWidget * w, void *data)
357 _Panel *panel = (_Panel *) data;
358 int row = WMGetListSelectedItemRow(panel->actLs);
360 WMSetTextFieldText(panel->shoT, NULL);
362 if (row >= 0) {
363 if (panel->shortcuts[row])
364 wfree(panel->shortcuts[row]);
365 panel->shortcuts[row] = NULL;
366 WMRedisplayWidget(panel->actLs);
370 static void typedKeys(void *observerData, WMNotification * notification)
372 _Panel *panel = (_Panel *) observerData;
373 int row = WMGetListSelectedItemRow(panel->actLs);
375 if (row < 0)
376 return;
378 if (panel->shortcuts[row])
379 wfree(panel->shortcuts[row]);
380 panel->shortcuts[row] = WMGetTextFieldText(panel->shoT);
381 if (strlen(panel->shortcuts[row]) == 0) {
382 wfree(panel->shortcuts[row]);
383 panel->shortcuts[row] = NULL;
385 WMRedisplayWidget(panel->actLs);
388 static void listClick(WMWidget * w, void *data)
390 _Panel *panel = (_Panel *) data;
391 int row = WMGetListSelectedItemRow(w);
393 WMSetTextFieldText(panel->shoT, panel->shortcuts[row]);
396 static void showData(_Panel * panel)
398 char *str;
399 int i;
401 for (i = 0; i < panel->actionCount; i++) {
403 str = GetStringForKey(keyOptions[i].key);
404 if (panel->shortcuts[i])
405 wfree(panel->shortcuts[i]);
406 if (str)
407 panel->shortcuts[i] = wtrimspace(str);
408 else
409 panel->shortcuts[i] = NULL;
411 if (panel->shortcuts[i] &&
412 (strcasecmp(panel->shortcuts[i], "none") == 0 || strlen(panel->shortcuts[i]) == 0)) {
413 wfree(panel->shortcuts[i]);
414 panel->shortcuts[i] = NULL;
417 WMRedisplayWidget(panel->actLs);
420 static void paintItem(WMList * lPtr, int index, Drawable d, char *text, int state, WMRect * rect)
422 int width, height, x, y;
423 _Panel *panel = (_Panel *) WMGetHangedData(lPtr);
424 WMScreen *scr = WMWidgetScreen(lPtr);
425 Display *dpy = WMScreenDisplay(scr);
426 WMColor *backColor = (state & WLDSSelected) ? panel->white : panel->gray;
428 width = rect->size.width;
429 height = rect->size.height;
430 x = rect->pos.x;
431 y = rect->pos.y;
433 XFillRectangle(dpy, d, WMColorGC(backColor), x, y, width, height);
435 if (panel->shortcuts[index]) {
436 WMPixmap *pix = WMGetSystemPixmap(scr, WSICheckMark);
437 WMSize size = WMGetPixmapSize(pix);
439 WMDrawPixmap(pix, d, x + (20 - size.width) / 2, (height - size.height) / 2 + y);
440 WMReleasePixmap(pix);
443 WMDrawString(scr, d, panel->black, panel->font, x + 20, y, text, strlen(text));
446 static void createPanel(Panel * p)
448 _Panel *panel = (_Panel *) p;
449 WMScreen *scr = WMWidgetScreen(panel->parent);
450 WMColor *color;
451 WMFont *boldFont;
452 int i;
454 panel->capturing = 0;
456 panel->white = WMWhiteColor(scr);
457 panel->black = WMBlackColor(scr);
458 panel->gray = WMGrayColor(scr);
459 panel->font = WMSystemFontOfSize(scr, 12);
461 panel->box = WMCreateBox(panel->parent);
462 WMSetViewExpandsToParent(WMWidgetView(panel->box), 2, 2, 2, 2);
464 boldFont = WMBoldSystemFontOfSize(scr, 12);
466 /* **************** Actions **************** */
467 panel->actL = WMCreateLabel(panel->box);
468 WMResizeWidget(panel->actL, 280, 20);
469 WMMoveWidget(panel->actL, 20, 10);
470 WMSetLabelFont(panel->actL, boldFont);
471 WMSetLabelText(panel->actL, _("Actions"));
472 WMSetLabelRelief(panel->actL, WRSunken);
473 WMSetLabelTextAlignment(panel->actL, WACenter);
474 color = WMDarkGrayColor(scr);
475 WMSetWidgetBackgroundColor(panel->actL, color);
476 WMReleaseColor(color);
477 WMSetLabelTextColor(panel->actL, panel->white);
479 panel->actLs = WMCreateList(panel->box);
480 WMResizeWidget(panel->actLs, 280, 190);
481 WMMoveWidget(panel->actLs, 20, 32);
482 WMSetListUserDrawProc(panel->actLs, paintItem);
483 WMHangData(panel->actLs, panel);
485 for (i = 0; i < sizeof(keyOptions)/sizeof(keyOptions[0]); i++) {
486 WMAddListItem(panel->actLs, _(keyOptions[i].title));
488 WMSetListAction(panel->actLs, listClick, panel);
490 panel->actionCount = WMGetListNumberOfRows(panel->actLs);
491 panel->shortcuts = wmalloc(sizeof(char *) * panel->actionCount);
493 /***************** Shortcut ****************/
495 panel->shoF = WMCreateFrame(panel->box);
496 WMResizeWidget(panel->shoF, 190, 210);
497 WMMoveWidget(panel->shoF, 315, 10);
498 WMSetFrameTitle(panel->shoF, _("Shortcut"));
500 panel->shoT = WMCreateTextField(panel->shoF);
501 WMResizeWidget(panel->shoT, 160, 20);
502 WMMoveWidget(panel->shoT, 15, 65);
503 WMAddNotificationObserver(typedKeys, panel, WMTextDidChangeNotification, panel->shoT);
505 panel->cleB = WMCreateCommandButton(panel->shoF);
506 WMResizeWidget(panel->cleB, 75, 24);
507 WMMoveWidget(panel->cleB, 15, 95);
508 WMSetButtonText(panel->cleB, _("Clear"));
509 WMSetButtonAction(panel->cleB, clearShortcut, panel);
511 panel->defB = WMCreateCommandButton(panel->shoF);
512 WMResizeWidget(panel->defB, 75, 24);
513 WMMoveWidget(panel->defB, 100, 95);
514 WMSetButtonText(panel->defB, _("Capture"));
515 WMSetButtonAction(panel->defB, captureClick, panel);
517 panel->instructionsL = WMCreateLabel(panel->shoF);
518 WMResizeWidget(panel->instructionsL, 160, 55);
519 WMMoveWidget(panel->instructionsL, 15, 140);
520 WMSetLabelTextAlignment(panel->instructionsL, WACenter);
521 WMSetLabelWraps(panel->instructionsL, True);
522 WMSetLabelText(panel->instructionsL, _("Click on Capture to interactively define the shortcut key."));
524 WMMapSubwidgets(panel->shoF);
526 WMReleaseFont(boldFont);
528 WMRealizeWidget(panel->box);
529 WMMapSubwidgets(panel->box);
531 showData(panel);
534 static void storeData(_Panel * panel)
536 int i;
537 char *str;
539 for (i = 0; i < panel->actionCount; i++) {
540 str = NULL;
541 if (panel->shortcuts[i]) {
542 str = wtrimspace(panel->shortcuts[i]);
543 if (strlen(str) == 0) {
544 wfree(str);
545 str = NULL;
548 if (str) {
549 SetStringForKey(str, keyOptions[i].key);
550 wfree(str);
551 } else {
552 SetStringForKey("None", keyOptions[i].key);
557 Panel *InitKeyboardShortcuts(WMScreen * scr, WMWidget * parent)
559 _Panel *panel;
561 panel = wmalloc(sizeof(_Panel));
563 panel->sectionName = _("Keyboard Shortcut Preferences");
565 panel->description = _("Change the keyboard shortcuts for actions such\n"
566 "as changing workspaces and opening menus.");
568 panel->parent = parent;
570 panel->callbacks.createWidgets = createPanel;
571 panel->callbacks.updateDomain = storeData;
573 AddSection(panel, ICON_FILE);
575 return panel;