mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 02:12:33 +01:00
2033 lines
48 KiB
C++
2033 lines
48 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Graphics object code.
|
|
|
|
***************************************************************************/
|
|
#include "frame.h"
|
|
ASSERTNAME
|
|
|
|
|
|
BEGIN_CMD_MAP(GOB, CMH)
|
|
ON_CID_GEN(cidKey, &GOB::FCmdKeyCore, pvNil)
|
|
ON_CID_GEN(cidSelIdle, &GOB::FCmdSelIdle, pvNil)
|
|
ON_CID_ME(cidActivateSel, &GOB::FCmdActivateSel, pvNil)
|
|
ON_CID_ME(cidBadKey, &GOB::FCmdBadKeyCore, pvNil)
|
|
ON_CID_ME(cidCloseWnd, &GOB::FCmdCloseWnd, pvNil)
|
|
ON_CID_ME(cidMouseDown, &GOB::FCmdTrackMouseCore, pvNil)
|
|
ON_CID_ME(cidTrackMouse, &GOB::FCmdTrackMouseCore, pvNil)
|
|
ON_CID_ME(cidMouseMove, &GOB::FCmdMouseMoveCore, pvNil)
|
|
END_CMD_MAP_NIL()
|
|
|
|
RTCLASS(GOB)
|
|
RTCLASS(GTE)
|
|
|
|
|
|
long GOB::_ginDefGob = kginSysInval;
|
|
long GOB::_gridLast;
|
|
|
|
|
|
/***************************************************************************
|
|
Fill in the elements of the GCB.
|
|
***************************************************************************/
|
|
void GCB::Set(long hid, PGOB pgob, ulong grfgob, long gin, RC *prcAbs, RC *prcRel)
|
|
{
|
|
Assert(hidNil != hid, "bad hid");
|
|
AssertNilOrPo(pgob, 0);
|
|
_hid = hid;
|
|
_pgob = pgob;
|
|
_grfgob = grfgob;
|
|
_gin = gin;
|
|
if (pvNil == prcAbs)
|
|
_rcAbs.Zero();
|
|
else
|
|
_rcAbs = *prcAbs;
|
|
if (pvNil == prcRel)
|
|
_rcRel.Zero();
|
|
else
|
|
_rcRel = *prcRel;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to shut down all GOBs.
|
|
***************************************************************************/
|
|
void GOB::ShutDown(void)
|
|
{
|
|
while (pvNil != _pgobScreen)
|
|
{
|
|
_pgobScreen->FAttachHwnd(hNil);
|
|
|
|
// freeing the _pgobScreen also updates _pgobScreen to its sibling.
|
|
// _pgobScreen is really the root of the forest.
|
|
_pgobScreen->Release();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a graphics object. pgob is either the parent of the new
|
|
gob or a sibling, according to (grfgob & fgobSibling).
|
|
***************************************************************************/
|
|
GOB::GOB(PGCB pgcb) : CMH(pgcb->_hid)
|
|
{
|
|
_Init(pgcb);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the gob.
|
|
***************************************************************************/
|
|
void GOB::_Init(PGCB pgcb)
|
|
{
|
|
AssertVarMem(pgcb);
|
|
AssertNilOrPo(pgcb->_pgob, 0);
|
|
|
|
_grid = ++_gridLast;
|
|
_ginDefault = pgcb->_gin;
|
|
_fCreating = fTrue;
|
|
|
|
if (pvNil == pgcb->_pgob)
|
|
{
|
|
Assert(pvNil == _pgobScreen, "screen gob already created");
|
|
_pgobScreen = this;
|
|
}
|
|
else if (pgcb->_grfgob & fgobSibling)
|
|
{
|
|
AssertPo(pgcb->_pgob, 0);
|
|
_pgobPar = pgcb->_pgob->_pgobPar;
|
|
_pgobSib = pgcb->_pgob->_pgobSib;
|
|
pgcb->_pgob->_pgobSib = this;
|
|
}
|
|
else
|
|
{
|
|
AssertPo(pgcb->_pgob, 0);
|
|
_pgobPar = pgcb->_pgob;
|
|
_pgobSib = pgcb->_pgob->_pgobChd;
|
|
pgcb->_pgob->_pgobChd = this;
|
|
}
|
|
|
|
if (pvNil != _pgobPar)
|
|
_pgpt = _pgobPar->_pgpt;
|
|
SetPos(&pgcb->_rcAbs, &pgcb->_rcRel);
|
|
AssertThis(0);
|
|
|
|
_fCreating = fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for GOB.
|
|
***************************************************************************/
|
|
GOB::GOB(long hid) : CMH(hid)
|
|
{
|
|
GCB gcb(hid, GOB::PgobScreen());
|
|
_Init(&gcb);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
First tells the app that the gob is dying; then calls Release on all direct
|
|
child gobs of this GOB; then calls delete on itself.
|
|
***************************************************************************/
|
|
void GOB::Release(void)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob;
|
|
|
|
if (--_cactRef > 0)
|
|
return;
|
|
|
|
//Mark this gob as being freed (may already be marked)
|
|
_fFreeing = fTrue;
|
|
|
|
//invalidate
|
|
if (pvNil == _pgobPar || !_pgobPar->_fFreeing)
|
|
InvalRc(pvNil);
|
|
|
|
while (pvNil != (pgob = _pgobChd))
|
|
pgob->Release();
|
|
|
|
delete this;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the graphics object class.
|
|
***************************************************************************/
|
|
GOB::~GOB(void)
|
|
{
|
|
AssertThis(0);
|
|
PGOB *ppgob;
|
|
|
|
//remove it from the sibling list
|
|
Assert(pvNil == _pgobChd, "gob still has children");
|
|
for (ppgob = pvNil != _pgobPar ? &_pgobPar->_pgobChd : &_pgobScreen;
|
|
*ppgob != this && pvNil != *ppgob;
|
|
ppgob = &(*ppgob)->_pgobSib)
|
|
{
|
|
}
|
|
if (*ppgob == this)
|
|
*ppgob = _pgobSib;
|
|
else
|
|
Bug("corrupt gob tree");
|
|
|
|
//nuke its port and hwnd
|
|
if (pvNil != _pgpt && (pvNil == _pgobPar || _pgpt != _pgobPar->_pgpt))
|
|
ReleasePpo(&_pgpt);
|
|
if (_hwnd != hNil)
|
|
_DestroyHwnd(_hwnd);
|
|
ReleasePpo(&_pglrtvm);
|
|
ReleasePpo(&_pcurs);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Called by OS specific code when an hwnd is activated or deactivated.
|
|
We inform the entire gob subtree for the hwnd so individual elements
|
|
can do whatever is necessary. This is a static member function.
|
|
***************************************************************************/
|
|
void GOB::ActivateHwnd(HWND hwnd, bool fActive)
|
|
{
|
|
PGOB pgob;
|
|
|
|
if (pvNil == (pgob = PgobFromHwnd(hwnd)))
|
|
return;
|
|
|
|
//if it's becoming active, bring it to the front in our gob tree.
|
|
if (fActive)
|
|
pgob->SendBehind(pvNil);
|
|
|
|
GTE gte;
|
|
ulong grfgte;
|
|
|
|
gte.Init(pgob, fgteBackToFront);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if (grfgte & fgtePre)
|
|
pgob->_ActivateHwnd(fActive);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make this the first child of its parent. Doesn't invalidate anything.
|
|
***************************************************************************/
|
|
void GOB::BringToFront(void)
|
|
{
|
|
AssertThis(0);
|
|
SendBehind(pvNil);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Put this GOB behind the given sibling. If pgobBehind is nil, does
|
|
the equivalent of a BringToFront. Asserts that pgobBehind and this
|
|
gob have the same parent. Does no invalidation.
|
|
***************************************************************************/
|
|
void GOB::SendBehind(PGOB pgobBehind)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pgobBehind, 0);
|
|
PGOB pgob;
|
|
|
|
if (pvNil != pgobBehind && pgobBehind->_pgobPar != _pgobPar)
|
|
{
|
|
Bug("don't have the same parent");
|
|
return;
|
|
}
|
|
|
|
pgob = PgobPrevSib();
|
|
if (pgob == pgobBehind)
|
|
return; //nothing to do
|
|
|
|
//take this gob out of the sibling list
|
|
if (pvNil == pgob)
|
|
{
|
|
Assert(_pgobPar->_pgobChd == this, "corrupt GOB tree");
|
|
_pgobPar->_pgobChd = _pgobSib;
|
|
}
|
|
else
|
|
{
|
|
Assert(pgob->_pgobSib == this, "corrupt GOB tree");
|
|
pgob->_pgobSib = _pgobSib;
|
|
}
|
|
|
|
//now insert it after pgobBehind
|
|
if (pvNil == pgobBehind)
|
|
{
|
|
_pgobSib = _pgobPar->_pgobChd;
|
|
_pgobPar->_pgobChd = this;
|
|
}
|
|
else
|
|
{
|
|
_pgobSib = pgobBehind->_pgobSib;
|
|
pgobBehind->_pgobSib = this;
|
|
AssertPo(pgobBehind, 0);
|
|
}
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Invalidate the given rc in this gob. If gin is ginNil, nothing is done.
|
|
If gin is kginRedraw, the area is redraw. If gin is kginMark, the area
|
|
is marked dirty at the framework level. If gin is kginSysInval, the
|
|
area is marked dirty at the operating system level. In all cases,
|
|
passing pvNil for prc affects the whole gob.
|
|
***************************************************************************/
|
|
void GOB::InvalRc(RC *prc, long gin)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(prc);
|
|
PT dpt;
|
|
RC rc;
|
|
PGOB pgob;
|
|
RCS rcs;
|
|
|
|
if (kginDefault == gin)
|
|
{
|
|
gin = _ginDefault;
|
|
if (kginDefault == gin)
|
|
gin = _ginDefGob;
|
|
}
|
|
|
|
if (ginNil == gin)
|
|
return;
|
|
|
|
GetRcVis(&rc, cooLocal);
|
|
if (pvNil != prc)
|
|
rc.FIntersect(prc);
|
|
if (rc.FEmpty())
|
|
return;
|
|
|
|
for (pgob = this; pvNil != pgob && pgob->_hwnd == hNil;
|
|
pgob = pgob->_pgobPar)
|
|
{
|
|
rc.Offset(pgob->_rcCur.xpLeft, pgob->_rcCur.ypTop);
|
|
}
|
|
if (pvNil == pgob)
|
|
return;
|
|
|
|
switch (gin)
|
|
{
|
|
default:
|
|
Bug("bad gin value");
|
|
break;
|
|
|
|
case kginDraw:
|
|
//do this so we do whatever the app does during a normal draw, such
|
|
//as drawing offscreen....
|
|
vpappb->UpdateHwnd(pgob->_hwnd, &rc);
|
|
break;
|
|
|
|
case kginMark:
|
|
vpappb->MarkRc(&rc, pgob);
|
|
break;
|
|
|
|
case kginSysInval:
|
|
rcs = RCS(rc);
|
|
InvalHwndRcs(pgob->_hwnd, &rcs);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Validate the given rc in this gob. If gin is ginNil, nothing is done.
|
|
If gin is kginRedraw, the area is validated at both the framework level
|
|
and the system level. If gin is kginMark or kginSysInval, the area is
|
|
validated only at the given level. In any case, passing pvNil for prc
|
|
affects the whole gob.
|
|
***************************************************************************/
|
|
void GOB::ValidRc(RC *prc, long gin)
|
|
{
|
|
AssertThis(0);
|
|
RC rc;
|
|
PT dpt;
|
|
PGOB pgob;
|
|
|
|
if (kginDefault == gin)
|
|
{
|
|
gin = _ginDefault;
|
|
if (kginDefault == gin)
|
|
gin = _ginDefGob;
|
|
}
|
|
|
|
if (ginNil == gin)
|
|
return;
|
|
|
|
GetRcVis(&rc, cooLocal);
|
|
if (pvNil != prc)
|
|
rc.FIntersect(prc);
|
|
if (rc.FEmpty())
|
|
return;
|
|
|
|
for (pgob = this; pvNil != pgob && pgob->_hwnd == hNil;
|
|
pgob = pgob->_pgobPar)
|
|
{
|
|
rc.Offset(pgob->_rcCur.xpLeft, pgob->_rcCur.ypTop);
|
|
}
|
|
if (pvNil == pgob)
|
|
return;
|
|
|
|
if (gin != kginSysInval)
|
|
{
|
|
// do a framework level validation
|
|
vpappb->UnmarkRc(&rc, pgob);
|
|
}
|
|
|
|
if (gin != kginMark)
|
|
{
|
|
// do a system level validation
|
|
RCS rcs;
|
|
|
|
rcs = RCS(rc);
|
|
ValidHwndRcs(pgob->_hwnd, &rcs);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the dirty portion of this gob. Return true iff the dirty rectangle
|
|
is non-empty. If gin is kginDraw, gets the union of the marked area
|
|
and system-invalidated area.
|
|
***************************************************************************/
|
|
bool GOB::FGetRcInval(RC *prc, long gin)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
RC rc;
|
|
PGOB pgob;
|
|
PT dpt(0, 0);
|
|
|
|
prc->Zero();
|
|
if (kginDefault == gin)
|
|
{
|
|
gin = _ginDefault;
|
|
if (kginDefault == gin)
|
|
gin = _ginDefGob;
|
|
}
|
|
|
|
GetRcVis(&rc, cooLocal);
|
|
if (rc.FEmpty() || ginNil == gin)
|
|
return fFalse;
|
|
|
|
for (pgob = this; pvNil != pgob && pgob->_hwnd == hNil;
|
|
pgob = pgob->_pgobPar)
|
|
{
|
|
dpt.Offset(pgob->_rcCur.xpLeft, pgob->_rcCur.ypTop);
|
|
}
|
|
rc.Offset(dpt.xp, dpt.yp);
|
|
if (pvNil == pgob)
|
|
return fFalse;
|
|
|
|
if (kginSysInval != gin)
|
|
{
|
|
//get any marked area
|
|
vpappb->FGetMarkedRc(pgob->_hwnd, prc);
|
|
}
|
|
|
|
if (kginMark != gin)
|
|
{
|
|
//get any system invalidated area
|
|
RCS rcs;
|
|
RC rcT;
|
|
|
|
#ifdef WIN
|
|
GetUpdateRect(pgob->_hwnd, &rcs, fFalse);
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
PPRT pprt;
|
|
|
|
rcs = (*pgob->_hwnd->updateRgn)->rgnBBox;
|
|
GetPort(&pprt);
|
|
SetPort(&pgob->_hwnd->port);
|
|
GlobalToLocal((PTS *)&rcs);
|
|
GlobalToLocal((PTS *)&rcs + 1);
|
|
SetPort(pprt);
|
|
#endif //MAC
|
|
rcT = RC(rcs);
|
|
if (rcT.FIntersect(&rc))
|
|
prc->Union(&rcT);
|
|
}
|
|
prc->Offset(-dpt.xp, -dpt.yp);
|
|
|
|
return !prc->FEmpty();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Scrolls the given rectangle in the GOB. Translates any invalid portion.
|
|
Handles this being covered by any GOBs or system windows. If prc is
|
|
nil, the entire content rectangle is used.
|
|
***************************************************************************/
|
|
void GOB::Scroll(RC *prc, long dxp, long dyp, long gin,
|
|
RC *prcBad1, RC *prcBad2)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(prc);
|
|
AssertNilOrVarMem(prcBad1);
|
|
AssertNilOrVarMem(prcBad2);
|
|
RC rc, rcBad1, rcBad2, rcInval, rcT;
|
|
PT dpt(0, 0);
|
|
PGOB pgob, pgobT;
|
|
GTE gte;
|
|
ulong grfgte, grfgteIn;
|
|
bool fFound;
|
|
|
|
if (kginDefault == gin)
|
|
{
|
|
gin = _ginDefault;
|
|
if (kginDefault == gin)
|
|
gin = _ginDefGob;
|
|
}
|
|
|
|
if (pvNil != prcBad1)
|
|
prcBad1->Zero();
|
|
if (pvNil != prcBad2)
|
|
prcBad2->Zero();
|
|
|
|
if (dxp == 0 && dyp == 0)
|
|
return;
|
|
|
|
GetRcVis(&rc, cooLocal);
|
|
if (pvNil != prc && !rc.FIntersect(prc))
|
|
return;
|
|
|
|
for (pgob = this; pvNil != pgob && pgob->_hwnd == hNil;
|
|
pgob = pgob->_pgobPar)
|
|
{
|
|
dpt.Offset(pgob->_rcCur.xpLeft, pgob->_rcCur.ypTop);
|
|
}
|
|
rc.Offset(dpt.xp, dpt.yp);
|
|
if (pvNil == pgob)
|
|
return;
|
|
|
|
//check for GOBs on top of this one.
|
|
gte.Init(pgob, fgteBackToFront);
|
|
fFound = fFalse;
|
|
grfgteIn = fgteNil;
|
|
while (gte.FNextGob(&pgobT, &grfgte, grfgteIn))
|
|
{
|
|
if (!(grfgte & fgtePre))
|
|
continue;
|
|
|
|
if (!fFound)
|
|
{
|
|
fFound = pgobT == this;
|
|
continue;
|
|
}
|
|
pgobT->GetRc(&rcT, cooHwnd);
|
|
if (rcT.FIntersect(&rc))
|
|
{
|
|
//there is a GOB on top of this one, just invalidate the
|
|
//rectangle to be scrolled
|
|
pgob->ValidRc(&rc, kginDraw);
|
|
pgob->InvalRc(&rc, gin);
|
|
if (pvNil != prcBad1)
|
|
prcBad1->OffsetCopy(&rc, -dpt.xp, -dpt.yp);
|
|
return;
|
|
}
|
|
grfgteIn = fgteSkipToSib;
|
|
}
|
|
|
|
//translate any marked area
|
|
if (FGetRcInval(&rcT, kginMark))
|
|
{
|
|
//something is marked
|
|
rcT.Offset(dpt.xp, dpt.yp);
|
|
if (rcT.FIntersect(&rc))
|
|
{
|
|
pgob->ValidRc(&rcT, kginMark);
|
|
rcT.Offset(dxp, dyp);
|
|
if (rcT.FIntersect(&rc))
|
|
pgob->InvalRc(&rcT, kginMark);
|
|
}
|
|
}
|
|
|
|
#ifdef WIN
|
|
// SW_INVALIDATE invalidates any uncovered stuff and translates any
|
|
// previously invalid stuff
|
|
RCS rcs = RCS(rc);
|
|
ScrollWindowEx(pgob->_hwnd, dxp, dyp, pvNil, &rcs,
|
|
hNil, pvNil, SW_INVALIDATE);
|
|
|
|
// compute the bad rectangles
|
|
GNV::GetBadRcForScroll(&rc, dxp, dyp, &rcBad1, &rcBad2);
|
|
|
|
if (pvNil != prcBad1)
|
|
prcBad1->OffsetCopy(&rcBad1, -dpt.xp, -dpt.yp);
|
|
if (pvNil != prcBad2)
|
|
prcBad2->OffsetCopy(&rcBad2, -dpt.xp, -dpt.yp);
|
|
|
|
switch (gin)
|
|
{
|
|
default:
|
|
Bug("bad gin");
|
|
break;
|
|
case kginDraw:
|
|
UpdateWindow(pgob->_hwnd);
|
|
break;
|
|
case kginSysInval:
|
|
break;
|
|
case kginMark:
|
|
vpappb->MarkRc(&rcBad1, pgob);
|
|
vpappb->MarkRc(&rcBad2, pgob);
|
|
// fall through
|
|
case ginNil:
|
|
if (!rcBad1.FEmpty())
|
|
{
|
|
rcs = RCS(rcBad1);
|
|
ValidateRect(pgob->_hwnd, &rcs);
|
|
}
|
|
if (!rcBad2.FEmpty())
|
|
{
|
|
rcs = RCS(rcBad2);
|
|
ValidateRect(pgob->_hwnd, &rcs);
|
|
}
|
|
break;
|
|
}
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
HRGN hrgn;
|
|
|
|
//Make sure the vis region intersected with the rectangle to scroll is
|
|
//a rectangle
|
|
if (!FCreateRgn(&hrgn, &rc) ||
|
|
!FIntersectRgn(hrgn, pgob->_hwnd->port.visRgn, hrgn) ||
|
|
!FRectRgn(hrgn, &rc))
|
|
{
|
|
//there is something on top of this one, just invalidate the
|
|
//rectangle to be scrolled
|
|
FreePhrgn(&hrgn);
|
|
pgob->ValidRc(&rc, kginDraw);
|
|
pgob->InvalRc(&rc, gin);
|
|
if (pvNil != prcBad1)
|
|
prcBad1->OffsetCopy(&rc, -dpt.xp, -dpt.yp);
|
|
return;
|
|
}
|
|
FreePhrgn(&hrgn);
|
|
|
|
GNV gnv(pgob);
|
|
gnv.ScrollRc(&rc, dxp, dyp, &rcBad1, &rcBad2);
|
|
|
|
//translate any invalid area
|
|
if (FGetRcInval(&rcT, kginSysInval))
|
|
{
|
|
//something is invalid
|
|
rcT.Offset(dpt.xp, dpt.yp);
|
|
if (rcT.FIntersect(&rc))
|
|
{
|
|
pgob->ValidRc(&rcT, kginSysInval);
|
|
rcT.Offset(dxp, dyp);
|
|
if (rcT.FIntersect(&rc))
|
|
pgob->InvalRc(&rcT, kginSysInval);
|
|
}
|
|
}
|
|
|
|
if (pvNil != prcBad1)
|
|
prcBad1->OffsetCopy(&rcBad1, -dpt.xp, -dpt.yp);
|
|
if (pvNil != prcBad2)
|
|
prcBad2->OffsetCopy(&rcBad2, -dpt.xp, -dpt.yp);
|
|
|
|
switch (gin)
|
|
{
|
|
default:
|
|
Bug("bad gin");
|
|
//fall through
|
|
case ginNil:
|
|
break;
|
|
case kginDraw:
|
|
vpappb->MarkRc(&rcBad1, pgob);
|
|
vpappb->MarkRc(&rcBad2, pgob);
|
|
vpappb->UpdateMarked();
|
|
break;
|
|
case kginSysInval:
|
|
pgob->InvalRc(&rcBad1);
|
|
pgob->InvalRc(&rcBad2);
|
|
break;
|
|
case kginMark:
|
|
vpappb->MarkRc(&rcBad1, pgob);
|
|
vpappb->MarkRc(&rcBad2, pgob);
|
|
break;
|
|
}
|
|
#endif //MAC
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the gob and its children into the given port. If the pgpt is nil,
|
|
use the GOB's UI (natural) port. If the prc is pvNil, use the GOB's
|
|
rectangle based at (0, 0). If prcClip is not nil, only GOB's that
|
|
intersect prcClip will be drawn. prcClip is in the GOB's local
|
|
coordinates.
|
|
***************************************************************************/
|
|
void GOB::DrawTree(PGPT pgpt, RC *prc, RC *prcClip, ulong grfgob)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pgpt, 0);
|
|
AssertNilOrVarMem(prc);
|
|
AssertNilOrVarMem(prcClip);
|
|
RC rcSrc, rcClip, rcSrcGob, rcClipGob, rcVis, rc;
|
|
//to translate from this->local to pgob->local coordinates add dpt
|
|
PT dpt;
|
|
|
|
if (pgpt == pvNil && (pgpt = _pgpt) == pvNil)
|
|
{
|
|
Bug("no port to draw to");
|
|
return;
|
|
}
|
|
if (pvNil != prc && prc->FEmpty())
|
|
return;
|
|
|
|
dpt = _rcCur.PtTopLeft();
|
|
|
|
//get the source and clip rectangles in local (this) coordinates
|
|
rcSrc = _rcCur;
|
|
if (rcSrc.FEmpty())
|
|
return;
|
|
rcSrc.OffsetToOrigin();
|
|
if (pvNil == prcClip)
|
|
rcClip = rcSrc;
|
|
else if (!rcClip.FIntersect(prcClip, &rcSrc))
|
|
return;
|
|
|
|
GNV gnv(pgpt);
|
|
GTE gte;
|
|
ulong grfgte, grfgteIn;
|
|
PGOB pgob;
|
|
|
|
gte.Init(this, fgteBackToFront);
|
|
grfgteIn = fgteNil;
|
|
while (gte.FNextGob(&pgob, &grfgte, grfgteIn))
|
|
{
|
|
if (pgob->_pgpt != _pgpt || pgob->_rcCur.FEmpty())
|
|
goto LNextSib;
|
|
|
|
grfgteIn = fgteNil;
|
|
if (grfgte & fgtePre)
|
|
{
|
|
if (grfgob & (fgobAutoVis | fgobUseVis))
|
|
{
|
|
pgob->GetRcVis(&rcVis, cooLocal);
|
|
if (rcVis.FEmpty())
|
|
goto LNextSib;
|
|
}
|
|
|
|
//get the source and clip rectangles in local (pgob) coordinates
|
|
rcSrcGob = pgob->_rcCur;
|
|
dpt.xp -= rcSrcGob.xpLeft;
|
|
dpt.yp -= rcSrcGob.ypTop;
|
|
rcSrcGob.OffsetToOrigin();
|
|
rcClipGob = rcClip + dpt;
|
|
if (!rcClipGob.FIntersect(&rcSrcGob))
|
|
goto LOffsetNextSib;
|
|
|
|
// set the source rectangle
|
|
gnv.SetRcSrc(&rcSrcGob);
|
|
|
|
//set the dest rc
|
|
rc = rcSrcGob - dpt;
|
|
if (pvNil != prc)
|
|
rc.Map(&rcSrc, prc);
|
|
gnv.SetRcDst(&rc);
|
|
|
|
//set the vis rectangle
|
|
if (grfgob & (fgobAutoVis | fgobUseVis))
|
|
{
|
|
if (!rcVis.FIntersect(&rcClipGob))
|
|
{
|
|
LOffsetNextSib:
|
|
dpt.xp += pgob->_rcCur.xpLeft;
|
|
dpt.yp += pgob->_rcCur.ypTop;
|
|
LNextSib:
|
|
grfgteIn = fgteSkipToSib;
|
|
continue;
|
|
}
|
|
|
|
if (!(grfgob & fgobUseVis) && rcSrcGob == rcVis)
|
|
gnv.SetRcVis(pvNil);
|
|
else
|
|
gnv.SetRcVis(&rcVis);
|
|
rcClipGob = rcVis;
|
|
}
|
|
|
|
//draw the gob
|
|
pgob->Draw(&gnv, &rcClipGob);
|
|
}
|
|
if (grfgte & fgtePost)
|
|
{
|
|
dpt.xp += pgob->_rcCur.xpLeft;
|
|
dpt.yp += pgob->_rcCur.ypTop;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the gob and its children into the given port. If the pgpt is nil,
|
|
use the GOB's UI (natural) port. If the prc is pvNil, use the GOB's
|
|
rectangle based at (0, 0). Only GOB's that intersect pregn will be
|
|
drawn. pregn is in the GOB's local coordinates.
|
|
***************************************************************************/
|
|
void GOB::DrawTreeRgn(PGPT pgpt, RC *prc, REGN *pregn, ulong grfgob)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pgpt, 0);
|
|
AssertNilOrVarMem(prc);
|
|
AssertPo(pregn, 0);
|
|
RC rcSrc, rcSrcGob, rcClipGob, rcVis, rc;
|
|
//to translate from this->local to pgob->local coordinates add dpt
|
|
PT dpt;
|
|
|
|
if (pgpt == pvNil && (pgpt = _pgpt) == pvNil)
|
|
{
|
|
Bug("no port to draw to");
|
|
return;
|
|
}
|
|
if (pvNil != prc && prc->FEmpty())
|
|
return;
|
|
if (pregn->FEmpty())
|
|
return;
|
|
|
|
dpt = _rcCur.PtTopLeft();
|
|
|
|
//get the source rectangle and clip region in local (this) coordinates
|
|
rcSrc = _rcCur;
|
|
rcSrc.OffsetToOrigin();
|
|
if (rcSrc.FEmpty())
|
|
return;
|
|
|
|
GNV gnv(pgpt);
|
|
GTE gte;
|
|
ulong grfgte, grfgteIn;
|
|
PGOB pgob;
|
|
PREGN pregnClip;
|
|
PREGN pregnClipGob = pvNil;
|
|
|
|
if (pvNil == (pregnClip = REGN::PregnNew(&rcSrc)) ||
|
|
pvNil == (pregnClipGob = REGN::PregnNew()) ||
|
|
!pregnClip->FIntersect(pregn))
|
|
{
|
|
goto LFail;
|
|
}
|
|
if (pregnClip->FEmpty())
|
|
goto LDone;
|
|
|
|
gte.Init(this, fgteBackToFront);
|
|
grfgteIn = fgteNil;
|
|
while (gte.FNextGob(&pgob, &grfgte, grfgteIn))
|
|
{
|
|
if (pgob->_pgpt != _pgpt || pgob->_rcCur.FEmpty())
|
|
goto LNextSib;
|
|
|
|
grfgteIn = fgteNil;
|
|
if (grfgte & fgtePre)
|
|
{
|
|
if (grfgob & (fgobAutoVis | fgobUseVis))
|
|
{
|
|
pgob->GetRcVis(&rcVis, cooLocal);
|
|
if (rcVis.FEmpty())
|
|
goto LNextSib;
|
|
}
|
|
|
|
//get the source and clip rectangles in local (pgob) coordinates
|
|
rcSrcGob = pgob->_rcCur;
|
|
dpt.xp -= rcSrcGob.xpLeft;
|
|
dpt.yp -= rcSrcGob.ypTop;
|
|
rcSrcGob.OffsetToOrigin();
|
|
|
|
pregnClipGob->SetRc(&rcSrcGob);
|
|
pregnClipGob->Offset(-dpt.xp, -dpt.yp);
|
|
if (!pregnClipGob->FIntersect(pregnClip))
|
|
goto LFail;
|
|
if (pregnClipGob->FEmpty(&rcClipGob))
|
|
goto LOffsetNextSib;
|
|
|
|
rcClipGob.Offset(dpt.xp, dpt.yp);
|
|
|
|
// set the source rectangle
|
|
gnv.SetRcSrc(&rcSrcGob);
|
|
|
|
//set the dest rc
|
|
rc = rcSrcGob - dpt;
|
|
if (pvNil != prc)
|
|
rc.Map(&rcSrc, prc);
|
|
gnv.SetRcDst(&rc);
|
|
|
|
//set the vis rectangle
|
|
if (grfgob & (fgobAutoVis | fgobUseVis))
|
|
{
|
|
if (!rcVis.FIntersect(&rcClipGob))
|
|
{
|
|
LOffsetNextSib:
|
|
dpt.xp += pgob->_rcCur.xpLeft;
|
|
dpt.yp += pgob->_rcCur.ypTop;
|
|
LNextSib:
|
|
grfgteIn = fgteSkipToSib;
|
|
continue;
|
|
}
|
|
|
|
if (!(grfgob & fgobUseVis) && rcSrcGob == rcVis)
|
|
gnv.SetRcVis(pvNil);
|
|
else
|
|
gnv.SetRcVis(&rcVis);
|
|
}
|
|
|
|
// draw the gob
|
|
// NOTE: we use pregn and not pregnClip or pregnClipGob for speed.
|
|
// Using pregn, the cached hrgn stuff kicks in to only require
|
|
// one hrgn creation. If we use pregnClip we only have one hrgn
|
|
// creation here, but another one when the offscreen bitmap is
|
|
// copied to the screen. Using pregnClipGob would cause lots
|
|
// of hregn creations.
|
|
pgpt->ClipToRegn(&pregn);
|
|
pgob->Draw(&gnv, &rcClipGob);
|
|
pgpt->ClipToRegn(&pregn);
|
|
}
|
|
if (grfgte & fgtePost)
|
|
{
|
|
dpt.xp += pgob->_rcCur.xpLeft;
|
|
dpt.yp += pgob->_rcCur.ypTop;
|
|
}
|
|
}
|
|
|
|
LDone:
|
|
ReleasePpo(&pregnClip);
|
|
ReleasePpo(&pregnClipGob);
|
|
return;
|
|
|
|
LFail:
|
|
pregn->FEmpty(&rc);
|
|
DrawTree(pgpt, prc, &rc, grfgob);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the GOB into the given graphics environment. On entry, the source
|
|
rectangle of the GNV is set to (0, 0, dxp, dyp), where dxp and dyp are
|
|
the width and height of the gob. The gob is free to change the source
|
|
rectangle, but should not touch the destination rectangle.
|
|
***************************************************************************/
|
|
void GOB::Draw(PGNV pgnv, RC *prcClip)
|
|
{
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Make this gob fill up its parent's interior.
|
|
***************************************************************************/
|
|
void GOB::Maximize(void)
|
|
{
|
|
AssertThis(0);
|
|
_rcAbs.Zero();
|
|
_rcRel.xpLeft = _rcRel.ypTop = krelZero;
|
|
_rcRel.xpRight = _rcRel.ypBottom = krelOne;
|
|
_SetRcCur();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the gob's position. Invalidates both the old and new position.
|
|
***************************************************************************/
|
|
void GOB::SetPos(RC *prcAbs, RC *prcRel)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(prcAbs);
|
|
AssertNilOrVarMem(prcRel);
|
|
if (prcAbs == pvNil)
|
|
_rcAbs.Zero();
|
|
else
|
|
_rcAbs = *prcAbs;
|
|
|
|
if (prcRel == pvNil)
|
|
_rcRel.Zero();
|
|
else
|
|
_rcRel = *prcRel;
|
|
|
|
_SetRcCur();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the gob's position.
|
|
***************************************************************************/
|
|
void GOB::GetPos(RC *prcAbs, RC *prcRel)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(prcAbs);
|
|
AssertNilOrVarMem(prcRel);
|
|
if (pvNil != prcAbs)
|
|
*prcAbs = _rcAbs;
|
|
if (pvNil != prcRel)
|
|
*prcRel = _rcRel;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the gob's rectangle from its hwnd.
|
|
***************************************************************************/
|
|
void GOB::SetRcFromHwnd(void)
|
|
{
|
|
AssertThis(0);
|
|
Assert(_hwnd != hNil, "no hwnd");
|
|
_SetRcCur();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the bounding rectangle of the gob in the given coordinates.
|
|
***************************************************************************/
|
|
void GOB::GetRc(RC *prc, long coo)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
PT dpt;
|
|
|
|
*prc = _rcCur;
|
|
_HwndGetDptFromCoo(&dpt, coo);
|
|
prc->Offset(dpt.xp - _rcCur.xpLeft, dpt.yp - _rcCur.ypTop);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the visible rectangle of the gob in the given coordinates.
|
|
***************************************************************************/
|
|
void GOB::GetRcVis(RC *prc, long coo)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
PT dpt;
|
|
|
|
*prc = _rcVis;
|
|
_HwndGetDptFromCoo(&dpt, coo);
|
|
prc->Offset(dpt.xp - _rcCur.xpLeft, dpt.yp - _rcCur.ypTop);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the rectangle for the gob in cooHwnd coordinates and return the
|
|
enclosing hwnd (if there is one). This is a protected API.
|
|
***************************************************************************/
|
|
HWND GOB::_HwndGetRc(RC *prc)
|
|
{
|
|
PT dpt;
|
|
HWND hwnd;
|
|
|
|
*prc = _rcCur;
|
|
hwnd = _HwndGetDptFromCoo(&dpt, cooHwnd);
|
|
prc->Offset(dpt.xp - _rcCur.xpLeft, dpt.yp - _rcCur.ypTop);
|
|
return hwnd;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the hwnd that contains this GOB.
|
|
***************************************************************************/
|
|
HWND GOB::HwndContainer(void)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob = this;
|
|
|
|
while (pvNil != pgob)
|
|
{
|
|
if (hNil != pgob->_hwnd)
|
|
return pgob->_hwnd;
|
|
pgob = pgob->_pgobPar;
|
|
}
|
|
return hNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map a point from cooSrc coordinates to cooDst coordinates (relative
|
|
to the gob).
|
|
***************************************************************************/
|
|
void GOB::MapPt(PT *ppt, long cooSrc, long cooDst)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ppt);
|
|
PT dpt;
|
|
|
|
_HwndGetDptFromCoo(&dpt, cooSrc);
|
|
ppt->xp -= dpt.xp;
|
|
ppt->yp -= dpt.yp;
|
|
_HwndGetDptFromCoo(&dpt, cooDst);
|
|
ppt->xp += dpt.xp;
|
|
ppt->yp += dpt.yp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map an rc from cooSrc coordinates to cooDst coordinates (relative to
|
|
the gob).
|
|
***************************************************************************/
|
|
void GOB::MapRc(RC *prc, long cooSrc, long cooDst)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
PT dpt;
|
|
|
|
_HwndGetDptFromCoo(&dpt, cooSrc);
|
|
prc->Offset(-dpt.xp, -dpt.yp);
|
|
_HwndGetDptFromCoo(&dpt, cooDst);
|
|
prc->Offset(dpt.xp, dpt.yp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the dxp and dyp to map from local coordinates to coo coordinates.
|
|
If coo is cooHwnd or cooGlobal, also return the containing hwnd
|
|
(otherwise return hNil).
|
|
***************************************************************************/
|
|
HWND GOB::_HwndGetDptFromCoo(PT *pdpt, long coo)
|
|
{
|
|
PGOB pgob, pgobT;
|
|
HWND hwnd = hNil;
|
|
|
|
switch (coo)
|
|
{
|
|
default:
|
|
Assert(coo == cooLocal, "bad coo");
|
|
pdpt->xp = pdpt->yp = 0;
|
|
break;
|
|
|
|
case cooParent:
|
|
pdpt->xp = _rcCur.xpLeft;
|
|
pdpt->yp = _rcCur.ypTop;
|
|
break;
|
|
|
|
case cooGpt:
|
|
pdpt->xp = pdpt->yp = 0;
|
|
for (pgob = this;
|
|
(pgobT = pgob->_pgobPar) != pvNil && pgobT->_pgpt == _pgpt;
|
|
pgob = pgobT)
|
|
{
|
|
pdpt->xp += pgob->_rcCur.xpLeft;
|
|
pdpt->yp += pgob->_rcCur.ypTop;
|
|
}
|
|
break;
|
|
|
|
case cooHwnd:
|
|
case cooGlobal:
|
|
pdpt->xp = pdpt->yp = 0;
|
|
for (pgob = this; pgob != pvNil && pgob->_hwnd == hNil;
|
|
pgob = pgob->_pgobPar)
|
|
{
|
|
pdpt->xp += pgob->_rcCur.xpLeft;
|
|
pdpt->yp += pgob->_rcCur.ypTop;
|
|
}
|
|
if (pvNil != pgob)
|
|
hwnd = pgob->_hwnd;
|
|
if (cooGlobal == coo && hNil != hwnd)
|
|
{
|
|
//Map from Hwnd to screen
|
|
PTS pts;
|
|
pts = PTS(*pdpt);
|
|
#ifdef WIN
|
|
ClientToScreen(hwnd, &pts);
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
PPRT pprt;
|
|
GetPort(&pprt);
|
|
SetPort(&hwnd->port);
|
|
LocalToGlobal(&pts);
|
|
SetPort(pprt);
|
|
#endif //MAC
|
|
*pdpt = PT(pts);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the minimum and maximum size for a gob.
|
|
***************************************************************************/
|
|
void GOB::GetMinMax(RC *prcMinMax)
|
|
{
|
|
prcMinMax->xpLeft = prcMinMax->ypTop = 0;
|
|
//yes kswMax for safety
|
|
prcMinMax->xpRight = prcMinMax->ypBottom = kswMax;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to find the gob containing the given point (in global
|
|
coordinates). If the mouse isn't over a GOB, this returns pvNil and
|
|
sets *pptLocal to the passed in (xp, yp).
|
|
***************************************************************************/
|
|
PGOB GOB::PgobFromPtGlobal(long xp, long yp, PT *pptLocal)
|
|
{
|
|
AssertNilOrVarMem(pptLocal);
|
|
HWND hwnd;
|
|
PTS pts;
|
|
PGOB pgob;
|
|
|
|
#ifdef MAC
|
|
PPRT pprt;
|
|
|
|
pts.h = (short)xp;
|
|
pts.v = (short)yp;
|
|
if (inContent != FindWindow(pts, (WindowPtr *)&hwnd) ||
|
|
hNil == hwnd ||
|
|
pvNil == (pgob = PgobFromHwnd(hwnd)))
|
|
{
|
|
if (pvNil != pptLocal)
|
|
{
|
|
pptLocal->xp = xp;
|
|
pptLocal->yp = yp;
|
|
}
|
|
return pvNil;
|
|
}
|
|
GetPort(&pprt);
|
|
SetPort(&hwnd->port);
|
|
GlobalToLocal(&pts);
|
|
SetPort(pprt);
|
|
return pgob->PgobFromPt(pts.h, pts.v, pptLocal);
|
|
#endif //MAC
|
|
#ifdef WIN
|
|
pts.x = xp;
|
|
pts.y = yp;
|
|
if (hNil == (hwnd = WindowFromPoint(pts)) ||
|
|
pvNil == (pgob = PgobFromHwnd(hwnd)))
|
|
{
|
|
if (pvNil != pptLocal)
|
|
{
|
|
pptLocal->xp = xp;
|
|
pptLocal->yp = yp;
|
|
}
|
|
return pvNil;
|
|
}
|
|
ScreenToClient(hwnd, &pts);
|
|
return pgob->PgobFromPt(pts.x, pts.y, pptLocal);
|
|
#endif //WIN
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determine which gob in the tree starting with this GOB the given point
|
|
is in. This may return pvNil if no gob claims to contain the given
|
|
point. xp, yp is assumed to be in this gob's parent's coordinates.
|
|
This is recursive, so a GOB can build it's own world and hit testing
|
|
method.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobFromPt(long xp, long yp, PT *pptLocal)
|
|
{
|
|
AssertThis(0);
|
|
|
|
xp -= _rcCur.xpLeft;
|
|
yp -= _rcCur.ypTop;
|
|
|
|
if (FPtInBounds(xp, yp))
|
|
{
|
|
// the point is in our bounding rectangle, so give the children
|
|
// a whack at it
|
|
PGOB pgob, pgobT;
|
|
|
|
for (pgob = _pgobChd; pvNil != pgob; pgob = pgob->_pgobSib)
|
|
{
|
|
if (pvNil != (pgobT = pgob->PgobFromPt(xp, yp, pptLocal)))
|
|
return pgobT;
|
|
}
|
|
}
|
|
|
|
// call FPtIn whether or not FInBounds returned true so a parent can will some
|
|
// extra space to a child
|
|
if (FPtIn(xp, yp))
|
|
{
|
|
if (pptLocal != pvNil)
|
|
{
|
|
pptLocal->xp = xp;
|
|
pptLocal->yp = yp;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determine whether the given point (in this gob's local coordinates)
|
|
is in this gob. This will be subclassed by all non-rectangular gobs
|
|
(including ones that don't want to respond to the mouse at all).
|
|
We handle tool tips here to avoid bugs of omission and for convenience.
|
|
***************************************************************************/
|
|
bool GOB::FPtIn(long xp, long yp)
|
|
{
|
|
AssertThis(0);
|
|
RC rc;
|
|
|
|
// tool tips and their children are "invisible".
|
|
if (khidToolTip == Hid())
|
|
return fFalse;
|
|
|
|
GetRc(&rc, cooLocal);
|
|
return rc.FPtIn(xp, yp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determine whether the given point (in this gob's local coordinates)
|
|
is in this gob's bounding rectangle. This indicates whether it's OK to
|
|
ask the GOB's children whether the point is in them. This will be
|
|
subclassed by all GOBs that don't want to respond to the mouse. We
|
|
handle tool tips here to avoid bugs of omission and for convenience.
|
|
If this returns false, PgobFromPt will still call FPtIn.
|
|
***************************************************************************/
|
|
bool GOB::FPtInBounds(long xp, long yp)
|
|
{
|
|
AssertThis(0);
|
|
RC rc;
|
|
|
|
// tool tips and their children are "invisible".
|
|
if (khidToolTip == Hid())
|
|
return fFalse;
|
|
|
|
GetRc(&rc, cooLocal);
|
|
return rc.FPtIn(xp, yp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Default mouse down handler just enqueues a cidActivateSel, cidSelIdle and
|
|
a cidTrackMouse command.
|
|
***************************************************************************/
|
|
void GOB::MouseDown(long xp, long yp, long cact, ulong grfcust)
|
|
{
|
|
AssertThis(0);
|
|
Assert(grfcust & fcustMouse, "grfcust wrong");
|
|
CMD_MOUSE cmd;
|
|
|
|
vpcex->EnqueueCid(cidActivateSel, this);
|
|
vpcex->EnqueueCid(cidSelIdle, pvNil, pvNil, fTrue, Hid());
|
|
cmd.cid = cidMouseDown;
|
|
cmd.pcmh = this;
|
|
cmd.pgg = pvNil;
|
|
cmd.xp = xp;
|
|
cmd.yp = yp;
|
|
cmd.grfcust = grfcust;
|
|
cmd.cact = cact;
|
|
vpcex->EnqueueCmd((PCMD)&cmd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the _rcCur values based on _rcAbs and _rcRel. If there is an OS
|
|
window associated with this GOB, set _rcCur based on the hwnd.
|
|
Invalidates the old and new rectangles.
|
|
***************************************************************************/
|
|
void GOB::_SetRcCur(void)
|
|
{
|
|
PGOB pgob;
|
|
GTE gte;
|
|
ulong grfgte;
|
|
RC rc, rcVis;
|
|
|
|
//invalidate the original rc
|
|
InvalRc(pvNil);
|
|
|
|
gte.Init(this, fgteNil);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if (!(grfgte & fgtePre))
|
|
continue;
|
|
|
|
//get the new rc and the rcVis of the parent (in the parent's local
|
|
//coordinates)
|
|
|
|
if (pgob->_hwnd != hNil)
|
|
{
|
|
RCS rcs;
|
|
|
|
GetClientRect(pgob->_hwnd, &rcs);
|
|
rc = rcs;
|
|
rcVis.Max();
|
|
}
|
|
else if (pgob->_pgobPar != pvNil)
|
|
{
|
|
long dxp;
|
|
long dyp;
|
|
|
|
dxp = pgob->_pgobPar->_rcCur.Dxp();
|
|
dyp = pgob->_pgobPar->_rcCur.Dyp();
|
|
rc.xpLeft = pgob->_rcAbs.xpLeft +
|
|
LwMulDiv(dxp, pgob->_rcRel.xpLeft, krelOne);
|
|
rc.ypTop = pgob->_rcAbs.ypTop +
|
|
LwMulDiv(dyp, pgob->_rcRel.ypTop, krelOne);
|
|
rc.xpRight = pgob->_rcAbs.xpRight +
|
|
LwMulDiv(dxp, pgob->_rcRel.xpRight, krelOne);
|
|
rc.ypBottom = pgob->_rcAbs.ypBottom +
|
|
LwMulDiv(dyp, pgob->_rcRel.ypBottom, krelOne);
|
|
pgob->_pgobPar->GetRcVis(&rcVis, cooLocal);
|
|
}
|
|
else
|
|
{
|
|
rc = pgob->_rcAbs;
|
|
rcVis.Max();
|
|
}
|
|
|
|
//intersect the parents visible portion with the new rc to get
|
|
//this gob's visible portion
|
|
rcVis.FIntersect(&rc);
|
|
|
|
pgob->_rcCur = rc;
|
|
pgob->_rcVis = rcVis;
|
|
|
|
if (grfgte & fgteRoot)
|
|
{
|
|
//invalidate the new rectangle - we do it here so children
|
|
//can draw and validate themselves if they want
|
|
InvalRc(pvNil);
|
|
}
|
|
|
|
//tell the gob that it has a new rectangle
|
|
pgob->_NewRc();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the previous sibling for the gob.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobPrevSib(void)
|
|
{
|
|
PGOB pgob;
|
|
|
|
pgob = _pgobPar == pvNil ? _pgobScreen : _pgobPar->_pgobChd;
|
|
if (pgob == this)
|
|
return pvNil;
|
|
|
|
for ( ; pgob != pvNil && pgob->_pgobSib != this; pgob = pgob->_pgobSib)
|
|
;
|
|
|
|
if (pgob == pvNil)
|
|
{
|
|
Bug("corrupt gob tree");
|
|
return pvNil;
|
|
}
|
|
Assert(pgob->_pgobSib == this, "wrong logic");
|
|
return pgob;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the last child of the gob.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobLastChild(void)
|
|
{
|
|
PGOB pgob;
|
|
|
|
if ((pgob = _pgobChd) == pvNil)
|
|
return pvNil;
|
|
|
|
for ( ; pgob->_pgobSib != pvNil; pgob = pgob->_pgobSib)
|
|
;
|
|
|
|
Assert(pgob->_pgobSib == pvNil, "wrong logic");
|
|
return pgob;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new MDI window and attach it to the gob.
|
|
***************************************************************************/
|
|
bool GOB::FCreateAndAttachMdi(PSTN pstnTitle)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstnTitle, 0);
|
|
HWND hwnd;
|
|
|
|
if ((hwnd = _HwndNewMdi(pstnTitle)) == hNil)
|
|
return fFalse;
|
|
if (!FAttachHwnd(hwnd))
|
|
{
|
|
_DestroyHwnd(hwnd);
|
|
return fFalse;
|
|
}
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method: find the currently active MDI gob.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobMdiActive(void)
|
|
{
|
|
HWND hwnd;
|
|
|
|
if (hNil == (hwnd = HwndMdiActive()))
|
|
return pvNil;
|
|
return PgobFromHwnd(hwnd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method: find the first gob of the given class in the screen's gob
|
|
tree.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobFromClsScr(long cls)
|
|
{
|
|
if (pvNil == _pgobScreen)
|
|
return pvNil;
|
|
return _pgobScreen->PgobFromCls(cls);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find a gob in this gob's subtree that is of the given class.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobFromCls(long cls)
|
|
{
|
|
AssertThis(0);
|
|
GTE gte;
|
|
ulong grfgte;
|
|
PGOB pgob;
|
|
|
|
gte.Init(this, fgteNil);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if (pgob->FIs(cls))
|
|
return pgob;
|
|
}
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find a direct child of this gob of the given class.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobChildFromCls(long cls)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob;
|
|
|
|
for (pgob = _pgobChd; pvNil != pgob; pgob = pgob->_pgobSib)
|
|
{
|
|
if (pgob->FIs(cls))
|
|
return pgob;
|
|
}
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find a gob of the given class in the parent chain of this gob.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobParFromCls(long cls)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob;
|
|
|
|
for (pgob = _pgobPar; pvNil != pgob; pgob = pgob->_pgobPar)
|
|
{
|
|
if (pgob->FIs(cls))
|
|
return pgob;
|
|
}
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method: find the first gob with the given hid in the screen's gob
|
|
tree.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobFromHidScr(long hid)
|
|
{
|
|
Assert(hid != hidNil, "nil hid");
|
|
if (pvNil == _pgobScreen)
|
|
return pvNil;
|
|
|
|
return _pgobScreen->PgobFromHid(hid);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find a gob in this gobs subtree having the given hid.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobFromHid(long hid)
|
|
{
|
|
AssertThis(0);
|
|
GTE gte;
|
|
ulong grfgte;
|
|
PGOB pgob;
|
|
|
|
gte.Init(this, fgteNil);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if (pgob->Hid() == hid)
|
|
return pgob;
|
|
}
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find a direct child of this gob having the given hid.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobChildFromHid(long hid)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob;
|
|
|
|
for (pgob = _pgobChd; pvNil != pgob; pgob = pgob->_pgobSib)
|
|
{
|
|
if (pgob->Hid() == hid)
|
|
return pgob;
|
|
}
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find a gob with the given hid in the parent chain of this gob.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobParFromHid(long hid)
|
|
{
|
|
AssertThis(0);
|
|
PGOB pgob;
|
|
|
|
for (pgob = _pgobPar; pvNil != pgob; pgob = pgob->_pgobPar)
|
|
{
|
|
if (pgob->Hid() == hid)
|
|
return pgob;
|
|
}
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find a gob in this gobs subtree having the given gob run-time id.
|
|
***************************************************************************/
|
|
PGOB GOB::PgobFromGrid(long grid)
|
|
{
|
|
AssertThis(0);
|
|
GTE gte;
|
|
ulong grfgte;
|
|
PGOB pgob;
|
|
|
|
gte.Init(this, fgteNil);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if (pgob->Grid() == grid)
|
|
return pgob;
|
|
}
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handles a close command.
|
|
***************************************************************************/
|
|
bool GOB::FCmdCloseWnd(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
Release();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handles a mouse track command.
|
|
***************************************************************************/
|
|
bool GOB::FCmdTrackMouse(PCMD_MOUSE pcmd)
|
|
{
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Command function to handle a key stroke.
|
|
***************************************************************************/
|
|
bool GOB::FCmdKey(PCMD_KEY pcmd)
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Command function to handle a bad key command (sent by a child to
|
|
its parent).
|
|
***************************************************************************/
|
|
bool GOB::FCmdBadKey(PCMD_BADKEY pcmd)
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Do selection idle processing. Make sure the selection is on or off
|
|
according to rglw[0] (non-zero means on) and set rglw[0] to false.
|
|
Always return false.
|
|
***************************************************************************/
|
|
bool GOB::FCmdSelIdle(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Activate the selection. Default does nothing.
|
|
***************************************************************************/
|
|
bool GOB::FCmdActivateSel(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
The mouse moved in this GOB, set the cursor.
|
|
***************************************************************************/
|
|
bool GOB::FCmdMouseMove(PCMD_MOUSE pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
vpappb->SetCurs(_pcurs);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Drag the rectangle, restricting to [zpMin, zpLim). While zp is in
|
|
[zpMinActive, zpLimActive), the bar is filled with solid invert, otherwise
|
|
with patterned (50%) invert.
|
|
***************************************************************************/
|
|
long GOB::ZpDragRc(RC *prc, bool fVert, long zp, long zpMin, long zpLim,
|
|
long zpMinActive, long zpLimActive)
|
|
{
|
|
RC rcBound, rcActive;
|
|
PT pt, dpt;
|
|
bool fActive, fActiveNew, fDown;
|
|
GNV gnv(this);
|
|
|
|
if (fVert)
|
|
{
|
|
pt.xp = 0;
|
|
pt.yp = zp;
|
|
rcBound.Set(0, zpMin, 1, zpLim);
|
|
rcActive.Set(0, zpMinActive, 1, zpLimActive);
|
|
}
|
|
else
|
|
{
|
|
pt.xp = zp;
|
|
pt.yp = 0;
|
|
rcBound.Set(zpMin, 0, zpLim, 1);
|
|
rcActive.Set(zpMinActive, 0, zpLimActive, 1);
|
|
}
|
|
|
|
//draw the initial bar
|
|
fActive = rcActive.FPtIn(pt.xp, pt.yp);
|
|
if (fActive)
|
|
gnv.FillRc(prc, kacrInvert);
|
|
else
|
|
gnv.FillRcApt(prc, &vaptGray, kacrInvert, kacrClear);
|
|
|
|
for (;;)
|
|
{
|
|
GetPtMouse(&dpt, &fDown);
|
|
if (!fDown)
|
|
break;
|
|
|
|
//pin the pt to rcBound
|
|
rcBound.PinPt(&dpt);
|
|
Assert(dpt.xp == 0 || dpt.yp == 0, "bad pinned point");
|
|
if (pt == dpt)
|
|
continue;
|
|
|
|
//move the bar
|
|
fActiveNew = rcActive.FPtIn(dpt.xp, dpt.yp);
|
|
dpt -= pt;
|
|
if (FPure(fActive) == FPure(fActiveNew))
|
|
{
|
|
//invert the two pieces of the difference between
|
|
//the new and old rectangles
|
|
RC rc1, rc2;
|
|
long dzp;
|
|
|
|
rc1 = *prc;
|
|
if (fVert)
|
|
rc1.Transform(fptTranspose);
|
|
rc2 = rc1;
|
|
Assert(dpt.xp == 0 || dpt.yp == 0, "bad pinned point");
|
|
dzp = dpt.xp + dpt.yp;
|
|
rc1.Offset(dzp, 0);
|
|
if (dzp < 0)
|
|
SortLw(&rc1.xpRight, &rc2.xpLeft);
|
|
else
|
|
SortLw(&rc2.xpRight, &rc1.xpLeft);
|
|
if (fVert)
|
|
{
|
|
rc1.Transform(fptTranspose);
|
|
rc2.Transform(fptTranspose);
|
|
}
|
|
if (fActive)
|
|
{
|
|
gnv.FillRc(&rc1, kacrInvert);
|
|
gnv.FillRc(&rc2, kacrInvert);
|
|
}
|
|
else
|
|
{
|
|
gnv.FillRcApt(&rc1, &vaptGray, kacrInvert, kacrClear);
|
|
gnv.FillRcApt(&rc2, &vaptGray, kacrInvert, kacrClear);
|
|
}
|
|
*prc += dpt;
|
|
}
|
|
else if (fActive)
|
|
{
|
|
//just draw the two
|
|
gnv.FillRc(prc, kacrInvert);
|
|
*prc += dpt;
|
|
gnv.FillRcApt(prc, &vaptGray, kacrInvert, kacrClear);
|
|
}
|
|
else
|
|
{
|
|
//just draw the two
|
|
gnv.FillRcApt(prc, &vaptGray, kacrInvert, kacrClear);
|
|
*prc += dpt;
|
|
gnv.FillRc(prc, kacrInvert);
|
|
}
|
|
fActive = fActiveNew;
|
|
pt += dpt;
|
|
}
|
|
|
|
//erase the current bar
|
|
if (fActive)
|
|
gnv.FillRc(prc, kacrInvert);
|
|
else
|
|
gnv.FillRcApt(prc, &vaptGray, kacrInvert, kacrClear);
|
|
return fVert ? pt.yp : pt.xp;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the cursor for this GOB to pcurs.
|
|
***************************************************************************/
|
|
void GOB::SetCurs(PCURS pcurs)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pcurs, 0);
|
|
|
|
SwapVars(&pcurs, &_pcurs);
|
|
if (pvNil != _pcurs)
|
|
_pcurs->AddRef();
|
|
ReleasePpo(&pcurs);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the cursor for this GOB as indicated.
|
|
***************************************************************************/
|
|
void GOB::SetCursCno(PRCA prca, CNO cno)
|
|
{
|
|
AssertPo(prca, 0);
|
|
PCURS pcurs;
|
|
|
|
if (pvNil == (pcurs = (PCURS)prca->PbacoFetch(kctgCursor,
|
|
cno, CURS::FReadCurs)))
|
|
{
|
|
Warn("cursor not found");
|
|
return;
|
|
}
|
|
SetCurs(pcurs);
|
|
ReleasePpo(&pcurs);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the address of the variable list belonging to this gob. When the
|
|
gob is freed, the pointer is no longer valid.
|
|
***************************************************************************/
|
|
PGL *GOB::Ppglrtvm(void)
|
|
{
|
|
AssertThis(0);
|
|
return &_pglrtvm;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Put up a tool tip if this GOB has one.
|
|
***************************************************************************/
|
|
bool GOB::FEnsureToolTip(PGOB *ppgobCurTip, long xpMouse, long ypMouse)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ppgobCurTip);
|
|
AssertNilOrPo(*ppgobCurTip, 0);
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the state of the GOB. Must be non-zero.
|
|
***************************************************************************/
|
|
long GOB::LwState(void)
|
|
{
|
|
AssertThis(0);
|
|
return 1;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the GOB.
|
|
***************************************************************************/
|
|
void GOB::AssertValid(ulong grf)
|
|
{
|
|
GOB_PAR::AssertValid(0);
|
|
if (hNil != _hwnd)
|
|
{
|
|
Assert(0 == _rcCur.xpLeft && 0 == _rcCur.ypTop,
|
|
"_hwnd based gob not at (0, 0)");
|
|
}
|
|
if (pvNil != _pgpt)
|
|
{
|
|
AssertPo(_pgpt, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory referenced by the gob.
|
|
***************************************************************************/
|
|
void GOB::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
GOB_PAR::MarkMem();
|
|
MarkMemObj(_pgpt);
|
|
MarkMemObj(_pglrtvm);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for this gob and all descendent gobs.
|
|
***************************************************************************/
|
|
void GOB::MarkGobTree(void)
|
|
{
|
|
GTE gte;
|
|
PGOB pgob;
|
|
ulong grfgte;
|
|
|
|
gte.Init(this, fgteNil);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if (grfgte & fgtePre)
|
|
pgob->MarkMem();
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a GOB tree enumerator.
|
|
***************************************************************************/
|
|
GTE::GTE(void)
|
|
{
|
|
_es = esDone;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize a GOB tree enumerator.
|
|
***************************************************************************/
|
|
void GTE::Init(PGOB pgob, ulong grfgte)
|
|
{
|
|
_pgobRoot = pgob;
|
|
_pgobCur = pvNil;
|
|
_fBackWards = FPure(grfgte & fgteBackToFront);
|
|
_es = pgob == pvNil ? esDone : esStart;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Goes to the next node in the sub tree being enumerated. Returns false
|
|
iff the enumeration is done.
|
|
***************************************************************************/
|
|
bool GTE::FNextGob(PGOB *ppgob, ulong *pgrfgteOut, ulong grfgte)
|
|
{
|
|
PGOB pgobT;
|
|
|
|
*pgrfgteOut = fgteNil;
|
|
switch (_es)
|
|
{
|
|
case esStart:
|
|
_pgobCur = _pgobRoot;
|
|
*pgrfgteOut |= fgteRoot;
|
|
goto LCheckForKids;
|
|
|
|
case esGoDown:
|
|
if (!(grfgte & fgteSkipToSib))
|
|
{
|
|
pgobT = _fBackWards ?
|
|
_pgobCur->PgobLastChild() : _pgobCur->_pgobChd;
|
|
if (pgobT != pvNil)
|
|
{
|
|
_pgobCur = pgobT;
|
|
goto LCheckForKids;
|
|
}
|
|
}
|
|
//fall through
|
|
case esGoRight:
|
|
//go to the sibling (if there is one) or parent
|
|
if (_pgobCur == _pgobRoot)
|
|
{
|
|
_es = esDone;
|
|
return fFalse;
|
|
}
|
|
pgobT = _fBackWards ?
|
|
_pgobCur->PgobPrevSib() : _pgobCur->_pgobSib;
|
|
if (pgobT != pvNil)
|
|
{
|
|
_pgobCur = pgobT;
|
|
LCheckForKids:
|
|
*pgrfgteOut |= fgtePre;
|
|
if (_pgobCur->_pgobChd == pvNil)
|
|
{
|
|
*pgrfgteOut |= fgtePost;
|
|
_es = esGoRight;
|
|
}
|
|
else
|
|
_es = esGoDown;
|
|
}
|
|
else
|
|
{
|
|
//no more siblings, go to parent
|
|
_pgobCur = _pgobCur->_pgobPar;
|
|
*pgrfgteOut |= fgtePost;
|
|
if (_pgobCur == _pgobRoot)
|
|
{
|
|
_es = esDone;
|
|
*pgrfgteOut |= fgteRoot;
|
|
}
|
|
else
|
|
_es = esGoRight;
|
|
}
|
|
break;
|
|
|
|
case esDone:
|
|
return fFalse;
|
|
}
|
|
|
|
*ppgob = _pgobCur;
|
|
return fTrue;
|
|
}
|