📄 rtext.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 RTEXT.C - Rich-text ptr class |
*
* This text ptr consists of a plain text ptr (_rpTX), a char-format
* run ptr (_rpCF), and a para-format run ptr (_rpPF). This module
* contains the methods to manipulate this combination of run ptrs
* consistently.
*
* Authors:<nl>
* Original RichEdit code: David R. Fulmer <nl>
* Main implementation: Murray Sargent <nl>
* Undo and notification implementations: Alex Gounares <nl>
*
*/
#include "_common.h"
#include "_edit.h"
#include "_frunptr.h"
#include "_rtext.h"
#include "_disp.h"
#include "_select.h"
#include "_m_undo.h"
#include "_antievt.h"
#include "_objmgr.h"
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;
}
#endif // DEBUG
//======================= CRchTxtPtr constructors ========================================
CRchTxtPtr::CRchTxtPtr(CTxtEdit *ped) :
_rpTX(ped, 0), _rpCF(NULL), _rpPF(NULL)
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::CRchTxtPtr");
InitRunPtrs(0);
}
CRchTxtPtr::CRchTxtPtr(CTxtEdit *ped, DWORD cp) :
_rpTX(ped, cp), _rpCF(NULL), _rpPF(NULL)
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::CRchTxtPtr");
InitRunPtrs(cp);
}
CRchTxtPtr::CRchTxtPtr (const CRchTxtPtr& rtp) :
_rpTX(rtp._rpTX), _rpCF(NULL), _rpPF(NULL)
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::CRchTxtPtr");
InitRunPtrs(rtp.GetCp());
}
CRchTxtPtr::CRchTxtPtr (const CDisplay * pdp) :
_rpTX(pdp->GetED(), 0), _rpCF(NULL), _rpPF(NULL)
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::CRchTxtPtr");
InitRunPtrs(0);
}
/*
* CRchTxtPtr::Advance(cch)
*
* @mfunc
* Move this rich-text ptr forward <p cch> characters. If <p cch>
* <lt> 0, move backward by -<p cch> characters.
*
* @rdesc
* cch actually moved
*
*/
LONG CRchTxtPtr::Advance(
LONG cch) // @parm count of characters to move - may be <lt> 0
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::Advance");
if( cch != 0 )
{
cch = _rpTX.AdvanceCp(cch);
_rpCF.AdvanceCp(cch);
_rpPF.AdvanceCp(cch);
}
_TEST_INVARIANT_
return cch;
}
/*
* CRchTxtPtr::AdvanceCRLF()
*
* @mfunc
* Advance this text ptr one char, treating CRLF as a single char.
*
* @rdesc
* cch actually moved
*/
LONG CRchTxtPtr::AdvanceCRLF()
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CRchTxtPtr::AdvanceCRLF");
LONG cch = _rpTX.AdvanceCpCRLF();
_rpPF.AdvanceCp(cch);
_rpCF.AdvanceCp(cch);
return cch;
}
/*
* CRchTxtPtr::BackupCRLF()
*
* @mfunc
* Backup this text ptr one char, treating CRLF as a single char.
*
* @rdesc
* cch actually moved
*/
LONG CRchTxtPtr::BackupCRLF()
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CRchTxtPtr::BackupCRLF");
LONG cch = _rpTX.BackupCpCRLF();
_rpPF.AdvanceCp(cch);
_rpCF.AdvanceCp(cch);
return cch;
}
/*
* CRchTxtPtr::ValidateCp(&cp)
*
* @mfunc
* If <p cp> <lt> 0, set it to 0; if it's <gt> text length, set it to
* text length.
*/
void CRchTxtPtr::ValidateCp(
LONG &cp) const // @parm new cp for this text ptr
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::ValidateCp");
LONG cchT = GetTextLength();
cp = min(cp, cchT); // Be sure cp is valid
cp = max(cp, 0);
}
/*
* 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();
}
/* 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();
}
LONG CRchTxtPtr::GetCchRunCF()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCchRunCF");
return _rpCF.IsValid() ? _rpCF.GetRun(0)->_cch : GetTextLength();
}
/* CRchTxtPtr::GetCchLeftRunCF() / GetCchLeftRunPF()
*
* @mfunc
* Return cch left in run, i.e., cchRun - ich
*
* @rdesc
* cch left in run
*/
LONG CRchTxtPtr::GetCchLeftRunCF()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCchLeftRunCF");
return _rpCF.IsValid()
? _rpCF.GetCchLeft() : GetTextLength() - GetCp();
}
LONG CRchTxtPtr::GetCchLeftRunPF()
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCchLeftRunPF");
return _rpPF.IsValid()
? _rpPF.GetCchLeft() : GetTextLength() - GetCp();
}
/*
* 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;
}
/*
* CRchTxtPtr::GetCF()/GetPF()
*
* @mfunc
* Return ptr to CCharFormat/CParaFormat at this text ptr. If no CF/PF runs
* are allocated, then return ptr to default format
*
* @rdesc
* Ptr to CCharFormat/CParaFormat at this text ptr
*/
const CCharFormat* CRchTxtPtr::GetCF() const
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetCF");
return GetPed()->GetCharFormat(_rpCF.GetFormat());
}
const CParaFormat* CRchTxtPtr::GetPF() const
{
TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CRchTxtPtr::GetPF");
return GetPed()->GetParaFormat(_rpPF.GetFormat());
}
/*
* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -