94ccae5ce02a5f58c5675a49b238f987eb5d5509
[reactos.git] / reactos / dll / win32 / comctl32 / hotkey.c
1 /*
2 * Hotkey control
3 *
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 2002 Gyorgy 'Nog' Jeney
6 * Copyright 2004 Robert Shearman
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 *
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Sep. 21, 2004, by Robert Shearman.
24 *
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features or bugs please note them below.
28 *
29 */
30
31 //#include <stdarg.h>
32 //#include <string.h>
33 //#include "windef.h"
34 //#include "winbase.h"
35 //#include "wingdi.h"
36 //#include "winuser.h"
37 //#include "winnls.h"
38 //#include "commctrl.h"
39 #include "comctl32.h"
40 #include <wine/debug.h>
41
42 WINE_DEFAULT_DEBUG_CHANNEL(hotkey);
43
44 typedef struct tagHOTKEY_INFO
45 {
46 HWND hwndSelf;
47 HWND hwndNotify;
48 HFONT hFont;
49 BOOL bFocus;
50 INT nHeight;
51 WORD HotKey;
52 WORD InvComb;
53 WORD InvMod;
54 BYTE CurrMod;
55 INT CaretPos;
56 DWORD ScanCode;
57 WCHAR strNone[15]; /* hope it's long enough ... */
58 } HOTKEY_INFO;
59
60 static const WCHAR HOTKEY_plussep[] = { ' ', '+', ' ' };
61 static LRESULT HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw);
62
63 #define IsOnlySet(flags) (infoPtr->CurrMod == (flags))
64
65 static BOOL
66 HOTKEY_IsCombInv(const HOTKEY_INFO *infoPtr)
67 {
68 TRACE("(infoPtr=%p)\n", infoPtr);
69 if((infoPtr->InvComb & HKCOMB_NONE) && !infoPtr->CurrMod)
70 return TRUE;
71 if((infoPtr->InvComb & HKCOMB_S) && IsOnlySet(HOTKEYF_SHIFT))
72 return TRUE;
73 if((infoPtr->InvComb & HKCOMB_C) && IsOnlySet(HOTKEYF_CONTROL))
74 return TRUE;
75 if((infoPtr->InvComb & HKCOMB_A) && IsOnlySet(HOTKEYF_ALT))
76 return TRUE;
77 if((infoPtr->InvComb & HKCOMB_SC) &&
78 IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL))
79 return TRUE;
80 if((infoPtr->InvComb & HKCOMB_SA) && IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_ALT))
81 return TRUE;
82 if((infoPtr->InvComb & HKCOMB_CA) &&
83 IsOnlySet(HOTKEYF_CONTROL | HOTKEYF_ALT))
84 return TRUE;
85 if((infoPtr->InvComb & HKCOMB_SCA) &&
86 IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL | HOTKEYF_ALT))
87 return TRUE;
88
89 TRACE("() Modifiers are valid\n");
90 return FALSE;
91 }
92 #undef IsOnlySet
93
94 static void
95 HOTKEY_DrawHotKey(HOTKEY_INFO *infoPtr, HDC hdc, LPCWSTR KeyName, WORD NameLen)
96 {
97 SIZE TextSize;
98 INT nXStart, nYStart;
99 COLORREF clrOldText, clrOldBk;
100 HFONT hFontOld;
101
102 /* Make a gap from the frame */
103 nXStart = GetSystemMetrics(SM_CXBORDER);
104 nYStart = GetSystemMetrics(SM_CYBORDER);
105
106 hFontOld = SelectObject(hdc, infoPtr->hFont);
107 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
108 {
109 clrOldText = SetTextColor(hdc, comctl32_color.clrGrayText);
110 clrOldBk = SetBkColor(hdc, comctl32_color.clrBtnFace);
111 }
112 else
113 {
114 clrOldText = SetTextColor(hdc, comctl32_color.clrWindowText);
115 clrOldBk = SetBkColor(hdc, comctl32_color.clrWindow);
116 }
117
118 TextOutW(hdc, nXStart, nYStart, KeyName, NameLen);
119
120 /* Get the text width for the caret */
121 GetTextExtentPoint32W(hdc, KeyName, NameLen, &TextSize);
122 infoPtr->CaretPos = nXStart + TextSize.cx;
123
124 SetBkColor(hdc, clrOldBk);
125 SetTextColor(hdc, clrOldText);
126 SelectObject(hdc, hFontOld);
127
128 /* position the caret */
129 SetCaretPos(infoPtr->CaretPos, nYStart);
130 }
131
132 /* Draw the names of the keys in the control */
133 static void
134 HOTKEY_Refresh(HOTKEY_INFO *infoPtr, HDC hdc)
135 {
136 WCHAR KeyName[64];
137 WORD NameLen = 0;
138 BYTE Modifier;
139
140 TRACE("(infoPtr=%p hdc=%p)\n", infoPtr, hdc);
141
142 if(!infoPtr->CurrMod && !infoPtr->HotKey) {
143 HOTKEY_DrawHotKey (infoPtr, hdc, infoPtr->strNone, 4);
144 return;
145 }
146
147 if(infoPtr->HotKey)
148 Modifier = HIBYTE(infoPtr->HotKey);
149 else if(HOTKEY_IsCombInv(infoPtr))
150 Modifier = infoPtr->InvMod;
151 else
152 Modifier = infoPtr->CurrMod;
153
154 if(Modifier & HOTKEYF_CONTROL) {
155 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_CONTROL, 0)),
156 KeyName, 64);
157 NameLen = lstrlenW(KeyName);
158 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
159 NameLen += 3;
160 }
161 if(Modifier & HOTKEYF_SHIFT) {
162 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_SHIFT, 0)),
163 &KeyName[NameLen], 64 - NameLen);
164 NameLen = lstrlenW(KeyName);
165 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
166 NameLen += 3;
167 }
168 if(Modifier & HOTKEYF_ALT) {
169 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_MENU, 0)),
170 &KeyName[NameLen], 64 - NameLen);
171 NameLen = lstrlenW(KeyName);
172 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
173 NameLen += 3;
174 }
175
176 if(infoPtr->HotKey) {
177 GetKeyNameTextW(infoPtr->ScanCode, &KeyName[NameLen], 64 - NameLen);
178 NameLen = lstrlenW(KeyName);
179 }
180 else
181 KeyName[NameLen] = 0;
182
183 HOTKEY_DrawHotKey (infoPtr, hdc, KeyName, NameLen);
184 }
185
186 static void
187 HOTKEY_Paint(HOTKEY_INFO *infoPtr, HDC hdc)
188 {
189 if (hdc)
190 HOTKEY_Refresh(infoPtr, hdc);
191 else {
192 PAINTSTRUCT ps;
193 hdc = BeginPaint (infoPtr->hwndSelf, &ps);
194 HOTKEY_Refresh (infoPtr, hdc);
195 EndPaint (infoPtr->hwndSelf, &ps);
196 }
197 }
198
199 static LRESULT
200 HOTKEY_GetHotKey(const HOTKEY_INFO *infoPtr)
201 {
202 TRACE("(infoPtr=%p) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr,
203 HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
204 return (LRESULT)infoPtr->HotKey;
205 }
206
207 static void
208 HOTKEY_SetHotKey(HOTKEY_INFO *infoPtr, WORD hotKey)
209 {
210 infoPtr->HotKey = hotKey;
211 infoPtr->ScanCode =
212 MAKELPARAM(0, MapVirtualKeyW(LOBYTE(infoPtr->HotKey), 0));
213 TRACE("(infoPtr=%p hotKey=%x) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr,
214 hotKey, HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
215 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
216 }
217
218 static void
219 HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WORD invComb, WORD invMod)
220 {
221 infoPtr->InvComb = invComb;
222 infoPtr->InvMod = invMod;
223 TRACE("(infoPtr=%p) Invalid Modifers: 0x%x, If Invalid: 0x%x\n", infoPtr,
224 infoPtr->InvComb, infoPtr->InvMod);
225 }
226
227
228 static LRESULT
229 HOTKEY_Create (HOTKEY_INFO *infoPtr, const CREATESTRUCTW *lpcs)
230 {
231 infoPtr->hwndNotify = lpcs->hwndParent;
232
233 HOTKEY_SetFont(infoPtr, GetStockObject(SYSTEM_FONT), 0);
234
235 return 0;
236 }
237
238
239 static LRESULT
240 HOTKEY_Destroy (HOTKEY_INFO *infoPtr)
241 {
242 /* free hotkey info data */
243 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
244 Free (infoPtr);
245 return 0;
246 }
247
248
249 static LRESULT
250 HOTKEY_EraseBackground (const HOTKEY_INFO *infoPtr, HDC hdc)
251 {
252 HBRUSH hBrush, hSolidBrush = NULL;
253 RECT rc;
254
255 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
256 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace);
257 else
258 {
259 hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT,
260 (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
261 if (!hBrush)
262 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow);
263 }
264
265 GetClientRect (infoPtr->hwndSelf, &rc);
266
267 FillRect (hdc, &rc, hBrush);
268
269 if (hSolidBrush)
270 DeleteObject(hSolidBrush);
271
272 return -1;
273 }
274
275
276 static inline LRESULT
277 HOTKEY_GetFont (const HOTKEY_INFO *infoPtr)
278 {
279 return (LRESULT)infoPtr->hFont;
280 }
281
282 static LRESULT
283 HOTKEY_KeyDown (HOTKEY_INFO *infoPtr, DWORD key, DWORD flags)
284 {
285 WORD wOldHotKey;
286 BYTE bOldMod;
287
288 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
289 return 0;
290
291 TRACE("() Key: %d\n", key);
292
293 wOldHotKey = infoPtr->HotKey;
294 bOldMod = infoPtr->CurrMod;
295
296 /* If any key is Pressed, we have to reset the hotkey in the control */
297 infoPtr->HotKey = 0;
298
299 switch (key)
300 {
301 case VK_RETURN:
302 case VK_TAB:
303 case VK_SPACE:
304 case VK_DELETE:
305 case VK_ESCAPE:
306 case VK_BACK:
307 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
308 return DefWindowProcW (infoPtr->hwndSelf, WM_KEYDOWN, key, flags);
309
310 case VK_SHIFT:
311 infoPtr->CurrMod |= HOTKEYF_SHIFT;
312 break;
313 case VK_CONTROL:
314 infoPtr->CurrMod |= HOTKEYF_CONTROL;
315 break;
316 case VK_MENU:
317 infoPtr->CurrMod |= HOTKEYF_ALT;
318 break;
319
320 default:
321 if(HOTKEY_IsCombInv(infoPtr))
322 infoPtr->HotKey = MAKEWORD(key, infoPtr->InvMod);
323 else
324 infoPtr->HotKey = MAKEWORD(key, infoPtr->CurrMod);
325 infoPtr->ScanCode = flags;
326 break;
327 }
328
329 if ((wOldHotKey != infoPtr->HotKey) || (bOldMod != infoPtr->CurrMod))
330 {
331 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
332
333 /* send EN_CHANGE notification */
334 SendMessageW(infoPtr->hwndNotify, WM_COMMAND,
335 MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE),
336 (LPARAM)infoPtr->hwndSelf);
337 }
338
339 return 0;
340 }
341
342
343 static LRESULT
344 HOTKEY_KeyUp (HOTKEY_INFO *infoPtr, DWORD key)
345 {
346 BYTE bOldMod;
347
348 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
349 return 0;
350
351 TRACE("() Key: %d\n", key);
352
353 bOldMod = infoPtr->CurrMod;
354
355 switch (key)
356 {
357 case VK_SHIFT:
358 infoPtr->CurrMod &= ~HOTKEYF_SHIFT;
359 break;
360 case VK_CONTROL:
361 infoPtr->CurrMod &= ~HOTKEYF_CONTROL;
362 break;
363 case VK_MENU:
364 infoPtr->CurrMod &= ~HOTKEYF_ALT;
365 break;
366 default:
367 return 1;
368 }
369
370 if (bOldMod != infoPtr->CurrMod)
371 {
372 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
373
374 /* send EN_CHANGE notification */
375 SendMessageW(infoPtr->hwndNotify, WM_COMMAND,
376 MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE),
377 (LPARAM)infoPtr->hwndSelf);
378 }
379
380 return 0;
381 }
382
383
384 static LRESULT
385 HOTKEY_KillFocus (HOTKEY_INFO *infoPtr)
386 {
387 infoPtr->bFocus = FALSE;
388 DestroyCaret ();
389
390 return 0;
391 }
392
393
394 static LRESULT
395 HOTKEY_LButtonDown (const HOTKEY_INFO *infoPtr)
396 {
397 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED))
398 SetFocus (infoPtr->hwndSelf);
399
400 return 0;
401 }
402
403
404 static inline LRESULT
405 HOTKEY_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs)
406 {
407 HOTKEY_INFO *infoPtr;
408 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
409 SetWindowLongW (hwnd, GWL_EXSTYLE,
410 dwExStyle | WS_EX_CLIENTEDGE);
411
412 /* allocate memory for info structure */
413 infoPtr = Alloc (sizeof(HOTKEY_INFO));
414 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
415
416 /* initialize info structure */
417 infoPtr->HotKey = infoPtr->InvComb = infoPtr->InvMod = infoPtr->CurrMod = 0;
418 infoPtr->CaretPos = GetSystemMetrics(SM_CXBORDER);
419 infoPtr->hwndSelf = hwnd;
420 LoadStringW(COMCTL32_hModule, HKY_NONE, infoPtr->strNone, 15);
421
422 return DefWindowProcW (infoPtr->hwndSelf, WM_NCCREATE, 0, (LPARAM)lpcs);
423 }
424
425 static LRESULT
426 HOTKEY_SetFocus (HOTKEY_INFO *infoPtr)
427 {
428 infoPtr->bFocus = TRUE;
429
430 CreateCaret (infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight);
431 SetCaretPos (infoPtr->CaretPos, GetSystemMetrics(SM_CYBORDER));
432 ShowCaret (infoPtr->hwndSelf);
433
434 return 0;
435 }
436
437
438 static LRESULT
439 HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw)
440 {
441 TEXTMETRICW tm;
442 HDC hdc;
443 HFONT hOldFont = 0;
444
445 infoPtr->hFont = hFont;
446
447 hdc = GetDC (infoPtr->hwndSelf);
448 if (infoPtr->hFont)
449 hOldFont = SelectObject (hdc, infoPtr->hFont);
450
451 GetTextMetricsW (hdc, &tm);
452 infoPtr->nHeight = tm.tmHeight;
453
454 if (infoPtr->hFont)
455 SelectObject (hdc, hOldFont);
456 ReleaseDC (infoPtr->hwndSelf, hdc);
457
458 if (redraw)
459 InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
460
461 return 0;
462 }
463
464 static LRESULT WINAPI
465 HOTKEY_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
466 {
467 HOTKEY_INFO *infoPtr = (HOTKEY_INFO *)GetWindowLongPtrW (hwnd, 0);
468 TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd, uMsg, wParam, lParam);
469 if (!infoPtr && (uMsg != WM_NCCREATE))
470 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
471 switch (uMsg)
472 {
473 case HKM_GETHOTKEY:
474 return HOTKEY_GetHotKey (infoPtr);
475 case HKM_SETHOTKEY:
476 HOTKEY_SetHotKey (infoPtr, (WORD)wParam);
477 break;
478 case HKM_SETRULES:
479 HOTKEY_SetRules (infoPtr, (WORD)wParam, (WORD)lParam);
480 break;
481
482 case WM_CHAR:
483 case WM_SYSCHAR:
484 return HOTKEY_KeyDown (infoPtr, MapVirtualKeyW(LOBYTE(HIWORD(lParam)), 1), lParam);
485
486 case WM_CREATE:
487 return HOTKEY_Create (infoPtr, (LPCREATESTRUCTW)lParam);
488
489 case WM_DESTROY:
490 return HOTKEY_Destroy (infoPtr);
491
492 case WM_ERASEBKGND:
493 return HOTKEY_EraseBackground (infoPtr, (HDC)wParam);
494
495 case WM_GETDLGCODE:
496 return DLGC_WANTCHARS | DLGC_WANTARROWS;
497
498 case WM_GETFONT:
499 return HOTKEY_GetFont (infoPtr);
500
501 case WM_KEYDOWN:
502 case WM_SYSKEYDOWN:
503 return HOTKEY_KeyDown (infoPtr, wParam, lParam);
504
505 case WM_KEYUP:
506 case WM_SYSKEYUP:
507 return HOTKEY_KeyUp (infoPtr, wParam);
508
509 case WM_KILLFOCUS:
510 return HOTKEY_KillFocus (infoPtr);
511
512 case WM_LBUTTONDOWN:
513 return HOTKEY_LButtonDown (infoPtr);
514
515 case WM_NCCREATE:
516 return HOTKEY_NCCreate (hwnd, (LPCREATESTRUCTW)lParam);
517
518 case WM_PRINTCLIENT:
519 case WM_PAINT:
520 HOTKEY_Paint(infoPtr, (HDC)wParam);
521 return 0;
522
523 case WM_SETFOCUS:
524 return HOTKEY_SetFocus (infoPtr);
525
526 case WM_SETFONT:
527 return HOTKEY_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam));
528
529 default:
530 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
531 ERR("unknown msg %04x wp=%08lx lp=%08lx\n",
532 uMsg, wParam, lParam);
533 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
534 }
535 return 0;
536 }
537
538
539 void
540 HOTKEY_Register (void)
541 {
542 WNDCLASSW wndClass;
543
544 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
545 wndClass.style = CS_GLOBALCLASS;
546 wndClass.lpfnWndProc = HOTKEY_WindowProc;
547 wndClass.cbClsExtra = 0;
548 wndClass.cbWndExtra = sizeof(HOTKEY_INFO *);
549 wndClass.hCursor = 0;
550 wndClass.hbrBackground = 0;
551 wndClass.lpszClassName = HOTKEY_CLASSW;
552
553 RegisterClassW (&wndClass);
554 }
555
556
557 void
558 HOTKEY_Unregister (void)
559 {
560 UnregisterClassW (HOTKEY_CLASSW, NULL);
561 }