//////////
//
//	File:		MakeEffectSlideShow.c
//
//	Contains:	QuickTime video effect support for QuickTime movies and still images.
//
//	Written by:	Tim Monroe
//				Based (heavily!) on the MakeEffectSlideShow code written by Sam Bushell.
//
//	Copyright:	© 1997-1999 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <8>	 	03/20/00	rtm		made changes to get things running under CarbonLib
//	   <7>	 	03/03/99	rtm		added support for MakeImageDescriptionForEffect (QT 4.0 and later)
//	   <6>	 	03/12/98	rtm		added global flag gDoneWithDialog to fix Windows dialog box problems
//	   <5>	 	02/28/98	rtm		revised event/message handling following QTShowEffect.c
//	   <4>	 	08/01/98	rtm		sync'ed with latest code from Sam: reworked QTEffects_GetFirstVideoTrackInMovie
//									to use GetMovieIndTrackType
//	   <3>	 	11/24/97	rtm		reworked event-loop processing: changed QTEffects_DoMakeEffectSlideShowMovieForFiles into
//									QTEffects_DisplayDialogForSources and QTEffects_RespondToDialogSelection
//	   <2>	 	11/21/97	rtm		further work
//	   <1>	 	11/06/97	rtm		first file; integrated existing code with shell framework;
//									added endian macros where appropriate
//	   
//	This application takes the video tracks from two or more movies, asks 
//	the user to select a 2-source effect, and makes a slide show movie 
//	which uses the effect to switch from one video track to the next. 
//	
//	Short video tracks are scaled up to a minimum length, and we can import
//	all sorts of graphical image formats as movies, so you can use this to 
//	generate a slide show movie from still images -- or even a collection
//	of stills and movies.
//	
//	Try it out: drag a bunch of JPEG files of the same size onto the application icon.
//	Alternatively, launch the application and then choose them from the Test... menu.
//
//////////

// header files
#include "MakeEffectSlideShow.h"

// global variables
QTParameterDialog			gEffectsDialog = 0L;
QTAtomContainer				gEffectSample = 0;				// effects sample
QTAtomContainer				gEffectList = 0;
PicHandle					gPosterA = NULL;
PicHandle					gPosterB = NULL;
Movie						gSrcMovies[kMaxNumSources];
Track						gSrcTracks[kMaxNumSources];
UInt16						gSpecCount = 0;		
FSSpec						gSpecList[kMaxNumSources];
Boolean						gDoneWithDialog = false;		// are we done using the effects parameters dialog box?


//////////
//
// QTEffects_GetFirstVideoTrackInMovie
// Return, through the theTrack parameter, the first video track in the specified movie.
//
// Actually, we look for the first track that has the kCharacteristicCanSendVideo characteristic,
// so we can apply effects to MPEG or QD3D tracks as well.
//
// If no such track is found, return invalidTrack as the function result.
//
//////////

OSErr QTEffects_GetFirstVideoTrackInMovie (Movie theMovie, Track *theTrack)
{
	*theTrack = GetMovieIndTrackType(theMovie, 1, kCharacteristicCanSendVideo, movieTrackCharacteristic | movieTrackEnabledOnly);
	
	if (*theTrack == NULL)
		return(invalidTrack);
		
	return(noErr);
}


//////////
//
// QTEffects_GetPosterPictFromFirstVideoTrackInMovieFile
// Get the poster picture for the first video track in the specified movie file.
//
//////////

PicHandle QTEffects_GetPosterPictFromFirstVideoTrackInMovieFile (FSSpec *theSpec)
{
	PicHandle	myPict = NULL;
	Movie		myMovie = NULL;
	Track		myTrack = NULL;
	short		myRefNum = 0;
	OSErr		myErr = noErr;
	
	myErr = OpenMovieFile(theSpec, &myRefNum, fsRdPerm);
	BailError(myErr);

	myErr = NewMovieFromFile(&myMovie, myRefNum, NULL, NULL, 0, NULL);
	BailError(myErr);
	
	SetMoviePlayHints(myMovie, hintsHighQuality, hintsHighQuality);

	myErr = CloseMovieFile(myRefNum);
	BailError(myErr);
	
	myErr = QTEffects_GetFirstVideoTrackInMovie(myMovie, &myTrack);
	BailNil(myTrack);
	
	myPict = GetTrackPict(myTrack, GetMoviePosterTime(myMovie));

bail:
	if (myMovie != NULL)
		DisposeMovie(myMovie);
	
	return(myPict);
}


//////////
//
// QTEffects_CopyPortionOfTrackToTrack
// Extract a portion of the source track and copy it to the destination track.
//
// The parameter theSourcePortions is formed by OR-ing any of the portion constants.
// For example, to copy the start and middle portions, set theSourcePortions to (eStartPortion | eMiddlePortion).
//
// We assume that the track time scale is kTimeScale; we also assume that the track has been scaled
// so that it is at least kMinimumDuration long.
//
//////////

OSErr QTEffects_CopyPortionOfTrackToTrack (Track theSourceTrack, UInt16 theSourcePortions, Track theDestTrack, TimeValue theDestStartTime, TimeValue *theDestDuration)
{
	TimeValue	mySourceTrackDuration;
	TimeValue	mySourceSegmentStart, mySourceSegmentEnd, mySourceSegmentDuration;
	OSErr		myErr = noErr;
	
	mySourceTrackDuration = GetTrackDuration(theSourceTrack);
	
	if (theSourcePortions & eStartPortion)
		mySourceSegmentStart = 0;
	else if (theSourcePortions & eMiddlePortion)
		mySourceSegmentStart = kEffectDuration;
	else
		mySourceSegmentStart = mySourceTrackDuration - kEffectDuration;
	
	if (theSourcePortions & eFinishPortion)
		mySourceSegmentEnd = mySourceTrackDuration;
	else if (theSourcePortions & eMiddlePortion)
		mySourceSegmentEnd = mySourceTrackDuration - kEffectDuration;
	else
		mySourceSegmentEnd = kEffectDuration;
	
	mySourceSegmentDuration = mySourceSegmentEnd - mySourceSegmentStart;
	
	myErr = InsertTrackSegment( theSourceTrack,
								theDestTrack,
								mySourceSegmentStart,
								mySourceSegmentDuration,
								theDestStartTime);
	
	*theDestDuration = mySourceSegmentDuration;
	
	return(myErr);
}


//////////
//
// QTEffects_DisplayDialogForSources
// Display the standard effects parameters dialog box for the movies passed in.
//
//////////

OSErr QTEffects_DisplayDialogForSources (FSSpec *theSpecList, UInt16 theSpecCount)
{
	OSErr					myErr = noErr;
	
	// make sure that there are enough sources: you can't make an omelette without enough eggs
	if (theSpecCount < 2) {
		myErr = paramErr;
		goto bail;
	}

	// assign source count to a global, so QTEffects_RespondToDialogSelection has access to it
	gSpecCount = theSpecCount;		
	
	// get a poster frame for the first two movies
	if (theSpecCount >= 1)
		gPosterA = QTEffects_GetPosterPictFromFirstVideoTrackInMovieFile(&theSpecList[0]);
	
	if (theSpecCount >= 2)
		gPosterB = QTEffects_GetPosterPictFromFirstVideoTrackInMovieFile(&theSpecList[1]);
	
	// ask the user to select a two-source effect

	myErr = QTNewAtomContainer(&gEffectSample);
	BailError(myErr);
	
	myErr = QTGetEffectsList(&gEffectList, 2, 2, 0);		// min == max == 2
	BailError(myErr);
	
	myErr = QTCreateStandardParameterDialog(gEffectList, gEffectSample, 0, &gEffectsDialog);
	BailError(myErr);
	
	// insert poster frames into dialog
	if (gPosterA != NULL) {
		QTParamPreviewRecord			pr;

		pr.sourcePicture = gPosterA;
		pr.sourceID = 1;
		QTStandardParameterDialogDoAction(gEffectsDialog, pdActionSetPreviewPicture, &pr);
	}

	if (gPosterB != NULL) {
		QTParamPreviewRecord			pr;

		pr.sourcePicture = gPosterB;
		pr.sourceID = 2;
		QTStandardParameterDialogDoAction(gEffectsDialog, pdActionSetPreviewPicture, &pr);
	}
	
	// now, the frontmost window is the standard effects parameter dialog box;
	// on the Mac, we call QTEffects_HandleEffectsDialogEvents in our main event loop
	// to find and process events targeted at the effects parameter dialog box; on Windows,
	// we need to use a different strategy: we install a modeless dialog callback procedure
	// that is called internally by QTML

#if TARGET_OS_WIN32
	gDoneWithDialog = false;
	
	// force the dialog box to be drawn
	{
		EventRecord			myEvent = {0};
		
		QTEffects_EffectsDialogCallback(&myEvent, FrontWindow(), 0);
	}
	
	SetModelessDialogCallbackProc(FrontWindow(), (QTModelessCallbackUPP)QTEffects_EffectsDialogCallback);
	QTMLSetWindowWndProc(FrontWindow(), QTEffects_CustomDialogWndProc);
#endif
	
bail:
	return(myErr);
}


//////////
//
// QTEffects_RespondToDialogSelection
// If theErr is codecParameterDialogConfirm, make an effects movie.
// If theErr is userCanceledErr, do any necessary clean up.
//
// Construct a movie that links a sequence of image or movie files together with real-time effects.
// That is, show the first image or movie, transition (fade or whatever) to the second, show the second,
// transition to the third, and so forth.
//
// We use NewMovieFromFile, which will use movie importers or graphics importers as appropriate,
// so each file we open may end up as either.
//
// If a movie has only one sample in its first video track, we assume it's a still image,
// and use several copies of that sample with different lengths. This is flawed, since the sample
// could be a tweened 3DMF movie.
//
//////////

void QTEffects_RespondToDialogSelection (OSErr theErr)
{
	Boolean					myDialogWasCancelled = false;
	short					myResID = movieInDataForkResID;
	UInt16					myMovieIter;
	short					mySrcMovieRefNum = 0;
	Movie					myPrevSrcMovie = NULL;
	Track					myPrevSrcTrack = NULL;
	Movie					myNextSrcMovie = NULL;
	Track					myNextSrcTrack = NULL;
	short					myDestMovieRefNum = 0;
	FSSpec					myFile;
	Boolean					myIsSelected = false;
	Boolean					myIsReplacing = false;	
	StringPtr 				myPrompt = QTUtils_ConvertCToPascalString(kEffectsSaveMoviePrompt);
	StringPtr 				myFileName = QTUtils_ConvertCToPascalString(kEffectsSaveMovieFileName);
	Movie					myDestMovie = NULL;
	Fixed					myDestMovieWidth, myDestMovieHeight;
	ImageDescriptionHandle	myDesc = NULL;
	Track					videoTrackFX, videoTrackA, videoTrackB;
	Media					videoMediaFX, videoMediaA, videoMediaB;
	TimeValue				myCurrentDuration = 0;
	TimeValue				myReturnedDuration;
	Boolean					isFirstTransition = true;
	TimeValue				myMediaTransitionDuration;
	TimeValue				myMediaFXStartTime, myMediaFXDuration;
	OSType					myEffectCode;
	long					myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
	long					myLong;
	OSErr					myErr = noErr;

	// standard parameter box has been dismissed, so remember that fact
	gEffectsDialog = 0L;
	
	myDialogWasCancelled = (theErr == userCanceledErr);
	
	// we're finished with the effect list and movie posters	
	QTDisposeAtomContainer(gEffectList);
	
	if (gPosterA != NULL)
		KillPicture(gPosterA);
		
	if (gPosterB != NULL)
		KillPicture(gPosterB);
	
	// when the sign says stop, then stop
	if (myDialogWasCancelled)
		goto bail;

	// add atoms naming the sources to gEffectSample
	myLong = EndianU32_NtoB(kSourceOneName);
	QTInsertChild(gEffectSample, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myLong), &myLong, NULL);

	myLong = EndianU32_NtoB(kSourceTwoName);
	QTInsertChild(gEffectSample, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myLong), &myLong, NULL);
	
	// extract the 'what' atom to find out what kind of effect it is
	{
		QTAtom			myEffectAtom;
		QTAtomID		myEffectAtomID;
		long			myEffectCodeSize;
		Ptr				myEffectCodePtr;

		myEffectAtom = QTFindChildByIndex(gEffectSample, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, &myEffectAtomID);
		
		myErr = QTLockContainer(gEffectSample);
		BailError(myErr);

		myErr = QTGetAtomDataPtr(gEffectSample, myEffectAtom, &myEffectCodeSize, &myEffectCodePtr);
		BailError(myErr);

		if (myEffectCodeSize != sizeof(OSType)) {
			myErr = paramErr;
			goto bail;
		}
		
		myEffectCode = *(OSType *)myEffectCodePtr;		// "tsk"
		myEffectCode = EndianU32_BtoN(myEffectCode);	// because the data is read from an atom container
		
		myErr = QTUnlockContainer(gEffectSample);
		BailError(myErr);
	}

	// ask the user for the name of the new movie file
	QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing);
	if (!myIsSelected)
		goto bail;				// deal with user cancelling

	// create a movie file for the destination movie
	myErr = CreateMovieFile(&myFile, FOUR_CHAR_CODE('TVOD'), 0, myFlags, &myDestMovieRefNum, &myDestMovie);
	BailError(myErr);
	
	// open the first file as a movie; call the first movie myPrevSrcMovie
	myErr = OpenMovieFile(&gSpecList[0], &mySrcMovieRefNum, fsRdPerm);
	BailError(myErr);
	
	myErr = NewMovieFromFile(&myPrevSrcMovie, mySrcMovieRefNum, NULL, NULL, 0, NULL);
	BailError(myErr);
	
	myErr = CloseMovieFile(mySrcMovieRefNum);
	BailError(myErr);
	
	// if the movie is shorter than kMinimumDuration, scale it to that length
	SetMovieTimeScale(myPrevSrcMovie, kTimeScale);
	myErr = QTEffects_GetFirstVideoTrackInMovie(myPrevSrcMovie, &myPrevSrcTrack);
	BailNil(myPrevSrcTrack);
	
	if (GetTrackDuration(myPrevSrcTrack) < kMinimumDuration) {
		myErr = ScaleTrackSegment(myPrevSrcTrack, 0, GetTrackDuration(myPrevSrcTrack), kMinimumDuration);
		BailError(myErr);
	}
	
	// find out how big the first movie is; we'll use it as the size of all our tracks
	GetTrackDimensions(myPrevSrcTrack, &myDestMovieWidth, &myDestMovieHeight);
	
#if USES_MAKE_IMAGE_DESC_FOR_EFFECT
	// create a new sample description for the effect,
	// which is just an image description specifying the effect and its dimensions
	myErr = MakeImageDescriptionForEffect(myEffectCode, &myDesc);
	if (myErr != noErr)
		BailError(myErr);
#else
	// create a new sample description for the effect,
	// which is just an image description specifying the effect and its dimensions
	myDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
	BailNil(myDesc);
	
	(**myDesc).idSize = sizeof(ImageDescription);
	(**myDesc).cType = myEffectCode;
	(**myDesc).hRes = 72L << 16;
	(**myDesc).vRes = 72L << 16;
	(**myDesc).dataSize = 0L;
	(**myDesc).frameCount = 1;
	(**myDesc).depth = 0;
	(**myDesc).clutID = -1;
#endif
	
	// fill in the fields of the sample description
	(**myDesc).vendor = kAppleManufacturer;
	(**myDesc).temporalQuality = codecNormalQuality;
	(**myDesc).spatialQuality = codecNormalQuality;
	(**myDesc).width = FixRound(myDestMovieWidth);
	(**myDesc).height = FixRound(myDestMovieHeight);

	// add three video tracks to the destination movie:
	// 	- videoTrackFX is where the effects and stills live; it's user-visible.
	//	- videoTrackA is where the "source A"s for effects live; it's hidden by the input map
	//	- videoTrackB is where the "source B"s for effects live; it's hidden by the input map
	videoTrackFX = NewMovieTrack(myDestMovie, myDestMovieWidth, myDestMovieHeight, 0);
	BailNil(videoTrackFX);
	videoMediaFX = NewTrackMedia(videoTrackFX, VideoMediaType, kTimeScale, NULL, 0);
	BailNil(videoMediaFX);
	myErr = BeginMediaEdits(videoMediaFX);
	BailError(myErr);
	
	videoTrackA = NewMovieTrack(myDestMovie, myDestMovieWidth, myDestMovieHeight, 0);
	BailNil(videoTrackA);
	videoMediaA = NewTrackMedia(videoTrackA, VideoMediaType, kTimeScale, NULL, 0);
	BailNil(videoMediaA);

	videoTrackB = NewMovieTrack(myDestMovie, myDestMovieWidth, myDestMovieHeight, 0);
	BailNil(videoTrackB);
	videoMediaB = NewTrackMedia(videoTrackB, VideoMediaType, kTimeScale, NULL, 0);
	BailNil(videoMediaB);

	// create the input map
	{
		long				myRefIndex1, myRefIndex2;
		QTAtomContainer		myInputMap;
		QTAtom				myInputAtom;
		OSType				myInputType;

		QTNewAtomContainer(&myInputMap);

		// first input
		if (videoTrackA) {
		
			AddTrackReference(videoTrackFX, videoTrackA, kTrackModifierReference, &myRefIndex1);
			QTInsertChild(myInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex1, 0, 0, NULL, &myInputAtom);
	
			myInputType = EndianU32_NtoB(kTrackModifierTypeImage);
			QTInsertChild(myInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myInputType), &myInputType, NULL);
	
			myLong = EndianU32_NtoB(kSourceOneName);
			QTInsertChild(myInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myLong), &myLong, NULL);
		}

		// second input
		if (videoTrackB) {
		
			AddTrackReference(videoTrackFX, videoTrackB, kTrackModifierReference, &myRefIndex2);
			QTInsertChild(myInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex2, 0, 0, NULL, &myInputAtom);
	
			myInputType = EndianU32_NtoB(kTrackModifierTypeImage);
			QTInsertChild(myInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myInputType), &myInputType, NULL);
	
			myLong = EndianU32_NtoB(kSourceTwoName);
			QTInsertChild(myInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myLong), &myLong, NULL);
		}

		// set that map
		SetMediaInputMap(GetTrackMedia(videoTrackFX), myInputMap);
		
		QTDisposeAtomContainer(myInputMap);
	}

	myCurrentDuration = 0;

#if MAKE_STILL_SECTIONS
	// copy the first sample of the first video track of the first movie to videoTrackFX, with duration kStillDuration.
	myErr = CopyPortionOfTrackToTrack(myPrevSrcTrack, eStartPortion + eMiddlePortion, videoTrackFX, myCurrentDuration, &myReturnedDuration);
	BailError(myErr);
	
	myCurrentDuration += myReturnedDuration;
#endif 

	// now process any remaining files
	myMovieIter = 1;
	while (myMovieIter < gSpecCount) {
		
		// open the next file as a movie; call it nextSourceMovie
		myErr = OpenMovieFile(&gSpecList[myMovieIter], &mySrcMovieRefNum, fsRdPerm);
		BailError(myErr);
		
		myErr = NewMovieFromFile(&myNextSrcMovie, mySrcMovieRefNum, NULL, NULL, 0, NULL);
		BailError(myErr);
		
		// we're done with the movie file, so close it
		myErr = CloseMovieFile(mySrcMovieRefNum);
		BailError(myErr);
		
		// if the movie is shorter than kMinimumDuration, scale it to that length
		SetMovieTimeScale(myNextSrcMovie, kTimeScale);
		myErr = QTEffects_GetFirstVideoTrackInMovie(myNextSrcMovie, &myNextSrcTrack);
		BailNil(myNextSrcTrack);
		
		if (GetTrackDuration(myNextSrcTrack) < kMinimumDuration) {
			myErr = ScaleTrackSegment(myNextSrcTrack, 0, GetTrackDuration(myNextSrcTrack), kMinimumDuration);
			BailError(myErr);
		}

		// create a transition effect from the previous source movie's first video sample to the next source movie's first video sample
		// (the effect should have duration kEffectDuration);
		// this involves adding one sample to each of the three video tracks:
		
		//    sample from previous source movie	 -> videoTrackA
		myErr = QTEffects_CopyPortionOfTrackToTrack(myPrevSrcTrack, eFinishPortion, videoTrackA, myCurrentDuration, &myReturnedDuration);
		BailError(myErr);
		
		//    sample from next source movie    	 -> videoTrackB
		myErr = QTEffects_CopyPortionOfTrackToTrack(myNextSrcTrack, eStartPortion, videoTrackB, myCurrentDuration, &myReturnedDuration);
		BailError(myErr);
		
		//    effect sample                 	  -> videoTrackFX
		if (isFirstTransition) {
			myMediaTransitionDuration = myReturnedDuration;
			myMediaFXStartTime = GetMediaDuration(videoMediaFX);
			myErr = AddMediaSample(videoMediaFX, gEffectSample, 0, GetHandleSize(gEffectSample), myMediaTransitionDuration, (SampleDescriptionHandle)myDesc, 1, 0, NULL);
			BailError(myErr);
			
			myMediaFXDuration = GetMediaDuration(videoMediaFX) - myMediaFXStartTime;
			isFirstTransition = false;
		}
		
		myErr = InsertMediaIntoTrack(videoTrackFX, myCurrentDuration, myMediaFXStartTime, myMediaFXDuration, FixRatio(myReturnedDuration, myMediaTransitionDuration));
		BailError(myErr);
		
		myCurrentDuration += myReturnedDuration;
		
#if MAKE_STILL_SECTIONS
		// copy the first video sample of myNextSrcMovie to videoTrackFX, with duration kStillDuration.
		myErr = QTEffects_CopyPortionOfTrackToTrack(myNextSrcTrack, eMiddlePortion + (myMovieIter + 1 == theSpecCount) ? eFinishPortion : 0, videoTrackFX, myCurrentDuration, &myReturnedDuration);
		BailError(myErr);
		
		myCurrentDuration += myReturnedDuration;
#endif // MAKE_STILL_SECTIONS
		
		// dispose of previous source movie.  
		DisposeMovie(myPrevSrcMovie);
		
		myPrevSrcMovie = myNextSrcMovie;
		myPrevSrcTrack = myNextSrcTrack;
		myNextSrcMovie = NULL;
		myNextSrcTrack = NULL;
		
		myMovieIter++;
	} // while
	
	myErr = EndMediaEdits(videoMediaFX);
	BailError(myErr);

	myErr = AddMovieResource(myDestMovie, myDestMovieRefNum, &myResID, "\pMovie 1");
	BailError(myErr);
	
	CloseMovieFile(myDestMovieRefNum);
	
	if (myPrevSrcMovie != NULL)
		DisposeMovie(myPrevSrcMovie);
		
	DisposeMovie(myDestMovie);
	
bail:
	free(myPrompt);
	free(myFileName);

	QTDisposeAtomContainer(gEffectSample);
	DisposeHandle((Handle)myDesc);

	return;
}


#if TARGET_OS_WIN32
//////////
//
// QTEffects_EffectsDialogCallback
// This function is called by QTML when it processes events for the standard or custom effects parameter dialog box.
// 
//////////

static void QTEffects_EffectsDialogCallback (EventRecord *theEvent, DialogRef theDialog, DialogItemIndex theItemHit)
{
	QTParamDialogEventRecord	myRecord;

	myRecord.theEvent = theEvent;
	myRecord.whichDialog = theDialog;
	myRecord.itemHit = theItemHit;

	// 
	QTStandardParameterDialogDoAction(gEffectsDialog, pdActionModelessCallback, &myRecord);
	
	// see if the event is meant for the effects parameter dialog box
	QTEffects_HandleEffectsDialogEvents(theEvent, theItemHit);
}


//////////
//
// QTEffects_CustomDialogWndProc
// Handle messages for the custom effects parameters dialog box.
// 
//////////

LRESULT CALLBACK QTEffects_CustomDialogWndProc (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam)
{
	EventRecord			myEvent = {0};
	
	if (!gDoneWithDialog && (theMessage == 0x7FFF))
		QTEffects_EffectsDialogCallback(&myEvent, GetNativeWindowPort(theWnd), 0);

	return(DefWindowProc(theWnd, theMessage, wParam, lParam));
}
#endif


//////////
//
// QTEffects_HandleEffectsDialogEvents
// Process events that might be targeted at the standard effects parameter dialog box.
// Return true if the event was completely handled.
// 
//////////

Boolean QTEffects_HandleEffectsDialogEvents (EventRecord *theEvent, DialogItemIndex theItemHit)
{
	Boolean			isHandled = false;
	OSErr			myErr = noErr;
	
	// pass the event to the standard effects parameter dialog box handler
	myErr = QTIsStandardParameterDialogEvent(theEvent, gEffectsDialog);
	
	// the result from QTIsStandardParameterDialogEvent tells us how to respond next
	switch (myErr) {
		
		case codecParameterDialogConfirm:
		case userCanceledErr:
			// the user clicked the OK or Cancel button; dismiss the dialog box and respond accordingly
			gDoneWithDialog = true;
			QTDismissStandardParameterDialog(gEffectsDialog);
			QTEffects_RespondToDialogSelection(myErr);
			gEffectsDialog = 0L;
			isHandled = true;
			break;
			
		case noErr:
			// the event was completely handled by QTIsStandardParameterDialogEvent
			isHandled = true;
			break;
			
		case featureUnsupported:
			// the event was not handled by QTIsStandardParameterDialogEvent;
			// let the event be processed normally
			isHandled = false;
			break;
			
		default:
			// the event was not handled by QTIsStandardParameterDialogEvent;
			// do not let the event be processed normally
			isHandled = true;
			break;
	}

	return(isHandled);
}


//////////
//
// QTEffects_PromptUserForFilesAndMakeEffect
// Let the user select some movies, then apply the effect to them.
//
// If the user cancels the first file-open dialog box, there are zero sources.
// If the user cancels the second file-open dialog box, there is one source.
// 
//////////

void QTEffects_PromptUserForFilesAndMakeEffect (void)
{
	QTFrameFileFilterUPP	myFileFilterUPP = NULL;
	int						mySpecCount = 0;
	OSType 					myTypeList[] = {kQTFileTypeMovie};
	short					myNumTypes = 1;
	OSErr					myErr = noErr;
	
#if TARGET_OS_MAC
	myNumTypes = 0;
#endif

	myFileFilterUPP = QTFrame_GetFileFilterUPP((ProcPtr)QTFrame_FilterFiles);

	// ask for up to kMaxNumSources movie files;
	// accept early cancels; they just mean there are fewer input movies
	mySpecCount = 0;
	while (mySpecCount < kMaxNumSources) {
		FSSpec	myFSSpec;

		myTypeList[0] = MovieFileType;

		myErr = QTFrame_GetOneFileWithPreview(myNumTypes, (QTFrameTypeListPtr)myTypeList, &myFSSpec, myFileFilterUPP);
		if (myErr != noErr)
			break;	// the user doesn't want any more source movies
	
		// save the FSSpec from the reply information
		gSpecList[mySpecCount] = myFSSpec;
		
		mySpecCount++;
	}
	
	QTEffects_DisplayDialogForSources(gSpecList, mySpecCount);
	
	if (myFileFilterUPP != NULL)
		DisposeNavObjectFilterUPP(myFileFilterUPP);
}
