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

📄 m_undo.cpp

📁 Windows CE 6.0 Word Application 源码
💻 CPP
📖 第 1 页 / 共 3 页
字号:
 *
 *	@comm
 *		This is a *PUBLIC* destructor
 *
 *	Algorithm:
 *		If this builder hasn't been committed to an undo stack
 *		via ::Done, then we must be sure to free up any resources
 *		(antievents) we may be hanging onto
 *
 */

CGenUndoBuilder::~CGenUndoBuilder( )
{
	_ped->GetCallMgr()->RevokeComponent((IReEntrantComponent *)this);

	if( _fAutoCommit == TRUE )
	{
		Done();
		return;
	}

	// free resources
	if( _pfirstae )
	{
		DestroyAEList(_pfirstae);
	}
}

/*
 *	CGenUndoBuilder::SetNameID (idName)
 *
 *	@mfunc
 *		Allows a name to be assigned to this anti-event collection.
 *		The ID should be an index that can be used to retrieve a
 *		language specific string (like "Paste").  This string is
 *		typically composed into undo menu items (i.e. "Undo Paste").
 */

void CGenUndoBuilder::SetNameID( 
	UNDONAMEID idName )			//@parm	the name ID for this undo operation
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CGenUndoBuilder::SetNameID");

	// don't delegate to the higher undobuilder, even if it exists. The
	// original name should win in re-entrancy cases.
	_idName = idName;
}


/*
 *	CGenUndoBuilder::AddAntiEvent (pae)
 *
 *	@mfunc
 *		Adds an anti-event to the end of the list
 *
 *	@rdesc 	NOERROR
 */

HRESULT CGenUndoBuilder::AddAntiEvent( 
	IAntiEvent *pae )		//@parm	the anti-event to add
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CGenUndoBuilder::AddAntiEvent");

	if( _publdrPrev )
	{
		return _publdrPrev->AddAntiEvent(pae);
	}

	pae->SetNext(_pfirstae);
	_pfirstae = pae;

	return NOERROR;
}

/*
 *	CGenUndoBuilder::GetTopAntiEvent
 *
 *	@mfunc	Gets the top anti-event for this context.
 *
 *	@comm	The current context can be either the current
 *			operation *or* to a previous operation if we are in
 *			merge typing mode.
 *
 *	@rdesc	the top anti-event
 *
 */
IAntiEvent *CGenUndoBuilder::GetTopAntiEvent()
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CGenUndoBuilder::GetTopAntiEvent");

	if( _publdrPrev )
	{
		Assert(_pfirstae == NULL);
		return _publdrPrev->GetTopAntiEvent();
	}

	if( !_pfirstae && _pundo )
	{
		return _pundo->GetMergeAntiEvent();
	}

	return _pfirstae;
}

/*
 *	CGenUndoBuilder::Done
 *
 *	@mfunc
 *		puts the combined anti-events (if any) into the undo stack
 */

HRESULT CGenUndoBuilder::Done( void )
{
	HRESULT hr = NOERROR;
	DWORD dwLim = DEFAULT_UNDO_SIZE;
	IUndoMgr *predo;
	IAntiEvent *paetemp;

	if( _publdrPrev )
	{
		Assert(_pfirstae == NULL);
		return NOERROR;
	}

	if( _ped->GetDetectURL() )
	{
		_ped->GetDetectURL()->ScanAndUpdate(this);
	}

	// if nothing changed, discard any selection anti-events
	// or other no-op actions.
	if( !_ped->GetCallMgr()->GetChangeEvent() )
	{
		Discard();
		return NOERROR;
	}

	if( _pfirstae )
	{
		if( !_pundo )
		{
			// yikes!  There is no undo stack; better create one.

			// if we are a redo guy, we should create a redo
			// stack the size of the undo stack

			if( _fRedo )
			{
				Assert(_ped->GetUndoMgr());

				dwLim = _ped->GetUndoMgr()->GetUndoLimit();
			}

			_pundo = _ped->CreateUndoMgr(dwLim,	_fRedo ? US_REDO : US_UNDO );

			// FUTURE:  A NULL ptr returned from CreateUndoMgr means either
			// 	we are out of memory, or the undo limit is set to 0.  For the
			// 	latter case, we have collected AE's to push onto a non-existent
			// 	undo stack.  It may be more efficient to not generate
			// 	the AE's at all when the undo limit is 0.

			if(!_pundo)
			{
				goto CleanUp;
			}
		}

		// we may need to flush the redo stack if we are adding
		// more anti-events to the undo stack *AND* we haven't been
		// told not to flush the redo stack.  The only time we won't
		// flush the redo stack is if it's the redo stack itself
		// adding anti-events to undo.

		if( !_fRedo )
		{
			// if our destination is the undo stack, then check
			// to see if we should flush
			if( !_fDontFlushRedo )
			{
				predo = _ped->GetRedoMgr();

				if( predo )
				{
					predo->ClearAll();
				}
			}
		}
#ifdef DEBUG
		else
		{
			Assert(!_fDontFlushRedo);
		}

#endif // DEBUG

		// if we should enter into the group typing state, inform
		// the undo manager.  Note that we only do this *iff* 
		// there is actually some anti-event to put in the undo
		// manager.  This makes the undo manager easier to implement
		if( _fStartGroupTyping )
		{
			_pundo->StartGroupTyping();
		}
		
		hr = _pundo->PushAntiEvent( _idName, _pfirstae );

		// the change event flag should be set if we're adding
		// undo items!   If this test is true, it probably means
		// the somebody earlier in the call stack sent change
		// notifiations (either via SendAllNotifications or
		// the CAutonotify class) _before_ this undo context
		// was committed _or_ it means that we were re-entered
		// in some way that was not handled properly.
		//
		// Needless to say, this is not an ideal state.

CleanUp:
		Assert(_ped->GetCallMgr()->GetChangeEvent());

		paetemp = _pfirstae;
		_pfirstae = NULL;
		
		CommitAEList(paetemp, _ped);

		if(!_pundo || hr != NOERROR)
		{
			// Either we failed to add the AE's to the undo stack
			// or the undo limit is 0 in which case there won't be
			// an undo stack to push the AE's onto.
			DestroyAEList(paetemp);
		}
	}

	return hr;
}

/*
 *	CGenUndoBuilder::Discard
 *
 *	@mfunc
 *		Gets rid of any anti-events that we may be hanging onto without
 *		executing or committing them.  Typically used for recovering
 *		from certain failure or re-entrancy scenarios.  Note that
 *		an _entire_ anti-event chain will be removed in this fashion.
 *
 *	@rdesc void
 */
void CGenUndoBuilder::Discard(void)
{
	if( _pfirstae )
	{
		DestroyAEList(_pfirstae);
		_pfirstae = NULL;
	}
	else if( _publdrPrev )
	{
		_publdrPrev->Discard();
	}
}


/*
 *	CGenUndoBuilder::StartGroupTyping
 *
 *	@mfunc
 *		hangs onto the the fact that group typing should start.
 *		We'll forward the the state transition to the undo manager
 *		only if an anti-event is actually added to the undo manager.
 *
 *	@devnote
 *		group typing is disabled for redo stacks.
 */

void CGenUndoBuilder::StartGroupTyping()
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CGenUndoBuilder::StartGroupTyping");

	_fStartGroupTyping = TRUE;
}

/*
 *	CGenUndoBuilder::StopGroupTyping
 *
 *	@mfunc
 *		forwards a stop grouped typing to the undo manager
 */

void CGenUndoBuilder::StopGroupTyping()
{
	TRACEBEGIN(TRCSUBSYSUNDO, TRCSCOPEINTERN, "CGenUndoBuilder::StopGroupTyping");

	if( _pundo )
	{
		_pundo->StopGroupTyping();
	}
}

//
//	CUndoStackGuard IMPLEMENTATION
//

/*
 *	CUndoStackGuard::CUndoStackGuard
 *
 *	@mfunc	Constructor.  Registers this object with the call manager
 *
 *	@rdesc	void
 */
CUndoStackGuard::CUndoStackGuard(
	CTxtEdit *ped)			//@parm the edit context
{
	_ped = ped;
	_fReEntered = FALSE;
	_hr = NOERROR;
	ped->GetCallMgr()->RegisterComponent(this, COMP_UNDOGUARD);
}

/*
 *	CUndoStackGuard::~CUndoStackGuard
 *
 *	@mfunc	Destructor.  Revokes the registration of this object
 *			with the call manager
 *
 *	@rdesc	void
 */
CUndoStackGuard::~CUndoStackGuard()
{
	_ped->GetCallMgr()->RevokeComponent(this);
}

/*
 *	CUndoStackGuard::SafeUndo
 *
 *	@mfunc	Loops through the given list of anti-events, invoking
 *			undo on each.  
 *
 *	@rdesc	HRESULT, from the undo actions
 *
 *	@devnote	This routine is coded so that OnEnterContext can pick up
 *			and continue the undo operation should we become re-entered
 */
HRESULT CUndoStackGuard::SafeUndo(
	IAntiEvent *pae,		//@parm the start of the anti-event list
	IUndoBuilder *publdr)	//@parm the undo builder to use
{
	HRESULT hr;

	_publdr = publdr;

	while( pae )
	{
		_paeNext = pae->GetNext();
		hr = pae->Undo(_ped, publdr);

		// save the first returned error.
		if( hr != NOERROR && _hr == NOERROR)
		{
			_hr = hr;
		}

		pae = (IAntiEvent *)_paeNext;
	}

	return _hr;
}

/*
 *	CUndoStackGuard::OnEnterContext
 *
 *	@mfunc	Handle re-entrancy during undo operations.
 *
 *	@rdesc	void
 *	
 *	@devnote If this method is called, it's pretty serious.  In general,
 *			we shoud never be re-entered while processing undo stuff.
 *			However, to ensure that, block the incoming call and process
 *			the remaining actions.
 */
void CUndoStackGuard::OnEnterContext()
{
	TRACEWARNSZ("ReEntered while processing undo.  Blocking call and");
	TRACEWARNSZ("	attempting to recover.");

	_fReEntered = TRUE;
	SafeUndo((IAntiEvent *)_paeNext, _publdr);
}	

//
//	PUBLIC helper functions
//

/*
 *	@func	DestroyAEList | Destroys a list of anti-events
 *
 *	@rdesc	void
 */
void DestroyAEList( 
	IAntiEvent *pae)	//@parm the anti-event from which to start
{
	IAntiEvent *pnext;

	while( pae )
	{
		pnext = pae->GetNext();
		pae->Destroy();
		pae = pnext;
	}
}

/*
 *	@func CommitAEList | Calls OnCommit to the given list of anti-events
 *
 *	@rdesc	void
 */
void CommitAEList(
	IAntiEvent *pae,	//@parm the anti-event from which to start
	CTxtEdit *ped)		//@parm the edit context
{
	IAntiEvent *pnext;

	while( pae )
	{
		pnext = pae->GetNext();
		pae->OnCommit(ped);
		pae = pnext;
	}
}

/*
 *	@func	HandleSelectionAEInfo | Tries to merge the given info with 
 *			the existing undo context; if that fails, then it allocates 
 *			a new selection anti-event to handle the info
 */
HRESULT HandleSelectionAEInfo(
	CTxtEdit *ped,			//@parm the edit context
	IUndoBuilder *publdr,	//@parm the undo context
	LONG cp,				//@parm the cp to use for the sel ae
	LONG cch,				//@parm the signed selection extension
	LONG cpNext,			//@parm the cp to use for the AE of the AE
	LONG cchNext,			//@parm the cch to use for the AE of the AE
	SELAE flags)			//@parm controls how to intepret the info
{
	IAntiEvent *pae;

	Assert(publdr);

	pae = publdr->GetTopAntiEvent();

	// first see if we can merge the selection info into any existing
	// anti-events.  Note that the selection anti-event may be anywhere
	// in the list, so go through them all
	if( pae )
	{
		SelRange sr;

		sr.cp		= cp;
		sr.cch		= cch;
		sr.cpNext	= cpNext;
		sr.cchNext	= cchNext;
		sr.flags	= flags;

		while( pae )
		{
			if( pae->MergeData(MD_SELECTIONRANGE, (void *)&sr) == NOERROR )
			{	
				break;
			}

			pae = pae->GetNext();
		}
	
		if( pae )
		{
			return NOERROR;
		}
	}

	// oops; can't do a merge.  Go ahead and create a new anti-event.

	Assert(!pae);

	pae = gAEDispenser.CreateSelectionAE(ped, cp, cch, cpNext, cchNext);

	if( pae )
	{
		publdr->AddAntiEvent(pae);
		return NOERROR;
	}

	return E_OUTOFMEMORY;
}

⌨️ 快捷键说明

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