📄 doc.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
* @doc INTERNAL
*
* @module DOC.C CTxtStory and CTxtArray implementation |
*
* Original Authors: <nl>
* Original RichEdit code: David R. Fulmer <nl>
* Christian Fortini <nl>
* Murray Sargent <nl>
*
* History: <nl>
* 6/25/95 alexgo Cleanup and reorganization
*
*/
#include "_common.h"
#include "_doc.h"
#include "_format.h"
ASSERTDATA
// =========================== Invariant stuff ======================
#define DEBUG_CLASSNAME CTxtArray
#include "_invar.h"
// ======================== CTxtArray class =========================
#ifdef DEBUG
/*
* CTxtArray::Invariant
*
* @mfunc Tests CTxtArray's state
*
* @rdesc returns TRUE always; failures are indicated by Asserts
*/
BOOL CTxtArray::Invariant( void ) const
{
static LONG numTests = 0;
numTests++; // how many times we've been called.
if ( Count() > 0 )
{
// make sure total characters stored in the blocks match the length
// that is stored in _cchText.
DWORD cch = 0, i, iMax;
iMax = Count();
for ( i = 0; i < iMax; i++ )
{
CTxtBlk *ptb = Elem(i);
// ptb shouldn't be NULL since we're within Count elements
Assert(ptb);
DWORD currCch = ptb->_cch;
cch += currCch;
Assert ( currCch >= 0 );
Assert ( currCch <= CchOfCb(ptb->_cbBlock) );
// while we're here, check the range of the interblock gaps.
Assert (ptb->_ibGap >= 0);
Assert (ptb->_ibGap <= ptb->_cbBlock);
DWORD cchGap = CchOfCb(ptb->_ibGap);
Assert ( cchGap >= 0 );
Assert ( cchGap <= currCch );
}
Assert ( cch == GetCch() );
}
return TRUE;
}
#endif // DEBUG
/*
* CTxtArray::CTxtArray
*
* @mfunc Text array constructor
*
*/
CTxtArray::CTxtArray() : CArray<CTxtBlk> ()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::CTxtArray()");
AssertSz(CchOfCb(cbBlockMost) - cchGapInitial >= cchBlkInitmGapI * 2,
"cchBlockMax - cchGapInitial must be at least (cchBlockInitial - cchGapInitial) * 2");
_cchText = 0;
// make sure we have no data to initialize
Assert(sizeof(CTxtArray) == (sizeof(CArray<CTxtBlk>)+ sizeof(_cchText)));
}
/*
* CTxtArray::~CTxtArray
*
* @mfunc Text array destructor
*/
CTxtArray::~CTxtArray()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::~CTxtArray");
DWORD itb = Count();
while(itb--)
{
Assert(Elem(itb) != NULL);
Elem(itb)->FreeBlock();
}
}
/*
* CTxtArray::GetCch()
*
* @mfunc Computes and return length of text in this text array
*
* @rdesc The number of character in this text array
*
* @devnote This call may be computationally expensive; we have to
* sum up the character sizes of all of the text blocks in
* the array.
*/
DWORD CTxtArray::GetCch() const
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::GetCch");
_TEST_INVARIANT_
DWORD itb = Count();
DWORD cch = 0;
while(itb--)
{
Assert(Elem(itb) != NULL);
cch += Elem(itb)->_cch;
}
return cch;
}
/*
* CTxtArray::AddBlock(itbNew, cb)
*
* @mfunc create new text block
*
* @rdesc
* FALSE if block could not be added
* non-FALSE otherwise
*
* @comm
* Side Effects:
* moves text block array
*/
BOOL CTxtArray::AddBlock(
DWORD itbNew, //@parm index of the new block
LONG cb) //@parm size of new block; if <lt>= 0, default is used
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::AddBlock");
_TEST_INVARIANT_
CTxtBlk *ptb;
if(cb <= 0)
cb = cbBlockInitial;
AssertSz(cb > 0, "CTxtArray::AddBlock() - adding block of size zero");
AssertSz(cb <= cbBlockMost, "CTxtArray::AddBlock() - block too big");
ptb = Insert(itbNew, 1);
if( !ptb || !ptb->InitBlock(cb))
{
TRACEERRSZSC("TXTARRAT::AddBlock() - unable to allocate new block", E_OUTOFMEMORY);
// GuyBark Jupiter 40329:
// The call to Insert() above added another entry into the text block array. The subsequent call InitBlock() will
// update its _cbBlock saying how big the block is, and its _pch pointer to the space for the text itself.
// However in the event of oom, we end up here with the pointer NULL, but _cbBlock still set. When I
// forced this to happen a couple of times on the desktop, RichEdit would later crash or hang. So tidy
// up the text block array by removing the now-useless block we just added.
RemoveBlocks(itbNew, 1);
return FALSE;
}
return TRUE;
}
/*
* CTxtArray::SplitBlock(itb, ichSplit, cchFirst, cchLast, fStreaming)
*
* @mfunc split a text block into two
*
* @rdesc
* FALSE if the block could not be split <nl>
* non-FALSE otherwise
*
* @comm
* Side Effects: <nl>
* moves text block array
*/
BOOL CTxtArray::SplitBlock(
DWORD itb, //@parm index of the block to split
DWORD ichSplit, //@parm character index within block at which to split
DWORD cchFirst, //@parm desired extra space in first block
DWORD cchLast, //@parm desired extra space in new block
BOOL fStreaming) //@parm TRUE if streaming in new text
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::SplitBlock");
_TEST_INVARIANT_
LPBYTE pbSrc;
LPBYTE pbDst;
CTxtBlk *ptb, *ptb1;
AssertSz(ichSplit > 0 || cchFirst > 0, "CTxtArray::SplitBlock(): splitting at beginning, but not adding anything");
AssertSz(itb >= 0, "CTxtArray::SplitBlock(): negative itb");
ptb = Elem(itb);
// compute size for first half
AssertSz(cchFirst + ichSplit <= CchOfCb(cbBlockMost),
"CTxtArray::SplitBlock(): first size too large");
cchFirst += ichSplit + cchGapInitial;
cchFirst = min(cchFirst, CchOfCb(cbBlockMost));
// compute size for second half
AssertSz(cchLast + ptb->_cch - ichSplit <= CchOfCb(cbBlockMost),
"CTxtArray::SplitBlock(): second size too large");
cchLast += ptb->_cch - ichSplit + cchGapInitial;
cchLast = min(cchLast, CchOfCb(cbBlockMost));
// allocate second block and move text to it
// ***** moves rgtb ***** //
// if streaming in, allocate a block that's as big as possible so that
// subsequent additions of text are faster
// we always fall back to smaller allocations so this won't cause
// unneccesary errors
// when we're done streaming we compress blocks, so this won't leave
// a big empty gap
if(fStreaming)
{
DWORD cb = cbBlockMost;
const DWORD cbMin = CbOfCch(cchLast);
while(cb >= cbMin && !AddBlock(itb + 1, cb))
cb -= cbBlockCombine;
if(cb >= cbMin)
goto got_block;
}
if(!AddBlock(itb + 1, CbOfCch(cchLast)))
{
TRACEERRSZSC("CTxtArray::SplitBlock(): unabled to add new block", E_FAIL);
return FALSE;
}
got_block:
ptb1 = Elem(itb+1); // recompute ptb after rgtb moves
ptb = Elem(itb); // recompute ptb after rgtb moves
ptb1->_cch = ptb->_cch - ichSplit;
ptb1->_ibGap = 0;
pbDst = (LPBYTE) (ptb1->_pch - ptb1->_cch) + ptb1->_cbBlock;
ptb->MoveGap(ptb->_cch); // make sure pch points to a continuous block of all text in ptb.
pbSrc = (LPBYTE) (ptb->_pch + ichSplit);
CopyMemory(pbDst, pbSrc, CbOfCch(ptb1->_cch));
ptb->_cch = ichSplit;
ptb->_ibGap = CbOfCch(ichSplit);
// resize the first block
if(CbOfCch(cchFirst) != ptb->_cbBlock)
{
//$ FUTURE: don't resize unless growing or shrinking considerably
if(!ptb->ResizeBlock(CbOfCch(cchFirst)))
{
TRACEERRSZSC("TXTARRA::SplitBlock(): unabled to resize block", E_OUTOFMEMORY);
return FALSE;
}
}
return TRUE;
}
/*
* CTxtArray::ShrinkBlocks()
*
* @mfunc Shrink all blocks to their minimal size
*
* @rdesc
* nothing
*
*/
void CTxtArray::ShrinkBlocks()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::ShrinkBlocks");
_TEST_INVARIANT_
DWORD itb = Count();
CTxtBlk *ptb;
while(itb--)
{
ptb = Elem(itb);
Assert(ptb);
ptb->ResizeBlock(CbOfCch(ptb->_cch));
}
}
/*
* CTxtArray::RemoveBlocks(itbFirst, ctbDel)
*
* @mfunc remove a range of text blocks
*
* @rdesc
* nothing
*
* @comm Side Effects: <nl>
* moves text block array
*/
VOID CTxtArray::RemoveBlocks(
DWORD itbFirst, //@parm index of first block to remove
DWORD ctbDel) //@parm number of blocks to remove
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTxtArray::RemoveBlocks");
_TEST_INVARIANT_
DWORD itb = itbFirst;
DWORD ctb = ctbDel;
AssertSz(itb + ctb <= Count(), "CTxtArray::RemoveBlocks(): not enough blocks");
while(ctb--)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -