| /* |
| * Hotkey control |
| * |
| * Copyright 1998, 1999 Eric Kohl |
| * Copyright 2002 Gyorgy 'Nog' Jeney |
| * Copyright 2004 Robert Shearman |
| * |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| * |
| * This code was audited for completeness against the documented features |
| * of Comctl32.dll version 6.0 on Sep. 21, 2004, by Robert Shearman. |
| * |
| * Unless otherwise noted, we believe this code to be complete, as per |
| * the specification mentioned above. |
| * If you discover missing features or bugs please note them below. |
| * |
| */ |
| |
| #include <stdarg.h> |
| #include <string.h> |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| #include "winnls.h" |
| #include "commctrl.h" |
| #include "comctl32.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(hotkey); |
| |
| typedef struct tagHOTKEY_INFO |
| { |
| HWND hwndSelf; |
| HWND hwndNotify; |
| HFONT hFont; |
| BOOL bFocus; |
| INT nHeight; |
| WORD HotKey; |
| WORD InvComb; |
| WORD InvMod; |
| BYTE CurrMod; |
| INT CaretPos; |
| DWORD ScanCode; |
| WCHAR strNone[15]; /* hope it's long enough ... */ |
| } HOTKEY_INFO; |
| |
| static const WCHAR HOTKEY_plussep[] = { ' ', '+', ' ' }; |
| static LRESULT HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw); |
| |
| #define IsOnlySet(flags) (infoPtr->CurrMod == (flags)) |
| |
| static BOOL |
| HOTKEY_IsCombInv(const 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, HDC hdc, LPCWSTR KeyName, WORD NameLen) |
| { |
| SIZE TextSize; |
| INT nXStart, nYStart; |
| COLORREF clrOldText, clrOldBk; |
| HFONT hFontOld; |
| |
| /* Make a gap from the frame */ |
| nXStart = GetSystemMetrics(SM_CXBORDER); |
| nYStart = GetSystemMetrics(SM_CYBORDER); |
| |
| hFontOld = SelectObject(hdc, infoPtr->hFont); |
| if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) |
| { |
| clrOldText = SetTextColor(hdc, comctl32_color.clrGrayText); |
| clrOldBk = SetBkColor(hdc, comctl32_color.clrBtnFace); |
| } |
| else |
| { |
| clrOldText = SetTextColor(hdc, comctl32_color.clrWindowText); |
| clrOldBk = SetBkColor(hdc, comctl32_color.clrWindow); |
| } |
| |
| TextOutW(hdc, nXStart, nYStart, KeyName, NameLen); |
| |
| /* Get the text width for the caret */ |
| GetTextExtentPoint32W(hdc, KeyName, NameLen, &TextSize); |
| infoPtr->CaretPos = nXStart + TextSize.cx; |
| |
| SetBkColor(hdc, clrOldBk); |
| SetTextColor(hdc, clrOldText); |
| SelectObject(hdc, hFontOld); |
| |
| /* position the caret */ |
| SetCaretPos(infoPtr->CaretPos, nYStart); |
| } |
| |
| /* Draw the names of the keys in the control */ |
| static void |
| HOTKEY_Refresh(HOTKEY_INFO *infoPtr, HDC hdc) |
| { |
| WCHAR KeyName[64]; |
| WORD NameLen = 0; |
| BYTE Modifier; |
| |
| TRACE("(infoPtr=%p hdc=%p)\n", infoPtr, hdc); |
| |
| if(!infoPtr->CurrMod && !infoPtr->HotKey) { |
| HOTKEY_DrawHotKey (infoPtr, hdc, infoPtr->strNone, 4); |
| 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, hdc, KeyName, NameLen); |
| } |
| |
| 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(const 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, WORD hotKey) |
| { |
| infoPtr->HotKey = hotKey; |
| infoPtr->ScanCode = |
| MAKELPARAM(0, MapVirtualKeyW(LOBYTE(infoPtr->HotKey), 0)); |
| TRACE("(infoPtr=%p hotKey=%x) Modifiers: 0x%x, Virtual Key: %d\n", infoPtr, |
| hotKey, HIBYTE(infoPtr->HotKey), LOBYTE(infoPtr->HotKey)); |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); |
| } |
| |
| static void |
| HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WORD invComb, WORD invMod) |
| { |
| infoPtr->InvComb = invComb; |
| infoPtr->InvMod = invMod; |
| TRACE("(infoPtr=%p) Invalid Modifers: 0x%x, If Invalid: 0x%x\n", infoPtr, |
| infoPtr->InvComb, infoPtr->InvMod); |
| } |
| |
| |
| static LRESULT |
| HOTKEY_Create (HOTKEY_INFO *infoPtr, const CREATESTRUCTW *lpcs) |
| { |
| infoPtr->hwndNotify = lpcs->hwndParent; |
| |
| HOTKEY_SetFont(infoPtr, GetStockObject(SYSTEM_FONT), 0); |
| |
| return 0; |
| } |
| |
| |
| static LRESULT |
| HOTKEY_Destroy (HOTKEY_INFO *infoPtr) |
| { |
| /* free hotkey info data */ |
| SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); |
| Free (infoPtr); |
| return 0; |
| } |
| |
| |
| static LRESULT |
| HOTKEY_EraseBackground (const HOTKEY_INFO *infoPtr, HDC hdc) |
| { |
| HBRUSH hBrush, hSolidBrush = NULL; |
| RECT rc; |
| |
| if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) |
| hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrBtnFace); |
| else |
| { |
| hBrush = (HBRUSH)SendMessageW(infoPtr->hwndNotify, WM_CTLCOLOREDIT, |
| (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf); |
| if (!hBrush) |
| hBrush = hSolidBrush = CreateSolidBrush(comctl32_color.clrWindow); |
| } |
| |
| GetClientRect (infoPtr->hwndSelf, &rc); |
| |
| FillRect (hdc, &rc, hBrush); |
| |
| if (hSolidBrush) |
| DeleteObject(hSolidBrush); |
| |
| return -1; |
| } |
| |
| |
| static inline LRESULT |
| HOTKEY_GetFont (const HOTKEY_INFO *infoPtr) |
| { |
| return (LRESULT)infoPtr->hFont; |
| } |
| |
| static LRESULT |
| HOTKEY_KeyDown (HOTKEY_INFO *infoPtr, DWORD key, DWORD flags) |
| { |
| WORD wOldHotKey; |
| BYTE bOldMod; |
| |
| if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) |
| return 0; |
| |
| TRACE("() Key: %d\n", key); |
| |
| wOldHotKey = infoPtr->HotKey; |
| bOldMod = infoPtr->CurrMod; |
| |
| /* If any key is Pressed, we have to reset the hotkey in the control */ |
| infoPtr->HotKey = 0; |
| |
| switch (key) |
| { |
| 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, key, flags); |
| |
| 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(key, infoPtr->InvMod); |
| else |
| infoPtr->HotKey = MAKEWORD(key, infoPtr->CurrMod); |
| infoPtr->ScanCode = flags; |
| break; |
| } |
| |
| if ((wOldHotKey != infoPtr->HotKey) || (bOldMod != infoPtr->CurrMod)) |
| { |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); |
| |
| /* send EN_CHANGE notification */ |
| SendMessageW(infoPtr->hwndNotify, WM_COMMAND, |
| MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE), |
| (LPARAM)infoPtr->hwndSelf); |
| } |
| |
| return 0; |
| } |
| |
| |
| static LRESULT |
| HOTKEY_KeyUp (HOTKEY_INFO *infoPtr, DWORD key) |
| { |
| BYTE bOldMod; |
| |
| if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) |
| return 0; |
| |
| TRACE("() Key: %d\n", key); |
| |
| bOldMod = infoPtr->CurrMod; |
| |
| switch (key) |
| { |
| 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; |
| } |
| |
| if (bOldMod != infoPtr->CurrMod) |
| { |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); |
| |
| /* send EN_CHANGE notification */ |
| SendMessageW(infoPtr->hwndNotify, WM_COMMAND, |
| MAKEWPARAM(GetDlgCtrlID(infoPtr->hwndSelf), EN_CHANGE), |
| (LPARAM)infoPtr->hwndSelf); |
| } |
| |
| return 0; |
| } |
| |
| |
| static LRESULT |
| HOTKEY_KillFocus (HOTKEY_INFO *infoPtr) |
| { |
| infoPtr->bFocus = FALSE; |
| DestroyCaret (); |
| |
| return 0; |
| } |
| |
| |
| static LRESULT |
| HOTKEY_LButtonDown (const HOTKEY_INFO *infoPtr) |
| { |
| if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)) |
| SetFocus (infoPtr->hwndSelf); |
| |
| return 0; |
| } |
| |
| |
| static inline LRESULT |
| HOTKEY_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs) |
| { |
| HOTKEY_INFO *infoPtr; |
| DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE); |
| SetWindowLongW (hwnd, GWL_EXSTYLE, |
| dwExStyle | WS_EX_CLIENTEDGE); |
| |
| /* allocate memory for info structure */ |
| infoPtr = Alloc (sizeof(HOTKEY_INFO)); |
| SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); |
| |
| /* initialize info structure */ |
| infoPtr->HotKey = infoPtr->InvComb = infoPtr->InvMod = infoPtr->CurrMod = 0; |
| infoPtr->CaretPos = GetSystemMetrics(SM_CXBORDER); |
| infoPtr->hwndSelf = hwnd; |
| LoadStringW(COMCTL32_hModule, HKY_NONE, infoPtr->strNone, 15); |
| |
| return DefWindowProcW (infoPtr->hwndSelf, WM_NCCREATE, 0, (LPARAM)lpcs); |
| } |
| |
| static LRESULT |
| HOTKEY_SetFocus (HOTKEY_INFO *infoPtr) |
| { |
| infoPtr->bFocus = TRUE; |
| |
| CreateCaret (infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight); |
| SetCaretPos (infoPtr->CaretPos, GetSystemMetrics(SM_CYBORDER)); |
| ShowCaret (infoPtr->hwndSelf); |
| |
| return 0; |
| } |
| |
| |
| static LRESULT |
| HOTKEY_SetFont (HOTKEY_INFO *infoPtr, HFONT hFont, BOOL redraw) |
| { |
| TEXTMETRICW tm; |
| HDC hdc; |
| HFONT hOldFont = 0; |
| |
| infoPtr->hFont = hFont; |
| |
| 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 (redraw) |
| InvalidateRect (infoPtr->hwndSelf, NULL, TRUE); |
| |
| return 0; |
| } |
| |
| static LRESULT WINAPI |
| HOTKEY_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
| { |
| HOTKEY_INFO *infoPtr = (HOTKEY_INFO *)GetWindowLongPtrW (hwnd, 0); |
| TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd, uMsg, wParam, lParam); |
| if (!infoPtr && (uMsg != WM_NCCREATE)) |
| return DefWindowProcW (hwnd, uMsg, wParam, lParam); |
| switch (uMsg) |
| { |
| case HKM_GETHOTKEY: |
| return HOTKEY_GetHotKey (infoPtr); |
| case HKM_SETHOTKEY: |
| HOTKEY_SetHotKey (infoPtr, (WORD)wParam); |
| break; |
| case HKM_SETRULES: |
| HOTKEY_SetRules (infoPtr, (WORD)wParam, (WORD)lParam); |
| break; |
| |
| case WM_CHAR: |
| case WM_SYSCHAR: |
| return HOTKEY_KeyDown (infoPtr, MapVirtualKeyW(LOBYTE(HIWORD(lParam)), 1), lParam); |
| |
| case WM_CREATE: |
| return HOTKEY_Create (infoPtr, (LPCREATESTRUCTW)lParam); |
| |
| case WM_DESTROY: |
| return HOTKEY_Destroy (infoPtr); |
| |
| case WM_ERASEBKGND: |
| return HOTKEY_EraseBackground (infoPtr, (HDC)wParam); |
| |
| case WM_GETDLGCODE: |
| return DLGC_WANTCHARS | DLGC_WANTARROWS; |
| |
| case WM_GETFONT: |
| return HOTKEY_GetFont (infoPtr); |
| |
| case WM_KEYDOWN: |
| case WM_SYSKEYDOWN: |
| return HOTKEY_KeyDown (infoPtr, wParam, lParam); |
| |
| case WM_KEYUP: |
| case WM_SYSKEYUP: |
| return HOTKEY_KeyUp (infoPtr, wParam); |
| |
| case WM_KILLFOCUS: |
| return HOTKEY_KillFocus (infoPtr); |
| |
| case WM_LBUTTONDOWN: |
| return HOTKEY_LButtonDown (infoPtr); |
| |
| case WM_NCCREATE: |
| return HOTKEY_NCCreate (hwnd, (LPCREATESTRUCTW)lParam); |
| |
| case WM_PRINTCLIENT: |
| case WM_PAINT: |
| HOTKEY_Paint(infoPtr, (HDC)wParam); |
| return 0; |
| |
| case WM_SETFOCUS: |
| return HOTKEY_SetFocus (infoPtr); |
| |
| case WM_SETFONT: |
| return HOTKEY_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam)); |
| |
| default: |
| if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg)) |
| ERR("unknown msg %04x wp=%08lx 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 = 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, NULL); |
| } |