// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "Private.h" #include "Globals.h" #include "BaseWindow.h" #define idTimer_UIObject 39772 //+--------------------------------------------------------------------------- // // CBaseWindow::ctor // //---------------------------------------------------------------------------- CBaseWindow::CBaseWindow() { _wndHandle = nullptr; _pParentWnd = nullptr; _pUIWnd = nullptr; _pTimerUIObj = nullptr; _pUIObjCapture = nullptr; _enableVirtualWnd = TRUE; _visibleVirtualWnd = TRUE; _RectOfVirtualWnd.left = 0; _RectOfVirtualWnd.top = 0; _RectOfVirtualWnd.right = 0; _RectOfVirtualWnd.bottom = 0; } //+--------------------------------------------------------------------------- // // CBaseWindow::dtor // //---------------------------------------------------------------------------- CBaseWindow::~CBaseWindow() { _SetThis(_wndHandle, nullptr); } //+--------------------------------------------------------------------------- // // CBaseWindow::_InitWindowClass // //---------------------------------------------------------------------------- /* static */ BOOL CBaseWindow::_InitWindowClass(_In_ LPCWSTR lpwszClassName, _Out_ ATOM *patom) { WNDCLASS wc; wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_IME; wc.lpfnWndProc = CBaseWindow::_WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = Global::dllInstanceHandle; wc.hIcon = nullptr; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH); wc.lpszMenuName = nullptr; wc.lpszClassName = lpwszClassName; *patom = RegisterClass(&wc); return (*patom != 0); } //+--------------------------------------------------------------------------- // // CBaseWindow::_UninitClass // //---------------------------------------------------------------------------- /* static */ void CBaseWindow::_UninitWindowClass(ATOM atom) { if (atom != 0) { UnregisterClass((LPCTSTR)atom, Global::dllInstanceHandle); } } //+--------------------------------------------------------------------------- // // CBaseWindow::_Create // //---------------------------------------------------------------------------- BOOL CBaseWindow::_Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParentWnd, int wndWidth, int wndHeight, _In_opt_ HWND parentWndHandle) { _pParentWnd = pParentWnd; if (atom != 0) { // create real window _wndHandle = CreateWindowEx(dwExStyle, (LPCTSTR)atom, NULL, dwStyle, 0, 0, wndWidth, wndHeight, _pParentWnd ? _pParentWnd->_GetWnd() : parentWndHandle, // parentWndHandle NULL, Global::dllInstanceHandle, this); // lpParam if (!_wndHandle) { return FALSE; } } return TRUE; } //+--------------------------------------------------------------------------- // // CBaseWindow::_Destroy // //---------------------------------------------------------------------------- void CBaseWindow::_Destroy() { if (_wndHandle != nullptr) { DestroyWindow(_wndHandle); _wndHandle = nullptr; } } //+--------------------------------------------------------------------------- // // CBaseWindow::_Move // //---------------------------------------------------------------------------- void CBaseWindow::_Move(int x, int y) { if (_wndHandle != nullptr) { SetWindowPos(_wndHandle, 0, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); } else { int dx = x - _RectOfVirtualWnd.left; int dy = y - _RectOfVirtualWnd.top; _RectOfVirtualWnd.left += dx; _RectOfVirtualWnd.top += dy; _RectOfVirtualWnd.right += dx; _RectOfVirtualWnd.bottom += dy; } } //+--------------------------------------------------------------------------- // // CBaseWindow::_Resize // //---------------------------------------------------------------------------- void CBaseWindow::_Resize(int x, int y, int cx, int cy) { if (_wndHandle != nullptr) { MoveWindow(_wndHandle, x, y, cx, cy, TRUE); } else { _RectOfVirtualWnd.left = x; _RectOfVirtualWnd.top = y; _RectOfVirtualWnd.right = x + cx; _RectOfVirtualWnd.bottom = y + cy; } } //+--------------------------------------------------------------------------- // // CBaseWindow::_Show // //---------------------------------------------------------------------------- void CBaseWindow::_Show(BOOL isShowWnd) { if (_wndHandle != nullptr) { if (isShowWnd) { ShowWindow(_wndHandle, SW_SHOWNA); } else { ShowWindow(_wndHandle, SW_HIDE); } } else { _visibleVirtualWnd = isShowWnd; } } //+--------------------------------------------------------------------------- // // CBaseWindow::_IsWindowVisible // //---------------------------------------------------------------------------- BOOL CBaseWindow::_IsWindowVisible() { if (_wndHandle != nullptr) { return IsWindowVisible(_wndHandle); } else { return _visibleVirtualWnd; } } //+--------------------------------------------------------------------------- // // CBaseWindow::_Enable // //---------------------------------------------------------------------------- void CBaseWindow::_Enable(BOOL enableWindowReceiveInput) { if (_wndHandle != nullptr) { EnableWindow(_wndHandle, enableWindowReceiveInput); } else { _enableVirtualWnd = enableWindowReceiveInput; } } //+--------------------------------------------------------------------------- // // CBaseWindow::_IsEnabled // //---------------------------------------------------------------------------- BOOL CBaseWindow::_IsEnabled() { if (_wndHandle != nullptr) { return IsWindowEnabled(_wndHandle); } else { return _enableVirtualWnd; } } //+--------------------------------------------------------------------------- // // CBaseWindow::_InvalidateRect // //---------------------------------------------------------------------------- void CBaseWindow::_InvalidateRect() { if (_wndHandle != nullptr) { InvalidateRect(_wndHandle, NULL, TRUE); } else { CBaseWindow *pobj = _pParentWnd; while (pobj != nullptr) { if (pobj->_wndHandle) { InvalidateRect(pobj->_wndHandle, &_RectOfVirtualWnd, TRUE); break; } pobj = pobj->_pParentWnd; } } } //+--------------------------------------------------------------------------- // // CBaseWindow::_GetWindowRect // //---------------------------------------------------------------------------- BOOL CBaseWindow::_GetWindowRect(_Inout_ LPRECT lpRect) { if (_wndHandle != nullptr) { return GetWindowRect(_wndHandle, lpRect); } else { *lpRect = _RectOfVirtualWnd; return TRUE; } } //+--------------------------------------------------------------------------- // // CBaseWindow::_GetClientRect // //---------------------------------------------------------------------------- BOOL CBaseWindow::_GetClientRect(_Inout_ LPRECT lpRect) { if (_wndHandle != nullptr) { return GetClientRect(_wndHandle, lpRect); } else { *lpRect = _RectOfVirtualWnd; return TRUE; } } //+--------------------------------------------------------------------------- // // _GetWindowExtent // //---------------------------------------------------------------------------- HRESULT CBaseWindow::_GetWindowExtent(_In_ const RECT *prcTextExtent, _In_opt_ RECT *prcCandidateExtent, _Inout_ POINT *pptCandidate) { RECT rcWorkArea = {0, 0, 0, 0}; // Get work area GetWorkAreaFromPoint(*(LPPOINT)&prcTextExtent->left, &rcWorkArea); // Calc candidate window extent if (prcCandidateExtent) { CalcFitPointAroundTextExtent(prcTextExtent, &rcWorkArea, prcCandidateExtent, pptCandidate); } return S_OK; } //+--------------------------------------------------------------------------- // // CalcFitPointAroundTextExtent // //---------------------------------------------------------------------------- void CBaseWindow::CalcFitPointAroundTextExtent(_In_ const RECT *prcTextExtent, _In_ const RECT *prcWorkArea, _In_ const RECT *prcWindow, _Out_ POINT *ppt) { RECT rcTargetWindow[2]; DWORD dwFlags[2]; // set rcTargetWindow[0] which rectangle attached on bottom side of text extent rcTargetWindow[0] = *prcWindow; OffsetRect(&rcTargetWindow[0], prcTextExtent->left, prcTextExtent->bottom); // set rcTargetWindow[1] which rectangle attached on top side of text extent rcTargetWindow[1] = *prcWindow; OffsetRect(&rcTargetWindow[1], prcTextExtent->left, prcTextExtent->top - (prcWindow->bottom - prcWindow->top)); // // check target rectangle fit in workarea // for (DWORD i = 0; i < ARRAYSIZE(rcTargetWindow); i++) { dwFlags[i] = RectInRect(prcWorkArea, &rcTargetWindow[i]); // generally, rcTargetWindow[0] or rcTargetWindow[1] have neither of these flags // but in the case where window is just complete way off the screen e.g. user // starts input, then drag window way off screen, both of these will be true if ((dwFlags[i] & RECT_OVER_TOP) || (dwFlags[i] & RECT_OVER_BOTTOM)) { continue; } if (dwFlags[i] == RECT_INSIDE) { *ppt = *(POINT*)&rcTargetWindow[i].left; return; } else if ((dwFlags[i] & RECT_OVER_LEFT) != 0) { ppt->x = 0; ppt->y = rcTargetWindow[i].top; return; } else if ((dwFlags[i] & RECT_OVER_RIGHT) != 0) { ppt->x = prcWorkArea->right - (prcWindow->right - prcWindow->left); ppt->y = rcTargetWindow[i].top; return; } } // if both rcTargetWindow have RECT_OVER_TOP or RECT_OVER_BOTTOM, // then "dock" the window to top or bottom of working area. if ((dwFlags[0] & RECT_OVER_TOP) != 0) { ppt->y = 0; } else { ppt->y = prcWorkArea->bottom - (prcWindow->bottom - prcWindow->top); } // dock to left/right edge if RECT_OVER_LEFT or RECT_OVER_RIGHT. // else just stay where we are if ((dwFlags[0] & RECT_OVER_LEFT) != 0) { ppt->x = 0; } else if ((dwFlags[0] & RECT_OVER_RIGHT) != 0) { ppt->x = prcWorkArea->right - (prcWindow->right - prcWindow->left); } else { ppt->x = prcTextExtent->left; } return; } //+--------------------------------------------------------------------------- // // RectInRect // //---------------------------------------------------------------------------- DWORD CBaseWindow::RectInRect(_In_ const RECT *prcLimit, _In_ const RECT *prcTarget) { DWORD dwFlags = 0; // Check if prcTarget is entirely inside prcLimit if (prcLimit->left <= prcTarget->left && prcTarget->right <= prcLimit->right && prcLimit->top <= prcTarget->top && prcTarget->bottom <= prcLimit->bottom) { return RECT_INSIDE; } // Check horizontal range. Target can be // - wider than the limit (assert here since it should never happen) // - entirely outside the limit (RECT_OVERLEFT or RECT_OVERRIGHT) // - partially inside the limit (RECT_OVERLEFT or RECT_OVERRIGHT) if (prcTarget->left < prcLimit->left && prcTarget->right > prcLimit->right) { assert(FALSE); dwFlags |= RECT_TOO_WIDE; } else if (prcTarget->left < prcLimit->left) { dwFlags |= RECT_OVER_LEFT; } else if (prcTarget->right > prcLimit->right) { dwFlags |= RECT_OVER_RIGHT; } // Check vertical range. Target can be // - taller than the limit (assert here since it should never happen) // - entirely outside the limit (RECT_OVERTOP or RECT_OVERBOTTOM) // - partially inside the limit (RECT_OVERTOP or RECT_OVERBOTTOM) if (prcTarget->top < prcLimit->top && prcTarget->bottom > prcLimit->bottom) { assert(FALSE); dwFlags |= RECT_TOO_TALL; } else if (prcTarget->top < prcLimit->top) { dwFlags |= RECT_OVER_TOP; } else if (prcTarget->bottom > prcLimit->bottom) { dwFlags |= RECT_OVER_BOTTOM; } return dwFlags; } //+--------------------------------------------------------------------------- // // CBaseWindow::_NotifyCommand // //---------------------------------------------------------------------------- LRESULT CBaseWindow::_NotifyCommand(UINT uMsg, DWORD dwSB, int nPos) { CBaseWindow* pUIWnd = _GetUIWnd(); if (pUIWnd && pUIWnd->_GetWnd()) { WPARAM wParam = MAKEWPARAM(dwSB, nPos); if (pUIWnd->_GetWnd()) { return SendMessage(pUIWnd->_GetWnd(), uMsg, wParam, (LPARAM)0); } } return 0; } //+--------------------------------------------------------------------------- // // _SetCaptureObject // //---------------------------------------------------------------------------- void CBaseWindow::_SetCaptureObject(_In_opt_ CBaseWindow *pUIObj) { CBaseWindow* pUIWnd = _GetTopmostUIWnd(); if (nullptr == pUIWnd) { return; } pUIWnd->_pUIObjCapture = pUIObj; if (pUIObj != nullptr) { SetCapture(pUIWnd->_GetWnd()); } else { ReleaseCapture(); } } //+--------------------------------------------------------------------------- // // _SetTimerObject // //---------------------------------------------------------------------------- void CBaseWindow::_SetTimerObject(_In_opt_ CBaseWindow *pUIObj, UINT uElapse) { CBaseWindow* pUIWnd = _GetTopmostUIWnd(); if (nullptr == pUIWnd) { return; } pUIWnd->_pTimerUIObj = pUIObj; if (pUIObj != nullptr) { SetTimer(pUIWnd->_GetWnd(), idTimer_UIObject, uElapse, NULL); } else { KillTimer(pUIWnd->_GetWnd(), idTimer_UIObject); } } //+--------------------------------------------------------------------------- // // CBaseWindow::_WindowProc // // CBaseWindow window proc. //---------------------------------------------------------------------------- /* static */ LRESULT CALLBACK CBaseWindow::_WindowProc(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) { if (uMsg == WM_CREATE) { _SetThis(wndHandle, ((CREATESTRUCT*)lParam)->lpCreateParams); } CBaseWindow* pv = _GetThis(wndHandle); if (!pv) { return DefWindowProc(wndHandle, uMsg, wParam, lParam); } if (uMsg == WM_TIMER) { switch (wParam) { case idTimer_UIObject: if (pv->_GetTimerObject() != nullptr) { pv->_GetTimerObject()->_OnTimer(); } break; } return 0; } else { return pv->_WindowProcCallback(wndHandle, uMsg, wParam, lParam); } } //+--------------------------------------------------------------------------- // // CBaseWindow::GetWorkAreaFromPoint // //---------------------------------------------------------------------------- void CBaseWindow::GetWorkAreaFromPoint(_In_ const POINT& ptPoint, _Out_ LPRECT lprcWorkArea) { if (lprcWorkArea == nullptr) { return; } lprcWorkArea->left = 0; lprcWorkArea->top = 0; lprcWorkArea->right = 0; lprcWorkArea->bottom = 0; HMONITOR hMonitor = MonitorFromPoint(ptPoint, MONITOR_DEFAULTTONEAREST); if (hMonitor) { MONITORINFO MonitorInfo = {0}; MonitorInfo.cbSize = sizeof(MONITORINFO); if (GetMonitorInfo(hMonitor, &MonitorInfo)) { *lprcWorkArea = MonitorInfo.rcWork; return; } } SystemParametersInfo(SPI_GETWORKAREA, 0, lprcWorkArea, 0); return; } BOOL CBaseWindow::_IsTimer() { CBaseWindow* pobj = _GetTopmostUIWnd(); if (pobj != nullptr) { return (pobj->_pTimerUIObj != nullptr); } return FALSE; } BOOL CBaseWindow::_IsCapture() { CBaseWindow* pobj = _GetTopmostUIWnd(); if (pobj != nullptr) { return (pobj->_pUIObjCapture != nullptr); } return FALSE; } CBaseWindow* CBaseWindow::_GetTopmostUIWnd() { CBaseWindow* pobj = this; while (pobj->_pParentWnd != nullptr) { pobj = pobj->_pParentWnd; } return pobj->_pUIWnd; }