| /* |
| * 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=%p)\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 (LRESULT)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=%p 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); |
| } |