/*----------------------------------------------------------------------- ** ** Hotkey.c ** ** Hotkey edit control. ** **-----------------------------------------------------------------------*/ // // Win32 REVIEW: // See all the Get/SetWindowInt(). // #include "ctlspriv.h" #define F_EXT 0x01000000L #define GWU_VIRTKEY 0 #define GWU_MODS 1*sizeof(ULONG_PTR) #define GWU_INVALID 2*sizeof(ULONG_PTR) #define GWU_DEFAULT 3*sizeof(ULONG_PTR) #define GWU_HFONT 4*sizeof(ULONG_PTR) #define GWU_YFONT 5*sizeof(ULONG_PTR) #define GWU_HTHEME 6*sizeof(ULONG_PTR) #define NUM_WND_EXTRA (GWU_HTHEME+sizeof(ULONG_PTR)) LRESULT CALLBACK HotKeyWndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam); BOOL InitHotKeyClass(HINSTANCE hInstance) { WNDCLASS wc; wc.lpfnWndProc = HotKeyWndProc; wc.lpszClassName = s_szHOTKEY_CLASS; wc.style = CS_GLOBALCLASS; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.cbClsExtra = 0; wc.cbWndExtra = NUM_WND_EXTRA; return (RegisterClass(&wc) || (GetLastError() == ERROR_CLASS_ALREADY_EXISTS)); } const UINT s_Combos[8] = { HKCOMB_NONE, HKCOMB_S, HKCOMB_C, HKCOMB_SC, HKCOMB_A, HKCOMB_SA, HKCOMB_CA, HKCOMB_SCA}; void SetHotKey(HWND hwnd, WORD wVirtKey, WORD wMods, BOOL fSendNotify) { /* don't invalidate if it's the same */ if (wVirtKey == GetWindowInt(hwnd, GWU_VIRTKEY) && wMods == GetWindowInt(hwnd, GWU_MODS)) return; SetWindowInt(hwnd, GWU_VIRTKEY ,wVirtKey); SetWindowInt(hwnd, GWU_MODS ,wMods); InvalidateRect(hwnd,NULL,TRUE); if (fSendNotify) { FORWARD_WM_COMMAND(GetParent(hwnd), GetDlgCtrlID(hwnd), hwnd, EN_CHANGE, SendMessage); } NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, hwnd, OBJID_CLIENT, 0); } DWORD GetKeyName(UINT vk, LPTSTR lpsz, DWORD cch, BOOL fExt) { LONG scan; scan = (LONG)MapVirtualKey(vk,0) << 16; if (fExt) { scan |= F_EXT; } return (cch > 0) ? GetKeyNameText(scan, lpsz, cch) : 0; } void PaintHotKey(register HWND hwnd) { TCHAR sz[128]; TCHAR szPlus[10]; int cch; register HDC hdc; UINT wMods; UINT wVirtKey; PAINTSTRUCT ps; int x, y; HANDLE hFont; LocalizedLoadString(IDS_PLUS, szPlus, ARRAYSIZE(szPlus)); wVirtKey = (UINT) GetWindowInt(hwnd, GWU_VIRTKEY); wMods = (UINT) GetWindowInt(hwnd, GWU_MODS); if (wVirtKey || wMods) { sz[0] = 0; cch = 0; if (wMods & HOTKEYF_CONTROL) { GetKeyName(VK_CONTROL, sz, ARRAYSIZE(sz), FALSE); StringCchCat(sz, ARRAYSIZE(sz), szPlus); } if (wMods & HOTKEYF_SHIFT) { cch = lstrlen(sz); GetKeyName(VK_SHIFT, sz+cch, ARRAYSIZE(sz)-cch, FALSE); StringCchCat(sz, ARRAYSIZE(sz), szPlus); } if (wMods & HOTKEYF_ALT) { cch = lstrlen(sz); GetKeyName(VK_MENU, sz+cch, ARRAYSIZE(sz)-cch, FALSE); StringCchCat(sz, ARRAYSIZE(sz), szPlus); } cch = lstrlen(sz); GetKeyName(wVirtKey, sz+cch, ARRAYSIZE(sz)-cch, wMods & HOTKEYF_EXT); } else { LocalizedLoadString(IDS_NONE,sz,100); } cch = lstrlen(sz); HideCaret(hwnd); InvalidateRect(hwnd, NULL, TRUE); hdc = BeginPaint(hwnd,&ps); hFont = SelectObject(hdc, (HFONT)GetWindowInt(hwnd,GWU_HFONT)); x = g_cxBorder; y = g_cyBorder; if (IsWindowEnabled(hwnd)) { SetBkColor(hdc, g_clrWindow); SetTextColor(hdc, g_clrWindowText); TextOut(hdc,x,y,sz,cch); } else { // set the background color to Grayed like edit controls SetBkColor(hdc, g_clrBtnFace); if (g_clrGrayText) { SetTextColor(hdc,g_clrGrayText); TextOut(hdc,x,y,sz,cch); } else { GrayString(hdc,NULL,NULL,(ULONG_PTR)(LPTSTR)sz,cch,x,y,0,0); } } MGetTextExtent(hdc, sz, cch, &x, NULL); if (GetFocus() == hwnd) SetCaretPos(x+g_cxBorder, g_cyBorder); ShowCaret(hwnd); EndPaint(hwnd,&ps); } void HKMSetRules(HWND hwnd, WPARAM wParam, LPARAM lParam) { SetWindowInt(hwnd, GWU_INVALID, wParam); SetWindowInt(hwnd, GWU_DEFAULT, lParam); } HFONT HKMSetFont(HWND hwnd, HFONT wParam) { HFONT lParam; HDC hdc; INT cy; lParam = (HFONT)GetWindowInt(hwnd,GWU_HFONT); SetWindowInt(hwnd,GWU_HFONT,(LONG_PTR)wParam); hdc = GetDC(hwnd); if (wParam) wParam = SelectObject(hdc, wParam); MGetTextExtent(hdc, TEXT("C"), 1, NULL, &cy); SetWindowInt(hwnd,GWU_YFONT,cy); if (wParam) SelectObject(hdc, wParam); ReleaseDC(hwnd,hdc); InvalidateRect(hwnd,NULL,TRUE); return lParam; } LRESULT CALLBACK HotKeyWndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { WORD wVirtKey; WORD wMods; RECT rc; HDC hdc; switch (wMsg) { case WM_NCCREATE: SetWindowBits(hwnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE); InitGlobalColors(); return TRUE; case WM_CREATE: { HTHEME hTheme; SetHotKey(hwnd, 0, 0, FALSE); HKMSetRules(hwnd, 0, 0); HKMSetFont(hwnd, g_hfontSystem); hTheme = OpenThemeData(hwnd, L"Combobox"); if (hTheme) SetWindowLongPtr(hwnd, GWU_HTHEME, (ULONG_PTR)hTheme); } break; case WM_DESTROY: { HTHEME hTheme = (HTHEME)GetWindowLongPtr(hwnd, GWU_HTHEME); if (hTheme) CloseThemeData(hTheme); } break; case WM_NCPAINT: { HTHEME hTheme = (HTHEME)GetWindowLongPtr(hwnd, GWU_HTHEME); if (hTheme) { HRGN hrgn = (wParam != 1) ? (HRGN)wParam : NULL; HBRUSH hbr = (HBRUSH)GetClassLongPtr(hwnd, GCLP_HBRBACKGROUND); if (CCDrawNonClientTheme(hTheme, hwnd, hrgn, hbr, 0, CBXS_NORMAL)) { break; } } } goto DoDefault; case WM_THEMECHANGED: { HTHEME hTheme = (HTHEME)GetWindowLongPtr(hwnd, GWU_HTHEME); if (hTheme) CloseThemeData(hTheme); hTheme = OpenThemeData(hwnd, L"Combobox"); SetWindowLongPtr(hwnd, GWU_HTHEME, (ULONG_PTR)hTheme); // Set a NULL if OpenThemeData fails } break; case WM_SETFOCUS: InvalidateRect(hwnd,NULL,TRUE); CreateCaret(hwnd,NULL,0,(int)GetWindowInt(hwnd,GWU_YFONT)); ShowCaret(hwnd); break; case WM_KILLFOCUS: if (!GetWindowInt(hwnd, GWU_VIRTKEY)) SetHotKey(hwnd, 0, 0, TRUE); DestroyCaret(); break; case WM_GETDLGCODE: return DLGC_WANTCHARS | DLGC_WANTARROWS; // | DLGC_WANTALLKEYS; case HKM_SETHOTKEY: SetHotKey(hwnd, LOBYTE(wParam), HIBYTE(wParam), FALSE); break; case HKM_GETHOTKEY: return (256*(BYTE)GetWindowInt(hwnd, GWU_MODS)) + ((BYTE)GetWindowInt(hwnd, GWU_VIRTKEY)); break; case HKM_SETRULES: HKMSetRules(hwnd, wParam, LOWORD(lParam)); break; case WM_LBUTTONDOWN: SetFocus(hwnd); break; case WM_SYSKEYDOWN: case WM_KEYDOWN: switch (wParam) { case VK_RETURN: case VK_TAB: case VK_SPACE: case VK_DELETE: case VK_ESCAPE: case VK_BACK: case VK_LWIN: case VK_RWIN: case VK_APPS: SetHotKey(hwnd, 0, 0, TRUE); return DefWindowProc(hwnd,wMsg,wParam,lParam); case VK_MENU: case VK_SHIFT: case VK_CONTROL: wVirtKey = 0; goto SetNewHotKey; default: wVirtKey = (WORD) wParam; SetNewHotKey: wMods = 0; if (GetKeyState(VK_CONTROL) < 0) wMods |= HOTKEYF_CONTROL; if (GetKeyState(VK_SHIFT) < 0) wMods |= HOTKEYF_SHIFT; if (GetKeyState(VK_MENU) < 0) wMods |= HOTKEYF_ALT; #define IsFUNKEY(vk) ((vk) >= VK_F1 && (vk) <= VK_F24) #define IsNUMKEY(vk) ((vk) >= VK_NUMPAD0 && (vk) <= VK_DIVIDE) // // dont enforce any rules on the Function keys or // on the number pad keys. // // if this combination is invalid, use the default if (!IsFUNKEY(wVirtKey) && !IsNUMKEY(wVirtKey) && (s_Combos[wMods] & GetWindowInt(hwnd, GWU_INVALID))) { wMods = (WORD)GetWindowInt(hwnd, GWU_DEFAULT); } if (lParam & F_EXT) wMods |= HOTKEYF_EXT; SetHotKey(hwnd, wVirtKey, wMods, TRUE); break; } break; case WM_SYSKEYUP: case WM_CHAR: case WM_SYSCHAR: case WM_KEYUP: if (!GetWindowInt(hwnd, GWU_VIRTKEY)) SetHotKey(hwnd, 0, 0, TRUE); break; case WM_GETFONT: return GetWindowInt(hwnd,GWU_HFONT); case WM_SETFONT: return (LRESULT)(UINT_PTR)HKMSetFont(hwnd, (HFONT)wParam); case WM_PAINT: PaintHotKey(hwnd); break; case WM_ERASEBKGND: HideCaret(hwnd); hdc = GetDC(hwnd); GetClientRect(hwnd, &rc); if (IsWindowEnabled(hwnd)) { FillRect(hdc, &rc, g_hbrWindow); } else { FillRect(hdc, &rc, g_hbrBtnFace); } ReleaseDC(hwnd, hdc); // lParam = DefWindowProc(hwnd,wMsg,wParam,lParam); ShowCaret(hwnd); return TRUE; case WM_GETOBJECT: if( lParam == OBJID_QUERYCLASSNAMEIDX ) return MSAA_CLASSNAMEIDX_HOTKEY; goto DoDefault; case WM_ENABLE: InvalidateRect(hwnd, NULL, TRUE); goto DoDefault; default: DoDefault: return DefWindowProc(hwnd,wMsg,wParam,lParam); } return 0L; }