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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(hotkey);
44 typedef struct tagHOTKEY_INFO
57 WCHAR strNone[15]; /* hope its long enough ... */
60 #define HOTKEY_GetInfoPtr(hwnd) ((HOTKEY_INFO *)GetWindowLongPtrA (hwnd, 0))
62 static const WCHAR HOTKEY_plussep[] = { ' ', '+', ' ' };
63 static LRESULT HOTKEY_SetFont (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam);
65 #define IsOnlySet(flags) (infoPtr->CurrMod == (flags))
68 HOTKEY_IsCombInv(HOTKEY_INFO *infoPtr)
70 TRACE("(infoPtr=%p)\n", infoPtr);
71 if((infoPtr->InvComb & HKCOMB_NONE) && !infoPtr->CurrMod)
73 if((infoPtr->InvComb & HKCOMB_S) && IsOnlySet(HOTKEYF_SHIFT))
75 if((infoPtr->InvComb & HKCOMB_C) && IsOnlySet(HOTKEYF_CONTROL))
77 if((infoPtr->InvComb & HKCOMB_A) && IsOnlySet(HOTKEYF_ALT))
79 if((infoPtr->InvComb & HKCOMB_SC) &&
80 IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL))
82 if((infoPtr->InvComb & HKCOMB_SA) && IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_ALT))
84 if((infoPtr->InvComb & HKCOMB_CA) &&
85 IsOnlySet(HOTKEYF_CONTROL | HOTKEYF_ALT))
87 if((infoPtr->InvComb & HKCOMB_SCA) &&
88 IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL | HOTKEYF_ALT))
91 TRACE("() Modifiers are valid\n");
97 HOTKEY_DrawHotKey(HOTKEY_INFO *infoPtr, LPCWSTR KeyName, WORD NameLen, HDC hdc)
100 INT nXStart, nYStart;
101 COLORREF clrOldText, clrOldBk;
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);
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 */
136 HOTKEY_Refresh(HOTKEY_INFO *infoPtr, HDC hdc)
142 TRACE("(infoPtr=%p hdc=%p)\n", infoPtr, hdc);
144 if(!infoPtr->CurrMod && !infoPtr->HotKey) {
145 HOTKEY_DrawHotKey (infoPtr, infoPtr->strNone, 4, hdc);
150 Modifier = HIBYTE(infoPtr->HotKey);
151 else if(HOTKEY_IsCombInv(infoPtr))
152 Modifier = infoPtr->InvMod;
154 Modifier = infoPtr->CurrMod;
156 if(Modifier & HOTKEYF_CONTROL) {
157 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_CONTROL, 0)),
159 NameLen = lstrlenW(KeyName);
160 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
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));
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));
178 if(infoPtr->HotKey) {
179 GetKeyNameTextW(infoPtr->ScanCode, &KeyName[NameLen], 64 - NameLen);
180 NameLen = lstrlenW(KeyName);
183 KeyName[NameLen] = 0;
185 HOTKEY_DrawHotKey (infoPtr, KeyName, NameLen, hdc);
189 HOTKEY_Paint(HOTKEY_INFO *infoPtr, HDC hdc)
192 HOTKEY_Refresh(infoPtr, hdc);
195 hdc = BeginPaint (infoPtr->hwndSelf, &ps);
196 HOTKEY_Refresh (infoPtr, hdc);
197 EndPaint (infoPtr->hwndSelf, &ps);
202 HOTKEY_GetHotKey(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;
210 HOTKEY_SetHotKey(HOTKEY_INFO *infoPtr, WPARAM wParam)
212 infoPtr->HotKey = (WORD)wParam;
214 MAKELPARAM(0, MapVirtualKeyW(LOBYTE(infoPtr->HotKey), 0));
215 TRACE("(infoPtr=%p wParam=%x) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr,
216 wParam, HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
217 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
221 HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
223 infoPtr->InvComb = (WORD)wParam;
224 infoPtr->InvMod = (WORD)lParam;
225 TRACE("(infoPtr=%p) Invalid Modifers: 0x%x, If Invalid: 0x%x\n", infoPtr,
226 infoPtr->InvComb, infoPtr->InvMod);
231 HOTKEY_Create (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
233 infoPtr->hwndNotify = ((LPCREATESTRUCTA)lParam)->hwndParent;
235 HOTKEY_SetFont(infoPtr, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
242 HOTKEY_Destroy (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
244 HWND hwnd = infoPtr->hwndSelf;
245 /* free hotkey info data */
247 SetWindowLongPtrW (hwnd, 0, 0);
253 HOTKEY_EraseBackground (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
255 HBRUSH hBrush, hSolidBrush = NULL;
258 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
259 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace);
262 hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT,
263 wParam, (LPARAM)infoPtr->hwndSelf);
265 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow);
268 GetClientRect (infoPtr->hwndSelf, &rc);
270 FillRect ((HDC)wParam, &rc, hBrush);
273 DeleteObject(hSolidBrush);
279 inline static LRESULT
280 HOTKEY_GetFont (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
282 return (LRESULT)infoPtr->hFont;
286 HOTKEY_KeyDown (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
291 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
294 TRACE("() Key: %d\n", wParam);
296 wOldHotKey = infoPtr->HotKey;
297 bOldMod = infoPtr->CurrMod;
299 /* If any key is Pressed, we have to reset the hotkey in the control */
310 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
311 return DefWindowProcW (infoPtr->hwndSelf, WM_KEYDOWN, wParam,
315 infoPtr->CurrMod |= HOTKEYF_SHIFT;
318 infoPtr->CurrMod |= HOTKEYF_CONTROL;
321 infoPtr->CurrMod |= HOTKEYF_ALT;
325 if(HOTKEY_IsCombInv(infoPtr))
326 infoPtr->HotKey = MAKEWORD(wParam, infoPtr->InvMod);
328 infoPtr->HotKey = MAKEWORD(wParam, infoPtr->CurrMod);
329 infoPtr->ScanCode = lParam;
333 if ((wOldHotKey != infoPtr->HotKey) || (bOldMod != infoPtr->CurrMod))
335 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
337 /* send EN_CHANGE notification */
338 SendMessageW(infoPtr->hwndNotify, WM_COMMAND,
339 MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE),
340 (LPARAM)infoPtr->hwndSelf);
348 HOTKEY_KeyUp (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
352 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
355 TRACE("() Key: %d\n", wParam);
357 bOldMod = infoPtr->CurrMod;
362 infoPtr->CurrMod &= ~HOTKEYF_SHIFT;
365 infoPtr->CurrMod &= ~HOTKEYF_CONTROL;
368 infoPtr->CurrMod &= ~HOTKEYF_ALT;
374 if (bOldMod != infoPtr->CurrMod)
376 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
378 /* send EN_CHANGE notification */
379 SendMessageW(infoPtr->hwndNotify, WM_COMMAND,
380 MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE),
381 (LPARAM)infoPtr->hwndSelf);
389 HOTKEY_KillFocus (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
391 infoPtr->bFocus = FALSE;
399 HOTKEY_LButtonDown (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
401 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED))
402 SetFocus (infoPtr->hwndSelf);
408 inline static LRESULT
409 HOTKEY_NCCreate (HWND hwnd, WPARAM wParam, LPARAM lParam)
411 HOTKEY_INFO *infoPtr;
412 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
413 SetWindowLongW (hwnd, GWL_EXSTYLE,
414 dwExStyle | WS_EX_CLIENTEDGE);
416 /* allocate memory for info structure */
417 infoPtr = (HOTKEY_INFO *)Alloc (sizeof(HOTKEY_INFO));
418 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
420 /* initialize info structure */
421 infoPtr->HotKey = infoPtr->InvComb = infoPtr->InvMod = infoPtr->CurrMod = 0;
422 infoPtr->CaretPos = GetSystemMetrics(SM_CXBORDER);
423 infoPtr->hwndSelf = hwnd;
424 LoadStringW(COMCTL32_hModule, HKY_NONE, infoPtr->strNone, 15);
426 return DefWindowProcW (infoPtr->hwndSelf, WM_NCCREATE, wParam, lParam);
430 HOTKEY_SetFocus (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
432 infoPtr->bFocus = TRUE;
434 CreateCaret (infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight);
435 SetCaretPos (infoPtr->CaretPos, GetSystemMetrics(SM_CYBORDER));
436 ShowCaret (infoPtr->hwndSelf);
443 HOTKEY_SetFont (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
449 infoPtr->hFont = (HFONT)wParam;
451 hdc = GetDC (infoPtr->hwndSelf);
453 hOldFont = SelectObject (hdc, infoPtr->hFont);
455 GetTextMetricsW (hdc, &tm);
456 infoPtr->nHeight = tm.tmHeight;
459 SelectObject (hdc, hOldFont);
460 ReleaseDC (infoPtr->hwndSelf, hdc);
463 InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
468 static LRESULT WINAPI
469 HOTKEY_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
471 HOTKEY_INFO *infoPtr = HOTKEY_GetInfoPtr (hwnd);
472 TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
473 if (!infoPtr && (uMsg != WM_NCCREATE))
474 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
478 return HOTKEY_GetHotKey (infoPtr);
480 HOTKEY_SetHotKey (infoPtr, wParam);
483 HOTKEY_SetRules (infoPtr, wParam, lParam);
488 return HOTKEY_KeyDown (infoPtr, MapVirtualKeyW(LOBYTE(HIWORD(lParam)), 1), lParam);
491 return HOTKEY_Create (infoPtr, wParam, lParam);
494 return HOTKEY_Destroy (infoPtr, wParam, lParam);
497 return HOTKEY_EraseBackground (infoPtr, wParam, lParam);
500 return DLGC_WANTCHARS | DLGC_WANTARROWS;
503 return HOTKEY_GetFont (infoPtr, wParam, lParam);
507 return HOTKEY_KeyDown (infoPtr, wParam, lParam);
511 return HOTKEY_KeyUp (infoPtr, wParam, lParam);
514 return HOTKEY_KillFocus (infoPtr, wParam, lParam);
517 return HOTKEY_LButtonDown (infoPtr, wParam, lParam);
520 return HOTKEY_NCCreate (hwnd, wParam, lParam);
523 HOTKEY_Paint(infoPtr, (HDC)wParam);
527 return HOTKEY_SetFocus (infoPtr, wParam, lParam);
530 return HOTKEY_SetFont (infoPtr, wParam, lParam);
533 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
534 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
535 uMsg, wParam, lParam);
536 return DefWindowProcW (hwnd, uMsg, wParam, lParam);
543 HOTKEY_Register (void)
547 ZeroMemory (&wndClass, sizeof(WNDCLASSW));
548 wndClass.style = CS_GLOBALCLASS;
549 wndClass.lpfnWndProc = HOTKEY_WindowProc;
550 wndClass.cbClsExtra = 0;
551 wndClass.cbWndExtra = sizeof(HOTKEY_INFO *);
552 wndClass.hCursor = 0;
553 wndClass.hbrBackground = 0;
554 wndClass.lpszClassName = HOTKEY_CLASSW;
556 RegisterClassW (&wndClass);
561 HOTKEY_Unregister (void)
563 UnregisterClassW (HOTKEY_CLASSW, NULL);