Milán Major

netscape-revival

1 branch
Code

lib/mac/VText/V_PowerPlant_Mods/LAEAction.cp

// ===========================================================================
//	LAEAction.cp			©1994 Metrowerks Inc. All rights reserved.
// ===========================================================================

#ifdef PowerPlant_PCH
#include PowerPlant_PCH
#endif

#include	<PP_Prefix.h>
#include	"LAEAction.h"
#include	<LSelection.h>
#include	<UAppleEventsMgr.h>
#include	<AERegistry.h>
#include	<LModelObject.h>
#include	<LRecalcAccumulator.h>
#include	<StClasses.h>
#include	<UAEGizmos.h>

//	===========================================================================
//	Maintenance / initialization:

LAEAction::LAEAction(
	ResIDT		inStringResID,
	Int16		inStringIndex,
	Boolean		inAlreadyDone
)	:	LAction(inStringResID, inStringIndex, inAlreadyDone)
{
	mRecordOnlyFinalState = false;
	mUndoAEKeyFed = typeNull;
	mPostUndoAEKeyFed = typeNull;
	mFromReplyKey = typeNull;
	
	mSelection = NULL;
	mRedoSelectionModifier = typeNull;
	mUndoSelectionModifier = typeNull;
	mPreRecordSelection = true;
	
	mAccumulator = NULL;
	
	//	Tell target focusing...
	LModelObject	*target = LModelObject::GetTellTarget();
	if (target) {
		StTempTellTarget	tell(NULL);	//	Make complete ospec
		target->MakeSpecifier(mTellTarget.mDesc);
	}
}

LAEAction::~LAEAction()
{
	ReplaceSharable(mSelection, (LSelection *)NULL, this);
}

void	LAEAction::Finalize(void)
{
	if (mRecordOnlyFinalState && mIsDone) {

		if (mSelection && mPreRecordSelection && (mOriginalSelection.mDesc.descriptorType != typeNull))
			mSelection->DoAESelection(mOriginalSelection, false);

		SendAppleEvent(mRedoEvent.mDesc, kAEDontExecute);
	}
	
	//	Once an event has been finalized, subsequent redo's & undo's must be
	//	recorded.
	SetRecordOnlyFinalState(false);
	
	inherited::Finalize();
}

void	LAEAction::SetRecordOnlyFinalState(Boolean inRecordOnlyFinalState)
{
	mRecordOnlyFinalState = inRecordOnlyFinalState;
}

void	LAEAction::SetRedoAE(AEEventClass inEventClass, AEEventID inEventID)
/*
	Don't use this routine if the associated event has parameters.
	Instead, make an AppleEvent and use SetRedo/UndoAE(AppleEvent).
*/
{
	UAppleEventsMgr::MakeAppleEvent(inEventClass, inEventID, mRedoEvent.mDesc);
}

//	===========================================================================
//	AE loading:

void	LAEAction::SetRedoAE(const AppleEvent &inAppleEvent)
{
	OSErr	err;
	
	err = AEDuplicateDesc(&inAppleEvent, &mRedoEvent.mDesc);
	ThrowIfOSErr_(err);
}

void	LAEAction::SetUndoAE(
	AEEventClass		inEventClass,
	AEEventID			inEventID,
	Boolean				inFeedUndoWithRedoReply)
/*
	Don't use this routine if the associated event has parameters.
	Instead, make an AppleEvent and use SetRedo/UndoAE(AppleEvent).
	
	Avoid using explicitly using the optional inFeedUndoWithRedoReply.
	Instead, call UndoAESetKeyFed after all SetUndoAE's have been called.
*/
{
	UAppleEventsMgr::MakeAppleEvent(inEventClass, inEventID, mUndoEvent.mDesc);
	
	if (inFeedUndoWithRedoReply)
		UndoAESetKeyFed(keyDirectObject);
}

void	LAEAction::SetUndoAE(
	const AppleEvent	&inAppleEvent,
	Boolean				inFeedUndoWithRedoReply)
/*	
	Avoid using explicitly using the optional inFeedUndoWithRedoReply.
	Instead, call UndoAESetKeyFed after all SetUndoAE's have been called.
*/
{
	OSErr	err;

	err = AEDuplicateDesc(&inAppleEvent, &mUndoEvent.mDesc);
	ThrowIfOSErr_(err);
	
	if (inFeedUndoWithRedoReply)
		UndoAESetKeyFed(keyDirectObject);
}

void	LAEAction::SetPostUndoAE(const AppleEvent &inAppleEvent)
{
	OSErr	err;

	err = AEDuplicateDesc(&inAppleEvent, &mPostUndoEvent.mDesc);
	ThrowIfOSErr_(err);
}

void	LAEAction::UndoAESetKeyFed(DescType inKey, DescType inFromReplyKey)
/*
	Used to set the "key parameter receiver" for the undo event
	from the reply of the redo event.
	
	Avoid calling this method with typeNull because that is the natural
	constructed state of an LAEAction.
*/
{
	mUndoAEKeyFed = inKey;
	mFromReplyKey = inFromReplyKey;
}

void	LAEAction::PostUndoAESetKeyFed(DescType inKey, DescType inFromReplyKey)
{
	mPostUndoAEKeyFed = inKey;
	mPostFromReplyKey = inFromReplyKey;
}

void	LAEAction::RedoAEAdd(AEKeyword theAEKeyword, const AEDesc &inDesc)
{
	OSErr	err;
	
	err = AEPutParamDesc(&mRedoEvent.mDesc, theAEKeyword, &inDesc);
	ThrowIfOSErr_(err);
}

void	LAEAction::UndoAEAdd(AEKeyword theAEKeyword, const AEDesc &inDesc)
{
	OSErr	err;
	
	err = AEPutParamDesc(&mUndoEvent.mDesc, theAEKeyword, &inDesc);
	ThrowIfOSErr_(err);
}

//	===========================================================================
//	Selection things:

void	LAEAction::SetSelection(
	LSelection	*inSelection,
	DescType	inRedoSelectionModifier,
	DescType	inUndoSelectionModifier)
{
	ReplaceSharable(mSelection, inSelection, this);
	
	mRedoSelectionModifier = inRedoSelectionModifier;
	mUndoSelectionModifier = inUndoSelectionModifier;
}

LSelection *	LAEAction::GetSelection(void)
{
	return mSelection;
}

void	LAEAction::SetPreRecordSelection(Boolean inPreRecordSelection)
{
	mPreRecordSelection = inPreRecordSelection;
}

Boolean	LAEAction::GetPreRecordSelection(void)
{
	return mPreRecordSelection;
}
	
//	===========================================================================
//	RecalcAccumulator:
//		req'd only for "cool" updating

LRecalcAccumulator *	LAEAction::GetRecalcAccumulator(void)
{
	return mAccumulator;
}

void	LAEAction::SetRecalcAccumulator(LRecalcAccumulator *inAccumulator)
{
	mAccumulator = inAccumulator;
}

//	===========================================================================
//	Implementation:

void	LAEAction::Redo(void)
{
	if (CanRedo()) {
		StRecalculator	update(mAccumulator);
		LAESubDesc		targetSD(mTellTarget);
		LModelObject	*oldTarget = LModelObject::GetTellTarget(),
						*target = NULL;
						
		if (targetSD.GetType() != typeNull) {
			StTempTellTarget	doFrom(NULL);
			
			target = targetSD.ToModelObjectOptional();
		}
		
		Try_ {
			LModelObject::DoAESwitchTellTarget(target);
			
			if (mSelection) {
				StAEDescriptor	temp;

				if (mOriginalSelection.mDesc.descriptorType == typeNull) {
				
					//	must be first time -- record selection in case there's repeated redo/undos
					mSelection->GetAEValue(temp.mDesc, mOriginalSelection.mDesc);
				} else {
				
					//	Use the previous selection.
					mSelection->SetAEValue(mOriginalSelection.mDesc, temp.mDesc);
				}
				if (mPreRecordSelection)
					mSelection->RecordPresentSelection();
			}

			RedoSelf();
			
			mIsDone = true;
			
			LModelObject::DoAESwitchTellTarget(oldTarget);
		} Catch_(inErr) {
			LModelObject::DoAESwitchTellTarget(oldTarget);
			Throw_(inErr);
		} EndCatch_;
	} else {
		mIsDone = true;
	}
}

void	LAEAction::Undo(void)
{
	if (CanUndo()) {
		StRecalculator	update(mAccumulator);
		StAEDescriptor	temp;
		LAESubDesc		targetSD(mTellTarget);
		LModelObject	*oldTarget = LModelObject::GetTellTarget(),
						*target = NULL;
						
		if (targetSD.GetType() != typeNull) {
			StTempTellTarget	doFrom(NULL);
			
			target = targetSD.ToModelObjectOptional();
		}
		
		Try_ {
			LModelObject::DoAESwitchTellTarget(target);
			
			UndoSelf();

			mIsDone = false;

			LModelObject::DoAESwitchTellTarget(oldTarget);
		} Catch_(inErr) {
			LModelObject::DoAESwitchTellTarget(oldTarget);
			Throw_(inErr);
		} EndCatch_;
	} else {
		mIsDone = false;
	}
}

void	LAEAction::RedoSelf(void)
{
	Assert_(CanRedo());
	
	//	Do the event...
	SendAppleEvent(mRedoEvent.mDesc, mRecordOnlyFinalState ? kAEDontRecord : 0);

	//	Record information from reply for undo
	if ((mUndoEvent.mDesc.descriptorType != typeNull) && (mUndoAEKeyFed != typeNull)) {
		StAEDescriptor	aeResult;
	
		//	keyDirectObject -- the result from the original operation
		Assert_(mFromReplyKey != typeNull);
		aeResult.GetParamDesc(mReply.mDesc, mFromReplyKey, typeWildCard);
		UndoAEAdd(mUndoAEKeyFed, aeResult.mDesc);
		
		mUndoAEKeyFed = typeNull;	//	Don't add it twice
	}
	
	//	Update selection
	if (mSelection)
		FixSelectionWReply(mRedoSelectionModifier);
}

void	LAEAction::UndoSelf(void)
{
	OSErr	err;
	
	Assert_(CanUndo());
	
	//	Do the event
	SendAppleEvent(mUndoEvent.mDesc, mRecordOnlyFinalState ? kAEDontRecord : 0);
	
	//	Update selection
	if (mSelection)
		FixSelectionWReply(mUndoSelectionModifier);

	//	Post undo AE...
	if (mPostUndoAEKeyFed != typeNull) {
		StAEDescriptor	aeResult;
	
		//	mPostFromReplyKey -- the result from the undo operation
		Assert_(mPostFromReplyKey != typeNull);
		aeResult.GetParamDesc(mReply.mDesc, mPostFromReplyKey, typeWildCard);

		err = AEPutParamDesc(&mPostUndoEvent.mDesc, mPostFromReplyKey, &aeResult.mDesc);
		ThrowIfOSErr_(err);
		
		mPostUndoAEKeyFed = typeNull;	//	Don't add it twice
	}
	if (mPostUndoEvent.mDesc.descriptorType != typeNull)
		SendAppleEvent(mPostUndoEvent.mDesc, mRecordOnlyFinalState ? kAEDontRecord : 0);
}

Boolean	LAEAction::CanRedo(void) const
{
	return	inherited::CanRedo() &&	(mRedoEvent.mDesc.descriptorType != typeNull);
}


Boolean	LAEAction::CanUndo(void) const
{
	return	inherited::CanUndo() && (mUndoEvent.mDesc.descriptorType != typeNull);
}

void	LAEAction::GetReplyDesc(AEDesc *outDesc)
{
	OSErr	err;
	
	err = AEDuplicateDesc(&mReply.mDesc, outDesc);
	ThrowIfOSErr_(err);
}

void	LAEAction::SendAppleEvent(AppleEvent &inAppleEvent, AESendMode inSendModifiers)
{
	StAEDescriptor	aeResult;
	StAEDescriptor	aeHole;
	OSErr			err;
	
	inSendModifiers |= kAEWaitReply;

	err = AEDisposeDesc(&mReply.mDesc);
	ThrowIfOSErr_(err);
	
	Int32	version;
	err = ::AEManagerInfo(keyAEVersion, &version);
	Boolean	dontExecuteWorks = (err == noErr)
				?	version >= 0x01018000
				:	false;
	
	if ((inSendModifiers & kAEDontExecute) && !dontExecuteWorks)
		return;
		
	err = AESend(&inAppleEvent, &mReply.mDesc, inSendModifiers, kAENormalPriority,
						kAEDefaultTimeout, nil, nil);
	ThrowIfOSErr_(err);
}

void	LAEAction::FixSelectionWReply(DescType inSelectionModifier)
{
	StAEDescriptor		aeResult;

	if (mSelection && inSelectionModifier != typeNull) {
		aeResult.GetOptionalParamDesc(mReply.mDesc, keyAEResult, typeWildCard);
		mSelection->DoAESelectAsResult(aeResult, inSelectionModifier);
	}
}

//	===========================================================================
//	LAEMoveAction:

LAEMoveAction::LAEMoveAction(
	ResIDT		inStringResID,
	Int16		inStringIndex,
	Boolean		inAlreadyDone
)	:	LAEAction(inStringResID, inStringIndex, inAlreadyDone)
{
}

LAEMoveAction::~LAEMoveAction()
{
}

void	LAEMoveAction::RedoSelf(void)
{
	SendAppleEvent(mRedoEvent.mDesc, mRecordOnlyFinalState ? kAEDontRecord : 0);

	if (mUndoAEKeyFed != typeNull) {
		StAEDescriptor	aeResult,
						aeHole;
	
		//	keyDirectObject -- the result from the original move
		Assert_(mFromReplyKey != typeNull);
		aeResult.GetParamDesc(mReply.mDesc, mFromReplyKey, typeWildCard);
		UndoAEAdd(keyDirectObject, aeResult.mDesc);
		
		//	keyAEInsertHere -- the resulting hole from the original move
		aeHole.GetParamDesc(mReply.mDesc, keyAEInsertHere, typeWildCard);
		UndoAEAdd(keyAEInsertHere, aeHole.mDesc);
		
		mUndoAEKeyFed = typeNull;	//	Don't add it twice
	}
	
	//	Update selection
	if (mSelection)
		FixSelectionWReply(mRedoSelectionModifier);
}