Milán Major

netscape-revival

1 branch
Code

lib/mac/VText/V_PowerPlant_Mods/LModelObject.cp

// ===========================================================================
//	LModelObject.cp			   ©1993-1996 Metrowerks Inc. All rights reserved.
// ===========================================================================
//
//	Mix-in class for supporting the Apple Event Object Model. Subclasses of
//	LModelObject represent Apple Event Object defined by the Apple Event
//	Registry.
//
//	=== Functions to Override ===
//
//	To Respond to Events:
//		HandleAppleEvent
//
//	To Support Elements:
//		CountSubModels
//		GetSubModelByPosition
//		GetSubModelByName
//		GetPositionOfSubModel
//		HandleCreateElementEvent
//
//	To Support Properties:
//		GetAEProperty
//		SetAEProperty

#ifdef PowerPlant_PCH
#include PowerPlant_PCH
#endif

#include <LModelObject.h>
#include <LModelDirector.h>
#include <LModelProperty.h>
#include <UAppleEventsMgr.h>
#include <UExtractFromAEDesc.h>
#include <LList.h>
#include <LListIterator.h>
#include <UMemoryMgr.h>
#include <AEPowerPlantSuite.h>

//	#define	UAEGIZMOS
#ifdef	UAEGIZMOS
	#include	<UAEGizmos.h>
#endif

#ifndef __AEOBJECTS__
#include <AEObjects.h>
#endif

#ifndef __AERegistry__
#include <AERegistry.h>
#endif

#ifndef __AEPACKOBJECT__
#include <AEPackObject.h>
#endif


//	¥ Class Variables
LModelObject*	LModelObject::sDefaultModel = nil;
LModelObject*	LModelObject::sStreamingModel = nil;
LList*			LModelObject::sLazyModels;

// ---------------------------------------------------------------------------
//		¥ LModelObject
// ---------------------------------------------------------------------------
//	Default Constructor

LModelObject::LModelObject()
{
	mSuperModel = nil;

	mModelKind = typeNull;
	mLaziness = false;
	mSubModels = nil;
	mDefaultSubModel = nil;
	mDefaultSuperModel = nil;
	
	SetStreamingModel(this);
}


// ---------------------------------------------------------------------------
//		¥ LModelObject(LModelObject*, DescType)
// ---------------------------------------------------------------------------
//	Construct a ModelObject with a specified SuperModel
//
//	Use the optional inKind parameter to indicate the AppleEvent class for
//	this object.

LModelObject::LModelObject(
	LModelObject	*inSuperModel,
	DescType		inKind)
{
	mSuperModel = inSuperModel;
	
	mModelKind = inKind;
	mLaziness = false;
	mSubModels = NULL;
	mDefaultSubModel = NULL;
	mDefaultSuperModel = NULL;

	if (mSuperModel != nil) {
		mSuperModel->AddSubModel(this);
	}
	
	SetStreamingModel(this);
}


// ---------------------------------------------------------------------------
//		¥ ~LModelObject
// ---------------------------------------------------------------------------
//	Destructor

LModelObject::~LModelObject()
{
	if (IsLazy()) {
		LModelObject	*thisMO = this;
		sLazyModels->Remove(&thisMO);
	}

	if (GetStreamingModel() == this) {
		SetStreamingModel(nil);
	}
	
	//	Make sure pointers to this object aren't left dangling.
	SetDefaultSubModel(NULL);	
	if (GetDefaultSuperModel()) {
		GetDefaultSuperModel()->SetDefaultSubModel(NULL);
	}

	//	Remove/delete submodels
	if (mSubModels) {
		LListIterator	iterator(*mSubModels, iterate_FromEnd);
		LModelObject	*sub;
		
		while (iterator.Previous(&sub)) {
			sub->SetSuperModel(nil);
			sub->SuperDeleted();
		}
		
		delete mSubModels;
	}
		
	if (mSuperModel != nil) {
		mSuperModel->RemoveSubModel(this);
		mSuperModel->Finalize();	//	to delete up chains of lazy objects
	}
}


// ---------------------------------------------------------------------------
//		¥ Finalize
// ---------------------------------------------------------------------------
//	This function gets called when a model object should consider deleting
//	itself.

void
LModelObject::Finalize()
{
		// Delete lazy ModelObjects that have no SubModels

	if ( IsLazy() &&
		 ((mSubModels == nil) || (mSubModels->GetCount() == 0)) ) {
		 
		delete this;
	}
}


// ---------------------------------------------------------------------------
//		¥ SuperDeleted
// ---------------------------------------------------------------------------
//	The super model of this object was just deleted and this object should
//	consider deleting itself.

void
LModelObject::SuperDeleted(void)
{
	if (!IsLazy()) {
		delete this;
	}
}


// ---------------------------------------------------------------------------
//		¥ SetSuperModel
// ---------------------------------------------------------------------------
//	Switch the SuperModel of a ModelObject.
//
//	You will rarely need to call this method -- constructors and the
//	destructor typically take care of such bookkeeping details.

void
LModelObject::SetSuperModel(
	LModelObject	*inSuperModel)
{
	//	Only switch if it will change something.
	if (inSuperModel == mSuperModel)
		return;

	if (mSuperModel != nil) {			// Detach from old SuperModel
		mSuperModel->RemoveSubModel(this);
	}

	mSuperModel = inSuperModel;

	if (mSuperModel != nil) {			// Attach to new SuperModel
		mSuperModel->AddSubModel(this);
	}
}


// ---------------------------------------------------------------------------
//		¥ SetModelKind
// ---------------------------------------------------------------------------
//	Set the AppleEvent Class ID of this ModelObject
//
//	You may specify the ModelKind in a constructor or with this method
//	shortly after object construction.
//
//	If the ModelKind is not specified during or after construction, it
//	defaults to "typeNull."
//
//	Being able to specify the model kind means a given subclass of
//	LModelObject can be used to implement more than one type of Apple Event
//	Object Model "class."

void
LModelObject::SetModelKind(DescType inModelKind)
{
	mModelKind = inModelKind;

	//	Is there an old GetModelKind() override interfering?
	Assert_(GetModelKind() == mModelKind);
}


// ---------------------------------------------------------------------------
//		¥ IsLazy
// ---------------------------------------------------------------------------
//	Return whether this ModelObject is "lazy."
//
//	A "lazy instantiated" model object is a transient object created solely
//	for the purpose of handling an AppleEvent.  After AppleEvent processing,
//	lazy objects are automatically deleted.
//
//	Default initialization sets laziness to false.  Laziness may be changed
//	with SetLaziness.

Boolean
LModelObject::IsLazy() const
{
	return mLaziness;
}


// ---------------------------------------------------------------------------
//		¥ SetLaziness
// ---------------------------------------------------------------------------
//	Changes whether this ModelObject is "Lazy."
//
//	Default initialization sets laziness to false.

void
LModelObject::SetLaziness(Boolean inBeLazy)
{
	if (mLaziness != inBeLazy) {
		
		if (inBeLazy) {
			AddLazy(this);
		} else {
			RemoveLazy(this);
		}
		
		mLaziness = inBeLazy;
	}
}

// ---------------------------------------------------------------------------
//		¥ SetUseSubModelList
// ---------------------------------------------------------------------------
//	Changes whether a "submodel list" is kept for this ModelObject.
//
//	A submodel list allows submodels to be added to an LModelObject without
//	having to override many of the "GetSubModelBy..." functions.  This is
//	useful in cases where a model object, such as a window, has several AEOM
//	addressable submodels.
//
//	Default initialization creates ModelObjects that don't keep a submodel
//	list
//
//	Note:	It doesn't makes sense to turn this feature on then off

void
LModelObject::SetUseSubModelList(Boolean inUseSubModelList)
{
	if (inUseSubModelList == (mSubModels != NULL) )
		return;
		
	if (inUseSubModelList) {
		mSubModels = new LList();
		return;
	}
	
	//	turn off
	Assert_(false);	//	but it really doesn't make sense to.
	delete mSubModels;
	mSubModels = nil;
}


// ---------------------------------------------------------------------------
//		¥ AddSubModel
// ---------------------------------------------------------------------------
//	Notify a ModelObject that is has a new SubModel
//
//	If the submodel list is being used, this default method will add the
//	submodel to the submodel list.
//
//	You will not need to call this method.  Instead use:
//
//		inSubModel->SetSuperModel(itsSuperModel);

void
LModelObject::AddSubModel(
	LModelObject	*inSubModel)
{
	if (mSubModels) {
		Int32	index = mSubModels->FetchIndexOf(&inSubModel);
		
		if (index) {
			Assert_(false);	//	Adding a pre-existent submodel!
			return;
		}
		
		mSubModels->InsertItemsAt(1, arrayIndex_Last, &inSubModel);
	}
}


// ---------------------------------------------------------------------------
//		¥ RemoveSubModel
// ---------------------------------------------------------------------------
//	Notify a ModelObject that a SubModel is being deleted
//
//	If the submodel list is being used, this method will remove the submodel
//	from the submodel list.
//
//	You will not need to call this method.  Instead use:
//
//		inSubModel->SetSuperModel(NULL);

void
LModelObject::RemoveSubModel(
	LModelObject	*inSubModel)
{
	if (mSubModels) {
		Int32	index = mSubModels->FetchIndexOf(&inSubModel);

		if (index == 0) {
			Assert_(false);	//	trying to remove non-existent submodel
			return;
		}
		
		mSubModels->RemoveItemsAt(1, index);
	}
}


// ---------------------------------------------------------------------------
//		¥ IsSubModelOf
// ---------------------------------------------------------------------------
//	Returns whether this is a submodel of inSuperModel

Boolean
LModelObject::IsSubModelOf(const LModelObject *inSuperModel) const
{
	if (mSuperModel == inSuperModel)
		return true;
	
	if (mSuperModel == nil)
		return false;
	
	return mSuperModel->IsSubModelOf(inSuperModel);
}


// ---------------------------------------------------------------------------
//		¥ GetDefaultSubModel
// ---------------------------------------------------------------------------
//	Returns the default submodel (if any) of this ModelObject.
//
//	The default submodel allows this ModelObject to serve as an alias to
//	the submodel.  Its use simplifies scripts.

LModelObject *
LModelObject::GetDefaultSubModel(void) const
{
	return mDefaultSubModel;
}


// ---------------------------------------------------------------------------
//		¥ SetDefaultSubModel
// ---------------------------------------------------------------------------
//	Sets the default submodel of this ModelObject.
//
//	The default sumodel must already be a submodel of this ModelObject.

void
LModelObject::SetDefaultSubModel(LModelObject *inSubModel)
{
	if (mDefaultSubModel != inSubModel) {
		//	Do the switch
		if (mDefaultSubModel)
			mDefaultSubModel->SetDefaultSuperModel(NULL);
		mDefaultSubModel = inSubModel;
		if (mDefaultSubModel)
			mDefaultSubModel->SetDefaultSuperModel(this);
	}
}


// ---------------------------------------------------------------------------
//		¥ IsDefaultSubModel
// ---------------------------------------------------------------------------
//	Is "this" ModelObject a default submodel of the
//	DefaultModel (Application).

Boolean
LModelObject::IsDefaultSubModel(void) const
{
	if (this == GetDefaultModel())
		return true;
		
	if (GetDefaultSuperModel()) {
		return GetDefaultSuperModel()->IsDefaultSubModel();
	} else {
		return false;
	}
}


// ---------------------------------------------------------------------------
//		¥ GetDefaultSuperModel
// ---------------------------------------------------------------------------
//	Returns the default supermodel of this ModelObject.
//
//	You should not need to explicitly call this method.
//	You should not need to override this method.

LModelObject *
LModelObject::GetDefaultSuperModel(void) const
{
	return mDefaultSuperModel;
}


// ---------------------------------------------------------------------------
//		¥ SetDefaultSuperModel
// ---------------------------------------------------------------------------
//	Sets the default supermodel of this ModelObject.
//
//	You should not need to explicitly call this method -- use
//	SetDefaultSubModel instead and it will call this method.
//	You should not need to override this method.

void
LModelObject::SetDefaultSuperModel(LModelObject *inSuperModel)
{
	mDefaultSuperModel = inSuperModel;
}


// ---------------------------------------------------------------------------
//		¥ CountSubModels
// ---------------------------------------------------------------------------
//	Return number of SubModels of the specified type
//
//	Must be overridden by subclasses which have SubModels that aren't
//	implemented using the submodel list (ie lazy instantiated submodels).
//
//	When overriding, you should add the inherited result to your overridden
//	result.

Int32
LModelObject::CountSubModels(
	DescType	inModelID) const
{
	Int32	count = 0;
	
	if (GetDefaultSubModel())
		count += GetDefaultSubModel()->CountSubModels(inModelID);

	if (mSubModels) {
		LListIterator	iterator(*mSubModels, iterate_FromStart);
		LModelObject	*p;
	
		while (iterator.Next(&p)) {
			if ((p->GetModelKind() == inModelID) || (inModelID == typeWildCard))
				count++;
		}
	}
	
	return count;
}


// ---------------------------------------------------------------------------
//		¥ GetModelToken
// ---------------------------------------------------------------------------
//	Get a Token for the specified Model(s).
//
//	This method not only includes possible resolutions using "this" but also
//	recursively includes resolutions on the default submodel of "this."
//
//	This function gives preference to resolutions off of the default submodel
//	rather than resolutions off of "this."  This is because:
//
//	¥	the default submodel is probably what was wanted anyway,
//
//	¥	if there is no default submodel there is no real performance penatly,
//
//	¥	it prevents "link" objects from erroneously adding their submodels
//		to the output token.
//
//	If a token is found in the default submodel, no resolution based off of
//	"this" will be made.  This has the consequence of models in the default
//	submodel effectively hiding similar models of "this."

void
LModelObject::GetModelToken(
	DescType		inModelID,
	DescType		inKeyForm,
	const AEDesc	&inKeyData,
	AEDesc			&outToken) const
{
	OSErr	err = noErr,
			errb = noErr;	//	mainly for step mode debugging
	AEDesc	originalOutToken = outToken;

	Assert_(outToken.descriptorType == typeNull ||
			outToken.descriptorType == type_ModelToken ||
			outToken.descriptorType == typeAEList);
		
	//	recursively try default submodel(s)...
	if (GetDefaultSubModel()) {
		#ifdef	Debug_Throw
			//	It IS okay for the try below to fail, so turn off
			//	Throw debugging.
			StValueChanger<EDebugAction>
				okayToFail(gDebugThrow, debugAction_Nothing);
		#endif

		Try_ {
			GetDefaultSubModel()->GetModelToken(inModelID, inKeyForm,
												inKeyData, outToken);
		} Catch_(inErr) {
			err = inErr;
		//	fall through...
		} EndCatch_;
	}

	//	"this" model object...
	 if (outToken.descriptorType == typeNull) {
		Try_ {
			GetModelTokenSelf(inModelID, inKeyForm, inKeyData, outToken);
		} Catch_(inErr) {
			errb = inErr;
			//	fall through...
		} EndCatch_;
	}

	if ( (outToken.descriptorType == originalOutToken.descriptorType) &&
		 (outToken.dataHandle == originalOutToken.dataHandle) ) {
		ThrowOSErr_(errAENoSuchObject);
	}
}


// ---------------------------------------------------------------------------
//		¥ GetModelTokenSelf
// ---------------------------------------------------------------------------
//	Get a Token for the specified Model(s) without considering the
//	default submodel.
//
//	This function dispatches a call to an accessor using a specific
//	means of identification (key form). You will rarely override this.
//
//	Do not call this member.  Consider calling GetModelToken instead.

void
LModelObject::GetModelTokenSelf(
	DescType		inModelID,
	DescType		inKeyForm,
	const AEDesc	&inKeyData,
	AEDesc			&outToken) const
{
	switch (inKeyForm) {
		
		case formAbsolutePosition: {
			Int32		subPosition = 0,
						subCount;
			DescType	subSpec = typeNull;
			Boolean		needCount = false;
			
			if (inKeyData.descriptorType != typeAbsoluteOrdinal) {
				UExtractFromAEDesc::TheInt32(inKeyData, subPosition);
			} else {
				subSpec = **(DescType**) inKeyData.dataHandle;
			}
			
			if (subPosition < 0) {
				needCount = true;
			}
			
			switch (subSpec) {
				case kAEMiddle:
				case kAELast:
				case kAEAny:
					needCount = true;
					break;
			}
			
			if (needCount) {
				subCount = CountSubModels(inModelID);
			}

			switch (subSpec) {
			
				case kAEFirst:
					subPosition = 1;
					break;
					
				case kAEMiddle:
					subPosition = (subCount + 1) / 2;
					break;
					
				case kAELast:
					subPosition = subCount;
					break;
					
				case kAEAny:
					subPosition = 0;
					if (subCount > 0) {
						subPosition = ((Uint16) Random() % (Uint16) subCount)
										+ 1;
					}
					break;
					
				case kAEAll:
					subPosition = position_AllSubModels;
					break;
				
				default:
					if (subPosition < 0) {	// Negative position counts back from
											//   end, with -1 being the last item
						subPosition += subCount + 1;
					}
					break;
			}

			if (subPosition == position_AllSubModels) {
				GetAllSubModels(inModelID, outToken);
			} else {
				GetSubModelByPosition(inModelID, subPosition, outToken);
			}
			break;
		}
			
		case formName: {
			Str255	subModelName;
			UExtractFromAEDesc::ThePString(inKeyData, subModelName);
			GetSubModelByName(inModelID, subModelName, outToken);
			break;
		}
			
		case formUniqueID:
			GetSubModelByUniqueID(inModelID, inKeyData, outToken);
			break;
			
		case formRelativePosition: {
			OSType	relativePosition;
			UExtractFromAEDesc::TheEnum(inKeyData, relativePosition);
			GetModelByRelativePosition(inModelID, relativePosition, outToken);
			break;
		}
		
		case formPropertyID: {
			DescType		propertyID;
			LModelObject	*property = NULL;
			
			UExtractFromAEDesc::TheType(inKeyData, propertyID);

			property = GetModelProperty(propertyID);
			//	That wasn't GetModelPropertyAll!
			//	If property exists, it is a real property...
			
			if (!property) {

				//	Need to see if a property exists under a "fabricated"
				//	LModelProperty.  This is done by seeing if GetAEProperty
				//	succeeds.

				StAEDescriptor	bogus;
				Boolean			propertyExists = false;
				{
					#ifdef	Debug_Throw
						//	It IS okay for the try below to fail, so turn off
						//	Throw debugging.
						StValueChanger<EDebugAction>
								okayToFail(gDebugThrow, debugAction_Nothing);
					#endif

					Try_ {
						GetAEProperty(propertyID, UAppleEventsMgr::sAnyType, bogus.mDesc);
						propertyExists = true;
					} Catch_(inErr) {
						//	Don't propogate
					} EndCatch_;
				}
				
				if (propertyExists) {
					//	Fabricate the property w/ GetModelPropertyAll
					property = GetModelPropertyAll(propertyID);
				}
			}

			if (property) {
				PutInToken(property, outToken);
			}
			break;
		}

		default:
			GetSubModelByComplexKey(inModelID, inKeyForm, inKeyData,
									outToken);
			break;
	}
}


// ---------------------------------------------------------------------------
//		¥ GetSubModelByPosition
// ---------------------------------------------------------------------------
//	Pass back a Token for the SubModel(s) of the specified type at the
//	specified position (1 being the first).
//
//	Must be overridden by subclasses which have SubModels that aren't
//	implemented using the submodel list (ie lazy instantiated submodels).

void
LModelObject::GetSubModelByPosition(
	DescType		inModelID,
	Int32			inPosition,
	AEDesc			&outToken) const
{
	if (mSubModels) {
		LListIterator	iterator(*mSubModels, iterate_FromStart);
		Int32			index = 0;
		LModelObject	*p;
		Boolean			found = false;
		
		while (iterator.Next(&p)) {
			if ((p->GetModelKind() == inModelID) || (inModelID == typeWildCard)) {
				index++;
				if (index == inPosition) {
					found = true;
					break;
				}
			}
		}
		
		if (found) {
			PutInToken(p, outToken);
		}
	}
}


// ---------------------------------------------------------------------------
//		¥ GetSubModelByName
// ---------------------------------------------------------------------------
//	Pass back a Token for the SubModel(s) of the specified type with the
//	specified name
//
//	Must be overridden by subclasses which have SubModels that aren't
//	implemented using the submodel list (ie lazy instantiated submodels).

void
LModelObject::GetSubModelByName(
	DescType		inModelID,
	Str255			inName,
	AEDesc			&outToken) const
{
	if (mSubModels) {
		LListIterator	iterator(*mSubModels, iterate_FromStart);
		LModelObject	*p;
		Boolean			found = false;
		StringPtr		str;
		
		while (iterator.Next(&p)) {
			if ((p->GetModelKind() == inModelID) || (inModelID == typeWildCard)) {
				str = p->GetModelNamePtr();
				if (str) {
					if (::EqualString(str, inName, true, true)) {
						found = true;
						break;
					}
				}
			}
		}
		
		if (found) {
			PutInToken(p, outToken);
			return;
		}
	}
}


// ---------------------------------------------------------------------------
//		¥ GetSubModelByUniqueID
// ---------------------------------------------------------------------------
//	Pass back a Token for the SubModel(s) of the specified type with the
//	specified unique ID
//
//	Must be overridden by subclasses which have SubModels that aren't
//	implemented using the submodel list (ie lazy instantiated submodels).
//
//	It is up to you to decide what constitutes a unique ID and you must also
//	provide a CompareToUniqueID().

void
LModelObject::GetSubModelByUniqueID(
	DescType		inModelID,
	const AEDesc	&inKeyData,
	AEDesc			&outToken) const
{
	if (mSubModels) {
		LListIterator	iterator(*mSubModels, iterate_FromStart);
		Int32			index = 0;
		LModelObject	*p;
		Boolean			found = false;
		
		while (iterator.Next(&p)) {
			if (p->GetModelKind() == inModelID) {
				if (p->CompareToUniqueID(kAEEquals, inKeyData)) {
					found = true;
					break;
				}
			}
		}
		
		if (found) {
			PutInToken(p, outToken);
			return;
		}
	}
}


// ---------------------------------------------------------------------------
//		¥ GetModelByRelativePosition
// ---------------------------------------------------------------------------
//	Pass back a Token for the Model of the specified type at the specified
//	relative position ("before" or "after" this ModelObject).
//
//	This function handles the case where the Model to get is of the same
//	kind as this ModelObject by getting the position of this ModelObject,
//	then getting the ModelObject at the position before or after it.
//	For example, to get the "paragraph after this paragraph", we get the
//	position of the is paragraph, add one, then get the paragraph at
//	that position.
//
//	Subclass should override this function to implement a more efficient
//	way to get the next or previous item, or to handle the case where the
//	object to get is a different kind of object. For example, to be able
//	to get the "table after this paragraph".

void
LModelObject::GetModelByRelativePosition(
	DescType		inModelID,
	OSType			inRelativePosition,
	AEDesc			&outToken) const
{
	if (inModelID == GetModelKind()) {
		LModelObject	*theSuper = mSuperModel;
		if (theSuper == nil) {
			theSuper = GetDefaultModel();
		}
	
		Int32	thePosition = theSuper->GetPositionOfSubModel(inModelID, this);

		switch (inRelativePosition) {

			case kAENext:
				thePosition += 1;
				break;
				
			case kAEPrevious:
				thePosition -= 1;
				break;
			
			default:
				return;
		}
		theSuper->GetSubModelByPosition(inModelID, thePosition, outToken);
	}
}


// ---------------------------------------------------------------------------
//		¥ GetSubModelByComplexKey
// ---------------------------------------------------------------------------
//	Pass back a Token for the SubModel(s) of the specified type identified
//	by a complex key. Complex keys are formRange, formTest, and formWhose.
//
//	Subclasses which support complex keys for identifying SubModels must
//	override this function.

void
LModelObject::GetSubModelByComplexKey(
	DescType		/* inModelID */,
	DescType		/* inKeyForm */,
	const AEDesc&	/* inKeyData */,
	AEDesc&			/* outToken */) const
{
	//	try submodels...
	//	How?  It is complex key & application specific.
}


// ---------------------------------------------------------------------------
//		¥ GetAllSubModels
// ---------------------------------------------------------------------------
//	Pass back a Token list for all SubModels of the specified type
//
//	This function uses a brute force approach:
//		Get the count of items
//		Get Token for each item in order and add it to the list
//
//	Override this function if a ModelObject can create this Token list
//	in a more efficient manner.

void
LModelObject::GetAllSubModels(
	DescType		inModelID,
	AEDesc			&outToken) const
{
	Int32	subCount = CountSubModels(inModelID);
	OSErr	err;
	
	if (subCount > 0) {
		if (outToken.descriptorType == typeNull) {
			err = ::AECreateList(nil, 0, false, &outToken);
			ThrowIfOSErr_(err);
		}
		
		for (Int32 i = 1; i <= subCount; i++) {
			StAEDescriptor	subToken;
			
			GetSubModelByPosition(inModelID, i, subToken.mDesc);
			err = ::AEPutDesc(&outToken, 0, &subToken.mDesc);
			ThrowIfOSErr_(err);
		}
	}
}


// ---------------------------------------------------------------------------
//		¥ GetPositionOfSubModel
// ---------------------------------------------------------------------------
//	Return the absolute position of the specified SubModel, with 1 being
//	the first
//
//	Must be overridden by subclasses which have SubModels that aren't
//	implemented using the submodel list (ie lazy instantiated submodels).

Int32
LModelObject::GetPositionOfSubModel(
	DescType			inModelID,
	const LModelObject	*inSubModel) const
{
	Int32			index = 0;

	//	try submodels...
	if (mSubModels) {
		LListIterator	iterator(*mSubModels, iterate_FromStart);
		LModelObject	*p;
		Boolean			found = false;
		
		while (iterator.Next(&p)) {
			if ((p->GetModelKind() == inModelID) || (inModelID == typeWildCard)) {
				index++;
				if (p == inSubModel)
					return index;
			}
		}
	}
	
	if (index == 0)
		ThrowOSErr_(errAENoSuchObject);	//	Actually more of an internal
										//	implementation error.
	
	return index;
}


// ---------------------------------------------------------------------------
//		¥ GetInsertionTarget
//		¥ GetInsertionContainer
//		¥ GetInsertionElement
// ---------------------------------------------------------------------------
//	These three functions help in dealing with typeInsertionLoc parameters.
//	
//	GetInsertionTarget converts the ModelObject referred to in the kAEObject
//	typeInsertionLoc sub parameter to the actual insertion target.
//	Specifically, GetInsertionTarget takes default submodels into
//	consideration.  The GetInsertionTarget result should be used as the
//	object to pass the other two messages thru.
//
//	GetInsertionContainer returns the "container" object of the
//	typeInsertionLoc parameter.
//
//	GetInsertionElement returns the actual element that represents the
//	typeInsertionLoc ModelObject.
//
//	All three methods take an "inInsertPosition" DescType parameter.
//	InInsertPosition corresponds to the kAEPosition value of the
//	typeInsertionLoc record.  If the AppleEvent parameter corresponding to
//	an insertion location is an object specifier instead of a
//	typeInsertionLoc, use an inInsertPosition value of typeNull.  This
//	convention is useful when an insertion location is completely
//	representable by an object specifier rather than having to use a 
//	typeInsertionLoc record.
//
//	You may need to override GetInsertionElement to lazy instantiate an
//	object, or allow its return value to be nil.  The later works fine
//	in HandleCreateElementEvents where the element is the result of the
//	HandleCreateElementEvent.

LModelObject *
LModelObject::GetInsertionTarget(DescType inInsertPosition) const
{
	LModelObject	*model = NULL;
	
	if (GetDefaultSubModel())
		model = GetDefaultSubModel()->GetInsertionTarget(inInsertPosition);

	if (model)
		return model;
	else
		return (LModelObject *) this;
}

LModelObject *
LModelObject::GetInsertionContainer(DescType inInsertPosition) const
{
	LModelObject	*container = NULL;

	switch (inInsertPosition) {

		case typeNull:
			container = ((LModelObject *)this)->GetSuperModel();
			break;
		
		case kAEBefore:		// For these positions, the insert target
		case kAEAfter:		// specifies an object in the same
		case kAEReplace:	// container as that in which to create
							// the new element
			container = ((LModelObject *)this)->GetSuperModel();
			break;
			
		case kAEBeginning:	// For these position, the insert target
		case kAEEnd:		// is the container in which to create
							// the new element
			container = (LModelObject *)this;
			break;
			
		default:
			ThrowOSErr_(errAEEventNotHandled);
			break;
	}
	
	return container;
}

LModelObject *
LModelObject::GetInsertionElement(DescType inInsertPosition) const
{
	LModelObject	*model = NULL;

	switch (inInsertPosition) {

		case typeNull:
			model = (LModelObject *)this;
			break;
			
		case kAEReplace:
			model = (LModelObject *)this;
			break;

		case kAEBefore:
		case kAEAfter:
		case kAEBeginning:
		case kAEEnd:
			//	you need to override this method to do the mapping to
			//	outModel or, if this is being used in a HandleCreateElementEvent,
			//	recognize the outModel hasn't been created yet.
			model = NULL;
			break;
		
		default:
			Throw_(errAEEventNotHandled);
			break;
	}
	
	return model;
}


// ---------------------------------------------------------------------------
//		¥ GetModelNamePtr
// ---------------------------------------------------------------------------
//	Return the name of a ModelObject

const StringPtr
LModelObject::GetModelNamePtr() const
{
	return nil;
}


// ---------------------------------------------------------------------------
//		¥ CompareToModel
// ---------------------------------------------------------------------------
//	Return result of comparing this ModelObject with another one
//
//	Subclasses must override this method to support accessing objects
//	by selection criteria, commonly called "whose" clauses.
//		For example:  get all words equal to word 1
//
//	This default implementation only provides model object pointer equivalence
//	and containment.

Boolean
LModelObject::CompareToModel(
	DescType		inComparisonOperator,
	LModelObject*	inCompareModel) const
{
	Boolean	result = false;
	
	switch (inComparisonOperator) {

		case kAEEquals:
			result = inCompareModel == this;
			break;
			
		case kAEContains:
			if (inCompareModel)
				result = inCompareModel->IsSubModelOf(this);
			break;
			
		
		default:
			ThrowOSErr_(errAEEventNotHandled);
			break;
	}
	
	return result;
}	


// ---------------------------------------------------------------------------
//		¥ CompareToDescriptor
// ---------------------------------------------------------------------------
//	Return result of comparing this ModelObject with Descriptor data
//
//	Subclasses must override this method to support accessing objects
//	by selection criteria, commonly called "whose" clauses.
//		For example:  get all words that contain "foo"

Boolean
LModelObject::CompareToDescriptor(
	DescType		/* inComparisonOperator */,
	const AEDesc&	/* inCompareDesc */) const
{
	ThrowOSErr_(errAEEventNotHandled);
	return false;
}


// ---------------------------------------------------------------------------
//		¥ CompareToUniqueID
// ---------------------------------------------------------------------------
//	Return result of comparing this ModelObject with a "unique id."
//
//	It is up to you to decide what constitutes a unique ID.

Boolean
LModelObject::CompareToUniqueID(
	DescType		/* inComparisonOperator */,
	const AEDesc&	/* inCompareDesc */) const
{
	ThrowOSErr_(errAEEventNotHandled);
	return false;
}


// ---------------------------------------------------------------------------
//		¥ CompareProperty
// ---------------------------------------------------------------------------
//	Return the result of comparing a property of this ModelObject with
//	another object
//
//	Subclasses must override this method to support accessing objects by
//	selection criteria on their properties, commonly called "whose" clauses.
//		For example:  get all words whose point size is less than 12 

Boolean
LModelObject::CompareProperty(
	DescType		/* inPropertyID */,
	DescType		/* inComparisonOperator */,
	const AEDesc&	/* inCompareObjectOrDesc */) const
{
	ThrowOSErr_(errAEEventNotHandled);
	return false;
}


// ---------------------------------------------------------------------------
//		¥ GetModelPropertyAll
// ---------------------------------------------------------------------------
//	Return a ModelObject object representing the specified property
//
//	This implementation always creates a LModelProperty object for the
//	specificied property (although the property may not really exist).
//	For nonexistent properties, a later attempt to get or set the property
//	value will pass back an error.
//
//	To return a special LModelObject for a given property,
//	override GetModelProperty (which is called by this function).

LModelObject*
LModelObject::GetModelPropertyAll(
	DescType	inProperty) const
{
	LModelObject	*property = GetModelProperty(inProperty);
	
	if (property == nil) {
		property = new LModelProperty(inProperty, (LModelObject *)this);
	}
	
	return property;
}


// ---------------------------------------------------------------------------
//		¥ GetModelProperty
// ---------------------------------------------------------------------------
//	Return a ModelObject object for explicitly defined properties
//
//	Must be overridden for subclasses which return "special" ModelObjects for
//	given property id's.
//
//	For the default case in overrides, return the inherited value.

LModelObject*
LModelObject::GetModelProperty(
	DescType	/* inProperty */) const
{
	return nil;
}


// ---------------------------------------------------------------------------
//		¥ MakeSpecifier
// ---------------------------------------------------------------------------
//	Make an Object Specifier for a ModelObject
//
//	This is a helper function that uses recursion to call MakeSelfSpecifier
//	for a ModelObject and all its SuperModels. You will not need to
//	override this function.

void
LModelObject::MakeSpecifier(
	AEDesc	&outSpecifier) const
{
	if (GetDefaultSuperModel() != NULL) {
		GetDefaultSuperModel()->MakeSpecifier(outSpecifier);
	} else {
		StAEDescriptor		superSpecifier;
		
		if (mSuperModel != nil) {
			mSuperModel->MakeSpecifier(superSpecifier.mDesc);
		}
		
		MakeSelfSpecifier(superSpecifier.mDesc, outSpecifier);
	}
}


// ---------------------------------------------------------------------------
//		¥ MakeSelfSpecifier
// ---------------------------------------------------------------------------
//	Make an Object Specifier for a ModelObject
//
//	This function creates a specifier using the absolute position of
//	a ModelObject within its SuperModel.
//
//	Override this function for subclasses which can't be specified by
//	absolute position or for which another means of identification
//	(for example, by name) is more appropriate or that don't have
//	a SuperModel (access from null container).

void
LModelObject::MakeSelfSpecifier(
	AEDesc	&inSuperSpecifier,
	AEDesc	&outSelfSpecifier) const
{
		// Find position of this ModelObject within its SuperModel
		// Error if SuperModel does not exist or if SuperModel
		// can't return a valid position for this object

	if (mSuperModel == nil) {
		ThrowOSErr_(errAEEventNotHandled);
	}

	Int32	modelIndex = mSuperModel->GetPositionOfSubModel(
										GetModelKind(), this);
	if (modelIndex == 0) {
		ThrowOSErr_(errAEEventNotHandled);
	}
	
	StAEDescriptor	absPosKeyData;
	OSErr	err = ::CreateOffsetDescriptor(modelIndex, &absPosKeyData.mDesc);
	ThrowIfOSErr_(err);
	
	err = ::CreateObjSpecifier(GetModelKind(), &inSuperSpecifier,
			formAbsolutePosition, &absPosKeyData.mDesc, false,
			&outSelfSpecifier);
	ThrowIfOSErr_(err);
}


// ---------------------------------------------------------------------------
//		¥ HandleAppleEventAll
// ---------------------------------------------------------------------------
//	Try handling an AppleEvent by both "this" ModelObject and its default
//	submodel.
//
//	If submodel handling succeeds, "this" is not tried.

void
LModelObject::HandleAppleEventAll(
	const AppleEvent	&inAppleEvent,
	AppleEvent			&outAEReply,
	AEDesc				&outResult,
	long				inAENumber)
{
	Boolean	handled = false;

	if (GetDefaultSubModel()) {
		try {
			#ifdef	Debug_Throw
				//	It IS okay for the try below to fail, so turn off
				//	Throw debugging.
				StValueChanger<EDebugAction>
					okayToFail(gDebugThrow, debugAction_Nothing);
			#endif
	
			GetDefaultSubModel()->HandleAppleEventAll(inAppleEvent,
									outAEReply, outResult, inAENumber);
			handled = true;
		}
		
		catch (ExceptionCode inErr) {
							// Do nothing. It's OK for the default
		}					// SubModel to fail.
	}

	if (!handled) {			// Default SubModel didn't handle it, so this
							// object must handle it
		HandleAppleEvent(inAppleEvent, outAEReply, outResult, inAENumber);
	}
}


// ---------------------------------------------------------------------------
//		¥ HandleCreateElementEventAll
// ---------------------------------------------------------------------------
//	Try handling a CreateElementEvent by both "this" ModelObject and its
//	default submodel.
//
//	If submodel handling succeeds, "this" is not tried.

LModelObject*
LModelObject::HandleCreateElementEventAll(
	DescType			inElemClass,
	DescType			inInsertPosition,
	LModelObject		*inTargetObject,
	const AppleEvent	&inAppleEvent,
	AppleEvent			&outAEReply)
{
	LModelObject	*model = NULL;
	OSErr			err = noErr;
	Boolean			handled = false;
	
	if (!handled && GetDefaultSubModel()) {
		Try_ {
			#ifdef	Debug_Throw
				//	It IS okay for the try below to fail, so turn off
				//	Throw debugging.
				StValueChanger<EDebugAction>
					okayToFail(gDebugThrow, debugAction_Nothing);
			#endif
	
			model = GetDefaultSubModel()->
						HandleCreateElementEventAll(
									inElemClass, inInsertPosition,
									inTargetObject, inAppleEvent, outAEReply);
			handled = true;
		} Catch_(inErr) {
			handled = false;
			err = inErr;	//	don't rethrow -- yet
		} EndCatch_;
	}
	
	if (!handled) {
		Try_ {
			model = HandleCreateElementEvent(
									inElemClass, inInsertPosition,
									inTargetObject, inAppleEvent, outAEReply);
			handled = true;
		} Catch_(inErr) {
			handled = false;
			err = inErr;	//	don't rethrow -- yet
		} EndCatch_;
	}
	
	if (!handled)
		ThrowOSErr_(err);

	return model;
}


// ---------------------------------------------------------------------------
//		¥ HandleAppleEvent
// ---------------------------------------------------------------------------
//	Respond to an AppleEvent. This is the default handler for AppleEvents
//	that do not have a specific handler.
//
//	Subclasses must override this function to support AppleEvents other than:
//		Create Element,
//		Get Data,
//		Set Data,
//		Clone,
//		Move,
//		CountElements,
//		Delete,
//	
//
//	The events above have specific handler methods which may be overridden.

void
LModelObject::HandleAppleEvent(
	const AppleEvent	&inAppleEvent,
	AppleEvent			&outAEReply,
	AEDesc				&outResult,
	long				inAENumber)
{
	switch (inAENumber) {

		case ae_Clone:
			HandleClone(inAppleEvent, outAEReply, outResult);
			break;

		case ae_Move:
			HandleMove(inAppleEvent, outAEReply, outResult);
			break;

		case ae_CountElements:
#ifdef	UAEGIZMOS
		{
			LAESubDesc	classSD(keyAEObjectClass, inAppleEvent, typeType);
			
			HandleCount(classSD.ToType(), outResult);
			break;
		}
#else
		{
			StAEDescriptor	classDesc(inAppleEvent, keyAEObjectClass, typeType);
			DescType		classType;
			UExtractFromAEDesc::TheType(classDesc, classType);
			HandleCount(classType, outResult);
			break;
		}
#endif
//		case ae_Create:						//	See HandleCreateElementEvent

		case ae_Delete:
			HandleDelete(outAEReply, outResult);
			break;
		
//		case ae_DoObjectsExist:				//	Implemented in elsewhere?

//		case ae_GetClassInfo:				//	Not implemented

		case ae_GetData:
		case ae_GetDataSize:
		case ae_SetData:
		{
			//	Must be referring to contents so...  simulate it.
			LModelObject	*content = GetModelPropertyAll(pContents);
			ThrowIfNULL_(content);
			content->HandleAppleEvent(inAppleEvent, outAEReply, outResult, inAENumber);
			break;
		}

		default:
			ThrowOSErr_(errAEEventNotHandled);
			break;
	}
}


// ---------------------------------------------------------------------------
//		¥ HandleCreateElementEvent
// ---------------------------------------------------------------------------
//	Respond to a Create Element AppleEvent ("make new" in AppleScript).
//
//	The parameters specify the Class ID for the new element, and where to
//	insert the new element in relation to a target object. Also, the
//	AppleEvent record may contain additional parameters that specify
//	initial values for the new element.
//
//	Subclasses which have SubModels which can be dynamically created should
//	override this function. Return a pointer to the newly created SubModel.
//	The calling function takes care of putting an object specifier for
//	this new SubModel in the AppleEvent reply. 

LModelObject*
LModelObject::HandleCreateElementEvent(
	DescType			/* inElemClass */,
	DescType			/* inInsertPosition */,
	LModelObject*		/* inTargetObject */,
	const AppleEvent&	/* inAppleEvent */,
	AppleEvent&			/* outAEReply */)
{
	ThrowOSErr_(errAEEventNotHandled);
	return nil;
}


// ---------------------------------------------------------------------------
//		¥ HandleCount
// ---------------------------------------------------------------------------
//	Respond to the Count Elements AppleEvent ("count" in AppleScript).
//
//	The parameters specify the Class ID of submodels to count.
//
//	You should not need to explicitly call this method.
//	You should not need to override this method.

void
LModelObject::HandleCount(
	DescType			inModelID,
	AppleEvent			&outResult)
{
	Int32	count = CountSubModels(inModelID);
	
	UAEDesc::AddPtr(&outResult, 0, typeLongInteger, &count, sizeof(count));
}

// ---------------------------------------------------------------------------
//		¥ HandleDelete
// ---------------------------------------------------------------------------
//	Respond to the Delete AppleEvent ("delete" in AppleScript).
//
//	You should not need to explicitly call this method.
//	You may need to override and inherit this method.

void
LModelObject::HandleDelete(
	AppleEvent&	/* outAEReply */,
	AEDesc&		/* outResult */)
{
	if (GetModelKind() == cProperty)
		Throw_(errAEEventNotHandled);	//	doesn't make sense to delete properties.	
	
	SetLaziness(true);	//	Should automatically delete when AE is done
}

// ---------------------------------------------------------------------------
//		¥ HandleClone
// ---------------------------------------------------------------------------
//	Respond to the Clone AppleEvent ("duplicate" in AppleScript).
//
//	You should not need to explicitly call this method.
//	You rarely need to override this method.

void
LModelObject::HandleClone(
	const AppleEvent	&inAppleEvent,
	AppleEvent&			/* outAEReply */,
	AEDesc				&outResult
)
{
#ifdef	UAEGIZMOS
	OSErr			err = noErr;
	StAEDescriptor	objProps,
					createEvent,
					replyEvent;
	DescType		objectClass = GetModelKind();
	LAESubDesc		targetSD(keyAEInsertHere, inAppleEvent);

	//	¥¥	Get data for clone object
	this->GetImportantAEProperties(objProps.mDesc);

	//	¥¥	Create element event
	{
		LAEStream	stream(kAECoreSuite, kAECreateElement);
		
		//	keyAEData
		//		left empty -- all data (if any) goes through objProps
		
		//	keyAEInsertHere
		stream.WriteKey(keyAEInsertHere);
		switch (targetSD.GetType()) {
	
			case typeNull:
				//	make an insertion location specifier for after this object
				stream.OpenRecord(typeInsertionLoc);
				
				stream.WriteKey(keyAEObject);
				stream.WriteSpecifier(this);
				
				stream.WriteKey(keyAEPosition);
				stream.WriteEnumDesc(kAEAfter);
				
				stream.CloseRecord();
				break;
	
			case typeObjectSpecifier:
				stream.WriteSubDesc(targetSD);
				objectClass = targetSD.KeyedItem(keyAEDesiredClass).ToType();
				break;
	
			case typeInsertionLoc:
			{
				stream.WriteSubDesc(targetSD);
				LModelObject	*target = targetSD.KeyedItem(keyAEObject).ToModelObject();
				target = target->GetInsertionTarget(targetSD.KeyedItem(keyAEPosition).ToEnum());
				objectClass = target->GetModelKind();
				break;
			}
		}
	
		//	keyAEObjectClass
		stream.WriteKey(keyAEObjectClass);
		stream.WriteTypeDesc(objectClass);
		
		//	keyAEPropData
		if (objProps.mDesc.descriptorType != typeNull) {
			stream.WriteKeyDesc(keyAEPropData, objProps.mDesc);
		}
		
		stream.Close(&createEvent.mDesc);
	}
	
	//	¥¥	Execute create element event (but don't record)
	StLazyLock	lockMe(this);	//	Don't lose ourself from implied FinalizeLazies().
	UAppleEventsMgr::SendAppleEventWithReply(createEvent.mDesc, replyEvent.mDesc, false);
	
	//	¥¥	Put result of create element event in reply
	err = AEGetKeyDesc(&replyEvent.mDesc, keyAEResult, typeObjectSpecifier, &outResult);
	ThrowIfOSErr_(err);
#else
	OSErr			err = noErr;
	StAEDescriptor	objProps,
					createEvent,
					replyEvent;
	DescType		objectClass = GetModelKind();
	StAEDescriptor	targetD(inAppleEvent, keyAEInsertHere);

	//	¥¥	Get data for clone object
	this->GetImportantAEProperties(objProps.mDesc);

	//	¥¥	Create element event
	{
		UAppleEventsMgr::MakeAppleEvent(kAECoreSuite, kAECreateElement, createEvent.mDesc);
		
		//	keyAEData
		//		left empty -- all data (if any) goes through objProps
		
		//	keyAEInsertHere
		StAEDescriptor	insertHere;
		switch (targetD.mDesc.descriptorType) {
	
			case typeNull:
			{
				//	make an insertion location specifier for after this object
				StAEDescriptor	ospec;
				MakeSpecifier(ospec.mDesc);
				UAEDesc::MakeInsertionLoc(ospec.mDesc, kAEAfter, &insertHere.mDesc);
				UAEDesc::AddKeyDesc(&createEvent.mDesc, keyAEInsertHere, insertHere);
				break;
			}
	
			case typeObjectSpecifier:
			{
				UAEDesc::AddKeyDesc(&createEvent.mDesc, keyAEInsertHere, targetD);
				StAEDescriptor	tokenD;
				err = LModelDirector::Resolve(targetD, tokenD.mDesc);
				ThrowIfOSErr_(err);
				LModelObject	*target = GetModelFromToken(tokenD);
				objectClass = target->GetModelKind();
				break;
			}
	
			case typeInsertionLoc:
			{
				UAEDesc::AddKeyDesc(&createEvent.mDesc, keyAEInsertHere, targetD);
				StAEDescriptor	objectD(targetD, keyAEObject, typeObjectSpecifier),
								tokenD;
				err = LModelDirector::Resolve(objectD, tokenD.mDesc);
				ThrowIfOSErr_(err);
				LModelObject	*target = GetModelFromToken(tokenD);
				StAEDescriptor	positionD(targetD, keyAEPosition, typeEnumeration);
				DescType		position;
				UExtractFromAEDesc::TheEnum(positionD, position);
				target = target->GetInsertionTarget(position);
				objectClass = target->GetModelKind();
				break;
			}
		}
	
		//	keyAEObjectClass
		StAEDescriptor	classD(typeType, &objectClass, sizeof(objectClass));
		UAEDesc::AddKeyDesc(&createEvent.mDesc, keyAEObjectClass, classD);
		
		//	keyAEPropData
		if (objProps.mDesc.descriptorType != typeNull) {
			UAEDesc::AddKeyDesc(&createEvent.mDesc, keyAEPropData, objProps);
		}
	}
	
	//	¥¥	Execute create element event (but don't record)
	StLazyLock	lockMe(this);	//	Don't lose ourself from implied FinalizeLazies().
	UAppleEventsMgr::SendAppleEventWithReply(createEvent.mDesc, replyEvent.mDesc, false);
	
	//	¥¥	Put result of create element event in reply
	err = AEGetKeyDesc(&replyEvent.mDesc, keyAEResult, typeObjectSpecifier, &outResult);
	ThrowIfOSErr_(err);
#endif
}

// ---------------------------------------------------------------------------
//		¥ HandleMove
// ---------------------------------------------------------------------------
//	Respond to the Move AppleEvent ("move" in AppleScript).
//
//	You should not need to explicitly call this method.
//	You will rarely need to override this method.

void
LModelObject::HandleMove(
	const AppleEvent	&inAppleEvent,
	AppleEvent			&outAEReply,
	AEDesc				&outResult)
{
#ifdef	UAEGIZMOS
	StAEDescriptor	cloneResult,
					deleteResult;
	LModelObject	*clone = NULL;
	
	//	¥¥	Clone behavior
	//
	//		If this is an offset based lazy object, the create element event
	//		in the default HandleClone should "magically" update this object's
	//		offsets as necessary.
	this->HandleClone(inAppleEvent, outAEReply, cloneResult.mDesc);
	
	//	¥¥	Delete behavior
	//
	//		If the object to be removed is also in a lazy object scheme, 
	//		conversion of the above cloneResult to an LModelObject will allow 
	//		the result object specifier (the new copy) to have its offsets
	//		"magically" updated by the HandleDelete.  So, get the clone now,
	//		do the delete, then make the adjusted result specifier.
	LAESubDesc		cloneSD(cloneResult.mDesc);
	clone = (LModelObject *)cloneSD.ToModelObject();
	this->HandleDelete(outAEReply, deleteResult.mDesc);

	//	¥¥	"Hole specifier?"
	//
	//	As an aid for undo inside of a PowerPlant app, the HandleDelete
	//	has already set the keyAEInsertHere parameter of the reply to the "hole"
	//	left by the deletion.

	//	¥¥	Result
	//
	//	Fill in the adjusted object specifier
	clone->MakeSpecifier(outResult);
#else
	StAEDescriptor	cloneResult,
					deleteResult;
	LModelObject	*clone = NULL;
	
	//	¥¥	Clone behavior
	//
	//		If this is an offset based lazy object, the create element event
	//		in the default HandleClone should "magically" update this object's
	//		offsets as necessary.
	this->HandleClone(inAppleEvent, outAEReply, cloneResult.mDesc);
	
	//	¥¥	Delete behavior
	//
	//		If the object to be removed is also in a lazy object scheme, 
	//		conversion of the above cloneResult to an LModelObject will allow 
	//		the result object specifier (the new copy) to have its offsets
	//		"magically" updated by the HandleDelete.  So, get the clone now,
	//		do the delete, then make the adjusted result specifier.
	OSErr	err;
	StAEDescriptor	token;
	err = LModelDirector::Resolve(cloneResult, token.mDesc);
	ThrowIfOSErr_(err);
	clone = GetModelFromToken(token);
	this->HandleDelete(outAEReply, deleteResult.mDesc);

	//	¥¥	"Hole specifier?"
	//
	//	As an aid for undo inside of a PowerPlant app, the HandleDelete
	//	has already set the keyAEInsertHere parameter of the reply to the "hole"
	//	left by the deletion.

	//	¥¥	Result
	//
	//	Fill in the adjusted object specifier
	clone->MakeSpecifier(outResult);
#endif
}


// ---------------------------------------------------------------------------
//		¥ GetAEProperty
// ---------------------------------------------------------------------------
//	Return a descriptor for the specified Property
//
//	Subclasses which have Properties must override this function

void
LModelObject::GetAEProperty(
	DescType		inProperty,
	const AEDesc&	/* inRequestedType */,
	AEDesc			&outPropertyDesc) const
{
	switch (inProperty) {
		case pClass:
		{
			DescType	value = GetModelKind();
				
			UAEDesc::AddPtr(&outPropertyDesc, 0, typeType, &value, sizeof(value));
			break;
		}
		
		case pContents:
			MakeSpecifier(outPropertyDesc);
			break;
		
		default:
			ThrowOSErr_(errAEUnknownObjectType);
			break;
	}
}


// ---------------------------------------------------------------------------
//		¥ SetAEProperty
// ---------------------------------------------------------------------------
//	Set a Property using data from a descriptor
//
//	Subclasses which have modifiable Properties must override this function

void
LModelObject::SetAEProperty(
	DescType		/* inProperty */,
	const AEDesc&	/* inValue */,
	AEDesc&			/* outAEReply */)
{
	ThrowOSErr_(errAEUnknownObjectType);
}


// ---------------------------------------------------------------------------
//		¥ GetImportantAEProperties
// ---------------------------------------------------------------------------
//	Return a record containing all "important" Properties
//
//	"Important" includes things necessary for cloning.
//
//	Subclasses which have Properties should override this function like:
//
//	{
//		inherited::GetImportantAEProperties(outRecord);
//
//		{	//	font
//			StAEDescriptor	aProp;
//			GetAEProperty(pFont, typeDesc, aProp.mDesc);
//			UAEDesc::AddKeyDesc(&outRecord, pFont, aProp.mDesc);
//		}
//		...
//		{	//	size
//			StAEDescriptor	aProp;
//			GetAEProperty(pSize, typeDesc, aProp.mDesc);
//			UAEDesc::AddKeyDesc(&outRecord, pSize, aProp.mDesc);
//		}
//	}

void
LModelObject::GetImportantAEProperties(AERecord &outRecord) const
{
	OSErr			err;
	StAEDescriptor	contents,
					reqType;
	
	#ifdef	Debug_Throw
		//	It IS okay for the try below to fail, so turn off
		//	Throw debugging.
		StValueChanger<EDebugAction>
			okayToFail(gDebugThrow, debugAction_Nothing);
	#endif

	Try_ {		
		GetAEProperty(pContents, reqType.mDesc, contents.mDesc);
		UAEDesc::AddKeyDesc(&outRecord, pContents, contents.mDesc);
	} Catch_(inErr) {
		err = inErr;
	} EndCatch_;
}


// ---------------------------------------------------------------------------
//		¥ SendSelfAE
// ---------------------------------------------------------------------------
//	Send an AppleEvent to the current process with this ModelObject as
//	the direct parameter

void
LModelObject::SendSelfAE(
	AEEventClass	inEventClass,
	AEEventID		inEventID,
	Boolean			inExecute)
{
	AppleEvent	theAppleEvent;
	UAppleEventsMgr::MakeAppleEvent(inEventClass, inEventID, theAppleEvent);

	StAEDescriptor	modelSpec;
	MakeSpecifier(modelSpec.mDesc);
	OSErr err = ::AEPutParamDesc(&theAppleEvent, keyDirectObject,
									&modelSpec.mDesc);
	ThrowIfOSErr_(err);

	UAppleEventsMgr::SendAppleEvent(theAppleEvent, inExecute);
}


// ===========================================================================
// ¥ Static Member Functions						 Static Member Functions ¥
// ===========================================================================

// ---------------------------------------------------------------------------
//	DefaultModel
// ---------------------------------------------------------------------------
//
//	The DefaultModel responds to all AppleEvents not directed at any
//	specific object. It also represents the "null" container and the
//	top container in the AppleEvent Object Model container hierarchy.
//	In most cases, the DefaultModel will be the "Application" object.
//
//	If you do not use a PowerPlant class that automatically sets the
//	DefaultModel (such as one of the Application-type classes), you
//	must create a class that handles the DefaultModel's responsibilities
//	and set the DefaultModel appropriately.

LModelObject*
LModelObject::GetDefaultModel()
{
	return sDefaultModel;
}


void
LModelObject::SetDefaultModel(
	LModelObject	*inModel)
{
	sDefaultModel = inModel;
}


// ---------------------------------------------------------------------------
//	StreamingModel
// ---------------------------------------------------------------------------
//
//	The StreamingModel refers to the last LModelObject that was constructed
//	and that still exists.  It is useful when constructing LModelObject
//	hierarchies.
//
//
//	Implementation note:
//	
//	The StreamingModel should not be a "claiming" shared object reference.
//	Doing such would mean LModelObjects couldn't go away as long as they were
//	the StreamingModel -- that would require extra & explicit code. So...
//	The sStreamingModel is a normal reference that is never left dangling
//	because ~LModelObject will SetStreamingModel(NULL) as necessary.


LModelObject*
LModelObject::GetStreamingModel()
{
	return sStreamingModel;
}

void
LModelObject::SetStreamingModel(
	LModelObject	*inModel)
{
	sStreamingModel = inModel;
}


// ---------------------------------------------------------------------------
//	TellTarget
// ---------------------------------------------------------------------------
//
//	The "TellTarget" (the default submodel for the Application) allows the
//	recording of more concise scripts.
//
//	Call DoAESwitchTellTarget when your application wishes to change the
//	"focus," or "TellTarget" of AppleEvent recording.  When calling
//	DoAESwitchTellTarget, any object specifiers constructed
//	with a previous TellTarget in place become "stale."  This means those
//	object specifiers shouldn't be used when sending or recording
//	subsequent AppleEvents.

void
LModelObject::DoAESwitchTellTarget(
	LModelObject	*inModelObject)
{
	if 	(inModelObject == GetTellTarget())
		return;	//	Don't record a non-effective change
	
	StAEDescriptor	appleEvent;
	UAppleEventsMgr::MakeAppleEvent(kAEPowerPlantSuite, kAESwitchTellTarget, appleEvent.mDesc);
	
	if (inModelObject && (inModelObject != GetTellTarget())) {
		StTempTellTarget	makeOSpecsBasedFrom(NULL);

		StAEDescriptor	ospec;
		inModelObject->MakeSpecifier(ospec.mDesc);
		UAEDesc::AddKeyDesc(&appleEvent.mDesc, keyAEData, ospec);
	}
	
	UAppleEventsMgr::SendAppleEvent(appleEvent.mDesc);
}

	
LModelObject *
LModelObject::GetTellTarget(void)
{
	LModelObject	*defModel = GetDefaultModel();
	
	ThrowIfNULL_(defModel);
	
	return defModel->GetDefaultSubModel();
}


void
LModelObject::SetTellTarget(LModelObject *inModel)
{
	LModelObject	*defModel = GetDefaultModel();
	
	ThrowIfNULL_(defModel);
	
	defModel->SetDefaultSubModel(inModel);
}


// ---------------------------------------------------------------------------
//		¥ PutInToken
// ---------------------------------------------------------------------------
//	Place the pointer to a ModelObject within a Token. Tokens are used
//	when resolving an AppleEvent object specifier

void
LModelObject::PutInToken(
	LModelObject	*inModel,
	AEDesc			&outToken)
{
	if (inModel == nil)
		ThrowOSErr_(errAENoSuchObject);
		
	SModelToken		theToken;
	theToken.modelObject = inModel;
	
	//	AddPtr will automatically convert outToken to an AEList
	//	when necessary.
	UAEDesc::AddPtr(&outToken, 0, type_ModelToken, &theToken, sizeof(theToken));
}


// ---------------------------------------------------------------------------
//		¥ GetModelFromToken
// ---------------------------------------------------------------------------
//	Return the ModelObject represented by a Token Descriptor record

LModelObject*
LModelObject::GetModelFromToken(
	const AEDesc	&inToken)
{
	LModelObject*	theModel = nil;

	switch (inToken.descriptorType) {

		case typeNull:
			theModel = GetDefaultModel();
			break;
			
		case type_ModelToken:
			theModel = (**((SModelTokenH) inToken.dataHandle)).modelObject;
			break;
			
		case typeAEList:
			SignalPStr_("\pCan't get token from a list");
			ThrowOSErr_(errAEUnknownObjectType);
			break;

		default:
			SignalPStr_("\pUnknown token type");
			ThrowOSErr_(errAEUnknownObjectType);
			break;
	}

	return theModel;
}


// ---------------------------------------------------------------------------
//		¥ FinalizeLazies
// ---------------------------------------------------------------------------
//	Send a Finalize message to all objects in the lazy list.

void
LModelObject::FinalizeLazies()
{
	Try_ {
		LListIterator	iterator(*sLazyModels, iterate_FromEnd);
		LModelObject	*model;

		while (iterator.Previous(&model)) {
		
			Try_ {
				model->Finalize();
			} Catch_(inErr) {
			} EndCatch_;
			
		}
		
	} Catch_(inErr) {
		Assert_(false);
		//	Because of usage by LModelDirector, never throw an exception.
	} EndCatch_;
}


// ---------------------------------------------------------------------------
//		¥ AddLazy
// ---------------------------------------------------------------------------
//	Adds the object to the "lazy" list of objects that receive Finalize
//	messages in response to FinalizeLazies.
//
//	You should never need to call this function.  Use SetLaziness(true)
//	instead.

void
LModelObject::AddLazy(
	LModelObject	*inModel)
{
	sLazyModels->InsertItemsAt(1, arrayIndex_Last, &inModel);
}


// ---------------------------------------------------------------------------
//		¥ RemoveLazy
// ---------------------------------------------------------------------------
//	Remove the object from the "lazy" list of objects.
//
//	You should never need to call this function.  Use SetLaziness(false)
//	instead.

void
LModelObject::RemoveLazy(
	LModelObject	*inModel)
{
	sLazyModels->Remove(&inModel);
}


// ===========================================================================
// ¥ Helper	Classes											  Helper Classes ¥
// ===========================================================================


// ---------------------------------------------------------------------------
//	StLazyLock
// ---------------------------------------------------------------------------
//
//	StLazyLock is an "St" class that temporarily changes the "laziness" of a
//	ModelObject to false -- preventing a ModelObject from being deleted 
//	during the scope of the StLazyLock.  This is similar to StSharer but,
//	when the StLazyLock is destroyed, the object will remain even if there
//	are no outstanding claims on the model.

StLazyLock::StLazyLock(
	LModelObject	*inModel)
{
	mModel = inModel;
	if (mModel) {
		mLaziness = mModel->IsLazy();
		mModel->SetLaziness(false);
	}
}

StLazyLock::~StLazyLock()
{
	if (mModel) {
		mModel->SetLaziness(mLaziness);
	}
}


// ---------------------------------------------------------------------------
//	StTempTellTarget
// ---------------------------------------------------------------------------
//
//	Sometimes it is useful to temporarily change the TellTarget (things like
//	recording a complete object specifier instead of a truncated object
//	specifier).  StTempTellTarget provides a exception safe mechanism for
//	doing this for a given scope.
//
//	Use an inModel of NULL to correspond to no TellTarget.

StTempTellTarget::StTempTellTarget(
	LModelObject	*inModel)
{
	mOldTarget = LModelObject::GetTellTarget();
	if (mOldTarget) {
		mOldTargetLaziness = mOldTarget->IsLazy();
		
		mOldTarget->SetLaziness(false);
	}
	
	LModelObject::SetTellTarget(inModel);
}

StTempTellTarget::~StTempTellTarget()
{
	LModelObject::SetTellTarget(mOldTarget);
	
	if (mOldTarget)
		mOldTarget->SetLaziness(mOldTargetLaziness);
}