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
, lstrlenW(infoPtr
->strNone
));
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 /* free hotkey info data */
243 SetWindowLongPtrW (infoPtr
->hwndSelf
, 0, 0);
250 HOTKEY_EraseBackground (const HOTKEY_INFO
*infoPtr
, HDC hdc
)
252 HBRUSH hBrush
, hSolidBrush
= NULL
;
255 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
256 hBrush
= hSolidBrush
= CreateSolidBrush(comctl32_color
.clrBtnFace
);
259 hBrush
= (HBRUSH
)SendMessageW(infoPtr
->hwndNotify
, WM_CTLCOLOREDIT
,
260 (WPARAM
)hdc
, (LPARAM
)infoPtr
->hwndSelf
);
262 hBrush
= hSolidBrush
= CreateSolidBrush(comctl32_color
.clrWindow
);
265 GetClientRect (infoPtr
->hwndSelf
, &rc
);
267 FillRect (hdc
, &rc
, hBrush
);
270 DeleteObject(hSolidBrush
);
276 static inline LRESULT
277 HOTKEY_GetFont (const HOTKEY_INFO
*infoPtr
)
279 return (LRESULT
)infoPtr
->hFont
;
283 HOTKEY_KeyDown (HOTKEY_INFO
*infoPtr
, DWORD key
, DWORD flags
)
288 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
291 TRACE("() Key: %d\n", key
);
293 wOldHotKey
= infoPtr
->HotKey
;
294 bOldMod
= infoPtr
->CurrMod
;
296 /* If any key is Pressed, we have to reset the hotkey in the control */
307 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
308 return DefWindowProcW (infoPtr
->hwndSelf
, WM_KEYDOWN
, key
, flags
);
311 infoPtr
->CurrMod
|= HOTKEYF_SHIFT
;
314 infoPtr
->CurrMod
|= HOTKEYF_CONTROL
;
317 infoPtr
->CurrMod
|= HOTKEYF_ALT
;
321 if(HOTKEY_IsCombInv(infoPtr
))
322 infoPtr
->HotKey
= MAKEWORD(key
, infoPtr
->InvMod
);
324 infoPtr
->HotKey
= MAKEWORD(key
, infoPtr
->CurrMod
);
325 infoPtr
->ScanCode
= flags
;
329 if ((wOldHotKey
!= infoPtr
->HotKey
) || (bOldMod
!= infoPtr
->CurrMod
))
331 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
333 /* send EN_CHANGE notification */
334 SendMessageW(infoPtr
->hwndNotify
, WM_COMMAND
,
335 MAKEWPARAM(GetDlgCtrlID(infoPtr
->hwndSelf
), EN_CHANGE
),
336 (LPARAM
)infoPtr
->hwndSelf
);
344 HOTKEY_KeyUp (HOTKEY_INFO
*infoPtr
, DWORD key
)
348 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
351 TRACE("() Key: %d\n", key
);
353 bOldMod
= infoPtr
->CurrMod
;
358 infoPtr
->CurrMod
&= ~HOTKEYF_SHIFT
;
361 infoPtr
->CurrMod
&= ~HOTKEYF_CONTROL
;
364 infoPtr
->CurrMod
&= ~HOTKEYF_ALT
;
370 if (bOldMod
!= infoPtr
->CurrMod
)
372 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
374 /* send EN_CHANGE notification */
375 SendMessageW(infoPtr
->hwndNotify
, WM_COMMAND
,
376 MAKEWPARAM(GetDlgCtrlID(infoPtr
->hwndSelf
), EN_CHANGE
),
377 (LPARAM
)infoPtr
->hwndSelf
);
385 HOTKEY_KillFocus (HOTKEY_INFO
*infoPtr
)
387 infoPtr
->bFocus
= FALSE
;
395 HOTKEY_LButtonDown (const HOTKEY_INFO
*infoPtr
)
397 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
))
398 SetFocus (infoPtr
->hwndSelf
);
404 static inline LRESULT
405 HOTKEY_NCCreate (HWND hwnd
, const CREATESTRUCTW
*lpcs
)
407 HOTKEY_INFO
*infoPtr
;
408 DWORD dwExStyle
= GetWindowLongW (hwnd
, GWL_EXSTYLE
);
409 SetWindowLongW (hwnd
, GWL_EXSTYLE
,
410 dwExStyle
| WS_EX_CLIENTEDGE
);
412 /* allocate memory for info structure */
413 infoPtr
= Alloc (sizeof(HOTKEY_INFO
));
414 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
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);
422 return DefWindowProcW (infoPtr
->hwndSelf
, WM_NCCREATE
, 0, (LPARAM
)lpcs
);
426 HOTKEY_SetFocus (HOTKEY_INFO
*infoPtr
)
428 infoPtr
->bFocus
= TRUE
;
430 CreateCaret (infoPtr
->hwndSelf
, NULL
, 1, infoPtr
->nHeight
);
431 SetCaretPos (infoPtr
->CaretPos
, GetSystemMetrics(SM_CYBORDER
));
432 ShowCaret (infoPtr
->hwndSelf
);
439 HOTKEY_SetFont (HOTKEY_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
)
445 infoPtr
->hFont
= hFont
;
447 hdc
= GetDC (infoPtr
->hwndSelf
);
449 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
451 GetTextMetricsW (hdc
, &tm
);
452 infoPtr
->nHeight
= tm
.tmHeight
;
455 SelectObject (hdc
, hOldFont
);
456 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
459 InvalidateRect (infoPtr
->hwndSelf
, NULL
, TRUE
);
464 static LRESULT WINAPI
465 HOTKEY_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
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
);
474 return HOTKEY_GetHotKey (infoPtr
);
476 HOTKEY_SetHotKey (infoPtr
, (WORD
)wParam
);
479 HOTKEY_SetRules (infoPtr
, (WORD
)wParam
, (WORD
)lParam
);
484 return HOTKEY_KeyDown (infoPtr
, MapVirtualKeyW(LOBYTE(HIWORD(lParam
)), 1), lParam
);
487 return HOTKEY_Create (infoPtr
, (LPCREATESTRUCTW
)lParam
);
490 return HOTKEY_Destroy (infoPtr
);
493 return HOTKEY_EraseBackground (infoPtr
, (HDC
)wParam
);
496 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
499 return HOTKEY_GetFont (infoPtr
);
503 return HOTKEY_KeyDown (infoPtr
, wParam
, lParam
);
507 return HOTKEY_KeyUp (infoPtr
, wParam
);
510 return HOTKEY_KillFocus (infoPtr
);
513 return HOTKEY_LButtonDown (infoPtr
);
516 return HOTKEY_NCCreate (hwnd
, (LPCREATESTRUCTW
)lParam
);
520 HOTKEY_Paint(infoPtr
, (HDC
)wParam
);
524 return HOTKEY_SetFocus (infoPtr
);
527 return HOTKEY_SetFont (infoPtr
, (HFONT
)wParam
, LOWORD(lParam
));
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
);
540 HOTKEY_Register (void)
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
;
553 RegisterClassW (&wndClass
);
558 HOTKEY_Unregister (void)
560 UnregisterClassW (HOTKEY_CLASSW
, NULL
);