/* * CRchTxtPtr::SetCp(cp) * * @mfunc * Set this rich text ptr's cp to cp */ DWORD CRchTxtPtr::SetCp( DWORD cp) // @parm new cp for this text ptr { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::SetCp"); CRchTxtPtr::Advance(cp - GetCp()); return GetCp(); }
LONG CRchTxtPtr::GetCchLeftRunPF() { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCchLeftRunPF"); return _rpPF.IsValid() ? _rpPF.GetCchLeft() : GetTextLength() - GetCp(); }
/* * CRchTxtPtr::CheckFormatRuns () * * @mfunc * Check the format runs against what's in CTxtStory. If * different, forces a rebind to <p cp> */ void CRchTxtPtr::CheckFormatRuns( ) { CTxtStory *pStory = GetPed()->GetTxtStory(); if( (pStory->GetCFRuns() != (CFormatRuns *)_rpCF._prgRun) || (pStory->GetPFRuns() != (CFormatRuns *)_rpPF._prgRun) ) { InitRunPtrs(GetCp()); } _TEST_INVARIANT_ }
/* * CRchTxtPtr::FindText(cpMost, dwFlags, pch, cchToFind) * * @mfunc * Find text in a range starting at this text pointer; * if found, moves this text pointer to that position. * * @rdesc * character position of first match * <lt> 0 if no match * * @devnote * Would be easy to match a single format (like Word 6) provided * cchToFind is nonzero. Else need to search runs (also pretty easy). * For format-sensitive searches, might be easier to search for matching * format run first and then within that run search for text. */ LONG CRchTxtPtr::FindText ( LONG cpMost, // @parm Limit of search; <lt> 0 for end of text DWORD dwFlags, // @parm FR_MATCHCASE case must match // FR_WHOLEWORD match must be a whole word TCHAR const *pch, // @parm Text to search for LONG cchToFind) // @parm Length of text to search for { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::FindText"); _TEST_INVARIANT_ LONG cpSave = GetCp(); LONG cpMatch = _rpTX.FindText(cpMost, dwFlags, pch, cchToFind); if(cpMatch >= 0) // cpMatch = -1 means "not found" SetRunPtrs(GetCp(), cpSave); // possible code for format-dependent Finds return cpMatch; }
/* * CLinePtr::GetAdjustedLineLength * * @mfunc returns the length of the line _without_ EOP markers * * @rdesc LONG; the length of the line */ LONG CLinePtr::GetAdjustedLineLength() { CLine* pline = GetCurrRun(); Assert(pline); LONG cchJunk; LONG cchTrim; CTreePos *ptpRet, *ptpPrev; LONG cpEndLine = _pdp->GetFirstCp() + GetCp() - GetIch() + pline->_cch; CTreePos* ptp = _pdp->GetMarkup()->TreePosAtCp(cpEndLine, &cchJunk); _pdp->EndNodeForLine(cpEndLine, ptp, &cchTrim, &ptpRet, NULL); cchTrim = min(cchTrim, pline->_cch); LONG cpNewMost = cpEndLine - cchTrim; if(ptpRet) { ptpPrev = ptpRet; if(ptpPrev->GetCch()==0 || ptpPrev->IsNode() || (ptpPrev->IsText() && ptpPrev->GetCp()>=cpNewMost)) { do { ptpPrev = ptpPrev->PreviousTreePos(); } while(ptpPrev->GetCch() == 0); } if(ptpPrev->IsEndElementScope() && ptpPrev->Branch()->Tag()==ETAG_BR) { cchTrim += 2; } else if(ptpPrev->IsText()) { CTxtPtr tp(_pdp->GetMarkup(), cpNewMost-1); if(tp.GetChar() == WCH_ENDPARA1) { cchTrim++; } } } cchTrim = min(cchTrim, pline->_cch); return pline->_cch-cchTrim; }
/* * CRchTxtPtr::Check_rpPF() * * @mfunc * enable _rpPF if it's not already enabled * * @rdesc * TRUE if _rpPF is enabled */ BOOL CRchTxtPtr::Check_rpPF() { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::Check_rpPF"); if(_rpPF.IsValid()) return TRUE; if( !IsRich() ) return FALSE; if( !_rpPF.InitRuns (GetCp(), GetTextLength(), &(GetPed()->GetTxtStory()->_pPFRuns)) ) { return FALSE; } CNotifyMgr *pnm = GetPed()->GetNotifyMgr(); // For notifying of changes if( pnm ) pnm->NotifyPostReplaceRange( // Notify interested parties (ITxNotify *)this, INFINITE, // of the change. 0, 0, INFINITE, INFINITE); return TRUE; }
// Move runptr by a certain number of cch/runs BOOL CLinePtr::RpAdvanceCp(long cch, BOOL fSkipFrame) { // See if this is a multi-line ptr Assert(_prgRun); if(cch == CRunPtr<CLine>::AdvanceCp(cch)) { if(fSkipFrame) { CLine* pLine=GetCurrRun(); // There might not be a valid run pointer. if(pLine && pLine->IsFrame()) { int iStep; long iRunEnd; Assert(NumRuns()); if(cch < 0) { iStep = -1; iRunEnd = 0; } else { iStep = 1; iRunEnd = NumRuns() - 1; } while(GetCurrRun()->IsFrame()) { if(GetIRun() == iRunEnd) { // If the last line is a frame line, then put the // line ptr at the end of the previous line. Assert // that we have a previous line: it will contain the // embedding character for the site in the frame line. Assert(iRunEnd > 0); SetIRun(iRunEnd - 1); if((pLine=GetCurrRun()) != NULL) { SetIch(pLine->_cch); } #ifdef _DEBUG { CTxtPtr tp(_pdp->GetMarkup(), GetCp()-1); TCHAR ch = tp.GetChar(); Assert('\r' != ch); } #endif return TRUE; } else { SetIRun(GetIRun()+iStep); } } } } } else { return FALSE; } return TRUE; }
/* * CRchTxtPtr::ChangeCase(cch, Type, publdr) * * @mfunc * Change case of cch chars starting at this text ptr according to Type, * which has the possible values: * * tomSentenceCase = 0: capitalize first letter of each sentence * tomLowerCase = 1: change all letters to lower case * tomUpperCase = 2: change all letters to upper case * tomTitleCase = 3: capitalize the first letter of each word * tomToggleCase = 4: toggle the case of each letter * * @rdesc * TRUE iff a change occurred * * @devnote * Since this routine only changes the case of characters, it has no * effect on rich-text formatting. However it is part of the CRchTxtPtr * class in order to notify the display of changes. CTxtRanges are also * notified just in case the text blocks are modified. */ BOOL CRchTxtPtr::ChangeCase ( LONG cch, //@parm # chars to change case for LONG Type, //@parm Type of change case command IUndoBuilder *publdr) //@parm UndoBuilder to receive anti-event // for any replacements { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ChangeCase"); _TEST_INVARIANT_ #define BUFFERLEN 256 LONG cchChunk, cchFirst, cchGet, cchLast; BOOL fAlpha, fToUpper, fUpper; // Flags controling case change BOOL fChange = FALSE; // No change yet BOOL fStart = TRUE; // Start of Word/Sentence TCHAR * pch; // Ptr to walk rgCh with WORD * pType; // Ptr to walk rgType with TCHAR rgCh[BUFFERLEN]; // Char buffer to work in WORD rgType[BUFFERLEN]; // C1_TYPE array for rgCh if( GetCp() ) { if( Type == tomSentenceCase ) { fStart = _rpTX.IsAtBOSentence(); } else if( Type == tomTitleCase ) { // check to see if we are at the beginning of // a word. This is the case if the character preceeding // our current position is white space. fStart = IsWhiteSpace(_rpTX.PrevChar()); _rpTX.AdvanceCp(1); } } while(cch > 0) // Do 'em all (or as many as { // in story) cchChunk = min(BUFFERLEN, cch); // Get next bufferful cch -= cchChunk; // Decrement the count cchGet = _rpTX.GetText(cchChunk, rgCh); // Manipulate chars in buffer if(cchGet < cchChunk) // (for undo, need to use { // ReplaceRange()) cch = 0; // No more chars in story, if(!cchGet) // so we'll be done break; // We're done already cchChunk = cchGet; // Something in this chunk } GetStringTypeEx(0, CT_CTYPE1, rgCh, // Find out whether chars are cchChunk, rgType); // UC, LC, or neither cchLast = 0; // Default nothing to replace cchFirst = -1; for(pch = rgCh, pType = rgType; // Process buffered chars cchChunk; cchChunk--, pch++, pType++) { fAlpha = *pType & (C1_UPPER | C1_LOWER); // Nonzero if UC or LC fUpper = (*pType & C1_UPPER) != 0; // TRUE if UC fToUpper = fStart ? TRUE : fUpper; // capitalize first letter of a // sentence switch(Type) { // Decide whether to change case tomLowerCase: // case and determine start fToUpper = FALSE; // of word/sentence for title break; // and sentence cases case tomUpperCase: fToUpper = TRUE; break; case tomToggleCase: fToUpper = !fUpper; break; case tomSentenceCase: if(*pch == TEXT('.')) // If sentence terminator, fStart = TRUE; // capitalize next alpha if(fAlpha) // If this char is alpha, next fStart = FALSE; // char can't start a break; // sentence case tomTitleCase: // If this char is alpha, next fStart = (fAlpha == 0); // char can't start a word break; default: return FALSE; } if(fAlpha && (fToUpper ^ fUpper)) // Only change case if it { // makes a difference (saves if(fToUpper) // on system calls and undos) CharUpperBuff(pch, 1); else CharLowerBuff(pch, 1); fChange = TRUE; // Return value: change made if( cchFirst == -1 ) // Save cch of unchanged cchFirst = cchGet-cchChunk; // leading string cchLast = cchChunk - 1; // Save cch of unchanged } // trailing string } if( cchFirst == -1 ) { Assert(cchLast == 0); cchFirst = cchGet; } Advance(cchFirst); // Skip unchanged leading cchGet -= cchFirst + cchLast; // string. cchGet = cch of ReplaceRange(cchGet, cchGet, rgCh // changed span + cchFirst, publdr, tomUndefined); Advance(cchLast); // Skip unchanged trailing } // string return fChange; }
/* * CRchTxtPtr::ReplaceRangeFormatting(cchOld, cchNew, iFormat, publdr, * ppaeCF, ppaePF, cchMove) * @mfunc * replace character and paragraph formatting at this text pointer * using CCharFormat with index iFormat * * @rdesc * count of new characters added * * @devnote * moves _rpCF and _rpPF to end of replaced text * moves format run arrays * CCharFormat for iFormat is fully configured, i.e., no NINCHes */ INLINE LONG CRchTxtPtr::ReplaceRangeFormatting( LONG cchOld, //@parm length of range to replace LONG cchNew, //@parm length of replacement text LONG iFormat, //@parm Char format to use IUndoBuilder *publdr, //@parm UndoBuilder to receive antievents IAntiEvent **ppaeCF, //@parm where to return 'extra' CF anti-events IAntiEvent **ppaePF, //@parm where to return extra PF anti-events LONG cchMove, //@parm cch to move between PF runs LONG cchPrevEOP, //@parm cch from _cp back to prev EOP LONG cchNextEOP) //@parm cch from _cp up to next EOP { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ReplaceRangeFormatting"); LONG cp = GetCp(); ICharFormatCache * pcfc; IParaFormatCache * ppfc; DWORD iRunMerge = 0; AssertSz(cchOld >= 0, "CRchTxtPtr::ReplaceRangeFormatting: Illegal cchOld"); if(_rpCF.IsValid()) { iRunMerge = _rpCF._iRun; if( iRunMerge > 0 ) { iRunMerge--; } if(FAILED(GetCharFormatCache(&pcfc))) return -1; // Access to CF caches failed if(cchOld > 0) { // add the soon-to-be deleted if( publdr ) // formats to the undo list { *ppaeCF = gAEDispenser.CreateReplaceFormattingAE( GetPed(), _rpCF, cchOld, pcfc, CharFormat); } // Delete/modify CF runs <--> _rpCF.Delete(cchOld, pcfc, 0); // to cchOld chars } // If we deleted all of text in story, don't bother adding a new // run. Else insert/modify CF runs corresponding to cchNew chars // // For IME composition in a plain text control, there is no // trailing carriage return and thus need the extra test. if(cchNew > 1 || cchNew && ( cchOld < (LONG)GetTextLength() || ( IsIMERich() && cchOld == (LONG)GetTextLength()))) { _rpCF.InsertFormat(cchNew, iFormat, pcfc); } #ifdef DEBUG else if( cchNew > 0 ) { // Make sure that we don't have any format runs; we should // be in the new document state. Assert(!_rpCF.IsValid()); } #endif // DEBUG if((cchOld || cchNew) && _rpCF.IsValid())// Deleting all text { // invalidates _rpCF _rpCF.MergeRuns(iRunMerge, pcfc); _rpCF.BindToCp(cp + cchNew); } } if(_rpPF.IsValid()) { _rpPF.AdjustForward(); // Be absolutely sure that // PF runs end with EOPs iRunMerge = _rpPF._iRun; if( iRunMerge > 0 ) { iRunMerge--; } if(FAILED(GetParaFormatCache(&ppfc))) return -1; // Access to PF caches failed if(cchOld) // Delete cchOld from PF runs { // add the soon-to-be deleted if( publdr ) // formats to the undo list { CFormatRunPtr rp(_rpPF); rp.AdvanceCp(cchPrevEOP); *ppaePF = gAEDispenser.CreateReplaceFormattingAE(GetPed(), rp, cchNextEOP - cchPrevEOP, ppfc, ParaFormat); } _rpPF.Delete(cchOld, ppfc, cchMove); } if(_rpPF.IsValid()) // Deleting all text { // invalidates _rpPF _rpPF.AdjustForward(); _rpPF.GetRun(0)->_cch += cchNew; // Insert cchNew into current _rpPF._ich += cchNew; // PF run if(cchOld || cchNew) { _rpPF.MergeRuns(iRunMerge, ppfc); _rpPF.BindToCp(cp + cchNew); } } } return cchNew; }
/* * CRchTxtPtr::ReplaceRange(cchOld, cchNew, *pch, pcpFirstRecalc, publdr, * iFormat) * @mfunc * Replace a range of text at this text pointer using CCharFormat iFormat * and updating other text runs as needed * * @rdesc * Count of new characters added * * @devnote * Moves this text pointer to end of replaced text. * May move text block and formatting arrays. */ LONG CRchTxtPtr::ReplaceRange( LONG cchOld, // @parm length of range to replace // (<lt> 0 means to end of text) LONG cchNew, // @parm length of replacement text TCHAR const *pch, // @parm replacement text IUndoBuilder *publdr, // @parm CCharFormat iFormat to use for cchNew LONG iFormat) { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ReplaceRange"); LONG cch; LONG cchEndEOP = 0; // Default 0 final EOP fixup LONG cchMove = 0; // Default nothing to move LONG cchNextEOP = cchOld; // cch to next EOP LONG cchPrevEOP = 0; // cch back to previous EOP LONG cpFR; // between PF runs LONG cpSave = GetCp(); LONG cpFormatMin = cpSave; // Used for notifications LONG cpFormat = cpSave; // Will add cchOld, maybe cchMove IAntiEvent * paeCF = NULL; IAntiEvent * paePF = NULL; CNotifyMgr * pnm; CObjectMgr * pobjmgr; _TEST_INVARIANT_ LONG cchEnd = GetTextLength() - GetCp(); if(cchOld < 0) cchOld = cchEnd; if(IsRich() && cchOld == cchEnd) // Attempting to delete up { // thru final EOP cchEndEOP = (GetPed()->Get10Mode()) // Calc cch of final EOP ? CCH_EOD_10 : CCH_EOD_20; if(cchEndEOP <= cchOld) // Don't delete it unless cchOld -= cchEndEOP; // converting from 2.0 } else if(_rpPF.IsValid() && cchOld) // If PARAFORMATs are enabled, { // get tp and rp at end of CFormatRunPtr rp(_rpPF); // range. Need bounding para CTxtPtr tp(_rpTX); // counts to save valid PF BOOL fIsAtBOP; // for undo tp.AdvanceCp(cchOld); rp.AdvanceCp(cchOld); cch = 0; if(tp.IsAfterEOP()) // Range ends with an EOP: { // get EOP length by cch = -tp.BackupCpCRLF(); // backing up over it tp.AdvanceCp(cch); // Advance past EOP } cchNextEOP = tp.FindEOP(tomForward); // Get cch up to next EOP fIsAtBOP = !GetCp() || _rpTX.IsAfterEOP(); if (!fIsAtBOP && cch == cchOld && // Deleting EOP alone before !rp.GetIch()) // new PARAFORMAT run start { // in para with more than EOP cchMove = cchNextEOP; // Need to move chars up to cpFormat += cchMove; // end of next para for } cchNextEOP += cchOld; // Count from GetCp() to EOP tp.SetCp(GetCp()); // Back to this ptr's _cp if(!fIsAtBOP) cchPrevEOP = tp.FindEOP(tomBackward);// Get cch to start of para // If deleting from within one format run up to or into another, set // up to move last para in starting format run into the run following // the deleted text if(rp.GetFormat() != _rpPF.GetFormat() // Change of format during && !fIsAtBOP && !cchMove) // deleted text not starting { // at BOP cchMove = cchPrevEOP; // Get cch to start of para cpFormatMin += cchMove; // in this ptr's run for } // moving into rp's run } Assert(cchNew >= 0 && cchOld >= 0); if(!(cchNew + cchOld)) // Nothing to do (note: all return 0; // these cch's are >= 0) // Handle pre-replace range notifications. This method is very // useful for delayed rendering of data copied to the clipboard. pnm = GetPed()->GetNotifyMgr(); if( pnm ) { pnm->NotifyPreReplaceRange((ITxNotify *)this, cpSave, cchOld, cchNew, cpFormatMin, cpFormat + cchOld); } if(iFormat >= 0) Check_rpCF(); // Get rid of objects first. This let's us guarantee that when we // insert the objects as part of an undo, the objects themselves are // restored _after_ their corresponding WCH_EMBEDDINGs have been // added to the backing store. if(GetObjectCount()) { pobjmgr = GetPed()->GetObjectMgr(); Assert(pobjmgr); pobjmgr->ReplaceRange(cpSave, cchOld, publdr); } if( ( IsRich() || IsIMERich() ) && iFormat != tomUndefined) // Rich text enabled { // The anti-events used below are a bit tricky (paeCF && paePF). // Essentially, this call, CRchTxtPtr::ReplaceRange generates one // 'combo' anti-event composed of up to two formatting AE's plus // the text anti-event. These anti-events are combined together // to prevent ordering problems during undo/redo. cpFR = ReplaceRangeFormatting(cchOld + cchEndEOP, cchNew + cchEndEOP, iFormat, publdr, &paeCF, &paePF, cchMove, cchPrevEOP, cchNextEOP); if (cchEndEOP) { // If we added in the EOP we need to back up by the EOP so // that the invariants don't get annoyed and the richtext object // doesn't get out of sync. _rpCF.AdvanceCp(-cchEndEOP); _rpPF.AdvanceCp(-cchEndEOP); } if(cpFR < 0) { Tracef(TRCSEVERR, "ReplaceRangeFormatting(%ld, %ld, %ld) failed", GetCp(), cchOld, cchNew); cch = 0; goto Exit; } } // As noted above in the call to ReplaceRangeFormatting, the anti-events // paeCF and paePF, if non-NULL, were generated by ReplaceRangeFormatting. // In order to solve ordering problems, the anti-event generated by this // method is actually a combo anti-event of text && formatting AE's. cch = _rpTX.ReplaceRange(cchOld, cchNew, pch, publdr, paeCF, paePF); if (cch != cchNew) { Tracef(TRCSEVERR, "_rpTX.ReplaceRange(%ld, %ld, ...) failed", cchOld, cchNew); // Boy, out of memory or something bad. Dump our formatting and hope // for the best. // // FUTURE: (alexgo) degrade more gracefully than loosing formatting // info. // Notify every interested party that they should dump their formatting if( pnm ) { pnm->NotifyPreReplaceRange(NULL, CONVERT_TO_PLAIN, 0, 0, 0, 0); } // Tell document to dump its format runs GetPed()->GetTxtStory()->DeleteFormatRuns(); goto Exit; } AssertSz(!_rpPF.IsValid() || _rpPF.GetIch() || !GetCp() || _rpTX.IsAfterEOP(), "CRchTxtPtr::ReplaceRange: EOP not at end of PF run"); // BUGBUG!! (alexgo) doesn't handle correctly the case where things fail // (due to out of memory or whatever). See also notes in CTxtPtr::HandleReplaceRange // Undo. The assert below is therefore somewhat bogus, but if it fires, // then our floating ranges are going to be in trouble until we fix // up the logic here. Assert(cch == cchNew); Exit: #ifdef DEBUG // test the invariant again before calling out to replace range notification; // in this way, we can catch bugs earlier. The invariant has its own // scope for convenience. if( 1 ) { _TEST_INVARIANT_ } #endif if( pnm ) { pnm->NotifyPostReplaceRange((ITxNotify *)this, cpSave, cchOld, cch, cpFormatMin, cpFormat + cchOld); } GetPed()->GetCallMgr()->SetChangeEvent(CN_TEXTCHANGED); return cch; }
ASSERTDATA #define DEBUG_CLASSNAME CRchTxtPtr #include "_invar.h" #ifdef DEBUG /* * CRchTxtPtr::Invariant */ BOOL CRchTxtPtr::Invariant( void ) const { unsigned ch; DWORD cch; DWORD cchLength = GetTextLength(); LONG cp; #ifdef EXTREME_CHECKING const TCHAR *pch; const TCHAR *pchReverse; LONG cchvalid; #endif // EXTREME_CHECKING _rpTX.Invariant(); _rpCF.Invariant(); _rpPF.Invariant(); if( _rpCF.IsValid() ) { // BEGIN tone down of assert for IME Rich composition. CFormatRun *pRun = _rpCF.Elem(0); if ( !IsIMERich() || !pRun || pRun->_iFormat != -1 || _rpCF.Count() != 1 ) // END tone down { Assert(GetCp() == (cp = _rpCF.GetCp())); Assert(cchLength == (cch = _rpCF.GetCch())); } } if( _rpPF.IsValid() ) { Assert(GetCp() == (cp = _rpPF.GetCp())); Assert(cchLength == (cch = _rpPF.GetCch())); CTxtPtr tp(_rpTX); tp.AdvanceCp(_rpPF.GetCchLeft() - 1); ch = tp.GetChar(); if(!IsASCIIEOP(ch)) { _rpTX.MoveGapToEndOfBlock(); // Make it easier to see AssertSz(FALSE, // what's going on "CRchTxtPtr::Invariant: PF run doesn't end with EOP"); } #ifdef EXTREME_CHECKING // we don't do this check normally as it is _extremely_ slow. // However, it's very useful for catching para-format run problems // now make sure that each para format run ends on a paragraph mark! CFormatRunPtr rppf(_rpPF); rppf.BindToCp(0); tp.BindToCp(0); do { tp.SetCp(rppf.GetCp() + rppf.GetRun(0)->_cch); if( !tp.IsAfterEOP() ) { cp = tp.GetCp(); pch = tp.GetPch(cchvalid); pchReverse = tp.GetPchReverse(cchvalid); pchReverse -= cchvalid; AssertSz(0, "ParaFormat Run not aligned along paragraphs!"); } } while( rppf.NextRun() ); #endif // EXTREME_CHECKING } return TRUE; }
/* CRchTxtPtr::GetIchRunXX() and CRchTxtPtr::GetCchRunXX() * * @mfunc * Text-run management to retrieve current text run cch and offset * * @rdesc * current run ich or cch * * @devnote * Use of queries like _rpCF.IsValid() instead of an inclusive fRich * allows rich-text formatting to be applied per rich-text category, * e.g., CHARFORMATs, but not necessarily PARAFORMATs. If the rp isn't * valid, _cp is used for ich and the document length is used for cch, * i.e., the values for a document describable by a single plain-text run */ LONG CRchTxtPtr::GetIchRunCF() { TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetIchRunCF"); return _rpCF.IsValid() ? _rpCF.GetIch() : GetCp(); }
/* * CRchTxtPtr::UnitCounter (Unit, &cUnit, cchMax) * * @mfunc * Helper function to count chars in <p cUnit> Units defined by <p Unit> * <p cUnit> is a signed count. If it extends beyond either end of the * story, count up to that end and update <p cUnit> accordingly. If * <p cchMax> is nonzero, stop counting when the count exceeds <p cchMax> * in magnitude. * * @rdesc * If unit is implemented, return cch corresponding to the units counted * (up to a maximum magnitude of <p cchMax>) and update cUnit; * else return tomForward to signal unit not implemented and cUnit = 0. * If unit is implemented but unavailable, e.g., tomObject with no * embedded objects, return tomBackward. * * @devnote * This is the basic engine used by the TOM CTxtRange::Move() and Index() * methods. */ LONG CRchTxtPtr::UnitCounter ( LONG Unit, //@parm Type of unit to count LONG & cUnit, //@parm Count of units to count chars for LONG cchMax) //@parm Maximum character count { TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CRchTxtPtr::UnitCounter"); LONG action; // Gives direction and tomWord commands LONG cch; // Collects cch counted LONG cchText = GetTextLength(); LONG cp = GetCp(); LONG iDir = cUnit > 0 ? 1 : -1; LONG j; // For-loop index CDisplay *pdp; // Used for tomLine case if(!cUnit) // Nothing to count { return ((DWORD)Unit > tomObject || !((IMPL >> Unit) & 1)) ? tomForward : 0; // Indicate Unit not } // implemented if(cchMax <= 0) cchMax = tomForward; // No cch limit switch(Unit) { case tomCharacter: // Smallest Unit cp += cUnit; // Requested new cp ValidateCp(cp); // Make sure it's OK cch = cUnit = cp - GetCp(); // How many cch, cUnits break; // actually moved case tomStory: // Largest Unit cch = (cUnit > 0) ? cchText - cp : -cp; // cch to start of story cUnit = cch ? iDir : 0; // If already at end/start, break; // of story, no count case tomCharFormat: // Constant CHARFORMAT cch = _rpCF.CountRuns(cUnit, cchMax, cchText); break; case tomParaFormat: // Constant PARAFORMAT cch = _rpPF.CountRuns(cUnit, cchMax, cchText); break; case tomObject: if(!GetObjectCount()) // No objects: can't move, so { cUnit = 0; // set cUnit = 0 and return tomBackward; // signal Unit unavailable } cch = GetPed()->_pobjmgr->CountObjects(cUnit, GetCp()); break; case tomLine: pdp = GetPed()->_pdp; if(pdp) // If this story has a display { // use a CLinePtr CLinePtr rp(pdp); pdp->WaitForRecalc(cp, -1); rp.RpSetCp(cp, FALSE); cch = rp.CountRuns(cUnit, cchMax, cchText); break; } // Else fall thru to treat as // tomPara default: // tp dependent cases { // Block to contain tp() which CTxtPtr tp(_rpTX); // takes time to construct if (cUnit < 0) // Counting backward { action = (Unit == tomWord) ? WB_MOVEWORDLEFT : tomBackward; } else // Counting forward { action = (Unit == tomWord) ? WB_MOVEWORDRIGHT : tomForward; } for (cch = 0, j = cUnit; j && abs(cch) < cchMax; j -= iDir) { cp = tp.GetCp(); // Save starting cp for switch (Unit) // calculating cch for this { // Unit case tomWord: tp.FindWordBreak(action); break; case tomSentence: tp.FindBOSentence(action); break; case tomLine: // Story has no line array: case tomParagraph: // treat as tomParagraph tp.FindEOP(action); break; default: cUnit = 0; return tomForward; // Return error } if(tp.GetCp() - cp == 0) // No count: break; // don't decrement cUnit cch += tp.GetCp() - cp; } cUnit -= j; // Discount any runs not } // counted if |cch| >= cchMax } if(abs(cch) > cchMax) // Keep cch within requested { // limit cch = cch > 0 ? cchMax : -cchMax; if(Unit == tomCharacter) cUnit = cch; } Advance(cch); // Move to new position return cch; // Total cch counted }