2025-11-28 00:35:46 +09:00

662 lines
17 KiB
C++

// 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;
}