📄 range.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 range.cpp - Implement the CTxtRange Class |
*
* This module implements the internal CTxtRange methods. See range2.c
* for the ITextRange methods
*
* Authors: <nl>
* Original RichEdit code: David R. Fulmer <nl>
* Christian Fortini <nl>
* Murray Sargent <nl>
*
* Revisions: <nl>
* AlexGo: update to runptr text ptr; floating ranges, multilevel undo
*
*/
#include "_common.h"
#include "_range.h"
#include "_edit.h"
#include "_text.h"
#include "_rtext.h"
#include "_m_undo.h"
#include "_antievt.h"
#include "_disp.h"
ASSERTDATA
TCHAR szEmbedding[] = {WCH_EMBEDDING, 0};
// =========================== Invariant stuff ======================================================
#define DEBUG_CLASSNAME CTxtRange
#include "_invar.h"
#ifdef DEBUG
BOOL
CTxtRange::Invariant( void ) const
{
LONG cpMin, cpMost;
LONG diff = GetRange(cpMin, cpMost);
Assert ( cpMin >= 0 );
Assert ( cpMin <= cpMost );
Assert ( cpMost <= GetTextLength() );
Assert ( cpMin != cpMost || cpMost <= (LONG)GetAdjustedTextLength());
static LONG numTests = 0;
numTests++; // how many times we've been called.
// make sure the selections are in range.
return CRchTxtPtr::Invariant();
}
#endif
CTxtRange::CTxtRange(CTxtEdit *ped, LONG cp, LONG cch) :
CRchTxtPtr(ped, cp),
_cch(0),
_iFormat(0),
_cRefs(0)
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CTxtRange::CTxtRange");
LONG cchText = GetAdjustedTextLength();
LONG cpOther = cp - cch; // Calculate cpOther with entry cp
_dwFlags = FALSE; // This range isn't a selection
_iFormat = -1; // Set up the default format, which
// doesn't get AddRefFormat'd
ValidateCp(cpOther); // Validate requested other end
cp = GetCp(); // Validated cp
if(cp == cpOther && cp > cchText) // IP cannot follow undeletable
cp = cpOther = SetCp(cchText); // EOP at end of story
_cch = cp - cpOther; // Store valid length
Update_iFormat(-1); // Choose _iFormat
CNotifyMgr *pnm = ped->GetNotifyMgr();
if( pnm )
pnm->Add( (ITxNotify *)this );
}
CTxtRange::CTxtRange(const CTxtRange &rg) :
CRchTxtPtr((CRchTxtPtr)rg),
_cch(0),
_iFormat(0),
_cRefs(0)
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CTxtRange::CTxtRange");
_cch = rg._cch;
_dwFlags = FALSE; // This range isn't a selection
_iFormat = -1; // Set up the default format, which
// doesn't get AddRefFormat'd
Set_iCF(rg._iFormat);
CNotifyMgr *pnm = GetPed()->GetNotifyMgr();
if( pnm )
pnm->Add( (ITxNotify *)this );
}
CTxtRange::~CTxtRange()
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CTxtRange::~CTxtRange");
if(!IsZombie())
{
CNotifyMgr *pnm = GetPed()->GetNotifyMgr();
if( pnm )
pnm->Remove( (ITxNotify *)this );
}
ReleaseFormats(_iFormat, -1);
}
CRchTxtPtr& CTxtRange::operator =(const CRchTxtPtr &rtp)
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CTxtRange::operator =");
_TEST_INVARIANT_ON(rtp)
LONG cpSave = GetCp(); // Save entry _cp for CheckChange()
CRchTxtPtr::operator =(rtp);
CheckChange(cpSave);
return *this;
}
CTxtRange& CTxtRange::operator =(const CTxtRange &rg)
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CTxtRange::operator =");
_TEST_INVARIANT_ON( rg );
LONG cchSave = _cch; // Save entry _cp, _cch for change check
LONG cpSave = GetCp();
CRchTxtPtr::operator =(rg);
_cch = rg._cch; // Can't use CheckChange(), since don't
// use _fExtend
Update_iFormat(-1);
_TEST_INVARIANT_
if( _fSel && (cpSave != GetCp() || cchSave != _cch) )
GetPed()->GetCallMgr()->SetSelectionChanged();
return *this;
}
/*
* CTxtRange::OnPreReplaceRange (cp, cchDel, cchNew, cpFormatMin,
* cpFormatMax)
*
* @mfunc
* called when the backing store changes
*
* @devnote
* 1) if this range is before the changes, do nothing
*
* 2) if the changes are before this range, simply
* add the delta change to GetCp()
*
* 3) if the changes overlap one end of the range, collapse
* that end to the edge of the modifications
*
* 4) if the changes are completely internal to the range,
* adjust _cch and/or GetCp() to reflect the new size. Note
* that two overlapping insertion points will be viewed as
* a 'completely internal' change.
*
* 5) if the changes overlap *both* ends of the range, collapse
* the range to cp
*
* Note that there is an ambiguous cp case; namely the changes
* occur *exactly* at a boundary. In this case, the type of
* range matters. If a range is normal, then the changes
* are assumed to fall within the range. If the range is
* is protected (either in reality or via DragDrop), then
* the changes are assumed to be *outside* of the range.
*/
void CTxtRange::OnPreReplaceRange (
DWORD cp, //@parm cp at start of change
DWORD cchDel, //@parm Count of chars deleted
DWORD cchNew, //@parm Count of chars inserted
DWORD cpFormatMin, //@parm the min cp of a format change
DWORD cpFormatMax) //@parm the max cp of a format change
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CTxtRange::OnPreReplaceRange");
if (CONVERT_TO_PLAIN == cp)
{
// We need to dump our formatting because it is gone.
_rpCF.SetToNull();
_rpPF.SetToNull();
if( _fSel )
{
GetPed()->_fUpdateSelection = TRUE;
}
Update_iFormat(-1);
return;
}
}
/*
* CTxtRange::OnPostReplaceRange (cp, cchDel, cchNew, cpFormatMin,
* cpFormatMax)
*
* @mfunc
* called when the backing store changes
*
* @devnote
* 1) if this range is before the changes, do nothing
*
* 2) if the changes are before this range, simply
* add the delta change to GetCp()
*
* 3) if the changes overlap one end of the range, collapse
* that end to the edge of the modifications
*
* 4) if the changes are completely internal to the range,
* adjust _cch and/or GetCp() to reflect the new size. Note
* that two overlapping insertion points will be viewed as
* a 'completely internal' change.
*
* 5) if the changes overlap *both* ends of the range, collapse
* the range to cp
*
* Note that there is an ambiguous cp case; namely the changes
* occur *exactly* at a boundary. In this case, the type of
* range matters. If a range is normal, then the changes
* are assumed to fall within the range. If the range is
* is protected (either in reality or via DragDrop), then
* the changes are assumed to be *outside* of the range.
*/
void CTxtRange::OnPostReplaceRange (
DWORD cp, //@parm cp at start of change
DWORD cchDel, //@parm Count of chars deleted
DWORD cchNew, //@parm Count of chars inserted
DWORD cpFormatMin, //@parm the min cp of a format change
DWORD cpFormatMax) //@parm the max cp of a format change
{
TRACEBEGIN(TRCSUBSYSRANG, TRCSCOPEINTERN, "CTxtRange::OnPostReplaceRange");
// NB!! We can't do invariant testing here, because we could
// be severely out of date!
DWORD cchtemp;
DWORD cpMin, cpMost;
LONG cchAdjTextLen;
LONG delta = cchNew - cchDel;
Assert (CONVERT_TO_PLAIN != cp);
GetRange((LONG&)cpMin, (LONG&)cpMost);
// This range is before the changes. Note: an insertion pt at cp
// shouldn't be changed
if( cp >= cpMost )
{
// double check to see if we need to fix up our format
// run pointers. If so, all we need to do is rebind
// our inherited rich text pointer
if( cpFormatMin <= cpMost || cpFormatMin == INFINITE)
{
InitRunPtrs(GetCp());
}
else
{
// It's possible that the format runs changed anyway,
// e.g., they became allocated, deallocated, or otherwise
// changed. Normally, BindToCp takes care of this
// situation, but we don't want to pay that cost all
// the time.
//
// Note that starting up the rich text subsystem will
// generate a notification with cpFormatMin == INFINITE
//
// So here, call CheckFormatRuns. This makes sure that
// the runs are in sync with what CTxtStory has
// (doing an InitRunPtrs() _only_ if absolutely necessary).
CheckFormatRuns();
}
return;
}
// Anywhere in the following that we want to increment the current cp by a
// delta, we are counting on the following invariant.
Assert(GetCp() >= 0);
// changes are entirely before this range. Specifically,
// that's determined by looking at the incoming cp *plus* the number
// of characters deleted
if( (cp + cchDel) < cpMin ||
(_fDragProtection == TRUE && (cp + cchDel) <= cpMin ))
{
cchtemp = _cch;
BindToCp(GetCp() + delta);
_cch = cchtemp;
}
// the changes are internal to the range or start within the
// range and go beyond.
else if( cp >= cpMin && cp <= cpMost )
{
// nobody should be modifying a drag-protected range. Unfortunately,
// Ren re-enters us with a SetText call during drag drop, so we need
// to handle this case 'gracefully'.
if( _fDragProtection )
{
TRACEWARNSZ("REENTERED during a DRAG DROP!! Trying to recover!");
}
if( cp + cchDel <= cpMost )
{
// changes are purely internal, so
// be sure to preserve the active end. Basically, if
// GetCp() *is* cpMin, then we only need to update _cch.
// Otherwise, GetCp() needs to be moved as well
if( _cch >= 0 )
{
Assert(GetCp() == (LONG)cpMost);
cchtemp = _cch;
BindToCp(GetCp() + delta);
_cch = cchtemp + delta;
}
else
{
BindToCp(GetCp());
_cch -= delta;
}
// Special case: the range is left with only the final EOP
// selected. This means all the characters in the range were
// deleted so we want to move the range back to an insertion
// point at the end of the text.
cchAdjTextLen = GetPed()->GetAdjustedTextLength();
if (GetCpMin() >= cchAdjTextLen)
{
// Reduce the range to an insertion point
_cch = 0;
_fExtend = FALSE;
// Set the cp to the end of the document.
SetCp(cchAdjTextLen);
}
}
else
{
// Changes extended beyond cpMost. In this case,
// we want to truncate cpMost to the *beginning* of
// the changes (i.e. cp)
if( _cch > 0 )
{
BindToCp(cp);
_cch = cp - cpMin;
}
else
{
BindToCp(cpMin);
_cch = cpMin - cp;
}
}
}
else if( (cp + cchDel) >= cpMost )
{
// nobody should be modifying a drag-protected range. Unfortunately,
// Ren re-enters us with a SetText call during drag drop, so we need
// to handle this case 'gracefully'.
if( _fDragProtection )
{
TRACEWARNSZ("REENTERED during a DRAG DROP!! Trying to recover!");
}
// entire range was deleted, so collapse to an insertion point at cp
BindToCp(cp);
_cch = 0;
}
else
{
// nobody should be modifying a drag-protected range. Unfortunately,
// Ren re-enters us with a SetText call during drag drop, so we need
// to handle this case 'gracefully'.
if( _fDragProtection )
{
TRACEWARNSZ("REENTERED during a DRAG DROP!! Trying to recover!");
}
// the change crossed over just cpMin. In this case move cpMin
// forward to the unchanged part
LONG cchdiff = (cp + cchDel) - cpMin;
Assert( (cp + cchDel) < cpMost );
Assert( (cp + cchDel) >= cpMin );
Assert( cp < cpMin );
cchtemp = _cch;
if( _cch > 0 )
{
BindToCp(GetCp() + delta);
_cch = cchtemp - cchdiff;
}
else
{
BindToCp(cp + cchNew);
_cch = cchtemp + cchdiff;
}
}
if( _fSel )
{
GetPed()->_fUpdateSelection = TRUE;
GetPed()->GetCallMgr()->SetSelectionChanged();
}
Update_iFormat(-1); // Make sure format is up to date
_TEST_INVARIANT_
}
/*
* CTxtRange::Zombie ()
*
* @mfunc
* Turn this range into a zombie (_cp = _cch = 0, NULL ped, ptrs to
* backing store arrays. CTxtRange methods like GetRange(),
* GetCpMost(), GetCpMin(), and GetTextLength() all work in zombie mode,
* returning zero values.
*/
void CTxtRange::Zombie()
{
CRchTxtPtr::Zombie();
_cch = 0;
}
/*
* CTxtRange::CheckChange(cpSave, cchSave)
*
* @mfunc
* Set _cch according to _fExtend and set selection-changed flag if
* this range is a CTxtSelection and the new _cp or _cch differ from
* cp and cch, respectively.
*
* @devnote
* We can count on GetCp() and cpSave both being <= GetTextLength(),
* but we can't leave GetCp() equal to GetTextLength() unless _cch ends
* up > 0.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -