kernelbase/tests: Use win_skip() for missing APIs.
[wine.git] / dlls / comctl32 / hotkey.c
blob1fc9658c1790892c43ba5d1a3a0fa32cb0ba694c
1 /*
2 * Hotkey control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 2002 Gyorgy 'Nog' Jeney
6 * Copyright 2004 Robert Shearman
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.
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.
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
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.
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.
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winnls.h"
39 #include "commctrl.h"
40 #include "comctl32.h"
41 #include "uxtheme.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(hotkey);
46 typedef struct tagHOTKEY_INFO
48 HWND hwndSelf;
49 HWND hwndNotify;
50 HFONT hFont;
51 BOOL bFocus;
52 INT nHeight;
53 WORD HotKey;
54 WORD InvComb;
55 WORD InvMod;
56 BYTE CurrMod;
57 INT CaretPos;
58 DWORD ScanCode;
59 WCHAR strNone[15]; /* hope it's long enough ... */
60 } HOTKEY_INFO;
62 static const WCHAR HOTKEY_plussep[] = { ' ', '+', ' ' };
63 static LRESULT HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw);
65 #define IsOnlySet(flags) (infoPtr->CurrMod == (flags))
67 static BOOL
68 HOTKEY_IsCombInv(const HOTKEY_INFO *infoPtr)
70 TRACE("(infoPtr=%p)\n", infoPtr);
71 if((infoPtr->InvComb & HKCOMB_NONE) && !infoPtr->CurrMod)
72 return TRUE;
73 if((infoPtr->InvComb & HKCOMB_S) && IsOnlySet(HOTKEYF_SHIFT))
74 return TRUE;
75 if((infoPtr->InvComb & HKCOMB_C) && IsOnlySet(HOTKEYF_CONTROL))
76 return TRUE;
77 if((infoPtr->InvComb & HKCOMB_A) && IsOnlySet(HOTKEYF_ALT))
78 return TRUE;
79 if((infoPtr->InvComb & HKCOMB_SC) &&
80 IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL))
81 return TRUE;
82 if((infoPtr->InvComb & HKCOMB_SA) && IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_ALT))
83 return TRUE;
84 if((infoPtr->InvComb & HKCOMB_CA) &&
85 IsOnlySet(HOTKEYF_CONTROL | HOTKEYF_ALT))
86 return TRUE;
87 if((infoPtr->InvComb & HKCOMB_SCA) &&
88 IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL | HOTKEYF_ALT))
89 return TRUE;
91 TRACE("() Modifiers are valid\n");
92 return FALSE;
94 #undef IsOnlySet
96 static void
97 HOTKEY_DrawHotKey(HOTKEY_INFO *infoPtr, HDC hdc, LPCWSTR KeyName, WORD NameLen)
99 SIZE TextSize;
100 INT nXStart, nYStart;
101 COLORREF clrOldText, clrOldBk;
102 HFONT hFontOld;
104 /* Make a gap from the frame */
105 nXStart = GetSystemMetrics(SM_CXBORDER);
106 nYStart = GetSystemMetrics(SM_CYBORDER);
108 hFontOld = SelectObject(hdc, infoPtr->hFont);
109 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
111 clrOldText = SetTextColor(hdc, comctl32_color.clrGrayText);
112 clrOldBk = SetBkColor(hdc, comctl32_color.clrBtnFace);
114 else
116 clrOldText = SetTextColor(hdc, comctl32_color.clrWindowText);
117 clrOldBk = SetBkColor(hdc, comctl32_color.clrWindow);
120 TextOutW(hdc, nXStart, nYStart, KeyName, NameLen);
122 /* Get the text width for the caret */
123 GetTextExtentPoint32W(hdc, KeyName, NameLen, &TextSize);
124 infoPtr->CaretPos = nXStart + TextSize.cx;
126 SetBkColor(hdc, clrOldBk);
127 SetTextColor(hdc, clrOldText);
128 SelectObject(hdc, hFontOld);
130 /* position the caret */
131 SetCaretPos(infoPtr->CaretPos, nYStart);
134 /* Draw the names of the keys in the control */
135 static void
136 HOTKEY_Refresh(HOTKEY_INFO *infoPtr, HDC hdc)
138 WCHAR KeyName[64];
139 WORD NameLen = 0;
140 BYTE Modifier;
142 TRACE("(infoPtr=%p hdc=%p)\n", infoPtr, hdc);
144 if(!infoPtr->CurrMod && !infoPtr->HotKey) {
145 HOTKEY_DrawHotKey (infoPtr, hdc, infoPtr->strNone, lstrlenW(infoPtr->strNone));
146 return;
149 if(infoPtr->HotKey)
150 Modifier = HIBYTE(infoPtr->HotKey);
151 else if(HOTKEY_IsCombInv(infoPtr))
152 Modifier = infoPtr->InvMod;
153 else
154 Modifier = infoPtr->CurrMod;
156 if(Modifier & HOTKEYF_CONTROL) {
157 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_CONTROL, 0)),
158 KeyName, 64);
159 NameLen = lstrlenW(KeyName);
160 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
161 NameLen += 3;
163 if(Modifier & HOTKEYF_SHIFT) {
164 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_SHIFT, 0)),
165 &KeyName[NameLen], 64 - NameLen);
166 NameLen = lstrlenW(KeyName);
167 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
168 NameLen += 3;
170 if(Modifier & HOTKEYF_ALT) {
171 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_MENU, 0)),
172 &KeyName[NameLen], 64 - NameLen);
173 NameLen = lstrlenW(KeyName);
174 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
175 NameLen += 3;
178 if(infoPtr->HotKey) {
179 GetKeyNameTextW(infoPtr->ScanCode, &KeyName[NameLen], 64 - NameLen);
180 NameLen = lstrlenW(KeyName);
182 else
183 KeyName[NameLen] = 0;
185 HOTKEY_DrawHotKey (infoPtr, hdc, KeyName, NameLen);
188 static void
189 HOTKEY_Paint(HOTKEY_INFO *infoPtr, HDC hdc)
191 if (hdc)
192 HOTKEY_Refresh(infoPtr, hdc);
193 else {
194 PAINTSTRUCT ps;
195 hdc = BeginPaint (infoPtr->hwndSelf, &ps);
196 HOTKEY_Refresh (infoPtr, hdc);
197 EndPaint (infoPtr->hwndSelf, &ps);
201 static LRESULT
202 HOTKEY_GetHotKey(const HOTKEY_INFO *infoPtr)
204 TRACE("(infoPtr=%p) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr,
205 HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
206 return (LRESULT)infoPtr->HotKey;
209 static void
210 HOTKEY_SetHotKey(HOTKEY_INFO *infoPtr, WORD hotKey)
212 infoPtr->HotKey = hotKey;
213 infoPtr->ScanCode =
214 MAKELPARAM(0, MapVirtualKeyW(LOBYTE(infoPtr->HotKey), 0));
215 TRACE("(infoPtr=%p hotKey=%x) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr,
216 hotKey, HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
217 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
220 static void
221 HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WORD invComb, WORD invMod)
223 infoPtr->InvComb = invComb;
224 infoPtr->InvMod = invMod;
225 TRACE("(infoPtr=%p) Invalid Modifiers: 0x%x, If Invalid: 0x%x\n", infoPtr,
226 infoPtr->InvComb, infoPtr->InvMod);
230 static LRESULT
231 HOTKEY_Create (HOTKEY_INFO *infoPtr, const CREATESTRUCTW *lpcs)
233 infoPtr->hwndNotify = lpcs->hwndParent;
235 HOTKEY_SetFont(infoPtr, GetStockObject(SYSTEM_FONT), 0);
237 return 0;
241 static LRESULT
242 HOTKEY_Destroy (HOTKEY_INFO *infoPtr)
244 /* Free hotkey info data */
245 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
246 Free (infoPtr);
247 return 0;
251 static LRESULT
252 HOTKEY_EraseBackground (const HOTKEY_INFO *infoPtr, HDC hdc)
254 HBRUSH hBrush, hSolidBrush = NULL;
255 RECT rc;
257 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
258 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace);
259 else
261 hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT,
262 (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
263 if (!hBrush)
264 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow);
267 GetClientRect (infoPtr->hwndSelf, &rc);
269 FillRect (hdc, &rc, hBrush);
271 if (hSolidBrush)
272 DeleteObject(hSolidBrush);
274 return -1;
278 static inline LRESULT
279 HOTKEY_GetFont (const HOTKEY_INFO *infoPtr)
281 return (LRESULT)infoPtr->hFont;
284 static LRESULT
285 HOTKEY_KeyDown (HOTKEY_INFO *infoPtr, DWORD key, DWORD flags)
287 WORD wOldHotKey;
288 BYTE bOldMod;
290 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
291 return 0;
293 TRACE("() Key: %ld\n", key);
295 wOldHotKey = infoPtr->HotKey;
296 bOldMod = infoPtr->CurrMod;
298 /* If any key is Pressed, we have to reset the hotkey in the control */
299 infoPtr->HotKey = 0;
301 switch (key)
303 case VK_RETURN:
304 case VK_TAB:
305 case VK_SPACE:
306 case VK_DELETE:
307 case VK_ESCAPE:
308 case VK_BACK:
309 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
310 return DefWindowProcW (infoPtr->hwndSelf, WM_KEYDOWN, key, flags);
312 case VK_SHIFT:
313 infoPtr->CurrMod |= HOTKEYF_SHIFT;
314 break;
315 case VK_CONTROL:
316 infoPtr->CurrMod |= HOTKEYF_CONTROL;
317 break;
318 case VK_MENU:
319 infoPtr->CurrMod |= HOTKEYF_ALT;
320 break;
322 default:
323 if(HOTKEY_IsCombInv(infoPtr))
324 infoPtr->HotKey = MAKEWORD(key, infoPtr->InvMod);
325 else
326 infoPtr->HotKey = MAKEWORD(key, infoPtr->CurrMod);
327 infoPtr->ScanCode = flags;
328 break;
331 if ((wOldHotKey != infoPtr->HotKey) || (bOldMod != infoPtr->CurrMod))
333 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
335 /* send EN_CHANGE notification */
336 SendMessageW(infoPtr->hwndNotify, WM_COMMAND,
337 MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE),
338 (LPARAM)infoPtr->hwndSelf);
341 return 0;
345 static LRESULT
346 HOTKEY_KeyUp (HOTKEY_INFO *infoPtr, DWORD key)
348 BYTE bOldMod;
350 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
351 return 0;
353 TRACE("() Key: %ld\n", key);
355 bOldMod = infoPtr->CurrMod;
357 switch (key)
359 case VK_SHIFT:
360 infoPtr->CurrMod &= ~HOTKEYF_SHIFT;
361 break;
362 case VK_CONTROL:
363 infoPtr->CurrMod &= ~HOTKEYF_CONTROL;
364 break;
365 case VK_MENU:
366 infoPtr->CurrMod &= ~HOTKEYF_ALT;
367 break;
368 default:
369 return 1;
372 if (bOldMod != infoPtr->CurrMod)
374 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
376 /* send EN_CHANGE notification */
377 SendMessageW(infoPtr->hwndNotify, WM_COMMAND,
378 MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE),
379 (LPARAM)infoPtr->hwndSelf);
382 return 0;
386 static LRESULT
387 HOTKEY_KillFocus (HOTKEY_INFO *infoPtr)
389 infoPtr->bFocus = FALSE;
390 DestroyCaret ();
392 return 0;
396 static LRESULT
397 HOTKEY_LButtonDown (const HOTKEY_INFO *infoPtr)
399 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED))
400 SetFocus (infoPtr->hwndSelf);
402 return 0;
406 static inline LRESULT
407 HOTKEY_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs)
409 HOTKEY_INFO *infoPtr;
410 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
411 SetWindowLongW (hwnd, GWL_EXSTYLE,
412 dwExStyle | WS_EX_CLIENTEDGE);
414 /* allocate memory for info structure */
415 infoPtr = Alloc(sizeof(*infoPtr));
416 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
418 /* initialize info structure */
419 infoPtr->HotKey = infoPtr->InvComb = infoPtr->InvMod = infoPtr->CurrMod = 0;
420 infoPtr->CaretPos = GetSystemMetrics(SM_CXBORDER);
421 infoPtr->hwndSelf = hwnd;
422 LoadStringW(COMCTL32_hModule, HKY_NONE, infoPtr->strNone, 15);
424 return DefWindowProcW (infoPtr->hwndSelf, WM_NCCREATE, 0, (LPARAM)lpcs);
427 static LRESULT
428 HOTKEY_NCPaint (HWND hwnd, HRGN region)
430 INT cxEdge, cyEdge;
431 HRGN clipRgn;
432 HTHEME theme;
433 LONG exStyle;
434 RECT r;
435 HDC dc;
437 theme = OpenThemeDataForDpi(NULL, WC_EDITW, GetDpiForWindow(hwnd));
438 if (!theme)
439 return DefWindowProcW(hwnd, WM_NCPAINT, (WPARAM)region, 0);
441 exStyle = GetWindowLongW(hwnd, GWL_EXSTYLE);
442 if (!(exStyle & WS_EX_CLIENTEDGE))
444 CloseThemeData(theme);
445 return DefWindowProcW(hwnd, WM_NCPAINT, (WPARAM)region, 0);
448 cxEdge = GetSystemMetrics(SM_CXEDGE);
449 cyEdge = GetSystemMetrics(SM_CYEDGE);
450 GetWindowRect(hwnd, &r);
452 /* New clipping region passed to default proc to exclude border */
453 clipRgn = CreateRectRgn(r.left + cxEdge, r.top + cyEdge, r.right - cxEdge, r.bottom - cyEdge);
454 if (region != (HRGN)1)
455 CombineRgn(clipRgn, clipRgn, region, RGN_AND);
456 OffsetRect(&r, -r.left, -r.top);
458 dc = GetDCEx(hwnd, region, DCX_WINDOW | DCX_INTERSECTRGN);
459 if (IsThemeBackgroundPartiallyTransparent(theme, 0, 0))
460 DrawThemeParentBackground(hwnd, dc, &r);
461 DrawThemeBackground(theme, dc, 0, 0, &r, 0);
462 ReleaseDC(hwnd, dc);
463 CloseThemeData(theme);
465 /* Call default proc to get the scrollbars etc. also painted */
466 DefWindowProcW(hwnd, WM_NCPAINT, (WPARAM)clipRgn, 0);
467 DeleteObject(clipRgn);
468 return 0;
471 static LRESULT
472 HOTKEY_SetFocus (HOTKEY_INFO *infoPtr)
474 infoPtr->bFocus = TRUE;
476 CreateCaret (infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight);
477 SetCaretPos (infoPtr->CaretPos, GetSystemMetrics(SM_CYBORDER));
478 ShowCaret (infoPtr->hwndSelf);
480 return 0;
484 static LRESULT
485 HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw)
487 TEXTMETRICW tm;
488 HDC hdc;
489 HFONT hOldFont = 0;
491 infoPtr->hFont = hFont;
493 hdc = GetDC (infoPtr->hwndSelf);
494 if (infoPtr->hFont)
495 hOldFont = SelectObject (hdc, infoPtr->hFont);
497 GetTextMetricsW (hdc, &tm);
498 infoPtr->nHeight = tm.tmHeight;
500 if (infoPtr->hFont)
501 SelectObject (hdc, hOldFont);
502 ReleaseDC (infoPtr->hwndSelf, hdc);
504 if (redraw)
505 InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
507 return 0;
510 static LRESULT WINAPI
511 HOTKEY_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
513 HOTKEY_INFO *infoPtr = (HOTKEY_INFO *)GetWindowLongPtrW (hwnd, 0);
515 TRACE("hwnd %p, msg %x, wparam %Ix, lparam %Ix\n", hwnd, uMsg, wParam, lParam);
517 if (!infoPtr && (uMsg != WM_NCCREATE))
518 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
519 switch (uMsg)
521 case HKM_GETHOTKEY:
522 return HOTKEY_GetHotKey (infoPtr);
523 case HKM_SETHOTKEY:
524 HOTKEY_SetHotKey (infoPtr, (WORD)wParam);
525 break;
526 case HKM_SETRULES:
527 HOTKEY_SetRules (infoPtr, (WORD)wParam, (WORD)lParam);
528 break;
530 case WM_CHAR:
531 case WM_SYSCHAR:
532 return HOTKEY_KeyDown (infoPtr, MapVirtualKeyW(LOBYTE(HIWORD(lParam)), 1), lParam);
534 case WM_CREATE:
535 return HOTKEY_Create (infoPtr, (LPCREATESTRUCTW)lParam);
537 case WM_DESTROY:
538 return HOTKEY_Destroy (infoPtr);
540 case WM_ERASEBKGND:
541 return HOTKEY_EraseBackground (infoPtr, (HDC)wParam);
543 case WM_GETDLGCODE:
544 return DLGC_WANTCHARS | DLGC_WANTARROWS;
546 case WM_GETFONT:
547 return HOTKEY_GetFont (infoPtr);
549 case WM_KEYDOWN:
550 case WM_SYSKEYDOWN:
551 return HOTKEY_KeyDown (infoPtr, wParam, lParam);
553 case WM_KEYUP:
554 case WM_SYSKEYUP:
555 return HOTKEY_KeyUp (infoPtr, wParam);
557 case WM_KILLFOCUS:
558 return HOTKEY_KillFocus (infoPtr);
560 case WM_LBUTTONDOWN:
561 return HOTKEY_LButtonDown (infoPtr);
563 case WM_NCCREATE:
564 return HOTKEY_NCCreate (hwnd, (LPCREATESTRUCTW)lParam);
566 case WM_NCPAINT:
567 return HOTKEY_NCPaint (hwnd, (HRGN)wParam);
569 case WM_PRINTCLIENT:
570 case WM_PAINT:
571 HOTKEY_Paint(infoPtr, (HDC)wParam);
572 return 0;
574 case WM_SETFOCUS:
575 return HOTKEY_SetFocus (infoPtr);
577 case WM_SETFONT:
578 return HOTKEY_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam));
580 default:
581 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
582 ERR("unknown msg %04x, wp %Ix, lp %Ix\n", uMsg, wParam, lParam);
583 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
585 return 0;
589 void
590 HOTKEY_Register (void)
592 WNDCLASSW wndClass;
594 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
595 wndClass.style = CS_GLOBALCLASS;
596 wndClass.lpfnWndProc = HOTKEY_WindowProc;
597 wndClass.cbClsExtra = 0;
598 wndClass.cbWndExtra = sizeof(HOTKEY_INFO *);
599 wndClass.hCursor = 0;
600 wndClass.hbrBackground = 0;
601 wndClass.lpszClassName = HOTKEY_CLASSW;
603 RegisterClassW (&wndClass);
607 void
608 HOTKEY_Unregister (void)
610 UnregisterClassW (HOTKEY_CLASSW, NULL);