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.
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(hotkey
);
44 typedef struct tagHOTKEY_INFO
57 WCHAR strNone
[15]; /* hope it's long enough ... */
60 static const WCHAR HOTKEY_plussep
[] = { ' ', '+', ' ' };
61 static LRESULT
HOTKEY_SetFont (HOTKEY_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
);
63 #define IsOnlySet(flags) (infoPtr->CurrMod == (flags))
66 HOTKEY_IsCombInv(const HOTKEY_INFO
*infoPtr
)
68 TRACE("(infoPtr=%p)\n", infoPtr
);
69 if((infoPtr
->InvComb
& HKCOMB_NONE
) && !infoPtr
->CurrMod
)
71 if((infoPtr
->InvComb
& HKCOMB_S
) && IsOnlySet(HOTKEYF_SHIFT
))
73 if((infoPtr
->InvComb
& HKCOMB_C
) && IsOnlySet(HOTKEYF_CONTROL
))
75 if((infoPtr
->InvComb
& HKCOMB_A
) && IsOnlySet(HOTKEYF_ALT
))
77 if((infoPtr
->InvComb
& HKCOMB_SC
) &&
78 IsOnlySet(HOTKEYF_SHIFT
| HOTKEYF_CONTROL
))
80 if((infoPtr
->InvComb
& HKCOMB_SA
) && IsOnlySet(HOTKEYF_SHIFT
| HOTKEYF_ALT
))
82 if((infoPtr
->InvComb
& HKCOMB_CA
) &&
83 IsOnlySet(HOTKEYF_CONTROL
| HOTKEYF_ALT
))
85 if((infoPtr
->InvComb
& HKCOMB_SCA
) &&
86 IsOnlySet(HOTKEYF_SHIFT
| HOTKEYF_CONTROL
| HOTKEYF_ALT
))
89 TRACE("() Modifiers are valid\n");
95 HOTKEY_DrawHotKey(HOTKEY_INFO
*infoPtr
, HDC hdc
, LPCWSTR KeyName
, WORD NameLen
)
99 COLORREF clrOldText
, clrOldBk
;
102 /* Make a gap from the frame */
103 nXStart
= GetSystemMetrics(SM_CXBORDER
);
104 nYStart
= GetSystemMetrics(SM_CYBORDER
);
106 hFontOld
= SelectObject(hdc
, infoPtr
->hFont
);
107 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
109 clrOldText
= SetTextColor(hdc
, comctl32_color
.clrGrayText
);
110 clrOldBk
= SetBkColor(hdc
, comctl32_color
.clrBtnFace
);
114 clrOldText
= SetTextColor(hdc
, comctl32_color
.clrWindowText
);
115 clrOldBk
= SetBkColor(hdc
, comctl32_color
.clrWindow
);
118 TextOutW(hdc
, nXStart
, nYStart
, KeyName
, NameLen
);
120 /* Get the text width for the caret */
121 GetTextExtentPoint32W(hdc
, KeyName
, NameLen
, &TextSize
);
122 infoPtr
->CaretPos
= nXStart
+ TextSize
.cx
;
124 SetBkColor(hdc
, clrOldBk
);
125 SetTextColor(hdc
, clrOldText
);
126 SelectObject(hdc
, hFontOld
);
128 /* position the caret */
129 SetCaretPos(infoPtr
->CaretPos
, nYStart
);
132 /* Draw the names of the keys in the control */
134 HOTKEY_Refresh(HOTKEY_INFO
*infoPtr
, HDC hdc
)
140 TRACE("(infoPtr=%p hdc=%p)\n", infoPtr
, hdc
);
142 if(!infoPtr
->CurrMod
&& !infoPtr
->HotKey
) {
143 HOTKEY_DrawHotKey (infoPtr
, hdc
, infoPtr
->strNone
, 4);
148 Modifier
= HIBYTE(infoPtr
->HotKey
);
149 else if(HOTKEY_IsCombInv(infoPtr
))
150 Modifier
= infoPtr
->InvMod
;
152 Modifier
= infoPtr
->CurrMod
;
154 if(Modifier
& HOTKEYF_CONTROL
) {
155 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_CONTROL
, 0)),
157 NameLen
= lstrlenW(KeyName
);
158 memcpy(&KeyName
[NameLen
], HOTKEY_plussep
, sizeof(HOTKEY_plussep
));
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
));
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
));
176 if(infoPtr
->HotKey
) {
177 GetKeyNameTextW(infoPtr
->ScanCode
, &KeyName
[NameLen
], 64 - NameLen
);
178 NameLen
= lstrlenW(KeyName
);
181 KeyName
[NameLen
] = 0;
183 HOTKEY_DrawHotKey (infoPtr
, hdc
, KeyName
, NameLen
);
187 HOTKEY_Paint(HOTKEY_INFO
*infoPtr
, HDC hdc
)
190 HOTKEY_Refresh(infoPtr
, hdc
);
193 hdc
= BeginPaint (infoPtr
->hwndSelf
, &ps
);
194 HOTKEY_Refresh (infoPtr
, hdc
);
195 EndPaint (infoPtr
->hwndSelf
, &ps
);
200 HOTKEY_GetHotKey(const HOTKEY_INFO
*infoPtr
)
202 TRACE("(infoPtr=%p) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr
,
203 HIBYTE(infoPtr
->HotKey
), LOBYTE(infoPtr
->HotKey
));
204 return (LRESULT
)infoPtr
->HotKey
;
208 HOTKEY_SetHotKey(HOTKEY_INFO
*infoPtr
, WORD hotKey
)
210 infoPtr
->HotKey
= hotKey
;
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
);
219 HOTKEY_SetRules(HOTKEY_INFO
*infoPtr
, WORD invComb
, WORD invMod
)
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
);
229 HOTKEY_Create (HOTKEY_INFO
*infoPtr
, const CREATESTRUCTW
*lpcs
)
231 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
233 HOTKEY_SetFont(infoPtr
, GetStockObject(SYSTEM_FONT
), 0);
240 HOTKEY_Destroy (HOTKEY_INFO
*infoPtr
)
242 HWND hwnd
= infoPtr
->hwndSelf
;
243 /* free hotkey info data */
245 SetWindowLongPtrW (hwnd
, 0, 0);
251 HOTKEY_EraseBackground (const HOTKEY_INFO
*infoPtr
, HDC hdc
)
253 HBRUSH hBrush
, hSolidBrush
= NULL
;
256 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
257 hBrush
= hSolidBrush
= CreateSolidBrush(comctl32_color
.clrBtnFace
);
260 hBrush
= (HBRUSH
)SendMessageW(infoPtr
->hwndNotify
, WM_CTLCOLOREDIT
,
261 (WPARAM
)hdc
, (LPARAM
)infoPtr
->hwndSelf
);
263 hBrush
= hSolidBrush
= CreateSolidBrush(comctl32_color
.clrWindow
);
266 GetClientRect (infoPtr
->hwndSelf
, &rc
);
268 FillRect (hdc
, &rc
, hBrush
);
271 DeleteObject(hSolidBrush
);
277 static inline LRESULT
278 HOTKEY_GetFont (const HOTKEY_INFO
*infoPtr
)
280 return (LRESULT
)infoPtr
->hFont
;
284 HOTKEY_KeyDown (HOTKEY_INFO
*infoPtr
, DWORD key
, DWORD flags
)
289 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
292 TRACE("() Key: %d\n", key
);
294 wOldHotKey
= infoPtr
->HotKey
;
295 bOldMod
= infoPtr
->CurrMod
;
297 /* If any key is Pressed, we have to reset the hotkey in the control */
308 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
309 return DefWindowProcW (infoPtr
->hwndSelf
, WM_KEYDOWN
, key
, flags
);
312 infoPtr
->CurrMod
|= HOTKEYF_SHIFT
;
315 infoPtr
->CurrMod
|= HOTKEYF_CONTROL
;
318 infoPtr
->CurrMod
|= HOTKEYF_ALT
;
322 if(HOTKEY_IsCombInv(infoPtr
))
323 infoPtr
->HotKey
= MAKEWORD(key
, infoPtr
->InvMod
);
325 infoPtr
->HotKey
= MAKEWORD(key
, infoPtr
->CurrMod
);
326 infoPtr
->ScanCode
= flags
;
330 if ((wOldHotKey
!= infoPtr
->HotKey
) || (bOldMod
!= infoPtr
->CurrMod
))
332 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
334 /* send EN_CHANGE notification */
335 SendMessageW(infoPtr
->hwndNotify
, WM_COMMAND
,
336 MAKEWPARAM(GetDlgCtrlID(infoPtr
->hwndSelf
), EN_CHANGE
),
337 (LPARAM
)infoPtr
->hwndSelf
);
345 HOTKEY_KeyUp (HOTKEY_INFO
*infoPtr
, DWORD key
, DWORD flags
)
349 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
352 TRACE("() Key: %d\n", key
);
354 bOldMod
= infoPtr
->CurrMod
;
359 infoPtr
->CurrMod
&= ~HOTKEYF_SHIFT
;
362 infoPtr
->CurrMod
&= ~HOTKEYF_CONTROL
;
365 infoPtr
->CurrMod
&= ~HOTKEYF_ALT
;
371 if (bOldMod
!= infoPtr
->CurrMod
)
373 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
375 /* send EN_CHANGE notification */
376 SendMessageW(infoPtr
->hwndNotify
, WM_COMMAND
,
377 MAKEWPARAM(GetDlgCtrlID(infoPtr
->hwndSelf
), EN_CHANGE
),
378 (LPARAM
)infoPtr
->hwndSelf
);
386 HOTKEY_KillFocus (HOTKEY_INFO
*infoPtr
, HWND receiveFocus
)
388 infoPtr
->bFocus
= FALSE
;
396 HOTKEY_LButtonDown (const HOTKEY_INFO
*infoPtr
)
398 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
))
399 SetFocus (infoPtr
->hwndSelf
);
405 static inline LRESULT
406 HOTKEY_NCCreate (HWND hwnd
, const CREATESTRUCTW
*lpcs
)
408 HOTKEY_INFO
*infoPtr
;
409 DWORD dwExStyle
= GetWindowLongW (hwnd
, GWL_EXSTYLE
);
410 SetWindowLongW (hwnd
, GWL_EXSTYLE
,
411 dwExStyle
| WS_EX_CLIENTEDGE
);
413 /* allocate memory for info structure */
414 infoPtr
= Alloc (sizeof(HOTKEY_INFO
));
415 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
417 /* initialize info structure */
418 infoPtr
->HotKey
= infoPtr
->InvComb
= infoPtr
->InvMod
= infoPtr
->CurrMod
= 0;
419 infoPtr
->CaretPos
= GetSystemMetrics(SM_CXBORDER
);
420 infoPtr
->hwndSelf
= hwnd
;
421 LoadStringW(COMCTL32_hModule
, HKY_NONE
, infoPtr
->strNone
, 15);
423 return DefWindowProcW (infoPtr
->hwndSelf
, WM_NCCREATE
, 0, (LPARAM
)lpcs
);
427 HOTKEY_SetFocus (HOTKEY_INFO
*infoPtr
, HWND lostFocus
)
429 infoPtr
->bFocus
= TRUE
;
431 CreateCaret (infoPtr
->hwndSelf
, NULL
, 1, infoPtr
->nHeight
);
432 SetCaretPos (infoPtr
->CaretPos
, GetSystemMetrics(SM_CYBORDER
));
433 ShowCaret (infoPtr
->hwndSelf
);
440 HOTKEY_SetFont (HOTKEY_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
)
446 infoPtr
->hFont
= hFont
;
448 hdc
= GetDC (infoPtr
->hwndSelf
);
450 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
452 GetTextMetricsW (hdc
, &tm
);
453 infoPtr
->nHeight
= tm
.tmHeight
;
456 SelectObject (hdc
, hOldFont
);
457 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
460 InvalidateRect (infoPtr
->hwndSelf
, NULL
, TRUE
);
465 static LRESULT WINAPI
466 HOTKEY_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
468 HOTKEY_INFO
*infoPtr
= (HOTKEY_INFO
*)GetWindowLongPtrW (hwnd
, 0);
469 TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
470 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
471 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
475 return HOTKEY_GetHotKey (infoPtr
);
477 HOTKEY_SetHotKey (infoPtr
, (WORD
)wParam
);
480 HOTKEY_SetRules (infoPtr
, (WORD
)wParam
, (WORD
)lParam
);
485 return HOTKEY_KeyDown (infoPtr
, MapVirtualKeyW(LOBYTE(HIWORD(lParam
)), 1), lParam
);
488 return HOTKEY_Create (infoPtr
, (LPCREATESTRUCTW
)lParam
);
491 return HOTKEY_Destroy (infoPtr
);
494 return HOTKEY_EraseBackground (infoPtr
, (HDC
)wParam
);
497 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
500 return HOTKEY_GetFont (infoPtr
);
504 return HOTKEY_KeyDown (infoPtr
, wParam
, lParam
);
508 return HOTKEY_KeyUp (infoPtr
, wParam
, lParam
);
511 return HOTKEY_KillFocus (infoPtr
, (HWND
)wParam
);
514 return HOTKEY_LButtonDown (infoPtr
);
517 return HOTKEY_NCCreate (hwnd
, (LPCREATESTRUCTW
)lParam
);
521 HOTKEY_Paint(infoPtr
, (HDC
)wParam
);
525 return HOTKEY_SetFocus (infoPtr
, (HWND
)wParam
);
528 return HOTKEY_SetFont (infoPtr
, (HFONT
)wParam
, LOWORD(lParam
));
531 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
532 ERR("unknown msg %04x wp=%08lx lp=%08lx\n",
533 uMsg
, wParam
, lParam
);
534 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
541 HOTKEY_Register (void)
545 ZeroMemory (&wndClass
, sizeof(WNDCLASSW
));
546 wndClass
.style
= CS_GLOBALCLASS
;
547 wndClass
.lpfnWndProc
= HOTKEY_WindowProc
;
548 wndClass
.cbClsExtra
= 0;
549 wndClass
.cbWndExtra
= sizeof(HOTKEY_INFO
*);
550 wndClass
.hCursor
= 0;
551 wndClass
.hbrBackground
= 0;
552 wndClass
.lpszClassName
= HOTKEY_CLASSW
;
554 RegisterClassW (&wndClass
);
559 HOTKEY_Unregister (void)
561 UnregisterClassW (HOTKEY_CLASSW
, NULL
);