| /* | |
| * Hotkey control | |
| * | |
| * Copyright 1998, 1999 Eric Kohl | |
| * Copyright 2002 Gyorgy 'Nog' Jeney | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| * | |
| * TODO: | |
| * - What are we meant to do with the WM_CHAR message? | |
| */ | |
| #include <string.h> | |
| #include "winbase.h" | |
| #include "commctrl.h" | |
| #include "comctl32.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(hotkey); | |
| typedef struct tagHOTKEY_INFO | |
| { | |
| HWND hwndSelf; | |
| HFONT hFont; | |
| BOOL bFocus; | |
| INT nHeight; | |
| WORD HotKey; | |
| WORD InvComb; | |
| WORD InvMod; | |
| BYTE CurrMod; | |
| INT CaretPos; | |
| DWORD ScanCode; | |
| WCHAR strNone[15]; /* hope its long enough ... */ | |
| } HOTKEY_INFO; | |
| #define HOTKEY_GetInfoPtr(hwnd) ((HOTKEY_INFO *)GetWindowLongA (hwnd, 0)) | |
| static const WCHAR HOTKEY_plussep[] = { ' ', '+', ' ' }; | |
| #define IsOnlySet(flags) (infoPtr->CurrMod == (flags)) | |
| static BOOL | |
| HOTKEY_IsCombInv(HOTKEY_INFO *infoPtr) | |
| { | |
| TRACE("(infoPtr=%p)\n", infoPtr); | |
| if((infoPtr->InvComb & HKCOMB_NONE) && !infoPtr->CurrMod) | |
| return TRUE; | |
| if((infoPtr->InvComb & HKCOMB_S) && IsOnlySet(HOTKEYF_SHIFT)) | |
| return TRUE; | |
| if((infoPtr->InvComb & HKCOMB_C) && IsOnlySet(HOTKEYF_CONTROL)) | |
| return TRUE; | |
| if((infoPtr->InvComb & HKCOMB_A) && IsOnlySet(HOTKEYF_ALT)) | |
| return TRUE; | |
| if((infoPtr->InvComb & HKCOMB_SC) && | |
| IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL)) | |
| return TRUE; | |
| if((infoPtr->InvComb & HKCOMB_SA) && IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_ALT)) | |
| return TRUE; | |
| if((infoPtr->InvComb & HKCOMB_CA) && | |
| IsOnlySet(HOTKEYF_CONTROL | HOTKEYF_ALT)) | |
| return TRUE; | |
| if((infoPtr->InvComb & HKCOMB_SCA) && | |
| IsOnlySet(HOTKEYF_SHIFT | HOTKEYF_CONTROL | HOTKEYF_ALT)) | |
| return TRUE; | |
| TRACE("() Modifiers are valid\n"); | |
| return FALSE; | |
| } | |
| #undef IsOnlySet | |
| static void | |
| HOTKEY_DrawHotKey(HOTKEY_INFO *infoPtr, LPCWSTR KeyName, WORD NameLen, | |
| LPRECT rc, HDC hdc) | |
| { | |
| SIZE TextSize; | |
| DWORD dwExStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_EXSTYLE); | |
| /* We have to allow some space for the frame to be drawn */ | |
| rc->left += 2; | |
| rc->top++; | |
| DrawTextW(hdc, KeyName, NameLen, rc, DT_LEFT | DT_VCENTER); | |
| rc->left -= 2; | |
| rc->top--; | |
| if(dwExStyle & WS_EX_CLIENTEDGE) | |
| DrawEdge(hdc, rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST); | |
| /* Get the text size and position the caret accordingly */ | |
| GetTextExtentPoint32W (hdc, KeyName, NameLen, &TextSize); | |
| infoPtr->CaretPos = TextSize.cx + 2; | |
| SetCaretPos(infoPtr->CaretPos, 3); | |
| } | |
| /* Draw the names of the keys in the control */ | |
| static void | |
| HOTKEY_Refresh(HOTKEY_INFO *infoPtr, HDC hdc) | |
| { | |
| WCHAR KeyName[sizeof(WCHAR) * 64]; | |
| WORD NameLen = 0; | |
| BYTE Modifier; | |
| RECT rc; | |
| GetClientRect(infoPtr->hwndSelf, &rc); | |
| TRACE("(infoPtr=%p hdc=%x)\n", infoPtr, hdc); | |
| if(!infoPtr->CurrMod && !infoPtr->HotKey) { | |
| HOTKEY_DrawHotKey (infoPtr, infoPtr->strNone, 4, &rc, hdc); | |
| return; | |
| } | |
| if(infoPtr->HotKey) | |
| Modifier = HIBYTE(infoPtr->HotKey); | |
| else if(HOTKEY_IsCombInv(infoPtr)) | |
| Modifier = infoPtr->InvMod; | |
| else | |
| Modifier = infoPtr->CurrMod; | |
| if(Modifier & HOTKEYF_CONTROL) { | |
| GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_CONTROL, 0)), | |
| KeyName, 64); | |
| NameLen = lstrlenW(KeyName); | |
| memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep)); | |
| NameLen += 3; | |
| } | |
| if(Modifier & HOTKEYF_SHIFT) { | |
| GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_SHIFT, 0)), | |
| &KeyName[NameLen], 64 - NameLen); | |
| NameLen = lstrlenW(KeyName); | |
| memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep)); | |
| NameLen += 3; | |
| } | |
| if(Modifier & HOTKEYF_ALT) { | |
| GetKeyNameTextW(MAKELPARAM(0, MapVirtualKeyW(VK_MENU, 0)), | |
| &KeyName[NameLen], 64 - NameLen); | |
| NameLen = lstrlenW(KeyName); | |
| memcpy(&KeyName[NameLen], HOTKEY_plussep, sizeof(HOTKEY_plussep)); | |
| NameLen += 3; | |
| } | |
| if(infoPtr->HotKey) { | |
| GetKeyNameTextW(infoPtr->ScanCode, &KeyName[NameLen], 64 - NameLen); | |
| NameLen = lstrlenW(KeyName); | |
| } | |
| else | |
| KeyName[NameLen] = 0; | |
| HOTKEY_DrawHotKey (infoPtr, KeyName, NameLen, &rc, hdc); | |
| } | |
| static void | |
| HOTKEY_Paint(HOTKEY_INFO *infoPtr, HDC hdc) | |
| { | |
| if (hdc) | |
| HOTKEY_Refresh(infoPtr, hdc); | |
| else { | |
| PAINTSTRUCT ps; | |
| hdc = BeginPaint (infoPtr->hwndSelf, &ps); | |
| HOTKEY_Refresh (infoPtr, hdc); | |
| EndPaint (infoPtr->hwndSelf, &ps); | |
| } | |
| } | |
| static LRESULT | |
| HOTKEY_GetHotKey(HOTKEY_INFO *infoPtr) | |
| { | |
| TRACE("(infoPtr=%p) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr, | |
| HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey)); | |
| return (LRESULT)infoPtr->HotKey; | |
| } | |
| static void | |
| HOTKEY_SetHotKey(HOTKEY_INFO *infoPtr, WPARAM wParam) | |
| { | |
| infoPtr->HotKey = (WORD)wParam; | |
| infoPtr->ScanCode = | |
| MAKELPARAM(0, MapVirtualKeyW(LOBYTE(infoPtr->HotKey), 0)); | |
| TRACE("(infoPtr=%p wParam=%x) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr, | |
| wParam, HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey)); | |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); | |
| } | |
| static void | |
| HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| infoPtr->InvComb = (WORD)wParam; | |
| infoPtr->InvMod = (WORD)lParam; | |
| TRACE("(infoPtr=%p) Invalid Modifers: 0x%x, If Invalid: 0x%x\n", infoPtr, | |
| infoPtr->InvComb, infoPtr->InvMod); | |
| } | |
| /* << HOTKEY_Char >> */ | |
| static LRESULT | |
| HOTKEY_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) | |
| { | |
| HOTKEY_INFO *infoPtr; | |
| TEXTMETRICW tm; | |
| HDC hdc; | |
| /* allocate memory for info structure */ | |
| infoPtr = (HOTKEY_INFO *)COMCTL32_Alloc (sizeof(HOTKEY_INFO)); | |
| SetWindowLongW (hwnd, 0, (DWORD)infoPtr); | |
| /* initialize info structure */ | |
| infoPtr->HotKey = infoPtr->InvComb = infoPtr->InvMod = infoPtr->CurrMod = 0; | |
| infoPtr->CaretPos = 2; | |
| infoPtr->hwndSelf = hwnd; | |
| LoadStringW(COMCTL32_hModule, HKY_NONE, infoPtr->strNone, 15); | |
| /* get default font height */ | |
| hdc = GetDC (hwnd); | |
| GetTextMetricsW (hdc, &tm); | |
| infoPtr->nHeight = tm.tmHeight; | |
| ReleaseDC (hwnd, hdc); | |
| return 0; | |
| } | |
| static LRESULT | |
| HOTKEY_Destroy (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| HWND hwnd = infoPtr->hwndSelf; | |
| /* free hotkey info data */ | |
| COMCTL32_Free (infoPtr); | |
| SetWindowLongW (hwnd, 0, 0); | |
| return 0; | |
| } | |
| static LRESULT | |
| HOTKEY_EraseBackground (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| HBRUSH hBrush; | |
| RECT rc; | |
| hBrush = | |
| (HBRUSH)SendMessageW (GetParent (infoPtr->hwndSelf), WM_CTLCOLOREDIT, | |
| wParam, (LPARAM)infoPtr->hwndSelf); | |
| if (hBrush) | |
| hBrush = (HBRUSH)GetStockObject (WHITE_BRUSH); | |
| GetClientRect (infoPtr->hwndSelf, &rc); | |
| FillRect ((HDC)wParam, &rc, hBrush); | |
| return -1; | |
| } | |
| inline static LRESULT | |
| HOTKEY_GetFont (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| return infoPtr->hFont; | |
| } | |
| static LRESULT | |
| HOTKEY_KeyDown (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| TRACE("() Key: %d\n", wParam); | |
| /* If any key is Pressed, we have to reset the hotkey in the control */ | |
| infoPtr->HotKey = 0; | |
| switch (wParam) { | |
| case VK_RETURN: | |
| case VK_TAB: | |
| case VK_SPACE: | |
| case VK_DELETE: | |
| case VK_ESCAPE: | |
| case VK_BACK: | |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); | |
| return DefWindowProcW (infoPtr->hwndSelf, WM_KEYDOWN, wParam, | |
| lParam); | |
| case VK_SHIFT: | |
| infoPtr->CurrMod |= HOTKEYF_SHIFT; | |
| break; | |
| case VK_CONTROL: | |
| infoPtr->CurrMod |= HOTKEYF_CONTROL; | |
| break; | |
| case VK_MENU: | |
| infoPtr->CurrMod |= HOTKEYF_ALT; | |
| break; | |
| default: | |
| if(HOTKEY_IsCombInv(infoPtr)) | |
| infoPtr->HotKey = MAKEWORD(wParam, infoPtr->InvMod); | |
| else | |
| infoPtr->HotKey = MAKEWORD(wParam, infoPtr->CurrMod); | |
| infoPtr->ScanCode = lParam; | |
| break; | |
| } | |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); | |
| return 0; | |
| } | |
| static LRESULT | |
| HOTKEY_KeyUp (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| TRACE("() Key: %d\n", wParam); | |
| switch (wParam) { | |
| case VK_SHIFT: | |
| infoPtr->CurrMod &= ~HOTKEYF_SHIFT; | |
| break; | |
| case VK_CONTROL: | |
| infoPtr->CurrMod &= ~HOTKEYF_CONTROL; | |
| break; | |
| case VK_MENU: | |
| infoPtr->CurrMod &= ~HOTKEYF_ALT; | |
| break; | |
| default: | |
| return 1; | |
| } | |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); | |
| return 0; | |
| } | |
| static LRESULT | |
| HOTKEY_KillFocus (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| infoPtr->bFocus = FALSE; | |
| DestroyCaret (); | |
| return 0; | |
| } | |
| static LRESULT | |
| HOTKEY_LButtonDown (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| SetFocus (infoPtr->hwndSelf); | |
| return 0; | |
| } | |
| inline static LRESULT | |
| HOTKEY_NCCreate (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| DWORD dwExStyle = GetWindowLongW (infoPtr->hwndSelf, GWL_EXSTYLE); | |
| SetWindowLongW (infoPtr->hwndSelf, GWL_EXSTYLE, | |
| dwExStyle | WS_EX_CLIENTEDGE); | |
| return DefWindowProcW (infoPtr->hwndSelf, WM_NCCREATE, wParam, lParam); | |
| } | |
| static LRESULT | |
| HOTKEY_SetFocus (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| infoPtr->bFocus = TRUE; | |
| CreateCaret (infoPtr->hwndSelf, (HBITMAP)0, 1, infoPtr->nHeight - 2); | |
| SetCaretPos (infoPtr->CaretPos, 3); | |
| ShowCaret (infoPtr->hwndSelf); | |
| return 0; | |
| } | |
| inline static LRESULT | |
| HOTKEY_SetFont (HOTKEY_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | |
| { | |
| TEXTMETRICW tm; | |
| HDC hdc; | |
| HFONT hOldFont = 0; | |
| infoPtr->hFont = (HFONT)wParam; | |
| hdc = GetDC (infoPtr->hwndSelf); | |
| if (infoPtr->hFont) | |
| hOldFont = SelectObject (hdc, infoPtr->hFont); | |
| GetTextMetricsW (hdc, &tm); | |
| infoPtr->nHeight = tm.tmHeight; | |
| if (infoPtr->hFont) | |
| SelectObject (hdc, hOldFont); | |
| ReleaseDC (infoPtr->hwndSelf, hdc); | |
| if (LOWORD(lParam)) | |
| InvalidateRect (infoPtr->hwndSelf, NULL, TRUE); | |
| return 0; | |
| } | |
| static LRESULT WINAPI | |
| HOTKEY_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| HOTKEY_INFO *infoPtr = HOTKEY_GetInfoPtr (hwnd); | |
| TRACE("hwnd=%x msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam); | |
| if (!infoPtr && (uMsg != WM_CREATE)) | |
| return DefWindowProcW (hwnd, uMsg, wParam, lParam); | |
| switch (uMsg) | |
| { | |
| case HKM_GETHOTKEY: | |
| return HOTKEY_GetHotKey (infoPtr); | |
| case HKM_SETHOTKEY: | |
| HOTKEY_SetHotKey (infoPtr, wParam); | |
| break; | |
| case HKM_SETRULES: | |
| HOTKEY_SetRules (infoPtr, wParam, lParam); | |
| break; | |
| /* case WM_CHAR: */ | |
| case WM_CREATE: | |
| return HOTKEY_Create (hwnd, wParam, lParam); | |
| case WM_DESTROY: | |
| return HOTKEY_Destroy (infoPtr, wParam, lParam); | |
| case WM_ERASEBKGND: | |
| return HOTKEY_EraseBackground (infoPtr, wParam, lParam); | |
| case WM_GETDLGCODE: | |
| return DLGC_WANTCHARS | DLGC_WANTARROWS; | |
| case WM_GETFONT: | |
| return HOTKEY_GetFont (infoPtr, wParam, lParam); | |
| case WM_KEYDOWN: | |
| case WM_SYSKEYDOWN: | |
| return HOTKEY_KeyDown (infoPtr, wParam, lParam); | |
| case WM_KEYUP: | |
| case WM_SYSKEYUP: | |
| return HOTKEY_KeyUp (infoPtr, wParam, lParam); | |
| case WM_KILLFOCUS: | |
| return HOTKEY_KillFocus (infoPtr, wParam, lParam); | |
| case WM_LBUTTONDOWN: | |
| return HOTKEY_LButtonDown (infoPtr, wParam, lParam); | |
| case WM_NCCREATE: | |
| return HOTKEY_NCCreate (infoPtr, wParam, lParam); | |
| case WM_PAINT: | |
| HOTKEY_Paint(infoPtr, (HDC)wParam); | |
| return 0; | |
| case WM_SETFOCUS: | |
| return HOTKEY_SetFocus (infoPtr, wParam, lParam); | |
| case WM_SETFONT: | |
| return HOTKEY_SetFont (infoPtr, wParam, lParam); | |
| /* case WM_SYSCHAR: */ | |
| default: | |
| if ((uMsg >= WM_USER) && (uMsg < WM_APP)) | |
| ERR("unknown msg %04x wp=%08x lp=%08lx\n", | |
| uMsg, wParam, lParam); | |
| return DefWindowProcW (hwnd, uMsg, wParam, lParam); | |
| } | |
| return 0; | |
| } | |
| void | |
| HOTKEY_Register (void) | |
| { | |
| WNDCLASSW wndClass; | |
| ZeroMemory (&wndClass, sizeof(WNDCLASSW)); | |
| wndClass.style = CS_GLOBALCLASS; | |
| wndClass.lpfnWndProc = (WNDPROC)HOTKEY_WindowProc; | |
| wndClass.cbClsExtra = 0; | |
| wndClass.cbWndExtra = sizeof(HOTKEY_INFO *); | |
| wndClass.hCursor = 0; | |
| wndClass.hbrBackground = 0; | |
| wndClass.lpszClassName = HOTKEY_CLASSW; | |
| RegisterClassW (&wndClass); | |
| } | |
| void | |
| HOTKEY_Unregister (void) | |
| { | |
| UnregisterClassW (HOTKEY_CLASSW, (HINSTANCE)NULL); | |
| } |