📄 text.cpp
字号:
TCHAR const *pch, //@parm replacement text
IUndoBuilder *publdr, //@parm if non-NULL, where to put an
// anti-event for this action
IAntiEvent *paeCF, //@parm char format AE
IAntiEvent *paePF ) //@parm paragraph formatting AE
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::ReplaceRange");
_TEST_INVARIANT_
DWORD cchAdded = 0;
DWORD cchInBlock;
DWORD cchNewInBlock;
CTxtBlk *ptb;
if(cchOld < 0)
cchOld = GetTextLength() - _cp;
if( publdr )
{
HandleReplaceRangeUndo( cchOld, cchNew, publdr, paeCF, paePF);
}
// blocks involving replacement
while(cchOld > 0 && cchNew > 0)
{
ptb = GetRun(0);
// cchOld should never be nonzero if the text run is empty
AssertSz(ptb,
"CTxtPtr::Replace() - Pointer to text block is NULL !");
ptb->MoveGap(_ich);
cchInBlock = min((DWORD)cchOld, ptb->_cch - _ich);
if(cchInBlock > 0)
{
cchOld -= cchInBlock;
ptb->_cch -= cchInBlock;
((CTxtArray *)_prgRun)->_cchText -= cchInBlock;
}
cchNewInBlock = CchOfCb(ptb->_cbBlock) - ptb->_cch;
// if there's room for a gap, leave one
if(cchNewInBlock > cchGapInitial)
cchNewInBlock -= cchGapInitial;
if(cchNewInBlock > cchNew)
cchNewInBlock = cchNew;
if(cchNewInBlock > 0)
{
CopyMemory(ptb->_pch + _ich, pch, CbOfCch(cchNewInBlock));
cchNew -= cchNewInBlock;
_cp += cchNewInBlock;
_ich += cchNewInBlock;
pch += cchNewInBlock;
cchAdded += cchNewInBlock;
ptb->_cch += cchNewInBlock;
ptb->_ibGap += CbOfCch(cchNewInBlock);
((CTxtArray *)_prgRun)->_cchText += cchNewInBlock;
}
if(_iRun >= Count() - 1 || !cchOld )
break;
// go to next block
_iRun++;
_ich = 0;
}
if(cchNew > 0)
cchAdded += InsertRange(cchNew, pch);
else if(cchOld > 0)
DeleteRange(cchOld);
return cchAdded;
}
/*
* CTxtPtr::HandleReplaceRangeUndo (cchOld, cchNew, pch, publdr)
*
* @mfunc
* worker function for ReplaceRange. Figures out what will happen in
* the replace range call and creates the appropriate anti-events
*
* @devnote
* We first check to see if our replace range data can be merged into
* an existing anti-event. If it can, then we just return.
* Otherwise, we copy the deleted characters into an allocated buffer
* and then create a ReplaceRange anti-event.
*
* In order to handle ordering problems between formatting and text
* anti-events (that is, text needs to exist before formatting can
* be applied), we have any formatting anti-events passed to us first.
*/
void CTxtPtr::HandleReplaceRangeUndo(
DWORD cchOld, //@parm Count of characters to delete
DWORD cchNew, //@parm Count of new characters to add
IUndoBuilder * publdr, //@parm Undo builder to receive anti-event
IAntiEvent * paeCF, //@parm char formatting AE
IAntiEvent * paePF ) //@parm paragraph formatting AE
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::HandleReplaceRangeUndo");
_TEST_INVARIANT_
IAntiEvent *pae = publdr->GetTopAntiEvent();
TCHAR * pch = NULL;
if( pae )
{
SimpleReplaceRange sr;
sr.cpMin = _cp;
sr.cpMax = _cp + cchNew;
sr.cchDel = cchOld;
#ifdef PWD_JUPITER // GuyBark Jupiter 18411: Don't attempt to merge events during an undo
if( !(publdr->_bNoAttemptToMerge) && pae->MergeData(MD_SIMPLE_REPLACERANGE, &sr) == NOERROR )
#else
if( pae->MergeData(MD_SIMPLE_REPLACERANGE, &sr) == NOERROR )
#endif // PWD_JUPITER
{
// if the data was merged successfully, then we do
// not need these anti-events
if( paeCF )
{
DestroyAEList(paeCF);
}
if( paePF )
{
DestroyAEList(paePF);
}
// we've done everything we need to.
return;
}
}
// allocate a buffer and grab the soon-to-be deleted
// text (if necessary)
if( cchOld > 0 )
{
pch = new TCHAR[cchOld];
if( pch )
{
GetText(cchOld, pch);
}
else
{
cchOld = 0;
}
}
// the new range will exist from our current position plus
// cchNew (because everything in cchOld gets deleted)
pae = gAEDispenser.CreateReplaceRangeAE(_ped, _cp, _cp + cchNew,
cchOld, pch, paeCF, paePF);
if( !pae )
{
delete [] pch;
}
if( pae )
{
publdr->AddAntiEvent(pae);
}
}
/*
* CTxtPtr::InsertRange(cch, pch)
*
* @mfunc
* Insert a range of characters at this text pointer
*
* @rdesc
* Count of characters successfully inserted
*
* @comm Side Effects: <nl>
* moves this text pointer to end of inserted text <nl>
* moves the text block array <nl>
*/
DWORD CTxtPtr::InsertRange (
DWORD cch, //@parm length of text to insert
TCHAR const *pch) //@parm text to insert
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::InsertRange");
_TEST_INVARIANT_
DWORD cchSave = cch;
DWORD cchInBlock;
DWORD cchFirst;
DWORD cchLast = 0;
DWORD ctbNew;
CTxtBlk *ptb;
// Ensure text array is allocated
if(!Count())
{
LONG cbSize = -1;
// If we don't have any blocks, allocate first block to be big enuf
// for the inserted text *only* if it's smaller than the normal block
// size. This allows us to be used efficiently as a display engine
// for small amounts of text.
if( cch < CchOfCb(cbBlockInitial) )
cbSize = CbOfCch(cch);
if( !((CTxtArray *)_prgRun)->AddBlock(0, cbSize) )
{
_ped->GetCallMgr()->SetOutOfMemory();
goto done;
}
}
ptb = GetRun(0);
cchInBlock = CchOfCb(ptb->_cbBlock) - ptb->_cch;
AssertSz(ptb->_cbBlock <= cbBlockMost, "block too big");
// try and resize without splitting...
if(cch > cchInBlock &&
cch <= cchInBlock + CchOfCb(cbBlockMost - ptb->_cbBlock))
{
if( !ptb->ResizeBlock(min(cbBlockMost,
CbOfCch(ptb->_cch + cch + cchGapInitial))) )
{
_ped->GetCallMgr()->SetOutOfMemory();
goto done;
}
cchInBlock = CchOfCb(ptb->_cbBlock) - ptb->_cch;
}
if(cch <= cchInBlock)
{
// all fits into block without any hassle
ptb->MoveGap(_ich);
CopyMemory(ptb->_pch + _ich, pch, CbOfCch(cch));
_cp += cch; // *this points at end of
_ich += cch; // insertion
ptb->_cch += cch;
((CTxtArray *)_prgRun)->_cchText += cch;
ptb->_ibGap += CbOfCch(cch);
return cch;
}
// won't all fit in this block
// figure out best division into blocks
TxDivideInsertion(cch, _ich, ptb->_cch - _ich,&cchFirst, &cchLast);
// Subtract cchLast up front so return value isn't negative
// if SplitBlock() fails
cch -= cchLast; // don't include last block in count for middle blocks
// split block containing insertion point
// ***** moves _prgtb ***** //
if(!((CTxtArray *)_prgRun)->SplitBlock(_iRun, _ich, cchFirst, cchLast,
_ped->IsStreaming()))
{
_ped->GetCallMgr()->SetOutOfMemory();
goto done;
}
ptb = GetRun(0); // recompute ptb after (*_prgRun) moves
// copy into first block (first half of split)
if(cchFirst > 0)
{
AssertSz(ptb->_ibGap == CbOfCch(_ich), "split first gap in wrong place");
AssertSz(cchFirst <= CchOfCb(ptb->_cbBlock) - ptb->_cch, "split first not big enough");
CopyMemory(ptb->_pch + _ich, pch, CbOfCch(cchFirst));
cch -= cchFirst;
pch += cchFirst;
_ich += cchFirst;
ptb->_cch += cchFirst;
((CTxtArray *)_prgRun)->_cchText += cchFirst;
ptb->_ibGap += CbOfCch(cchFirst);
}
// copy into middle blocks
// FUTURE: (jonmat) I increased the size for how large a split block
// could be and this seems to increase the performance, we should test
// the block size difference on a retail build, however. 5/15/1995
ctbNew = cch / cchBlkInsertmGapI /* cchBlkInitmGapI */;
if(ctbNew <= 0 && cch > 0)
ctbNew = 1;
for(; ctbNew > 0; ctbNew--)
{
cchInBlock = cch / ctbNew;
AssertSz(cchInBlock > 0, "nothing to put into block");
// ***** moves _prgtb ***** //
if(!((CTxtArray *)_prgRun)->AddBlock(++_iRun,
CbOfCch(cchInBlock + cchGapInitial)))
{
_ped->GetCallMgr()->SetOutOfMemory();
BindToCp(_cp); //force a rebind;
goto done;
}
// NOTE: next line intentionally advances ptb to next CTxtBlk
ptb = GetRun(0);
AssertSz(ptb->_ibGap == 0, "New block not added correctly");
CopyMemory(ptb->_pch, pch, CbOfCch(cchInBlock));
cch -= cchInBlock;
pch += cchInBlock;
_ich = cchInBlock;
ptb->_cch = cchInBlock;
((CTxtArray *)_prgRun)->_cchText += cchInBlock;
ptb->_ibGap = CbOfCch(cchInBlock);
}
AssertSz(cch == 0, "Didn't use up all text");
// copy into last block (second half of split)
if(cchLast > 0)
{
AssertSz(_iRun < Count()-1, "no last block");
ptb = Elem(++_iRun);
AssertSz(ptb->_ibGap == 0, "split last gap in wrong place");
AssertSz(cchLast <= CchOfCb(ptb->_cbBlock) - ptb->_cch,
"split last not big enuf");
CopyMemory(ptb->_pch, pch, CbOfCch(cchLast));
// don't subtract cchLast from cch; it's already been done
_ich = cchLast;
ptb->_cch += cchLast;
((CTxtArray *)_prgRun)->_cchText += cchLast;
ptb->_ibGap = CbOfCch(cchLast);
cchLast = 0; // Inserted all requested chars
}
done:
AssertSz(cch + cchLast >= 0, "we should have inserted some characters");
AssertSz(cch + cchLast <= cchSave, "don't insert more than was asked for");
cch = cchSave - cch - cchLast; // # chars successfully inserted
_cp += cch;
AssertSz (GetTextLength() ==
((CTxtArray *)_prgRun)->GetCch(),
"CTxtPtr::InsertRange(): _prgRun->_cchText messed up !");
return cch;
}
/*
* TxDivideInsertion(cch, ichBlock, cchAfter, pcchFirst, pcchLast)
*
* @func
* Find best way to distribute an insertion
*
* @rdesc
* nothing
*/
LOCAL void TxDivideInsertion(
DWORD cch, //@parm length of text to insert
DWORD ichBlock, //@parm offset within block to insert text
DWORD cchAfter, //@parm length of text after insertion in block
DWORD *pcchFirst, //@parm exit: length of text to put in first block
DWORD *pcchLast) //@parm exit: length of text to put in last block
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "TxDivideInsertion");
DWORD cchFirst = max(0, (LONG)(cchBlkCombmGapI - ichBlock));
DWORD cchLast = max(0, (LONG)(cchBlkCombmGapI - cchAfter));
DWORD cchPartial;
DWORD cchT;
// Fill first and last blocks to min block size if possible
cchFirst = min(cch, cchFirst);
cch -= cchFirst;
cchLast = min(cch, cchLast);
cch -= cchLast;
// How much is left over when we divide up the rest?
cchPartial = cch % cchBlkInsertmGapI;
if(cchPartial > 0)
{
// Fit as much as the leftover as possible in the first and last
// w/o growing the first and last over cbBlockInitial
cchT = max(0, (LONG)(cchBlkInsertmGapI - ichBlock - cchFirst));
cchT = min(cchT, cchPartial);
cchFirst += cchT;
cch -= cchT;
cchPartial -= cchT;
if(cchPartial > 0)
{
cchT = max(0, (LONG)(cchBlkInsertmGapI - cchAfter - cchLast));
cchT = min(cchT, cchPartial);
cchLast += cchT;
}
}
*pcchFirst = cchFirst;
*pcchLast = cchLast;
}
/*
* CTxtPtr::DeleteRange(cch)
*
* @mfunc
* Delete cch characters starting at this text pointer
*
* @rdesc
* nothing
*
* @comm Side Effects: <nl>
* moves text block array
*/
void CTxtPtr::DeleteRange(
DWORD cch) //@parm length of text to delete
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtPtr::DeleteRange");
_TEST_INVARIANT_
DWORD cchInBlock;
DWORD ctbDel = 0; // Default no blocks to delete
DWORD itb;
CTxtBlk * ptb = GetRun(0);
AssertSz(ptb,
"CTxtPtr::DeleteRange: want to delete, but no text blocks");
if (cch > GetTextLength() - _cp) // Don't delete beyond end of story
cch = GetTextLength() - _cp;
((CTxtArray *)_prgRun)->_cchText -= cch;
// remove from first block
ptb->MoveGap(_ich);
cchInBlock = min(cch, ptb->_cch - _ich);
cch -= cchInBlock;
ptb->_cch -= cchInBlock;
#ifdef DEBUG
((CTxtArray *)_prgRun)->Invariant();
#endif // DEBUG
for(itb = ptb->_cch ? _iRun + 1 : _iRun;
cch && cch >= Elem(itb)->_cch; ctbDel++, itb++)
{
// More to go: scan for complete blocks to remove
cch -= Elem(itb)->_cch;
}
if(ctbDel)
{
// ***** moves (*_prgRun) ***** //
itb -= ctbDel;
((CTxtArray *)_prgRun)->RemoveBlocks(itb, ctbDel);
}
// remove from last block
if(cch > 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -