libport: Remove support for PPC32.
[wine.git] / dlls / comctl32 / hotkey.c
blob58717a2a493b9d59274941742ec0602109ab52d4
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 <string.h>
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wingdi.h"
36 #include "winuser.h"
37 #include "winnls.h"
38 #include "commctrl.h"
39 #include "comctl32.h"
40 #include "wine/debug.h"
41 #include "wine/heap.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(hotkey);
45 typedef struct tagHOTKEY_INFO
47 HWND hwndSelf;
48 HWND hwndNotify;
49 HFONT hFont;
50 BOOL bFocus;
51 INT nHeight;
52 WORD HotKey;
53 WORD InvComb;
54 WORD InvMod;
55 BYTE CurrMod;
56 INT CaretPos;
57 DWORD ScanCode;
58 WCHAR strNone[15]; /* hope it's long enough ... */
59 } HOTKEY_INFO;
61 static const WCHAR HOTKEY_plussep[] = { ' ', '+', ' ' };
62 static LRESULT HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw);
64 #define IsOnlySet(flags) (infoPtr->CurrMod == (flags))
66 static BOOL
67 HOTKEY_IsCombInv(const HOTKEY_INFO *infoPtr)
69 TRACE("(infoPtr=%p)\n", infoPtr);
70 if((infoPtr->InvComb & HKCOMB_NONE) && !infoPtr->CurrMod)
71 return TRUE;
72 if((infoPtr->InvComb & HKCOMB_S) && IsOnlySet(HOTKEYF_SHIFT))
73 return TRUE;
74 if((infoPtr->InvComb & HKCOMB_C) && IsOnlySet(HOTKEYF_CONTROL))
75 return TRUE;
76 if((infoPtr->InvComb & HKCOMB_A) && IsOnlySet(HOTKEYF_ALT))
77 return TRUE;
78 if((infoPtr->InvComb & HKCOMB_SC) &&
79 IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL))
80 return TRUE;
81 if((infoPtr->InvComb & HKCOMB_SA) && IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_ALT))
82 return TRUE;
83 if((infoPtr->InvComb & HKCOMB_CA) &&
84 IsOnlySet(HOTKEYF_CONTROL | HOTKEYF_ALT))
85 return TRUE;
86 if((infoPtr->InvComb & HKCOMB_SCA) &&
87 IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL | HOTKEYF_ALT))
88 return TRUE;
90 TRACE("() Modifiers are valid\n");
91 return FALSE;
93 #undef IsOnlySet
95 static void
96 HOTKEY_DrawHotKey(HOTKEY_INFO *infoPtr, HDC hdc, LPCWSTR KeyName, WORD NameLen)
98 SIZE TextSize;
99 INT nXStart, nYStart;
100 COLORREF clrOldText, clrOldBk;
101 HFONT hFontOld;
103 /* Make a gap from the frame */
104 nXStart = GetSystemMetrics(SM_CXBORDER);
105 nYStart = GetSystemMetrics(SM_CYBORDER);
107 hFontOld = SelectObject(hdc, infoPtr->hFont);
108 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
110 clrOldText = SetTextColor(hdc, comctl32_color.clrGrayText);
111 clrOldBk = SetBkColor(hdc, comctl32_color.clrBtnFace);
113 else
115 clrOldText = SetTextColor(hdc, comctl32_color.clrWindowText);
116 clrOldBk = SetBkColor(hdc, comctl32_color.clrWindow);
119 TextOutW(hdc, nXStart, nYStart, KeyName, NameLen);
121 /* Get the text width for the caret */
122 GetTextExtentPoint32W(hdc, KeyName, NameLen, &TextSize);
123 infoPtr->CaretPos = nXStart + TextSize.cx;
125 SetBkColor(hdc, clrOldBk);
126 SetTextColor(hdc, clrOldText);
127 SelectObject(hdc, hFontOld);
129 /* position the caret */
130 SetCaretPos(infoPtr->CaretPos, nYStart);
133 /* Draw the names of the keys in the control */
134 static void
135 HOTKEY_Refresh(HOTKEY_INFO *infoPtr, HDC hdc)
137 WCHAR KeyName[64];
138 WORD NameLen = 0;
139 BYTE Modifier;
141 TRACE("(infoPtr=%p hdc=%p)\n", infoPtr, hdc);
143 if(!infoPtr->CurrMod && !infoPtr->HotKey) {
144 HOTKEY_DrawHotKey (infoPtr, hdc, infoPtr->strNone, lstrlenW(infoPtr->strNone));
145 return;
148 if(infoPtr->HotKey)
149 Modifier = HIBYTE(infoPtr->HotKey);
150 else if(HOTKEY_IsCombInv(infoPtr))
151 Modifier = infoPtr->InvMod;
152 else
153 Modifier = infoPtr->CurrMod;
155 if(Modifier & HOTKEYF_CONTROL) {
156 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_CONTROL, 0)),
157 KeyName, 64);
158 NameLen = lstrlenW(KeyName);
159 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
160 NameLen += 3;
162 if(Modifier & HOTKEYF_SHIFT) {
163 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_SHIFT, 0)),
164 &KeyName[NameLen], 64 - NameLen);
165 NameLen = lstrlenW(KeyName);
166 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
167 NameLen += 3;
169 if(Modifier & HOTKEYF_ALT) {
170 GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_MENU, 0)),
171 &KeyName[NameLen], 64 - NameLen);
172 NameLen = lstrlenW(KeyName);
173 memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep));
174 NameLen += 3;
177 if(infoPtr->HotKey) {
178 GetKeyNameTextW(infoPtr->ScanCode, &KeyName[NameLen], 64 - NameLen);
179 NameLen = lstrlenW(KeyName);
181 else
182 KeyName[NameLen] = 0;
184 HOTKEY_DrawHotKey (infoPtr, hdc, KeyName, NameLen);
187 static void
188 HOTKEY_Paint(HOTKEY_INFO *infoPtr, HDC hdc)
190 if (hdc)
191 HOTKEY_Refresh(infoPtr, hdc);
192 else {
193 PAINTSTRUCT ps;
194 hdc = BeginPaint (infoPtr->hwndSelf, &ps);
195 HOTKEY_Refresh (infoPtr, hdc);
196 EndPaint (infoPtr->hwndSelf, &ps);
200 static LRESULT
201 HOTKEY_GetHotKey(const HOTKEY_INFO *infoPtr)
203 TRACE("(infoPtr=%p) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr,
204 HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
205 return (LRESULT)infoPtr->HotKey;
208 static void
209 HOTKEY_SetHotKey(HOTKEY_INFO *infoPtr, WORD hotKey)
211 infoPtr->HotKey = hotKey;
212 infoPtr->ScanCode =
213 MAKELPARAM(0, MapVirtualKeyW(LOBYTE(infoPtr->HotKey), 0));
214 TRACE("(infoPtr=%p hotKey=%x) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr,
215 hotKey, HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey));
216 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
219 static void
220 HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WORD invComb, WORD invMod)
222 infoPtr->InvComb = invComb;
223 infoPtr->InvMod = invMod;
224 TRACE("(infoPtr=%p) Invalid Modifiers: 0x%x, If Invalid: 0x%x\n", infoPtr,
225 infoPtr->InvComb, infoPtr->InvMod);
229 static LRESULT
230 HOTKEY_Create (HOTKEY_INFO *infoPtr, const CREATESTRUCTW *lpcs)
232 infoPtr->hwndNotify = lpcs->hwndParent;
234 HOTKEY_SetFont(infoPtr, GetStockObject(SYSTEM_FONT), 0);
236 return 0;
240 static LRESULT
241 HOTKEY_Destroy (HOTKEY_INFO *infoPtr)
243 /* free hotkey info data */
244 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
245 heap_free (infoPtr);
246 return 0;
250 static LRESULT
251 HOTKEY_EraseBackground (const HOTKEY_INFO *infoPtr, HDC hdc)
253 HBRUSH hBrush, hSolidBrush = NULL;
254 RECT rc;
256 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
257 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace);
258 else
260 hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT,
261 (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
262 if (!hBrush)
263 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow);
266 GetClientRect (infoPtr->hwndSelf, &rc);
268 FillRect (hdc, &rc, hBrush);
270 if (hSolidBrush)
271 DeleteObject(hSolidBrush);
273 return -1;
277 static inline LRESULT
278 HOTKEY_GetFont (const HOTKEY_INFO *infoPtr)
280 return (LRESULT)infoPtr->hFont;
283 static LRESULT
284 HOTKEY_KeyDown (HOTKEY_INFO *infoPtr, DWORD key, DWORD flags)
286 WORD wOldHotKey;
287 BYTE bOldMod;
289 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
290 return 0;
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 */
298 infoPtr->HotKey = 0;
300 switch (key)
302 case VK_RETURN:
303 case VK_TAB:
304 case VK_SPACE:
305 case VK_DELETE:
306 case VK_ESCAPE:
307 case VK_BACK:
308 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
309 return DefWindowProcW (infoPtr->hwndSelf, WM_KEYDOWN, key, flags);
311 case VK_SHIFT:
312 infoPtr->CurrMod |= HOTKEYF_SHIFT;
313 break;
314 case VK_CONTROL:
315 infoPtr->CurrMod |= HOTKEYF_CONTROL;
316 break;
317 case VK_MENU:
318 infoPtr->CurrMod |= HOTKEYF_ALT;
319 break;
321 default:
322 if(HOTKEY_IsCombInv(infoPtr))
323 infoPtr->HotKey = MAKEWORD(key, infoPtr->InvMod);
324 else
325 infoPtr->HotKey = MAKEWORD(key, infoPtr->CurrMod);
326 infoPtr->ScanCode = flags;
327 break;
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);
340 return 0;
344 static LRESULT
345 HOTKEY_KeyUp (HOTKEY_INFO *infoPtr, DWORD key)
347 BYTE bOldMod;
349 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
350 return 0;
352 TRACE("() Key: %d\n", key);
354 bOldMod = infoPtr->CurrMod;
356 switch (key)
358 case VK_SHIFT:
359 infoPtr->CurrMod &= ~HOTKEYF_SHIFT;
360 break;
361 case VK_CONTROL:
362 infoPtr->CurrMod &= ~HOTKEYF_CONTROL;
363 break;
364 case VK_MENU:
365 infoPtr->CurrMod &= ~HOTKEYF_ALT;
366 break;
367 default:
368 return 1;
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);
381 return 0;
385 static LRESULT
386 HOTKEY_KillFocus (HOTKEY_INFO *infoPtr)
388 infoPtr->bFocus = FALSE;
389 DestroyCaret ();
391 return 0;
395 static LRESULT
396 HOTKEY_LButtonDown (const HOTKEY_INFO *infoPtr)
398 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED))
399 SetFocus (infoPtr->hwndSelf);
401 return 0;
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 = heap_alloc_zero (sizeof(*infoPtr));
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);
426 static LRESULT
427 HOTKEY_SetFocus (HOTKEY_INFO *infoPtr)
429 infoPtr->bFocus = TRUE;
431 CreateCaret (infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight);
432 SetCaretPos (infoPtr->CaretPos, GetSystemMetrics(SM_CYBORDER));
433 ShowCaret (infoPtr->hwndSelf);
435 return 0;
439 static LRESULT
440 HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw)
442 TEXTMETRICW tm;
443 HDC hdc;
444 HFONT hOldFont = 0;
446 infoPtr->hFont = hFont;
448 hdc = GetDC (infoPtr->hwndSelf);
449 if (infoPtr->hFont)
450 hOldFont = SelectObject (hdc, infoPtr->hFont);
452 GetTextMetricsW (hdc, &tm);
453 infoPtr->nHeight = tm.tmHeight;
455 if (infoPtr->hFont)
456 SelectObject (hdc, hOldFont);
457 ReleaseDC (infoPtr->hwndSelf, hdc);
459 if (redraw)
460 InvalidateRect (infoPtr->hwndSelf, NULL, TRUE);
462 return 0;
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);
472 switch (uMsg)
474 case HKM_GETHOTKEY:
475 return HOTKEY_GetHotKey (infoPtr);
476 case HKM_SETHOTKEY:
477 HOTKEY_SetHotKey (infoPtr, (WORD)wParam);
478 break;
479 case HKM_SETRULES:
480 HOTKEY_SetRules (infoPtr, (WORD)wParam, (WORD)lParam);
481 break;
483 case WM_CHAR:
484 case WM_SYSCHAR:
485 return HOTKEY_KeyDown (infoPtr, MapVirtualKeyW(LOBYTE(HIWORD(lParam)), 1), lParam);
487 case WM_CREATE:
488 return HOTKEY_Create (infoPtr, (LPCREATESTRUCTW)lParam);
490 case WM_DESTROY:
491 return HOTKEY_Destroy (infoPtr);
493 case WM_ERASEBKGND:
494 return HOTKEY_EraseBackground (infoPtr, (HDC)wParam);
496 case WM_GETDLGCODE:
497 return DLGC_WANTCHARS | DLGC_WANTARROWS;
499 case WM_GETFONT:
500 return HOTKEY_GetFont (infoPtr);
502 case WM_KEYDOWN:
503 case WM_SYSKEYDOWN:
504 return HOTKEY_KeyDown (infoPtr, wParam, lParam);
506 case WM_KEYUP:
507 case WM_SYSKEYUP:
508 return HOTKEY_KeyUp (infoPtr, wParam);
510 case WM_KILLFOCUS:
511 return HOTKEY_KillFocus (infoPtr);
513 case WM_LBUTTONDOWN:
514 return HOTKEY_LButtonDown (infoPtr);
516 case WM_NCCREATE:
517 return HOTKEY_NCCreate (hwnd, (LPCREATESTRUCTW)lParam);
519 case WM_PRINTCLIENT:
520 case WM_PAINT:
521 HOTKEY_Paint(infoPtr, (HDC)wParam);
522 return 0;
524 case WM_SETFOCUS:
525 return HOTKEY_SetFocus (infoPtr);
527 case WM_SETFONT:
528 return HOTKEY_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam));
530 default:
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);
536 return 0;
540 void
541 HOTKEY_Register (void)
543 WNDCLASSW wndClass;
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);
558 void
559 HOTKEY_Unregister (void)
561 UnregisterClassW (HOTKEY_CLASSW, NULL);