441 lines
13 KiB
C++
441 lines
13 KiB
C++
#include "stdafx.h"
|
|
#include "Lava.h"
|
|
#include "HWndContainer.h"
|
|
|
|
#include "MsgHelp.h"
|
|
#include "Spy.h"
|
|
|
|
#define PROFILE_DRAW 0
|
|
|
|
#if PROFILE_DRAW
|
|
#include <icecap.h>
|
|
#endif
|
|
|
|
#if DBG
|
|
UINT g_uMsgEnableSpy = RegisterWindowMessage(TEXT("GadgetSpy Enable"));
|
|
UINT g_uMsgFindGadget = RegisterWindowMessage(TEXT("GadgetSpy FindGadget"));
|
|
#endif // DBG
|
|
|
|
/***************************************************************************\
|
|
*****************************************************************************
|
|
*
|
|
* API Implementation
|
|
*
|
|
*****************************************************************************
|
|
\***************************************************************************/
|
|
|
|
//------------------------------------------------------------------------------
|
|
HWndContainer *
|
|
GetHWndContainer(DuVisual * pgad)
|
|
{
|
|
DuContainer * pcon = pgad->GetContainer();
|
|
AssertReadPtr(pcon);
|
|
|
|
HWndContainer * pconHWND = CastHWndContainer(pcon);
|
|
return pconHWND;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*****************************************************************************
|
|
*
|
|
* class HWndContainer
|
|
*
|
|
*****************************************************************************
|
|
\***************************************************************************/
|
|
|
|
//------------------------------------------------------------------------------
|
|
HWndContainer::HWndContainer()
|
|
{
|
|
m_hwndOwner = NULL;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
HWndContainer::~HWndContainer()
|
|
{
|
|
//
|
|
// Need to destroy the gadget tree before this class is destructed since
|
|
// it may need to make calls to the container during its destruction. If
|
|
// we don't do this here, it may end up calling pure-virtual's on the base
|
|
// class.
|
|
//
|
|
|
|
ContextLock cl;
|
|
Verify(cl.LockNL(ContextLock::edDefer));
|
|
xwDestroyGadget();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
HRESULT
|
|
HWndContainer::Build(HWND hwnd, HWndContainer ** ppconNew)
|
|
{
|
|
// Check parameters
|
|
if (!ValidateHWnd(hwnd)) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Create a new container
|
|
HWndContainer * pconNew = ClientNew(HWndContainer);
|
|
if (pconNew == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pconNew->m_hwndOwner = hwnd;
|
|
|
|
RECT rcClient;
|
|
GetClientRect(hwnd, &rcClient);
|
|
pconNew->m_sizePxl.cx = rcClient.right;
|
|
pconNew->m_sizePxl.cy = rcClient.bottom;
|
|
|
|
*ppconNew = pconNew;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
HWndContainer::OnInvalidate(const RECT * prcInvalidContainerPxl)
|
|
{
|
|
if ((!InlineIsRectEmpty(prcInvalidContainerPxl)) &&
|
|
(prcInvalidContainerPxl->left <= m_sizePxl.cx) &&
|
|
(prcInvalidContainerPxl->top <= m_sizePxl.cy) &&
|
|
(prcInvalidContainerPxl->right >= 0) &&
|
|
(prcInvalidContainerPxl->bottom >= 0)) {
|
|
|
|
// TODO: How do we handle multiple layers / background?
|
|
|
|
#if 0
|
|
Trace("HWndContainer::OnInvalidate(): %d, %d, %d, %d\n",
|
|
prcInvalidContainerPxl->left, prcInvalidContainerPxl->top,
|
|
prcInvalidContainerPxl->right, prcInvalidContainerPxl->bottom);
|
|
#endif
|
|
InvalidateRect(m_hwndOwner, prcInvalidContainerPxl, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
HWndContainer::OnGetRect(RECT * prcDesktopPxl)
|
|
{
|
|
GetClientRect(m_hwndOwner, prcDesktopPxl);
|
|
ClientToScreen(m_hwndOwner, (LPPOINT) &(prcDesktopPxl->left));
|
|
ClientToScreen(m_hwndOwner, (LPPOINT) &(prcDesktopPxl->right));
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
HWndContainer::OnStartCapture()
|
|
{
|
|
SetCapture(m_hwndOwner);
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
HWndContainer::OnEndCapture()
|
|
{
|
|
ReleaseCapture();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
BOOL
|
|
HWndContainer::OnTrackMouseLeave()
|
|
{
|
|
TRACKMOUSEEVENT tme;
|
|
tme.cbSize = sizeof(tme);
|
|
tme.dwFlags = TME_LEAVE | TME_HOVER;
|
|
tme.dwHoverTime = HOVER_DEFAULT;
|
|
tme.hwndTrack = m_hwndOwner;
|
|
|
|
return TrackMouseEvent(&tme);
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
HWndContainer::OnSetFocus()
|
|
{
|
|
if (GetFocus() != m_hwndOwner) {
|
|
//
|
|
// Setting focus is a little more complicated than pure HWND's. This is
|
|
// because Gadgets greatly simplify several things
|
|
//
|
|
// 1. SetFocus
|
|
// 2. Setup caret, if any
|
|
//
|
|
|
|
//Trace("HWndContainer::OnSetFocus()\n");
|
|
SetFocus(m_hwndOwner);
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
HWndContainer::OnRescanMouse(POINT * pptContainerPxl)
|
|
{
|
|
POINT ptCursor;
|
|
if (!GetCursorPos(&ptCursor)) {
|
|
ptCursor.x = -20000;
|
|
ptCursor.y = -20000;
|
|
}
|
|
|
|
ScreenToClient(m_hwndOwner, &ptCursor);
|
|
*pptContainerPxl = ptCursor;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
BOOL
|
|
HWndContainer::xdHandleMessage(UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT * pr, UINT nMsgFlags)
|
|
{
|
|
if (m_pgadRoot == NULL) {
|
|
return FALSE; // If don't have a root, there is nothing to handle.
|
|
}
|
|
|
|
POINT ptContainerPxl;
|
|
*pr = 0;
|
|
|
|
switch (nMsg)
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_MBUTTONDBLCLK:
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
{
|
|
GMSG_MOUSECLICK msg;
|
|
GdConvertMouseClickMessage(&msg, nMsg, wParam);
|
|
|
|
ptContainerPxl.x = GET_X_LPARAM(lParam);
|
|
ptContainerPxl.y = GET_Y_LPARAM(lParam);
|
|
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
return m_pgadRoot->xdHandleMouseMessage(&msg, ptContainerPxl);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
ptContainerPxl.x = GET_X_LPARAM(lParam);
|
|
ptContainerPxl.y = GET_Y_LPARAM(lParam);
|
|
|
|
// unlike every other mouse message, the x,y params for the
|
|
// mouse wheel are in *screen* coordinates, not *client*
|
|
// coordinates -- convert 'em here to "play along"
|
|
ScreenToClient(m_hwndOwner, &ptContainerPxl);
|
|
|
|
GMSG_MOUSEWHEEL msg;
|
|
GdConvertMouseWheelMessage(&msg, wParam);
|
|
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
return m_pgadRoot->xdHandleMouseMessage(&msg, ptContainerPxl);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_MOUSEMOVE:
|
|
case WM_MOUSEHOVER:
|
|
{
|
|
GMSG_MOUSE msg;
|
|
GdConvertMouseMessage(&msg, nMsg, wParam);
|
|
|
|
ptContainerPxl.x = GET_X_LPARAM(lParam);
|
|
ptContainerPxl.y = GET_Y_LPARAM(lParam);
|
|
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
return m_pgadRoot->xdHandleMouseMessage(&msg, ptContainerPxl);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_MOUSELEAVE:
|
|
{
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
m_pgadRoot->xdHandleMouseLeaveMessage();
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_CAPTURECHANGED:
|
|
if (m_hwndOwner != (HWND) lParam) {
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
m_pgadRoot->xdHandleMouseLostCapture();
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// WM_SETFOCUS and WM_KILLFOCUS will restore and save (respectively) Gadget focus only
|
|
// if we are gaining for losing focus from an HWND outside our tree.
|
|
//
|
|
|
|
case WM_SETFOCUS:
|
|
{
|
|
if ((m_hwndOwner != (HWND)wParam) && (IsChild(m_hwndOwner, (HWND)wParam) == FALSE)) {
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
return m_pgadRoot->xdHandleKeyboardFocus(GSC_SET);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_KILLFOCUS:
|
|
{
|
|
if ((m_hwndOwner != (HWND)wParam) && (IsChild(m_hwndOwner, (HWND)wParam) == FALSE)) {
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
return m_pgadRoot->xdHandleKeyboardFocus(GSC_LOST);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_CHAR:
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
case WM_SYSCHAR:
|
|
case WM_SYSKEYDOWN:
|
|
case WM_SYSKEYUP:
|
|
{
|
|
GMSG_KEYBOARD msg;
|
|
GdConvertKeyboardMessage(&msg, nMsg, wParam, lParam);
|
|
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
return m_pgadRoot->xdHandleKeyboardMessage(&msg, nMsgFlags);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_ERASEBKGND:
|
|
return TRUE;
|
|
|
|
case WM_PAINT:
|
|
if ((!m_fManualDraw) && (m_pgadRoot != NULL)) {
|
|
PAINTSTRUCT ps;
|
|
if (BeginPaint(m_hwndOwner, &ps) != NULL) {
|
|
|
|
#if PROFILE_DRAW
|
|
StartProfile(PROFILE_GLOBALLEVEL, PROFILE_CURRENTID);
|
|
#endif
|
|
|
|
{
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edNone)) {
|
|
m_pgadRoot->xrDrawTree(NULL, ps.hdc, &ps.rcPaint, 0);
|
|
}
|
|
}
|
|
|
|
#if PROFILE_DRAW
|
|
StopProfile(PROFILE_GLOBALLEVEL, PROFILE_CURRENTID);
|
|
#endif
|
|
|
|
EndPaint(m_hwndOwner, &ps);
|
|
}
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGED:
|
|
{
|
|
WINDOWPOS * pwp = (WINDOWPOS *) lParam;
|
|
|
|
UINT nFlags = 0;
|
|
if (!TestFlag(pwp->flags, SWP_NOSIZE)) {
|
|
RECT rcClient;
|
|
GetClientRect(m_hwndOwner, &rcClient);
|
|
|
|
nFlags |= SGR_SIZE;
|
|
m_sizePxl.cx = rcClient.right;
|
|
m_sizePxl.cy = rcClient.bottom;
|
|
}
|
|
|
|
//
|
|
// Even if the window has moved, we don't need to change the
|
|
// root gadget since it is relative to the container and that
|
|
// has not changed.
|
|
//
|
|
|
|
// TODO: Need to change this to SGR_ACTUAL
|
|
|
|
if (nFlags != 0) {
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edDefer)) {
|
|
VerifyHR(m_pgadRoot->xdSetLogRect(0, 0, m_sizePxl.cx, m_sizePxl.cy, nFlags));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_PARENTNOTIFY:
|
|
|
|
// TODO: Need to notify the root gadget that an HWND has been created
|
|
// or destroyed so that it can create an adapter gadget to back
|
|
// it.
|
|
|
|
break;
|
|
|
|
case WM_GETROOTGADGET:
|
|
if (m_pgadRoot != NULL) {
|
|
*pr = (LRESULT) m_pgadRoot->GetHandle();
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
#if DBG
|
|
default:
|
|
if (nMsg == g_uMsgEnableSpy) {
|
|
if (m_pgadRoot != NULL) {
|
|
HGADGET hgadSelect = (HGADGET) lParam;
|
|
HCURSOR hcurOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edNone)) {
|
|
Spy::BuildSpy(m_hwndOwner, m_pgadRoot->GetHandle(), hgadSelect);
|
|
SetCursor(hcurOld);
|
|
}
|
|
}
|
|
} else if (nMsg == g_uMsgFindGadget) {
|
|
POINT ptFindScreenPxl;
|
|
ptFindScreenPxl.x = GET_X_LPARAM(lParam);
|
|
ptFindScreenPxl.y = GET_Y_LPARAM(lParam);
|
|
ScreenToClient(m_hwndOwner, &ptFindScreenPxl);
|
|
|
|
ContextLock cl;
|
|
if (cl.LockNL(ContextLock::edNone)) {
|
|
POINT ptClientPxl;
|
|
DuVisual * pgadFound = m_pgadRoot->FindFromPoint(ptFindScreenPxl, GS_VISIBLE, &ptClientPxl);
|
|
if (wParam) {
|
|
DuVisual::DEBUG_SetOutline(pgadFound);
|
|
}
|
|
|
|
if (pgadFound != NULL) {
|
|
*pr = (LRESULT) pgadFound->GetHandle();
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
#endif // DBG
|
|
}
|
|
|
|
return FALSE;
|
|
}
|