//////////
//
//	File:		QTWiredSpritesJr.c
//
//	Contains:	QuickTime wired sprites support for QuickTime movies.
//
//	Written by:	Tim Monroe
//
//	Copyright:	© 2001 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <1>	 	01/18/01	rtm		first file; based on existing QTSprites and QTWiredSprites sample code
//	   
//
//////////

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

#include "QTWiredSpritesJr.h"


//////////
//
// QTWired_CreateDraggableIconMovie
// Create a QuickTime movie containing a sprite track.
//
//////////

OSErr QTWired_CreateDraggableIconMovie (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 = QTWired_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
	//
	//////////
	
	QTWired_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);
}


//////////
//
// QTWired_AddIconMovieSamplesToMedia
// Build the key frame for the icon sprite movie.
//
//////////

OSErr QTWired_AddIconMovieSamplesToMedia (Media theMedia)
{
	QTAtomContainer			mySample = NULL;
	QTAtomContainer			mySpriteData = NULL;
	RGBColor				myKeyColor;
	Point					myLocation;
	short					isVisible, myIndex;
	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, kNewQTIconID, &myKeyColor, 1, &myPoint, NULL);
	SpriteUtils_AddPICTImageToKeyFrameSample(mySample, kNewQTIconDownID, &myKeyColor, 2, &myPoint, NULL);

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

	// the QT icon sprite
	myLocation.h 	= kIconDimension + (kIconDimension / 2);
	myLocation.v	= kIconDimension + (kIconDimension / 2);
	isVisible		= true;
	myIndex			= kNewQTIconImageIndex;
	
	SpriteUtils_SetSpriteData(mySpriteData, &myLocation, &isVisible, NULL, &myIndex, NULL, NULL, NULL);
	SpriteUtils_AddSpriteToSample(mySample, mySpriteData, kQTIconSpriteAtomID);
	QTWired_MakeSpriteDraggable(mySample, kQTIconSpriteAtomID);
	QTWired_AddCursorChangeToSprite(mySample, kQTIconSpriteAtomID);
	SpriteUtils_AddSpriteSampleToMedia(theMedia, mySample, kSpriteMediaFrameDurationIcon, true, NULL);	

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

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


//////////
//
// QTWired_AddControllerButtonSamplesToMedia
// Build the key frame for the controller sprite track.
//
//////////

void QTWired_AddControllerButtonSamplesToMedia (Media theMedia, long theTrackWidth, long theTrackHeight, TimeValue theDuration)
{
	QTAtomContainer			mySample = NULL;
	QTAtomContainer			myActions = NULL;
	QTAtomContainer			myPauseButton, myPlayButton, myPrevButton, myNextButton, myStartButton, myEndButton;
	RGBColor				myKeyColor;
	Point					myLocation;
	short					isVisible, myIndex, myLayer;
	short					myCount;
	long					mySixth;
#if !USE_WIRED_UTILITIES
	QTAtom					myEventAtom = 0;
	QTAtom					myActionAtom = 0;
	long					myAction;
#endif
	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 < 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 Pause button sprite
	myErr = QTNewAtomContainer(&myPauseButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kPauseSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= theTrackHeight - (kButtonHeight + 5);
	isVisible		= true;
	myIndex			= kPauseUpIndex;
	myLayer			= 1;
	
	SpriteUtils_SetSpriteData(myPauseButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myPauseButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kPauseDownIndex, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myPauseButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kPauseUpIndex, NULL);
	WiredUtils_AddMovieSetRateAction(myPauseButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton, 0);
	SpriteUtils_AddSpriteToSample(mySample, myPauseButton, kPauseSpriteAtomID);	

	// the Play button sprite
	myErr = QTNewAtomContainer(&myPlayButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kPlaySpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= theTrackHeight - (kButtonHeight + 5);
	isVisible		= true;
	myIndex			= kPlayUpIndex;
	myLayer			= 1;
	
	SpriteUtils_SetSpriteData(myPlayButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myPlayButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kPlayDownIndex, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myPlayButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kPlayUpIndex, NULL);
	WiredUtils_AddMovieSetRateAction(myPlayButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton, fixed1);
	SpriteUtils_AddSpriteToSample(mySample, myPlayButton, kPlaySpriteAtomID);	

	// the Go-To-Start button sprite
	myErr = QTNewAtomContainer(&myStartButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kToBeginSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= theTrackHeight - (kButtonHeight + 5);
	isVisible		= true;
	myIndex			= kToBeginUpIndex;
	myLayer			= 1;
	
	SpriteUtils_SetSpriteData(myStartButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	
#if USE_WIRED_UTILITIES
	// add actions to the Start button, using the wired sprite utilities
	WiredUtils_AddSpriteSetImageIndexAction(myStartButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kToBeginDownIndex, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myStartButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kToBeginUpIndex, NULL);
	WiredUtils_AddMovieGoToBeginningAction(myStartButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
#else
	// add actions to the Start button, without using the wired sprite utilities;
	// significantly longer, eh?

	//////////
	//
	// add a mouse click event handler
	//
	//////////

	// add a kQTEventMouseClick event atom to the Start button atom
	myErr = QTInsertChild(myStartButton, kParentAtomIsContainer, kQTEventType, kQTEventMouseClick, 1, 0, NULL, &myEventAtom);
	if (myErr != noErr)
		goto bail;
		
	// add an action atom to the event handler
	myErr = QTInsertChild(myStartButton, myEventAtom, kAction, 0, 0, 0, NULL, &myActionAtom);
	if (myErr != noErr)
		goto bail;
	
	myAction = EndianU32_NtoB(kActionSpriteSetImageIndex);
	myErr = QTInsertChild(myStartButton, myActionAtom, kWhichAction, 1, 1, sizeof(myAction), &myAction, NULL);
	if (myErr != noErr)
		goto bail;

	// add a parameter to the set image index action: image index
	myIndex = EndianU32_NtoB(kToBeginDownIndex);
	myErr = QTInsertChild(myStartButton, myActionAtom, kActionParameter, 0, (short)kFirstParam, sizeof(myIndex), &myIndex, NULL);
	if (myErr != noErr)
		goto bail;

	//////////
	//
	// add a mouse click end event handler
	//
	//////////

	// add a kQTEventMouseClickEnd event atom to the Start button atom
	myErr = QTInsertChild(myStartButton, kParentAtomIsContainer, kQTEventType, kQTEventMouseClickEnd, 1, 0, NULL, &myEventAtom);
	if (myErr != noErr)
		goto bail;
		
	// add an action atom to the event handler
	myErr = QTInsertChild(myStartButton, myEventAtom, kAction, 0, 0, 0, NULL, &myActionAtom);
	if (myErr != noErr)
		goto bail;
	
	myAction = EndianU32_NtoB(kActionSpriteSetImageIndex);
	myErr = QTInsertChild(myStartButton, myActionAtom, kWhichAction, 1, 1, sizeof(myAction), &myAction, NULL);
	if (myErr != noErr)
		goto bail;

	// add a parameter to the set image index action: image index
	myIndex = EndianU32_NtoB(kToBeginUpIndex);
	myErr = QTInsertChild(myStartButton, myActionAtom, kActionParameter, 0, (short)kFirstParam, sizeof(myIndex), &myIndex, NULL);
	if (myErr != noErr)
		goto bail;

	//////////
	//
	// add a mouse button click event handler
	//
	//////////

	// add a kQTEventMouseClickEndTriggerButton event atom to the Start button atom
	myErr = QTInsertChild(myStartButton, kParentAtomIsContainer, kQTEventType, kQTEventMouseClickEndTriggerButton, 1, 0, NULL, &myEventAtom);
	if (myErr != noErr)
		goto bail;
		
	// add an action atom to the event handler
	myErr = QTInsertChild(myStartButton, myEventAtom, kAction, 0, 0, 0, NULL, &myActionAtom);
	if (myErr != noErr)
		goto bail;
	
	myAction = EndianU32_NtoB(kActionMovieGoToBeginning);
	myErr = QTInsertChild(myStartButton, myActionAtom, kWhichAction, 1, 1, sizeof(myAction), &myAction, NULL);
	if (myErr != noErr)
		goto bail;
#endif

	SpriteUtils_AddSpriteToSample(mySample, myStartButton, kToBeginSpriteAtomID);	

	// the Go-To-End button sprite
	myErr = QTNewAtomContainer(&myEndButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kToEndSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= theTrackHeight - (kButtonHeight + 5);
	isVisible		= true;
	myIndex			= kToEndUpIndex;
	myLayer			= 1;
	
	SpriteUtils_SetSpriteData(myEndButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myEndButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kToEndDownIndex, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myEndButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kToEndUpIndex, NULL);
	WiredUtils_AddMovieGoToEndAction(myEndButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
	SpriteUtils_AddSpriteToSample(mySample, myEndButton, kToEndSpriteAtomID);	

	// the Step Back button sprite
	myErr = QTNewAtomContainer(&myPrevButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kBackStepSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= theTrackHeight - (kButtonHeight + 5);
	isVisible		= true;
	myIndex			= kBackStepUpIndex;
	myLayer			= 1;
	
	SpriteUtils_SetSpriteData(myPrevButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myPrevButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kBackStepDownIndex, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myPrevButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kBackStepUpIndex, NULL);
	WiredUtils_AddMovieStepBackwardAction(myPrevButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
	SpriteUtils_AddSpriteToSample(mySample, myPrevButton, kBackStepSpriteAtomID);	

	// the Step Forward button sprite
	myErr = QTNewAtomContainer(&myNextButton);
	if (myErr != noErr)
		goto bail;

	myLocation.h 	= (kAheadStepSpritePosition * mySixth) + ((mySixth - kButtonWidth) / 2);
	myLocation.v	= theTrackHeight - (kButtonHeight + 5);
	isVisible		= true;
	myIndex			= kAheadStepUpIndex;
	myLayer			= 1;
	
	SpriteUtils_SetSpriteData(myNextButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myNextButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kAheadStepDownIndex, NULL);
	WiredUtils_AddSpriteSetImageIndexAction(myNextButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kAheadStepUpIndex, NULL);
	WiredUtils_AddMovieStepForwardAction(myNextButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
	SpriteUtils_AddSpriteToSample(mySample, myNextButton, kAheadStepSpriteAtomID);	

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

bail:	
	if (mySample != NULL)
		QTDisposeAtomContainer(mySample);
	if (myPauseButton != NULL)
		QTDisposeAtomContainer(myPauseButton);	
	if (myPlayButton != NULL)
		QTDisposeAtomContainer(myPlayButton);	
	if (myPrevButton != NULL)
		QTDisposeAtomContainer(myPrevButton);	
	if (myNextButton != NULL)
		QTDisposeAtomContainer(myNextButton);	
	if (myStartButton != NULL)
		QTDisposeAtomContainer(myStartButton);	
	if (myEndButton != NULL)
		QTDisposeAtomContainer(myEndButton);	
}


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

void QTWired_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);
	}
}
	

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

OSErr QTWired_AddSpriteControllerTrack (Movie theMovie)
{
	Track					myTrack = NULL;
	Media					myMedia = NULL;
	MatrixRecord			myMatrix;
	RGBColor				myKeyColor;
	Fixed					myWidth, myHeight;
	Rect					myRect;
	TimeValue				myDuration = 0L;
	TimeValue				myTimeScale = 0L;
	OSType					myType = FOUR_CHAR_CODE('none');
	MatrixRecord			myInverseMatrix;
	OSErr					myErr = noErr;

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

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

	GetMovieBox(theMovie, &myRect);
	myWidth = Long2Fix(myRect.right - myRect.left);
	myHeight = Long2Fix(myRect.bottom - myRect.top);
	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, &myInverseMatrix))
		SetTrackMatrix(myTrack, &myInverseMatrix);

	myErr = BeginMediaEdits(myMedia);
	if (myErr != noErr)
		goto bail;
	
	//////////
	//
	// add sprite images and sprites to the sprite track; add actions to the sprites
	//
	//////////
	
	QTWired_AddControllerButtonSamplesToMedia(myMedia, myRect.right - myRect.left, myRect.bottom - myRect.top, 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
	//
	//////////
	
	QTWired_SetTrackProperties(myMedia, kNoQTIdleEvents);
	
	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, QTWired_GetLowestLayerInMovie(theMovie) - 1);
	
	// select the "no-interface" movie controller
	myType = EndianU32_NtoB(myType);
	SetUserDataItem(GetMovieUserData(theMovie), &myType, sizeof(myType), kUserDataMovieControllerType, 1);
	
bail:
	return(myErr);
}


//////////
//
// QTWired_MakeSpriteDraggable
// Add atoms to the specified sprite to make it draggable.
//
// To make a sprite draggable, we need to install handlers for three kinds of events: mouse click, mouse click end,
// and idle. On mouse click (within the sprite), we set a track variable to 1; on mouse click end, we set that variable
// to 0. Thus, the value of that variable can be used to tell us if the mouse is down within the sprite. On idle, we
// look to see if the value of the variable is 1; if it is, we set the position of the sprite to the current mouse position.
//
//////////

OSErr QTWired_MakeSpriteDraggable (QTAtomContainer theContainer, QTAtomID theID)
{
	QTAtom								mySpriteAtom = 0;
	QTAtom								myEventAtom = 0;
	QTAtom								myActionAtom = 0;
	QTAtom								myParamAtom = 0;
	QTAtom								myConditionalAtom, myExpressionAtom, myOperatorAtom, myActionListAtom, myParameterAtom;
	short								myOperandIndex;
	QTAtomID							myVariableID;
	float								myConstantValue;
	Boolean								myIsAbsolute;
#if !USE_WIRED_UTILITIES
	QTAtom								myOperandAtom, myOperandTypeAtom;
	long								myAction;
	float								myVariableValue;
#endif
	OSErr								myErr = noErr;
	
	// find the sprite atom with the specified ID in the specified container
	mySpriteAtom = QTFindChildByID(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, NULL);
	if (mySpriteAtom == 0) {
		myErr = paramErr;
		goto bail;
	}
	
	//////////
	//
	// add a mouse click event handler
	//
	//////////
	
#if USE_WIRED_UTILITIES
	myErr = WiredUtils_AddSpriteTrackSetVariableAction(theContainer, mySpriteAtom, kQTEventMouseClick, kMouseStateVariableID, 1, 0, NULL, 0);
	if (myErr != noErr)
		goto bail;
#else
	// find the event atom of type kQTEventMouseClick in the sprite atom
	myEventAtom = QTFindChildByID(theContainer, mySpriteAtom, kQTEventType, kQTEventMouseClick, NULL);
	if (myEventAtom == 0) {
		// if there is none, insert a new event atom of type kQTEventMouseClick into the sprite atom
		myErr = QTInsertChild(theContainer, mySpriteAtom, kQTEventType, kQTEventMouseClick, 1, 0, NULL, &myEventAtom);
		if (myErr != noErr)
			goto bail;
	}
	
	// add an action atom to the mouse click event handler
	myErr = QTInsertChild(theContainer, myEventAtom, kAction, 0, 0, 0, NULL, &myActionAtom);
	if (myErr != noErr)
		goto bail;
	
	myAction = EndianU32_NtoB(kActionSpriteTrackSetVariable);
	myErr = QTInsertChild(theContainer, myActionAtom, kWhichAction, 1, 1, sizeof(myAction), &myAction, NULL);
	if (myErr != noErr)
		goto bail;
			
	// add parameters to the set variable action: variable ID (QTAtomID) and value (float)
	myVariableID = EndianU32_NtoB(kMouseStateVariableID);
	myErr = QTInsertChild(theContainer, myActionAtom, kActionParameter, 0, (short)kFirstParam, sizeof(myVariableID), &myVariableID, NULL);
	if (myErr != noErr)
		goto bail;
	
	myVariableValue = (float)1;
	EndianUtils_Float_NtoB(&myVariableValue);
	myErr = QTInsertChild(theContainer, myActionAtom, kActionParameter, 0, (short)kSecondParam, sizeof(myVariableValue), &myVariableValue, NULL);
	if (myErr != noErr)
		goto bail;
#endif
	
	//////////
	//
	// add a mouse click end event handler
	//
	//////////
	
#if USE_WIRED_UTILITIES
	myErr = WiredUtils_AddSpriteTrackSetVariableAction(theContainer, mySpriteAtom, kQTEventMouseClickEnd, kMouseStateVariableID, 0, 0, NULL, 0);
	if (myErr != noErr)
		goto bail;
#else
	// find the event atom of type kQTEventMouseClick in the sprite atom
	myEventAtom = QTFindChildByID(theContainer, mySpriteAtom, kQTEventType, kQTEventMouseClickEnd, NULL);
	if (myEventAtom == 0) {
		// if there is none, insert a new event atom of type kQTEventMouseClick into the sprite atom
		myErr = QTInsertChild(theContainer, mySpriteAtom, kQTEventType, kQTEventMouseClickEnd, 1, 0, NULL, &myEventAtom);
		if (myErr != noErr)
			goto bail;
	}
	
	// add an action atom to the mouse click event handler
	myErr = QTInsertChild(theContainer, myEventAtom, kAction, 0, 0, 0, NULL, &myActionAtom);
	if (myErr != noErr)
		goto bail;
	
	myAction = EndianU32_NtoB(kActionSpriteTrackSetVariable);
	myErr = QTInsertChild(theContainer, myActionAtom, kWhichAction, 1, 1, sizeof(myAction), &myAction, NULL);
	if (myErr != noErr)
		goto bail;
	
	// add parameters to the set variable action: variable ID (QTAtomID) and value (float)
	myVariableID = EndianU32_NtoB(kMouseStateVariableID);
	myErr = QTInsertChild(theContainer, myActionAtom, kActionParameter, 0, (short)kFirstParam, sizeof(myVariableID), &myVariableID, NULL);
	if (myErr != noErr)
		goto bail;
	
	myVariableValue = (float)0;
	EndianUtils_Float_NtoB(&myVariableValue);
	myErr = QTInsertChild(theContainer, myActionAtom, kActionParameter, 0, (short)kSecondParam, sizeof(myVariableValue), &myVariableValue, NULL);
	if (myErr != noErr)
		goto bail;
#endif
	
	//////////
	//
	// add an idle event handler
	//
	//////////
	
#if USE_WIRED_UTILITIES
	myErr = WiredUtils_AddQTEventAndActionAtoms(theContainer, mySpriteAtom, 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;

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

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

	myErr = WiredUtils_AddActionAtom(theContainer, myActionListAtom, kActionSpriteTranslate, &myActionAtom);
	if (myErr != noErr)
		goto bail;
		
	// add parameters to the translate action: Fixed x, Fixed y, Boolean isAbsolute

	// first parameter: get current mouse position x
	myErr = WiredUtils_AddActionParameterAtom(theContainer, myActionAtom, kFirstParam, 0, NULL, &myParameterAtom);
	if (myErr != noErr)
		goto bail;

	myErr = WiredUtils_AddExpressionContainerAtomType(theContainer, myParameterAtom, &myExpressionAtom);
	if (myErr != noErr)
		goto bail;

	myErr = WiredUtils_AddOperandAtom(theContainer, myExpressionAtom, kOperandMouseLocalHLoc, 1, NULL, 0);
	if (myErr != noErr)
		goto bail;
		
	// second parameter: get current mouse position y
	myErr = WiredUtils_AddActionParameterAtom(theContainer, myActionAtom, kSecondParam, 0, NULL, &myParameterAtom);
	if (myErr != noErr)
		goto bail;

	myErr = WiredUtils_AddExpressionContainerAtomType(theContainer, myParameterAtom, &myExpressionAtom);
	if (myErr != noErr)
		goto bail;

	myErr = WiredUtils_AddOperandAtom(theContainer, myExpressionAtom, kOperandMouseLocalVLoc, 1, NULL, 0);
	if (myErr != noErr)
		goto bail;
	
	// third parameter: true
	myIsAbsolute = true;
	myErr = WiredUtils_AddActionParameterAtom(theContainer, myActionAtom, kThirdParam, sizeof(myIsAbsolute), &myIsAbsolute, NULL);
		
#else
	// find the event atom of type kQTEventIdle in the sprite atom
	myEventAtom = QTFindChildByID(theContainer, mySpriteAtom, kQTEventType, kQTEventIdle, NULL);
	if (myEventAtom == 0) {
		// if there is none, insert a new event atom of type kQTEventIdle into the sprite atom
		myErr = QTInsertChild(theContainer, mySpriteAtom, kQTEventType, kQTEventIdle, 1, 0, NULL, &myEventAtom);
		if (myErr != noErr)
			goto bail;
	}
	
	// add an action atom to the mouse click event handler
	myErr = QTInsertChild(theContainer, myEventAtom, kAction, 0, 0, 0, NULL, &myActionAtom);
	if (myErr != noErr)
		goto bail;
	
	myAction = EndianU32_NtoB(kActionCase);
	myErr = QTInsertChild(theContainer, myActionAtom, kWhichAction, 1, 1, sizeof(myAction), &myAction, NULL);
	if (myErr != noErr)
		goto bail;

	// add a parameter atom to the kActionCase action atom; this will serve as a parent to hold conditional atom
	myErr = QTInsertChild(theContainer, myActionAtom, kActionParameter, 1, kFirstParam, 0, NULL, &myParamAtom);
	if (myErr != noErr)
		goto bail;
	
	// the condition atom is the parent of the expression and action list atoms
	myErr = QTInsertChild(theContainer, myParamAtom, kConditionalAtomType, 0, 1, 0, NULL, &myConditionalAtom);
	if (myErr != noErr)
		goto bail;

	myErr = QTInsertChild(theContainer, myConditionalAtom, kExpressionContainerAtomType, 1, 1, 0, NULL, &myExpressionAtom);
	if (myErr != noErr)
		goto bail;

	myErr = QTInsertChild(theContainer, myExpressionAtom, kOperatorAtomType, kOperatorEqualTo, 1, 0, NULL, &myOperatorAtom);
	if (myErr != noErr)
		goto bail;
		
	// add the operands to the operator atom
	myOperandIndex = 1;	
	myConstantValue = 1;
	myErr = QTInsertChild(theContainer, myOperatorAtom, kOperandAtomType, 0, myOperandIndex, 0, NULL, &myOperandAtom);
	if (myErr != noErr)
		goto bail;
	
	myErr = QTInsertChild(theContainer, myOperandAtom, kOperandConstant, 1, 1, 0, NULL, &myOperandTypeAtom);
	if (myErr != noErr)
		goto bail;

	EndianUtils_Float_NtoB(&myConstantValue);
	myErr = QTSetAtomData(theContainer, myOperandTypeAtom, sizeof(myConstantValue), &myConstantValue);
	
	myOperandIndex = 2;
	myVariableID = kMouseStateVariableID;
	myErr = QTInsertChild(theContainer, myOperatorAtom, kOperandAtomType, 0, myOperandIndex, 0, NULL, &myOperandAtom);
	if (myErr != noErr)
		goto bail;
	
	myErr = QTInsertChild(theContainer, myOperandAtom, kOperandSpriteTrackVariable, 1, 1, 0, NULL, &myOperandTypeAtom);
	if (myErr != noErr)
		goto bail;
	
	myVariableID = EndianU32_NtoB(myVariableID);
	myErr = QTInsertChild(theContainer, myOperandTypeAtom, kActionParameter, 1, 1, sizeof(myVariableID), &myVariableID, NULL);
	if (myErr != noErr)
		goto bail;

	// add an action list atom
	myErr = QTInsertChild(theContainer, myConditionalAtom, kActionListAtomType, 1, 1, 0, NULL, &myActionListAtom);
	if (myErr != noErr)
		goto bail;

	// add sprite translate action
	myErr = QTInsertChild(theContainer, myActionListAtom, kAction, 0, 0, 0, NULL, &myActionAtom);
	if (myErr != noErr)
		goto bail;
	
	myAction = EndianU32_NtoB(kActionSpriteTranslate);
	myErr = QTInsertChild(theContainer, myActionAtom, kWhichAction, 1, 1, sizeof(myAction), &myAction, NULL);
	if (myErr != noErr)
		goto bail;

	// add parameters to the translate action: Fixed x, Fixed y, Boolean isAbsolute
	
	// first parameter: get current mouse position x
	myErr = QTInsertChild(theContainer, myActionAtom, kActionParameter, 0, (short)kFirstParam, 0, NULL, &myParameterAtom);
	if (myErr != noErr)
		goto bail;
		
	myErr = QTInsertChild(theContainer, myParameterAtom, kExpressionContainerAtomType, 1, 1, 0, NULL, &myExpressionAtom);
	if (myErr != noErr)
		goto bail;

	myErr = QTInsertChild(theContainer, myExpressionAtom, kOperandAtomType, 0, 1, 0, NULL, &myOperandAtom);
	if (myErr != noErr)
		goto bail;
		
	myErr = QTInsertChild(theContainer, myOperandAtom, kOperandMouseLocalHLoc, 1, 1, 0, NULL, NULL);
	if (myErr != noErr)
		goto bail;

	// second parameter: get current mouse position y
	myErr = QTInsertChild(theContainer, myActionAtom, kActionParameter, 0, (short)kSecondParam, 0, NULL, &myParameterAtom);
	if (myErr != noErr)
		goto bail;
		
	myErr = QTInsertChild(theContainer, myParameterAtom, kExpressionContainerAtomType, 1, 1, 0, NULL, &myExpressionAtom);
	if (myErr != noErr)
		goto bail;

	myErr = QTInsertChild(theContainer, myExpressionAtom, kOperandAtomType, 0, 1, 0, NULL, &myOperandAtom);
	if (myErr != noErr)
		goto bail;
		
	myErr = QTInsertChild(theContainer, myOperandAtom, kOperandMouseLocalVLoc, 1, 1, 0, NULL, NULL);
	if (myErr != noErr)
		goto bail;

	// third parameter: true
	myIsAbsolute = true;
	myErr = QTInsertChild(theContainer, myActionAtom, kActionParameter, 0, (short)kThirdParam, sizeof(myIsAbsolute), &myIsAbsolute, NULL);
#endif
	
bail:
	return(myErr);
}


//////////
//
// QTWired_AddCursorChangeToSprite
// Add cursor-change-on-mouse-over and -down actions to the sprite having the specified ID
// in the specified atom container.
//
// Here we use the new "sprite behaviors atom" introduced in QuickTime 4.0, which
// simplifies adding button-like capabilities to sprites.
//
//////////

OSErr QTWired_AddCursorChangeToSprite (QTAtomContainer theContainer, QTAtomID theID)
{
	QTAtom								mySpriteAtom = 0;
	QTAtom								myBehaviorAtom = 0;
	QTSpriteButtonBehaviorStruct		myBehaviorRec;
	OSErr								myErr = paramErr;
	
	// find the sprite atom with the specified ID in the specified container
	mySpriteAtom = QTFindChildByID(theContainer, kParentAtomIsContainer, kSpriteAtomType, theID, NULL);
	if (mySpriteAtom == 0)
		goto bail;
	
	// insert a new sprite behaviors atom into the sprite atom
	myErr = QTInsertChild(theContainer, mySpriteAtom, kSpriteBehaviorsAtomType, 1, 1, 0, NULL, &myBehaviorAtom);
	if (myErr != noErr)
		goto bail;
	
	// set the sprite cursor behavior; -1 means: no change associated with this state transition
	myBehaviorRec.notOverNotPressedStateID = EndianS32_NtoB(-1);
	myBehaviorRec.overNotPressedStateID = EndianS32_NtoB(kQTCursorOpenHand);
	myBehaviorRec.overPressedStateID = EndianS32_NtoB(kQTCursorClosedHand);
	myBehaviorRec.notOverPressedStateID = EndianS32_NtoB(-1);

	myErr = QTInsertChild(theContainer, myBehaviorAtom, kSpriteCursorBehaviorAtomType, 1, 1, sizeof(QTSpriteButtonBehaviorStruct), &myBehaviorRec, NULL);

bail:
	return(myErr);
}


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

short QTWired_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);
}

