//////////
//
//	File:		MacFramework.c
//
//	Contains:	Code for the QuickTime sample code framework that is specific to the Macintosh. 
//				This code handles windows, menus, events, and other low-level things. Put your
//				application-specific code into the file ComApplication.c. 
//
//	Written by:	Tim Monroe
//				Based on the QTShell code written by Tim Monroe, which in turn was based on the MovieShell
//				code written by Apple DTS (Kent Sandvik). This current version is now very far removed from
//				MovieShell.
//
//	Copyright:	© 1999 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <15>	 	03/11/02	rtm		added support for Carbon events; set USE_CARBON_EVENTS to 1 to use
//	   <14>	 	03/16/01	rtm		added HideWindow to QTFrame_DestroyMovieWindow to prevent custom
//									window definition procs from being called during window tear-down
//	   <13>	 	02/01/01	rtm		fixed *Proc names to *UPP, to conform to Universal Headers 3.4b4
//	   <12>	 	08/11/00	rtm		revised event-handling so that QTFrame_HandleEvent passes event to
//									the movie controller
//	   <11>	 	07/06/00	rtm		made changes to support new parameter to QTFrame_AdjustMenus
//	   <10>	 	03/02/00	rtm		made changes to get things running under CarbonLib
//	   <9>	 	01/14/00	rtm		reworked window-dragging and -updating code to support graphics files
//	   <8>	 	12/23/99	rtm		tweaked mouseDown handling in QTFrame_StandardModalDialogEventFilter
//	   <7>	 	12/16/99	rtm		added gMouseClickTime global variable and code to set it in mouseUp
//									case in QTFrame_HandleEvent
//	   <6>	 	12/12/99	rtm		tweaked inDrag handling in QTFrame_HandleEvent
//	   <5>	 	11/30/99	rtm		option-click in close box now closes all open movie windows (per HIG)
//	   <4>	 	11/29/99	rtm		reworked QTFrame_DestroyMovieWindow to use NavAskSaveChanges
//	   <3>	 	11/16/99	rtm		factored QTFrame_HandleEvent out of QTFrame_MainEventLoop, so we can
//									handle events from other code, if necessary
//	   <2>	 	11/11/99	rtm		made some preliminary changes for Carbon; still some work to be done
//	   <1>	 	11/05/99	rtm		first file; based on earlier sample code
//	   
//////////

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

#include "MacFramework.h"


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

Boolean 			gShuttingDown = false;				// are we shutting down?
Boolean				gAppInForeground;					// is our application in the foreground?	
Boolean				gHasNewDialogCalls;					// are the new Dialog Manager functions available?

Str255 				gWindowTitle = kDefaultWindowTitle;	// default name for created windows
Rect 				gWindowRect = kDefaultWindowRect;	// default rectangle for created windows
GrowZoneUPP			gAppGrowZoneUPP;					// UPP to our grow zone callback
ModalFilterUPP		gModalFilterUPP;					// UPP to our custom dialog event filter
UserItemUPP			gUserItemProcUPP;					// UPP to our custom dialog user item procedure
Handle				gEmergencyMemory;					// handle to our emergency memory reserve

short 				gAppResFile = kInvalidFileRefNum;	// file reference number for this application's resource file
FSSpec				gAppFSSpec;							// file specification for the application itself
Str255				gAppName;							// the name of this application

long				gMouseClickTime;					// for double-click calculation
extern Handle 		gValidFileTypes;					// the list of file types that our application can open

long				gRunningUnderX = false;				// are we running under Mac OS X?

#if USE_CARBON_EVENTS
EventHandlerUPP 	gAppEventHandlerUPP = NULL;
EventHandlerUPP 	gWinEventHandlerUPP = NULL;
EventHandlerUPP 	gWinEventModalHandlerUPP = NULL;
EventLoopTimerUPP	gWinTimerHandlerUPP = NULL;
Boolean				gMenuIsTracking = false;
#endif

#if USE_TASK_MGMT
QTNextTaskNeededSoonerCallbackUPP
				 	gQTTaskSoonerUPP = NULL;
#endif


//////////
//
// main
// The main function for this application.
//
// Set up the application's execution environment; make sure QuickTime (etc.) is installed,
// then start handling events.
//
//////////

int main (void)
{
	OSErr		myErr = noErr;
	
	QTFrame_InitMacEnvironment(10L);
	
	// make sure that QuickTime is available and that we can initialize it
	if (!QTUtils_IsQuickTimeInstalled()) {
		QTFrame_ShowWarning("\pQuickTime is not installed on this computer. Exiting.", 0);
		ExitToShell();
	}

	myErr = EnterMovies();
	if (myErr != noErr) {
		QTFrame_ShowWarning("\pCould not initialize QuickTime. Exiting.", myErr);
		ExitToShell();
	}

	// do any application-specific initialization
	QTApp_Init(kInitAppPhase_BothPhases);

	// get and process events until the user quits
	QTFrame_MainEventLoop();
	
	ExitMovies();
	ExitToShell();
	
	return(0);
}


//////////
//
// QTFrame_InitMacEnvironment
// Initialize the Macintosh runtime environment.
//
//////////

static void QTFrame_InitMacEnvironment (long theNumMoreMasters)
{
#if TARGET_API_MAC_CARBON
#pragma unused(theNumMoreMasters)
#endif

	short			myVRefNum;
	long			myDirID;

#if USE_CARBON_EVENTS
	EventTypeSpec	myEventSpec[] = { 	
										{kEventClassMenu, kEventMenuBeginTracking},
										{kEventClassMenu, kEventMenuEndTracking},
										{kEventClassCommand, kEventCommandProcess},
										{kEventClassCommand, kEventCommandUpdateStatus}
								  	};

	gAppEventHandlerUPP = NewEventHandlerUPP(QTFrame_CarbonEventAppHandler);
	if (gAppEventHandlerUPP != NULL)
		InstallEventHandler(GetApplicationEventTarget(), gAppEventHandlerUPP, GetEventTypeCount(myEventSpec), myEventSpec, NULL, NULL);
	
	gWinEventHandlerUPP = NewEventHandlerUPP(QTFrame_CarbonEventWindowHandler);	
	gWinEventModalHandlerUPP = NewEventHandlerUPP(QTFrame_CarbonEventModalWindowHandler);
	gWinTimerHandlerUPP = NewEventLoopTimerUPP(QTFrame_CarbonEventWindowTimer);

	#if USE_TASK_MGMT
	gQTTaskSoonerUPP = NewQTNextTaskNeededSoonerCallbackUPP(QTFrame_NextTaskNeededSoonerProcedure);
	#endif

#endif // USE_CARBON_EVENTS

#if !TARGET_API_MAC_CARBON	
	long			myIndex;

	// add more space to the stack
	SetApplLimit((Ptr)(GetApplLimit() - kExtraStackSpaceSize));
			
	// expand heap zone to its limit
	MaxApplZone();
	
	// allocate some additional master pointer blocks
	for (myIndex = 0; myIndex < theNumMoreMasters; myIndex++)
		MoreMasters();

	InitGraf(&qd.thePort);
	InitFonts();
	InitWindows();
	InitMenus();
	TEInit();
	InitDialogs(NULL);
#endif
	InitCursor();

	// initialize and install the menu bar
	QTFrame_InitMenuBar();

#if !TARGET_API_MAC_CARBON
	// install a grow zone procedure to handle low memory situations
	gEmergencyMemory = NewHandle(kEmergencyMemorySize);
	if (gEmergencyMemory != NULL) {
		gAppGrowZoneUPP = NewGrowZoneUPP(QTFrame_GrowZoneProcedure);
		SetGrowZone(gAppGrowZoneUPP);
	}
#endif
	
	// initialize foreground/background state
	gAppInForeground = true;

	// see whether the new Dialog Manager functions are available
#if TARGET_API_MAC_CARBON
	gHasNewDialogCalls = true;
#else
	gHasNewDialogCalls = QTUtils_TrapAvailable(_DialogDispatch);
#endif
		
	// create modal dialog filter and user item UPPs
	gModalFilterUPP = NewModalFilterUPP(QTFrame_StandardModalDialogEventFilter);
	gUserItemProcUPP = NewUserItemUPP(QTFrame_StandardUserItemProcedure);

	// get the application's resource file
	gAppResFile = CurResFile();
	
	// get the application's name from the resource file
	GetIndString(gAppName, kAppNameResID, kAppNameResIndex);
	
	// get the application's location and save it in gAppFSSpec
	HGetVol(NULL, &myVRefNum, &myDirID);
	FSMakeFSSpec(myVRefNum, myDirID, gAppName, &gAppFSSpec);
}


//////////
//
// QTFrame_GrowZoneProcedure
// A grow zone procedure. This is straight out of IM: Memory (pp. 1-46ff)
//
//////////

PASCAL_RTN long QTFrame_GrowZoneProcedure (Size theBytesNeeded)
{
#pragma unused(theBytesNeeded)

	long		myA5;
	Size		myBytesFreed;
	
	// get current A5; we might get called at a time that A5 isn't valid
	myA5 = SetCurrentA5();
	
	if ((*gEmergencyMemory != NULL) && (gEmergencyMemory != GZSaveHnd())) {
		EmptyHandle(gEmergencyMemory);
		myBytesFreed = kEmergencyMemorySize;
	} else {
		myBytesFreed = 0;						// no more memory to release	
	}	
		
	myA5 = SetA5(myA5);
	
	return(myBytesFreed);
}


//////////
//
// QTFrame_InitMenuBar
// Set up the menu bar.
//
//////////

static Boolean QTFrame_InitMenuBar (void)
{
	Handle		myMenuBar = NULL;
	long		myResponse;
	OSErr		myErr = noErr;
	MenuRef		myMenu = NULL;

	myMenuBar = GetNewMBar(kMenuBarResID);

#if TARGET_API_MAC_CARBON	
	// to get the theme-savvy menu bar, we need to call RegisterAppearanceClient.
	RegisterAppearanceClient();
#endif

	if (myMenuBar == NULL)
		return(false);
	
	SetMenuBar(myMenuBar);									// install the menus
	DisposeHandle(myMenuBar);
	
	// see whether we are running under MacOS X;
	// if so, remove the Quit menu item and its preceding separator line
	myErr = Gestalt(gestaltSystemVersion, &myResponse);
	if (myErr == noErr)
		if (myResponse >= 0x00000A00) {
			DeleteMenuItem(GetMenuHandle(kFileMenuResID), MENU_ITEM(IDM_EXIT));
			DeleteMenuItem(GetMenuHandle(kFileMenuResID), MENU_ITEM(IDM_EXIT) - 1);	// the separator line
			gRunningUnderX = true;
		}
		
#if !TARGET_API_MAC_CARBON	
	AppendResMenu(GetMenuHandle(kAppleMenuResID), 'DRVR');	// add desk accessory names to Apple menu
#endif

#if USE_CARBON_EVENTS
	myMenu = GetMenuHandle(kAppleMenuResID);
	SetMenuItemCommandID(myMenu, kAboutMenuItem, COMMAND_ID(MENU_IDENTIFIER(kAppleMenuResID,kAboutMenuItem)));
	
	myMenu = GetMenuHandle(kFileMenuResID);
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_FILENEW), COMMAND_ID(IDM_FILENEW));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_FILEOPEN), COMMAND_ID(IDM_FILEOPEN));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_FILECLOSE), COMMAND_ID(IDM_FILECLOSE));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_FILESAVE), COMMAND_ID(IDM_FILESAVE));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_FILESAVEAS), COMMAND_ID(IDM_FILESAVEAS));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_EXIT), COMMAND_ID(IDM_EXIT));

	myMenu = GetMenuHandle(kEditMenuResID);
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_EDITUNDO), COMMAND_ID(IDM_EDITUNDO));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_EDITCUT), COMMAND_ID(IDM_EDITCUT));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_EDITCOPY), COMMAND_ID(IDM_EDITCOPY));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_EDITPASTE), COMMAND_ID(IDM_EDITPASTE));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_EDITCLEAR), COMMAND_ID(IDM_EDITCLEAR));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_EDITSELECTALL), COMMAND_ID(IDM_EDITSELECTALL));
	SetMenuItemCommandID(myMenu, MENU_ITEM(IDM_EDITSELECTNONE), COMMAND_ID(IDM_EDITSELECTNONE));
#endif

	QTFrame_AdjustMenus(NULL, NULL, 0L);
	DrawMenuBar();
	
	return(true);
}


//////////
//
// QTFrame_MainEventLoop
// Retrieve and process events.
//
//////////

static void QTFrame_MainEventLoop (void)
{
#if USE_CARBON_EVENTS
	RunApplicationEventLoop();
#else
	EventRecord				myEvent;
	long					myDuration = kWNEMinimumSleep;
	
	while (!gShuttingDown) {
	#if !TARGET_API_MAC_CARBON
		// make sure we've still got our memory reserve; reallocate it if it's been used
		if ((gEmergencyMemory == NULL) || (*gEmergencyMemory == NULL))
			ReallocateHandle(gEmergencyMemory, kEmergencyMemorySize);
	#endif

	#if USE_TASK_MGMT
		// get the number of ticks until QuickTime's next task
		QTGetTimeUntilNextTask(&myDuration, 60);

		if (myDuration == 0)
			myDuration = kWNEMinimumSleep;
	#endif
	
		// get the next event in the queue
		WaitNextEvent(everyEvent, &myEvent, myDuration, NULL);
		
		// handle the event
		QTFrame_HandleEvent(&myEvent);

	} // while (!gShuttingDown)
#endif // USE_CARBON_EVENTS
}


//////////
//
// QTFrame_HandleEvent
// Process an event.
//
//////////

void QTFrame_HandleEvent (EventRecord *theEvent)
{
	WindowPtr				myWindow = NULL;
	WindowObject 			myWindowObject = NULL;
	short					myWindowPart;
	Rect					myScreenRect;
	Boolean					isEventHandled = false;

	// first, perform any application-specific event handling
	isEventHandled = QTApp_HandleEvent(theEvent);
		
	// then, if this event hasn't been handled, let all movie controllers have access to the event
	if (!isEventHandled)
		isEventHandled = QTFrame_CheckMovieControllers(theEvent);

	// then, if this event has been handled, return; otherwise, handle it here
	if (isEventHandled)
		return;
		
	myWindow = FrontWindow();

	switch (theEvent->what) {
		case mouseUp:
			gMouseClickTime = TickCount();		// for double-click calculation
			break;

		case mouseDown:
		
			myWindowPart = FindWindow(theEvent->where, &myWindow);

			// menu bar and window-related events:			
			switch (myWindowPart) {
				case inSysWindow:
#if !TARGET_API_MAC_CARBON
					// a mouse click in a window belonging to a desk accessory
					SystemClick(theEvent, myWindow);
#endif
					break;
					
				case inMenuBar:
					// a mouse click in the menu bar
					QTFrame_AdjustMenus(FrontWindow(), NULL, (long)theEvent->modifiers);
					QTFrame_HandleMenuCommand(MenuSelect(theEvent->where));
					break;
					
				case inDrag: {
					Rect 				myRect;
					
					myWindowObject = QTFrame_GetWindowObjectFromWindow(myWindow);
					if (myWindowObject != NULL) {
						
						if ((**myWindowObject).fMovie != NULL)
							GetMovieBox((**myWindowObject).fMovie, &myRect);
						
						if ((**myWindowObject).fGraphicsImporter != NULL)
							GraphicsImportGetNaturalBounds((**myWindowObject).fGraphicsImporter, &myRect);
						
						GetRegionBounds(GetGrayRgn(), &myScreenRect);
						DragAlignedWindow(myWindow, theEvent->where, &myScreenRect, &myRect, NULL);

					} else {
						GetRegionBounds(GetGrayRgn(), &myScreenRect);
						DragWindow(myWindow, theEvent->where, &myScreenRect);
					}
					
					break;							
				}
					
				case inContent:
					if (myWindow != FrontWindow()) {
						SelectWindow(myWindow);
						MacSetPort((GrafPtr)GetWindowPort(myWindow));
					} else {
						QTApp_HandleContentClick(myWindow, theEvent);
					}
					
					break;
				
				case inGoAway:
					if (TrackGoAway(myWindow, theEvent->where)) {
						// if the option key is down, close all open movie windows; otherwise, close just the frontmost
						if (theEvent->modifiers & optionKey)
							QTFrame_CloseMovieWindows();
						else
							QTFrame_DestroyMovieWindow(myWindow);
					}
					break;
			} // end switch(myWindowPart)
			break;

		// system-level events:
		case updateEvt:
			myWindow = (WindowPtr)theEvent->message;
			if (myWindow != NULL) {
				// if the window contains an image, draw it using GraphicsImportDraw
				myWindowObject = QTFrame_GetWindowObjectFromWindow(myWindow);
				if (myWindowObject != NULL)
					if ((**myWindowObject).fGraphicsImporter != NULL)
						GraphicsImportDraw((**myWindowObject).fGraphicsImporter);
				
				// now do any application-specific drawing
				QTApp_Draw(myWindow);
			}
			break;
			
		case keyDown:
		case autoKey:
			QTFrame_HandleKeyPress(theEvent);
			break;
		
		case diskEvt: {
#if !TARGET_API_MAC_CARBON
			Point			myPoint = {100, 100};
		
			if (HiWord(theEvent->message) != noErr)
				(void)DIBadMount(myPoint, theEvent->message);
#endif
			break;
		}
		
		case activateEvt:
			myWindow = (WindowReference)theEvent->message;
			
		 	if (QTFrame_IsMovieWindow(myWindow))
				QTFrame_ActivateController(myWindow, ((theEvent->modifiers & activeFlag) != 0 ));
			break;
			
		case osEvt:
			switch ((theEvent->message >> 24) & 0x00ff) {		// get high byte of message
				case suspendResumeMessage:
				
					// set the foreground/background state
					gAppInForeground = (theEvent->message & resumeFlag) != 0;
					
					// activate the front window, if there is one	
					if (myWindow != NULL)
						QTFrame_ActivateController(myWindow, gAppInForeground);
					break;
				
				case mouseMovedMessage:
					break;
			}
			break;
		
		case kHighLevelEvent:
			AEProcessAppleEvent(theEvent);
			break;
			
		case nullEvent:
			// do idle-time processing for all open movie windows
			myWindow = QTFrame_GetFrontMovieWindow();
			while (myWindow != NULL) {
				if (gAppInForeground)
					QTApp_Idle(myWindow);
					
				myWindow = QTFrame_GetNextMovieWindow(myWindow);
			}
			break;
	} // switch (theEvent->what)
}
		

//////////
//
// QTFrame_HandleMenuCommand
// Handle a menu selection.
//
//////////

Boolean QTFrame_HandleMenuCommand (long theMenuResult)
{
	short		myMenuID, myMenuItem;
	Cursor		myArrow;
	Boolean		myIsHandled = false;
	
	MacSetCursor(GetQDGlobalsArrow(&myArrow));

	myMenuID = HiWord(theMenuResult);
	myMenuItem = LoWord(theMenuResult);
	
	switch (myMenuID) {
	
		case kAppleMenuResID:
			switch (myMenuItem) {
				case kAboutMenuItem:		// About box
					QTFrame_ShowAboutBox();
					myIsHandled = true;
					break;
				
				default:	 				// Apple menu handling
#if !TARGET_API_MAC_CARBON
					{
						Str255		myDAName;
						
						GetMenuItemText(GetMenuHandle(kAppleMenuResID), myMenuItem, myDAName);
						(void)OpenDeskAcc(myDAName);
					}
					myIsHandled = true;
#endif
					break;
			}
			break;

		case kFileMenuResID:
			myIsHandled = QTFrame_HandleFileMenuItem(QTFrame_GetFrontMovieWindow(), MENU_IDENTIFIER(myMenuID, myMenuItem));
			break;
	
		case kEditMenuResID:
			myIsHandled = QTFrame_HandleEditMenuItem(QTFrame_GetFrontMovieWindow(), MENU_IDENTIFIER(myMenuID, myMenuItem));
			break;

		default:
			// do any application-specific menu handling
			myIsHandled = QTApp_HandleMenu(MENU_IDENTIFIER(myMenuID, myMenuItem));
			break;
		
	} // switch (myMenuID)
	
	HiliteMenu(0);
	
	return(myIsHandled);
}


//////////
//
// QTFrame_HandleKeyPress
// Handle key presses. This is modelled on Inside Macintosh: Macintosh Toolbox Essentials, p. 3-78.
//
//////////

void QTFrame_HandleKeyPress (EventRecord *theEvent)
{
	char		myKey;
	
	myKey = theEvent->message & charCodeMask;
	
	if (theEvent->modifiers & cmdKey) {
		// if the command key is down, it must be a keyboard shortcut for a menu selection;
		// adjust the menus and find the menu command that the shortcut picks out
		QTFrame_AdjustMenus(FrontWindow(), NULL, (long)theEvent->modifiers);
		QTFrame_HandleMenuCommand(MenuKey(myKey));
	} else {
		// otherwise, we'll assume it's meant for our application
		QTApp_HandleKeyPress(myKey);
	}
}


//////////
//
// QTFrame_QuitFramework
// Do any framework-specific shut-down.
//
//////////

void QTFrame_QuitFramework (void)
{
	// set our global flag to indicate we're shutting down
	gShuttingDown = true;
	
	// do application-specific processing that must occur before movie windows are closed
	QTApp_Stop(kStopAppPhase_BeforeDestroyWindows);
	
	// close all open movie windows; note that the user can cancel the shutting down
	QTFrame_CloseMovieWindows();
		
	// test the quit flag; a call to QTFrame_DestroyMovieWindow may have reset it
	if (!gShuttingDown)
		return;
	
	// do application-specific processing that must occur after movie windows are closed
	QTApp_Stop(kStopAppPhase_AfterDestroyWindows);

	// release our list of valid file types
	if (gValidFileTypes != NULL)
		DisposeHandle(gValidFileTypes);

#if !TARGET_API_MAC_CARBON	
	// release the grow zone memory
	if (gEmergencyMemory != NULL)
		DisposeHandle(gEmergencyMemory);
#endif
	
#if USE_CARBON_EVENTS
	QuitApplicationEventLoop();

	if (gAppEventHandlerUPP != NULL)
		DisposeEventHandlerUPP(gAppEventHandlerUPP);

	if (gWinEventHandlerUPP != NULL)
		DisposeEventHandlerUPP(gWinEventHandlerUPP);
	
	if (gWinEventModalHandlerUPP != NULL)
		DisposeEventHandlerUPP(gWinEventModalHandlerUPP);

	if (gWinTimerHandlerUPP != NULL)
		DisposeEventLoopTimerUPP(gWinTimerHandlerUPP);
#endif

#if USE_TASK_MGMT
	if (gQTTaskSoonerUPP != NULL)
		DisposeQTNextTaskNeededSoonerCallbackUPP(gQTTaskSoonerUPP);
#endif

	// release any routine descriptors
	if (gAppGrowZoneUPP != NULL)
		DisposeGrowZoneUPP(gAppGrowZoneUPP);
		
	if (gModalFilterUPP != NULL)
		DisposeModalFilterUPP(gModalFilterUPP);
		
	if (gUserItemProcUPP != NULL)
		DisposeUserItemUPP(gUserItemProcUPP);	
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Framework utilities.
//
// The framework uses the following functions to create movies and handle dialog boxes. You probably won't
// need to use them directly.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////
//
// QTFrame_CreateMovieWindow
// Create a new window to display the movie in.
//
//////////

WindowReference QTFrame_CreateMovieWindow (void)
{
	WindowReference		myWindow = NULL;
	
	// create a new window to display the movie in
	myWindow = NewCWindow(NULL, &gWindowRect, gWindowTitle, false, noGrowDocProc, (WindowPtr)-1L, true, 0);

	// create a new window object associated with the new window
	QTFrame_CreateWindowObject(myWindow);

#if USE_CARBON_EVENTS
{
	WindowObject		myWindowObject = NULL;
	EventTypeSpec		myEventSpec[] = { 	
											{kEventClassKeyboard, kEventRawKeyDown},
											{kEventClassKeyboard, kEventRawKeyRepeat},
											{kEventClassKeyboard, kEventRawKeyUp},
											{kEventClassWindow, kEventWindowUpdate},
											{kEventClassWindow, kEventWindowDrawContent},
											{kEventClassWindow, kEventWindowActivated},
											{kEventClassWindow, kEventWindowDeactivated},
											{kEventClassWindow, kEventWindowHandleContentClick},
											{kEventClassWindow, kEventWindowClose}
								  		};
								  		
	// install Carbon event handlers for this window
	InstallStandardEventHandler(GetWindowEventTarget(myWindow));
	if (gWinEventHandlerUPP != NULL)
		InstallEventHandler(GetWindowEventTarget(myWindow), gWinEventHandlerUPP, GetEventTypeCount(myEventSpec), myEventSpec, QTFrame_GetWindowObjectFromWindow(myWindow), NULL);

	#if !USE_CARBON_MOVIE_CONTROL
	myWindowObject = QTFrame_GetWindowObjectFromWindow(myWindow);
	if (myWindowObject == NULL)
		return(myWindow);
	
	if (gWinTimerHandlerUPP != NULL)
		InstallEventLoopTimer(GetMainEventLoop(), 0, TicksToEventTime(kWNEMinimumSleep), gWinTimerHandlerUPP, myWindowObject, &(**myWindowObject).fTimerRef);
	#if USE_TASK_MGMT
	if ((gQTTaskSoonerUPP != NULL) && ((**myWindowObject).fTimerRef != NULL))
		QTInstallNextTaskNeededSoonerCallback(gQTTaskSoonerUPP, 1000, 0, (void *)(**myWindowObject).fTimerRef);
	#endif // USE_TASK_MGMT
	#endif // !USE_CARBON_MOVIE_CONTROL
}
#endif

	return(myWindow);
}


//////////
//
// QTFrame_DestroyMovieWindow
// Close the specified movie window.
//
//////////

void QTFrame_DestroyMovieWindow (WindowReference theWindow)
{
	WindowObject		myWindowObject = NULL;
	OSErr				myErr = noErr;
	
	// get the window object associated with the specified window
	myWindowObject = QTFrame_GetWindowObjectFromWindow(theWindow);
	if (myWindowObject == NULL) {
		// if the window passed in isn't a movie window, just dispose of it and return
		if (theWindow != NULL) {
			DisposeWindow(theWindow);
			theWindow = NULL;
		}
		return;
	}
	
	// if the window's data is "dirty", give the user a chance to save it
	if ((**myWindowObject).fIsDirty) {
		Str255						myString;
		NavAskSaveChangesAction		myAction;
		NavAskSaveChangesResult		myResult;
		NavDialogOptions			myDialogOptions;
		NavEventUPP					myEventUPP = NewNavEventUPP(QTFrame_HandleNavEvent);

		// get the title of the window
		GetWTitle(theWindow, myString);
		
		// install the application and document names
		NavGetDefaultDialogOptions(&myDialogOptions);
		BlockMoveData(gAppName, myDialogOptions.clientName, gAppName[0] + 1);
		BlockMoveData(myString, myDialogOptions.savedFileName, myString[0] + 1);
		
		// specify the action
		myAction = gShuttingDown ? kNavSaveChangesQuittingApplication : kNavSaveChangesClosingDocument;
		
		// display the "Save changes" dialog box
		myErr = NavAskSaveChanges(&myDialogOptions, myAction, &myResult, myEventUPP, NULL);
		if (myErr != noErr)
			myResult = kNavAskSaveChangesCancel;
			
		switch (myResult) {
			case kNavAskSaveChangesSave:
				// save the data in the window
				QTFrame_UpdateMovieFile(theWindow);
				break;
				
			case kNavAskSaveChangesCancel:
				// do not close the window, and do not quit the application
				gShuttingDown = false;
				return;
				
			case kNavAskSaveChangesDontSave:
				// discard any unsaved changes (that is, don't do anything)
				break;
		}

		DisposeNavEventUPP(myEventUPP);
	}

	// if we got to this point, it's okay to hide, close, and destroy the window
#if USE_CARBON_EVENTS
	if ((**myWindowObject).fTimerRef != NULL)
		RemoveEventLoopTimer((**myWindowObject).fTimerRef);
#endif

	HideWindow(theWindow);
	QTFrame_CloseWindowObject(myWindowObject);

	DisposeWindow(theWindow);
}


//////////
//
// QTFrame_StandardUserItemProcedure
// A standard user-item procedure to outline the OK button in a modal dialog.
//
//////////

PASCAL_RTN void QTFrame_StandardUserItemProcedure (DialogPtr theDialog, short theItem)
{
#pragma unused(theItem)

	short				myItemKind;			// for GetDialogItem
	Handle				myItemHandle;		// for GetDialogItem
	Rect				myItemRect;			// for GetDialogItem

	if (!gHasNewDialogCalls) {		// no need to do any of this if the new Dialog Manager calls are available
		GetDialogItem(theDialog, kStdOkItemIndex, &myItemKind, &myItemHandle, &myItemRect);		
		MacInsetRect(&myItemRect, -4, -4);
		PenSize(3, 3);
		FrameRoundRect(&myItemRect, 16, 16);
		PenSize(1, 1);
	}
}


//////////
//
// QTFrame_StandardModalDialogEventFilter
// A standard modal dialog event filter. 
//
//////////

PASCAL_RTN Boolean QTFrame_StandardModalDialogEventFilter (DialogPtr theDialog, EventRecord *theEvent, short *theItemHit)
{
	Boolean				myEventHandled = false;
	short				myItemKind;			// for GetDialogItem
	Handle				myItemHandle;		// for GetDialogItem
	Rect				myItemRect;			// for GetDialogItem
	unsigned long		myTicks;			// for Delay
	char				myKey;		
	WindowReference		myWindow = NULL;
	short				myPart;	
		
	switch (theEvent->what) {
		case updateEvt:
			// update the specified window, if it's behind the modal dialog box
			myWindow = (WindowReference)theEvent->message;
			if ((myWindow != NULL) && (myWindow != GetDialogWindow(theDialog))) {
				QTFrame_HandleEvent(theEvent);
				myEventHandled = false;		// so sayeth IM
			}
			break;

		case nullEvent:
			// do idle-time processing for all open windows in our window list
			if (gAppInForeground)
				QTFrame_IdleMovieWindows();

			myEventHandled = false;
			break;
			
		case keyDown:
		case autoKey:
			// if new Dialog Manager calls are NOT available, handle certain key presses
			if (!gHasNewDialogCalls) {
				// first, map Command-period to Escape key...
				myKey = theEvent->message & charCodeMask;
				if (theEvent->modifiers & cmdKey)
					if (myKey == kPeriod)
						myKey = kEscapeKey;
						
				// ...then, handle the standard keyboard equivalents of OK and Cancel buttons
				switch (myKey) {
					case kReturnKey:
					case kEnterKey:
						*theItemHit = kStdOkItemIndex;
						GetDialogItem(theDialog, kStdOkItemIndex, &myItemKind, &myItemHandle, &myItemRect);
						HiliteControl((ControlHandle)myItemHandle, kControlButtonPart);
						Delay(kMyButtonDelay, &myTicks);
						HiliteControl((ControlHandle)myItemHandle, kControlNoPart);
						myEventHandled = true;
						break;
					case kEscapeKey:
						*theItemHit = kStdCancelItemIndex;
						GetDialogItem(theDialog, kStdCancelItemIndex, &myItemKind, &myItemHandle, &myItemRect);
						HiliteControl((ControlHandle)myItemHandle, kControlButtonPart);
						Delay(kMyButtonDelay, &myTicks);
						HiliteControl((ControlHandle)myItemHandle, kControlNoPart);
						myEventHandled = true;
						break;
					default:
						break;
				}
			}
			break;
			
		case mouseDown:
			myPart = FindWindow(theEvent->where, &myWindow);
			if ((myPart == inDrag) && (myWindow == GetDialogWindow(theDialog))) {
				Rect		myScreenRect;
				
				GetRegionBounds(GetGrayRgn(), &myScreenRect);
				DragWindow(myWindow, theEvent->where, &myScreenRect);
				myEventHandled = true;
			}
			break;
			
		default:
			myEventHandled = false;
			break;
	}	
	
	// let the OS's standard filter proc handle the event, if it hasn't already been handled
	if (gHasNewDialogCalls && (myEventHandled == false))
		myEventHandled = StdFilterProc(theDialog, theEvent, theItemHit);
	
	return(myEventHandled);
}


//////////
//
// QTFrame_CheckMovieControllers
// Let all movie controllers have a chance to process the event.
//
// Returns true if the event was handled by some movie controller, false otherwise
//
//////////

static Boolean QTFrame_CheckMovieControllers (EventRecord *theEvent)
{	
	WindowPtr				myWindow = NULL;
	MovieController			myMC = NULL;
	
	myWindow = QTFrame_GetFrontMovieWindow();
	while (myWindow != NULL) {
		myMC = QTFrame_GetMCFromWindow(myWindow);
		if (myMC != NULL)
			if (MCIsPlayerEvent(myMC, theEvent))
				return(true);
		
		myWindow = QTFrame_GetNextMovieWindow(myWindow);
	}
	
	return(false);
}


//////////
//
// QTFrame_ShowAboutBox
// Display and manage the About dialog box.
//
//////////

void QTFrame_ShowAboutBox (void)
{
	DialogPtr			myDialog = NULL;
	WindowRef			myWindow = NULL;
	short 				mySavedResFile;
	GrafPtr				mySavedPort;
	short				myItemKind;
	Handle				myItemHandle;
	Rect				myItemRect;
#if USE_CARBON_EVENTS
	EventTypeSpec		myEventSpec[] = { 	
											{kEventClassWindow, kEventWindowDrawContent},
											{kEventClassControl, kEventControlHit}
								 	 	};
#else
	short 				myItem;
#endif
	
	// get the current resource file and port
	mySavedResFile = CurResFile();
	GetPort(&mySavedPort);
	
	// set the application's resource file;
	// otherwise, we'd get the dialog's resources from the current resource file,
	// which might not be the correct one....
	UseResFile(gAppResFile);
	
	// deactivate any frontmost movie window
	QTFrame_ActivateController(QTFrame_GetFrontMovieWindow(), false);
	
	myDialog = GetNewDialog(kAboutBoxID, NULL, (WindowPtr)-1L);
	if (myDialog == NULL)
		goto bail;
		
	myWindow = GetDialogWindow(myDialog);
	if (myWindow == NULL)
		goto bail;
		
	SetPortDialogPort(myDialog);
		
	if (gHasNewDialogCalls)
		SetDialogDefaultItem(myDialog, kStdOkItemIndex);
	
	// make sure that the OK button is outlined in bold, even if new Dialog Manager calls not available
	GetDialogItem(myDialog, kOKButtonUserItem, &myItemKind, &myItemHandle, &myItemRect);
	SetDialogItem(myDialog, kOKButtonUserItem, myItemKind, (Handle)gUserItemProcUPP, &myItemRect);

#if USE_CARBON_EVENTS
	// install a window handler for the dialog window
	GetDialogItem(myDialog, kStdOkItemIndex, &myItemKind, &myItemHandle, &myItemRect);
	if (myItemHandle != NULL)
		SetWindowDefaultButton(myWindow, (ControlHandle)myItemHandle);

	InstallStandardEventHandler(GetWindowEventTarget(myWindow));
	if (gWinEventModalHandlerUPP != NULL)
		InstallEventHandler(GetWindowEventTarget(myWindow), gWinEventModalHandlerUPP, GetEventTypeCount(myEventSpec), myEventSpec, (void *)myDialog, NULL);

	// display and handle events in the dialog box until the user clicks OK
	RunAppModalLoopForWindow(myWindow);
#else
	// display and handle events in the dialog box until the user clicks OK
	do {
		ModalDialog(gModalFilterUPP, &myItem);
	} while (myItem != kStdOkItemIndex);
#endif

bail:
	// restore the previous resource file and port
	MacSetPort(mySavedPort);
	UseResFile(mySavedResFile);
	
	if (myDialog != NULL)
		DisposeDialog(myDialog);
}


//////////
//
// QTFrame_ShowWarning
// Display a warning box.
//
//////////

void QTFrame_ShowWarning (Str255 theMessage, OSErr theErr)
{
	Str255				myString;
	short 				mySavedResFile;
	
	// get the current resource file and set the application's resource file
	mySavedResFile = CurResFile();
	UseResFile(gAppResFile);

	// insert argument into the message text template, to get the message text
	NumToString(theErr, myString);
	ParamText("\pWarning!", theMessage, theErr ? myString: NULL, NULL);

	// display the dialog box
	Alert(kAlertErrorID, gModalFilterUPP);
	
	// restore the original resource file
	UseResFile(mySavedResFile);
}


#if USE_CARBON_EVENTS
//////////
//
// QTFrame_CarbonEventAppHandler
// Handle Carbon events targeted at the application.
//
//////////

PASCAL_RTN OSStatus QTFrame_CarbonEventAppHandler (EventHandlerCallRef theCallRef, EventRef theEvent, void *theRefCon)
{
#pragma unused(theCallRef, theRefCon)
	UInt32				myClass, myKind;
	HICommand			myCommand;
	OSStatus			myErr = eventNotHandledErr;
	
	myClass = GetEventClass(theEvent);
	myKind = GetEventKind(theEvent);
	
	switch (myClass) {
		case kEventClassCommand:
			switch (myKind) {
				case kEventCommandProcess:
					myErr = GetEventParameter(theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(myCommand), NULL, &myCommand);
					if ((myErr == noErr) && ((myCommand.attributes & kHICommandFromMenu) != 0))
						myErr = QTFrame_HandleMenuCommand(myCommand.commandID) ? noErr : eventNotHandledErr;
					break;
					
				case kEventCommandUpdateStatus:
					myErr = GetEventParameter(theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(myCommand), NULL, &myCommand);
					if ((myErr == noErr) && ((myCommand.attributes & kHICommandFromMenu) != 0))
						QTFrame_AdjustMenus(FrontWindow(), NULL, 0L);
					break;
			}
			break;
			
		case kEventClassMenu:
			switch (myKind) {
				case kEventMenuBeginTracking:
					gMenuIsTracking = true;
					break;

				case kEventMenuEndTracking:
					gMenuIsTracking = false;
					break;
			}
			break;
	}

	return(myErr);
}


//////////
//
// QTFrame_CarbonEventWindowHandler
// Handle Carbon events targeted at a movie window.
//
// On entry, theRefCon is the WindowObject for the window.
//
//////////

PASCAL_RTN OSStatus QTFrame_CarbonEventWindowHandler (EventHandlerCallRef theCallRef, EventRef theEvent, void *theRefCon)
{
#pragma unused(theCallRef)
	UInt32				myClass, myKind;
	UInt32				myModifiers;
	WindowRef			myWindow = NULL;
	EventRecord			myEvent;
	WindowObject		myWindowObject = (WindowObject)theRefCon;
	OSStatus			myErr = eventNotHandledErr;
	
	myClass = GetEventClass(theEvent);
	myKind = GetEventKind(theEvent);
	
	switch (myClass) {
		case kEventClassKeyboard:
			switch (myKind) {
				case kEventRawKeyDown:
				case kEventRawKeyRepeat:
				case kEventRawKeyUp:
					if (ConvertEventRefToEventRecord(theEvent, &myEvent))
						QTFrame_HandleEvent(&myEvent);
					myErr = noErr;
					break;
			}
			break;
			
		case kEventClassWindow:
			switch (myKind) {
				case kEventWindowUpdate:
				case kEventWindowDrawContent:
				case kEventWindowActivated:
				case kEventWindowDeactivated:
					if (ConvertEventRefToEventRecord(theEvent, &myEvent))
						QTFrame_HandleEvent(&myEvent);
					myErr = noErr;
					break;
					
				case kEventWindowHandleContentClick:
					myErr = GetEventParameter(theEvent, kEventParamDirectObject, typeWindowRef, NULL, sizeof(myWindow), NULL, &myWindow);
					if (myErr == noErr) {
						
						GetEventParameter(theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(myModifiers), NULL, &myModifiers);
						GetEventParameter(theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &myEvent.where);
						GetEventParameter(theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &myEvent.when);
						
						myEvent.what = mouseDown;
						myEvent.message = (long)myWindow;
						myEvent.modifiers = myModifiers;
						myEvent.when = EventTimeToTicks(GetCurrentEventTime());
												
						QTFrame_HandleEvent(&myEvent);
						myErr = noErr;
					}
					break;

				case kEventWindowClose:
					if (myWindowObject != NULL) {
						QTFrame_DestroyMovieWindow((**myWindowObject).fWindow);
						myErr = noErr;
					}
					break;
					
				default:
					break;
			}
			break;
	}

	return(myErr);
}


//////////
//
// QTFrame_CarbonEventModalWindowHandler
// Handle Carbon events targeted at a modal dialog window.
//
// On entry, theRefCon is the DialogPtr for the dialog box.
//
//////////

PASCAL_RTN OSStatus QTFrame_CarbonEventModalWindowHandler (EventHandlerCallRef theCallRef, EventRef theEvent, void *theRefCon)
{
#pragma unused(theCallRef)
	UInt32		myClass, myKind;
	DialogPtr	myDialog = (DialogPtr)theRefCon;
	OSStatus	myErr = eventNotHandledErr;
	
	myClass = GetEventClass(theEvent);
	myKind = GetEventKind(theEvent);
		
	switch (myClass) {
		case kEventClassWindow:
			switch (myKind) {
				case kEventWindowDrawContent:
					if (myDialog != NULL)
						DrawDialog(myDialog);
					myErr = noErr;
					break;
			}
			break;

		case kEventClassControl:
			switch (myKind) {
				case kEventControlHit:
					if (myDialog != NULL)
						myErr = QuitAppModalLoopForWindow(GetDialogWindow(myDialog));					
					break;
			}
			break;
	}

	return(myErr);
}


//////////
//
// QTFrame_CarbonEventWindowTimer
// Handle time-triggered actions for a specific window.
//
// The theRefCon paramter is a window object for the window to be tasked.
//
//////////

PASCAL_RTN void QTFrame_CarbonEventWindowTimer (EventLoopTimerRef theTimer, void *theRefCon)
{
	WindowObject	myWindowObject = (WindowObject)theRefCon;
	
#if USE_TASK_MGMT
	long			myDuration = 0L;

	// get the number of milliseconds until QuickTime's next task
	QTGetTimeUntilNextTask(&myDuration, 1000);

	if (myDuration == 0)
		myDuration = kMinAppTaskInMillisecs;

	// set the timer to fire at that time
	if (theTimer != NULL)
		SetEventLoopTimerNextFireTime(theTimer, myDuration * kEventDurationMillisecond);
#endif

	// just pretend a null event has been received....
	if ((myWindowObject != NULL) && ((**myWindowObject).fController != NULL))
		if (!gMenuIsTracking || gRunningUnderX)
			MCIdle((**myWindowObject).fController);
}
#endif // USE_CARBON_EVENTS


#if USE_TASK_MGMT
//////////
//
// QTFrame_NextTaskNeededSoonerProcedure
// Handle task-sooner notifications.
//
// We assume that theRefCon is an event loop timer reference.
//
//////////

PASCAL_RTN void QTFrame_NextTaskNeededSoonerProcedure (TimeValue theDuration, unsigned long theFlags, void *theRefCon)
{
#pragma unused(theFlags)

	EventLoopTimerRef	myTimer = (EventLoopTimerRef)theRefCon;
	
	if (myTimer != NULL)
		SetEventLoopTimerNextFireTime(myTimer, theDuration * kEventDurationMillisecond);
}
#endif // USE_TASK_MGMT
