mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 10:22:40 +01:00
1403 lines
33 KiB
C++
1403 lines
33 KiB
C++
|
/* Copyright (c) Microsoft Corporation.
|
||
|
Licensed under the MIT License. */
|
||
|
|
||
|
/***************************************************************************
|
||
|
Author: ShonK
|
||
|
Project: Kauai
|
||
|
Reviewed:
|
||
|
Copyright (c) Microsoft Corporation
|
||
|
|
||
|
Code for implementing help balloons in kidspace.
|
||
|
|
||
|
***************************************************************************/
|
||
|
#include "kidframe.h"
|
||
|
ASSERTNAME
|
||
|
|
||
|
BEGIN_CMD_MAP(TXHG, TXRG)
|
||
|
ON_CID_GEN(cidSelIdle, pvNil, pvNil)
|
||
|
ON_CID_ME(cidActivateSel, pvNil, pvNil)
|
||
|
END_CMD_MAP_NIL()
|
||
|
|
||
|
RTCLASS(TXHD)
|
||
|
RTCLASS(TXHG)
|
||
|
RTCLASS(HBAL)
|
||
|
RTCLASS(HBTN)
|
||
|
|
||
|
const achar kchHelpString = '~';
|
||
|
|
||
|
/***************************************************************************
|
||
|
Constructor for a help text document.
|
||
|
***************************************************************************/
|
||
|
TXHD::TXHD(PRCA prca, PDOCB pdocb, ulong grfdoc) : TXHD_PAR(pdocb, grfdoc)
|
||
|
{
|
||
|
AssertPo(prca, 0);
|
||
|
_prca = prca;
|
||
|
_prca->AddRef();
|
||
|
_htop.cnoBalloon = cnoNil;
|
||
|
_htop.cnoScript = cnoNil;
|
||
|
_htop.ckiSnd.ctg = ctgNil;
|
||
|
_htop.ckiSnd.cno = cnoNil;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Destructor for a help text document.
|
||
|
***************************************************************************/
|
||
|
TXHD::~TXHD(void)
|
||
|
{
|
||
|
ReleasePpo(&_prca);
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/***************************************************************************
|
||
|
Assert the validity of a TXHD.
|
||
|
***************************************************************************/
|
||
|
void TXHD::AssertValid(ulong grf)
|
||
|
{
|
||
|
TXHD_PAR::AssertValid(0);
|
||
|
AssertPo(_prca, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Mark memory for the TXHD.
|
||
|
***************************************************************************/
|
||
|
void TXHD::MarkMem(void)
|
||
|
{
|
||
|
AssertValid(0);
|
||
|
TXHD_PAR::MarkMem();
|
||
|
MarkMemObj(_prca);
|
||
|
}
|
||
|
#endif //DEBUG
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Static method to read a help text document from the given (pcfl, ctg, cno)
|
||
|
and using the given prca as the source for pictures and buttons.
|
||
|
***************************************************************************/
|
||
|
PTXHD TXHD::PtxhdReadChunk(PRCA prca, PCFL pcfl, CTG ctg, CNO cno,
|
||
|
PSTRG pstrg, ulong grftxhd)
|
||
|
{
|
||
|
AssertPo(prca, 0);
|
||
|
AssertPo(pcfl, 0);
|
||
|
PTXHD ptxhd;
|
||
|
|
||
|
if (pvNil == (ptxhd = NewObj TXHD(prca)) ||
|
||
|
!ptxhd->_FReadChunk(pcfl, ctg, cno, pstrg, grftxhd))
|
||
|
{
|
||
|
PushErc(ercHelpReadFailed);
|
||
|
ReleasePpo(&ptxhd);
|
||
|
}
|
||
|
|
||
|
AssertNilOrPo(ptxhd, fobjAssertFull);
|
||
|
return ptxhd;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Read the given chunk into this TXRD.
|
||
|
***************************************************************************/
|
||
|
bool TXHD::_FReadChunk(PCFL pcfl, CTG ctg, CNO cno, PSTRG pstrg, ulong grftxhd)
|
||
|
{
|
||
|
AssertPo(pcfl, 0);
|
||
|
AssertNilOrPo(pstrg, 0);
|
||
|
BLCK blck;
|
||
|
KID kid;
|
||
|
HTOPF htopf;
|
||
|
long stid, lw;
|
||
|
long cp, cpMac, cpMin;
|
||
|
STN stn;
|
||
|
bool fRet = fFalse;
|
||
|
|
||
|
if (pcfl->FForest(ctg, cno))
|
||
|
{
|
||
|
CKI cki;
|
||
|
|
||
|
if (pvNil == (pcfl = pcfl->PcflReadForest(ctg, cno, fFalse)))
|
||
|
goto LFail;
|
||
|
if (!pcfl->FGetCkiCtg(ctg, 0, &cki))
|
||
|
goto LFail;
|
||
|
cno = cki.cno;
|
||
|
}
|
||
|
else
|
||
|
pcfl->AddRef();
|
||
|
|
||
|
// The old version of HTOP didn't have the ckiSnd - accept both old and new
|
||
|
// versions.
|
||
|
htopf.htop.ckiSnd.ctg = ctgNil;
|
||
|
htopf.htop.ckiSnd.cno = cnoNil;
|
||
|
if (!pcfl->FFind(ctg, cno, &blck) || !blck.FUnpackData() ||
|
||
|
size(HTOPF) != blck.Cb() && offset(HTOPF, htop.ckiSnd) != blck.Cb() ||
|
||
|
!blck.FRead(&htopf))
|
||
|
{
|
||
|
goto LFail;
|
||
|
}
|
||
|
|
||
|
if (htopf.bo == kboOther)
|
||
|
SwapBytesBom(&htopf.htop, kbomHtop);
|
||
|
else if (htopf.bo != kboCur)
|
||
|
goto LFail;
|
||
|
|
||
|
if (!pcfl->FGetKidChidCtg(ctg, cno, 0, kctgRichText, &kid))
|
||
|
goto LFail;
|
||
|
|
||
|
if (!TXHD_PAR::_FReadChunk(pcfl, kid.cki.ctg, kid.cki.cno,
|
||
|
FPure(grftxhd & ftxhdCopyText)))
|
||
|
{
|
||
|
goto LFail;
|
||
|
}
|
||
|
|
||
|
if ((grftxhd & ftxhdExpandStrings) && pvNil != pstrg)
|
||
|
{
|
||
|
SetInternal();
|
||
|
SuspendUndo();
|
||
|
cpMac = CpMac();
|
||
|
for (cp = 0; cp < cpMac; )
|
||
|
{
|
||
|
if (_ChFetch(cp) != kchHelpString)
|
||
|
{
|
||
|
cp++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
cpMin = cp++;
|
||
|
for (stid = 0; cp < cpMac && FIn(lw = _ChFetch(cp) - '0', 0, 10); cp++)
|
||
|
stid = stid * 10 + lw;
|
||
|
if (!pstrg->FGet(stid, &stn))
|
||
|
Warn("string missing");
|
||
|
if (FReplaceRgch(stn.Psz(), stn.Cch(), cpMin, cp - cpMin, fdocNil))
|
||
|
{
|
||
|
cp = cpMin + stn.Cch();
|
||
|
cpMac = CpMac();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fRet = fTrue;
|
||
|
_htop = htopf.htop;
|
||
|
AssertThis(0);
|
||
|
|
||
|
LFail:
|
||
|
// Release our hold on the CFL
|
||
|
ReleasePpo(&pcfl);
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Do any necessary munging of the AG entry on open. Return false if
|
||
|
we don't recognize this argument type.
|
||
|
***************************************************************************/
|
||
|
bool TXHD::_FOpenArg(long icact, byte sprm, short bo, short osk)
|
||
|
{
|
||
|
CTG ctg;
|
||
|
CNO cno;
|
||
|
long cb;
|
||
|
long rglw[2];
|
||
|
long clw;
|
||
|
|
||
|
if (TXHD_PAR::_FOpenArg(icact, sprm, bo, osk))
|
||
|
return fTrue;
|
||
|
|
||
|
cb = _pagcact->Cb(icact);
|
||
|
switch (sprm)
|
||
|
{
|
||
|
case sprmGroup:
|
||
|
if (cb < size(byte) + size(CNO))
|
||
|
return fFalse;
|
||
|
if (bo == kboOther)
|
||
|
{
|
||
|
_pagcact->GetRgb(icact, size(byte), size(CNO), &cno);
|
||
|
SwapBytesRglw(&cno, 1);
|
||
|
_pagcact->PutRgb(icact, size(byte), size(CNO), &cno);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case sprmObject:
|
||
|
if (cb < size(CTG))
|
||
|
return fFalse;
|
||
|
_pagcact->GetRgb(icact, 0, size(CTG), &ctg);
|
||
|
if (bo == kboOther)
|
||
|
{
|
||
|
SwapBytesRglw(&ctg, 1);
|
||
|
_pagcact->PutRgb(icact, 0, size(CTG), &ctg);
|
||
|
}
|
||
|
cb -= size(CTG);
|
||
|
|
||
|
switch (ctg)
|
||
|
{
|
||
|
case kctgMbmp:
|
||
|
case kctgEditControl:
|
||
|
clw = 1;
|
||
|
goto LSwapBytes;
|
||
|
|
||
|
case kctgGokd:
|
||
|
clw = 2;
|
||
|
LSwapBytes:
|
||
|
AssertIn(clw, 1, CvFromRgv(rglw) + 1);
|
||
|
if (cb < clw * size(long))
|
||
|
return fFalse;
|
||
|
|
||
|
if (bo == kboOther)
|
||
|
{
|
||
|
_pagcact->GetRgb(icact, size(CTG), clw * size(long), rglw);
|
||
|
SwapBytesRglw(rglw, clw);
|
||
|
_pagcact->PutRgb(icact, size(CTG), clw * size(long), rglw);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return fFalse;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Save a help topic to the given chunky file. Fill in *pcki with where
|
||
|
we put the root chunk.
|
||
|
***************************************************************************/
|
||
|
bool TXHD::FSaveToChunk(PCFL pcfl, CKI *pcki, bool fRedirectText)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pcfl, 0);
|
||
|
AssertVarMem(pcki);
|
||
|
BLCK blck;
|
||
|
CKI cki;
|
||
|
HTOPF htopf;
|
||
|
|
||
|
pcki->ctg = kctgHelpTopic;
|
||
|
htopf.bo = kboCur;
|
||
|
htopf.osk = koskCur;
|
||
|
htopf.htop = _htop;
|
||
|
if (!pcfl->FAdd(size(HTOPF), pcki->ctg, &pcki->cno, &blck))
|
||
|
{
|
||
|
PushErc(ercHelpSaveFailed);
|
||
|
return fFalse;
|
||
|
}
|
||
|
if (!blck.FWrite(&htopf))
|
||
|
goto LFail;
|
||
|
|
||
|
if (!TXHD_PAR::FSaveToChunk(pcfl, &cki, fRedirectText))
|
||
|
goto LFail;
|
||
|
|
||
|
//add the text chunk and write it
|
||
|
if (!pcfl->FAdoptChild(pcki->ctg, pcki->cno, cki.ctg, cki.cno))
|
||
|
{
|
||
|
pcfl->Delete(cki.ctg, cki.cno);
|
||
|
LFail:
|
||
|
pcfl->Delete(pcki->ctg, pcki->cno);
|
||
|
PushErc(ercHelpSaveFailed);
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Get the bounding rectangle for the given object.
|
||
|
***************************************************************************/
|
||
|
bool TXHD::_FGetObjectRc(long icact, byte sprm, PGNV pgnv, PCHP pchp, RC *prc)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(icact, 0, _pagcact->IvMac());
|
||
|
AssertIn(sprm, sprmMinObj, 0x100);
|
||
|
AssertPo(pgnv, 0);
|
||
|
AssertVarMem(pchp);
|
||
|
AssertVarMem(prc);
|
||
|
long cb;
|
||
|
PMBMP pmbmp;
|
||
|
PCRF pcrf;
|
||
|
KID kid;
|
||
|
long rglw[2];
|
||
|
|
||
|
if (sprmObject != sprm)
|
||
|
return fFalse;
|
||
|
|
||
|
Assert(size(CTG) == size(long), 0);
|
||
|
cb = _pagcact->Cb(icact);
|
||
|
if (cb < size(rglw))
|
||
|
return fFalse;
|
||
|
_pagcact->GetRgb(icact, 0, size(rglw), rglw);
|
||
|
switch ((CTG)rglw[0])
|
||
|
{
|
||
|
case kctgMbmp:
|
||
|
pmbmp = (PMBMP)_prca->PbacoFetch(rglw[0], rglw[1], MBMP::FReadMbmp);
|
||
|
goto LHaveMbmp;
|
||
|
|
||
|
case kctgGokd:
|
||
|
pcrf = _prca->PcrfFindChunk(rglw[0], rglw[1]);
|
||
|
if (pvNil == pcrf)
|
||
|
return fFalse;
|
||
|
if (!pcrf->Pcfl()->FGetKidChidCtg(rglw[0], rglw[1],
|
||
|
0x10000, kctgMbmp, &kid))
|
||
|
{
|
||
|
return fFalse;
|
||
|
}
|
||
|
pmbmp = (PMBMP)pcrf->PbacoFetch(kid.cki.ctg, kid.cki.cno, MBMP::FReadMbmp);
|
||
|
LHaveMbmp:
|
||
|
if (pvNil == pmbmp)
|
||
|
return fFalse;
|
||
|
pmbmp->GetRc(prc);
|
||
|
ReleasePpo(&pmbmp);
|
||
|
prc->Offset(-prc->xpLeft, -prc->ypBottom);
|
||
|
return fTrue;
|
||
|
|
||
|
case kctgEditControl:
|
||
|
pgnv->SetFont(pchp->onn, pchp->grfont, pchp->dypFont, tahLeft, tavBaseline);
|
||
|
pgnv->GetRcFromRgch(prc, pvNil, 0);
|
||
|
prc->Inset(0, -1);
|
||
|
prc->xpLeft = 0;
|
||
|
prc->xpRight = rglw[1];
|
||
|
return fTrue;
|
||
|
|
||
|
default:
|
||
|
return fFalse;
|
||
|
}
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Draw the given object.
|
||
|
***************************************************************************/
|
||
|
bool TXHD::_FDrawObject(long icact, byte sprm, PGNV pgnv, long *pxp, long yp,
|
||
|
PCHP pchp, RC *prcClip)
|
||
|
{
|
||
|
AssertIn(icact, 0, _pagcact->IvMac());
|
||
|
Assert(sprm >= sprmObject, 0);
|
||
|
AssertPo(pgnv, 0);
|
||
|
AssertVarMem(pxp);
|
||
|
AssertVarMem(pchp);
|
||
|
AssertVarMem(prcClip);
|
||
|
long cb;
|
||
|
RC rc;
|
||
|
PMBMP pmbmp;
|
||
|
PCRF pcrf;
|
||
|
KID kid;
|
||
|
long rglw[2];
|
||
|
bool fDrawMbmp = fTrue;
|
||
|
|
||
|
if (sprmObject != sprm)
|
||
|
return fFalse;
|
||
|
|
||
|
cb = _pagcact->Cb(icact);
|
||
|
if (cb < size(rglw))
|
||
|
return fFalse;
|
||
|
_pagcact->GetRgb(icact, 0, size(rglw), rglw);
|
||
|
switch ((CTG)rglw[0])
|
||
|
{
|
||
|
case kctgMbmp:
|
||
|
pmbmp = (PMBMP)_prca->PbacoFetch(rglw[0], rglw[1], MBMP::FReadMbmp);
|
||
|
goto LHaveMbmp;
|
||
|
|
||
|
case kctgGokd:
|
||
|
fDrawMbmp = !_fHideButtons;
|
||
|
pcrf = _prca->PcrfFindChunk(rglw[0], rglw[1]);
|
||
|
if (pvNil == pcrf)
|
||
|
return fFalse;
|
||
|
if (!pcrf->Pcfl()->FGetKidChidCtg(rglw[0], rglw[1],
|
||
|
ChidFromSnoDchid(ksnoInit, 0), kctgMbmp, &kid))
|
||
|
{
|
||
|
return fFalse;
|
||
|
}
|
||
|
pmbmp = (PMBMP)pcrf->PbacoFetch(kid.cki.ctg, kid.cki.cno, MBMP::FReadMbmp);
|
||
|
LHaveMbmp:
|
||
|
if (pvNil == pmbmp)
|
||
|
return fFalse;
|
||
|
pmbmp->GetRc(&rc);
|
||
|
rc.Offset(*pxp - rc.xpLeft, yp - rc.ypBottom);
|
||
|
if (kacrClear != pchp->acrBack)
|
||
|
pgnv->FillRc(&rc, pchp->acrBack);
|
||
|
if (fDrawMbmp)
|
||
|
pgnv->DrawMbmp(pmbmp, &rc);
|
||
|
ReleasePpo(&pmbmp);
|
||
|
if (pchp->grfont & fontBoxed)
|
||
|
{
|
||
|
pgnv->SetPenSize(1, 1);
|
||
|
pgnv->FrameRcApt(&rc, &vaptGray, pchp->acrFore, kacrClear);
|
||
|
}
|
||
|
*pxp += rc.Dxp();
|
||
|
return fTrue;
|
||
|
|
||
|
case kctgEditControl:
|
||
|
pgnv->SetFont(pchp->onn, pchp->grfont, pchp->dypFont, tahLeft, tavBaseline);
|
||
|
pgnv->GetRcFromRgch(&rc, pvNil, 0, 0, yp);
|
||
|
rc.Inset(0, -1);
|
||
|
rc.xpLeft = *pxp;
|
||
|
rc.xpRight = rc.xpLeft + rglw[1];
|
||
|
*pxp = rc.xpRight;
|
||
|
pgnv->SetPenSize(1, 1);
|
||
|
pgnv->FrameRc(&rc, kacrBlack);
|
||
|
rc.Inset(1, 1);
|
||
|
pgnv->FillRc(&rc, pchp->acrBack);
|
||
|
return fTrue;
|
||
|
|
||
|
default:
|
||
|
return fFalse;
|
||
|
}
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Insert a picture into the help text document.
|
||
|
***************************************************************************/
|
||
|
bool TXHD::FInsertPicture(CNO cno, void *pvExtra, long cbExtra,
|
||
|
long cp, long ccpDel, PCHP pchp, ulong grfdoc)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPvCb(pvExtra, cbExtra);
|
||
|
AssertIn(cp, 0, CpMac());
|
||
|
AssertIn(ccpDel, 0, CpMac() - cp);
|
||
|
AssertNilOrVarMem(pchp);
|
||
|
CKI cki;
|
||
|
void *pv = &cki;
|
||
|
bool fRet = fFalse;
|
||
|
|
||
|
cki.ctg = kctgMbmp;
|
||
|
cki.cno = cno;
|
||
|
if (cbExtra > 0)
|
||
|
{
|
||
|
if (!FAllocPv(&pv, size(CKI) + cbExtra, fmemNil, mprNormal))
|
||
|
return fFalse;
|
||
|
CopyPb(&cki, pv, size(CKI));
|
||
|
CopyPb(pvExtra, PvAddBv(pv, size(CKI)), cbExtra);
|
||
|
}
|
||
|
|
||
|
fRet = FInsertObject(pv, size(CKI) + cbExtra, cp, ccpDel, pchp, grfdoc);
|
||
|
|
||
|
if (pv != &cki)
|
||
|
FreePpv(&pv);
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Insert a new button
|
||
|
***************************************************************************/
|
||
|
bool TXHD::FInsertButton(CNO cno, CNO cnoTopic, void *pvExtra, long cbExtra,
|
||
|
long cp, long ccpDel, PCHP pchp, ulong grfdoc)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPvCb(pvExtra, cbExtra);
|
||
|
AssertIn(cp, 0, CpMac());
|
||
|
AssertIn(ccpDel, 0, CpMac() - cp);
|
||
|
AssertNilOrVarMem(pchp);
|
||
|
byte rgb[size(CKI) + size(long)];
|
||
|
CKI *pcki = (CKI *)rgb;
|
||
|
CNO *pcnoTopic = (CNO *)(pcki + 1);;
|
||
|
void *pv = rgb;
|
||
|
bool fRet = fFalse;
|
||
|
|
||
|
pcki->ctg = kctgGokd;
|
||
|
pcki->cno = cno;
|
||
|
*pcnoTopic = cnoTopic;
|
||
|
if (cbExtra > 0)
|
||
|
{
|
||
|
if (!FAllocPv(&pv, size(rgb) + cbExtra, fmemNil, mprNormal))
|
||
|
return fFalse;
|
||
|
CopyPb(rgb, pv, size(rgb));
|
||
|
CopyPb(pvExtra, PvAddBv(pv, size(rgb)), cbExtra);
|
||
|
}
|
||
|
|
||
|
fRet = FInsertObject(pv, size(rgb) + cbExtra, cp, ccpDel, pchp, grfdoc);
|
||
|
|
||
|
if (pv != rgb)
|
||
|
FreePpv(&pv);
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Group the given text into the given group. lw == 0 indicates no group.
|
||
|
Any non-zero number is a group.
|
||
|
***************************************************************************/
|
||
|
bool TXHD::FGroupText(long cp1, long cp2, byte bGroup, CNO cnoTopic,
|
||
|
PSTN pstnTopic)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertNilOrPo(pstnTopic, 0);
|
||
|
AssertIn(cp1, 0, CpMac());
|
||
|
AssertIn(cp2, 0, CpMac());
|
||
|
SPVM spvm;
|
||
|
|
||
|
SortLw(&cp1, &cp2);
|
||
|
if (cp1 == cp2)
|
||
|
return fTrue;
|
||
|
|
||
|
if (!FSetUndo(cp1, cp2, cp2 - cp1))
|
||
|
return fFalse;
|
||
|
|
||
|
spvm.sprm = sprmGroup;
|
||
|
spvm.lwMask = -1;
|
||
|
if (bGroup == 0)
|
||
|
{
|
||
|
// means no grouping
|
||
|
spvm.lw = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
byte rgb[size(byte) + size(CNO) + kcbMaxDataStn];
|
||
|
long cb = size(byte) + size(CNO);
|
||
|
|
||
|
rgb[0] = bGroup;
|
||
|
CopyPb(&cnoTopic, rgb + size(byte), size(CNO));
|
||
|
if (pvNil != pstnTopic && pstnTopic->Cch() > 0)
|
||
|
{
|
||
|
pstnTopic->GetData(rgb + cb);
|
||
|
cb += pstnTopic->CbData();
|
||
|
}
|
||
|
|
||
|
if (!_FEnsureInAg(sprmGroup, rgb, cb, &spvm.lw))
|
||
|
{
|
||
|
CancelUndo();
|
||
|
return fFalse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!_pglmpe->FEnsureSpace(2))
|
||
|
{
|
||
|
_ReleaseRgspvm(&spvm, 1);
|
||
|
CancelUndo();
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
_ApplyRgspvm(cp1, cp2 - cp1, &spvm, 1);
|
||
|
CommitUndo();
|
||
|
|
||
|
AssertThis(0);
|
||
|
InvalAllDdg(cp1, cp2 - cp1, cp2 - cp1);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Determine if the given cp is in a grouped text range.
|
||
|
***************************************************************************/
|
||
|
bool TXHD::FGrouped(long cp, long *pcpMin, long *pcpLim, byte *pbGroup,
|
||
|
CNO *pcnoTopic, PSTN pstnTopic)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertIn(cp, 0, CpMac());
|
||
|
AssertNilOrVarMem(pcpMin);
|
||
|
AssertNilOrVarMem(pcpLim);
|
||
|
AssertNilOrVarMem(pbGroup);
|
||
|
AssertNilOrVarMem(pcnoTopic);
|
||
|
AssertNilOrPo(pstnTopic, 0);
|
||
|
MPE mpe;
|
||
|
byte bGroup = 0;
|
||
|
|
||
|
if (!_FFindMpe(_SpcpFromSprmCp(sprmGroup, cp), &mpe, pcpLim))
|
||
|
{
|
||
|
mpe.lw = 0;
|
||
|
mpe.spcp = 0;
|
||
|
}
|
||
|
|
||
|
if (mpe.lw > 0)
|
||
|
{
|
||
|
byte *prgb;
|
||
|
long cb;
|
||
|
|
||
|
prgb = (byte *)_pagcact->PvLock(mpe.lw - 1, &cb);
|
||
|
cb -= size(byte) + size(CNO); // group number, cnoTopic
|
||
|
if (cb < 0)
|
||
|
goto LFail;
|
||
|
|
||
|
bGroup = prgb[0];
|
||
|
if (bGroup == 0)
|
||
|
{
|
||
|
LFail:
|
||
|
Bug("bad group data");
|
||
|
_pagcact->Unlock();
|
||
|
goto LNotGrouped;
|
||
|
}
|
||
|
|
||
|
if (pvNil != pcnoTopic)
|
||
|
CopyPb(prgb + size(byte), pcnoTopic, size(CNO));
|
||
|
if (pvNil != pstnTopic)
|
||
|
{
|
||
|
if (cb > 0)
|
||
|
pstnTopic->FSetData(prgb + size(byte) + size(CNO), cb);
|
||
|
else
|
||
|
pstnTopic->SetNil();
|
||
|
}
|
||
|
_pagcact->Unlock();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LNotGrouped:
|
||
|
if (pvNil != pcnoTopic)
|
||
|
*pcnoTopic = cnoNil;
|
||
|
if (pvNil != pstnTopic)
|
||
|
pstnTopic->SetNil();
|
||
|
}
|
||
|
|
||
|
if (pvNil != pbGroup)
|
||
|
*pbGroup = bGroup;
|
||
|
if (pvNil != pcpMin)
|
||
|
*pcpMin = _CpFromSpcp(mpe.spcp);
|
||
|
|
||
|
return bGroup != 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Get the help topic information.
|
||
|
***************************************************************************/
|
||
|
void TXHD::GetHtop(PHTOP phtop)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(phtop);
|
||
|
|
||
|
*phtop = _htop;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Set the topic info.
|
||
|
***************************************************************************/
|
||
|
void TXHD::SetHtop(PHTOP phtop)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(phtop);
|
||
|
|
||
|
_htop = *phtop;
|
||
|
SetDirty();
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Constructor for a TXHG.
|
||
|
***************************************************************************/
|
||
|
TXHG::TXHG(PWOKS pwoks, PTXHD ptxhd, PGCB pgcb) : TXRG(ptxhd, pgcb)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
AssertPo(pwoks, 0);
|
||
|
|
||
|
_pwoks = pwoks;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Create a new help topic display gob.
|
||
|
***************************************************************************/
|
||
|
PTXHG TXHG::PtxhgNew(PWOKS pwoks, PTXHD ptxhd, PGCB pgcb)
|
||
|
{
|
||
|
PTXHG ptxhg;
|
||
|
|
||
|
if (pvNil == (ptxhg = NewObj TXHG(pwoks, ptxhd, pgcb)))
|
||
|
return pvNil;
|
||
|
if (!ptxhg->_FInit())
|
||
|
{
|
||
|
ReleasePpo(&ptxhg);
|
||
|
return pvNil;
|
||
|
}
|
||
|
return ptxhg;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Inititalize the display gob for a help balloon topic.
|
||
|
***************************************************************************/
|
||
|
bool TXHG::_FInit(void)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
PRCA prca;
|
||
|
long cp, cb;
|
||
|
void *pv;
|
||
|
CKI *pcki;
|
||
|
long dxp;
|
||
|
CNO cno;
|
||
|
long xp, ypBase;
|
||
|
CNO cnoTopic;
|
||
|
byte bGroup;
|
||
|
long lwMax;
|
||
|
RTVN rtvn;
|
||
|
long hid;
|
||
|
CHP chp;
|
||
|
RC rc;
|
||
|
EDPAR edpar;
|
||
|
STN stn;
|
||
|
PTXHD ptxhd = Ptxhd();
|
||
|
|
||
|
if (!TXHG_PAR::_FInit())
|
||
|
return fFalse;
|
||
|
|
||
|
// find the max of the group numbers
|
||
|
lwMax = 0;
|
||
|
for (cp = 0; cp < ptxhd->CpMac(); )
|
||
|
{
|
||
|
ptxhd->FGrouped(cp, pvNil, &cp, &bGroup);
|
||
|
lwMax = LwMax((long)bGroup, lwMax);
|
||
|
}
|
||
|
|
||
|
// find a base hid that covers lwMax buttons
|
||
|
_hidBase = 0;
|
||
|
if (lwMax > 0)
|
||
|
{
|
||
|
_hidBase = CMH::HidUnique(lwMax) - 1;
|
||
|
stn = PszLit("_gidBase");
|
||
|
rtvn.SetFromStn(&stn);
|
||
|
if (!FAssignRtvm(PgobPar()->Ppglrtvm(), &rtvn, _hidBase))
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
prca = Ptxhd()->Prca();
|
||
|
for (cp = 0; Ptxhd()->FFetchObject(cp, &cp, &pv, &cb); cp++)
|
||
|
{
|
||
|
if (pvNil == pv)
|
||
|
continue;
|
||
|
|
||
|
if (cb < size(CTG))
|
||
|
goto LContinue;
|
||
|
|
||
|
switch (*(CTG *)pv)
|
||
|
{
|
||
|
case kctgEditControl:
|
||
|
if (cb < size(ECOS))
|
||
|
goto LContinue;
|
||
|
dxp = ((ECOS *)pv)->dxp;
|
||
|
FreePpv(&pv);
|
||
|
|
||
|
// get the bounding rectangle
|
||
|
_GetXpYpFromCp(cp, pvNil, pvNil, &xp, &ypBase, fFalse);
|
||
|
_FetchChp(cp, &chp);
|
||
|
_pgnv->SetFont(chp.onn, chp.grfont, chp.dypFont,
|
||
|
tahLeft, tavBaseline);
|
||
|
_pgnv->GetRcFromRgch(&rc, pvNil, 0);
|
||
|
rc.Offset(0, ypBase + chp.dypOffset);
|
||
|
rc.xpLeft = xp + 1;
|
||
|
rc.xpRight = xp + dxp - 1;
|
||
|
|
||
|
Ptxhd()->FGrouped(cp, pvNil, pvNil, &bGroup);
|
||
|
if (bGroup == 0 ||
|
||
|
_pwoks->PcmhFromHid(hid = _hidBase + bGroup) != pvNil)
|
||
|
{
|
||
|
hid = CMH::HidUnique();
|
||
|
}
|
||
|
if (chp.acrBack == kacrClear)
|
||
|
chp.acrBack = kacrWhite;
|
||
|
edpar.Set(hid, this, fgobNil, kginMark, &rc, pvNil, chp.onn,
|
||
|
chp.grfont, chp.dypFont, tahLeft, tavTop,
|
||
|
chp.acrFore, chp.acrBack);
|
||
|
if (pvNil == EDSL::PedslNew(&edpar))
|
||
|
return fFalse;
|
||
|
break;
|
||
|
|
||
|
case kctgGokd:
|
||
|
if (cb < size(CKI) + size(CNO))
|
||
|
goto LContinue;
|
||
|
pcki = (CKI *)pv;
|
||
|
cno = pcki->cno;
|
||
|
cnoTopic = *(CNO *)(pcki + 1);
|
||
|
FreePpv(&pv);
|
||
|
|
||
|
_GetXpYpFromCp(cp, pvNil, pvNil, &xp, &ypBase, fFalse);
|
||
|
_FetchChp(cp, &chp);
|
||
|
Ptxhd()->FGrouped(cp, pvNil, pvNil, &bGroup,
|
||
|
cnoTopic == cnoNil ? &cnoTopic : pvNil);
|
||
|
if (bGroup == 0 ||
|
||
|
_pwoks->PcmhFromHid(hid = _hidBase + bGroup) != pvNil)
|
||
|
{
|
||
|
hid = CMH::HidUnique();
|
||
|
}
|
||
|
if (pvNil == HBTN::PhbtnNew(_pwoks, this, hid, cno, prca, bGroup,
|
||
|
cnoTopic, xp, ypBase + chp.dypOffset))
|
||
|
{
|
||
|
return fFalse;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
LContinue:
|
||
|
FreePpv(&pv);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
AssertThis(0);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return whether the point is over hot (marked text).
|
||
|
***************************************************************************/
|
||
|
bool TXHG::FPtIn(long xp, long yp)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
|
||
|
if (!TXHG_PAR::FPtIn(xp, yp))
|
||
|
return fFalse;
|
||
|
return FGroupFromPt(xp, yp);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Track the mouse.
|
||
|
***************************************************************************/
|
||
|
bool TXHG::FCmdTrackMouse(PCMD_MOUSE pcmd)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(pcmd);
|
||
|
|
||
|
pcmd->grfcust = _pwoks->GrfcustAdjust(pcmd->grfcust);
|
||
|
if (pcmd->cid == cidMouseDown)
|
||
|
{
|
||
|
// first response to mouse down
|
||
|
Assert(vpcex->PgobTracking() == pvNil, "mouse already being tracked!");
|
||
|
|
||
|
if (!FGroupFromPt(pcmd->xp, pcmd->yp, &_bTrack, &_cnoTrack))
|
||
|
return fTrue;
|
||
|
|
||
|
vpcex->TrackMouse(this);
|
||
|
SetCursor(pcmd->grfcust);
|
||
|
_grfcust = pcmd->grfcust;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Assert(vpcex->PgobTracking() == this, "not tracking mouse!");
|
||
|
Assert(pcmd->cid == cidTrackMouse, 0);
|
||
|
if (!(pcmd->grfcust & fcustMouse))
|
||
|
{
|
||
|
byte bGroup;
|
||
|
CNO cnoTopic;
|
||
|
vpcex->EndMouseTracking();
|
||
|
|
||
|
if (FGroupFromPt(pcmd->xp, pcmd->yp, &bGroup, &cnoTopic) &&
|
||
|
bGroup == _bTrack && cnoTopic == _cnoTrack)
|
||
|
{
|
||
|
DoHit(bGroup, cnoTopic, _grfcust, hidNil);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
An edit control got a bad key.
|
||
|
***************************************************************************/
|
||
|
bool TXHG::FCmdBadKey(PCMD_BADKEY pcmd)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(pcmd);
|
||
|
|
||
|
if (!FIn(pcmd->hid, _hidBase + 1, _hidBase + 257))
|
||
|
return fFalse;
|
||
|
|
||
|
pcmd->grfcust = _pwoks->GrfcustAdjust(pcmd->grfcust);
|
||
|
_FRunScript((byte)(pcmd->hid - _hidBase), pcmd->grfcust, pcmd->hid,
|
||
|
(achar)pcmd->ch);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return the number of the group text that the given point is in.
|
||
|
***************************************************************************/
|
||
|
bool TXHG::FGroupFromPt(long xp, long yp, byte *pbGroup, CNO *pcnoTopic)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertNilOrVarMem(pbGroup);
|
||
|
AssertNilOrVarMem(pcnoTopic);
|
||
|
long cp;
|
||
|
|
||
|
if (!_FGetCpFromPt(xp, yp, &cp, fFalse))
|
||
|
return 0;
|
||
|
return Ptxhd()->FGrouped(cp, pvNil, pvNil, pbGroup, pcnoTopic);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
A child button was hit, take action.
|
||
|
***************************************************************************/
|
||
|
void TXHG::DoHit(byte bGroup, CNO cnoTopic, ulong grfcust, long hidHit)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
long lwRet = 0;
|
||
|
|
||
|
// run the script
|
||
|
if (!_FRunScript(bGroup, grfcust, hidHit, chNil, cnoTopic, &lwRet))
|
||
|
return;
|
||
|
|
||
|
if (cnoNil != cnoTopic && !lwRet)
|
||
|
_pwoks->PhbalNew(PgobPar()->PgobPar(), Ptxhd()->Prca(), cnoTopic);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Run the script. Returns false iff the TXHG doesn't exist after
|
||
|
running the script.
|
||
|
***************************************************************************/
|
||
|
bool TXHG::_FRunScript(byte bGroup, ulong grfcust, long hidHit, achar ch,
|
||
|
CNO cnoTopic, long *plwRet)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertNilOrVarMem(plwRet);
|
||
|
|
||
|
PSCPT pscpt;
|
||
|
PSCEG psceg;
|
||
|
HTOP htop;
|
||
|
bool fRet = fTrue;
|
||
|
PTXHD ptxhd = Ptxhd();
|
||
|
PRCA prca = ptxhd->Prca();
|
||
|
|
||
|
if (pvNil != plwRet)
|
||
|
*plwRet = 0;
|
||
|
|
||
|
ptxhd->GetHtop(&htop);
|
||
|
if (cnoNil == htop.cnoScript)
|
||
|
return fTrue;
|
||
|
|
||
|
pscpt = (PSCPT)prca->PbacoFetch(kctgScript, htop.cnoScript,
|
||
|
SCPT::FReadScript);
|
||
|
if (pvNil != pscpt && pvNil != (psceg = _pwoks->PscegNew(prca, this)))
|
||
|
{
|
||
|
AssertPo(pscpt, 0);
|
||
|
AssertPo(psceg, 0);
|
||
|
|
||
|
PWOKS pwoks = _pwoks;
|
||
|
long grid = Grid();
|
||
|
long rglw[5];
|
||
|
|
||
|
rglw[0] = (long)bGroup;
|
||
|
rglw[1] = grfcust;
|
||
|
rglw[2] = hidHit;
|
||
|
rglw[3] = (long)(byte)ch;
|
||
|
rglw[4] = cnoTopic;
|
||
|
|
||
|
//be careful not to use TXHG variables here in case the TXHG is
|
||
|
//freed while the script is running.
|
||
|
if (!psceg->FRunScript(pscpt, rglw, 5, plwRet) && pvNil != plwRet)
|
||
|
*plwRet = 0;
|
||
|
ReleasePpo(&psceg);
|
||
|
|
||
|
fRet = (this == pwoks->PgobFromGrid(grid));
|
||
|
}
|
||
|
ReleasePpo(&pscpt);
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
This handles cidMouseMove.
|
||
|
***************************************************************************/
|
||
|
bool TXHG::FCmdMouseMove(PCMD_MOUSE pcmd)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(pcmd);
|
||
|
ulong grfcust = _pwoks->GrfcustAdjust(pcmd->grfcust);
|
||
|
|
||
|
if (FGroupFromPt(pcmd->xp, pcmd->yp))
|
||
|
grfcust |= fcustHotText;
|
||
|
|
||
|
SetCursor(grfcust);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Set the cursor for this TXHG and the given cursor state.
|
||
|
***************************************************************************/
|
||
|
void TXHG::SetCursor(ulong grfcust)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
PGOB pgob;
|
||
|
|
||
|
for (pgob = this; ; )
|
||
|
{
|
||
|
pgob = pgob->PgobPar();
|
||
|
if (pvNil == pgob)
|
||
|
{
|
||
|
vpappb->SetCurs(pvNil);
|
||
|
break;
|
||
|
}
|
||
|
if (pgob->FIs(kclsGOK))
|
||
|
{
|
||
|
((PGOK)pgob)->SetCursor(grfcust | fcustChildGok);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Create a new help topic balloon based on the given topic number.
|
||
|
***************************************************************************/
|
||
|
PHBAL HBAL::PhbalCreate(PWOKS pwoks, PGOB pgobPar, PRCA prca,
|
||
|
CNO cnoTopic, PHTOP phtop)
|
||
|
{
|
||
|
AssertPo(pwoks, 0);
|
||
|
AssertPo(pgobPar, 0);
|
||
|
AssertPo(prca, 0);
|
||
|
AssertNilOrVarMem(phtop);
|
||
|
PCRF pcrf;
|
||
|
PTXHD ptxhd;
|
||
|
PHBAL phbal;
|
||
|
|
||
|
pcrf = prca->PcrfFindChunk(kctgHelpTopic, cnoTopic);
|
||
|
if (pvNil == pcrf)
|
||
|
return pvNil;
|
||
|
|
||
|
ptxhd = TXHD::PtxhdReadChunk(prca, pcrf->Pcfl(), kctgHelpTopic, cnoTopic,
|
||
|
pwoks->Pstrg());
|
||
|
if (pvNil == ptxhd)
|
||
|
return pvNil;
|
||
|
|
||
|
ptxhd->HideButtons();
|
||
|
phbal = PhbalNew(pwoks, pgobPar, prca, ptxhd, phtop);
|
||
|
ReleasePpo(&ptxhd);
|
||
|
|
||
|
return phbal;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Static method to create a new help balloon based on the given help
|
||
|
topic document and htop.
|
||
|
***************************************************************************/
|
||
|
PHBAL HBAL::PhbalNew(PWOKS pwoks, PGOB pgobPar, PRCA prca,
|
||
|
PTXHD ptxhd, PHTOP phtop)
|
||
|
{
|
||
|
AssertPo(pwoks, 0);
|
||
|
AssertPo(pgobPar, 0);
|
||
|
AssertPo(ptxhd, 0);
|
||
|
AssertPo(prca, 0);
|
||
|
AssertNilOrVarMem(phtop);
|
||
|
HTOP htop;
|
||
|
GCB gcb;
|
||
|
PHBAL phbal;
|
||
|
long grid;
|
||
|
|
||
|
ptxhd->GetHtop(&htop);
|
||
|
if (pvNil != phtop)
|
||
|
{
|
||
|
// merge the given htop with the topic's htop.
|
||
|
if (cnoNil != phtop->cnoBalloon)
|
||
|
htop.cnoBalloon = phtop->cnoBalloon;
|
||
|
if (hidNil != phtop->hidThis)
|
||
|
htop.hidThis = phtop->hidThis;
|
||
|
if (hidNil != phtop->hidTarget)
|
||
|
htop.hidTarget = phtop->hidTarget;
|
||
|
if (cnoNil != phtop->cnoScript)
|
||
|
htop.cnoScript = phtop->cnoScript;
|
||
|
htop.dxp += phtop->dxp;
|
||
|
htop.dyp += phtop->dyp;
|
||
|
if (cnoNil != phtop->ckiSnd.cno && ctgNil != phtop->ckiSnd.ctg)
|
||
|
htop.ckiSnd = phtop->ckiSnd;
|
||
|
}
|
||
|
|
||
|
if (htop.hidThis == hidNil)
|
||
|
htop.hidThis = CMH::HidUnique();
|
||
|
else if (pvNil != (phbal = (PHBAL)pwoks->PcmhFromHid(htop.hidThis)))
|
||
|
{
|
||
|
if (!phbal->FIs(kclsHBAL))
|
||
|
{
|
||
|
Bug("command handler with this ID already exists");
|
||
|
return pvNil;
|
||
|
}
|
||
|
|
||
|
AssertPo(phbal, 0);
|
||
|
|
||
|
#ifdef REVIEW //shonk: this makes little sense and is bug-prone
|
||
|
if (htop.cnoBalloon == phbal->_pgokd->Cno() && prca == phbal->_prca)
|
||
|
{
|
||
|
// same hid, same GOKD, same prca, so just change the topic
|
||
|
if (!phbal->FSetTopic(ptxhd, &htop, prca))
|
||
|
return pvNil;
|
||
|
return phbal;
|
||
|
}
|
||
|
#endif //REVIEW
|
||
|
|
||
|
// free the balloon and create the new one.
|
||
|
ReleasePpo(&phbal);
|
||
|
}
|
||
|
|
||
|
gcb.Set(htop.hidThis, pgobPar, fgobNil, kginMark);
|
||
|
if (pvNil == (phbal = NewObj HBAL(&gcb)))
|
||
|
return pvNil;
|
||
|
grid = phbal->Grid();
|
||
|
|
||
|
if (!phbal->_FInit(pwoks, ptxhd, &htop, prca))
|
||
|
{
|
||
|
ReleasePpo(&phbal);
|
||
|
return pvNil;
|
||
|
}
|
||
|
|
||
|
if (!phbal->_FEnterState(ksnoInit))
|
||
|
{
|
||
|
Warn("HBAL immediately destroyed!");
|
||
|
return pvNil;
|
||
|
}
|
||
|
|
||
|
// initialize the topic
|
||
|
phbal->_ptxhg->DoHit(0, cnoNil, fcustNil, hidNil);
|
||
|
if (phbal != pwoks->PgobFromGrid(grid))
|
||
|
{
|
||
|
Warn("HBAL immediately destroyed 2!");
|
||
|
return pvNil;
|
||
|
}
|
||
|
|
||
|
AssertPo(phbal, 0);
|
||
|
return phbal;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Constructor for a help balloon.
|
||
|
***************************************************************************/
|
||
|
HBAL::HBAL(GCB *pgcb) : HBAL_PAR(pgcb)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Initialize the help balloon.
|
||
|
***************************************************************************/
|
||
|
bool HBAL::_FInit(PWOKS pwoks, PTXHD ptxhd, HTOP *phtop, PRCA prca)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
AssertPo(ptxhd, 0);
|
||
|
AssertVarMem(phtop);
|
||
|
AssertPo(prca, 0);
|
||
|
|
||
|
if (!HBAL_PAR::_FInit(pwoks, phtop->cnoBalloon, prca))
|
||
|
return fFalse;
|
||
|
|
||
|
return _FSetTopic(ptxhd, phtop, prca);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Set the topic for this balloon. Returns false if setting the topic
|
||
|
fails or if the balloon is instantly killed by a script.
|
||
|
***************************************************************************/
|
||
|
bool HBAL::FSetTopic(PTXHD ptxhd, PHTOP phtop, PRCA prca)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(ptxhd, 0);
|
||
|
AssertVarMem(phtop);
|
||
|
AssertPo(prca, 0);
|
||
|
|
||
|
if (!_FSetTopic(ptxhd, phtop, prca))
|
||
|
return fFalse;
|
||
|
|
||
|
return _FEnterState(ksnoInit);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Set the topic in the help balloon. Don't enter the initial state.
|
||
|
***************************************************************************/
|
||
|
bool HBAL::_FSetTopic(PTXHD ptxhd, PHTOP phtop, PRCA prca)
|
||
|
{
|
||
|
AssertBaseThis(0);
|
||
|
AssertPo(ptxhd, 0);
|
||
|
AssertVarMem(phtop);
|
||
|
AssertPo(prca, 0);
|
||
|
|
||
|
PGOB pgob;
|
||
|
GCB gcb;
|
||
|
PT pt, ptReg;
|
||
|
STN stn;
|
||
|
RTVN rtvn;
|
||
|
PTXHG ptxhgSave = _ptxhg;
|
||
|
|
||
|
// create the topic DDG.
|
||
|
gcb.Set(CMH::HidUnique(), this, fgobNil, kginMark);
|
||
|
if (pvNil == (_ptxhg = TXHG::PtxhgNew(_pwoks, ptxhd, &gcb)))
|
||
|
goto LFail;
|
||
|
|
||
|
// set the sound variables
|
||
|
stn = PszLit("_ctgSound");
|
||
|
rtvn.SetFromStn(&stn);
|
||
|
if (!FAssignRtvm(_ptxhg->Ppglrtvm(), &rtvn, phtop->ckiSnd.ctg))
|
||
|
goto LFail;
|
||
|
stn = PszLit("_cnoSound");
|
||
|
rtvn.SetFromStn(&stn);
|
||
|
if (!FAssignRtvm(_ptxhg->Ppglrtvm(), &rtvn, phtop->ckiSnd.cno))
|
||
|
{
|
||
|
LFail:
|
||
|
ReleasePpo(&_ptxhg);
|
||
|
|
||
|
// restore the previous topic DDG
|
||
|
_ptxhg = ptxhgSave;
|
||
|
return fFalse;
|
||
|
}
|
||
|
ReleasePpo(&ptxhgSave);
|
||
|
|
||
|
_ptxhg->GetNaturalSize(&_dxpPref, &_dypPref);
|
||
|
if (hidNil == phtop->hidTarget ||
|
||
|
pvNil == (pgob = _pwoks->PgobFromHid(phtop->hidTarget)))
|
||
|
{
|
||
|
pgob = PgobPar();
|
||
|
}
|
||
|
|
||
|
if (pgob->FIs(kclsGOK))
|
||
|
((PGOK)pgob)->GetPtReg(&pt);
|
||
|
else
|
||
|
{
|
||
|
RC rc;
|
||
|
|
||
|
pgob->GetRc(&rc, cooParent);
|
||
|
pt.xp = rc.XpCenter();
|
||
|
pt.yp = rc.YpCenter();
|
||
|
}
|
||
|
pgob->MapPt(&pt, cooParent, cooGlobal);
|
||
|
|
||
|
// point the balloon at the gob
|
||
|
PgobPar()->MapPt(&pt, cooGlobal, cooLocal);
|
||
|
GetPtReg(&ptReg);
|
||
|
_SetGorp(_pgorp, pt.xp - ptReg.xp + phtop->dxp,
|
||
|
pt.yp - ptReg.yp + phtop->dyp);
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Our representation is changing, so make sure we stay inside our parent
|
||
|
and reposition the TXHG.
|
||
|
***************************************************************************/
|
||
|
void HBAL::_SetGorp(PGORP pgorp, long dxp, long dyp)
|
||
|
{
|
||
|
RC rc1, rc2, rc3;
|
||
|
|
||
|
HBAL_PAR::_SetGorp(pgorp, dxp, dyp);
|
||
|
|
||
|
// make sure we stay inside our parent
|
||
|
GetRc(&rc1, cooParent);
|
||
|
PgobPar()->GetRc(&rc2, cooLocal);
|
||
|
rc3.FIntersect(&rc1, &rc2);
|
||
|
if (rc3 != rc1)
|
||
|
{
|
||
|
rc1.PinToRc(&rc2);
|
||
|
SetPos(&rc1);
|
||
|
}
|
||
|
|
||
|
// position the TXHG.
|
||
|
GetRcContent(&rc1);
|
||
|
rc2.Set(0, 0, _dxpPref, _dypPref);
|
||
|
rc2.CenterOnRc(&rc1);
|
||
|
_ptxhg->SetPos(&rc2);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Constructor for a help balloon button.
|
||
|
***************************************************************************/
|
||
|
HBTN::HBTN(GCB *pgcb) : HBTN_PAR(pgcb)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Create a new help balloon button
|
||
|
***************************************************************************/
|
||
|
PHBTN HBTN::PhbtnNew(PWOKS pwoks, PGOB pgobPar, long hid, CNO cno, PRCA prca,
|
||
|
byte bGroup, CNO cnoTopic, long xpLeft, long ypBottom)
|
||
|
{
|
||
|
AssertPo(pwoks, 0);
|
||
|
AssertNilOrPo(pgobPar, 0);
|
||
|
Assert(hid != hidNil, "nil ID");
|
||
|
AssertPo(prca, 0);
|
||
|
GCB gcb;
|
||
|
PHBTN phbtn;
|
||
|
RC rcAbs;
|
||
|
|
||
|
if (pvNil != pwoks->PcmhFromHid(hid))
|
||
|
{
|
||
|
Bug("command handler with this ID already exists");
|
||
|
return pvNil;
|
||
|
}
|
||
|
|
||
|
gcb.Set(hid, pgobPar, fgobNil, kginMark);
|
||
|
if (pvNil == (phbtn = NewObj HBTN(&gcb)))
|
||
|
return pvNil;
|
||
|
|
||
|
phbtn->_bGroup = bGroup;
|
||
|
phbtn->_cnoTopic = cnoTopic;
|
||
|
if (!phbtn->_FInit(pwoks, cno, prca))
|
||
|
{
|
||
|
ReleasePpo(&phbtn);
|
||
|
return pvNil;
|
||
|
}
|
||
|
|
||
|
if (!phbtn->_FEnterState(ksnoInit))
|
||
|
{
|
||
|
Warn("GOK immediately destroyed!");
|
||
|
return pvNil;
|
||
|
}
|
||
|
phbtn->GetRc(&rcAbs, cooParent);
|
||
|
rcAbs.Offset(xpLeft - rcAbs.xpLeft, ypBottom - rcAbs.ypBottom);
|
||
|
phbtn->SetPos(&rcAbs, pvNil);
|
||
|
|
||
|
AssertPo(phbtn, 0);
|
||
|
return phbtn;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Test whether the given point is in this button or its related text.
|
||
|
***************************************************************************/
|
||
|
bool HBTN::FPtIn(long xp, long yp)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
PTXHG ptxhg;
|
||
|
PT pt(xp, yp);
|
||
|
byte bGroup;
|
||
|
CNO cnoTopic;
|
||
|
|
||
|
if (HBTN_PAR::FPtIn(xp, yp))
|
||
|
return fTrue;
|
||
|
|
||
|
if (_bGroup == 0 || !PgobPar()->FIs(kclsTXHG))
|
||
|
return fFalse;
|
||
|
|
||
|
ptxhg = (PTXHG)PgobPar();
|
||
|
MapPt(&pt, cooLocal, cooParent);
|
||
|
|
||
|
if (!ptxhg->FGroupFromPt(pt.xp, pt.yp, &bGroup, &cnoTopic))
|
||
|
return fFalse;
|
||
|
return bGroup == _bGroup && cnoTopic == _cnoTopic;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
The button has been clicked on. Tell the TXHG to do its thing.
|
||
|
***************************************************************************/
|
||
|
bool HBTN::FCmdClicked(PCMD_MOUSE pcmd)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(pcmd);
|
||
|
|
||
|
PTXHG ptxhg;
|
||
|
long hid = Hid();
|
||
|
|
||
|
if (!PgobPar()->FIs(kclsTXHG))
|
||
|
{
|
||
|
Bug("why isn't my parent a TXHG?");
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
ptxhg = (PTXHG)PgobPar();
|
||
|
ptxhg->DoHit(_bGroup, _cnoTopic, pcmd->grfcust, hid);
|
||
|
|
||
|
return fTrue;
|
||
|
}
|
||
|
|