⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 m_undo.cpp

📁 Windows CE 6.0 Word Application 源码
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
// 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	M_UNDO.C	|
 *
 *	Purpose:
 *		Implementation of the global mutli-undo stack
 *
 * 	Author:
 *		alexgo  3/25/95
 */

#include "_common.h"
#include "_m_undo.h"
#include "_edit.h"
#include "_disp.h"
#include "_urlsup.h"
#include "_antievt.h"

ASSERTDATA

//
// PUBLIC METHODS
//

/*
 *	CUndoStack::CUndoStack (ped)
 *
 *	@mfunc	Constructor
 */
CUndoStack::CUndoStack( 
	CTxtEdit *ped,		//@parm	the CTxtEdit parent
	DWORD & rdwLim,		//@parm the initial limit
	USFlags	flags )		//@parm flags for this undo stack
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::CUndoStack");

	_ped = ped;

	_prgActions = NULL;
	_index = 0;
	_dwLim = 0;

	//we should be creating an undo stack if there's nothing to
	//put in it!
	Assert(rdwLim);
	SetUndoLimit(rdwLim);

	if( flags & US_REDO )
	{
		_fRedo = TRUE;
	}
}

/*
 *	CUndoStack::~CUndoStack
 *
 *	@mfunc Destructor
 *
 *	@comm
 *		deletes any remaining anti-events.  The anti event dispenser
 *		should *not* clean up because of this!!
 */
CUndoStack::~CUndoStack()
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::~CUndoStack");

	// clear out any remaining antievents
	ClearAll();

	delete _prgActions;
}

/*
 *	CUndoStack::Destroy 
 *
 *	@mfunc
 *		deletes this instance
 */
void CUndoStack::Destroy()
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::Destroy");

	delete this;
}

/*
 * 	CUndoStack::SetUndoLimit (dwLim)
 *
 *	@mfunc
 *		allows the undo stack to be enlarged or reduced
 *
 *	@rdesc
 *		the size to which the stack is actually set.
 *
 *	@comm
 *		the algorithm we use is the following:	 <nl>
 *			try to allocate space for the requested size.
 *			if there's not enough memory then we try to recover
 *			with the largest block possible.
 *
 *			if the requested size is bigger than the default,
 *			and the current size is less than the default, go 
 *			ahead and try to allocate the default.
 *
 *			if that fails then just stick with the existing stack
 */
DWORD CUndoStack::SetUndoLimit( 
	DWORD dwLim )			//@parm	the new undo limit.  May not
							// be zero
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::SetUndoLimit");

	UndoAction *prgnew = NULL;

	// if the undo limit is zero, we should get rid of the entire
	// undo stack instead.

	Assert(dwLim);

	if( _fSingleLevelMode )
	{
		// if fSingleLevelMode is on, we can't be the redo stack
		Assert(_fRedo == FALSE);

		if( dwLim != 1 )
		{
			TRACEERRORSZ("Trying to grow/shrink the undo buffer while in"
				"single level mode");
			
			dwLim = 1;
		}
	}

	prgnew = new UndoAction[dwLim];

	if( prgnew )
	{
		TransferToNewBuffer(prgnew, dwLim);
	}
	else if( dwLim > DEFAULT_UNDO_SIZE && _dwLim < DEFAULT_UNDO_SIZE )
	{
		// we are trying to grow past the default but failed.  So
		// try to allocate the default
		prgnew = new UndoAction[DEFAULT_UNDO_SIZE];

		if( prgnew )
		{
			TransferToNewBuffer(prgnew, DEFAULT_UNDO_SIZE);
		}
	}
	
	// in either success or failure, _dwLim will be set correctly.	
	return _dwLim;
}

/*
 *	CUndoStack::GetUndoLimit 
 *
 *	@mfunc
 *		gets the current limit size
 *
 *	@rdesc	
 *		the current undo limit
 */
DWORD CUndoStack::GetUndoLimit()
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::GetUndoLimit");

	return _dwLim;
}

/*
 *	CUndoStack::PushAntiEvent (pae)
 *
 *	@mfunc
 *		adds an undoable event to the event stack
 *
 *	@rdesc	HRESULT
 *
 *	@comm
 *	Algorithm:
 *
 *		if merging is set, then we to merge the given anti-event
 *		list *into* the current list (assuming it's a typing
 *		undo action).
 *
 */
HRESULT CUndoStack::PushAntiEvent( 
	UNDONAMEID idName,		//@parm	name for this AE collection
	IAntiEvent *pae )		//@parm the AE collection
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::PushAntiEvent");

	// Fix for WinCEOS RAID #12736
	if (_ped->_DeleteBeforeConvert == 2) {
		_ped->_DeleteBeforeConvert = 0;
		idName = UID_TYPING;
		_fMerge = TRUE;
	}
	if (_ped->_DeleteBeforeConvert == 1) {
		_ped->_DeleteBeforeConvert = 2;
		idName = UID_TYPING;
	}

	// _index should be at next available position

	if( !_fMerge )
	{
		// clear out any existing event	
		if( _prgActions[_index].pae != NULL )
		{
			DestroyAEList(_prgActions[_index].pae);
			_prgActions[_index].pae = NULL;
		}

		if( _fRedo )
		{
			_ped->GetCallMgr()->SetNewRedo();
		}
		else
		{
			_ped->GetCallMgr()->SetNewUndo();
		}
	}

	if( _fMerge )
	{
		IAntiEvent *paetemp = pae, *paeNext;
		DWORD i = GetPrev();

		// if these asserts fail, then somebody did not call 
		// StopGroupTyping

		Assert(_prgActions[i].id == idName);
		Assert(idName == UID_TYPING);

		// put the existing anti-event chain onto the *end* of the
		// current one.

		while( (paeNext = paetemp->GetNext()) != NULL )
		{
			paetemp = paeNext;
		}

		paetemp->SetNext(_prgActions[i].pae);
		
		_index = i;

		// fall through!
	}
	else if( _fGroupTyping )
	{
		// in this case, we are *starting* a group typing session.
		// Any subsequent push'es of anti events should be merged
		_fMerge = TRUE;

		// fall through!!
	}

	_prgActions[_index].pae = pae;
	_prgActions[_index].id = idName;
	
	Next();


	return NOERROR;
}

/*
 *	CUndoStack::PopAndExecuteAntiEvent( void )
 *
 *	@mfunc
 *		Undo!  Takes the most recent anti-event and executes it
 *
 *	@rdesc	HRESULT from invoking the anti-events (AEs)
 */

HRESULT CUndoStack::PopAndExecuteAntiEvent( 
	DWORD dwDoToCookie)		//@parm if non-NULL, undo up to this point.
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::PopAndExecuteAntiEvent");

	HRESULT hresult = NOERROR;
	IAntiEvent *pae, *paeDoTo;
	DWORD i, j;
	CCallMgr *pcallmgr = _ped->GetCallMgr();

	// we need to check to see if there are any non-empty undo builders
	// higher on the stack.  In this case, we have been re-entered
	if( pcallmgr->IsReEntered() )
	{
		IUndoBuilder *publdr;

		// there are two cases to handle: we are invoking redo or we
		// are invoking undo.  If we are invoking undo and there are 
		// existing undo actions in the undo builder, then simply commit
		// those actions and undo them.  We can assert in this case
		// that the redo stack is empty.
		//
		// In the second case if we are invoking redo while there are
		// undo actions in progress, simply cancel the call.  When the
		// undo actions are added, they will clear the redo stack.
		// 
		// We never need to check for a redo builder as that _only_
		// gets created in this routine and it's use is carefully guarded.


		publdr = (CGenUndoBuilder *)pcallmgr->GetComponent(COMP_UNDOBUILDER);

		
		// commit the anti-events to this undo stack, so that we will simply
		// undo them first.
		if( publdr )
		{			
			TRACEWARNSZ("Undo/Redo Invoked with uncommitted anti-events");
			TRACEWARNSZ("		Recovering....");

			if( _fRedo )
			{
				// if we are the redo stack, simply fail the redo call
				return NOERROR;
			}
			else
			{
				// just commit the anti-events and the routine below
				// will take of the rest.
				publdr->Done();
			}
		}
	}

	// if we are in single level mode, check to see if our current buffer is
	// empty.  If so, simply delegate to the redo stack if it exists.  We only
	// support this mode for dwDoToCookies being NULL.  Note that we can't call
	// CanUndo here as it will consider the redo stack as well

	if( _fSingleLevelMode && !_prgActions[GetPrev()].pae )
	{
		Assert(_fRedo == FALSE);
		Assert(dwDoToCookie == 0);

		if( _ped->GetRedoMgr() )
		{
			return _ped->GetRedoMgr()->PopAndExecuteAntiEvent(0);
		}
		// nothing to redo && nothing to do here; don't bother continuing	
		return NOERROR;
	}

	// this next bit of logic is tricky.  What is says is create
	// an undo builder for the stack *opposite* of the current one
	// (namely, undo actions go on the redo stack and vice versa).
	// Also, if we are the redo stack, then we don't want to flush
	// the redo stack as anti-events are added to the undo stack.

	CGenUndoBuilder undobldr(_ped, 
					(!_fRedo ? UB_REDO : UB_DONTFLUSHREDO) | UB_AUTOCOMMIT);
					
	// obviously, we can't be grouping typing if we're undoing!
	StopGroupTyping();

	// _index by default points to the next available slot
	// so we need to back up to the previous one.
	Prev();

	// do some verification on the cookie--make sure it's one
	// of ours
	paeDoTo = (IAntiEvent *)dwDoToCookie;

	if( paeDoTo != NULL )
	{
		for( i = 0, j = _index; i < _dwLim; i++ )
		{
			if( IsCookieInList(_prgActions[j].pae, (IAntiEvent *)paeDoTo) )
			{
				paeDoTo = _prgActions[j].pae;
				break;
			}
			// go backwards through the ring buffer; typically
			// paeDoTo will be "close" to the top
			
			if( !j )
			{
				j = _dwLim - 1;
			}
			else
			{
				j--;
			}
		}
		
		if( i == _dwLim )
		{
			TRACEERRORSZ("Invalid Cookie passed into Undo; cookie ignored");
			hresult = E_INVALIDARG;
			paeDoTo = NULL;
		}
	}
	else
	{
		paeDoTo = _prgActions[_index].pae;
	}

	undobldr.SetNameID(_prgActions[_index].id);


	while( paeDoTo )
	{
		CUndoStackGuard guard(_ped);

		pae = _prgActions[_index].pae;
		Assert(pae);

		// fixup our state _before_ calling Undo, so 
		// that we can handle being re-entered.
		_prgActions[_index].pae = NULL;

		hresult = guard.SafeUndo(pae, &undobldr);

		DestroyAEList(pae);

		if( pae == paeDoTo || guard.WasReEntered() )
		{
			paeDoTo = NULL;
		}
		
		Prev();
	}

	// put _index at the next unused slot
	Next();

	return hresult;
}

/* 
 *	CUndoStack::GetNameIDFromTopAE
 *
 *	@mfunc
 *		retrieves the name of the most recent undo-able operation
 *
 *	@rdesc	the name ID of the most recent collection of anti-events
 */
UNDONAMEID CUndoStack::GetNameIDFromAE( 
	DWORD dwAECookie)		//@parm the anti-event whose name is desired;
							// 0 for the the top
{
	IAntiEvent *pae = (IAntiEvent *)dwAECookie;
	DWORD	i, j = GetPrev();	// _index by default points to the next available
							// slot

	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CUndoStack::GetNameIDFromTopAE");

	if( pae == NULL )
	{
		pae = _prgActions[j].pae;
	}

	if( _fSingleLevelMode && !pae)
	{
		// if fSingleLevelMode is on, we can't be the redo stack
		Assert(_fRedo == FALSE);

		// if pae is NULL, our answer may be on the redo stack.  Note that
		// we if somebody tries to pass in a cookie while in SingleLevelMode,
		// they won't be able to get actions off the redo stack.
		if( _ped->GetRedoMgr() )
		{
			return _ped->GetRedoMgr()->GetNameIDFromAE(0);
		}
	}		

	for( i = 0; i < _dwLim; i++ )
	{
		if( _prgActions[j].pae == pae )
		{
			return _prgActions[j].id;
		}

		if( j == 0 )

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -