📄 m_undo.cpp
字号:
{
j = _dwLim - 1;
}
else
{
j--;
}
}
return UID_UNKNOWN;
}
/*
* CUndoStack::GetMergeAntiEvent (void)
*
* @mfunc If we are in merge typing mode, then return the topmost
* anti-event
*
* @rdesc NULL or the current AntiEvent if in merge mode
*/
IAntiEvent *CUndoStack::GetMergeAntiEvent(void)
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::GetMergeAntiEvent");
DWORD i;
if( _fMerge == TRUE )
{
i = GetPrev(); // _index by default points to the next available
// slot
Assert(_prgActions[i].pae); //can't be in merge anti event mode
//if there is no anti-event to merge
//with!!!
return _prgActions[i].pae;
}
return NULL;
}
/*
* CUndoStack::GetTopAECookie
*
* @mfunc Returns a cookie to the topmost anti-event.
*
* @rdesc A cookie value. Note that this cookie is just the anti-event
* pointer, but clients shouldn't really know that.
*/
DWORD CUndoStack::GetTopAECookie( void )
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::GetTopAECookie");
DWORD i = GetPrev();
return (DWORD)_prgActions[i].pae;
}
/*
* CUndoStack::ClearAll ()
*
* @mfunc
* removes any anti-events that are currently in the undo stack
*/
void CUndoStack::ClearAll( void )
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::ClearAll");
DWORD i;
for( i = 0; i < _dwLim; i++ )
{
if( _prgActions[i].pae != NULL )
{
DestroyAEList(_prgActions[i].pae);
_prgActions[i].pae = NULL;
}
}
// just in case we've been grouping typing; clear the
// state.
StopGroupTyping();
}
/*
* CUndoStack::CanUndo
*
* @mfunc
* indicates whether or not can undo operation can be performed
* (in other words, are there any anti-events in our buffer)
*
* @rdesc
* TRUE -- anti-events exist <nl>
* FALSE -- no anti-events <nl>
*/
BOOL CUndoStack::CanUndo()
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::CanUndo");
DWORD i = GetPrev(); // _index by default points to the next available
// slot
if( _prgActions[i].pae != NULL )
{
return TRUE;
}
else if( _fSingleLevelMode )
{
// if fSingleLevelMode is on, we can't be the redo stack
Assert(_fRedo == FALSE);
// if we are in single level mode, we are the undo stack. Check to
// see if the redo stack can do something here.
if( _ped->GetRedoMgr() )
{
return _ped->GetRedoMgr()->CanUndo();
}
}
return FALSE;
}
/*
* CUndoStack::StartGroupTyping
*
* @mfunc
* TOGGLES the group typing flag on. If fGroupTyping is set, then
* all *typing* events will be merged together
*
* @comm
* Algorithm:
*
* There are three interesting states: <nl>
* -no group merge; every action just gets pushed onto the stack <nl>
* -group merge started; the first action is pushed onto the stack<nl>
* -group merge in progress; every action (as long as it's "typing")
* is merged into the prior state <nl>
*
* See the state diagram in the implemenation doc for more details
*/
void CUndoStack::StartGroupTyping()
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::StartGroupTyping");
if( _fGroupTyping == TRUE )
{
_fMerge = TRUE;
}
else
{
Assert(_fMerge == FALSE);
_fGroupTyping = TRUE;
}
}
/*
* CUndoStack::StopGroupTyping
*
* @mfunc
* TOGGLES the group typing flag off. If fGroupTyping is not set,
* then no merging of typing anti-events will be done
*/
void CUndoStack::StopGroupTyping()
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::StopGroupTyping");
_fGroupTyping = FALSE;
_fMerge = FALSE;
}
/*
* CUndoStack::EnableSingleLevelMode
*
* @mfunc Turns on single level undo mode; in this mode, we behave just like
* RichEdit 1.0 w.r.t. to Undo.
*
* @comm This special mode means that undo is 1 level deep and everything
* is accessed via UNDO messages. Thus, instead of redo to undo an
* undo action, you simply use another undo message.
*
* @devnote This call is _ONLY_ allowed for the UndoStack; the redo
* stack simply tags along. Note that caller is responsible for
* ensuring that we are in an empty state.
*/
HRESULT CUndoStack::EnableSingleLevelMode()
{
Assert(_ped->GetRedoMgr() == NULL ||
_ped->GetRedoMgr()->CanUndo() == FALSE);
Assert(CanUndo() == FALSE);
Assert(_fRedo == FALSE);
_fSingleLevelMode = TRUE;
// for single level undo mode, it is very important to get
// just 1 entry in the undo stack. If we can't do that,
// then we better just fail.
if( SetUndoLimit(1) != 1 )
{
_fSingleLevelMode = FALSE;
return E_OUTOFMEMORY;
}
if( _ped->GetRedoMgr() )
{
// doesn't matter if the redo manager fails to reset
_ped->GetRedoMgr()->SetUndoLimit(1);
}
return NOERROR;
}
/*
* CUndoStack::DisableSingleLevelMode
*
* @mfunc This turns off the 1.0 undo compatibility mode and restores us to
* the RichEdit 2.0 default undo state
*/
void CUndoStack::DisableSingleLevelMode()
{
Assert(_ped->GetRedoMgr() == NULL ||
_ped->GetRedoMgr()->CanUndo() == FALSE);
Assert(CanUndo() == FALSE);
Assert(_fRedo == FALSE);
_fSingleLevelMode = FALSE;
// we don't care about failures here; multi-level undo mode
// can handle any sized undo stack
SetUndoLimit(DEFAULT_UNDO_SIZE);
if( _ped->GetRedoMgr() )
{
// doesn't matter if the redo manager can't grow back in
// size; it just means that we won't have full redo capability.
_ped->GetRedoMgr()->SetUndoLimit(DEFAULT_UNDO_SIZE);
}
}
//
// PRIVATE METHODS
//
/*
* CUndoStack::Next
*
* @mfunc
* sets _index to the next available slot
*/
void CUndoStack::Next( void )
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::Next");
_index++;
if( _index == _dwLim )
{
_index = 0;
}
}
/*
* CUndoStack::Prev
*
* @mfunc
* sets _index to the previous slot
*/
void CUndoStack::Prev( void )
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::Prev");
_index = GetPrev();
}
/*
* CUndoStack::GetPrev
*
* @mfunc
* figures out what the index to the previous slot
* *should* be (but does not set it)
*
* @rdesc the index of what the previous slot would be
*/
DWORD CUndoStack::GetPrev( void )
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::GetPrev");
DWORD i = _index;
if( i == 0 )
{
i = _dwLim - 1;
}
else
{
i--;
}
return i;
}
/*
* CUndoStack::IsCookieInList
*
* @mfunc
* determines whether or not the given DoTo cookie is in
* the list of anti-events.
*
* @rdesc TRUE/FALSE
*/
BOOL CUndoStack::IsCookieInList(
IAntiEvent *pae, //@parm the list to check
IAntiEvent *paeCookie) //@parm the cookie to check
{
while( pae )
{
if( pae == paeCookie )
{
return TRUE;
}
pae = pae->GetNext();
}
return FALSE;
}
/*
* CUndoStack::TransferToNewBuffer
*
* @mfunc
* transfers existing anti-events to the given buffer and
* swaps this undo stack to use the new buffer
*
* @rdesc void
*
* @comm The algorithm is very straightforward; go backwards in
* the ring buffer copying antievents over until either there
* are no more anti-events or the new buffer is full. Discard
* any remaining anti-events.
*/
void CUndoStack::TransferToNewBuffer(UndoAction *prgnew, DWORD dwLimNew)
{
TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::TransferToNewBuffer");
DWORD iOld = 0,
iNew = 0,
iCopyStart = 0;
// first, clear the new buffer.
memset(prgnew, 0, dwLimNew * sizeof(UndoAction));
// if there is nothing to copy, don't bother
if( NULL == _prgActions || NULL == _prgActions[GetPrev()].pae )
{
goto SetState;
}
// this is a bit counter-intuitive, but since the stack is really
// a ring buffer, go *forwards* until you hit a non-NULL slot.
// This will be the _end_ of the existing antievents.
//
// However, we need to make sure that if dwLimNew is
// _smaller_ than _dwLim we only copy the final dwLimNew
// anti-events. We'll set iCopyStart to indicate when
// we can start copying stuff.
if( dwLimNew < _dwLim )
{
iCopyStart = _dwLim - dwLimNew;
}
for(; iOld < _dwLim; iOld++, Next() )
{
if( NULL == _prgActions[_index].pae )
{
continue;
}
else if( iOld >= iCopyStart )
{
Assert(iNew < dwLimNew );
// copy anti-events over
prgnew[iNew] = _prgActions[_index];
iNew++;
}
else
{
// otherwise, get rid of them
DestroyAEList(_prgActions[_index].pae);
_prgActions[_index].pae = NULL;
}
}
SetState:
//we start at index iNew
_index = (iNew == dwLimNew ) ? 0 : iNew;
Assert(iNew <= dwLimNew);
_dwLim = dwLimNew;
if( _prgActions )
{
delete _prgActions;
}
_prgActions = prgnew;
}
//
// CGenUndoBuilder implementation
//
//
// Public methods
//
/*
* CGenUndoBuilder::CGenUndoBuilder (pstack)
*
* @mfunc Constructor
*
* @comm
* This is a *PUBLIC* constructor
*
*/
CGenUndoBuilder::CGenUndoBuilder(
CTxtEdit *ped, //@parm the overall context
DWORD flags) : //@parm flags (usually UB_AUTOCOMMIT)
_fAutoCommit(1),
_pfirstae(NULL)
{
CompName name = COMP_UNDOBUILDER;
CCallMgr * pcallmgr = ped->GetCallMgr();
_idName = UID_UNKNOWN;
_ped = ped;
_pfirstae = NULL;
_fStartGroupTyping = FALSE;
_fRedo = FALSE;
_fDontFlushRedo = FALSE;
if( (flags & UB_AUTOCOMMIT) )
{
_fAutoCommit = TRUE;
}
if( (flags & UB_REDO) )
{
_fRedo = TRUE;
name = COMP_REDOBUILDER;
_pundo = ped->GetRedoMgr();
}
else
{
_pundo = ped->GetUndoMgr();
}
if( (flags & UB_DONTFLUSHREDO) )
{
_fDontFlushRedo = TRUE;
}
#ifdef PWD_JUPITER // GuyBark Jupiter 18411: Don't attempt to merge events during an undo
_bNoAttemptToMerge = FALSE;
#endif // PWD_JUPITER
// now link ourselves to any undobuilders that are higher up on
// the stack. Note that is is legal for multiple undo builders
// to live within the same call context.
_publdrPrev = (CGenUndoBuilder *)_ped->GetCallMgr()->GetComponent(name);
// If we are in the middle of an undo, then we'll have two undo stacks
// active, the undo stack and the redo stack. Don't like the two
// together.
if( _fDontFlushRedo )
{
_publdrPrev = NULL;
}
_ped->GetCallMgr()->RegisterComponent((IReEntrantComponent *)this,
name);
}
/*
* CGenUndoBuilder::~CGenUndoBuilder
*
* @mfunc Destructor
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -