//////////
//
//	File:		QTActionTargets.c
//
//	Contains:	QuickTime wired sprites target support for QuickTime movies.
//
//	Written by:	Tim Monroe
//
//	Copyright:	© 2001 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <2>	 	04/07/01	rtm		added theMovieHasID parameter to QTUtils_GetMovieTargetID
//	   <1>	 	02/28/01	rtm		first file; based on existing QTWiredSpritesJr sample code
//	   
//
//////////

//////////
//
// header files
//
//////////

#include "QTActionTargets.h"


//////////
//
// global variables
//
//////////

extern short 				gAppResFile;						// file reference number for this application's resource file
extern ModalFilterUPP		gModalFilterUPP;					// UPP to our custom dialog event filter


//////////
//
// QTTarg_GetStringFromUser
// Display a dialog box to elicit a string from the user; return a C string that contains the text in the
// dialog box when the user clicks the OK button; otherwise, return NULL.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////

char *QTTarg_GetStringFromUser (short thePromptStringIndex)
{
	short 			myItem;
	short 			mySavedResFile;
	GrafPtr			mySavedPort;
	DialogPtr		myDialog = NULL;
	short			myItemKind;
	Handle			myItemHandle;
	Rect			myItemRect;
	Str255			myString;
	char			*myCString = NULL;
	OSErr			myErr = noErr;

	//////////
	//
	// save the current resource file and graphics port
	//
	//////////

	mySavedResFile = CurResFile();
	GetPort(&mySavedPort);

	// set the application's resource file
	UseResFile(gAppResFile);

	//////////
	//
	// create the dialog box in which the user will enter a URL
	//
	//////////

	myDialog = GetNewDialog(kGetStr_DLOGID, NULL, (WindowPtr)-1L);
	if (myDialog == NULL)
		goto bail;

	QTFrame_ActivateController(QTFrame_GetFrontMovieWindow(), false);
	
	MacSetPort(GetDialogPort(myDialog));
	
	SetDialogDefaultItem(myDialog, kGetStr_OKButton);
	SetDialogCancelItem(myDialog, kGetStr_CancelButton);
	
	// set the prompt string	
	GetIndString(myString, kTextKindsResourceID, thePromptStringIndex);

	GetDialogItem(myDialog, kGetStr_StrLabelItem, &myItemKind, &myItemHandle, &myItemRect);
	SetDialogItemText(myItemHandle, myString);
	
	MacShowWindow(GetDialogWindow(myDialog));
	
	//////////
	//
	// display and handle events in the dialog box until the user clicks OK or Cancel
	//
	//////////
	
	do {
		ModalDialog(gModalFilterUPP, &myItem);
	} while ((myItem != kGetStr_OKButton) && (myItem != kGetStr_CancelButton));
	
	//////////
	//
	// handle the selected button
	//
	//////////
	
	if (myItem != kGetStr_OKButton) {
		myErr = userCanceledErr;
		goto bail;
	}
	
	// retrieve the edited text
	GetDialogItem(myDialog, kGetStr_StrTextItem, &myItemKind, &myItemHandle, &myItemRect);
	GetDialogItemText(myItemHandle, myString);
	myCString = QTUtils_ConvertPascalToCString(myString);
	
bail:
	// restore the previous resource file and graphics port
	MacSetPort(mySavedPort);
	UseResFile(mySavedResFile);
	
	if (myDialog != NULL)
		DisposeDialog(myDialog);

	return(myCString);
}


//////////
//
// QTTarg_ShowStringToUser 
// Display a string in an alert box.
//
//////////

void QTTarg_ShowStringToUser (StringPtr theString)
{
	short 		mySavedResFile;

	// get the current resource file and set the application's resource file
	mySavedResFile = CurResFile();
	UseResFile(gAppResFile);

	ParamText(theString, NULL, NULL, NULL);
	Alert(kQTTargAlertID, NULL);
	
	// restore the original resource file
	UseResFile(mySavedResFile);
}


//////////
//
// QTTarg_CreateTwinSpritesMovie
// Create a movie with two sprites that target each other.
//
//////////

OSErr QTTarg_CreateTwinSpritesMovie (void)
{
	Movie					myMovie = NULL;
	Track					myTrack = NULL;
	Media					myMedia = NULL;
	FSSpec					myFile;
	Boolean					myIsSelected = false;
	Boolean					myIsReplacing = false;	
	Fixed					myHeight = 0;
	Fixed					myWidth = 0;
	StringPtr 				myPrompt = QTUtils_ConvertCToPascalString(kSpriteSavePrompt);
	StringPtr 				myFileName = QTUtils_ConvertCToPascalString(kSpriteSaveMovieFileName);
	long					myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
	OSType					myType = FOUR_CHAR_CODE('none');
	short					myResRefNum = 0;
	short					myResID = movieInDataForkResID;
	OSErr					myErr = noErr;

	//////////
	//
	// create a new movie file
	//
	//////////

	// prompt the user for the destination file name
	QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing);
	myErr = myIsSelected ? noErr : userCanceledErr;
	if (!myIsSelected)
		goto bail;

	// create a movie file for the destination movie
	myErr = CreateMovieFile(&myFile, FOUR_CHAR_CODE('TVOD'), smSystemScript, myFlags, &myResRefNum, &myMovie);
	if (myErr != noErr)
		goto bail;
	
	// select the "no-interface" movie controller
	myType = EndianU32_NtoB(myType);
	SetUserDataItem(GetMovieUserData(myMovie), &myType, sizeof(myType), kUserDataMovieControllerType, 1);
	
	//////////
	//
	// create the sprite track and media
	//
	//////////
	
	myWidth = Long2Fix(kIconSpriteTrackWidth);
	myHeight = Long2Fix(kIconSpriteTrackHeight);

	myTrack = NewMovieTrack(myMovie, myWidth, myHeight, kNoVolume);
	myMedia = NewTrackMedia(myTrack, SpriteMediaType, kSpriteMediaTimeScale, NULL, 0);

	myErr = BeginMediaEdits(myMedia);
	if (myErr != noErr)
		goto bail;

	//////////
	//
	// add the appropriate samples to the sprite media
	//
	//////////
	
	myErr = QTTarg_AddIconMovieSamplesToMedia(myMedia);
	if (myErr != noErr)
		goto bail;
	
	myErr = EndMediaEdits(myMedia);
	if (myErr != noErr)
		goto bail;
	
	// add the media to the track
	InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
		
	//////////
	//
	// set the sprite track properties
	//
	//////////
	
	QTTarg_SetTrackProperties(myMedia, 1);
	
	//////////
	//
	// add the movie resource to the movie file
	//
	//////////
	
	myErr = AddMovieResource(myMovie, myResRefNum, &myResID, myFile.name);
		
bail:
	if (myResRefNum != 0)
		CloseMovieFile(myResRefNum);

	if (myMovie != NULL)
		DisposeMovie(myMovie);
		
	free(myPrompt);
	free(myFileName);

	return(myErr);
}


//////////
//
// QTTarg_AddIconMovieSamplesToMedia
// Build the key frame for the icon sprites movie.
//
//////////

OSErr QTTarg_AddIconMovieSamplesToMedia (Media theMedia)
{
	QTAtomContainer			mySample = NULL;
	QTAtomContainer			mySpriteData = NULL;
	QTAtom					myActionAtom = 0;
	QTAtom					myTargetAtom = 0;
	RGBColor				myKeyColor;
	Point					myLocation;
	short					isVisible, myIndex;
	Fixed					myDegrees;
	FixedPoint				myPoint;
	OSErr					myErr = noErr;
	
	//////////
	//
	// create a key frame sample containing the sprite images
	//
	//////////

	// create a new, empty key frame sample
	myErr = QTNewAtomContainer(&mySample);
	if (myErr != noErr)
		goto bail;

	myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff;		// white
	
	myPoint.x = Long2Fix(kIconDimension / 2);
	myPoint.y = Long2Fix(kIconDimension / 2);

	// add images to the key frame sample
	SpriteUtils_AddPICTImageToKeyFrameSample(mySample, kOldQTIconID, &myKeyColor, kOldQTIconSpriteAtomID, &myPoint, NULL);
	SpriteUtils_AddPICTImageToKeyFrameSample(mySample, kNewQTIconID, &myKeyColor, kNewQTIconSpriteAtomID, &myPoint, NULL);

	//////////
	//
	// add the initial sprite properties and actions to the key frame sample
	//
	//////////
	
	// the old QT icon sprite
	myErr = QTNewAtomContainer(&mySpriteData);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= kIconDimension + (kIconDimension / 2);
	myLocation.v	= ((kIconSpriteTrackHeight - kIconDimension) / 2) + (kIconDimension / 2);
	isVisible		= true;
	myIndex			= kOldQTIconImageIndex;
	
	SpriteUtils_SetSpriteData(mySpriteData, &myLocation, &isVisible, NULL, &myIndex, NULL, NULL, NULL);
	WiredUtils_AddQTEventAndActionAtoms(mySpriteData, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton, kActionSpriteRotate, &myActionAtom);
	myDegrees = EndianS32_NtoB(FixRatio(90, 1));
	WiredUtils_AddActionParameterAtom(mySpriteData, myActionAtom, kFirstParam, sizeof(myDegrees), &myDegrees, NULL);
	WiredUtils_AddSpriteIDActionTargetAtom(mySpriteData, myActionAtom, 2, NULL);
	SpriteUtils_AddSpriteToSample(mySample, mySpriteData, kOldQTIconSpriteAtomID);
	
	QTDisposeAtomContainer(mySpriteData);
	
	// the new QT icon sprite
	myErr = QTNewAtomContainer(&mySpriteData);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= kIconSpriteTrackWidth - (kIconDimension + (kIconDimension / 2));
	myLocation.v	= ((kIconSpriteTrackHeight - kIconDimension) / 2) + (kIconDimension / 2);
	isVisible		= true;
	myIndex			= kNewQTIconImageIndex;
	
	SpriteUtils_SetSpriteData(mySpriteData, &myLocation, &isVisible, NULL, &myIndex, NULL, NULL, NULL);
	WiredUtils_AddQTEventAndActionAtoms(mySpriteData, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton, kActionSpriteRotate, &myActionAtom);
	myDegrees = EndianS32_NtoB(FixRatio(90, 1));
	WiredUtils_AddActionParameterAtom(mySpriteData, myActionAtom, kFirstParam, sizeof(myDegrees), &myDegrees, NULL);
	WiredUtils_AddSpriteIDActionTargetAtom(mySpriteData, myActionAtom, 1, NULL);
	SpriteUtils_AddSpriteToSample(mySample, mySpriteData, kNewQTIconSpriteAtomID);
	
	SpriteUtils_AddSpriteSampleToMedia(theMedia, mySample, kSpriteMediaFrameDurationIcon, true, NULL);	

bail:	
	if (mySample != NULL)
		QTDisposeAtomContainer(mySample);

	if (mySpriteData != NULL)
		QTDisposeAtomContainer(mySpriteData);
	
	return(myErr);
}


//////////
//
// QTTarg_AddVRControllerButtonSamplesToMedia
// Build the key frame for the VR controller sprite track.
//
//////////

void QTTarg_AddVRControllerButtonSamplesToMedia (Media theMedia, long theTrackWidth, long theTrackHeight, TimeValue theDuration)
{
#pragma unused(theTrackHeight)
	QTAtomContainer			mySample = NULL;
	QTAtomContainer			myActions = NULL;
	QTAtomContainer			myTiltUpButton, myPanRightButton, myTiltDownButton, myZoomInButton, myPanLeftButton, myZoomOutButton;
	RGBColor				myKeyColor;
	Point					myLocation;
	short					isVisible, myIndex, myLayer;
	short					myCount;
	long					mySixth;
	QTAtom					myActionAtom = 0;
	QTAtom					myParamAtom = 0;
	float					myDeltaAngle = 0;
	StringPtr				myMovieNames[2];
	OSErr					myErr = noErr;
	
	//////////
	//
	// set up the two target movie names
	//
	//////////

	myMovieNames[0] = QTUtils_ConvertCToPascalString(kTarget1);
	myMovieNames[1] = QTUtils_ConvertCToPascalString(kTarget2);

	//////////
	//
	// create a key frame sample containing the sprite images
	//
	//////////

	// create a new, empty key frame sample
	myErr = QTNewAtomContainer(&mySample);
	if (myErr != noErr)
		goto bail;

	myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff;		// white
	
	// add images to the key frame sample
	for (myCount = 0; myCount < kNumControllerImages; myCount++)
		SpriteUtils_AddPICTImageToKeyFrameSample(mySample, kFirstControllerImageID + myCount, &myKeyColor, myCount + 1, NULL, NULL);

	// assign group IDs to the images
	SpriteUtils_AssignImageGroupIDsToKeyFrame(mySample);
	
	//////////
	//
	// add the initial sprite properties and actions to the key frame sample
	//
	//////////
	
	mySixth = theTrackWidth / kNumControllerButtons;

	// the Tilt-Up button sprite
	myErr = QTNewAtomContainer(&myTiltUpButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kTiltPosSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= 0;
	isVisible		= true;
	myIndex			= kTiltPosUpIndex;
	myLayer			= 1;
	myDeltaAngle	= 1;
	EndianUtils_Float_NtoB(&myDeltaAngle);
	
	for (myCount = 0; myCount <= 1; myCount++) {
		SpriteUtils_SetSpriteData(myTiltUpButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
		WiredUtils_AddSpriteSetImageIndexAction(myTiltUpButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, kTiltPosDownIndex, NULL);
		WiredUtils_AddSpriteSetImageIndexAction(myTiltUpButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, kTiltPosUpIndex, NULL);
		WiredUtils_AddSpriteTrackSetVariableAction(myTiltUpButton, kParentAtomIsContainer, kQTEventMouseEnter, kMouseOverTiltUpVariableID, 1, 0, NULL, 0);
		WiredUtils_AddSpriteTrackSetVariableAction(myTiltUpButton, kParentAtomIsContainer, kQTEventMouseExit, kMouseOverTiltUpVariableID, 0, 0, NULL, 0);
		QTTarg_AddIdleEventVarTestAction(myTiltUpButton, kParentAtomIsContainer, kMouseOverTiltUpVariableID, 1, kActionQTVRSetTiltAngle, &myActionAtom);
		WiredUtils_AddActionParameterAtom(myTiltUpButton, myActionAtom, kFirstParam, sizeof(myDeltaAngle), &myDeltaAngle, NULL);
		WiredUtils_AddActionParameterOptions(myTiltUpButton, myActionAtom, 1, kActionFlagActionIsDelta, 0, NULL, 0, NULL);
		WiredUtils_AddMovieNameActionTargetAtom(myTiltUpButton, myActionAtom, myMovieNames[myCount], NULL);
	}
	
	SpriteUtils_AddSpriteToSample(mySample, myTiltUpButton, kTiltPosSpriteAtomID);	

	// the Pan-Right button sprite
	myErr = QTNewAtomContainer(&myPanRightButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kPanRightSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= 0;
	isVisible		= true;
	myIndex			= kPanRightUpIndex;
	myLayer			= 1;
	myDeltaAngle	= -3;
	EndianUtils_Float_NtoB(&myDeltaAngle);
	
	SpriteUtils_SetSpriteData(myPanRightButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);

	for (myCount = 0; myCount <= 1; myCount++) {
		WiredUtils_AddSpriteSetImageIndexAction(myPanRightButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, kPanRightDownIndex, NULL);
		WiredUtils_AddSpriteSetImageIndexAction(myPanRightButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, kPanRightUpIndex, NULL);
		WiredUtils_AddSpriteTrackSetVariableAction(myPanRightButton, kParentAtomIsContainer, kQTEventMouseEnter, kMouseOverPanRightVariableID, 1, 0, NULL, 0);
		WiredUtils_AddSpriteTrackSetVariableAction(myPanRightButton, kParentAtomIsContainer, kQTEventMouseExit, kMouseOverPanRightVariableID, 0, 0, NULL, 0);
		QTTarg_AddIdleEventVarTestAction(myPanRightButton, kParentAtomIsContainer, kMouseOverPanRightVariableID, 1, kActionQTVRSetPanAngle, &myActionAtom);
		WiredUtils_AddActionParameterAtom(myPanRightButton, myActionAtom, kFirstParam, sizeof(myDeltaAngle), &myDeltaAngle, NULL);
		WiredUtils_AddActionParameterOptions(myPanRightButton, myActionAtom, 1, kActionFlagActionIsDelta | kActionFlagParameterWrapsAround, 0, NULL, 0, NULL);
		WiredUtils_AddMovieNameActionTargetAtom(myPanRightButton, myActionAtom, myMovieNames[myCount], NULL);
	}
	
	SpriteUtils_AddSpriteToSample(mySample, myPanRightButton, kPanRightSpriteAtomID);	

	// the Pan-Left button sprite
	myErr = QTNewAtomContainer(&myPanLeftButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kPanLeftSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= 0;
	isVisible		= true;
	myIndex			= kPanLeftUpIndex;
	myLayer			= 1;
	myDeltaAngle	= 3;
	EndianUtils_Float_NtoB(&myDeltaAngle);
	
	SpriteUtils_SetSpriteData(myPanLeftButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	
	for (myCount = 0; myCount <= 1; myCount++) {
		WiredUtils_AddSpriteSetImageIndexAction(myPanLeftButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, kPanLeftDownIndex, NULL);
		WiredUtils_AddSpriteSetImageIndexAction(myPanLeftButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, kPanLeftUpIndex, NULL);
		WiredUtils_AddSpriteTrackSetVariableAction(myPanLeftButton, kParentAtomIsContainer, kQTEventMouseEnter, kMouseOverPanLeftVariableID, 1, 0, NULL, 0);
		WiredUtils_AddSpriteTrackSetVariableAction(myPanLeftButton, kParentAtomIsContainer, kQTEventMouseExit, kMouseOverPanLeftVariableID, 0, 0, NULL, 0);
		QTTarg_AddIdleEventVarTestAction(myPanLeftButton, kParentAtomIsContainer, kMouseOverPanLeftVariableID, 1, kActionQTVRSetPanAngle, &myActionAtom);
		WiredUtils_AddActionParameterAtom(myPanLeftButton, myActionAtom, kFirstParam, sizeof(myDeltaAngle), &myDeltaAngle, NULL);
		WiredUtils_AddActionParameterOptions(myPanLeftButton, myActionAtom, 1, kActionFlagActionIsDelta | kActionFlagParameterWrapsAround, 0, NULL, 0, NULL);
		WiredUtils_AddMovieNameActionTargetAtom(myPanLeftButton, myActionAtom, myMovieNames[myCount], NULL);
	}

	SpriteUtils_AddSpriteToSample(mySample, myPanLeftButton, kPanLeftSpriteAtomID);	

	// the Zoom-Out button sprite
	myErr = QTNewAtomContainer(&myZoomOutButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kZoomOutSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= 0;
	isVisible		= true;
	myIndex			= kZoomOutUpIndex;
	myLayer			= 1;
	myDeltaAngle	= 1;
	EndianUtils_Float_NtoB(&myDeltaAngle);
	
	SpriteUtils_SetSpriteData(myZoomOutButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	
	for (myCount = 0; myCount <= 1; myCount++) {
		WiredUtils_AddSpriteSetImageIndexAction(myZoomOutButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, kZoomOutDownIndex, NULL);
		WiredUtils_AddSpriteSetImageIndexAction(myZoomOutButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, kZoomOutUpIndex, NULL);
		WiredUtils_AddSpriteTrackSetVariableAction(myZoomOutButton, kParentAtomIsContainer, kQTEventMouseEnter, kMouseOverZoomOutVariableID, 1, 0, NULL, 0);
		WiredUtils_AddSpriteTrackSetVariableAction(myZoomOutButton, kParentAtomIsContainer, kQTEventMouseExit, kMouseOverZoomOutVariableID, 0, 0, NULL, 0);
		QTTarg_AddIdleEventVarTestAction(myZoomOutButton, kParentAtomIsContainer, kMouseOverZoomOutVariableID, 1, kActionQTVRSetFieldOfView, &myActionAtom);
		WiredUtils_AddActionParameterAtom(myZoomOutButton, myActionAtom, kFirstParam, sizeof(myDeltaAngle), &myDeltaAngle, NULL);
		WiredUtils_AddActionParameterOptions(myZoomOutButton, myActionAtom, 1, kActionFlagActionIsDelta, 0, NULL, 0, NULL);
		WiredUtils_AddMovieNameActionTargetAtom(myZoomOutButton, myActionAtom, myMovieNames[myCount], NULL);
	}

	SpriteUtils_AddSpriteToSample(mySample, myZoomOutButton, kZoomOutSpriteAtomID);	

	// the Tilt-Down button sprite
	myErr = QTNewAtomContainer(&myTiltDownButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kTiltNegSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= 0;
	isVisible		= true;
	myIndex			= kTiltNegUpIndex;
	myLayer			= 1;
	myDeltaAngle	= -1;
	EndianUtils_Float_NtoB(&myDeltaAngle);
	
	SpriteUtils_SetSpriteData(myTiltDownButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);

	for (myCount = 0; myCount <= 1; myCount++) {
		WiredUtils_AddSpriteSetImageIndexAction(myTiltDownButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, kTiltNegDownIndex, NULL);
		WiredUtils_AddSpriteSetImageIndexAction(myTiltDownButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, kTiltNegUpIndex, NULL);
		WiredUtils_AddSpriteTrackSetVariableAction(myTiltDownButton, kParentAtomIsContainer, kQTEventMouseEnter, kMouseOverTiltDownVariableID, 1, 0, NULL, 0);
		WiredUtils_AddSpriteTrackSetVariableAction(myTiltDownButton, kParentAtomIsContainer, kQTEventMouseExit, kMouseOverTiltDownVariableID, 0, 0, NULL, 0);
		QTTarg_AddIdleEventVarTestAction(myTiltDownButton, kParentAtomIsContainer, kMouseOverTiltDownVariableID, 1, kActionQTVRSetTiltAngle, &myActionAtom);
		WiredUtils_AddActionParameterAtom(myTiltDownButton, myActionAtom, kFirstParam, sizeof(myDeltaAngle), &myDeltaAngle, NULL);
		WiredUtils_AddActionParameterOptions(myTiltDownButton, myActionAtom, 1, kActionFlagActionIsDelta, 0, NULL, 0, NULL);
		WiredUtils_AddMovieNameActionTargetAtom(myTiltDownButton, myActionAtom, myMovieNames[myCount], NULL);
	}

	SpriteUtils_AddSpriteToSample(mySample, myTiltDownButton, kTiltNegSpriteAtomID);	

	// the Zoom-In button sprite
	myErr = QTNewAtomContainer(&myZoomInButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kZoomInSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= 0;
	isVisible		= true;
	myIndex			= kZoomInUpIndex;
	myLayer			= 1;
	myDeltaAngle	= -1;
	EndianUtils_Float_NtoB(&myDeltaAngle);
	
	SpriteUtils_SetSpriteData(myZoomInButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	
	for (myCount = 0; myCount <= 1; myCount++) {
		WiredUtils_AddSpriteSetImageIndexAction(myZoomInButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, kZoomInDownIndex, NULL);
		WiredUtils_AddSpriteSetImageIndexAction(myZoomInButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, kZoomInUpIndex, NULL);
		WiredUtils_AddSpriteTrackSetVariableAction(myZoomInButton, kParentAtomIsContainer, kQTEventMouseEnter, kMouseOverZoomInVariableID, 1, 0, NULL, 0);
		WiredUtils_AddSpriteTrackSetVariableAction(myZoomInButton, kParentAtomIsContainer, kQTEventMouseExit, kMouseOverZoomInVariableID, 0, 0, NULL, 0);
		QTTarg_AddIdleEventVarTestAction(myZoomInButton, kParentAtomIsContainer, kMouseOverZoomInVariableID, 1, kActionQTVRSetFieldOfView, &myActionAtom);
		WiredUtils_AddActionParameterAtom(myZoomInButton, myActionAtom, kFirstParam, sizeof(myDeltaAngle), &myDeltaAngle, NULL);
		WiredUtils_AddActionParameterOptions(myZoomInButton, myActionAtom, 1, kActionFlagActionIsDelta, 0, NULL, 0, NULL);
		WiredUtils_AddMovieNameActionTargetAtom(myZoomInButton, myActionAtom, myMovieNames[myCount], NULL);
	}

	SpriteUtils_AddSpriteToSample(mySample, myZoomInButton, kZoomInSpriteAtomID);	

	SpriteUtils_AddSpriteSampleToMedia(theMedia, mySample, theDuration, true, NULL);	

bail:
	free(myMovieNames[0]);
	free(myMovieNames[1]);

	if (mySample != NULL)
		QTDisposeAtomContainer(mySample);
	if (myTiltUpButton != NULL)
		QTDisposeAtomContainer(myTiltUpButton);	
	if (myPanRightButton != NULL)
		QTDisposeAtomContainer(myPanRightButton);	
	if (myTiltDownButton != NULL)
		QTDisposeAtomContainer(myTiltDownButton);	
	if (myZoomInButton != NULL)
		QTDisposeAtomContainer(myZoomInButton);	
	if (myPanLeftButton != NULL)
		QTDisposeAtomContainer(myPanLeftButton);	
	if (myZoomOutButton != NULL)
		QTDisposeAtomContainer(myZoomOutButton);	
}


//////////
//
// QTTarg_AddTextButtonSamplesToMedia
// Build the key frame for the text button sprite track.
//
//////////

void QTTarg_AddTextButtonSamplesToMedia (Media theMedia, long theTrackWidth, long theTrackHeight, TimeValue theDuration)
{
#pragma unused(theTrackWidth, theTrackHeight)
	QTAtomContainer			mySample = NULL;
	QTAtomContainer			myTextButton;
	QTAtom					myActionAtom = 0;
	RGBColor				myKeyColor;
	Point					myLocation;
	short					isVisible, myIndex, myLayer;
	short					myCount;
	OSErr					myErr = noErr;
	
	//////////
	//
	// create a key frame sample containing the sprite images
	//
	//////////

	// create a new, empty key frame sample
	myErr = QTNewAtomContainer(&mySample);
	if (myErr != noErr)
		goto bail;

	myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff;		// white
	
	// add images to the key frame sample
	for (myCount = 0; myCount < kNumTextImages; myCount++)
		SpriteUtils_AddPICTImageToKeyFrameSample(mySample, kFirstTextImageID + myCount, &myKeyColor, myCount + 1, NULL, NULL);

	// assign group IDs to the images
	SpriteUtils_AssignImageGroupIDsToKeyFrame(mySample);
	
	//////////
	//
	// add the initial sprite properties and actions to the key frame sample
	//
	//////////
	
	// the Text button sprite
	myErr = QTNewAtomContainer(&myTextButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= kButtonWidth / 2;
	myLocation.v	= kButtonHeight / 2;
	isVisible		= true;
	myIndex			= kTextUpIndex;
	myLayer			= 1;
	
	SpriteUtils_SetSpriteData(myTextButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	
	WiredUtils_AddSpriteSetImageIndexAction(myTextButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kTextDownIndex, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myTextButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kTextUpIndex, NULL);
	WiredUtils_AddQTEventAndActionAtoms(myTextButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton, kActionTrackSetEnabled, &myActionAtom);
	WiredUtils_AddActionParameterOptions(myTextButton, myActionAtom, 1, kActionFlagActionIsToggle, 0, NULL, 0, NULL);
	WiredUtils_AddTrackTargetAtom(myTextButton, myActionAtom, kTargetTrackType, (void *)TextMediaType, 1);
	
	SpriteUtils_AddSpriteToSample(mySample, myTextButton, kTextSpriteAtomID);	
	SpriteUtils_AddSpriteSampleToMedia(theMedia, mySample, theDuration, true, NULL);	

bail:	
	if (mySample != NULL)
		QTDisposeAtomContainer(mySample);
	if (myTextButton != NULL)
		QTDisposeAtomContainer(myTextButton);	
}


//////////
//
// QTTarg_AddVRSpriteControllerTrack
// Add a track to the specified VR movie that contains wired sprites for controlling the movie.
//
// We add six sprites to the track, which will function like buttons. On mouse over, the image
// of the sprite is changed to the "clicked" image; on mouse exit, the image is returned to the
// normal "unclicked" image; on idle events we execute an action (for instance kActionMovieGoToBeginning).
// No other logic and no sprite track variables are required here.
//
//////////

OSErr QTTarg_MakeDualVRControllerMovie (void)
{
	Movie					myMovie = NULL;
	Track					myTrack = NULL;
	Media					myMedia = NULL;
	RGBColor				myKeyColor;
	Fixed					myWidth, myHeight;
	FSSpec					myFile;
	Boolean					myIsSelected = false;
	Boolean					myIsReplacing = false;	
	StringPtr 				myPrompt = QTUtils_ConvertCToPascalString(kSpriteSavePrompt);
	StringPtr 				myFileName = QTUtils_ConvertCToPascalString(kSpriteSaveMovieFileName);
	long					myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
	short					myResRefNum = 0;
	short					myResID = movieInDataForkResID;
	OSType					myType = FOUR_CHAR_CODE('none');
	OSErr					myErr = noErr;

	//////////
	//
	// create a new movie file
	//
	//////////

	// prompt the user for the destination file name
	QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing);
	myErr = myIsSelected ? noErr : userCanceledErr;
	if (!myIsSelected)
		goto bail;

	// create a movie file for the destination movie
	myErr = CreateMovieFile(&myFile, FOUR_CHAR_CODE('TVOD'), smSystemScript, myFlags, &myResRefNum, &myMovie);
	if (myErr != noErr)
		goto bail;
	
	// select the "no-interface" movie controller
	myType = EndianU32_NtoB(myType);
	SetUserDataItem(GetMovieUserData(myMovie), &myType, sizeof(myType), kUserDataMovieControllerType, 1);

	//////////
	//
	// get some information about the target movie
	//
	//////////

	myWidth = Long2Fix(kVRControlMovieWidth);
	myHeight = Long2Fix(kVRControlMovieHeight);
	
	//////////
	//
	// create a new sprite track in the target movie
	//
	//////////
	
	myTrack = NewMovieTrack(myMovie, myWidth, myHeight, kNoVolume);
	myMedia = NewTrackMedia(myTrack, SpriteMediaType, kVRControlMovieDuration, NULL, 0);

	myErr = BeginMediaEdits(myMedia);
	if (myErr != noErr)
		goto bail;
	
	//////////
	//
	// add sprite images and sprites to the sprite track; add actions to the sprites
	//
	//////////
	
	QTTarg_AddVRControllerButtonSamplesToMedia(myMedia, kVRControlMovieWidth, kVRControlMovieHeight, kVRControlMovieDuration);
	
	//////////
	//
	// insert media into track
	//
	//////////
	
	myErr = EndMediaEdits(myMedia);
	if (myErr != noErr)
		goto bail;
	
	// add the media to the track
	InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
		
	//////////
	//
	// set the sprite track properties
	//
	//////////
	
	QTTarg_SetTrackProperties(myMedia, 0);								// idle as fast as possible
	
	myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff;		// white
	MediaSetGraphicsMode(GetMediaHandler(myMedia), transparent, &myKeyColor);
	
	//////////
	//
	// add the movie resource to the movie file
	//
	//////////
	
	myErr = AddMovieResource(myMovie, myResRefNum, &myResID, myFile.name);
		
bail:
	if (myResRefNum != 0)
		CloseMovieFile(myResRefNum);

	if (myMovie != NULL)
		DisposeMovie(myMovie);
		
	free(myPrompt);
	free(myFileName);

	return(myErr);
}


//////////
//
// QTTarg_AddTextToggleButtonTrack
// Add a track to the specified movie that contains a wired sprite for toggling the visibility
// state of the first text track in the movie.
//
//////////

OSErr QTTarg_AddTextToggleButtonTrack (Movie theMovie)
{
	Track					myTrack = NULL;
	Media					myMedia = NULL;
	MatrixRecord			myMatrix;
	RGBColor				myKeyColor;
	Fixed					myWidth, myHeight;
	TimeValue				myDuration = 0L;
	TimeValue				myTimeScale = 0L;
	OSErr					myErr = noErr;

	//////////
	//
	// get some information about the target movie
	//
	//////////

	if (theMovie == NULL) {
		myErr = paramErr;
		goto bail;
	}

	myWidth = Long2Fix(2 * kButtonWidth);
	myHeight = Long2Fix(2 * kButtonHeight);
	myDuration = GetMovieDuration(theMovie);
	myTimeScale = GetMovieTimeScale(theMovie);
	
	//////////
	//
	// create a new sprite track in the target movie
	//
	//////////
	
	myTrack = NewMovieTrack(theMovie, myWidth, myHeight, kNoVolume);
	myMedia = NewTrackMedia(myTrack, SpriteMediaType, myTimeScale, NULL, 0);

	// set the track matrix to compensate for any existing movie matrix
	GetMovieMatrix(theMovie, &myMatrix);
	if (InverseMatrix(&myMatrix, &myMatrix))
		SetTrackMatrix(myTrack, &myMatrix);

	myErr = BeginMediaEdits(myMedia);
	if (myErr != noErr)
		goto bail;
	
	//////////
	//
	// add sprite images and sprites to the sprite track; add actions to the sprites
	//
	//////////
	
	QTTarg_AddTextButtonSamplesToMedia(myMedia, 2 * kButtonWidth, 2 * kButtonHeight, myDuration);
	
	//////////
	//
	// insert media into track
	//
	//////////
	
	myErr = EndMediaEdits(myMedia);
	if (myErr != noErr)
		goto bail;
	
	// add the media to the track
	InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
		
	//////////
	//
	// set the sprite track properties
	//
	//////////
	
	QTTarg_SetTrackProperties(myMedia, kNoQTIdleEvents);				// no idle events
	
	myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff;		// white
	MediaSetGraphicsMode(GetMediaHandler(myMedia), transparent, &myKeyColor);
	
	// make sure that the sprite track is in the frontmost layer
	SetTrackLayer(myTrack, kMaxLayerNumber);
	SetTrackLayer(myTrack, QTTarg_GetLowestLayerInMovie(theMovie) - 1);
		
bail:
	return(myErr);
}


//////////
//
// QTTarg_SetTrackProperties
// Set the track properties for the specified sample sprite movie.
//
//////////

void QTTarg_SetTrackProperties (Media theMedia, UInt32 theIdleFrequency)
{
	QTAtomContainer		myTrackProperties;
	RGBColor			myBackgroundColor;
	Boolean				hasActions;
	UInt32				myFrequency;
	OSErr				myErr = noErr;
		
	// add a background color to the sprite track
	myBackgroundColor.red = EndianU16_NtoB(0xffff);
	myBackgroundColor.green = EndianU16_NtoB(0xffff);
	myBackgroundColor.blue = EndianU16_NtoB(0xffff);
	
	myErr = QTNewAtomContainer(&myTrackProperties);
	if (myErr == noErr) {
		QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyBackgroundColor, 1, 1, sizeof(myBackgroundColor), &myBackgroundColor, NULL);

		// tell the movie controller that this sprite track has actions
		hasActions = true;
		QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyHasActions, 1, 1, sizeof(hasActions), &hasActions, NULL);
	
		// tell the sprite track to generate QTIdleEvents
		myFrequency = EndianU32_NtoB(theIdleFrequency);
		QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyQTIdleEventsFrequency, 1, 1, sizeof(myFrequency), &myFrequency, NULL);

		SetMediaPropertyAtom(theMedia, myTrackProperties);
		
		QTDisposeAtomContainer(myTrackProperties);
	}
}
	

//////////
//
// QTTarg_GetLowestLayerInMovie
// Return the layer of the frontmost layer in the specified movie.
//
//////////

short QTTarg_GetLowestLayerInMovie (Movie theMovie)
{
	long		myCount = 0;
	long		myIndex;
	short		myLayer = 0;
	short		myMinLayer = kMaxLayerNumber;
	
	myCount = GetMovieTrackCount(theMovie);
	
	for (myIndex = 1; myIndex <= myCount; myIndex++) {
		myLayer = GetTrackLayer(GetMovieIndTrack(theMovie, myIndex));
		if (myLayer < myMinLayer)
			myMinLayer = myLayer;
	}
	
	return(myMinLayer);
}


//////////
//
// QTTarg_AddIdleEventVarTestAction
// Add, to the specified atom, a conditional atom that executes the specified action if theVariableID = theTrueValue.
//
//////////

OSErr QTTarg_AddIdleEventVarTestAction (QTAtomContainer theContainer, QTAtom theAtom, QTAtomID theVariableID, UInt32 theTrueValue, long theActionConstant, QTAtom *theNewActionAtom)
{
	QTAtom		myActionAtom = 0;
	QTAtom		myParamAtom = 0;
	QTAtom		myConditionalAtom = 0;
	QTAtom		myExpressionAtom = 0;
	QTAtom		myOperatorAtom = 0;
	QTAtom		myActionListAtom = 0;
	OSErr		myErr = noErr;
	
	myErr = WiredUtils_AddQTEventAndActionAtoms(theContainer, theAtom, kQTEventIdle, kActionCase, &myActionAtom);
	if (myErr != noErr)
		goto bail;

	// add a parameter atom to the kActionCase action atom; this will serve as a parent to hold the expression and action atoms
	myErr = WiredUtils_AddActionParameterAtom(theContainer, myActionAtom, kFirstParam, 0, NULL, &myParamAtom);
	if (myErr != noErr)
		goto bail;
	
	myErr = WiredUtils_AddConditionalAtom(theContainer, myParamAtom, 1, &myConditionalAtom);
	if (myErr != noErr)
		goto bail;
		
	myErr = WiredUtils_AddExpressionContainerAtomType(theContainer, myConditionalAtom, &myExpressionAtom);
	if (myErr != noErr)
		goto bail;
		
	myErr = WiredUtils_AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo, &myOperatorAtom);
	if (myErr != noErr)
		goto bail;

	myErr = WiredUtils_AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant, 1, NULL, theTrueValue);
	if (myErr != noErr)
		goto bail;

	myErr = WiredUtils_AddVariableOperandAtom(theContainer, myOperatorAtom, 2, 0, NULL, 0, theVariableID);
	if (myErr != noErr)
		goto bail;
		
	myErr = WiredUtils_AddActionListAtom(theContainer, myConditionalAtom, &myActionListAtom);
	if (myErr != noErr)
		goto bail;

	myErr = WiredUtils_AddActionAtom(theContainer, myActionListAtom, theActionConstant, theNewActionAtom);
	if (myErr != noErr)
		goto bail;

bail:
	return(myErr);
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Target utilities
//
// Use these functions to get, set, and find wired action movie targets.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////
//
// QTUtils_GetMovieTargetName
// Get the movie target name stored in the specified movie.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////

char *QTUtils_GetMovieTargetName (Movie theMovie)
{
	UserData		myUserData = NULL;
	char 			*myString = NULL;
	
	// make sure we've got a movie
	if (theMovie == NULL)
		goto bail;
	
	// get the movie's user data list
	myUserData = GetMovieUserData(theMovie);
	if (myUserData == NULL)
		goto bail;

	// find the "value" of the user data item of type 'plug' that begins with the string "moviename="
	myString = QTUtils_GetUserDataPrefixedValue(myUserData, FOUR_CHAR_CODE('plug'), kMovieNamePrefix);

bail:
	return(myString);
}


//////////
//
// QTUtils_SetMovieTargetName
// Set the movie target name stored in the specified movie.
//
//////////

OSErr QTUtils_SetMovieTargetName (Movie theMovie, char *theTargetName)
{
	UserData		myUserData = NULL;
	char 			*myString = NULL;
	Handle			myHandle = NULL;
	OSErr			myErr = noErr;

	// make sure we've got a movie and a name
	if ((theMovie == NULL) || (theTargetName == NULL))
		return(paramErr);
		
	// get the movie's user data list
	myUserData = GetMovieUserData(theMovie);
	if (myUserData == NULL)
		return(paramErr);

	// remove any existing movie target name
	while (QTUtils_FindUserDataItemWithPrefix(myUserData, FOUR_CHAR_CODE('plug'), kMovieNamePrefix) != 0)
		RemoveUserData(myUserData, FOUR_CHAR_CODE('plug'), QTUtils_FindUserDataItemWithPrefix(myUserData, FOUR_CHAR_CODE('plug'), kMovieNamePrefix));

	// create the user data item data
	myString = malloc(strlen(kMovieNamePrefix) + strlen(theTargetName) + 2 + 1);	// 2 + 1 == '\"' + '\"' + '\0'
	if (myString != NULL) {
		myString[0] = '\0';
		strcat(myString, kMovieNamePrefix);
		strcat(myString, "\"");
		strcat(myString, theTargetName);
		strcat(myString, "\"");

		// add in a new user data item
		PtrToHand(myString, &myHandle, strlen(myString));
		if (myHandle != NULL)
			myErr = AddUserData(myUserData, myHandle, FOUR_CHAR_CODE('plug'));
	} else {
		myErr = memFullErr;
	}
	
	free(myString);
	
	if (myHandle != NULL)
		DisposeHandle(myHandle);

	return(myErr);
}


//////////
//
// QTUtils_GetMovieTargetID
// Get the movie target ID stored in the specified movie.
//
//////////

long QTUtils_GetMovieTargetID (Movie theMovie, Boolean *theMovieHasID)
{
	UserData		myUserData = NULL;
	long			myID = 0;
	char 			*myString = NULL;
	StringPtr 		myPString = NULL;
	Boolean			myMovieHasID = false;
	OSErr			myErr = noErr;
	
	// make sure we've got a movie
	if (theMovie == NULL)
		goto bail;
	
	// get the movie's user data list
	myUserData = GetMovieUserData(theMovie);
	if (myUserData == NULL)
		goto bail;

	// find the "value" of the user data item of type 'plug' that begins with the string "movieid="
	myString = QTUtils_GetUserDataPrefixedValue(myUserData, FOUR_CHAR_CODE('plug'), kMovieIDPrefix);
	
	// convert the string into a number
	if (myString != NULL) {
		myPString = QTUtils_ConvertCToPascalString(myString);
		StringToNum(myPString, &myID);
		myMovieHasID = true;
	}

bail:
	free(myString);
	free(myPString);

	if (theMovieHasID != NULL)
		*theMovieHasID = myMovieHasID;
		
	return(myID);
}


//////////
//
// QTUtils_SetMovieTargetID
// Set the movie target ID stored in the specified movie.
//
//////////

OSErr QTUtils_SetMovieTargetID (Movie theMovie, long theTargetID)
{
	UserData		myUserData = NULL;
	long			myID = 0;
	char 			*myString = NULL;
	Str255	 		myPString;
	char 			*myCString = NULL;
	Handle			myHandle = NULL;
	OSErr			myErr = noErr;

	// make sure we've got a movie
	if (theMovie == NULL)
		return(paramErr);
		
	// get the movie's user data list
	myUserData = GetMovieUserData(theMovie);
	if (myUserData == NULL)
		return(paramErr);

	// remove any existing movie target ID
	while (QTUtils_FindUserDataItemWithPrefix(myUserData, FOUR_CHAR_CODE('plug'), kMovieIDPrefix) != 0)
		RemoveUserData(myUserData, FOUR_CHAR_CODE('plug'), QTUtils_FindUserDataItemWithPrefix(myUserData, FOUR_CHAR_CODE('plug'), kMovieIDPrefix));

	// convert the ID into a string
	NumToString(theTargetID, myPString);
	myCString = QTUtils_ConvertPascalToCString(myPString);
	if (myCString == NULL)
		return(paramErr);
	
	// create the user data item data
	myString = malloc(strlen(kMovieIDPrefix) + strlen(myCString) + 2 + 1);	// 2 + 1 == '\"' + '\"' + '\0'
	if (myString != NULL) {
		myString[0] = '\0';
		strcat(myString, kMovieIDPrefix);
		strcat(myString, "\"");
		strcat(myString, myCString);
		strcat(myString, "\"");

		// add in a new user data item
		PtrToHand(myString, &myHandle, strlen(myString));
		if (myHandle != NULL)
			myErr = AddUserData(myUserData, myHandle, FOUR_CHAR_CODE('plug'));

	} else {
		myErr = memFullErr;
	}
	
	free(myString);
	free(myCString);
	
	if (myHandle != NULL)
		DisposeHandle(myHandle);
	
	return(myErr);
}


//////////
//
// QTUtils_FindUserDataItemWithPrefix
// Return the index of the user data item of the specified type whose data begins with the specified string of characters.
//
//////////

static long QTUtils_FindUserDataItemWithPrefix (UserData theUserData, OSType theType, char *thePrefix)
{
	Handle			myData = NULL;
	long			myCount = 0;
	long			myIndex = 0;
	long			myItemIndex = 0;
	OSErr			myErr = noErr;
	
	// make sure we've got some valid user data
	if (theUserData == NULL)
		goto bail;
	
	// allocate a handle for GetUserData
	myData = NewHandle(0);
	if (myData == NULL)
		goto bail;

	myCount = CountUserDataType(theUserData, theType);
	for (myIndex = 1; myIndex <= myCount; myIndex++) {
		myErr = GetUserData(theUserData, myData, theType, myIndex);
		if (myErr == noErr) {
			if (GetHandleSize(myData) < strlen(thePrefix))
				continue;

			// see if the user data begins with the specified prefix (IdenticalText is case-insensitive)
			if (IdenticalText(*myData, thePrefix, strlen(thePrefix), strlen(thePrefix), NULL) == 0) {
				myItemIndex = myIndex;
				goto bail;
			}
		}
	}

bail:
	if (myData != NULL)
		DisposeHandle(myData);
		
	return(myItemIndex);
}


//////////
//
// QTUtils_GetUserDataPrefixedValue
// Return the string value associated with the specified prefix. The string value may or may not be
// enclosed in double quotes, so we need to handle both cases. (With double quotes is preferred.)
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////

static char *QTUtils_GetUserDataPrefixedValue (UserData theUserData, OSType theType, char *thePrefix)
{
	long			myIndex = 0;
	Handle			myData = NULL;
	long			myLength = 0;
	long			myOffset = 0;
	char 			*myString = NULL;
	OSErr			myErr = noErr;
	
	if (theUserData == NULL)
		goto bail;

	// allocate a handle for GetUserData
	myData = NewHandle(0);
	if (myData == NULL)
		goto bail;
	
	myIndex = QTUtils_FindUserDataItemWithPrefix(theUserData, theType, thePrefix);
	if (myIndex > 0) {
		myErr = GetUserData(theUserData, myData, theType, myIndex);
		if (myErr == noErr) {
			if ((*myData)[strlen(thePrefix)] == '"') {
				myLength = GetHandleSize(myData) - strlen(thePrefix) - 2;
				myOffset = 1;
			} else {
				myLength = GetHandleSize(myData) - strlen(thePrefix);
				myOffset = 0;
			}
		
			myString = malloc(myLength + 1);
			if (myString != NULL) {
				memcpy(myString, *myData + strlen(thePrefix) + myOffset, myLength);
				myString[myLength] = '\0';
			}
		}
	}

bail:
	if (myData != NULL)
		DisposeHandle(myData);

	return(myString);
}


//////////
//
// QTFrame_FindExternalMovieTarget
// Find the external movie targeted by theEMRecPtr.
//
//////////

void QTFrame_FindExternalMovieTarget (MovieController theMC, QTGetExternalMoviePtr theEMRecPtr)
{
#if ALLOW_SELF_TARGETING
#pragma unused(theMC)
#endif
	WindowReference			myWindow = NULL;
	Movie					myTargetMovie = NULL;
	MovieController			myTargetMC = NULL;
	Boolean					myFoundIt = false;
	
	if (theEMRecPtr == NULL)
		return;
	
	// loop through all open movies until we find the one requested
	myWindow = QTFrame_GetFrontMovieWindow();
	while (myWindow != NULL) {
		Movie				myMovie = NULL;
		MovieController		myMC = NULL;
	
		myMC = QTFrame_GetMCFromWindow(myWindow);
		
#if ALLOW_SELF_TARGETING
		if (myMC != NULL) {
#else
		if ((myMC != NULL) && (myMC != theMC)) {
#endif		
			myMovie = MCGetMovie(myMC);
			
			if (theEMRecPtr->targetType == kTargetMovieName) {
				char 		*myStr = NULL;
				
				myStr = QTUtils_GetMovieTargetName(myMovie);
				if (myStr != NULL) {
					if (IdenticalText(&theEMRecPtr->movieName[1], myStr, theEMRecPtr->movieName[0], strlen(myStr), NULL) == 0)
						myFoundIt = true;
					free(myStr);
				}
			}
			
			if (theEMRecPtr->targetType == kTargetMovieID) {
				long		myID = 0;
				Boolean		myMovieHasID = false;
				
				myID = QTUtils_GetMovieTargetID(myMovie, &myMovieHasID);
				if ((theEMRecPtr->movieID == myID) && myMovieHasID)
					myFoundIt = true;
			}
			
			if (myFoundIt) {
				myTargetMovie = myMovie;
				myTargetMC = myMC;
				break;		// break out of while loop
			}
		}
		
		myWindow = QTFrame_GetNextMovieWindow(myWindow);
	}	// while
	
	*theEMRecPtr->theMovie = myTargetMovie;
	*theEMRecPtr->theController = myTargetMC;
}
