1480 lines
36 KiB
C++
1480 lines
36 KiB
C++
|
/*
|
||
|
* @doc INTERNAL
|
||
|
*
|
||
|
* @module FRUNPTR.C -- FormatRunPtr methods |
|
||
|
*
|
||
|
* common code to handle character and paragraph format runs
|
||
|
*
|
||
|
* Original Authors: <nl>
|
||
|
* Original RichEdit 1.0 code: David R. Fulmer <nl>
|
||
|
* Christian Fortini <nl>
|
||
|
* Murray Sargent <nl>
|
||
|
*
|
||
|
* History:
|
||
|
* 6/25/95 alexgo convert to use Auto-Doc and simplified backing
|
||
|
* store model
|
||
|
*
|
||
|
* @devnote
|
||
|
* BOR and EOR mean Beginning Of Run and End Of Run, respectively
|
||
|
*
|
||
|
* Copyright (c) 1995-1999, Microsoft Corporation. All rights reserved.
|
||
|
*/
|
||
|
|
||
|
#include "_common.h"
|
||
|
#include "_edit.h"
|
||
|
#include "_frunptr.h"
|
||
|
#include "_rtext.h"
|
||
|
#include "_font.h"
|
||
|
|
||
|
ASSERTDATA
|
||
|
|
||
|
//
|
||
|
// Invariant stuff
|
||
|
//
|
||
|
#define DEBUG_CLASSNAME CFormatRunPtr
|
||
|
|
||
|
#include "_invar.h"
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/*
|
||
|
* CFormatRunPtr::Invariant
|
||
|
*
|
||
|
* @mfunc Invariant for format run pointers
|
||
|
*
|
||
|
* @rdesc BOOL
|
||
|
*/
|
||
|
BOOL CFormatRunPtr::Invariant() const
|
||
|
{
|
||
|
if(IsValid())
|
||
|
{
|
||
|
CFormatRun *prun = GetRun(0);
|
||
|
if(prun && _iRun)
|
||
|
{
|
||
|
Assert(prun->_cch > 0);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Assert(_ich == 0);
|
||
|
}
|
||
|
return CRunPtrBase::Invariant();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::InitRuns(ich, cch, iFormat, ppfrs)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Setup this format run ptr for rich-text operation, namely,
|
||
|
* allocate CArray<lt>CFormatRun<gt> if not allocated, assign it to this
|
||
|
* run ptr's _pRuns, add initial run if no runs are present, and store
|
||
|
* initial cch and ich
|
||
|
*
|
||
|
* @rdesc
|
||
|
* TRUE if succeeds
|
||
|
*/
|
||
|
BOOL CFormatRunPtr::InitRuns(
|
||
|
LONG ich, //@parm # chars in initial run
|
||
|
LONG cch, //@parm char offset in initial run
|
||
|
CFormatRuns **ppfrs) //@parm ptr to CFormatRuns ptr
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::InitRuns");
|
||
|
|
||
|
_TEST_INVARIANT_
|
||
|
|
||
|
AssertSz( ppfrs,
|
||
|
"FRP::InitRuns: illegal ptr to runs");
|
||
|
AssertSz( !IsValid(),
|
||
|
"FRP::InitRuns: ptr already valid");
|
||
|
|
||
|
if(!*ppfrs) // Allocate format runs
|
||
|
{
|
||
|
_pRuns = (CRunArray *) new CFormatRuns();
|
||
|
if(!_pRuns)
|
||
|
goto NoRAM;
|
||
|
*ppfrs = (CFormatRuns *)_pRuns;
|
||
|
}
|
||
|
else // Format runs already alloc'd
|
||
|
_pRuns = (CRunArray *)*ppfrs; // Cache ptr to runs
|
||
|
|
||
|
if(!Count()) // No runs yet, so add one
|
||
|
{
|
||
|
CFormatRun *pRun= Add(1, NULL);
|
||
|
if(!pRun)
|
||
|
goto NoRAM;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
PvSet(*(void**)_pRuns);
|
||
|
#endif
|
||
|
_ich = ich;
|
||
|
|
||
|
ZeroMemory(pRun, sizeof(*pRun));
|
||
|
pRun->_cch = cch; // Define its _cch
|
||
|
pRun->_iFormat = -1; // and _iFormat
|
||
|
}
|
||
|
else
|
||
|
BindToCp(ich); // Format runs are in place
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
NoRAM:
|
||
|
TRACEERRSZSC("CFormatRunPtr::InitRuns: Out Of RAM", E_OUTOFMEMORY);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::Delete(cch, pf, cchMove)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Delete/modify runs starting at this run ptr up to cch chars. <nl>
|
||
|
* There are 7 possibilities: <nl>
|
||
|
* 1. cch comes out of this run with count left over, i.e.,
|
||
|
* cch <lt>= (*this)->_cch - _ich && (*this)->_cch > cch
|
||
|
* (simple: no runs deleted/merged, just subtract cch) <nl>
|
||
|
* 2. cch comes out of this run and empties run and doc
|
||
|
* (simple: no runs left to delete/merge) <nl>
|
||
|
* 3. cch comes out of this run and empties run, which is last
|
||
|
* (need to delete run, no merge possibility) <nl>
|
||
|
* 4. cch comes out of this run and empties run, which is first
|
||
|
* (need to delete run, no merge possibility) <nl>
|
||
|
* 5. cch exceeds count available in this run and this run is last
|
||
|
* (simple: treat as 3.) <nl>
|
||
|
* 6. cch comes out of this run and empties run with runs before
|
||
|
* and after (need to delete run; merge possibility) <nl>
|
||
|
* 7. cch comes partly out of this run and partly out of later run(s)
|
||
|
* (may need to delete and merge) <nl>
|
||
|
*
|
||
|
* @comm
|
||
|
* PARAFORMATs have two special cases that use the cchMove argument set
|
||
|
* up in CRchTxtPtr::ReplaceRange().
|
||
|
*/
|
||
|
void CFormatRunPtr::Delete(
|
||
|
LONG cch, //@parm # chars to modify format runs for
|
||
|
IFormatCache *pf, //@parm IFormatCache ptr for ReleaseFormat
|
||
|
LONG cchMove) //@parm cch to move between runs (always 0 for CF)
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::Delete");
|
||
|
|
||
|
_TEST_INVARIANT_
|
||
|
|
||
|
// We should not have any boundary cases for empty or NULL pointers.
|
||
|
// (i.e. if there's no text, then nobody should be calling delete).
|
||
|
|
||
|
Assert(IsValid());
|
||
|
|
||
|
LONG cchEnd = 0; // Probably unnecessary: see below
|
||
|
LONG cRun = 1;
|
||
|
BOOL fLast = (_iRun == Count() - 1);
|
||
|
LONG ifmtEnd, ifmtStart;
|
||
|
CFormatRun * pRun = Elem(_iRun);
|
||
|
CFormatRun * pRunRp;
|
||
|
LONG cchChunk = pRun->_cch - _ich;
|
||
|
CFormatRunPtr rp(*this); // Clone this run ptr
|
||
|
CBiDiLevel levelStart = {0,0};
|
||
|
CBiDiLevel levelEnd = {0,0};
|
||
|
|
||
|
rp.AdjustBackward(); // If at BOR, move to prev EOR
|
||
|
ifmtStart = rp.GetRun(0)->_iFormat; // to get start format
|
||
|
levelStart = rp.GetRun(0)->_level; // and level
|
||
|
rp = *this; // In case RpAdjustCp() backed up
|
||
|
|
||
|
// Process deletes confined to this run first, since their logic tends to
|
||
|
// clutter up other cases
|
||
|
|
||
|
AssertSz(cch >= 0, "FRP::Delete: cch < 0");
|
||
|
|
||
|
if(fLast) // Handle oversized cch on last
|
||
|
cch = min(cch, cchChunk); // run here
|
||
|
|
||
|
if(cch <= cchChunk) // cch comes out of this run
|
||
|
{
|
||
|
pRun->_cch -= cch;
|
||
|
Assert(pRun->_cch >= 0);
|
||
|
if(cchMove) // If nonzero here, we are
|
||
|
{ // deleting EOP at end of run
|
||
|
rp.AdjustForward(); // Adjust rp to beginning of
|
||
|
goto move; // next run and go move cchMove
|
||
|
} // chars back into this run
|
||
|
if(pRun->_cch) // Something left in run: done
|
||
|
return;
|
||
|
// Note: _ich = 0
|
||
|
if(!_iRun || fLast) // This run is either first
|
||
|
{ // or last
|
||
|
AdjustBackward(); // If last, go to prev EOR
|
||
|
if(_ich) // This run is empty so delete
|
||
|
cRun++; // Compensate for cRun-- coming up
|
||
|
ifmtStart = -2; // No runs eligible for merging
|
||
|
} // so use unmatchable ifmtStart
|
||
|
rp.NextRun(); // Set up to get next _iFormat
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rp.Move(cch); // Move clone to end of delete
|
||
|
pRunRp = rp.GetRun(0);
|
||
|
cRun = rp._iRun - _iRun // If at EOR, then need to add
|
||
|
+ (rp._ich == pRunRp->_cch); // one more run to delete
|
||
|
pRun->_cch = _ich; // Shorten this run to _ich chars
|
||
|
pRunRp->_cch -= rp._ich; // Shorten last run by rp._ich
|
||
|
rp._ich = 0;
|
||
|
|
||
|
Assert(pRunRp->_cch >= 0);
|
||
|
AssertSz(cRun > 0, "FRP: bogus runptr");
|
||
|
|
||
|
if(!_iRun) // First run?
|
||
|
ifmtStart = -2; // Then we cannot merge runs so
|
||
|
} // set to unmergable format
|
||
|
|
||
|
ifmtEnd = -3; // Default invalid format at end
|
||
|
if(rp.IsValid())
|
||
|
{
|
||
|
// FUTURE (murrays): probably rp is always valid here now and
|
||
|
// pRun->_cch is nonzero
|
||
|
pRun = rp.GetRun(0);
|
||
|
if (pRun->_cch) // run not empty
|
||
|
{
|
||
|
ifmtEnd = pRun->_iFormat; // Remember end format and count
|
||
|
levelEnd = pRun->_level;
|
||
|
cchEnd = pRun->_cch; // in case of merge
|
||
|
}
|
||
|
else if(rp._iRun != rp.Count() - 1) // run not last
|
||
|
{
|
||
|
pRun = rp.GetRun(1);
|
||
|
ifmtEnd = pRun->_iFormat; // Remember end format and count
|
||
|
levelEnd = pRun->_level;
|
||
|
cchEnd = pRun->_cch; // in case of merge
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rp = *this; // Default to delete this run
|
||
|
if(_ich) // There are chars in this run
|
||
|
{
|
||
|
if(cchMove + _ich == 0) // Need to combine all chars of
|
||
|
{ // this run with run after del,
|
||
|
pf->AddRef(ifmtEnd); // so setup merge below using
|
||
|
ifmtStart = ifmtEnd; // ifmtEnd. This run then takes
|
||
|
pf->Release(GetRun(0)->_iFormat);
|
||
|
GetRun(0)->_iFormat = ifmtEnd; // place of run after del.
|
||
|
GetRun(0)->_level = levelEnd;
|
||
|
cchMove = 0; // cchMove all accounted for
|
||
|
}
|
||
|
rp.NextRun(); // Don't delete this run; start
|
||
|
cRun--; // with next one
|
||
|
}
|
||
|
|
||
|
AdjustBackward(); // If !_ich, go to prev EOR
|
||
|
|
||
|
if(ifmtEnd >=0 && // Same formats: merge runs
|
||
|
ifmtEnd == ifmtStart &&
|
||
|
levelStart == levelEnd)
|
||
|
{
|
||
|
GetRun(0)->_cch += cchEnd; // Add last-run cch to this one's
|
||
|
Assert(GetRun(0)->_cch >= 0);
|
||
|
cRun++; // Setup to eat last run
|
||
|
}
|
||
|
|
||
|
if(cRun > 0) // There are run(s) to delete
|
||
|
{
|
||
|
rp.Remove(cRun, pf);
|
||
|
if(!Count()) // If no more runs, keep this rp
|
||
|
_ich = _iRun = 0; // valid by pointing at cp = 0
|
||
|
}
|
||
|
|
||
|
move:
|
||
|
if(cchMove) // Need to move some cch between
|
||
|
{ // this run and next (See
|
||
|
GetRun(0)->_cch += cchMove; // CRchTxtPtr::ReplaceRange())
|
||
|
rp.GetRun(0)->_cch -= cchMove;
|
||
|
|
||
|
Assert(GetRun(0)->_cch >= 0);
|
||
|
Assert(rp.GetRun(0)->_cch >= 0);
|
||
|
Assert(_iRun < rp._iRun);
|
||
|
|
||
|
if(!rp.GetRun(0)->_cch) // If all chars moved out of rp's
|
||
|
rp.Remove(1, pf); // run, delete it
|
||
|
|
||
|
if(cchMove < 0) // Moved -cchMove chars from this
|
||
|
{ // run to next
|
||
|
if(!GetRun(0)->_cch)
|
||
|
Remove(1, pf);
|
||
|
else
|
||
|
_iRun++; // Keep this run ptr in sync with
|
||
|
|
||
|
_ich = -cchMove; // cp (can't use NextRun() due
|
||
|
} // to Invariants)
|
||
|
}
|
||
|
AdjustForward(); // Don't leave ptr at EOR unless
|
||
|
} // there are no more runs
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::InsertFormat(cch, ifmt, pf)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Insert cch chars with format ifmt into format runs starting at
|
||
|
* this run ptr
|
||
|
*
|
||
|
* @rdesc
|
||
|
* count of characters added
|
||
|
*
|
||
|
* @devnote
|
||
|
* It is the caller's responsibility to ensure that we are in the
|
||
|
* "normal" or "empty" state. A format run pointer doesn't know about
|
||
|
* CTxtStory, so it can't create the run array without outside help.
|
||
|
*/
|
||
|
LONG CFormatRunPtr::InsertFormat(
|
||
|
LONG cch, //@parm # chars to insert
|
||
|
LONG ifmt, //@parm format to use
|
||
|
IFormatCache *pf) //@parm pointer to IFormatCache to AddRefFormat
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::InsertFormat");
|
||
|
|
||
|
LONG cRun;
|
||
|
CFormatRun *pRun;
|
||
|
CFormatRun *pRunPrev;
|
||
|
LONG cchRun; // Current-run length,
|
||
|
LONG ich; // offset, and
|
||
|
LONG iFormat; // format
|
||
|
|
||
|
_TEST_INVARIANT_
|
||
|
|
||
|
Assert(_pRuns);
|
||
|
if(!IsValid())
|
||
|
{
|
||
|
// Empty run case (occurs when inserting after all text is deleted)
|
||
|
pRun = Add(1, NULL);
|
||
|
goto StoreNewRunData; // (located at end of function)
|
||
|
}
|
||
|
|
||
|
// Go to previous run if at a boundary case
|
||
|
AdjustBackward();
|
||
|
pRun = Elem(_iRun); // Try other cases
|
||
|
cchRun = pRun->_cch;
|
||
|
iFormat = pRun->_iFormat;
|
||
|
ich = _ich;
|
||
|
|
||
|
// Same run case. Note that there is an additional boundary case; if we
|
||
|
// are the _end_ of one run, then the next run may have the necessary
|
||
|
// format.
|
||
|
if(ifmt == iFormat) // IP already has correct fmt
|
||
|
{
|
||
|
pRun->_cch += cch;
|
||
|
_ich += cch; // Inc offset to keep in sync
|
||
|
return cch;
|
||
|
}
|
||
|
if(_ich == pRun->_cch && _iRun < _pRuns->Count() - 1)
|
||
|
{
|
||
|
AdjustForward();
|
||
|
pRun = Elem(_iRun);
|
||
|
|
||
|
Assert(pRun);
|
||
|
|
||
|
if(pRun->_iFormat == ifmt)
|
||
|
{
|
||
|
pRun->_cch += cch;
|
||
|
_ich += cch;
|
||
|
return cch;
|
||
|
}
|
||
|
AdjustBackward();
|
||
|
}
|
||
|
|
||
|
// Prior run case (needed when formatting change occurs on line break
|
||
|
// and caret is at beginning of new line)
|
||
|
if(!ich && _iRun > 0 ) // IP at start of run
|
||
|
{
|
||
|
pRunPrev = GetPtr(pRun, -1);
|
||
|
if( ifmt == pRunPrev->_iFormat) // Prev run has same format:
|
||
|
{ // add count to prev run and
|
||
|
pRunPrev->_cch += cch;
|
||
|
return cch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create new run[s] cases. There is a special case for a format
|
||
|
// run of zero length: just re-use it.
|
||
|
if(!pRun->_cch)
|
||
|
{
|
||
|
// This assert has been toned down to ignore a plain text control
|
||
|
// being forced into IME Rich Composition.
|
||
|
AssertSz( /* FALSE */ pRun->_iFormat == -1 && Count() == 1,
|
||
|
"CFormatRunPtr::InsertFormat: 0-length run");
|
||
|
pf->Release(pRun->_iFormat);
|
||
|
}
|
||
|
else // Need to create 1 or 2 new
|
||
|
{ // runs for insertion
|
||
|
cRun = 1; // Default 1 new run
|
||
|
if(ich && ich < cchRun) // Not at beginning or end of
|
||
|
cRun++; // run, so need two new runs
|
||
|
|
||
|
// The following insert call adds one or two runs at the current
|
||
|
// position. If the new run is inserted at the beginning or end
|
||
|
// of the current run, the latter needs no change; however, if
|
||
|
// the new run splits the current run in two, both pieces have
|
||
|
// to be updated (cRun == 2 case).
|
||
|
|
||
|
pRun = Insert(cRun); // Insert cRun run(s)
|
||
|
if(!pRun) // Out of RAM. Can't insert
|
||
|
{ // new format, but can keep
|
||
|
_ich += cch; // run ptr and format runs
|
||
|
GetRun(0)->_cch += cch; // valid. Note: doesn't
|
||
|
return cch; // signal any error; no access
|
||
|
} // to _ped->_fErrSpace
|
||
|
|
||
|
if(ich) // Not at beginning of run,
|
||
|
{
|
||
|
pRunPrev = pRun; // Previous run is current run
|
||
|
IncPtr(pRun); // New run is next run
|
||
|
VALIDATE_PTR(pRun);
|
||
|
pRun->_cch = cch; // Keep NextRun() invariant happy
|
||
|
NextRun(); // Point this runptr at it too
|
||
|
if(cRun == 2) // Are splitting current run
|
||
|
{ // _iFormat's are already set
|
||
|
AssertSz(pRunPrev->_iFormat == iFormat,
|
||
|
"CFormatRunPtr::InsertFormat: bad format inserted");
|
||
|
pRunPrev->_cch = ich; // Divide up original cch
|
||
|
GetPtr(pRun, 1)->_cch // accordingly
|
||
|
= cchRun - ich;
|
||
|
pf->AddRef(iFormat); // Addref iFormat for extra run
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
StoreNewRunData:
|
||
|
pf->AddRef(ifmt); // Addref ifmt
|
||
|
ZeroMemory(pRun, sizeof(*pRun));
|
||
|
pRun->_iFormat = ifmt; // Store insert format and count
|
||
|
pRun->_cch = cch; // of new run
|
||
|
_ich = cch; // cp goes at end of insertion
|
||
|
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::MergeRuns(iRun, pf)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Merge adjacent runs that have the same format between this run
|
||
|
* <md CFormatRunPtr::_iRun> and that for <p iRun>
|
||
|
*
|
||
|
* @comm
|
||
|
* Changes this run ptr
|
||
|
*/
|
||
|
void CFormatRunPtr::MergeRuns(
|
||
|
LONG iRun, //@parm last run to check (can preceed or follow
|
||
|
// <md CFormatRunPtr::_iRun>)
|
||
|
IFormatCache *pf) //@parm pointer to IFormatCache to ReleaseFormat
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::MergeRuns");
|
||
|
|
||
|
LONG cch;
|
||
|
LONG cRuns = iRun - _iRun;
|
||
|
LONG iDirection = 1; // Default going forward
|
||
|
CFormatRun *pRun;
|
||
|
|
||
|
_TEST_INVARIANT_
|
||
|
|
||
|
if(cRuns < 0)
|
||
|
{
|
||
|
cRuns = -cRuns;
|
||
|
iDirection = -1;
|
||
|
}
|
||
|
if(!IsValid()) // Allow starting run to be
|
||
|
{ // invalid
|
||
|
Assert(FALSE); // I think this is old...
|
||
|
ChgRun(iDirection);
|
||
|
}
|
||
|
|
||
|
while(cRuns--)
|
||
|
{
|
||
|
if(!GetRun(0)->_cch && !_iRun && _iRun < Count() - 1)
|
||
|
{
|
||
|
if(iDirection > 0)
|
||
|
PrevRun();
|
||
|
Remove(1, pf);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
pRun = GetRun(0); // Save the current run
|
||
|
|
||
|
if(!ChgRun(iDirection)) // Go to next (or prev) run
|
||
|
return; // No more runs to check
|
||
|
|
||
|
if(pRun->SameFormat(GetRun(0)))
|
||
|
{ // Like formatted runs
|
||
|
if(iDirection > 0) // Point at the first of the
|
||
|
PrevRun(); // two runs
|
||
|
cch = GetRun(0)->_cch; // Save its count
|
||
|
Remove(1, pf); // Remove it
|
||
|
GetRun(0)->_cch += cch; // Add its count to the other's,
|
||
|
} // i.e., they're merged
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::Remove(cRun, flag, pf)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Remove cRun runs starting at _iRun
|
||
|
*/
|
||
|
void CFormatRunPtr::Remove(
|
||
|
LONG cRun,
|
||
|
IFormatCache *pf)
|
||
|
{
|
||
|
CFormatRun *pRun = GetRun(0); // Point at run(s) to delete
|
||
|
|
||
|
for(LONG j = 0; j < cRun; j++, IncPtr(pRun))
|
||
|
pf->Release(pRun->_iFormat); // Decrement run reference count
|
||
|
|
||
|
CRunPtr<CFormatRun>::Remove(cRun);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::SetFormat(ifmt, cch, pf, pLevel)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Set format for up to cch chars of this run to ifmt, splitting run
|
||
|
* as needed, and returning the character count actually processed
|
||
|
*
|
||
|
* @rdesc
|
||
|
* character count of run chunk processed, CP_INFINITE on failure
|
||
|
* this points at next run
|
||
|
*
|
||
|
* Comments:
|
||
|
* Changes this run ptr. cch must be >= 0.
|
||
|
*
|
||
|
* Note 1) for the first run in a series, _ich may not = 0, and 2) cch
|
||
|
* may be <lt>, =, or <gt> the count remaining in the run. The algorithm
|
||
|
* doesn't split runs when the format doesn't change.
|
||
|
*/
|
||
|
LONG CFormatRunPtr::SetFormat(
|
||
|
LONG ifmt, //@parm format index to use
|
||
|
LONG cch, //@parm character count of remaining format range
|
||
|
IFormatCache * pf, //@parm pointer to IFormatCache to
|
||
|
CBiDiLevel* pLevel) //@parm pointer to BiDi level structure
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::SetFormat");
|
||
|
// AddRefFormat/ReleaseFormat
|
||
|
LONG cchChunk;
|
||
|
LONG iFormat;
|
||
|
CFormatRun * pRun;
|
||
|
CFormatRun * pChgRun; // run that was reformatted
|
||
|
CBiDiLevel level;
|
||
|
|
||
|
_TEST_INVARIANT_
|
||
|
|
||
|
if(!IsValid())
|
||
|
return 0;
|
||
|
|
||
|
pRun = GetRun(0); // pRun points at current run in
|
||
|
cchChunk = pRun->_cch - _ich; // this function
|
||
|
iFormat = pRun->_iFormat;
|
||
|
level = pRun->_level;
|
||
|
pChgRun = pRun;
|
||
|
|
||
|
AssertSz(cch, "Have to have characters to format!");
|
||
|
AssertSz(pRun->_cch, "uh-oh, empty format run detected");
|
||
|
|
||
|
if(ifmt != iFormat || (pLevel && level != *pLevel)) // New and current formats differ
|
||
|
{
|
||
|
AssertSz(cchChunk, "Caller did not call AdjustForward");
|
||
|
|
||
|
if(_ich) // Not at either end of run: need
|
||
|
{ // to split into two runs of
|
||
|
if(!(pRun = Insert(1))) // counts _ich and _pRun->_cch
|
||
|
{ // - _ich, respectively
|
||
|
return CP_INFINITE; // Out of RAM: do nothing; just
|
||
|
} // keep current format
|
||
|
pRun->_cch = _ich;
|
||
|
pRun->_iFormat = iFormat; // New run has same format
|
||
|
pRun->_level = level; // and same level
|
||
|
pf->AddRef(iFormat); // Increment format ref count
|
||
|
NextRun(); // Go to second (original) run
|
||
|
IncPtr(pRun); // Point pRun at current run
|
||
|
pRun->_cch = cchChunk; // Note: IncPtr is a bit more
|
||
|
pChgRun = pRun;
|
||
|
} // efficient than GetRun, but
|
||
|
// trickier to code right
|
||
|
if(cch < cchChunk) // cch doesn't cover whole run:
|
||
|
{ // need to split into two runs
|
||
|
if(!(pRun = Insert(1)))
|
||
|
{
|
||
|
// Out of RAM, so formatting's wrong, oh well. We actually
|
||
|
// "processed" all of the characters, so return that (though
|
||
|
// the tail end formatting isn't split out right)
|
||
|
return cch;
|
||
|
}
|
||
|
pRun->_cch = cch; // New run gets the cch
|
||
|
pRun->_iFormat = ifmt; // and the new format
|
||
|
pChgRun = pRun;
|
||
|
IncPtr(pRun); // Point pRun at current run
|
||
|
pRun->_cch = cchChunk - cch; // Set leftover count
|
||
|
}
|
||
|
else // cch as big or bigger than
|
||
|
{ // current run
|
||
|
pf->Release(iFormat); // Free run's current format
|
||
|
pRun->_iFormat = ifmt; // Change it to new format
|
||
|
pChgRun = pRun;
|
||
|
} // May get merged later
|
||
|
pf->AddRef(ifmt); // Increment new format ref count
|
||
|
}
|
||
|
else if(!cchChunk)
|
||
|
{
|
||
|
pRun->_cch += cch; // Add cch to end of current run
|
||
|
cchChunk = cch; // Report that all cch are done
|
||
|
IncPtr(pRun);
|
||
|
pRun->_cch -= cch; // Remove count from next run
|
||
|
if(!pRun->_cch) // Next run is now empty, so
|
||
|
{ // remove it
|
||
|
_iRun++;
|
||
|
Remove(1, pf);
|
||
|
_iRun--; // Backup to start run
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Record embedding level to changed run
|
||
|
if (pLevel)
|
||
|
pChgRun->_level = *pLevel;
|
||
|
|
||
|
cch = min(cch, cchChunk);
|
||
|
Move(cch);
|
||
|
AdjustForward();
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::GetFormat()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* return format index at current run pointer position
|
||
|
*
|
||
|
* @rdesc
|
||
|
* current format index
|
||
|
*/
|
||
|
short CFormatRunPtr::GetFormat() const
|
||
|
{
|
||
|
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFormatRunPtr::GetFormat");
|
||
|
_TEST_INVARIANT_
|
||
|
|
||
|
return IsValid() ? GetRun(0)->_iFormat : -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::SplitFormat(IFormatCache*)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Split a format run
|
||
|
*
|
||
|
* @rdesc
|
||
|
* If succeeded the run pointer moves to the next splitted run
|
||
|
*/
|
||
|
void CFormatRunPtr::SplitFormat(IFormatCache* pf)
|
||
|
{
|
||
|
if (!_ich || _ich == GetRun(0)->_cch)
|
||
|
return;
|
||
|
|
||
|
CFormatRun* pRun = GetRun(0);
|
||
|
LONG iFormat = pRun->_iFormat;
|
||
|
LONG cch = pRun->_cch - _ich;
|
||
|
CBiDiLevel level = pRun->_level;
|
||
|
|
||
|
if (pRun = Insert(1))
|
||
|
{
|
||
|
pRun->_cch = _ich;
|
||
|
pRun->_iFormat = iFormat;
|
||
|
pRun->_level = level;
|
||
|
pf->AddRef(iFormat);
|
||
|
NextRun();
|
||
|
IncPtr(pRun);
|
||
|
pRun->_cch = cch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::SetLevel(level)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Set run's embedding level
|
||
|
*/
|
||
|
void CFormatRunPtr::SetLevel (CBiDiLevel& level)
|
||
|
{
|
||
|
if (!IsValid())
|
||
|
{
|
||
|
Assert(FALSE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CFormatRun* pRun = GetRun(0);
|
||
|
|
||
|
if (pRun)
|
||
|
pRun->_level = level;
|
||
|
}
|
||
|
|
||
|
BYTE CFormatRunPtr::GetLevel (CBiDiLevel* pLevel)
|
||
|
{
|
||
|
CFormatRun* pRun;
|
||
|
|
||
|
|
||
|
if (!IsValid() || !(pRun = GetRun(0)))
|
||
|
{
|
||
|
Assert(FALSE);
|
||
|
|
||
|
if (pLevel)
|
||
|
{
|
||
|
pLevel->_value = 0;
|
||
|
pLevel->_fStart = FALSE;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (pLevel)
|
||
|
*pLevel = pRun->_level;
|
||
|
|
||
|
return pRun->_level._value;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CFormatRunPtr::AdjustFormatting(cch, pf)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Use the same format index for the cch chars at this run ptr
|
||
|
* as that immediately preceeding it (if on run edge).
|
||
|
*
|
||
|
* @devnote
|
||
|
* This runptr ends up pointing at what was the preceeding run,
|
||
|
* since the current run has been moved into the preceeding run.
|
||
|
*
|
||
|
* FUTURE: might be better to take the cch equal to chars in
|
||
|
* the following run.
|
||
|
*/
|
||
|
void CFormatRunPtr::AdjustFormatting(
|
||
|
LONG cch, //@parm Count of chars to extend formatting
|
||
|
IFormatCache *pf) //@parm Format cache ptr for AddRef/Release
|
||
|
{
|
||
|
if(!IsValid())
|
||
|
return; // Nothing to merge
|
||
|
|
||
|
CFormatRunPtr rp(*this);
|
||
|
CBiDiLevel level;
|
||
|
// Move this run ptr to end of
|
||
|
AdjustBackward(); // preceeding run (if at run edge)
|
||
|
rp.AdjustForward(); // (merge may delete run at entry)
|
||
|
if(_iRun != rp._iRun) // On a format edge: copy previous
|
||
|
{ // format index over
|
||
|
GetLevel(&level);
|
||
|
rp.SetFormat(GetFormat(), cch, pf, &level); // Format cch chars at this
|
||
|
rp.MergeRuns(_iRun, pf); // runptr
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////// CCFRunPtr ///////////////////////////////
|
||
|
|
||
|
CCFRunPtr::CCFRunPtr(const CRchTxtPtr &rtp)
|
||
|
: CFormatRunPtr(rtp._rpCF)
|
||
|
{
|
||
|
_ped = rtp.GetPed();
|
||
|
}
|
||
|
|
||
|
CCFRunPtr::CCFRunPtr(const CFormatRunPtr &rp, CTxtEdit *ped)
|
||
|
: CFormatRunPtr(rp)
|
||
|
{
|
||
|
_ped = ped;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CCFRunPtr::IsMask(dwMask, MaskOp)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* return TRUE according to the mask operation MaskOp operating on
|
||
|
* _dwEffects.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* TRUE if bits in CCharFormat::dwEffects correspond to those in dwMask
|
||
|
*/
|
||
|
BOOL CCFRunPtr::IsMask(
|
||
|
DWORD dwMask, //@parm Bit mask to use on dwEffects
|
||
|
MASKOP MaskOp) //@parm Logic operation for bits
|
||
|
{
|
||
|
DWORD dwEffects = _ped->GetCharFormat(GetFormat())->_dwEffects;
|
||
|
|
||
|
if(MaskOp == MO_EXACT) // Bit masks must be identical
|
||
|
return dwEffects == dwMask;
|
||
|
|
||
|
dwEffects &= dwMask;
|
||
|
if(MaskOp == MO_OR) // TRUE if one or more effect bits
|
||
|
return dwEffects != 0; // identified by mask are on
|
||
|
|
||
|
if(MaskOp == MO_AND) // TRUE if all effect bits
|
||
|
return dwEffects == dwMask; // identified by mask are on
|
||
|
|
||
|
AssertSz(FALSE, "CCFRunPtr::IsMask: illegal mask operation");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CCFRunPtr::IsInHidden()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* return TRUE if CCharFormat for this run ptr has CFE_HIDDEN bit set
|
||
|
*
|
||
|
* @rdesc
|
||
|
* TRUE if CCharFormat for this run ptr has CFE_HIDDEN bit set
|
||
|
*/
|
||
|
BOOL CCFRunPtr::IsInHidden()
|
||
|
{
|
||
|
if (!IsValid())
|
||
|
return FALSE; // No format run, not hidden
|
||
|
|
||
|
AdjustForward();
|
||
|
BOOL fHidden = IsHidden();
|
||
|
if(_ich)
|
||
|
return fHidden;
|
||
|
|
||
|
AdjustBackward();
|
||
|
return fHidden && IsHidden();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CCFRunPtr::FindUnhidden()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find nearest expanded CF going forward. If none, find nearest going
|
||
|
* backward. If none, go to start of document
|
||
|
*
|
||
|
* @rdesc
|
||
|
* cch to nearest expanded CF as explained in function description
|
||
|
*
|
||
|
* @devnote
|
||
|
* changes this run ptr
|
||
|
*/
|
||
|
LONG CCFRunPtr::FindUnhidden()
|
||
|
{
|
||
|
LONG cch = FindUnhiddenForward();
|
||
|
|
||
|
if(IsHidden())
|
||
|
cch = FindUnhiddenBackward();
|
||
|
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CCFRunPtr::FindUnhiddenForward()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find nearest expanded CF going forward. If none, go to EOD
|
||
|
*
|
||
|
* @rdesc
|
||
|
* cch to nearest expanded CF going forward
|
||
|
*
|
||
|
* @devnote
|
||
|
* changes this run ptr
|
||
|
*/
|
||
|
LONG CCFRunPtr::FindUnhiddenForward()
|
||
|
{
|
||
|
LONG cch = 0;
|
||
|
|
||
|
AdjustForward();
|
||
|
while(IsHidden())
|
||
|
{
|
||
|
cch += GetCchLeft();
|
||
|
if(!NextRun())
|
||
|
break;
|
||
|
}
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CCFRunPtr::MatchFormatSignature
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Match the current format's font signature with the script (index to codepage).
|
||
|
* It takes care single-codepage fonts which implicitly supports ASCII range.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* return how font matched
|
||
|
*/
|
||
|
|
||
|
inline int CCFRunPtr::MatchFormatSignature (
|
||
|
const CCharFormat* pCF,
|
||
|
int iCharRep,
|
||
|
int iMatchCurrent,
|
||
|
QWORD * pqwFontSig)
|
||
|
{
|
||
|
QWORD qwFontSig = 0;
|
||
|
|
||
|
if (GetFontSignatureFromFace(pCF->_iFont, &qwFontSig) != 0)
|
||
|
{
|
||
|
if (pqwFontSig)
|
||
|
*pqwFontSig = qwFontSig;
|
||
|
|
||
|
if (iMatchCurrent & MATCH_ASCII && fc().GetInfoFlags(pCF->_iFont).fNonBiDiAscii)
|
||
|
return MATCH_ASCII;
|
||
|
|
||
|
if (FontSigFromCharRep(iCharRep) & ~FASCII & qwFontSig)
|
||
|
return MATCH_FONT_SIG;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CCFRunPtr::GetPreferredFontInfo(iCharRep, &iCharRepRet, &iFont, &yHeight, &bPitchAndFamily,
|
||
|
* iFormat, iMatchCurrent, piFormatOut )
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find the preferred font for the given code page around the range.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* boolean true if suitable font found, false otherwise.
|
||
|
*/
|
||
|
bool CCFRunPtr::GetPreferredFontInfo(
|
||
|
BYTE iCharRep,
|
||
|
BYTE & iCharRepRet,
|
||
|
SHORT& iFont,
|
||
|
SHORT& yHeight, // return in twips
|
||
|
BYTE& bPitchAndFamily,
|
||
|
int iFormat,
|
||
|
int iMatchCurrent,
|
||
|
int *piFormatOut)
|
||
|
{
|
||
|
int i;
|
||
|
bool fr = false;
|
||
|
static int const MAX_FONTSEARCH = 256;
|
||
|
const CCharFormat *pCF;
|
||
|
const CCharFormat *pCFCurrent;
|
||
|
const CCharFormat *pCFPrevious = NULL;
|
||
|
int iMatch = 0; // how signature match?
|
||
|
QWORD qwFontSigCurrent = 0;
|
||
|
SHORT yNewHeight = 0;
|
||
|
bool fUseUIFont = _ped->fUseUIFont() || _ped->Get10Mode();
|
||
|
|
||
|
Assert(!(iMatchCurrent & MATCH_ASCII) || iCharRep == ANSI_INDEX);
|
||
|
|
||
|
if(_ped->fUseUIFont())
|
||
|
pCFCurrent = _ped->GetCharFormat(-1); // Plain text or UI font specified
|
||
|
else
|
||
|
pCFCurrent = _ped->GetCharFormat(iFormat != -1 ? iFormat : GetFormat());
|
||
|
|
||
|
if (iMatchCurrent == GET_HEIGHT_ONLY) // Just do the font autosizing.
|
||
|
{
|
||
|
fr = true;
|
||
|
pCF = NULL;
|
||
|
goto DO_SIZE;
|
||
|
}
|
||
|
|
||
|
if ((iMatchCurrent & MATCH_FONT_SIG) &&
|
||
|
(iMatch = MatchFormatSignature(pCFCurrent, iCharRep, iMatchCurrent, &qwFontSigCurrent)) != 0)
|
||
|
{
|
||
|
pCF = pCFCurrent; // Setup to use it
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int iFormatOut;
|
||
|
|
||
|
// Try searching backwards
|
||
|
if (IsValid()) // If doc has CF runs
|
||
|
AdjustBackward();
|
||
|
i = MAX_FONTSEARCH; // Don't be searching for years
|
||
|
iFormatOut = GetFormat();
|
||
|
pCF = _ped->GetCharFormat(iFormatOut);
|
||
|
while (i--)
|
||
|
{
|
||
|
if(iCharRep == pCF->_iCharRep) // Equal charset ids?
|
||
|
{
|
||
|
pCFPrevious = pCF;
|
||
|
break;
|
||
|
}
|
||
|
if (!PrevRun()) // Done searching?
|
||
|
break;
|
||
|
iFormatOut = GetFormat();
|
||
|
pCF = _ped->GetCharFormat(iFormatOut);
|
||
|
}
|
||
|
pCF = pCFPrevious;
|
||
|
if (piFormatOut && pCF)
|
||
|
{
|
||
|
*piFormatOut = iFormatOut;
|
||
|
return true; // Done since we only ask for the format.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Try match charset if requested
|
||
|
if(!pCF && iMatchCurrent == MATCH_CURRENT_CHARSET)
|
||
|
{
|
||
|
CCcs* pccs = _ped->GetCcs(pCFCurrent, W32->GetYPerInchScreenDC());
|
||
|
if (pccs)
|
||
|
{
|
||
|
if(pccs->BestCharRep(iCharRep, DEFAULT_INDEX, MATCH_CURRENT_CHARSET) != DEFAULT_INDEX)
|
||
|
pCF = pCFCurrent; // Current font can do it
|
||
|
pccs->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Try default document format
|
||
|
if (!pCF)
|
||
|
{
|
||
|
pCF = _ped->GetCharFormat(-1);
|
||
|
if(iCharRep != pCF->_iCharRep) // Diff charset ids?
|
||
|
pCF = NULL;
|
||
|
}
|
||
|
|
||
|
DO_SIZE:
|
||
|
yHeight = pCFCurrent->_yHeight; // Assume current height
|
||
|
|
||
|
if (!pCF)
|
||
|
{
|
||
|
// Default to table if no match.
|
||
|
fr = W32->GetPreferredFontInfo(
|
||
|
iCharRep, fUseUIFont, iFont, (BYTE&)yNewHeight, bPitchAndFamily );
|
||
|
|
||
|
if (!_ped->_fAutoFontSizeAdjust && iCharRep == THAI_INDEX)
|
||
|
// Kick in font size adjusting in first bind to Thai.
|
||
|
_ped->_fAutoFontSizeAdjust = TRUE;
|
||
|
}
|
||
|
|
||
|
if (pCF)
|
||
|
{
|
||
|
// Found previous or current font
|
||
|
iFont = pCF->_iFont;
|
||
|
bPitchAndFamily = pCF->_bPitchAndFamily;
|
||
|
|
||
|
if (pCF == pCFCurrent && (iMatchCurrent & MATCH_FONT_SIG) &&
|
||
|
(IsFECharRep(pCF->_iCharRep) && W32->IsFECodePageFont(qwFontSigCurrent) ||
|
||
|
iMatch == MATCH_ASCII && iCharRep == ANSI_INDEX))
|
||
|
{
|
||
|
// The current font matches the requested signature.
|
||
|
// If it's a East Asia or ASCII font, we leave the charset intact.
|
||
|
iCharRepRet = pCF->_iCharRep;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_ped->_fAutoFontSizeAdjust && iFont != pCFCurrent->_iFont)
|
||
|
{
|
||
|
if (IsValid())
|
||
|
{
|
||
|
// If the last run format is available. We will scale the size relative to it.
|
||
|
AdjustBackward();
|
||
|
if (GetIch() > 0)
|
||
|
{
|
||
|
pCFCurrent = _ped->GetCharFormat(GetFormat());
|
||
|
yHeight = pCFCurrent->_yHeight;
|
||
|
}
|
||
|
AdjustForward();
|
||
|
}
|
||
|
|
||
|
if (iFont != pCFCurrent->_iFont)
|
||
|
{
|
||
|
// Scale the height relative to the preceding format
|
||
|
if (pCF)
|
||
|
yNewHeight = GetFontLegitimateSize(iFont, fUseUIFont, iCharRep);
|
||
|
|
||
|
if (yNewHeight)
|
||
|
{
|
||
|
// Get legitimate size of current font
|
||
|
SHORT yDefHeight = GetFontLegitimateSize(pCFCurrent->_iFont,
|
||
|
fUseUIFont, pCFCurrent->_iCharRep);
|
||
|
|
||
|
// Calculate the new height relative to the current height
|
||
|
if (yDefHeight)
|
||
|
{
|
||
|
if (fUseUIFont)
|
||
|
{
|
||
|
// For UIFont, we only convert from one preferred size to another preferred size.
|
||
|
if (pCFCurrent->_yHeight / TWIPS_PER_POINT == yDefHeight)
|
||
|
yHeight = yNewHeight * TWIPS_PER_POINT;
|
||
|
}
|
||
|
else
|
||
|
yHeight = (SHORT)MulDiv(pCFCurrent->_yHeight, yNewHeight, yDefHeight);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!yHeight)
|
||
|
yHeight = (SHORT)MulDiv(pCFCurrent->_yHeight, yNewHeight, 10);
|
||
|
|
||
|
return pCF || fr;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CCFRunPtr::FindUnhiddenBackward()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find nearest expanded CF going backward. If none, go to BOD
|
||
|
*
|
||
|
* @rdesc
|
||
|
* cch to nearest expanded CF going backward
|
||
|
*
|
||
|
* @devnote
|
||
|
* changes this run ptr
|
||
|
*/
|
||
|
LONG CCFRunPtr::FindUnhiddenBackward()
|
||
|
{
|
||
|
LONG cch = 0;
|
||
|
|
||
|
AdjustBackward();
|
||
|
while(IsHidden())
|
||
|
{
|
||
|
cch -= GetIch();
|
||
|
if(!_iRun)
|
||
|
break;
|
||
|
_ich = 0;
|
||
|
AdjustBackward();
|
||
|
}
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
///////////////////////////// CPFRunPtr ///////////////////////////////
|
||
|
|
||
|
CPFRunPtr::CPFRunPtr(const CRchTxtPtr &rtp)
|
||
|
: CFormatRunPtr(rtp._rpPF)
|
||
|
{
|
||
|
_ped = rtp.GetPed();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::FindHeading(cch, lHeading)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find heading with number lHeading (e.g., = 1 for Heading 1) or above
|
||
|
* in a range starting at this PFrun pointer. If successful, this run
|
||
|
* ptr points at the matching run; else it remains unchanged.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* cch to matching heading or tomBackward if not found
|
||
|
*
|
||
|
* @devnote
|
||
|
* changes this run ptr
|
||
|
*/
|
||
|
LONG CPFRunPtr::FindHeading(
|
||
|
LONG cch, //@parm Max cch to move
|
||
|
LONG& lHeading) //@parm Lowest lHeading to match
|
||
|
{
|
||
|
LONG cchSave = cch;
|
||
|
LONG ichSave = _ich;
|
||
|
LONG iRunSave = _iRun;
|
||
|
LONG OutlineLevel;
|
||
|
|
||
|
Assert((unsigned)lHeading <= NHSTYLES);
|
||
|
|
||
|
if(!IsValid())
|
||
|
return tomBackward;
|
||
|
|
||
|
while(TRUE)
|
||
|
{
|
||
|
OutlineLevel = GetOutlineLevel();
|
||
|
|
||
|
if (!(OutlineLevel & 1) &&
|
||
|
(!lHeading || (lHeading - 1)*2 >= OutlineLevel))
|
||
|
{
|
||
|
lHeading = OutlineLevel/2 + 1; // Return heading # found
|
||
|
return cchSave - cch; // Return how far away it was
|
||
|
}
|
||
|
|
||
|
if(cch >= 0)
|
||
|
{
|
||
|
cch -= GetCchLeft();
|
||
|
if(cch <= 0 || !NextRun())
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cch += GetIch();
|
||
|
if(cch > 0 || !_iRun)
|
||
|
break;
|
||
|
AdjustBackward();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_ich = ichSave;
|
||
|
_iRun = iRunSave;
|
||
|
return tomBackward; // Didn't find desired heading
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::FindRowEnd(TableLevel)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Advance this ptr just past table-row terminator that matches
|
||
|
* the passed-in table level
|
||
|
*
|
||
|
* @rdesc
|
||
|
* TRUE if matching table row end is found
|
||
|
*
|
||
|
* @devnote
|
||
|
* changes this run ptr only if TableLevel is found within cch chars
|
||
|
*/
|
||
|
BOOL CPFRunPtr::FindRowEnd(
|
||
|
LONG TableLevel) //@parm Table level to match
|
||
|
{
|
||
|
LONG ichSave = _ich;
|
||
|
LONG iRunSave = _iRun;
|
||
|
|
||
|
Assert(IsValid());
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if(IsTableRowDelimiter() && GetPF()->_bTableLevel == (BYTE)TableLevel)
|
||
|
{
|
||
|
NextRun(); // Bypass delimiter
|
||
|
return TRUE;
|
||
|
}
|
||
|
} while(NextRun());
|
||
|
|
||
|
_ich = ichSave; // Restore run ptr indices
|
||
|
_iRun = iRunSave;
|
||
|
return FALSE; // Didn't find desired heading
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::IsCollapsed()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* return TRUE if CParaFormat for this run ptr has PFE_COLLAPSED bit set
|
||
|
*
|
||
|
* @rdesc
|
||
|
* TRUE if CParaFormat for this run ptr has PFE_COLLAPSED bit set
|
||
|
*/
|
||
|
BOOL CPFRunPtr::IsCollapsed()
|
||
|
{
|
||
|
return (_ped->GetParaFormat(GetFormat())->_wEffects & PFE_COLLAPSED) != 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::IsTableRowDelimiter()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* return TRUE if CParaFormat for this run ptr has PFE_TABLEROWDELIMITER bit set
|
||
|
*
|
||
|
* @rdesc
|
||
|
* TRUE if CParaFormat for this run ptr has PFE_TABLEROWDELIMITER bit set
|
||
|
*/
|
||
|
BOOL CPFRunPtr::IsTableRowDelimiter()
|
||
|
{
|
||
|
return (_ped->GetParaFormat(GetFormat())->_wEffects & PFE_TABLEROWDELIMITER) != 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::InTable()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* return TRUE if CParaFormat for this run ptr has PFE_TABLE bit set
|
||
|
*
|
||
|
* @rdesc
|
||
|
* TRUE if CParaFormat for this run ptr has PFE_TABLE bit set
|
||
|
*/
|
||
|
BOOL CPFRunPtr::InTable()
|
||
|
{
|
||
|
return (_ped->GetParaFormat(GetFormat())->_wEffects & PFE_TABLE) != 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::FindExpanded()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find nearest expanded PF going forward. If none, find nearest going
|
||
|
* backward. If none, go to start of document
|
||
|
*
|
||
|
* @rdesc
|
||
|
* cch to nearest expanded PF as explained in function description
|
||
|
*
|
||
|
* @devnote
|
||
|
* Moves this run ptr the amount returned (cch)
|
||
|
*/
|
||
|
LONG CPFRunPtr::FindExpanded()
|
||
|
{
|
||
|
LONG cch, cchRun;
|
||
|
|
||
|
for(cch = 0; IsCollapsed(); cch += cchRun) // Try to find expanded PF
|
||
|
{ // run going forward
|
||
|
cchRun = GetCchLeft();
|
||
|
if(!NextRun()) // Aren't any
|
||
|
{
|
||
|
Move(-cch); // Go back to starting point
|
||
|
return FindExpandedBackward(); // Try to find expanded PF
|
||
|
} // run going backward
|
||
|
}
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::FindExpandedForward()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find nearest expanded PF going forward. If none, go to EOD
|
||
|
*
|
||
|
* @rdesc
|
||
|
* cch to nearest expanded PF going forward
|
||
|
*
|
||
|
* @devnote
|
||
|
* advances this run ptr the amount returned (cch)
|
||
|
*/
|
||
|
LONG CPFRunPtr::FindExpandedForward()
|
||
|
{
|
||
|
LONG cch = 0;
|
||
|
|
||
|
while(IsCollapsed())
|
||
|
{
|
||
|
LONG cchLeft = GetCchLeft();
|
||
|
_ich += cchLeft; // Update _ich in case
|
||
|
cch += cchLeft; // if(!NextRun()) breaks
|
||
|
if(!NextRun())
|
||
|
break;
|
||
|
}
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::FindExpandedBackward()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find nearest expanded PF going backward. If none, go to BOD
|
||
|
*
|
||
|
* @rdesc
|
||
|
* cch to nearest expanded PF going backward
|
||
|
*
|
||
|
* @devnote
|
||
|
* Moves this run ptr the amount returned (cch)
|
||
|
*/
|
||
|
LONG CPFRunPtr::FindExpandedBackward()
|
||
|
{
|
||
|
LONG cch = 0;
|
||
|
|
||
|
while(IsCollapsed())
|
||
|
{
|
||
|
cch -= GetIch();
|
||
|
_ich = 0;
|
||
|
if(!_iRun)
|
||
|
break;
|
||
|
AdjustBackward();
|
||
|
}
|
||
|
return cch;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::GetOutlineLevel()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find outline level this rp is pointing at
|
||
|
*
|
||
|
* @rdesc
|
||
|
* Outline level this rp is pointing at
|
||
|
*/
|
||
|
LONG CPFRunPtr::GetOutlineLevel()
|
||
|
{
|
||
|
const CParaFormat *pPF = _ped->GetParaFormat(GetFormat());
|
||
|
LONG OutlineLevel = pPF->_bOutlineLevel;
|
||
|
|
||
|
AssertSz(IsHeadingStyle(pPF->_sStyle) ^ (OutlineLevel & 1),
|
||
|
"CPFRunPtr::GetOutlineLevel: sStyle/bOutlineLevel mismatch");
|
||
|
|
||
|
return OutlineLevel;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::GetStyle()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Find style this rp is pointing at
|
||
|
*
|
||
|
* @rdesc
|
||
|
* Style this rp is pointing at
|
||
|
*/
|
||
|
LONG CPFRunPtr::GetStyle()
|
||
|
{
|
||
|
const CParaFormat *pPF = _ped->GetParaFormat(GetFormat());
|
||
|
LONG Style = pPF->_sStyle;
|
||
|
|
||
|
AssertSz(IsHeadingStyle(Style) ^ (pPF->_bOutlineLevel & 1),
|
||
|
"CPFRunPtr::GetStyle: sStyle/bOutlineLevel mismatch");
|
||
|
|
||
|
return Style;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::ResolveRowStartPF()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Resolve table row start PF corresponding to the current table row
|
||
|
* end. Assumes that all table rows contained in the current row are
|
||
|
* resolved, which should be the case for nested tables in RTF.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* TRUE iff success
|
||
|
*/
|
||
|
BOOL CPFRunPtr::ResolveRowStartPF()
|
||
|
{
|
||
|
AdjustBackward();
|
||
|
LONG iFormat = GetFormat();
|
||
|
Assert(IsTableRowDelimiter());
|
||
|
|
||
|
const CParaFormat *pPF = NULL;
|
||
|
|
||
|
while(PrevRun())
|
||
|
{
|
||
|
pPF = _ped->GetParaFormat(GetFormat());
|
||
|
if((pPF->_wEffects & PFE_TABLEROWDELIMITER) && pPF->_iTabs == -1)
|
||
|
break;
|
||
|
}
|
||
|
Assert(IsTableRowDelimiter());
|
||
|
Assert(pPF->_iTabs == -1);
|
||
|
|
||
|
CFormatRun* pRun = GetRun(0);
|
||
|
IParaFormatCache *pf = GetParaFormatCache();
|
||
|
|
||
|
pf->Release(pRun->_iFormat);
|
||
|
pf->AddRef(iFormat);
|
||
|
pRun->_iFormat = iFormat;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::GetMinTableLevel(cch)
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Get the lowest table level in the range of cch chars from this
|
||
|
* run ptr. This is the lesser of the level ending at the range
|
||
|
* cpMost and that starting at cpMin. Leave this run ptr at cpMin.
|
||
|
*
|
||
|
* @rdesc
|
||
|
* Lowest table level in the cch chars from this run ptr
|
||
|
*/
|
||
|
LONG CPFRunPtr::GetMinTableLevel(
|
||
|
LONG cch) //@parm cch to check for table level
|
||
|
{
|
||
|
if(cch > 0)
|
||
|
AdjustBackward();
|
||
|
|
||
|
const CParaFormat *pPF = GetPF();
|
||
|
LONG Level = pPF->_bTableLevel; // Default: level at active end
|
||
|
|
||
|
if(cch)
|
||
|
{
|
||
|
Move(-cch); // Go find table level at other
|
||
|
pPF = GetPF(); // end of range
|
||
|
if(pPF->_bTableLevel < Level)
|
||
|
Level = pPF->_bTableLevel;
|
||
|
if(cch < 0) // Range active end at cpMin
|
||
|
Move(cch); // Start at cpMin
|
||
|
}
|
||
|
AssertSz(Level >= 0, "CPFRunPtr::GetMinTableLevel: invalid table level");
|
||
|
return Level;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CPFRunPtr::GetTableLevel()
|
||
|
*
|
||
|
* @mfunc
|
||
|
* Get table level this run ptr is at
|
||
|
*
|
||
|
* @rdesc
|
||
|
* Table level this run ptr is at
|
||
|
*/
|
||
|
LONG CPFRunPtr::GetTableLevel()
|
||
|
{
|
||
|
const CParaFormat *pPF = _ped->GetParaFormat(GetFormat());
|
||
|
AssertSz(!(pPF->_wEffects & PFE_TABLE) || pPF->_bTableLevel > 0,
|
||
|
"CPFRunPtr::GetTableLevel: invalid table level");
|
||
|
return pPF->_bTableLevel;
|
||
|
}
|
||
|
|