Microsoft-3D-Movie-Maker/kauai/SRC/TEXTDOC.CPP
2022-05-03 16:31:19 -07:00

1888 lines
43 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Author: ShonK
Project: Kauai
Reviewed:
Copyright (c) Microsoft Corporation
For editing a text file or text stream as a document. Unlike the edit
controls in text.h/text.cpp, all the text need not be in memory (this
uses a BSF) and there can be multiple views on the same text.
***************************************************************************/
#include "frame.h"
ASSERTNAME
RTCLASS(TXDC)
RTCLASS(TXDD)
/***************************************************************************
Constructor for a text document.
***************************************************************************/
TXDC::TXDC(PDOCB pdocb, ulong grfdoc) : DOCB(pdocb, grfdoc)
{
_pbsf = pvNil;
_pfil = pvNil;
}
/***************************************************************************
Destructor for a text document.
***************************************************************************/
TXDC::~TXDC(void)
{
ReleasePpo(&_pbsf);
ReleasePpo(&_pfil);
}
/***************************************************************************
Create a new document based on the given text file and or text stream.
***************************************************************************/
PTXDC TXDC::PtxdcNew(PFNI pfni, PBSF pbsf, PDOCB pdocb, ulong grfdoc)
{
AssertNilOrPo(pfni, ffniFile);
AssertNilOrPo(pbsf, 0);
AssertNilOrPo(pdocb, 0);
PTXDC ptxdc;
if (pvNil == (ptxdc = NewObj TXDC(pdocb, grfdoc)))
return pvNil;
if (!ptxdc->_FInit(pfni, pbsf))
ReleasePpo(&ptxdc);
return ptxdc;
}
/***************************************************************************
Initialize the TXDC.
***************************************************************************/
bool TXDC::_FInit(PFNI pfni, PBSF pbsf)
{
AssertNilOrPo(pfni, ffniFile);
AssertNilOrPo(pbsf, 0);
if (pvNil != pfni)
{
if (pvNil == (_pfil = FIL::PfilOpen(pfni)))
return fFalse;
}
if (pvNil != pbsf)
{
pbsf->AddRef();
_pbsf = pbsf;
}
else if (pvNil == (_pbsf = NewObj BSF))
return fFalse;
else if (pvNil != _pfil && _pfil->FpMac() > 0)
{
//initialize the BSF to just point to the file
FLO flo;
flo.pfil = _pfil;
flo.fp = 0;
flo.cb = _pfil->FpMac();
if (!_pbsf->FReplaceFlo(&flo, fFalse, 0, 0))
return fFalse;
}
AssertThis(0);
return fTrue;
}
/***************************************************************************
Create a new TXDD to display the TXDC.
***************************************************************************/
PDDG TXDC::PddgNew(PGCB pgcb)
{
AssertThis(0);
return TXDD::PtxddNew(this, pgcb, _pbsf, vpappb->OnnDefFixed(),
fontNil, vpappb->DypTextDef());
}
/***************************************************************************
Get the current FNI for the doc. Return false if the doc is not
currently based on an FNI (it's a new doc or an internal one).
***************************************************************************/
bool TXDC::FGetFni(FNI *pfni)
{
AssertThis(0);
AssertBasePo(pfni, 0);
if (pvNil == _pfil || _pfil->FTemp())
return fFalse;
_pfil->GetFni(pfni);
return fTrue;
}
/***************************************************************************
Save the document and optionally set this fni as the current one.
If the doc is currently based on an FNI, pfni may be nil, indicating
that this is a normal save (not save as). If pfni is not nil and
fSetFni is false, this just writes a copy of the doc but doesn't change
the doc one bit.
***************************************************************************/
bool TXDC::FSaveToFni(FNI *pfni, bool fSetFni)
{
AssertThis(0);
AssertNilOrPo(pfni, ffniFile);
FLO flo;
FNI fniT;
if (pvNil == pfni)
{
if (pvNil == _pfil)
{
Bug("Can't do a normal save - no file");
return fFalse;
}
_pfil->GetFni(&fniT);
pfni = &fniT;
fSetFni = fTrue;
}
if (pvNil == (flo.pfil = FIL::PfilCreateTemp(pfni)))
goto LFail;
flo.fp = 0;
flo.cb = _pbsf->IbMac();
if (!_pbsf->FWriteRgb(&flo))
goto LFail;
// redirect the BSF to the new file
if (fSetFni)
_pbsf->FReplaceFlo(&flo, fFalse, 0, flo.cb);
if (!flo.pfil->FSetFni(pfni))
{
LFail:
ReleasePpo(&flo.pfil);
return fFalse;
}
flo.pfil->SetTemp(fFalse);
if (fSetFni)
{
ReleasePpo(&_pfil);
_pfil = flo.pfil;
_fDirty = fFalse;
}
else
ReleasePpo(&flo.pfil);
return fTrue;
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a TXDC.
***************************************************************************/
void TXDC::AssertValid(ulong grf)
{
TXDC_PAR::AssertValid(0);
AssertPo(_pbsf, 0);
AssertNilOrPo(_pfil, 0);
}
/***************************************************************************
Mark memory for the TXDC.
***************************************************************************/
void TXDC::MarkMem(void)
{
AssertValid(0);
TXDC_PAR::MarkMem();
MarkMemObj(_pbsf);
}
#endif //DEBUG
/***************************************************************************
Constructor for a text document display gob.
***************************************************************************/
TXDD::TXDD(PDOCB pdocb, PGCB pgcb, PBSF pbsf, long onn, ulong grfont, long dypFont)
: DDG(pdocb, pgcb)
{
AssertPo(pbsf, 0);
Assert(vntl.FValidOnn(onn), "bad onn");
AssertIn(dypFont, 1, kswMax);
_pbsf = pbsf;
_onn = onn;
_grfont = grfont;
_dypFont = dypFont;
//get the _dypLine and _dxpTab values
RC rc;
achar ch = kchSpace;
GNV gnv(this);
gnv.SetFont(_onn, _grfont, _dypFont);
gnv.GetRcFromRgch(&rc, &ch, 1, 0, 0);
_dypLine = rc.Dyp();
_dxpTab = LwMul(rc.Dxp(), 4);
}
/***************************************************************************
Destructor for TXDD.
***************************************************************************/
TXDD::~TXDD(void)
{
ReleasePpo(&_pglichStarts);
}
/***************************************************************************
Create a new TXDD.
***************************************************************************/
PTXDD TXDD::PtxddNew(PDOCB pdocb, PGCB pgcb, PBSF pbsf,
long onn, ulong grfont, long dypFont)
{
PTXDD ptxdd;
if (pvNil == (ptxdd = NewObj TXDD(pdocb, pgcb, pbsf, onn, grfont, dypFont)))
return pvNil;
if (!ptxdd->_FInit())
{
ReleasePpo(&ptxdd);
return pvNil;
}
ptxdd->Activate(fTrue);
AssertPo(ptxdd, 0);
return ptxdd;
}
/***************************************************************************
Initialize the TXDD.
***************************************************************************/
bool TXDD::_FInit(void)
{
long ib;
if (!TXDD_PAR::_FInit())
return fFalse;
if (pvNil == (_pglichStarts = GL::PglNew(size(long))))
return fFalse;
_pglichStarts->SetMinGrow(20);
ib = 0;
if (!_pglichStarts->FPush(&ib))
return fFalse;
_Reformat(0);
AssertThis(0);
return fTrue;
}
/***************************************************************************
The TXDD has changed sizes, set the _clnDisp.
***************************************************************************/
void TXDD::_NewRc(void)
{
AssertThis(0);
RC rc;
GetRc(&rc, cooLocal);
_clnDisp = LwMax(1, LwDivAway(rc.Dyp(), _dypLine));
_clnDispWhole = LwMax(1, rc.Dyp()/ _dypLine);
_Reformat(_clnDisp);
TXDD_PAR::_NewRc();
}
/***************************************************************************
Deactivate the TXDD - turn off the selection.
***************************************************************************/
void TXDD::_Activate(bool fActive)
{
AssertThis(0);
TXDD_PAR::_Activate(fActive);
if (!fActive)
_SwitchSel(fFalse, fFalse);
}
/***************************************************************************
Find new line starts starting at lnMin.
***************************************************************************/
void TXDD::_Reformat(long lnMin, long *pclnIns, long *pclnDel)
{
AssertThis(0);
AssertIn(lnMin, 0, kcbMax);
long ich, ichT;
long ln, clnIns, clnDel;
lnMin = LwMin(lnMin, _pglichStarts->IvMac() - 1);
ich = *_QichLn(lnMin);
clnIns = clnDel = 0;
for (ln = lnMin + 1; ln <= _clnDisp; ln++)
{
if (!_FFindNextLineStart(ich, &ich))
{
_pglichStarts->FSetIvMac(ln);
return;
}
//delete starts that are before ich
for (;;)
{
if (ln >= _pglichStarts->IvMac())
break;
ichT = *_QichLn(ln);
if (ichT == ich)
goto LDone;
if (ichT > ich)
break;
_pglichStarts->Delete(ln);
clnDel++;
}
//add the new line start
if (!_pglichStarts->FInsert(ln, &ich))
{
if (ln >= _pglichStarts->IvMac())
{
Warn("Reformatting failed");
return;
}
*_QichLn(ln) = ich;
}
clnIns++;
}
LDone:
//truncate the list of line starts
if (_pglichStarts->IvMac() > _clnDisp + 1)
_pglichStarts->FSetIvMac(_clnDisp + 1);
if (pvNil != pclnIns)
*pclnIns = clnIns;
if (pvNil != pclnDel)
*pclnDel = clnDel;
}
/***************************************************************************
Find new line starts starting at lnMin.
***************************************************************************/
void TXDD::_ReformatEdit(long ichMinEdit, long cchIns, long cchDel,
long *plnNew, long *pclnIns, long *pclnDel)
{
AssertThis(0);
AssertIn(ichMinEdit, 0, kcbMax);
AssertIn(cchIns, 0, _pbsf->IbMac() - ichMinEdit + 1);
AssertIn(cchDel, 0, kcbMax);
AssertNilOrVarMem(plnNew);
AssertNilOrVarMem(pclnIns);
AssertNilOrVarMem(pclnDel);
long ich;
long ln, clnDel;
// if the first displayed character was affected, reset it
// to a valid line start
if (FIn(*_QichLn(0) - 1, ichMinEdit, ichMinEdit + cchDel))
{
AssertDo(_FFindLineStart(ichMinEdit, &ich), 0);
*_QichLn(0) = ich;
}
//skip unaffected lines
for (ln = 0; ln < _pglichStarts->IvMac() && *_QichLn(ln) <= ichMinEdit; ln++)
;
clnDel = 0;
if (cchDel > 0)
{
//remove any deleted lines
while (ln < _pglichStarts->IvMac() && *_QichLn(ln) <= ichMinEdit + cchDel)
{
_pglichStarts->Delete(ln);
clnDel++;
}
}
if (cchIns != cchDel)
{
long lnT;
for (lnT = ln; lnT < _pglichStarts->IvMac(); lnT++)
*_QichLn(lnT) += cchIns - cchDel;
}
_Reformat(LwMax(0, ln - 1), pclnIns);
_Reformat(_clnDisp);
if (pvNil != plnNew)
*plnNew = ln;
if (pvNil != pclnDel)
*pclnDel = clnDel;
}
/***************************************************************************
Fetch a character of the stream through the cache.
***************************************************************************/
bool TXDD::_FFetchCh(long ich, achar *pch)
{
AssertThis(0);
AssertIn(_ichMinCache, 0, _pbsf->IbMac() + 1);
AssertIn(_ichLimCache, _ichMinCache, _pbsf->IbMac() + 1);
if (!FIn(ich, _ichMinCache, _ichLimCache))
{
//not a cache hit
long ichMinCache, ichLimCache;
long ichLim = _pbsf->IbMac();
if (!FIn(ich, 0, ichLim))
{
TrashVar(pch);
return fFalse;
}
//need to fetch some characters - try to center ich in the new cached data
ichMinCache = LwMax(0,
LwMin(ich - size(_rgchCache) / 2, ichLim - size(_rgchCache)));
ichLimCache = LwMin(ichLim, ichMinCache + size(_rgchCache));
AssertIn(ich, ichMinCache, ichLimCache);
//see if we can use some of the currently cached characters
if (_ichMinCache >= _ichLimCache)
goto LFetchAll;
if (FIn(_ichMinCache, ich, ichLimCache))
{
if (ichLimCache > _ichLimCache)
{
ichLimCache = _ichLimCache;
ichMinCache = LwMax(0, ichLimCache - size(_rgchCache));
}
BltPb(_rgchCache, _rgchCache + (_ichMinCache - ichMinCache),
ichLimCache - _ichMinCache);
_pbsf->FetchRgb(ichMinCache, _ichMinCache - ichMinCache,
_rgchCache);
}
else if (FIn(_ichLimCache, ichMinCache, ich + 1))
{
if (ichMinCache < _ichMinCache)
{
ichMinCache = _ichMinCache;
ichLimCache = LwMin(ichLim, ichMinCache + size(_rgchCache));
}
BltPb(_rgchCache + (ichMinCache - _ichMinCache), _rgchCache,
_ichLimCache - ichMinCache);
_pbsf->FetchRgb(_ichLimCache, ichLimCache - _ichLimCache,
_rgchCache + (_ichLimCache - ichMinCache));
}
else
{
LFetchAll:
_pbsf->FetchRgb(ichMinCache, ichLimCache - ichMinCache, _rgchCache);
}
_ichMinCache = ichMinCache;
_ichLimCache = ichLimCache;
AssertIn(ich, _ichMinCache, _ichLimCache);
}
*pch = _rgchCache[ich - _ichMinCache];
return fTrue;
}
/***************************************************************************
Find the start of the line that ich is on.
***************************************************************************/
bool TXDD::_FFindLineStart(long ich, long *pich)
{
AssertThis(0);
AssertVarMem(pich);
achar ch;
long dichLine = 0;
long ichOrig = ich;
if (ich > 0 && _FFetchCh(ich, &ch) && ch == kchLineFeed)
dichLine++;
for (;;)
{
if (!_FFetchCh(--ich, &ch))
{
if (ich == -1)
{
*pich = 0;
return fTrue;
}
TrashVar(pich);
return fFalse;
}
switch (ch)
{
case kchLineFeed:
dichLine++;
break;
case kchReturn:
if ((*pich = ich + 1 + dichLine) <= ichOrig)
return fTrue;
//fall through
default:
dichLine = 0;
break;
}
}
Bug("How did we get here?");
}
/***************************************************************************
Find the next line start after ich. If prgch is not nil, fills it
with the characters between ich and the line start (but not more
than cchMax characters).
***************************************************************************/
bool TXDD::_FFindNextLineStart(long ich, long *pich, achar *prgch, long cchMax)
{
AssertThis(0);
AssertVarMem(pich);
AssertIn(cchMax, 0, kcbMax);
achar ch;
bool fCr = fFalse;
if (pvNil != prgch)
AssertPvCb(prgch, cchMax);
else
cchMax = 0;
for ( ; ; ich++)
{
if (!_FFetchCh(ich, &ch))
{
if (fCr && ich == _pbsf->IbMac())
{
*pich = ich;
return fTrue;
}
TrashVar(pich);
return fFalse;
}
switch (ch)
{
case kchLineFeed:
break;
case kchReturn:
if (!fCr)
{
fCr = fTrue;
break;
}
//fall through
default:
if (fCr)
{
*pich = ich;
return fTrue;
}
break;
}
if (cchMax > 0)
{
*prgch++ = ch;
cchMax--;
}
}
Bug("how did we get here?");
}
/***************************************************************************
Find the start of the line that ich is on. This routine assumes
that _pglichStarts is valid and tries to use it.
***************************************************************************/
bool TXDD::_FFindLineStartCached(long ich, long *pich)
{
AssertThis(0);
long ln = _LnFromIch(ich);
if (FIn(ln, 0, _clnDisp))
{
*pich = *_QichLn(ln);
return fTrue;
}
return _FFindLineStart(ich, pich);
}
/***************************************************************************
Find the next line start after ich. If prgch is not nil, fills it
with the characters between ich and the line start (but not more
than cchMax characters).
***************************************************************************/
bool TXDD::_FFindNextLineStartCached(long ich, long *pich, achar *prgch, long cchMax)
{
AssertThis(0);
if (pvNil == prgch || cchMax == 0)
{
long ln = _LnFromIch(ich);
if (FIn(ln, 0, _pglichStarts->IvMac() - 1))
{
*pich = *_QichLn(ln + 1);
return fTrue;
}
}
return _FFindNextLineStart(ich, pich, prgch, cchMax);
}
/***************************************************************************
Draw the contents of the gob.
***************************************************************************/
void TXDD::Draw(PGNV pgnv, RC *prcClip)
{
AssertThis(0);
AssertPo(pgnv, 0);
AssertVarMem(prcClip);
RC rc;
long yp;
long ln, lnLim;
long cchDraw;
achar rgch[kcchMaxLine];
GetRc(&rc, cooLocal);
if (!rc.FIntersect(prcClip))
return;
pgnv->SetFont(_onn, _grfont, _dypFont);
ln = rc.ypTop / _dypLine;
yp = LwMul(ln, _dypLine);
lnLim = LwMin(_pglichStarts->IvMac(), LwDivAway(rc.ypBottom, _dypLine));
for ( ; ln < lnLim; yp += _dypLine)
{
_FetchLineLn(ln++, rgch, size(rgch), &cchDraw);
_DrawLine(pgnv, prcClip, yp, rgch, cchDraw);
}
rc.ypTop = yp;
pgnv->FillRc(&rc, kacrWhite);
if (_fSelOn)
_InvertSel(pgnv, fTrue);
}
/***************************************************************************
Fetch the characters for the given line.
***************************************************************************/
void TXDD::_FetchLineLn(long ln, achar *prgch, long cchMax, long *pcch, long *pichMin)
{
AssertThis(0);
long ichMin, ichLim;
ichMin = _IchMinLn(ln);
ichLim = _IchMinLn(ln + 1);
*pcch = LwMin(cchMax, ichLim - ichMin);
_pbsf->FetchRgb(ichMin, *pcch, prgch);
if (pvNil != pichMin)
*pichMin = ichMin;
}
/***************************************************************************
Fetch the characters for the given line.
***************************************************************************/
void TXDD::_FetchLineIch(long ich, achar *prgch, long cchMax, long *pcch, long *pichMin)
{
AssertThis(0);
long ichMin, ichLim;
AssertDo(_FFindLineStartCached(ich, &ichMin), 0);
if (!_FFindNextLineStartCached(ichMin, &ichLim, prgch, cchMax))
ichLim = _pbsf->IbMac();
*pcch = LwMin(cchMax, ichLim - ichMin);
if (pvNil != pichMin)
*pichMin = ichMin;
}
/***************************************************************************
Draw the line in the given GNV.
***************************************************************************/
void TXDD::_DrawLine(PGNV pgnv, RC *prcClip, long yp, achar *prgch, long cch)
{
AssertThis(0);
long xp, xpOrigin, xpPrev;
long ich, ichMin;
RC rc;
xpPrev = 0;
xp = xpOrigin = kdxpIndentTxdd - _scvHorz;
ich = 0;
while (ich < cch)
{
while (ich < cch)
{
switch (prgch[ich])
{
case kchTab:
xp = xpOrigin + LwRoundAway(xp - xpOrigin + 1, _dxpTab);
break;
case kchReturn:
case kchLineFeed:
break;
default:
goto LNonWhite;
}
ich++;
}
LNonWhite:
//erase any blank portion of the line
if (xp > xpPrev && xp > prcClip->xpLeft && xpPrev < prcClip->xpRight)
{
rc.Set(xpPrev, yp, xp, yp + _dypLine);
pgnv->FillRc(&rc, kacrWhite);
}
ichMin = ich;
while (ich < cch)
{
switch (prgch[ich])
{
case kchTab:
case kchReturn:
case kchLineFeed:
goto LEndRun;
}
ich++;
}
LEndRun:
if (ich > ichMin)
{
pgnv->GetRcFromRgch(&rc, prgch + ichMin, ich - ichMin);
if (xp + rc.Dxp() > prcClip->xpLeft && xp < prcClip->xpRight)
pgnv->DrawRgch(prgch + ichMin, ich - ichMin, xp, yp, kacrBlack, kacrWhite);
xp += rc.Dxp();
}
xpPrev = xp;
}
//erase any remaining portion of the line
if (xpPrev < prcClip->xpRight)
{
rc.Set(xpPrev, yp, prcClip->xpRight, yp + _dypLine);
pgnv->FillRc(&rc, kacrWhite);
}
}
/***************************************************************************
Return the maximum scroll value for this view of the doc.
***************************************************************************/
long TXDD::_ScvMax(bool fVert)
{
if (fVert)
{
long ich;
if (!_FFindLineStartCached(_pbsf->IbMac() - 1, &ich))
ich = 0;
return ich;
}
return LwMul(_dxpTab, 100);
}
/***************************************************************************
Perform a scroll according to scaHorz and scaVert.
***************************************************************************/
void TXDD::_Scroll(long scaHorz, long scaVert, long scvHorz, long scvVert)
{
AssertThis(0);
RC rc;
long dxp, dyp;
GetRc(&rc, cooLocal);
dxp = 0;
switch (scaHorz)
{
default:
Assert(scaHorz == scaNil, "bad scaHorz");
break;
case scaPageUp:
dxp = -LwMulDiv(rc.Dxp(), 9, 10);
goto LHorz;
case scaPageDown:
dxp = LwMulDiv(rc.Dxp(), 9, 10);
goto LHorz;
case scaLineUp:
dxp = -rc.Dxp() / 10;
goto LHorz;
case scaLineDown:
dxp = rc.Dxp() / 10;
goto LHorz;
case scaToVal:
dxp = scvHorz - _scvHorz;
LHorz:
dxp = LwBound(_scvHorz + dxp, 0, _ScvMax(fFalse) + 1) - _scvHorz;
_scvHorz += dxp;
break;
}
dyp = 0;
if (scaVert != scaNil)
{
long ichMin;
long ichMinNew;
long cln;
long ichT;
ichMin = *_QichLn(0);
switch (scaVert)
{
default:
Bug("bad scaVert");
ichMinNew = ichMin;
break;
case scaToVal:
scvVert = LwBound(scvVert, 0, _ScvMax(fTrue) + 1);
AssertDo(_FFindLineStartCached(scvVert, &ichMinNew), 0);
for (cln = 0, ichT = LwMin(ichMin, ichMinNew);
cln < _clnDisp && _FFindNextLineStartCached(ichT, &ichT) &&
(ichT <= ichMin || ichT <= ichMinNew); )
{
cln++;
}
if (ichMin > ichMinNew)
cln = -cln;
dyp = LwMul(cln, _dypLine);
break;
case scaPageDown:
cln = LwMax(1, _clnDispWhole - 1);
goto LDown;
case scaLineDown:
cln = 1;
LDown:
cln = LwMin(cln, _pglichStarts->IvMac() - 1);
dyp = LwMul(cln, _dypLine);
ichMinNew = *_QichLn(cln);
break;
case scaPageUp:
cln = LwMax(1, _clnDispWhole - 1);
goto LUp;
case scaLineUp:
cln = 1;
LUp:
ichMinNew = ichMin;
while (cln-- > 0 && _FFindLineStart(ichMinNew - 1, &ichT))
{
dyp -= _dypLine;
ichMinNew = ichT;
}
break;
}
_scvVert = ichMinNew;
if (ichMinNew != ichMin)
{
*_QichLn(0) = ichMinNew;
_Reformat(0);
_Reformat(_clnDisp);
}
}
_SetScrollValues();
if (dxp != 0 || dyp != 0)
_ScrollDxpDyp(dxp, dyp);
}
/***************************************************************************
Do idle processing. If this handler has the active selection, 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 TXDD::FCmdSelIdle(PCMD pcmd)
{
AssertThis(0);
//if rglw[1] is this one's hid, don't change the sel state.
if (pcmd->rglw[1] != Hid())
{
if (!pcmd->rglw[0])
_SwitchSel(fFalse, fFalse);
else if (_ichAnchor != _ichOther || _tsSel == 0)
_SwitchSel(fTrue, fTrue);
else if (DtsCaret() < TsCurrent() - _tsSel)
_SwitchSel(!_fSelOn, fTrue);
}
pcmd->rglw[0] = fFalse;
return fFalse;
}
/***************************************************************************
Set the selection.
***************************************************************************/
void TXDD::SetSel(long ichAnchor, long ichOther, bool fDraw)
{
AssertThis(0);
long ichMac = _pbsf->IbMac();
ichAnchor = LwBound(ichAnchor, 0, ichMac + 1);
ichOther = LwBound(ichOther, 0, ichMac + 1);
if (ichAnchor == _ichAnchor && ichOther == _ichOther)
return;
if (_fSelOn)
{
GNV gnv(this);
if (_ichAnchor != ichAnchor || _ichAnchor == _ichOther ||
ichAnchor == ichOther)
{
_InvertSel(&gnv, fDraw);
_ichAnchor = ichAnchor;
_ichOther = ichOther;
_InvertSel(&gnv, fDraw);
_tsSel = TsCurrent();
}
else
{
//they have the same anchor and neither is an insertion
_InvertIchRange(&gnv, _ichOther, ichOther, fDraw);
_ichOther = ichOther;
}
}
else
{
_ichAnchor = ichAnchor;
_ichOther = ichOther;
_tsSel = 0L;
}
}
/***************************************************************************
Turn the sel on or off according to fOn.
***************************************************************************/
void TXDD::_SwitchSel(bool fOn, bool fDraw)
{
AssertThis(0);
if (FPure(fOn) != FPure(_fSelOn))
{
GNV gnv(this);
_InvertSel(&gnv, fDraw);
_fSelOn = FPure(fOn);
_tsSel = TsCurrent();
}
}
/***************************************************************************
Invert the current selection.
***************************************************************************/
void TXDD::_InvertSel(PGNV pgnv, bool fDraw)
{
AssertThis(0);
AssertPo(pgnv, 0);
RC rc, rcT;
long ln;
if (_ichAnchor == _ichOther)
{
//insertion bar
ln = _LnFromIch(_ichAnchor);
if (!FIn(ln, 0, _clnDisp))
return;
rc.xpLeft = _XpFromLnIch(pgnv, ln, _ichAnchor) - 1;
rc.xpRight = rc.xpLeft + 2;
rc.ypTop = LwMul(ln, _dypLine);
rc.ypBottom = rc.ypTop + _dypLine;
GetRc(&rcT, cooLocal);
if (rcT.FIntersect(&rc))
{
if (fDraw)
pgnv->FillRc(&rcT, kacrInvert);
else
InvalRc(&rcT, kginMark);
}
}
else
_InvertIchRange(pgnv, _ichAnchor, _ichOther, fDraw);
}
/***************************************************************************
Invert a range.
***************************************************************************/
void TXDD::_InvertIchRange(PGNV pgnv, long ich1, long ich2, bool fDraw)
{
AssertThis(0);
AssertPo(pgnv, 0);
AssertIn(ich1, 0, _pbsf->IbMac() + 1);
AssertIn(ich2, 0, _pbsf->IbMac() + 1);
RC rc, rcClip, rcT;
long ln1, ln2, xp2;
if (ich1 == ich2)
return;
SortLw(&ich1, &ich2);
ln1 = _LnFromIch(ich1);
ln2 = _LnFromIch(ich2);
if (ln1 >= _clnDisp || ln2 < 0)
return;
GetRc(&rcClip, cooLocal);
rc.xpLeft = _XpFromLnIch(pgnv, ln1, ich1);
rc.ypTop = LwMul(ln1, _dypLine);
rc.ypBottom = LwMul(ln1 + 1, _dypLine);
xp2 = _XpFromLnIch(pgnv, ln2, ich2);
if (ln2 == ln1)
{
//only one line involved
rc.xpRight = xp2;
if (rcT.FIntersect(&rc, &rcClip))
{
if (fDraw)
pgnv->HiliteRc(&rcT, kacrWhite);
else
InvalRc(&rcT);
}
return;
}
//invert the sel on the first line
rc.xpRight = rcClip.xpRight;
if (rcT.FIntersect(&rc, &rcClip))
{
if (fDraw)
pgnv->HiliteRc(&rcT, kacrWhite);
else
InvalRc(&rcT);
}
//invert the main rectangular block
rc.xpLeft = kdxpIndentTxdd - _scvHorz;
rc.ypTop = rc.ypBottom;
rc.ypBottom = LwMul(ln2, _dypLine);
if (rcT.FIntersect(&rc, &rcClip))
{
if (fDraw)
pgnv->HiliteRc(&rcT, kacrWhite);
else
InvalRc(&rcT);
}
//invert the last line
rc.ypTop = rc.ypBottom;
rc.ypBottom = LwMul(ln2 + 1, _dypLine);
rc.xpRight = xp2;
if (rcT.FIntersect(&rc, &rcClip))
{
if (fDraw)
pgnv->HiliteRc(&rcT, kacrWhite);
else
InvalRc(&rcT);
}
}
/***************************************************************************
Find the line in the TXDD that is displaying the given ich. Returns -1
if the ich is before the first displayed ich and returns _clnDisp if
ich is after the last displayed ich.
***************************************************************************/
long TXDD::_LnFromIch(long ich)
{
AssertThis(0);
long lnMin, lnLim, ln;
long ichT;
lnMin = 0;
lnLim = LwMin(_clnDisp + 1, _pglichStarts->IvMac());
while (lnMin < lnLim)
{
ln = (lnMin + lnLim) / 2;
ichT = *_QichLn(ln);
if (ichT < ich)
lnMin = ln + 1;
else if (ichT > ich)
lnLim = ln;
else
return ln;
}
return lnMin - 1;
}
/***************************************************************************
Return the ich of the first character on the given line. If ln < 0,
returns 0; if ln >= _clnDisp, returns IbMac().
***************************************************************************/
long TXDD::_IchMinLn(long ln)
{
AssertThis(0);
long ich;
if (ln < 0)
return 0;
if (ln >= _pglichStarts->IvMac())
return _pbsf->IbMac();
ich = *_QichLn(ln);
return ich;
}
/***************************************************************************
Return the xp location of the given ich on the given line.
***************************************************************************/
long TXDD::_XpFromLnIch(PGNV pgnv, long ln, long ich)
{
AssertThis(0);
AssertPo(pgnv, 0);
RC rc;
long cch, ichT;
achar rgch[kcchMaxLine];
if (!FIn(ln, 0, LwMin(_clnDisp, _pglichStarts->IvMac())))
return 0;
ichT = *_QichLn(ln);
_FetchLineLn(ln, rgch, LwMin(ich - ichT, size(rgch)), &cch);
return _XpFromRgch(pgnv, rgch, cch);
}
/***************************************************************************
Return the xp location of the given ich.
***************************************************************************/
long TXDD::_XpFromIch(long ich)
{
AssertThis(0);
long ichMin, cch;
achar rgch[kcchMaxLine];
if (!_FFindLineStartCached(ich, &ichMin))
return 0;
GNV gnv(this);
cch = LwMin(size(rgch), ich - ichMin);
_pbsf->FetchRgb(ichMin, cch, rgch);
return _XpFromRgch(&gnv, rgch, cch);
}
/***************************************************************************
Return the xp location of the end of the given (rgch, cch), assuming
it starts at the beginning of a line.
***************************************************************************/
long TXDD::_XpFromRgch(PGNV pgnv, achar *prgch, long cch)
{
AssertThis(0);
long xp, xpOrigin;
long ich, ichMin;
RC rc;
pgnv->SetFont(_onn, _grfont, _dypFont);
xp = xpOrigin = kdxpIndentTxdd - _scvHorz;
ich = 0;
while (ich < cch)
{
while (ich < cch)
{
switch (prgch[ich])
{
case kchTab:
xp = xpOrigin + LwRoundAway(xp - xpOrigin + 1, _dxpTab);
break;
case kchReturn:
case kchLineFeed:
break;
default:
goto LNonWhite;
}
ich++;
}
LNonWhite:
ichMin = ich;
while (ich < cch)
{
switch (prgch[ich])
{
case kchTab:
case kchReturn:
case kchLineFeed:
goto LEndRun;
}
ich++;
}
LEndRun:
if (ich > ichMin)
{
pgnv->GetRcFromRgch(&rc, prgch + ichMin, ich - ichMin);
xp += rc.Dxp();
}
}
return xp;
}
/***************************************************************************
Find the character that is closest to xp on the given line.
***************************************************************************/
long TXDD::_IchFromLnXp(long ln, long xp)
{
AssertThis(0);
long ichMin, cch;
achar rgch[kcchMaxLine];
if (ln < 0)
return 0;
_FetchLineLn(ln, rgch, size(rgch), &cch, &ichMin);
return _IchFromRgchXp(rgch, cch, ichMin, xp);
}
/***************************************************************************
Find the character that is closest to xp on the same line as the given
character.
***************************************************************************/
long TXDD::_IchFromIchXp(long ich, long xp)
{
AssertThis(0);
long ichMin, cch;
achar rgch[kcchMaxLine];
_FetchLineIch(ich, rgch, size(rgch), &cch, &ichMin);
return _IchFromRgchXp(rgch, cch, ichMin, xp);
}
/***************************************************************************
Find the character that is closest to xp on the given line.
***************************************************************************/
long TXDD::_IchFromRgchXp(achar *prgch, long cch, long ichMinLine, long xp)
{
AssertThis(0);
long xpT;
long ich, ichMin, ichLim;
GNV gnv(this);
while (cch > 0 && (prgch[cch - 1] == kchReturn || prgch[cch - 1] == kchLineFeed))
cch--;
ichLim = ichMinLine + cch;
ichMin = ichMinLine;
while (ichMin < ichLim)
{
ich = (ichMin + ichLim) / 2;
xpT = _XpFromRgch(&gnv, prgch, ich - ichMinLine);
if (xpT < xp)
ichMin = ich + 1;
else
ichLim = ich;
}
if (ichMin > ichMinLine &&
LwAbs(xp - _XpFromRgch(&gnv, prgch, ichMin - 1 - ichMinLine))
< LwAbs(xp - _XpFromRgch(&gnv, prgch, ichMin - ichMinLine)))
{
ichMin--;
}
return ichMin;
}
/***************************************************************************
Make sure the selection is visible (or at least _ichOther is).
***************************************************************************/
void TXDD::ShowSel(bool fDraw)
{
AssertThis(0);
long ln, lnHope;
long dxpScroll, dichScroll;
long xpMin, xpLim;
RC rc;
long ichAnchor = _ichAnchor;
//find the lines we want to show
ln = _LnFromIch(_ichOther);
lnHope = _LnFromIch(ichAnchor);
GetRc(&rc, cooLocal);
dichScroll = 0;
if (!FIn(ln, 0, _clnDispWhole) || !FIn(lnHope, 0, _clnDispWhole))
{
//count the number of lines between _ichOther and ichAnchor
long ichMinLine, ich;
long ichMin = LwMin(ichAnchor, _ichOther);
long ichLim = LwMax(ichAnchor, _ichOther);
long cln = 0;
AssertDo(_FFindLineStartCached(ichMin, &ichMinLine), 0);
for (ich = ichMin; cln < _clnDispWhole && _FFindNextLineStartCached(ich, &ich) &&
ich < ichLim; cln++)
{
}
if (cln >= _clnDispWhole)
{
//just show _ichOther
AssertDo(_FFindLineStartCached(_ichOther, &ichMinLine), 0);
ichAnchor = _ichOther;
lnHope = ln;
cln = 0;
}
if (ln < 0 || lnHope < 0)
{
//scroll up
dichScroll = ichMinLine - _scvVert;
}
else if (ln >= _clnDispWhole || lnHope >= _clnDispWhole)
{
//scroll down
cln = LwMax(0, _clnDispWhole - cln - 1);
//move cln lines back from ichMinLine
while (cln-- > 0 && _FFindLineStartCached(ichMinLine - 1, &ichMin))
ichMinLine = ichMin;
dichScroll = ichMinLine - _scvVert;
}
}
//now do the horizontal stuff
xpMin = _XpFromIch(_ichOther);
xpLim = _XpFromIch(ichAnchor);
if (LwAbs(xpLim - xpMin) > rc.Dxp())
{
//can't show both
if (xpMin > xpLim)
{
xpLim = xpMin;
xpMin = xpLim - rc.Dxp();
}
else
xpLim = xpMin + rc.Dxp();
}
else
SortLw(&xpMin, &xpLim);
dxpScroll = LwMax(LwMin(0, rc.xpRight - xpLim), rc.xpLeft - xpMin);
if (dxpScroll != 0 || dichScroll != 0)
{
_Scroll(scaToVal, scaToVal, _scvHorz - dxpScroll,
_scvVert + dichScroll);
}
}
/***************************************************************************
Handle a mousedown in the TXDD.
***************************************************************************/
bool TXDD::FCmdTrackMouse(PCMD_MOUSE pcmd)
{
AssertThis(0);
AssertVarMem(pcmd);
RC rc;
long ich;
long scaHorz, scaVert;
long xp = pcmd->xp;
long yp = pcmd->yp;
if (pcmd->cid == cidMouseDown)
{
Assert(vpcex->PgobTracking() == pvNil, "mouse already being tracked!");
vpcex->TrackMouse(this);
}
else
{
Assert(vpcex->PgobTracking() == this, "not tracking mouse!");
Assert(pcmd->cid == cidTrackMouse, 0);
}
//do autoscrolling
GetRc(&rc, cooLocal);
if (!FIn(xp, rc.xpLeft, rc.xpRight))
{
scaHorz = (xp < rc.xpLeft) ? scaLineUp : scaLineDown;
xp = LwBound(xp, rc.xpLeft, rc.xpRight);
}
else
scaHorz = scaNil;
if (!FIn(yp, rc.ypTop, rc.ypBottom))
{
scaVert = (yp < rc.ypTop) ? scaLineUp : scaLineDown;
yp = LwBound(yp, rc.ypTop, rc.ypBottom);
}
else
scaVert = scaNil;
if (scaHorz != scaNil || scaVert != scaNil)
_Scroll(scaHorz, scaVert);
//set the selection
ich = _IchFromLnXp(yp / _dypLine, xp);
if (pcmd->cid != cidMouseDown || (pcmd->grfcust & fcustShift))
SetSel(_ichAnchor, ich, fTrue);
else
SetSel(ich, ich, fTrue);
_SwitchSel(fTrue, fTrue); //make sure the selection is on
ShowSel(fTrue);
if (!(pcmd->grfcust & fcustMouse))
vpcex->EndMouseTracking();
return fTrue;
}
/***************************************************************************
Handle a key down.
***************************************************************************/
bool TXDD::FCmdKey(PCMD_KEY pcmd)
{
AssertThis(0);
const long kcchInsBuf = 64;
AssertThis(0);
AssertVarMem(pcmd);
ulong grfcust;
long vkDone;
long dich, dln, ichLim, ichT, ichMin;
achar ch;
long cact;
CMD cmd;
achar rgch[kcchInsBuf + 1];
// keep fetching characters until we get a cursor key, delete key or
// until the buffer is full.
vkDone = vkNil;
ichLim = 0;
do
{
grfcust = pcmd->grfcust;
switch (pcmd->vk)
{
//these keys all terminate the key fetching loop
case kvkHome:
case kvkEnd:
case kvkLeft:
case kvkRight:
case kvkUp:
case kvkDown:
case kvkPageUp:
case kvkPageDown:
case kvkDelete:
case kvkBack:
vkDone = pcmd->vk;
goto LInsert;
default:
if (chNil == pcmd->ch)
break;
for (cact = 0; cact < pcmd->cact && ichLim < kcchInsBuf; cact++)
{
rgch[ichLim++] = (achar)pcmd->ch;
#ifdef WIN
if ((achar)pcmd->ch == kchReturn)
rgch[ichLim++] = kchLineFeed;
#endif //WIN
}
break;
}
pcmd = (PCMD_KEY)&cmd;
}
while (ichLim < kcchInsBuf && vpcex->FGetNextKey(&cmd));
LInsert:
if (ichLim > 0)
{
//have some characters to insert
FReplace(rgch, ichLim, _ichAnchor, _ichOther, fTrue);
}
dich = 0;
dln = 0;
switch (vkDone)
{
case kvkHome:
if (grfcust & fcustCmd)
dich = -_pbsf->IbMac() - ichLim - 1;
else if (_FFindLineStartCached(_ichOther, &ichT))
dich = ichT - _ichOther;
_fXpValid = fFalse;
goto LSetSel;
case kvkEnd:
if (grfcust & fcustCmd)
dich = _pbsf->IbMac() + ichLim + 1;
else
{
if (!_FFindNextLineStartCached(_ichOther, &ichT))
ichT = _pbsf->IbMac();
//don't advance past trailing line feed and return characters
while (ichT > _ichOther && _FFetchCh(ichT - 1, &ch) &&
(ch == kchReturn || ch == kchLineFeed))
{
ichT--;
}
dich = ichT - _ichOther;
}
_fXpValid = fFalse;
goto LSetSel;
case kvkLeft:
dich = -1;
while (_FFetchCh(_ichOther + dich, &ch) && ch == kchLineFeed)
dich--;
_fXpValid = fFalse;
goto LSetSel;
case kvkRight:
dich = 1;
while (_FFetchCh(_ichOther + dich, &ch) && ch == kchLineFeed)
dich++;
_fXpValid = fFalse;
goto LSetSel;
case kvkUp:
dln = -1;
goto LLineSel;
case kvkDown:
dln = 1;
goto LLineSel;
case kvkPageUp:
dln = -LwMax(1, _clnDispWhole - 1);
goto LLineSel;
case kvkPageDown:
dln = LwMax(1, _clnDispWhole - 1);
LLineSel:
if (!_fXpValid)
{
//get the xp of _ichOther
_xpSel = _XpFromIch(_ichOther) + _scvHorz;
_fXpValid = fTrue;
}
if (dln > 0)
{
ichMin = _ichOther;
while (dln-- > 0 && _FFindNextLineStartCached(ichMin, &ichT))
ichMin = ichT;
if (dln >= 0)
{
//goto end of doc
dich = _pbsf->IbMac() - _ichOther;
_fXpValid = fFalse;
}
else
goto LFindIch;
}
else
{
AssertDo(_FFindLineStartCached(_ichOther, &ichT), 0);
ichMin = ichT;
while (dln++ < 0 && _FFindLineStartCached(ichMin - 1, &ichT))
ichMin = ichT;
if (dln <= 0)
{
//goto top of doc
dich = -_ichOther;
_fXpValid = fFalse;
}
else
{
LFindIch:
//ichMin is the start of the line to move the selection to
dich = _IchFromIchXp(ichMin, _xpSel - _scvHorz) - _ichOther;
}
}
LSetSel:
//move the selection
if (grfcust & fcustShift)
{
//extend selection
SetSel(_ichAnchor, _ichOther + dich, fTrue);
ShowSel(fTrue);
}
else
{
long ichAnchor = _ichAnchor;
if (ichAnchor == _ichOther)
ichAnchor += dich;
else if ((dich > 0) != (ichAnchor > _ichOther))
ichAnchor = _ichOther;
SetSel(ichAnchor, ichAnchor, fTrue);
ShowSel(fTrue);
}
break;
case kvkDelete:
dich = 1;
goto LDelete;
case kvkBack:
dich = -1;
LDelete:
if (_ichAnchor != _ichOther)
dich = _ichOther - _ichAnchor;
else
dich = LwBound(_ichAnchor + dich, 0, _pbsf->IbMac() + 1) - _ichAnchor;
if (dich != 0)
FReplace(pvNil, 0, _ichAnchor, _ichAnchor + dich, fTrue);
break;
}
return fTrue;
}
/***************************************************************************
Replaces the characters between ich1 and ich2 with the given ones.
***************************************************************************/
bool TXDD::FReplace(achar *prgch, long cch, long ich1, long ich2, bool fDraw)
{
AssertThis(0);
_SwitchSel(fFalse, fTrue);
SortLw(&ich1, &ich2);
if (!_pbsf->FReplace(prgch, cch, ich1, ich2 - ich1))
return fFalse;
_InvalAllTxdd(ich1, cch, ich2 - ich1);
ich1 += cch;
SetSel(ich1, ich1, fTrue);
ShowSel(fTrue);
return fTrue;
}
/***************************************************************************
Invalidate all TXDDs on this text doc. Also dirties the document.
Should be called by any code that edits the document.
***************************************************************************/
void TXDD::_InvalAllTxdd(long ich, long cchIns, long cchDel)
{
AssertThis(0);
long ipddg;
PDDG pddg;
//mark the document dirty
_pdocb->SetDirty();
//inform the TXDDs
for (ipddg = 0; pvNil != (pddg = _pdocb->PddgGet(ipddg)); ipddg++)
{
if (pddg->FIs(kclsTXDD))
((PTXDD)pddg)->_InvalIch(ich, cchIns, cchDel);
}
}
/***************************************************************************
Invalidate the display from ich. If we're the active TXDD, also redraw.
***************************************************************************/
void TXDD::_InvalIch(long ich, long cchIns, long cchDel)
{
AssertThis(0);
Assert(!_fSelOn, "why is the sel on during an invalidation?");
RC rcLoc, rc;
long ichAnchor, ichOther;
long lnNew, clnIns, clnDel;
long yp, dypIns, dypDel;
//adjust the sel
ichAnchor = _ichAnchor;
ichOther = _ichOther;
FAdjustIv(&ichAnchor, ich, cchIns, cchDel);
FAdjustIv(&ichOther, ich, cchIns, cchDel);
if (ichAnchor != _ichAnchor || ichOther != _ichOther)
SetSel(ichAnchor, ichOther, fFalse);
//adjust the cache
if (_ichLimCache > _ichMinCache)
{
if (FPure(_ichLimCache <= ich) != FPure(_ichMinCache <= ich) ||
FPure(_ichLimCache >= ich + cchDel) !=
FPure(_ichMinCache >= ich + cchDel) ||
!FAdjustIv(&_ichLimCache, ich, cchIns, cchDel) ||
!FAdjustIv(&_ichMinCache, ich, cchIns, cchDel))
{
_ichMinCache = _ichLimCache = 0;
}
}
//reformat
_ReformatEdit(ich, cchIns, cchDel, &lnNew, &clnIns, &clnDel);
if (lnNew > 0)
{
lnNew--;
clnIns++;
clnDel++;
}
//determine the dirty rectangles and if we're active, update them
GetRc(&rcLoc, cooLocal);
if (!_fActive)
{
rc = rcLoc;
rc.ypTop = LwMul(lnNew, _dypLine);
if (clnIns == clnDel)
rc.ypBottom = LwMul(lnNew + clnIns, _dypLine);
InvalRc(&rc);
return;
}
dypIns = LwMul(clnIns, _dypLine);
dypDel = LwMul(clnDel, _dypLine);
yp = LwMul(lnNew, _dypLine);
rc = rcLoc;
rc.ypTop = yp;
rc.ypBottom = yp + LwMin(dypIns, dypDel);
if (_clnDisp > lnNew + clnIns - clnDel && clnIns != clnDel)
{
//have some bits to blt vertically
rc = rcLoc;
rc.ypTop = yp + LwMin(dypIns, dypDel);
Scroll(&rc, 0, dypIns - dypDel, kginDraw);
rc.ypBottom = rc.ypTop;
rc.ypTop = yp;
}
if (!rc.FEmpty())
InvalRc(&rc, kginDraw);
_fXpValid = fFalse;
}
/***************************************************************************
If ppdocb != pvNil, copy the selection to a new document and return
true. If ppdocb == pvNil just return whether the selection is
non-empty.
***************************************************************************/
bool TXDD::_FCopySel(PDOCB *ppdocb)
{
AssertThis(0);
PTXDC ptxdc;
long ich1, ich2;
if ((ich1 = _ichOther) == (ich2 = _ichAnchor))
return fFalse;
if (pvNil == ppdocb)
return fTrue;
SortLw(&ich1, &ich2);
if (pvNil != (ptxdc = TXDC::PtxdcNew()))
{
if (!ptxdc->Pbsf()->FReplaceBsf(_pbsf, ich1, ich2 - ich1, 0, 0))
ReleasePpo(&ptxdc);
}
*ppdocb = ptxdc;
return pvNil != *ppdocb;
}
/***************************************************************************
Clear (delete) the current selection.
***************************************************************************/
void TXDD::_ClearSel(void)
{
AssertThis(0);
FReplace(pvNil, 0, _ichAnchor, _ichOther, fTrue);
}
/***************************************************************************
Paste the given doc into this one.
***************************************************************************/
bool TXDD::_FPaste(PCLIP pclip, bool fDoIt, long cid)
{
AssertThis(0);
AssertPo(pclip, 0);
long ich1, ich2, cch;
PTXDC ptxdc;
PBSF pbsf;
if (cidPaste != cid || !pclip->FGetFormat(kclsTXDC))
return fFalse;
if (!fDoIt)
return fTrue;
if (!pclip->FGetFormat(kclsTXDC, (PDOCB *)&ptxdc))
return fFalse;
AssertPo(ptxdc, 0);
if (pvNil == (pbsf = ptxdc->Pbsf()) || 0 >= (cch = pbsf->IbMac()))
{
ReleasePpo(&ptxdc);
return fTrue;
}
_SwitchSel(fFalse, fTrue);
ich1 = _ichAnchor;
ich2 = _ichOther;
SortLw(&ich1, &ich2);
if (!_pbsf->FReplaceBsf(pbsf, 0, cch, ich1, ich2 - ich1))
{
ReleasePpo(&ptxdc);
return fFalse;
}
ReleasePpo(&ptxdc);
_InvalAllTxdd(ich1, cch, ich2 - ich1);
ich1 += cch;
SetSel(ich1, ich1, fTrue);
ShowSel(fTrue);
return fTrue;
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a TXDD.
***************************************************************************/
void TXDD::AssertValid(ulong grf)
{
//REVIEW shonk: fill in more
TXDD_PAR::AssertValid(0);
AssertPo(_pbsf, 0);
AssertPo(_pglichStarts, 0);
}
/***************************************************************************
Mark memory for the TXDD.
***************************************************************************/
void TXDD::MarkMem(void)
{
AssertValid(0);
TXDD_PAR::MarkMem();
MarkMemObj(_pbsf);
MarkMemObj(_pglichStarts);
}
#endif //DEBUG