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.
41 #include "wine/debug.h"
42 #include "wine/heap.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(hotkey
);
46 typedef struct tagHOTKEY_INFO
59 WCHAR strNone
[15]; /* hope it's long enough ... */
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))
68 HOTKEY_IsCombInv(const 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
, HDC hdc
, LPCWSTR KeyName
, WORD NameLen
)
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
, hdc
, infoPtr
->strNone
, lstrlenW(infoPtr
->strNone
));
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
, hdc
, KeyName
, NameLen
);
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(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
;
210 HOTKEY_SetHotKey(HOTKEY_INFO
*infoPtr
, WORD hotKey
)
212 infoPtr
->HotKey
= hotKey
;
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
);
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
);
231 HOTKEY_Create (HOTKEY_INFO
*infoPtr
, const CREATESTRUCTW
*lpcs
)
233 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
235 HOTKEY_SetFont(infoPtr
, GetStockObject(SYSTEM_FONT
), 0);
242 HOTKEY_Destroy (HOTKEY_INFO
*infoPtr
)
244 /* free hotkey info data */
245 SetWindowLongPtrW (infoPtr
->hwndSelf
, 0, 0);
252 HOTKEY_EraseBackground (const HOTKEY_INFO
*infoPtr
, HDC hdc
)
254 HBRUSH hBrush
, hSolidBrush
= NULL
;
257 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
258 hBrush
= hSolidBrush
= CreateSolidBrush(comctl32_color
.clrBtnFace
);
261 hBrush
= (HBRUSH
)SendMessageW(infoPtr
->hwndNotify
, WM_CTLCOLOREDIT
,
262 (WPARAM
)hdc
, (LPARAM
)infoPtr
->hwndSelf
);
264 hBrush
= hSolidBrush
= CreateSolidBrush(comctl32_color
.clrWindow
);
267 GetClientRect (infoPtr
->hwndSelf
, &rc
);
269 FillRect (hdc
, &rc
, hBrush
);
272 DeleteObject(hSolidBrush
);
278 static inline LRESULT
279 HOTKEY_GetFont (const HOTKEY_INFO
*infoPtr
)
281 return (LRESULT
)infoPtr
->hFont
;
285 HOTKEY_KeyDown (HOTKEY_INFO
*infoPtr
, DWORD key
, DWORD flags
)
290 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
293 TRACE("() Key: %d\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 */
309 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
310 return DefWindowProcW (infoPtr
->hwndSelf
, WM_KEYDOWN
, key
, flags
);
313 infoPtr
->CurrMod
|= HOTKEYF_SHIFT
;
316 infoPtr
->CurrMod
|= HOTKEYF_CONTROL
;
319 infoPtr
->CurrMod
|= HOTKEYF_ALT
;
323 if(HOTKEY_IsCombInv(infoPtr
))
324 infoPtr
->HotKey
= MAKEWORD(key
, infoPtr
->InvMod
);
326 infoPtr
->HotKey
= MAKEWORD(key
, infoPtr
->CurrMod
);
327 infoPtr
->ScanCode
= flags
;
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
);
346 HOTKEY_KeyUp (HOTKEY_INFO
*infoPtr
, DWORD key
)
350 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
353 TRACE("() Key: %d\n", key
);
355 bOldMod
= infoPtr
->CurrMod
;
360 infoPtr
->CurrMod
&= ~HOTKEYF_SHIFT
;
363 infoPtr
->CurrMod
&= ~HOTKEYF_CONTROL
;
366 infoPtr
->CurrMod
&= ~HOTKEYF_ALT
;
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
);
387 HOTKEY_KillFocus (HOTKEY_INFO
*infoPtr
)
389 infoPtr
->bFocus
= FALSE
;
397 HOTKEY_LButtonDown (const HOTKEY_INFO
*infoPtr
)
399 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
))
400 SetFocus (infoPtr
->hwndSelf
);
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
= heap_alloc_zero (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
);
428 HOTKEY_NCPaint (HWND hwnd
, HRGN region
)
437 theme
= OpenThemeData(NULL
, WC_EDITW
);
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);
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
);
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
);
485 HOTKEY_SetFont (HOTKEY_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
)
491 infoPtr
->hFont
= hFont
;
493 hdc
= GetDC (infoPtr
->hwndSelf
);
495 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
);
497 GetTextMetricsW (hdc
, &tm
);
498 infoPtr
->nHeight
= tm
.tmHeight
;
501 SelectObject (hdc
, hOldFont
);
502 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
505 InvalidateRect (infoPtr
->hwndSelf
, NULL
, TRUE
);
510 static LRESULT WINAPI
511 HOTKEY_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
513 HOTKEY_INFO
*infoPtr
= (HOTKEY_INFO
*)GetWindowLongPtrW (hwnd
, 0);
514 TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
515 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
516 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
520 return HOTKEY_GetHotKey (infoPtr
);
522 HOTKEY_SetHotKey (infoPtr
, (WORD
)wParam
);
525 HOTKEY_SetRules (infoPtr
, (WORD
)wParam
, (WORD
)lParam
);
530 return HOTKEY_KeyDown (infoPtr
, MapVirtualKeyW(LOBYTE(HIWORD(lParam
)), 1), lParam
);
533 return HOTKEY_Create (infoPtr
, (LPCREATESTRUCTW
)lParam
);
536 return HOTKEY_Destroy (infoPtr
);
539 return HOTKEY_EraseBackground (infoPtr
, (HDC
)wParam
);
542 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
545 return HOTKEY_GetFont (infoPtr
);
549 return HOTKEY_KeyDown (infoPtr
, wParam
, lParam
);
553 return HOTKEY_KeyUp (infoPtr
, wParam
);
556 return HOTKEY_KillFocus (infoPtr
);
559 return HOTKEY_LButtonDown (infoPtr
);
562 return HOTKEY_NCCreate (hwnd
, (LPCREATESTRUCTW
)lParam
);
565 return HOTKEY_NCPaint (hwnd
, (HRGN
)wParam
);
569 HOTKEY_Paint(infoPtr
, (HDC
)wParam
);
573 return HOTKEY_SetFocus (infoPtr
);
576 return HOTKEY_SetFont (infoPtr
, (HFONT
)wParam
, LOWORD(lParam
));
579 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
580 ERR("unknown msg %04x wp=%08lx lp=%08lx\n",
581 uMsg
, wParam
, lParam
);
582 return DefWindowProcW (hwnd
, uMsg
, wParam
, lParam
);
589 HOTKEY_Register (void)
593 ZeroMemory (&wndClass
, sizeof(WNDCLASSW
));
594 wndClass
.style
= CS_GLOBALCLASS
;
595 wndClass
.lpfnWndProc
= HOTKEY_WindowProc
;
596 wndClass
.cbClsExtra
= 0;
597 wndClass
.cbWndExtra
= sizeof(HOTKEY_INFO
*);
598 wndClass
.hCursor
= 0;
599 wndClass
.hbrBackground
= 0;
600 wndClass
.lpszClassName
= HOTKEY_CLASSW
;
602 RegisterClassW (&wndClass
);
607 HOTKEY_Unregister (void)
609 UnregisterClassW (HOTKEY_CLASSW
, NULL
);