1 /* 2 * Hotkey control 3 * 4 * Copyright 1998, 1999 Eric Kohl 5 * Copyright 2002 Gyorgy 'Nog' Jeney 6 * Copyright 2004 Robert Shearman 7 * 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. 12 * 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. 17 * 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 21 * 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. 24 * 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. 28 * 29 */ 30 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" 42 43 WINE_DEFAULT_DEBUG_CHANNEL(hotkey); 44 45 typedef struct tagHOTKEY_INFO 46 { 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; 60 61 static const WCHAR HOTKEY_plussep[] = { ' ', '+', ' ' }; 62 static LRESULT HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw); 63 64 #define IsOnlySet(flags) (infoPtr->CurrMod == (flags)) 65 66 static BOOL 67 HOTKEY_IsCombInv(const HOTKEY_INFO *infoPtr) 68 { 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; 89 90 TRACE("() Modifiers are valid\n"); 91 return FALSE; 92 } 93 #undef IsOnlySet 94 95 static void 96 HOTKEY_DrawHotKey(HOTKEY_INFO *infoPtr, HDC hdc, LPCWSTR KeyName, WORD NameLen) 97 { 98 SIZE TextSize; 99 INT nXStart, nYStart; 100 COLORREF clrOldText, clrOldBk; 101 HFONT hFontOld; 102 103 /* Make a gap from the frame */ 104 nXStart = GetSystemMetrics(SM_CXBORDER); 105 nYStart = GetSystemMetrics(SM_CYBORDER); 106 107 hFontOld = SelectObject(hdc, infoPtr->hFont); 108 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) 109 { 110 clrOldText = SetTextColor(hdc, comctl32_color.clrGrayText); 111 clrOldBk = SetBkColor(hdc, comctl32_color.clrBtnFace); 112 } 113 else 114 { 115 clrOldText = SetTextColor(hdc, comctl32_color.clrWindowText); 116 clrOldBk = SetBkColor(hdc, comctl32_color.clrWindow); 117 } 118 119 TextOutW(hdc, nXStart, nYStart, KeyName, NameLen); 120 121 /* Get the text width for the caret */ 122 GetTextExtentPoint32W(hdc, KeyName, NameLen, &TextSize); 123 infoPtr->CaretPos = nXStart + TextSize.cx; 124 125 SetBkColor(hdc, clrOldBk); 126 SetTextColor(hdc, clrOldText); 127 SelectObject(hdc, hFontOld); 128 129 /* position the caret */ 130 SetCaretPos(infoPtr->CaretPos, nYStart); 131 } 132 133 /* Draw the names of the keys in the control */ 134 static void 135 HOTKEY_Refresh(HOTKEY_INFO *infoPtr, HDC hdc) 136 { 137 WCHAR KeyName[64]; 138 WORD NameLen = 0; 139 BYTE Modifier; 140 141 TRACE("(infoPtr=%p hdc=%p)\n", infoPtr, hdc); 142 143 if(!infoPtr->CurrMod && !infoPtr->HotKey) { 144 HOTKEY_DrawHotKey (infoPtr, hdc, infoPtr->strNone, lstrlenW(infoPtr->strNone)); 145 return; 146 } 147 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; 154 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; 161 } 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; 168 } 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; 175 } 176 177 if(infoPtr->HotKey) { 178 GetKeyNameTextW(infoPtr->ScanCode, &KeyName[NameLen], 64 - NameLen); 179 NameLen = lstrlenW(KeyName); 180 } 181 else 182 KeyName[NameLen] = 0; 183 184 HOTKEY_DrawHotKey (infoPtr, hdc, KeyName, NameLen); 185 } 186 187 static void 188 HOTKEY_Paint(HOTKEY_INFO *infoPtr, HDC hdc) 189 { 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); 197 } 198 } 199 200 static LRESULT 201 HOTKEY_GetHotKey(const HOTKEY_INFO *infoPtr) 202 { 203 TRACE("(infoPtr=%p) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr, 204 HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey)); 205 return (LRESULT)infoPtr->HotKey; 206 } 207 208 static void 209 HOTKEY_SetHotKey(HOTKEY_INFO *infoPtr, WORD hotKey) 210 { 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); 217 } 218 219 static void 220 HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WORD invComb, WORD invMod) 221 { 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); 226 } 227 228 229 static LRESULT 230 HOTKEY_Create (HOTKEY_INFO *infoPtr, const CREATESTRUCTW *lpcs) 231 { 232 infoPtr->hwndNotify = lpcs->hwndParent; 233 234 HOTKEY_SetFont(infoPtr, GetStockObject(SYSTEM_FONT), 0); 235 236 return 0; 237 } 238 239 240 static LRESULT 241 HOTKEY_Destroy (HOTKEY_INFO *infoPtr) 242 { 243 /* free hotkey info data */ 244 SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); 245 heap_free (infoPtr); 246 return 0; 247 } 248 249 250 static LRESULT 251 HOTKEY_EraseBackground (const HOTKEY_INFO *infoPtr, HDC hdc) 252 { 253 HBRUSH hBrush, hSolidBrush = NULL; 254 RECT rc; 255 256 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) 257 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace); 258 else 259 { 260 hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT, 261 (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf); 262 if (!hBrush) 263 hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow); 264 } 265 266 GetClientRect (infoPtr->hwndSelf, &rc); 267 268 FillRect (hdc, &rc, hBrush); 269 270 if (hSolidBrush) 271 DeleteObject(hSolidBrush); 272 273 return -1; 274 } 275 276 277 static inline LRESULT 278 HOTKEY_GetFont (const HOTKEY_INFO *infoPtr) 279 { 280 return (LRESULT)infoPtr->hFont; 281 } 282 283 static LRESULT 284 HOTKEY_KeyDown (HOTKEY_INFO *infoPtr, DWORD key, DWORD flags) 285 { 286 WORD wOldHotKey; 287 BYTE bOldMod; 288 289 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) 290 return 0; 291 292 TRACE("() Key: %d\n", key); 293 294 wOldHotKey = infoPtr->HotKey; 295 bOldMod = infoPtr->CurrMod; 296 297 /* If any key is Pressed, we have to reset the hotkey in the control */ 298 infoPtr->HotKey = 0; 299 300 switch (key) 301 { 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); 310 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; 320 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; 328 } 329 330 if ((wOldHotKey != infoPtr->HotKey) || (bOldMod != infoPtr->CurrMod)) 331 { 332 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 333 334 /* send EN_CHANGE notification */ 335 SendMessageW(infoPtr->hwndNotify, WM_COMMAND, 336 MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE), 337 (LPARAM)infoPtr->hwndSelf); 338 } 339 340 return 0; 341 } 342 343 344 static LRESULT 345 HOTKEY_KeyUp (HOTKEY_INFO *infoPtr, DWORD key) 346 { 347 BYTE bOldMod; 348 349 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) 350 return 0; 351 352 TRACE("() Key: %d\n", key); 353 354 bOldMod = infoPtr->CurrMod; 355 356 switch (key) 357 { 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; 369 } 370 371 if (bOldMod != infoPtr->CurrMod) 372 { 373 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 374 375 /* send EN_CHANGE notification */ 376 SendMessageW(infoPtr->hwndNotify, WM_COMMAND, 377 MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE), 378 (LPARAM)infoPtr->hwndSelf); 379 } 380 381 return 0; 382 } 383 384 385 static LRESULT 386 HOTKEY_KillFocus (HOTKEY_INFO *infoPtr) 387 { 388 infoPtr->bFocus = FALSE; 389 DestroyCaret (); 390 391 return 0; 392 } 393 394 395 static LRESULT 396 HOTKEY_LButtonDown (const HOTKEY_INFO *infoPtr) 397 { 398 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)) 399 SetFocus (infoPtr->hwndSelf); 400 401 return 0; 402 } 403 404 405 static inline LRESULT 406 HOTKEY_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs) 407 { 408 HOTKEY_INFO *infoPtr; 409 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE); 410 SetWindowLongW (hwnd, GWL_EXSTYLE, 411 dwExStyle | WS_EX_CLIENTEDGE); 412 413 /* allocate memory for info structure */ 414 infoPtr = heap_alloc_zero (sizeof(*infoPtr)); 415 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); 416 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); 422 423 return DefWindowProcW (infoPtr->hwndSelf, WM_NCCREATE, 0, (LPARAM)lpcs); 424 } 425 426 static LRESULT 427 HOTKEY_SetFocus (HOTKEY_INFO *infoPtr) 428 { 429 infoPtr->bFocus = TRUE; 430 431 CreateCaret (infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight); 432 SetCaretPos (infoPtr->CaretPos, GetSystemMetrics(SM_CYBORDER)); 433 ShowCaret (infoPtr->hwndSelf); 434 435 return 0; 436 } 437 438 439 static LRESULT 440 HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw) 441 { 442 TEXTMETRICW tm; 443 HDC hdc; 444 HFONT hOldFont = 0; 445 446 infoPtr->hFont = hFont; 447 448 hdc = GetDC (infoPtr->hwndSelf); 449 if (infoPtr->hFont) 450 hOldFont = SelectObject (hdc, infoPtr->hFont); 451 452 GetTextMetricsW (hdc, &tm); 453 infoPtr->nHeight = tm.tmHeight; 454 455 if (infoPtr->hFont) 456 SelectObject (hdc, hOldFont); 457 ReleaseDC (infoPtr->hwndSelf, hdc); 458 459 if (redraw) 460 InvalidateRect (infoPtr->hwndSelf, NULL, TRUE); 461 462 return 0; 463 } 464 465 static LRESULT WINAPI 466 HOTKEY_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 467 { 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) 473 { 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; 482 483 case WM_CHAR: 484 case WM_SYSCHAR: 485 return HOTKEY_KeyDown (infoPtr, MapVirtualKeyW(LOBYTE(HIWORD(lParam)), 1), lParam); 486 487 case WM_CREATE: 488 return HOTKEY_Create (infoPtr, (LPCREATESTRUCTW)lParam); 489 490 case WM_DESTROY: 491 return HOTKEY_Destroy (infoPtr); 492 493 case WM_ERASEBKGND: 494 return HOTKEY_EraseBackground (infoPtr, (HDC)wParam); 495 496 case WM_GETDLGCODE: 497 return DLGC_WANTCHARS | DLGC_WANTARROWS; 498 499 case WM_GETFONT: 500 return HOTKEY_GetFont (infoPtr); 501 502 case WM_KEYDOWN: 503 case WM_SYSKEYDOWN: 504 return HOTKEY_KeyDown (infoPtr, wParam, lParam); 505 506 case WM_KEYUP: 507 case WM_SYSKEYUP: 508 return HOTKEY_KeyUp (infoPtr, wParam); 509 510 case WM_KILLFOCUS: 511 return HOTKEY_KillFocus (infoPtr); 512 513 case WM_LBUTTONDOWN: 514 return HOTKEY_LButtonDown (infoPtr); 515 516 case WM_NCCREATE: 517 return HOTKEY_NCCreate (hwnd, (LPCREATESTRUCTW)lParam); 518 519 case WM_PRINTCLIENT: 520 case WM_PAINT: 521 HOTKEY_Paint(infoPtr, (HDC)wParam); 522 return 0; 523 524 case WM_SETFOCUS: 525 return HOTKEY_SetFocus (infoPtr); 526 527 case WM_SETFONT: 528 return HOTKEY_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam)); 529 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); 535 } 536 return 0; 537 } 538 539 540 void 541 HOTKEY_Register (void) 542 { 543 WNDCLASSW wndClass; 544 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; 553 554 RegisterClassW (&wndClass); 555 } 556 557 558 void 559 HOTKEY_Unregister (void) 560 { 561 UnregisterClassW (HOTKEY_CLASSW, NULL); 562 } 563