//
//
//	Ellipsoid.cpp
//
//	Ellipsoid.cpp implements a sample parametric object with the kDirectModeling
//	Extended Property. 
//
//	EllipsoidSample.rsrc contains a 'PXpr' resource with 'Has Extended Propeties' set.
//
//	With 'Has Extended Propeties' set, the object definition procedure is called with 
//	a kObjOnInitXProperties action/event.  In the kObjOnInitXProperties handler, the 
//	kDirectModeling property is installed using GS_SetObjectProperty.  Once installed
//	the object then can be called with direct modeling events.  Handling the direct modeling
//	events allows the user to interactively reshape the object.
//
//	Other services demonstrated by this example:
//
//	1) Custom editable dataBar. See kObjOnDM_CustomBarEvent below.
//	2) Custom UI in the shape pane.  See GS_DEMO_BUTTON_PARAMETER
//	
//	
//	Copyright © 2003 Nemetschek North America 
//	All Rights Reserved.
//
//

#include <VWSDK.h>

#include <MiniCadCallbacks.h>
#include <ExtendedProperties.h>
#include "CustomObjects.h"

#include <GSString.h>
#include <RecordHandler.h>
#include <MCMatrix.h>


//	DirectModelingData::fHotSpots constants:
const short kHot_i_MaxID = 0;	//index 
const short kHot_i_MinID = 1;	//index 
const short kHot_j_MaxID = 2;	//index 
const short kHot_j_MinID = 3;	//index 
const short kHot_k_MaxID = 4;	//index 
const short kHot_k_MinID = 6;	//index 

//	Forward declarations:
static MCObjectHandle GetSelectedReshapeObject();
static Boolean GetMyParameterValues(MCObjectHandle objHand, WorldCoord& outWidth, WorldCoord& outDepth, WorldCoord& outHeight);
static Boolean SetMyParameterValues(MCObjectHandle objHand, const WorldCoord& inRadius_i_Val, const WorldCoord& inRadius_j_Val, const WorldCoord& inRadius_k_Val);

#define GS_DEMO_BUTTON_WIDGET //	define GS_DEMO_BUTTON_PARAMETER to get a properties button on the shape pane of the Object info Palette.  

#ifdef GS_DEMO_BUTTON_WIDGET
const int kButtonMapping = 23;	// user supplied ID to distinguish buttons in kObjOnObjectUIButtonHit handler.
const int kButtonMapping2 = 24;	// user supplied ID to distinguish buttons in kObjOnObjectUIButtonHit handler.
#endif


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class DirectModelingData
//
//	DirectModelingData provides data and behaviors to handle the direct modeling events
//	posted by the 3D - reshape tool. 
//	
//
//	Public API called by the object definition procedure (ODP) below.  
{
public:
	void Init();		// No contructor called for this class - it is allocated with GS_NewPtr.
	long OnSelect();
	long OnCancel();
	long OnMouseDown();
	long OnCustomBarEvent(const CustomBarEventData& inEventData);
	long OnComplete();
	void OnDraw();
	void Update();
	void OnPause();
	void OnResume();


private:

	void AddHotSpots();
	void RemoveHotSpots();
	void InitHotSpotsIDs();
	
	enum EHotSpotIDConstants
	{ 	
		kNoHotSpotIDNoIndex = -1,	// 
		
		kMax_i_HotSpotIDsIndex = 0,	// 	
		kMin_i_HotSpotIDsIndex = 1,	//
		kMax_j_HotSpotIDsIndex = 2,	//
		kMin_j_HotSpotIDsIndex = 3, // these are indices into the fHotSpotIDs
		kMax_k_HotSpotIDsIndex = 4,	// 	
		kMin_k_HotSpotIDsIndex = 5,	// 	
		
		
		kNumHotSpots = 6		// size of fHotSpotIDs
		
	};
	
	enum EResizeMode { eFromCenter, eFromOppositeEnd };
	
	EHotSpotIDConstants GetHotIndex(long inHotSpotID);
	Boolean InstallConstraintAlongLine();
	Boolean InstallCustomBar();
	void RemoveCustomBar();
	Boolean GetReshapeParameters(EResizeMode inResizeMode, const WorldPt3& reshapePt, WorldCoord& outRadius_i_Val, WorldCoord& outRadius_j_Val, WorldCoord& outRadius_k_Val);
	void GetDrawData(EResizeMode inResizeMode, WorldPt3& outWorldOrigin, WorldPt3& outWorldEnd_i_Axis, WorldPt3& outWorldEnd_j_Axis, WorldPt3& outWorldEnd_k_Axis);
	void GetCustomBarFieldLabelString(Str255& outString);	
	void PauseCustomBar();
	void ResumeCustomBar();

	
	//	DATA:
	long 			fCustomBarID;
	WorldCoord* 	fLockedBarValuePtr;	// non-nil when user has locked a value in the dataBar
	Boolean 		fIsValid;			// Set in UpdateMethod
	TransformMatrix	fTransformToGlobal;
	WorldCoord		fRadius_i;
	WorldCoord		fRadius_j;
	WorldCoord		fRadius_k;
	WorldPt3		fWorldCenter;
	WorldPt3		fRadius_i_UnitVector;
	WorldPt3		fRadius_j_UnitVector;
	WorldPt3		fRadius_k_UnitVector;
	long					fHotSpotIDs[kNumHotSpots];
	EHotSpotIDConstants		fActiveHotSpotIDIndex;
};	// end class DirectModelingData







/////////////////////////////////////////////
/////////
/////////	BEGIN OBJECT DEFINITION PROCEDURE
/////////


//-------------------------------------------
long ParametricMain(long action, MCObjectHandle objHand, long message, long &userData)
//
//	Main entry point for plug-in object.  This is the object definition procedure.
//	See MiniCadHookIntf.h for constants.
//
{
	::GS_InitializeVCOM( gCBP );

	long	reply = kParametricNoErr;


	switch (action) {

		case kParametricInitGlobals : {
				//
				//	This action is called when an workspace is activated and serves the 
				//	primary purpose of allowing the plug-in to define any 
				//	defaults and global information and to initialize data space that 
				//	may be stored in userData.
				//

				// Construct explicitly with 'placement new'. Get memory from the app because
				// memory allocated with normal 'new' disappears when the dll unloads.

			/* Example:

				void* pRawMemory = GS_NewPtr(gCBP, sizeof(FooClass));
				if (pRawMemory) {
					FooClass* pConstructedMemory = (FooClass*) new(pRawMemory) FooClass();	
					userData = (long) pConstructedMemory;
				}
				else {
					userData = nil;
				}
	
			*/

				break;
			}

		case kParametricDisposeGlobals : {
				//
				//	This action is called when a workspace is deactivated and serves the 
				//	primary purpose of allowing the external function to dispose data 
				//	space that may be stored in userData.
				//

				// Destruct explicitly when using 'placement new'

			/* Example:

				if (userData) {
					FooClass* pData = (FooClass*) userData;
					pData->~FooClass();
					GS_DisposePtr(gCBP, pData);
					userData = nil;
				}
	
			*/
				
				break;
			}

		case kParametricRecalculate : {
			//
			//	This action is called to regenerate the look of a plug-in object in response to a change 
			//	in one of the parameter values on the object info palette, or a rotate, move, attribute change.
			//
			//	For this action, the message argument contains a handle to a wall, if we are inserted in a wall directly.
			//  
							
			WorldCoord myRadius_i, myRadius_j, myRadius_k = 1;
			
			if (	GetMyParameterValues(objHand, myRadius_i, myRadius_j, myRadius_k)
				&&	myRadius_i != 0 && myRadius_j != 0 && myRadius_k != 0) {
				
				WorldPt3 origin(0,0,0);
				MCObjectHandle obj = GS_CreateSphere(gCBP, origin, 1);
				obj = GS_ConvertToNURBS(gCBP, obj, false);
				Vector scale(myRadius_i, myRadius_j, myRadius_k);
				(void) GS_ScaleObject3D(gCBP, obj, origin, scale); 
				
			}
			break;
		}

		case kParametricPreference : {
			//
			//	This action gives the object an opportunity to pose a custom dialog for parameter value editting.
			//	If this object handles the request and poses its own dialog, then it returns kParametricPrefOK or kParametricPrefCancel.
			//	If this object does not have a custom dialog, it returns kParametricNotImplemented.
			// 	Most parametrics will choose not to implement this optional action, and simply set reply to kParametricNotImplemented 
			//  and let VectorWorks application pose the standard dialog. 
			           
			reply = kParametricNotImplemented;
			break;
		}

		case kObjOnInitXProperties: {
			//
			//	This action/event is called by the application once. Here the objecct can supply
			//	any Extended Propertis using the GS_SetObjectProperty APIs.
			//
			//	The message parameter contains the CodeRefID required by the GS_SetObjectProperty APIs.
			//
			reply = kParametricError;	
			
			CodeRefID objID = message;
			
			//	This object will handle direct modeling events.  The 3D reshape tool uses direct modeling
			//	events to allow objects to provide custom reshape behaviors.
			//
			if (GS_SetObjectProperty(gCBP, objID, kDirectModeling, true)) {
			
				#ifdef GS_DEMO_BUTTON_WIDGET
				
				//	Below we add a properties button using by setting the kObjXPropHasUIOverride
				//	property and calling GS_AppendWidget.
				//
				if (GS_SetObjectProperty(gCBP, objID, kObjXPropHasUIOverride, true)) {
				
					//
					//	Insert all the object parameters. as they appear without kObjXPropHasUIOverride set.
					//	GS_AppendWidget(...) and GS_InsertWidget(...) can be used to pick and choose the parameters
					//	displayed


					Str255 buttonString, button2String, staticText;
					GS_GetResourceString(gCBP, 11000, 5, buttonString);	
					GS_GetResourceString(gCBP, 11000, 7, button2String);
					GS_GetResourceString(gCBP, 11000, 8, staticText);

					Str255 parameter1Text, parameter2Text, parameter3Text;
					GS_GetResourceString(gCBP, 11000, 1, parameter1Text);
					GS_GetResourceString(gCBP, 11000, 2, parameter2Text);
					GS_GetResourceString(gCBP, 11000, 3, parameter3Text);

					long unusedData = 0;


					//
					// The following define can be commented or uncommented to illustrate
					// different methods of inserting parameters.

					//#define USING_INSERT_ALL_PARAMETERS


					#ifdef USING_INSERT_ALL_PARAMETERS
						// This block illustrates inserting parameters all at once using GS_InsertAllParametersAsWidgets


						// GS_InsertAllParametersAsWidgets(CallBackPtr cbp, long ObjectID):
						//
						//		Inserts all parameters from the Prm# resource into the parameter list, in order
						//
						//		- cbp			- callback pointer
						//		- ObjectID		- ID of plug-in obtained from the message parameter

						GS_InsertAllParametersAsWidgets(gCBP, objID);
						
						// GS_AppendWidget(CallBackPtr cbp, long ObjectID, int widgetType, int eventID, ConstStr255Param text, long data):
						//
						//		This is for use only with the NEW parameter types, button and static text
						//
						//		- cbp			- callback pointer
						//		- ObjectID		- ID of plug-in obtained from the message parameter
						//		- widgetType	- One of the new widget types (12 = button, 13 = static text)
						//		- eventID		- Value that you would like VectorWorks to send in the message parameter when that widget is clicked
						//		- text			- Associated text for that widget
						//		- data			- Reserved for future use

						GS_AppendWidget(gCBP, objID, kWidgetButton, kButtonMapping, buttonString, unusedData);


						// GS_InsertWidget(CallBackPtr cbp, long ObjectID, int position, int widgetType, int eventID, ConstStr255Param text, long data):
						//
						//		This is for use only with the NEW parameter types, button and static text
						//
						//		- cbp			- callback pointer
						//		- ObjectID		- ID of plug-in obtained from the message parameter
						//		- position		- 0-based position at which this new widget will reside
						//		- widgetType	- One of the new widget types (12 = button, 13 = static text)
						//		- eventID		- Value that you would like VectorWorks to send in the message parameter when that widget is clicked
						//		- text			- Associated text for that widget
						//		- data			- Reserved for future use

						GS_InsertWidget(gCBP, objID, 0, kWidgetButton, kButtonMapping2, button2String, unusedData);
						
						GS_InsertWidget(gCBP, objID, 1, kWidgetStaticText, -1, staticText, unusedData);	// eventID doesn't matter for static text, because it doesn't generate events

					#else
						// This block illustrates inserting parameters individually


						// GS_InsertParameterWidget(CallBackPtr cbp, long ObjectID, int position, int parameterID, Constr255Param text, long data)
						//
						//		This is for use only with parameters that are already defined in the Prm# resource
						//
						//		- cbp			- callback pointer
						//		- ObjectID		- ID of plug-in obtained from the message parameter
						//		- position		- 0-based position at which this new widget will reside
						//		- parameterID	- 1-based index of an actual plug-in parameter to insert (ie, a parameter defined in the Prm# resource)
						//		- text			- Associated text for that widget
						//		- data			- Reserved for future use

						GS_InsertParameterWidget(gCBP, objID, 0, 1, parameter1Text, 0);		// Insert parameter 1 at position 0
						GS_InsertParameterWidget(gCBP, objID, 1, 2, parameter2Text, 0);		// Insert parameter 2 at position 1


						// GS_AppendParameterWidget(CallBackPtr cbp, long ObjectID, int parameterID, Constr255Param text, long data)
						//
						//		This is for use only with parameters that are already defined in the Prm# resource
						//
						//		- cbp			- callback pointer
						//		- ObjectID		- ID of plug-in obtained from the message parameter
						//		- parameterID	- 1-based index of an actual plug-in parameter to insert (ie, a parameter defined in the Prm# resource)
						//		- text			- Associated text for that widget
						//		- data			- Reserved for future use

						GS_AppendParameterWidget(gCBP, objID, 3, parameter3Text, 0);		// Append parameter 3 to end of parameters


						// For illustration, the buttons and static text will be inserted at different positions than in the previous block

						GS_InsertWidget(gCBP, objID, 0, kWidgetButton, kButtonMapping, buttonString, unusedData);
						GS_AppendWidget(gCBP, objID, kWidgetStaticText, -1, staticText, unusedData);
						GS_AppendWidget(gCBP, objID, kWidgetButton, kButtonMapping2, button2String, unusedData);
					#endif
				}
				#endif
				
				GS_SetObjectPropertyChar(gCBP, objID, kObjXPropSpecialEdit, kObjXPropSpecialEditProperties);
				
				reply = kParametricNoErr;
			}
			if (reply == kParametricNoErr) {
				
				Boolean handlesShapePaneWidgetPrepEvent = true;
				if (!GS_SetObjectProperty(gCBP, objID, kObjXHasCustomWidgetVisibilities, handlesShapePaneWidgetPrepEvent))
					reply = kParametricError;
			}	
	
			break;
		}
		
		#ifdef GS_DEMO_BUTTON_WIDGET
		
		case kObjOnObjectUIButtonHit: {

			switch (message) {
				case kButtonMapping:
					GS_PostNoteAlert(gCBP, 11000, 6);
					break;

				case kButtonMapping2:
					GS_PostNoteAlert(gCBP, 11000, 9);
					break;
			}

			break;
		}
		#endif

		case kObjOnDM_Select: {
			//
			//	The direct modeling event source, the 3D - reshape tool, has just selected an object of this type.
			//	 
			#if _DEBUG
			if (userData != 0)
				DSTOP((kSteve, "We expect userData to be deallocated in kOnDM_Cancel\n"));
			#endif

			DirectModelingData* dmData = (DirectModelingData*) GS_NewPtr(gCBP, sizeof(DirectModelingData));
			
			if (dmData) {
				dmData->Init();
				dmData->OnSelect();
				userData = (long) dmData;
			}
				
			
			break;
		}

		case kObjOnDM_BeginPauseEvent: {
		
			//
			//	The direct modeling event source, the 3D - reshape tool, is being paused due to a 
			//	temporary tool swicth.  Keep data that needs to remain when kOnDM_EndPauseEvent is received.
			//	 
			DirectModelingData* dmData = (DirectModelingData*)userData;
			
			if (dmData) {
						
				dmData->OnPause();
			}
			break;
		}
		
		case kObjOnDM_EndPauseEvent: {
		
			//
			//	The direct modeling event source, the 3D - reshape tool, is being restored after a 
			//	temporary tool swicth.  Restore any data that was removed with kObjOnDM_BeginPauseEvent.
			//	 
			DirectModelingData* dmData = (DirectModelingData*)userData;
			
			if (dmData) {
				
				dmData->OnResume();
			}
			break;
		}
		
		case kObjOnDM_MouseDown: {
			
			DirectModelingData* dmData = (DirectModelingData*)userData;
			
			if (dmData) {
				
				reply = dmData->OnMouseDown();
			}
			break;
		}
		
		
		case kObjOnDM_Cancel: {
			DirectModelingData* dmData = (DirectModelingData*)userData;
			
			if (dmData) {
				
				dmData->OnCancel();
									
				GS_DisposePtr(gCBP, dmData);
				userData = 0;
			}
			break;
		}
		
		case kObjOnDM_CustomBarEvent: {
		
			DirectModelingData* dmData = (DirectModelingData*)userData;
			CustomBarEventData* eventData = (CustomBarEventData*) message;
			
			if (dmData && eventData) {

				dmData->OnCustomBarEvent(*eventData);
			}
			break;
		}
		
		case kObjOnDM_Draw: {
			
			//
			//	The direct modeling event source, the 3D - reshape tool, needs to draw the current reshape
			//	state.  If the kObjOnDM_MouseDown handler found a hotspot hit, then the object is being reshaped.
			//	Check the current state of the tool system by accessing GS_GetToolPtCurrent3D and draw an interactive 
			//	reshape preview.
			//	 
			if (GS_GetNumToolPts(gCBP) > 0) {
			
				// We only draw if reshaping.
				DirectModelingData* dmData = (DirectModelingData*)userData;
				
				if (dmData) {
					
					dmData->OnDraw();
				}
			}
			break;
		}
		
		case kObjOnDM_Complete: {
		
			//
			//	The direct modeling event source, the 3D - reshape tool, has completed a reshape. Check the current state 
			//	of the tool system by accessing the second tool point via GS_GetToolPt3D.  Use the tools final point to
			//	modify the selected object.
			//	 
			DirectModelingData* dmData = (DirectModelingData*)userData;
			
			if (dmData) {
				
				dmData->OnComplete();
			}
			break;
		}



		case kObjOnWidgetPrep: {
			long OnObjectWidgetPrep(MCObjectHandle myObjectHandle, long message, long& userData);

			reply = OnObjectWidgetPrep(objHand, message, userData);
			break;
		}
		
	}

	return reply;
}


/////////
/////////	END OBJECT DEFINITION PROCEDURE
/////////
/////////////////////////////////////////////






// 	Index into Parameter list inside the record node is 1-based.
const short kRadius_i_ParamIndex = 1;	
const short kRadius_j_ParamIndex = 2;
const short kRadius_k_ParamIndex = 3;

//-------------------------------------------
static Boolean GetMyParameterValues(MCObjectHandle objHand, 
								WorldCoord& outRadius_i_Val, 
								WorldCoord& outRadius_j_Val,
								WorldCoord& outRadius_k_Val)
{
	Boolean retVal = true;

	// read record node on aux list of given object
	RecordHandle parmRecordHand = NULL;
	FormatHandle parmFormatHand = NULL;
	if (	(GetCustomObjectDefinition(gCBP, objHand, parmRecordHand, parmFormatHand) == (Boolean) true)
		&& 	parmRecordHand 
		&& 	parmFormatHand) 
	{
		TRecordHandler rh(parmRecordHand);
		TFormatHandler fh(parmFormatHand);
			
		WorldCoord coordValue = 0.0;

		// Parameter 1 - 'p1' = "Radius_i"
		TRecordItem widthItem(fh.GetFieldStyle(kRadius_i_ParamIndex));	// must initialize with correct type for this field
		if (rh.GetFieldObject(kRadius_i_ParamIndex, widthItem)) {
			widthItem.GetFieldValue(coordValue);
			outRadius_i_Val = coordValue;
		}
		else {
			retVal = false;
			DSTOP((kSteve, "Failed to get a plug-in parameter value for parameter p1."));
		}
		
		if (retVal) {
		
			// Parameter 2 - 'p2' = "Radius_j"
			TRecordItem depthItem(fh.GetFieldStyle(kRadius_j_ParamIndex));
			if (rh.GetFieldObject(kRadius_j_ParamIndex, depthItem)) {
				depthItem.GetFieldValue(coordValue);
				outRadius_j_Val = coordValue;
			}
			else {
				retVal = false;
				DSTOP((kSteve, "Failed to get a plug-in parameter value for parameter p2."));
			}
		}
		
		if (retVal) {
		
			// Parameter 3 - 'p3' = "Radius_k"
			TRecordItem heightItem(fh.GetFieldStyle(kRadius_k_ParamIndex));
			if (rh.GetFieldObject(kRadius_k_ParamIndex, heightItem)) {
				heightItem.GetFieldValue(coordValue);
				outRadius_k_Val = coordValue;
			}
			else {
				retVal = false;
				DSTOP((kSteve, "Failed to get a plug-in parameter value for parameter p3."));
			}
		}
	}
	return retVal;
}

//-------------------------------------------
static Boolean SetMyParameterValues(MCObjectHandle objHand, 
								const WorldCoord& inRadius_i_Val, 
								const WorldCoord& inRadius_j_Val,
								const WorldCoord& inRadius_k_Val)
{
	Boolean retVal = true;

	// read record node on aux list of given object
	RecordHandle parmRecordHand = NULL;
	FormatHandle parmFormatHand = NULL;
	if (	(GetCustomObjectDefinition(gCBP, objHand, parmRecordHand, parmFormatHand) == (Boolean) true)
		&& 	parmRecordHand 
		&& 	parmFormatHand) 
	{
		TRecordHandler rh(parmRecordHand);
		TFormatHandler fh(parmFormatHand);
			
		WorldCoord coordValue = 0.0;

		// Parameter 1 - 'p1' = "Radius_i"
		TRecordItem radius_i_Item(fh.GetFieldStyle(kRadius_i_ParamIndex));	// must initialize with correct type for this field
		if (rh.GetFieldObject(kRadius_i_ParamIndex, radius_i_Item)) {
			radius_i_Item.SetFieldValue(fabs(inRadius_i_Val));
			rh.SetFieldObject(kRadius_i_ParamIndex, radius_i_Item);
		}
		else {
			retVal = false;
			DSTOP((kSteve, "Failed to get a plug-in parameter value for parameter p1."));
		}
		
		if (retVal) {
		
			// Parameter 2 - 'p2' = "Radius_j"
			TRecordItem radius_j_Item(fh.GetFieldStyle(kRadius_j_ParamIndex));
			if (rh.GetFieldObject(kRadius_j_ParamIndex, radius_j_Item)) {
				radius_j_Item.SetFieldValue(fabs(inRadius_j_Val));
				rh.SetFieldObject(kRadius_j_ParamIndex, radius_j_Item);
			}
			else {
				retVal = false;
				DSTOP((kSteve, "Failed to get a plug-in parameter value for parameter p2."));
			}
		}
		
		if (retVal) {
		
			// Parameter 3 - 'p3' = "Radius_k"
			TRecordItem radius_k_Item(fh.GetFieldStyle(kRadius_k_ParamIndex));
			if (rh.GetFieldObject(kRadius_k_ParamIndex, radius_k_Item)) {
				radius_k_Item.SetFieldValue(fabs(inRadius_k_Val));
				rh.SetFieldObject(kRadius_k_ParamIndex, radius_k_Item);
			}
			else {
				retVal = false;
				DSTOP((kSteve, "Failed to get a plug-in parameter value for parameter p3."));
			}
		}
	}
	return retVal;
}

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static void GetTransformToGlobal(MCObjectHandle inCoordSystemContainer, TransformMatrix& outTransforToGlobal)
{
	
	if (inCoordSystemContainer) {
	
		GS_GetEntityMatrix(gCBP, inCoordSystemContainer, outTransforToGlobal);
		MCObjectHandle parentTrav = GS_ParentObject(gCBP, inCoordSystemContainer);
		short thisType = GS_GetObjectType(gCBP, parentTrav);
		while (parentTrav && parentTrav != GS_GetCurrentLayer(gCBP)) {
		
			if (	thisType == kCSGTreeNode
				||	thisType == kSweepNode
				||	thisType == kMultiExtrudeNode
				||	thisType == kExtrudeNode
				||	thisType == kParametricNode
				||	thisType == kSymbolNode)
		{
				TransformMatrix thisMat;
				GS_GetEntityMatrix(gCBP, parentTrav, thisMat);
				outTransforToGlobal = MatrixMultiply(outTransforToGlobal, thisMat);
			}
			parentTrav = GS_ParentObject(gCBP, parentTrav);
			thisType = GS_GetObjectType(gCBP, parentTrav);
		}
	}
}

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static void TransformToGlobal(const TransformMatrix& theMat, WorldPt3& ioLocation)
{

	if (!theMat.IsIdentity())
		ioLocation = PointTransformN(ioLocation, theMat);
}

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static void TransformToLocal(const TransformMatrix& theMat, WorldPt3& ioLocation)
{
	if (!theMat.IsIdentity())
		ioLocation = InversePointTransformN(ioLocation, theMat);
}


//--------------------------------------------------------------------------------------------
void DirectModelingData::Init()
//
//	No constructor because allocated with GS_NewPtr
{
	fCustomBarID = 0;
	fLockedBarValuePtr = nil;	
	IdentityMatrix(fTransformToGlobal);
	//fRadius_i = 0;
	//fRadius_j = 0;
	//fRadius_k = 0;
	fActiveHotSpotIDIndex = kNoHotSpotIDNoIndex;
	fWorldCenter.Set(0,0,0);
	fRadius_i_UnitVector.Set(1,0,0);
	fRadius_j_UnitVector.Set(0,1,0);
	fRadius_k_UnitVector.Set(0,0,1);
}

//--------------------------------------------------------------------------------------------
void  DirectModelingData::InitHotSpotsIDs()
{
	for (short i = 0; i < kNumHotSpots; i++) {
	
		fHotSpotIDs[i] = 0;
	}
}


//--------------------------------------------------------------------------------------------
void DirectModelingData::Update()
//
//	Update the state as a function of the current selection. 

{
	MCObjectHandle h = GetSelectedReshapeObject();
	
	if (h) {
		
		GetTransformToGlobal(h, fTransformToGlobal);
		fIsValid = GetMyParameterValues(h, fRadius_i, fRadius_j, fRadius_k);
		
		fWorldCenter.Set(0,0,0);
		TransformToGlobal(fTransformToGlobal, fWorldCenter);

		WorldPt3 axisEndPt;
		
		//	i axis vector:
		axisEndPt.Set(1,0,0);
		TransformToGlobal(fTransformToGlobal, axisEndPt);
		fRadius_i_UnitVector = (axisEndPt - fWorldCenter).Normal();

		//	j axis vector:
		axisEndPt.Set(0,1,0);
		TransformToGlobal(fTransformToGlobal, axisEndPt);
		fRadius_j_UnitVector = (axisEndPt - fWorldCenter).Normal();

		//	k axis vector:
		axisEndPt.Set(0,0,1);
		TransformToGlobal(fTransformToGlobal, axisEndPt);
		fRadius_k_UnitVector = (axisEndPt - fWorldCenter).Normal();

	}
	else
		fIsValid = false;
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::OnPause()
{
	this->PauseCustomBar();
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::OnResume()
{
	this->ResumeCustomBar();
	this->InstallConstraintAlongLine();
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::AddHotSpots()
//
//	Add hotspots.  Use state info gathered in this->Update() to specify hotspots in global coordinates.
//	
{
	TXString emptyTip = "";	//	Hotspot tips are unused in VW 11.
	const long kHotSpotCursorID = 107;
	
	//	Add Max_i:
	fHotSpotIDs[kMax_i_HotSpotIDsIndex] = GS_AddHotSpot(gCBP, kModel3DSelBox, fWorldCenter + fRadius_i * fRadius_i_UnitVector, kHotSpotCursorID, emptyTip);
	
	//	Add Min_i:
	fHotSpotIDs[kMin_i_HotSpotIDsIndex] = GS_AddHotSpot(gCBP, kModel3DSelBox, fWorldCenter - fRadius_i * fRadius_i_UnitVector, kHotSpotCursorID, emptyTip);

	//	Add Max_j:
	fHotSpotIDs[kMax_j_HotSpotIDsIndex] = GS_AddHotSpot(gCBP, kModel3DSelBox, fWorldCenter + fRadius_j * fRadius_j_UnitVector, kHotSpotCursorID, emptyTip);
	
	//	Add Min_j:
	fHotSpotIDs[kMin_j_HotSpotIDsIndex] = GS_AddHotSpot(gCBP, kModel3DSelBox, fWorldCenter - fRadius_j * fRadius_j_UnitVector, kHotSpotCursorID, emptyTip);

	//	Add Max_k:
	fHotSpotIDs[kMax_k_HotSpotIDsIndex] = GS_AddHotSpot(gCBP, kModel3DSelBox, fWorldCenter + fRadius_k * fRadius_k_UnitVector, kHotSpotCursorID, emptyTip);
	
	//	Add Min_k:
	fHotSpotIDs[kMin_k_HotSpotIDsIndex] = GS_AddHotSpot(gCBP, kModel3DSelBox, fWorldCenter - fRadius_k * fRadius_k_UnitVector, kHotSpotCursorID, emptyTip);
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::RemoveHotSpots()
//
//	Remove all the hotspots stored in fHotSpotIDs array. Clear the referrences in fHotSpotIDs array.
{
	for (short i = 0; i < kNumHotSpots; i++) {
	
		if (fHotSpotIDs[i] != 0) {
			GS_RemoveHotSpot(gCBP,fHotSpotIDs[i]);
			fHotSpotIDs[i] = 0;
		}
	}
}
		
//--------------------------------------------------------------------------------------------
long DirectModelingData::OnSelect()
//
//	Evaluate the selection and provide hotspots as appropriate for current context.
{
	
	long retVal = kParametricNoErr;
	
	// Update state to match selection:
	this->Update();	
	
	// Add Hot Spots:
	this->AddHotSpots();
	
	return retVal;
}
		
//--------------------------------------------------------------------------------------------
long DirectModelingData::OnCancel()
//
//	XXX???
{
	long retVal = kParametricNoErr;
	
	this->RemoveHotSpots();
	this->RemoveCustomBar();
	return retVal;
}

//--------------------------------------------------------------------------------------------
DirectModelingData::EHotSpotIDConstants DirectModelingData::GetHotIndex(long inHotSpotID)
//
//	Given a HotSpot ID find the corresponding index in fHotSpotIDs
{
	EHotSpotIDConstants retVal = kNoHotSpotIDNoIndex;
	
	for (short i = 0; i < kNumHotSpots; i++) {
	
		if (fHotSpotIDs[i] == inHotSpotID) {
			
			retVal = EHotSpotIDConstants(i);
			break;
		}
	}
	return retVal;
}

//--------------------------------------------------------------------------------------------
Boolean DirectModelingData::InstallConstraintAlongLine()
{
	Boolean retVal = true;
	Vector	axisUnitEndPoint; 	// in local space. transform to global below
	
	switch (fActiveHotSpotIDIndex) {
	
		case kMax_i_HotSpotIDsIndex: 		
		case kMin_i_HotSpotIDsIndex: axisUnitEndPoint = fRadius_i_UnitVector; break;
		
		case kMax_j_HotSpotIDsIndex: 
		case kMin_j_HotSpotIDsIndex: axisUnitEndPoint = fRadius_j_UnitVector; break;
		
		case kMax_k_HotSpotIDsIndex: 
		case kMin_k_HotSpotIDsIndex: axisUnitEndPoint = fRadius_k_UnitVector; break;
		
		default: retVal = false;
	}
	
	if (retVal) {
	
		Ray constraintRay;
		constraintRay.vertex = fWorldCenter;
		
		constraintRay.direction = axisUnitEndPoint;
		
		GS_InstallConstraintAlong3DLine(gCBP, constraintRay);
	}
	return retVal;
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::PauseCustomBar()
{
	if (fCustomBarID != 0) {
	
		GS_CustomBarInstall(gCBP, 0);
	}
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::ResumeCustomBar()
{
	if (fCustomBarID != 0) {
	
		GS_CustomBarInstall(gCBP, fCustomBarID);
	}
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::RemoveCustomBar()
{
	if (fCustomBarID != 0) {
	
		GS_CustomBarRelease(gCBP, fCustomBarID);
		fCustomBarID = 0;
		
		GS_DisposePtr(gCBP, fLockedBarValuePtr);
		fLockedBarValuePtr = nil;
	}
}

//--------------------------------------------------------------------------------------------
Boolean DirectModelingData::InstallCustomBar()
{
	Boolean retVal = true;
	Ray constraintRay;
	
	if (GS_CustomBarCreate(gCBP, 1, fCustomBarID)) {
	
		retVal = GS_CustomBarInstall(gCBP, fCustomBarID);
	}
	return retVal;
}

//--------------------------------------------------------------------------------------------
long DirectModelingData::OnMouseDown()
{
	long retVal = 0;
	
	Boolean outIs3D;
	WorldPt3 location;

	long hotSpotID = GS_TrackHotSpot(gCBP, outIs3D, location);
	if (hotSpotID != 0) {
	
		fActiveHotSpotIDIndex = GetHotIndex(hotSpotID);
		
		// Install a constraint along line
		this->InstallConstraintAlongLine();	// constraint is removed by application with next tool point
		
		// Instal customBar
		this->InstallCustomBar();	// Custom Bar must be removed
		
		EConstrainType constrainType = kConsNone;
		GS_SetProgramVariable(gCBP, varSmartCursorConstrainMode, (void*) &constrainType);

		retVal = 1;
	}
	else {
		
		fActiveHotSpotIDIndex = kNoHotSpotIDNoIndex;
	}	
	return retVal;
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::GetCustomBarFieldLabelString(Str255& outString)
{
	switch (fActiveHotSpotIDIndex) {
	
		case kMax_i_HotSpotIDsIndex:
		case kMin_i_HotSpotIDsIndex: {
		
			GS_GetResourceString(gCBP, 11000, 1, outString);
			break;
		}
		
		case kMax_j_HotSpotIDsIndex:
		case kMin_j_HotSpotIDsIndex: {
		
			GS_GetResourceString(gCBP, 11000, 2, outString);
			break;
		}
		
		case kMax_k_HotSpotIDsIndex:
		case kMin_k_HotSpotIDsIndex: {
		
			GS_GetResourceString(gCBP, 11000, 3, outString);
			break;
		}
		default:
			break;
	}
}

//--------------------------------------------------------------------------------------------
long DirectModelingData::OnCustomBarEvent(const CustomBarEventData& inEventData)
{
	long retVal = kParametricNoErr;
	
	switch (inEventData.fCustomBarEventSelector) {
	
		case kCustomBarSetup: {
					
			//////////////////////////////////////////////
			/////////////	The custom bar is being installed. Specify the field info.
			CustomBarFieldInfo fi;
			fi.fIsEditable = true;
			Str255 labelString;
			this->GetCustomBarFieldLabelString(labelString);
			if (fCustomBarID != 0) {
			
				StringCopy(labelString, fi.fFieldLabel, sizeof(Str20));
			
				(void) GS_CustomBarSetFieldInfo(gCBP, fCustomBarID, 0, fi);
			}
			break;
		}
		
		case kCustomBarSetdown: break;
		
		case kCustomBarUpdateValues: {

			//////////////////////////////////////////////
			/////////////	The mouse/cursor has just changed. Update the bar display.
			if (fCustomBarID != 0) {	
			
				// the user is reshaping the height hot spot
				//DSTOP((dmDataCB->fHotID != dmDataCB->fTopHotSpotID, kSteve, 
				//		"Stop Here. We expect the dataBar to match the hot spot\n"));
				
				if (fLockedBarValuePtr)
					GS_CustomBarSetFieldWorldCoord(gCBP , fCustomBarID, 0, *fLockedBarValuePtr);
				else {
					//	THis looks like the last height computed not the current height.
					//	If you finish the reshape the height of the object is not the same as displayed in the databar field
					WorldPt3 reshapePt;
					WorldCoord a,b,c;
					if (	GS_GetToolPtCurrent3D(gCBP, reshapePt)
						&&	this->GetReshapeParameters(eFromCenter, reshapePt, a,b,c)) 
					{	
						WorldCoord radiusVal = a;
						switch (fActiveHotSpotIDIndex) {
						
							case kMax_i_HotSpotIDsIndex:
							case kMin_i_HotSpotIDsIndex: {
							
								radiusVal = fabs(a);
								break;
							}
							
							case kMax_j_HotSpotIDsIndex:
							case kMin_j_HotSpotIDsIndex:  {
							
								radiusVal = fabs(b);
								break;
							}
							
							case kMax_k_HotSpotIDsIndex:
							case kMin_k_HotSpotIDsIndex:   {
							
								radiusVal = fabs(c);
								break;
							}
							default:
								break;
						}
						GS_CustomBarSetFieldWorldCoord(gCBP , fCustomBarID, 0, radiusVal);
					}
				}
			}
			break;
		}
		
		case kCustomBarLockValue: {
			
			//////////////////////////////////////////////
			/////////////	The user has locked a data bar field.  Get the value and store it for use in other 
			/////////////	event handlers such as kObjOnDM_Draw
			CustomBarLockValueData* lockDataPtr = (CustomBarLockValueData*) inEventData.fData;
			if (lockDataPtr) {
			
				if (fCustomBarID != 0) {

					CustomBarFieldInfo fi;
					(void) GS_CustomBarGetFieldInfo(gCBP, fCustomBarID, lockDataPtr->fFieldIndex, fi);
			
					WorldCoord coordValue = 0;
					if ( GS_DimStringToCoord(gCBP, lockDataPtr->fUserLockString, coordValue)) {
					
						fi.fIsLocked = true;
						fLockedBarValuePtr = (WorldCoord*) GS_NewPtr(gCBP, sizeof(WorldCoord));
						if (fLockedBarValuePtr)
							*fLockedBarValuePtr = coordValue;
						
						GS_CustomBarSetFieldWorldCoord(gCBP , fCustomBarID, 0, fabs(coordValue));
					}
				}
			}
			break;
		}
		
		case kCustomBarUnlockValue: {
		
			//////////////////////////////////////////////
			/////////////	The user has unlocked a data bar field.  Clear the state stored in kCustomBarLockValue
		
			//	CustomBarEventData::fData contains the index of the field posting the event
			CustomBarFieldInfo fi;
			
			// With this event eventData->fData contains a short.
			short fieldIndex = short(inEventData.fData);

			fi.fIsLocked = false;
			
			if (fCustomBarID != 0) {
			
				//	fData is a short
				(void) GS_CustomBarGetFieldInfo(gCBP, fCustomBarID, fieldIndex, fi);
				GS_DisposePtr(gCBP, fLockedBarValuePtr);
				fLockedBarValuePtr = nil;
			}

			(void) GS_CustomBarSetFieldInfo(gCBP, fCustomBarID, fieldIndex, fi);

			break;
		}
		default: break;
	}
	return retVal;
}

//--------------------------------------------------------------------------------------------
Boolean DirectModelingData::GetReshapeParameters(EResizeMode inResizeMode,
												const WorldPt3& reshapePt,
												WorldCoord& outRadius_i_Val, 
												WorldCoord& outRadius_j_Val,
												WorldCoord& outRadius_k_Val)
{
	Boolean retVal = false;
	
	if (inResizeMode == eFromCenter) 
	{	
		WorldPt3 reshapeVector = reshapePt - fWorldCenter;
		
		outRadius_i_Val = fRadius_i;
		outRadius_j_Val = fRadius_j;
		outRadius_k_Val = fRadius_k;
		
		switch (fActiveHotSpotIDIndex) {
		
			case kMax_i_HotSpotIDsIndex:
			case kMin_i_HotSpotIDsIndex: {
				
				if (fLockedBarValuePtr)
					outRadius_i_Val = *fLockedBarValuePtr;
				else
					outRadius_i_Val = DotProduct(reshapeVector, fRadius_i_UnitVector);
				break;
			}
			
			case kMax_j_HotSpotIDsIndex:
			case kMin_j_HotSpotIDsIndex:  {
			
				if (fLockedBarValuePtr)
					outRadius_j_Val = *fLockedBarValuePtr;
				else
					outRadius_j_Val = DotProduct(reshapeVector, fRadius_j_UnitVector);
				break;
			}
			
			case kMax_k_HotSpotIDsIndex:
			case kMin_k_HotSpotIDsIndex:   {
			
				if (fLockedBarValuePtr)
					outRadius_k_Val = *fLockedBarValuePtr;
				else
					outRadius_k_Val = DotProduct(reshapeVector, fRadius_k_UnitVector);
				break;
			}
			default:
				break;
		}

		retVal = true;
	}
	return retVal;
}
 
//::::::::::::::::::::::::::::::::
static void DrawBox3D(const WorldPt3& center,
						const Vector& i,
						const Vector& j,
						const Vector& k)
{
	GS_DrawCoordLine3D(gCBP, center + i + j + k, center + i + j - k);
	GS_DrawCoordLine3D(gCBP, center + i - j + k, center + i - j - k);
	GS_DrawCoordLine3D(gCBP, center - i + j + k, center - i + j - k);
	GS_DrawCoordLine3D(gCBP, center - i - j + k, center - i - j - k);

	GS_DrawCoordLine3D(gCBP, center + i + j + k, center - i + j + k);
	GS_DrawCoordLine3D(gCBP, center + i + j - k, center - i + j - k);
	GS_DrawCoordLine3D(gCBP, center + i - j + k, center - i - j + k);
	GS_DrawCoordLine3D(gCBP, center + i - j - k, center - i - j - k);

	GS_DrawCoordLine3D(gCBP, center + i + j + k, center + i - j + k);
	GS_DrawCoordLine3D(gCBP, center + i + j - k, center + i - j - k);
	GS_DrawCoordLine3D(gCBP, center - i + j + k, center - i - j + k);
	GS_DrawCoordLine3D(gCBP, center - i + j - k, center - i - j - k);		
}

static void DrawPlanarEllipse(const WorldPt3& c, const Vector& i, const Vector& j)
{
	WorldPt3 lastPt = c + i;
	double radianInc = PI/16;
	double stopRadian = 2 * PI+radianInc/2;
	double currentRadian = radianInc;
	WorldPt3 thisPt = c + cos(currentRadian)*i + sin(currentRadian)*j;
	while (currentRadian < stopRadian) {
	
		GS_DrawCoordLine3D(gCBP, lastPt, thisPt);
		lastPt = thisPt;
		currentRadian += radianInc;
		thisPt = c + cos(currentRadian)*i + sin(currentRadian)*j;
	}
	
}

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
static void DrawDashed3DSeg(CallBackPtr inCBP, const WorldPt3& point1, const WorldPt3& point2, WorldCoord inDashPixel_Coords)
{
	if (point2 != point1) {
	
		WorldCoord length = (point2 - point1).Magnitude();
		WorldCoord runningLength = inDashPixel_Coords;
		WorldPt3 dashStart = point1;
		WorldPt3 dashVector = inDashPixel_Coords*(point2 - point1)/length;
		while (runningLength <= length) {
						
			GS_DrawCoordLine3D(inCBP, dashStart, dashStart + dashVector);
			runningLength += 2*inDashPixel_Coords;
			dashStart = dashStart + dashVector + dashVector;
		}
	}
}

//--------------------------------------------------------------------------------------------
void DirectModelingData::OnDraw()
//
//	Ge the current tool point value and draw.
{
	WorldPt3 currentPt;
	WorldCoord a,b,c;
	
	if (	GS_GetToolPtCurrent3D(gCBP, currentPt)
		&&	GetReshapeParameters(eFromCenter, currentPt, a,b,c)) 
	{
	
		WorldPt3 i = fRadius_i_UnitVector * fabs(a);	
		WorldPt3 j = fRadius_j_UnitVector * fabs(b);
		WorldPt3 k = fRadius_k_UnitVector * fabs(c);

		const short k_One_Pixel = 1;
		WorldCoord onePixel_Coords;
		GS_PixelLengthToCoordLength(gCBP, k_One_Pixel, onePixel_Coords);
		
		WorldCoord sixPixelCoords = 6*onePixel_Coords;
		
		
		switch (fActiveHotSpotIDIndex) {
		
			case kMax_i_HotSpotIDsIndex:
			case kMin_i_HotSpotIDsIndex: {
			
				if (fActiveHotSpotIDIndex == kMax_i_HotSpotIDsIndex && a > 0)
					DrawDashed3DSeg(gCBP, fWorldCenter, fWorldCenter + i, sixPixelCoords);
				else
					DrawDashed3DSeg(gCBP, fWorldCenter, fWorldCenter - i, sixPixelCoords);
					
				DrawPlanarEllipse(fWorldCenter, i, j);
				DrawPlanarEllipse(fWorldCenter, i, k);
				DrawPlanarEllipse(fWorldCenter, j, k);
				break;
			}
			
			case kMax_j_HotSpotIDsIndex:
			case kMin_j_HotSpotIDsIndex:  {
			
				if (fActiveHotSpotIDIndex == kMax_j_HotSpotIDsIndex && b > 0)
					DrawDashed3DSeg(gCBP, fWorldCenter, fWorldCenter + j, sixPixelCoords);
				else
					DrawDashed3DSeg(gCBP, fWorldCenter, fWorldCenter - j, sixPixelCoords);
					
				DrawPlanarEllipse(fWorldCenter, i, j);
				DrawPlanarEllipse(fWorldCenter, i, k);
				DrawPlanarEllipse(fWorldCenter, j, k);
				break;
			}
			
			case kMax_k_HotSpotIDsIndex:
			case kMin_k_HotSpotIDsIndex:   {
			
				if (fActiveHotSpotIDIndex == kMax_k_HotSpotIDsIndex && c > 0)
					DrawDashed3DSeg(gCBP, fWorldCenter, fWorldCenter + k, sixPixelCoords);
				else
					DrawDashed3DSeg(gCBP, fWorldCenter, fWorldCenter - k, sixPixelCoords);
					
				DrawPlanarEllipse(fWorldCenter, i, j);
				DrawPlanarEllipse(fWorldCenter, i, k);
				DrawPlanarEllipse(fWorldCenter, j, k);
				break;
			}
			default:
				break;
		}
	}
}


//--------------------------------------------------------------------------------------------
long DirectModelingData::OnComplete()
//
//	The user has completed the resize gesture.  Use the tool point data 
//	to make make the chaneg to the selected object.
{
	long retVal = kParametricNoErr;
	WorldPt3 endPoint;
	
	MCObjectHandle h = GetSelectedReshapeObject();
	
	WorldCoord newRadius_i = fRadius_i;
	WorldCoord newRadius_j = fRadius_j;
	WorldCoord newRadius_k = fRadius_k;
	
	
	if (	GS_GetToolPt3D(gCBP, 1, endPoint)
		&&	this->GetReshapeParameters(eFromCenter, endPoint, newRadius_i, newRadius_j, newRadius_k) ) 
	{
		// This is not an object that can be part of a parametric constraint
		// but this is the general pattern to follow when modifying object geometry.
		
		GS_CreateConstraintModel(gCBP, nil, true);
		GS_SupportUndoAndRemove(gCBP);

		GS_SetUndoMethod(gCBP, kUndoSwapObjects);
		
		Str255 theUndoEventString;	
		GS_GetResourceString(gCBP, 11000, 4, theUndoEventString);	//	STR# (11000,4)
		
		GS_NameUndoEvent(gCBP, theUndoEventString);
		
		GS_AddBothSwapObject(gCBP, h);
		
		if (SetMyParameterValues(h, newRadius_i, newRadius_j, newRadius_k)) {
		
			GS_ResetObject(gCBP, h);
		}
		
		//	This is how you end an event when you modify object geometry.
		if (!GS_UpdateConstraintModel(gCBP))
			GS_UndoAndRemove(gCBP);
		else
			GS_EndUndoEvent(gCBP);
	}
	
	this->RemoveHotSpots();
	this->RemoveCustomBar();
	this->Update();
	this->AddHotSpots();
	return retVal;
}

static MCObjectHandle GetSelectedReshapeObject()
//
//	Returns the first selected object.  
{
	MCObjectHandle firstSelected = GS_FirstSelectedObject(gCBP);
		
	return firstSelected;

}



//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
long OnObjectWidgetPrep(MCObjectHandle myObjectHandle, long message, long& userData)
{
	userData;
	long retVal = 0;

	OnObjectWidgetPrepCall* thisCall = (OnObjectWidgetPrepCall*) message;
	if (thisCall) {
		
		WidgetInfoType* widgetInfoArray = (WidgetInfoType*) thisCall->fData;
						
		if (widgetInfoArray) {
	
			long numWidgetInfos = thisCall->fSpecifier;
				
			for (short widgetInfoIndex = 0; widgetInfoIndex < numWidgetInfos; widgetInfoIndex++) {
			
				int mappedParamIndex = widgetInfoArray[widgetInfoIndex].fMappingID;		// This is the field this param corresponds to in format
				
				if (mappedParamIndex == kButtonMapping)
					widgetInfoArray[widgetInfoIndex].fFlag = WidgetInfoType::kDisable;
				else
					widgetInfoArray[widgetInfoIndex].fFlag = WidgetInfoType::kShow;
			}	
		}
		retVal = kObjectEventHandled;
	}
	return retVal;
}

