📄 antievt.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 ANTIEVT.C |
*
* Purpose:
* implemenation of common anti-events and a caching mechanism
*
* Author:
* alexgo 3/25/95
*/
#include "_common.h"
#include "_m_undo.h"
#include "_antievt.h"
#include "_edit.h"
#include "_range.h"
#include "_select.h"
#include "_format.h"
#include "_coleobj.h"
#include "_objmgr.h"
ASSERTDATA
//
// CAntiEventDispenser global instance
//
CAntiEventDispenser gAEDispenser;
//
// CBaseAE PUBLIC methods
//
/*
* CBaseAE::Destroy (void)
*
* @mfunc
* sends the Destroy notification to the next anti-event in the list
*/
void CBaseAE::Destroy( void )
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CBaseAE::Destroy");
;
}
/*
* CBaseAE::Undo (ped)
*
* @mfunc
* sends the Undo notification to the next anti-event in the list
*
* @rdesc HRESULT
*/
HRESULT CBaseAE::Undo(
CTxtEdit *ped, //@parm the context for this undo operation
IUndoBuilder *publdr) //@parm the undo context.
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CBaseAE::Undo");
return NOERROR;
}
/*
* CBaseAE::OnCommit
*
* @mfunc called after the anti-event is added to the undo stack
*
* @rdesc void
*/
void CBaseAE::OnCommit(
CTxtEdit *ped) //@parm the edit context
{
;
}
/*
* CBaseAE::MergeData
*
* @mfunc simply forwards the merge data request to the next anti-evt
* (if one exists)
*
* @rdesc HRESULT. If S_FALSE, indicates that nothing could be done
* with the merge data.
*/
HRESULT CBaseAE::MergeData(
DWORD dwDataType, //@parm the type of data in <p pdata>
void *pdata) //@parm the merge data
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CBaseAE::MergeData");
return S_FALSE;
}
/*
* CBaseAE::SetNext
*
* @mfunc
* informs this anti-event of the anti-event which should follow it
*/
void CBaseAE::SetNext(
IAntiEvent *pNext ) //@parm the AntiEvent to link to
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CBaseAE::SetNext");
_pnext = pNext;
}
/*
* CBaseAE::GetNext
*
* @mfunc
* retrieves the next element (if any)
*
* @rdesc a pointer to the next AntiEvent
*/
IAntiEvent *CBaseAE::GetNext( void )
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CBaseAE::GetNext");
return _pnext;
}
//
// CBaseAE PROTECTED methods
//
/*
* CBaseAE::CBaseAE
*
* @mfunc Constructor
*/
CBaseAE::CBaseAE()
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CBaseAE::CBaseAE");
_pnext = NULL;
}
/*
* CReplaceRangeAE::Destroy
*
* @mfunc
* deletes this instance
*/
void CReplaceRangeAE::Destroy( void )
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CReplaceRangeAE::Destroy");
if( _paeCF )
{
_paeCF->Destroy();
_paeCF = NULL;
}
if( _paePF )
{
_paePF->Destroy();
_paePF = NULL;
}
CBaseAE::Destroy();
delete this;
}
/*
* CReplaceRangeAE::Undo (ped)
*
* @mfunc
* undoes a CTxtPtr::ReplaceRange operation
*
* @comm
* Algorithm:
*
* A replace range works as follows: delete n characters and in their
* place, put m characters.
*
* To undo this, we delete m characters and restore the n that were
* originally deleted. Note that we restore the n characters with
* default formatting. If there was any other formatting to those
* characters, a separate anti-event (CReplaceFormattingAE) will
* apply the correct formatting.
*/
HRESULT CReplaceRangeAE::Undo(
CTxtEdit *ped, //@parm the context for this undo operation
IUndoBuilder *publdr) //@parm the undo context
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CReplaceRangeAE::Undo");
CRchTxtPtr rtp(ped, _cpMin);
CTxtSelection *psel = ped->GetSel();
#ifdef PWD_JUPITER // GuyBark Jupiter 18411: Don't attempt to merge events during an undo
publdr->_bNoAttemptToMerge = TRUE;
#endif // PWD_JUPITER
rtp.ReplaceRange(_cpMax - _cpMin, _cchDel, _pchDel,
publdr, -1);
#ifdef PWD_JUPITER // GuyBark Jupiter 18411: Don't attempt to merge events during an undo
publdr->_bNoAttemptToMerge = FALSE;
#endif // PWD_JUPITER
// passing NULL for the publdr is *extremely* important
// below. The rich text pointer ReplaceRange call will
// already generate the appropriate anti-events for any
// deleted formatting, so we do not need to repeat that here.
if( _paeCF )
{
_paeCF->Undo(ped, NULL);
}
if( _paePF )
{
_paePF->Undo(ped, NULL);
}
return CBaseAE::Undo(ped, publdr);
}
/*
* CReplaceRangeAE::MergeData (dwDataType, pdata)
*
* @mfunc gives the caller a chance to extend the current anti-event
* if we're in merge typing mode
*
* @comm if the requested data can be trivially merged into this
* anti-event, then do so; otherwise, return S_FALSE.
*
* There are two cases of interest: <nl>
* 1. typing another character
* 2. backspacing over a character in this merge
* typing session.
*/
HRESULT CReplaceRangeAE::MergeData(
DWORD dwDataType, //@parm the type of <p pdata>
void *pdata ) //@parm the merge data
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CReplaceRangeAE::MergeData");
if( dwDataType == MD_SIMPLE_REPLACERANGE )
{
SimpleReplaceRange *psr = (SimpleReplaceRange *)pdata;
// test for the first case, just typing another character
if( psr->cpMin == _cpMax && psr->cchDel == 0 )
{
_cpMax = psr->cpMax;
return NOERROR;
}
// the second case, deleting text stored in this antievent
else if( psr->cpMax == psr->cpMin &&
psr->cpMin + psr->cchDel == _cpMax &&
psr->cpMin >= _cpMin )
{
_cpMax = psr->cpMax;
return NOERROR;
}
}
return S_FALSE;
}
/*
* CReplaceRangeAE::CReplaceRangeAE (cpMin, cpMax)
*
* @mfunc Constructor for a text replace range anti-event
*
*/
CReplaceRangeAE::CReplaceRangeAE(
DWORD cpMin, //@parm the cp starting the *final* range
DWORD cpMax, //@parm the cp ending the *final* range
DWORD cchDel, //@parm # of chars deleted during ReplaceRange
TCHAR *pchDel, //@parm the deleted characters. Ownership of
// the memory is transferred to this object.
IAntiEvent *paeCF, //@parm the anti-event for any character formatting
// replacement
IAntiEvent *paePF) //@parm the anti-event for any paragraph formatting
// replacement
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CReplaceRangeAE::CReplaceRangeAE");
_cpMin = cpMin;
_cpMax = cpMax;
_cchDel = cchDel;
_pchDel = pchDel;
_paeCF = paeCF;
_paePF = paePF;
}
/*
* CReplaceRangeAE::~CReplaceRangeAE
*
* @mfunc Destructor
*/
CReplaceRangeAE::~CReplaceRangeAE()
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CReplaceRangeAE::~CReplaceRangeAE");
if( _pchDel )
{
delete _pchDel;
}
}
/*
* CReplaceFormattingAE
*
* @mfunc Destroys this instance
*/
void CReplaceFormattingAE::Destroy()
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CReplaceFormattingAE::Destroy");
CBaseAE::Destroy();
delete this;
}
/*
* CReplaceFormattingAE::Undo
*
* @mfunc Undoes a formatting operation
*
* @devnote This anti-event assumes that the text to which formatting
* should be applied exists!!
*/
HRESULT CReplaceFormattingAE::Undo(
CTxtEdit *ped, //@parm The CTxtEdit closure
IUndoBuilder *publdr) //@parm the undo builder context
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CReplaceFormattingAE::Undo");
HRESULT hr;
DWORD i;
IFormatCache *pf;
DWORD cchEaten;
DWORD iRunMerge;
CNotifyMgr *pnm;
DWORD cchTotal = 0;
CFormatRuns **ppfmtruns;
CTxtStory *pStory = ped->GetTxtStory();
IAntiEvent *pae;
LONG delta;
// first, set things up correctly for whether we are paragraph
// or character formatting
CFormatRunPtr rp(_fPara ? pStory->GetPFRuns() :pStory->GetCFRuns());
if( _fPara )
{
hr = GetParaFormatCache((IParaFormatCache **)&pf);
}
else
{
hr = GetCharFormatCache((ICharFormatCache **)&pf);
}
if( hr != NOERROR )
{
return hr;
}
// first, count up the total number of characters affected
for( i = 0 ; i < _cRuns; i++ )
{
cchTotal += _prgRuns[i]._cch;
}
pnm = ped->GetNotifyMgr();
// we are going to be adding in some runs, so be sure the format
// run array is allocated!
if( !rp.IsValid() )
{
if( _fPara )
{
ppfmtruns = &(pStory->_pPFRuns);
}
else
{
ppfmtruns = &(pStory->_pCFRuns);
}
if( !rp.InitRuns(0, ped->GetTextLength(), ppfmtruns) )
{
return E_OUTOFMEMORY;
}
// tell folks we allocated a new run
if( pnm )
{
pnm->NotifyPostReplaceRange(NULL, INFINITE, 0, 0, INFINITE, INFINITE);
}
}
// now do a pre-notification of the change we are about to make
// This let's objects like a delayed render data object grab
// any data *before* we change it.
rp.BindToCp(_cp);
// do a little more checking
Assert(rp.GetCch() == ped->GetTextLength());
if( pnm )
{
pnm->NotifyPreReplaceRange(NULL, INFINITE, 0, 0, _cp, _cp + cchTotal );
}
// we want to merge runs with where we start plus one behind.
iRunMerge = rp._iRun;
if( iRunMerge > 0 )
iRunMerge--;
// if we need to be able to undo this opertion, go through and
// save existing run information
if( publdr )
{
pae = gAEDispenser.CreateReplaceFormattingAE(ped, rp, cchTotal,
pf, _fPara);
if( pae )
{
publdr->AddAntiEvent(pae);
}
}
// now go through and apply the saved formatting.
for( i = 0; i < _cRuns; i++ )
{
cchEaten = 0;
// use a do-while, because we may have a zero-length
// format run. We know we need to do "something" at
// least once, because otherwise, we would not have
// bothered creating a run!
do
{
delta = rp.SetFormat(_prgRuns[i]._iFormat,
_prgRuns[i]._cch - cchEaten, pf);
if( delta == -1 )
{
ped->GetCallMgr()->SetOutOfMemory();
break;
}
cchEaten += delta;
} while( cchEaten < _prgRuns[i]._cch );
}
// merge formatting runs in case there are duplicate formatting
// runs side by side
rp.NextRun();
rp.MergeRuns(iRunMerge, pf);
// make sure the runs are still OK.
Assert(rp.GetCch() == ped->GetTextLength());
if( pnm )
{
pnm->NotifyPostReplaceRange(NULL, INFINITE, 0, 0, _cp, _cp + cchTotal);
}
ped->GetCallMgr()->SetChangeEvent(CN_GENERIC);
return CBaseAE::Undo(ped, publdr);
}
/*
* CReplaceFormattingAE::CReplaceFormattingAE
*
* @mfunc Constructor. During construction, we will loop through and
* find all of the formats for the given text range
*/
CReplaceFormattingAE::CReplaceFormattingAE(
CFormatRunPtr &rpIn, //@parm the run pointer to start with
DWORD cch, //@parm the number of characters to
//find formatting info on
IFormatCache *pf, //@parm the format cache (to AddRef/
//Release formats)
BOOL fPara) //@parm If TRUE, the the formatting is
//for paragraphs
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CReplaceFormattingAE::CReplaceFormattingAE");
LONG cchtemp = (LONG)cch;
DWORD i;
DWORD cchLeft;
CFormatRunPtr rp(rpIn); //we use 2 format run pointers to avoid
CFormatRunPtr rpTemp(rpIn); //backing up after counting the number of
//format runs
Assert(pf);
_cp = rpIn.GetCp();
_fPara = fPara;
// count the number of formats needed. Recall that even
// if 0 characters are to be deleted, we may still be
// "deleting" a zero length format run.
_cRuns = 0;
do
{
_cRuns++;
cchLeft = rp.GetCchLeft();
cchtemp -= min((LONG)cchLeft, cchtemp);
rp.NextRun();
} while( cchtemp > 0 );
_prgRuns = new CFormatRun[_cRuns];
if( !_prgRuns )
{
_cRuns = 0;
return;
}
for( i = 0; i < _cRuns; i++ )
{
_prgRuns[i]._cch = min( cch, rpTemp.GetCchLeft());
_prgRuns[i]._iFormat = rpTemp.GetRun(0)->_iFormat;
pf->AddRefFormat(_prgRuns[i]._iFormat);
rpTemp.NextRun();
cch -= _prgRuns[i]._cch;
}
Assert(cch == 0);
return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -