1/*
2 * MoviePlayer.c --
3 *
4 *	QuickTime Movie widget for Tcl/Tk, part of QuickTimeTcl.
5 *
6 * Copyright (c) 1998       Bruce O'Neel
7 * Copyright (c) 2000-2008  Mats Bengtsson
8 *
9 * $Id: MoviePlayer.c,v 1.31 2008/04/29 13:11:31 matben Exp $
10 */
11
12#include "MoviePlayer.h"
13
14/*
15 * Used for controller timer callback. Presently unused.
16 */
17
18typedef struct ControllerTimerRecord {
19    MoviePlayer     *movPtr;
20    char            *tclCmd;
21} ControllerTimerRecord;
22
23TrackScrap   		gTrackScrap;
24
25/*
26 * Record to handle callbacks to scripy level when async movie loading (-url).
27 */
28
29typedef struct AsyncLoadHandlerEntry {
30    MoviePlayer				*movPtr;
31    Tcl_TimerToken			timerToken;     /* Tcl's token for the timer handler. */
32    Tcl_Obj 				*commandObjPtr;	/* Command to invoke when the
33     * handler is invoked. */
34    Tcl_HashEntry			*hashPtr;		/* Self referencing; useful when freeing */
35} AsyncLoadHandlerEntry;
36
37static GWorldPtr    gOffscreenGWorldPtr = NULL;
38
39
40/*
41 * We need a global doubly linked list for our movies since we cant get them
42 * to the Mac native event handler in any other way.
43 */
44
45static MoviePlayerList  *gMoviePlayerListPtr = NULL;
46
47/*
48 * Need to keep a ref count on how many movies actually opened.
49 * Different from 'MoviePlayerList' since movie "eventually" disposed.
50 */
51
52static int gMovieRefCount = 0;
53
54/*
55 * Time interval for Carbon event timer (in secs). Apple recommendation.
56 */
57
58#if TARGET_API_MAC_CARBON
59
60EventLoopTimerRef   gCarbonMovieTimerRef = NULL;
61const EventTime 	kCarbonMovieTimerSecs = kEventDurationSecond / 10;
62long				kMinimumIdleDurationInMillis = 20;
63
64/*
65 * Hash table to keep track of Carbon window event handlers.
66 * It maps a WindowRef (WindowPtr) to its corresponding event handler.
67 */
68
69static Tcl_HashTable 	*gCarbonWindowHandlerHashPtr = NULL;
70
71typedef struct CarbonWindowHandlerEntry {
72    EventHandlerRef		handlerRef;
73    long				refCount;		/* Number of movies in this window. */
74} CarbonWindowHandlerEntry;
75
76#endif
77
78/*
79 * We need a global variable to set if either or both of MoviePlayer or
80 * SeqGrabber is running. This is used by our common Mac event procedure to
81 * figure out if we should call MoviePlayerMacEvent and/or SeqGrabberMacEvent.
82 * It is declared external in SeqGrabber.c.
83 */
84
85long    gIsRunningFlags = 0L;
86
87/*
88 * Keeps track of freeing things in correct order when closing down.
89 */
90static enum QTTclFreeState gExitState = 0;
91
92static Boolean gHasQTVRManager = false;
93
94/*
95 * For dispatching movie commands. Order!!!
96 */
97
98static char *allMovieCmds[] = {
99    "add",
100    "callback",
101    "cget",
102    "clear",
103    "compress",
104    "configure",
105    "controllerinfo",
106    "copy",
107    "cut",
108    "effect",
109    "export",
110    "fieldofview",
111    "flatten",
112    "getpreferredrate",
113    "getrate",
114    "getselection",
115    "gettime",
116    "haschanged",
117    "hotspot",
118    "isdone",
119    "ismovie",
120    "ispanoramic",
121    "isscrapmovie",
122    "isvisual",
123    "new",
124    "nextinterestingtime",
125    "pan",
126    "panoinfo",
127    "paste",
128    "picture",
129    "play",
130    "playhints",
131    "rate",
132    "save",
133    "saveas",
134    "select",
135    "setpreferredrate",
136    "setrate",
137    "settime",
138    "size",
139    "step",
140    "stop",
141    "tilt",
142    "time",
143    "timecode",
144    "tracks",
145    "undo",
146    "userdata",
147    (char *) NULL
148};
149
150enum {
151    kMovieCmdAdd                = 0L,
152    kMovieCmdCallBack,
153    kMovieCmdCget,
154    kMovieCmdClear,
155    kMovieCmdCompress,
156    kMovieCmdConfigure,
157    kMovieCmdControllerinfo,
158    kMovieCmdCopy,
159    kMovieCmdCut,
160    kMovieCmdEffect,
161    kMovieCmdExport,
162    kMovieCmdFieldofview,
163    kMovieCmdFlatten,
164    kMovieCmdGetpreferredrate,
165    kMovieCmdGetrate,
166    kMovieCmdGetselection,
167    kMovieCmdGettime,
168    kMovieCmdHasChanged,
169    kMovieCmdHotspot,
170    kMovieCmdIsdone,
171    kMovieCmdIsmovie,
172    kMovieCmdIspanoramic,
173    kMovieCmdIsscrapmovie,
174    kMovieCmdIsvisual,
175    kMovieCmdNew,
176    kMovieCmdNextInterestingTime,
177    kMovieCmdPan,
178    kMovieCmdPanoInfo,
179    kMovieCmdPaste,
180    kMovieCmdPicture,
181    kMovieCmdPlay,
182    kMovieCmdPlayHints,
183    kMovieCmdRate,
184    kMovieCmdSave,
185    kMovieCmdSaveas,
186    kMovieCmdSelect,
187    kMovieCmdSetpreferredrate,
188    kMovieCmdSetrate,
189    kMovieCmdSettime,
190    kMovieCmdSize,
191    kMovieCmdStep,
192    kMovieCmdStop,
193    kMovieCmdTilt,
194    kMovieCmdTime,
195    kMovieCmdTimeCode,
196    kMovieCmdTracks,
197    kMovieCmdUndo,
198    kMovieCmdUserData,
199    kMovieCmdUndocumeted1,
200    kMovieCmdUndocumeted2
201};
202
203static char *allMovieFlattenOpts[] = {
204    "-dialog", "-dontinterleave", "-forceresourcebeforedata",
205    (char *) NULL
206};
207
208enum {
209    kMovieFlattenOptDialog           		= 0L,
210    kMovieFlattenOptDontInterleave,
211    kMovieFlattenOptForceResourceBeforeData
212};
213
214static char *allMoviePlayHintsOpts[] = {
215    "-scrubmode",
216    "-usesoundinterp",
217    "-allowinterlace",
218    "-allowblacklining",
219    "-dontpurge",
220    "-inactive",
221    "-highquality",
222    (char *) NULL
223};
224
225enum {
226    kMoviePlayHintsOptScrubMode				= 0L,
227    kMoviePlayHintsOptUseSoundInterp,
228    kMoviePlayHintsOptAllowInterlace,
229    kMoviePlayHintsOptAllowBlacklining,
230    kMoviePlayHintsOptDontPurge,
231    kMoviePlayHintsOptInactive,
232    kMoviePlayHintsOptHighQuality
233};
234
235static Tk_ClassProcs MoviePlayerProcs = {
236    sizeof(Tk_ClassProcs),
237    MoviePlayerWorldChanged,	/* geometryProc */
238    NULL,						/* createProc */
239    NULL						/* modalproc */
240};
241
242/*
243 * Information used for parsing configuration options.
244 * Mask bits for options changed.
245 */
246
247enum {
248    MOV_CONF_NEWGWORLD              	= (1L << 0),
249    MOV_CONF_FILE                     	= (1L << 1),
250    MOV_CONF_URL                     	= (1L << 2),
251    MOV_CONF_MCEDIT                 	= (1L << 3),
252    MOV_CONF_LOOPSTATE               	= (1L << 4),
253    MOV_CONF_PALINDROME_LOOPSTATE      	= (1L << 5),
254    MOV_CONF_VOLUME                 	= (1L << 6),
255    MOV_CONF_PREFERRED_RATE           	= (1L << 7),
256    MOV_CONF_QTVR_QUALITY_STATIC        = (1L << 8),
257    MOV_CONF_QTVR_QUALITY_MOTION        = (1L << 9),
258    MOV_CONF_LOAD_INTO_RAM        		= (1L << 10),
259    MOV_CONF_PROGRESS_PROC        		= (1L << 11),
260    MOV_CONF_CUSTOM_BUTTON        		= (1L << 12),
261    MOV_CONF_RESIZEABLE	        		= (1L << 13),
262    MOV_CONF_HIGHLIGHTS	        		= (1L << 14)
263};
264
265/*
266 * The following table defines the legal values for the -qtvrqualitystatic option.
267 */
268
269static CONST char *kQTVRQualityArr[] = {
270    "min", "low", "normal", "high", "max", (char *) NULL
271};
272#define	MOV_QTVR_QUALITY_MIN			0
273#define	MOV_QTVR_QUALITY_LOW			1
274#define	MOV_QTVR_QUALITY_NORMAL			2
275#define	MOV_QTVR_QUALITY_HIGH			3
276#define	MOV_QTVR_QUALITY_MAX			4
277
278#define BLACK		"Black"
279#define WHITE		"White"
280
281#if TARGET_API_MAC_CARBON
282#define NORMAL_BG	"systemWindowBody"
283#endif
284#ifdef _WIN32
285#define NORMAL_BG	"SystemButtonFace"
286#endif
287
288
289static Tk_OptionSpec MoviePlayerConfigSpecs[] = {
290    {TK_OPTION_BOOLEAN, "-controller", "controller", "Controller",
291	"1", -1, Tk_Offset(MoviePlayer, wantController), 0,
292    (ClientData) NULL, 0},
293    {TK_OPTION_BOOLEAN, "-custombutton", "customButton", "CustomButton",
294	"0", -1, Tk_Offset(MoviePlayer, custombutton), 0,
295    (ClientData) NULL, MOV_CONF_CUSTOM_BUTTON},
296    {TK_OPTION_STRING, "-file", "file", "File",
297	NULL, Tk_Offset(MoviePlayer, fileNamePtr), -1, TK_OPTION_NULL_OK,
298    (ClientData) NULL, MOV_CONF_FILE},
299    {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground",
300    NORMAL_BG, -1, Tk_Offset(MoviePlayer, highlightBgColorPtr), 0, 0, MOV_CONF_HIGHLIGHTS},
301    {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
302    BLACK, -1, Tk_Offset(MoviePlayer, highlightColorPtr), 0, 0, MOV_CONF_HIGHLIGHTS},
303    {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", "HighlightThickness",
304    "0", -1, Tk_Offset(MoviePlayer, highlightWidth), 0, 0, MOV_CONF_HIGHLIGHTS},
305    {TK_OPTION_PIXELS, "-height", "height", "Height",
306	"0", -1, Tk_Offset(MoviePlayer, height), 0,
307    (ClientData) NULL, MOV_CONF_NEWGWORLD},
308    {TK_OPTION_STRING, "-loadcommand", "loadCommand", "LoadCommand",
309	NULL, -1, Tk_Offset(MoviePlayer, loadCommand), TK_OPTION_NULL_OK,
310    (ClientData) NULL, 0},
311    {TK_OPTION_BOOLEAN, "-loadintoram", "loadIntoRam", "LoadIntoRam",
312	"0", -1, Tk_Offset(MoviePlayer, loadIntoRam), 0,
313    (ClientData) NULL, MOV_CONF_LOAD_INTO_RAM},
314    {TK_OPTION_BOOLEAN, "-loopstate", "loopstate", "Loopstate",
315	"0", -1, Tk_Offset(MoviePlayer, loopstate), 0,
316    (ClientData) NULL, MOV_CONF_LOOPSTATE},
317    {TK_OPTION_STRING, "-mccommand", "mcCommand", "MCCommand",
318	NULL, -1, Tk_Offset(MoviePlayer, mcCallbackProc), TK_OPTION_NULL_OK,
319    (ClientData) NULL, 0},
320    {TK_OPTION_BOOLEAN, "-mcedit", "mcEdit", "MCEdit",
321	"0", -1, Tk_Offset(MoviePlayer, mcEdit), 0,
322    (ClientData) NULL, MOV_CONF_MCEDIT},
323    {TK_OPTION_BOOLEAN, "-palindromeloopstate", "palindromeLoopstate", "PalindromeLoopstate",
324	"0", -1, Tk_Offset(MoviePlayer, palindromeloopstate), 0,
325    (ClientData) NULL, MOV_CONF_PALINDROME_LOOPSTATE},
326    {TK_OPTION_DOUBLE, "-preferredrate", "preferredRate", "PreferredRate",
327	"1.0", -1, Tk_Offset(MoviePlayer, preferredRate), 0,
328    (ClientData) NULL, MOV_CONF_PREFERRED_RATE},
329    {TK_OPTION_STRING, "-progressproc", "progressProc", "ProgressProc",
330	NULL, -1, Tk_Offset(MoviePlayer, progressProc), TK_OPTION_NULL_OK,
331    (ClientData) NULL, MOV_CONF_PROGRESS_PROC},
332    {TK_OPTION_BOOLEAN, "-qtprogress", "qtProgress", "QTProgress",
333	"1", -1, Tk_Offset(MoviePlayer, qtprogress), 0,
334    (ClientData) NULL, MOV_CONF_PROGRESS_PROC},
335    {TK_OPTION_STRING_TABLE, "-qtvrqualitystatic", "qtvrQualityStatic", "QTVRQualityStatic",
336	"normal", -1, Tk_Offset(MoviePlayer, indQTVRQualityStatic), 0,
337    (ClientData) kQTVRQualityArr, MOV_CONF_QTVR_QUALITY_STATIC},
338    {TK_OPTION_STRING_TABLE, "-qtvrqualitymotion", "qtvrQualityMotion", "QTVRQualityMotion",
339	"normal", -1, Tk_Offset(MoviePlayer, indQTVRQualityMotion), 0,
340    (ClientData) kQTVRQualityArr, MOV_CONF_QTVR_QUALITY_MOTION},
341    {TK_OPTION_BOOLEAN, "-resizable", "resizable", "Resizable",
342	"0", -1, Tk_Offset(MoviePlayer, resizable), 0,
343    (ClientData) NULL, MOV_CONF_RESIZEABLE},
344    {TK_OPTION_BOOLEAN, "-swing", "swing", "Swing",
345	"0", -1, Tk_Offset(MoviePlayer, swing), 0,
346    (ClientData) NULL, 0},
347    {TK_OPTION_INT, "-swingspeed", "swingSpeed", "SwingSpeed",
348	"5", -1, Tk_Offset(MoviePlayer,swingSpeed), 0,
349    (ClientData) NULL, 0},
350    {TK_OPTION_STRING, "-url", "url", "Url",
351	NULL, -1, Tk_Offset(MoviePlayer, url), TK_OPTION_NULL_OK,
352    (ClientData) NULL, MOV_CONF_URL},
353    {TK_OPTION_INT, "-volume", "volume", "Volume",
354	"255", -1, Tk_Offset(MoviePlayer, volume), 0,
355    (ClientData) NULL, MOV_CONF_VOLUME},
356    {TK_OPTION_PIXELS, "-width", "width", "Width",
357	"0", -1, Tk_Offset(MoviePlayer, width), 0,
358    (ClientData) NULL, MOV_CONF_NEWGWORLD},
359    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
360    (char *) NULL, 0, 0, 0, 0}
361};
362
363/*
364 * Declarations for functions in this source file only.
365 */
366
367static int 		MoviePlayerWidgetCmd( ClientData clientData,
368			    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] );
369static MoviePlayer	*CreateMoviePlayer( Tk_Window tkwin );
370static int              ConfigureMoviePlayer( Tcl_Interp *interp,
371			     MoviePlayer *movPtr, int objc, Tcl_Obj *CONST objv[] );
372static int              ConfigureQTVRMovie( Tcl_Interp *interp,
373			    MoviePlayer * movPtr, int setQTVRQualityStatic,
374			    int setQTVRQualityMotion );
375static int 		GetMovie( MoviePlayer *movPtr );
376static int 		GetMovieFromUrl( MoviePlayer *movPtr );
377static void 		MoviePlayerDeletedProc( ClientData clientData );
378static void 		MoviePlayerEventProc( ClientData clientData,
379			    XEvent *eventPtr );
380static void             AddOrRemoveMovieController( MoviePlayer *movPtr );
381static void		AddMovieToOpenMovies( MoviePlayer *movPtr );
382static void             RemoveMovieFromOpenMovies( MoviePlayer *movPtr );
383static void 		DisplayMovie( ClientData clientData );
384static void 		DestroyMovie( MoviePlayer *movPtr );
385static void		MoviePlayerFree( char *clientData );
386static void             DisposeMovieAtIdle( ClientData clientData );
387static void		MovieExitProc( ClientData clientData );
388static void		ExitMoviePlayer( ClientData clientData );
389static void             CheckMovieLoadState( MoviePlayer *movPtr );
390static int 		MoviePlayerMacEvent( EventRecord *eventPtr,
391			    WindowRef serveWindowRef );
392#ifdef _WIN32
393static int		MoviePlayerMacEventWin( EventRecord *eventPtr );
394#endif
395static int 		ProcessVectorSubcmd( ClientData clientData,
396			    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] );
397static int 		ProcessSpriteSubcmd( ClientData clientData,
398			    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] );
399static pascal Boolean   MovieControllerCallbackFunction( MovieController mc,
400			    short action, void *params, long refCon );
401static void             InstallExtendedSCCallbackProcedures(
402			    ComponentInstance ci, long refCon );
403static pascal Boolean   MySCFilterDialogProc( DialogPtr dialogPtr,
404			    EventRecord * event, short *itemHit, long refCon );
405static pascal OSErr 	MovieProgressFunction( Movie theMovie, short message,
406			    short whatOperation, Fixed percentDone, long refcon );
407static void             ControllerCallbackTimer( ClientData clientData );
408
409static int		ComressMovieCmd(Tcl_Interp *interp,
410			    int objc, Tcl_Obj *CONST objv[],
411			    MoviePlayer *movPtr);
412static int		NewMovieCmd(Tcl_Interp *interp,
413			    int objc, Tcl_Obj *CONST objv[],
414			    MoviePlayer *movPtr);
415static int		FlattenMovieCmd(Tcl_Interp *interp,
416			    int objc, Tcl_Obj *CONST objv[],
417			    MoviePlayer *movPtr);
418
419static void		AsyncLoadHandlerProc( ClientData clientData );
420static void		AsyncLoadFree( MoviePlayer *movPtr );
421
422#if TARGET_OS_MAC
423static void		SetMoviePlayerRectBox( MoviePlayer *movPtr );
424#endif
425
426#if TARGET_API_MAC_CARBON
427static pascal void      MoviePlayerCarbonTimer( EventLoopTimerRef theTimer,
428			    void *userData );
429static OSStatus		InstallMovieIdlingEventLoopTimer( void );
430static void 		TaskNeededSoonerCallback( TimeValue duration,
431			    unsigned long flags, void *refcon );
432static void		CarbonTimerNextTime( void );
433static void		InstallCarbonWindowEventHandler( MoviePlayer *movPtr );
434static OSStatus		MovieCarbonWindowEventHandler( EventHandlerCallRef callRef,
435			    EventRef eventRef, void *userData );
436static void		MovieDestroyCarbonWindowHandlerCleanup( MoviePlayer *movPtr );
437#endif
438
439#ifdef _WIN32
440LRESULT CALLBACK	QTWinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
441#endif
442
443/*
444 *----------------------------------------------------------------------
445 *
446 * MoviePlayerObjCmd --
447 *
448 *		Create a MoviePlayer widget.
449 *
450 * Results:
451 *		Standard TCL result
452 *
453 * Side effects:
454 *		Creates a command and allocates memory.  Possibly allocates
455 *		mac structures INITS the Movie toolbox, this could conflict
456 *		with someone else
457 *
458 *----------------------------------------------------------------------
459 */
460
461int
462MoviePlayerObjCmd(
463		  ClientData clientData, 		/* NULL */
464		  Tcl_Interp *interp,
465		  int objc,
466		  Tcl_Obj *CONST objv[] )
467{
468    MoviePlayer 	*movPtr;
469    Tk_Window   	tkwin;
470    Tk_OptionTable 	optionTable;
471    long        	gestalt;
472    long		eventFlags = 0;
473    static int  	enterMoviesCalled = 0;
474    OSErr       	err = noErr;
475    Rect        	aRect = { 0, 0, 10, 10 };   /* Just a dummy rect. */
476
477    if (noErr != Gestalt( gestaltQuickTime, &gestalt )) {
478	Tcl_SetObjResult( interp, Tcl_NewStringObj( "QuickTime not installed", -1 ) );
479	return TCL_ERROR;
480    }
481    QTTclDebugPrintf( interp, 2, "MoviePlayerObjCmd" );
482
483    /*
484     * Init QuickTime movies.
485     */
486
487    if (!enterMoviesCalled) {
488	long    attr;
489
490        err = EnterMovies();
491	if (err != noErr) {
492            CheckAndSetErrorResult( interp, err );
493	    return TCL_ERROR;
494	}
495	enterMoviesCalled++;
496	gIsRunningFlags |= MOVIE_PLAYER_RUNS;
497	gTrackScrap.movPtr = NULL;
498	Tcl_CreateExitHandler( MovieExitProc, (ClientData) NULL );
499
500        /*
501         * We use one and the same offscreen GWorld for all movies.
502         */
503
504	err = MySafeNewGWorld( &gOffscreenGWorldPtr, 32, &aRect, NULL, NULL, 0 );
505	if (err != noErr) {
506	    CheckAndSetErrorResult( interp, err );
507	    return TCL_ERROR;
508	}
509
510	/*
511	 * Check the availablity of the QTVR manager.
512	 */
513
514	gHasQTVRManager = false;
515	if (noErr == Gestalt( gestaltQTVRMgrAttr, &attr )) {
516	    if (attr & (1 << gestaltQTVRMgrPresent)) {
517		gHasQTVRManager = true;
518	    }
519	}
520
521	/*
522	 * The video effects record needs to be inited.
523	 */
524
525	EffectsInit();
526
527        QTTclDebugPrintf( interp, 2, "KeyPress=%2d KeyRelease=%2d ButtonPress=%2d ButtonRelease=%2d",
528			 KeyPress, KeyRelease, ButtonPress, ButtonRelease );
529        QTTclDebugPrintf( interp, 2, "FocusIn=%2d FocusOut=%2d Expose=%2d DestroyNotify=%2d UnmapNotify=%2d MapNotify=%2d ConfigureNotify=%2d",
530			 FocusIn, FocusOut, Expose, DestroyNotify, UnmapNotify, MapNotify, ConfigureNotify );
531        QTTclDebugPrintf( interp, 3, "\tgOffscreenGWorldPtr=0x%.8x", gOffscreenGWorldPtr );
532        QTTclDebugPrintf( interp, 3,
533			 "\tREDRAW_PENDING=%d, NEWGWORLD=%d, UPDATEMOVIE=%d",
534			 REDRAW_PENDING, NEWGWORLD, UPDATEMOVIE );
535    }
536
537    if (objc < 2) {
538	Tcl_WrongNumArgs( interp, 1, objv, "pathName ?options?" );
539	return TCL_ERROR;
540    }
541    tkwin = Tk_CreateWindowFromPath( interp, Tk_MainWindow(interp),
542				    Tcl_GetString(objv[1]), (char *) NULL );
543    if (tkwin == NULL) {
544	return TCL_ERROR;
545    }
546
547    /*
548     * Create the option table for this widget class.  If it has already
549     * been created, the cached pointer will be returned.
550     */
551
552    optionTable = Tk_CreateOptionTable( interp, MoviePlayerConfigSpecs );
553
554    Tk_SetClass( tkwin, "Movie" );
555    movPtr = CreateMoviePlayer( tkwin );
556
557    Tk_SetClassProcs( tkwin, &MoviePlayerProcs, (ClientData) movPtr );
558    movPtr->tkwin = tkwin;
559    movPtr->display = Tk_Display( tkwin );
560    movPtr->interp = interp;
561    movPtr->widgetCmd = Tcl_CreateObjCommand( interp, Tk_PathName(movPtr->tkwin),
562					     MoviePlayerWidgetCmd, (ClientData) movPtr, MoviePlayerDeletedProc );
563    movPtr->optionTable = optionTable;
564
565    /*
566     * Movie Player specific attributes.
567     */
568
569    movPtr->aMovie = NULL;
570    movPtr->filename = NULL;
571    movPtr->fileNamePtr	= NULL;
572    movPtr->url = NULL;
573    movPtr->flags = 0;
574    movPtr->state = 0;
575    movPtr->width = 0;
576    movPtr->height = 0;
577    movPtr->mheight = 0;
578    movPtr->mwidth = 0;
579    movPtr->padx = 0;
580    movPtr->pady = 0;
581    movPtr->highlightWidth = 0;
582    movPtr->inset = movPtr->padx + movPtr->highlightWidth;
583    movPtr->volume = 255;
584    movPtr->isVisual = 1;
585    movPtr->undoCount = 0;
586    movPtr->editStates = NULL;
587    movPtr->editStatesSize = 0;
588    movPtr->loadIntoRam = 0;
589    movPtr->qtvrInstance = NULL;
590    movPtr->indQTVRQualityStatic = -1;
591    movPtr->indQTVRQualityMotion = -1;
592    movPtr->funcQTVRIntercept = NULL;
593    movPtr->swing = 0;
594    movPtr->swingSpeed = 5;
595    movPtr->trackSelect->trackID = 0;
596
597    /* A resource number -1 means movie in data fork. */
598    movPtr->resourceNumber = -1;
599    movPtr->progressProc = NULL;
600    movPtr->grafPtr = NULL;
601    movPtr->qtprogress = 1;
602    movPtr->mcCallbackProc = NULL;
603    movPtr->loadCommand = NULL;
604    movPtr->loadState = 0;
605    movPtr->aController = NULL;
606    movPtr->wantController = 1;
607    movPtr->controllerHeight = 0;
608    movPtr->mcEdit = 0;
609    movPtr->resizable = 0;
610    movPtr->custombutton = 0;
611    movPtr->loopstate = 0;
612    movPtr->palindromeloopstate = 0;
613    movPtr->rate = (Fixed) 0;
614    movPtr->preferredRate = 1.0;
615    movPtr->insideMCCommand = 0;
616    movPtr->asyncLoadHashTablePtr = NULL;
617    movPtr->callBackHashTablePtr = NULL;
618    movPtr->tmpMovieRecordPtr = NULL;
619
620    QTTclDebugPrintf( interp, 2, "\tmovPtr=%d", movPtr );
621
622    /*
623     * This is an extremly important choice of which events shall be handled
624     * by tk. Using the ExposureMask seems to cause infinite loops,
625     * in particular when the controller is obscured.
626     */
627
628#if TARGET_OS_MAC
629    eventFlags = StructureNotifyMask | FocusChangeMask |
630    VisibilityChangeMask | KeyReleaseMask | ButtonPressMask;
631#else
632    eventFlags = ExposureMask | StructureNotifyMask | FocusChangeMask |
633    VisibilityChangeMask | KeyReleaseMask | ButtonPressMask;
634#endif
635    Tk_CreateEventHandler( movPtr->tkwin, eventFlags,
636			  MoviePlayerEventProc, (ClientData) movPtr );
637
638    if (Tk_InitOptions( interp, (char *) movPtr, optionTable, movPtr->tkwin )
639	!= TCL_OK) {
640        Tk_DestroyWindow( movPtr->tkwin );
641        return TCL_ERROR;
642    }
643
644    /*
645     * Configure the widget; parse the command line arguments and look for defaults
646     * in the resource database. The last 0 means to ...
647     */
648
649    if (ConfigureMoviePlayer( interp, movPtr, objc - 2, objv + 2 )
650	!= TCL_OK) {
651	Tk_DestroyWindow( movPtr->tkwin );
652	return TCL_ERROR;
653    }
654
655    /*
656     * Windows specific code to create a Mac style graphics port, and associate
657     * it with a Windows HWND. Get the winproc given by tk, save it to be called
658     * later, and set our own winproc.
659     */
660
661#ifdef _WIN32
662    {
663        HWND        tempHwnd = NULL;
664
665        if (Tk_WindowId(movPtr->tkwin) == None) {
666            Tk_MakeWindowExist( movPtr->tkwin );
667        }
668        tempHwnd = TkWinGetHWND( Tk_WindowId(movPtr->tkwin) );
669        movPtr->hwnd = tempHwnd;
670    	CreatePortAssociation( tempHwnd, NULL, 0L );
671        movPtr->winEventProc = GetWindowLong( tempHwnd, GWL_WNDPROC );
672        SetWindowLong( tempHwnd, GWL_WNDPROC, (LONG) QTWinProc );
673    }
674#endif  // _WIN32
675
676    /*
677     * We should add our movie to our global list of open movies already here.
678     * This is mainly to get a handle on the original WndProc on Windows, so it
679     * can get called even if not widget on display.
680     */
681
682    AddMovieToOpenMovies( movPtr );
683
684    /*
685     * Set the NEWGWORLD in 'flags' to require an update and to find the geometry.
686     * If a remote movie is async loading, a 1x1 size is given in 'MoviePlayerWorldChanged'.
687     */
688
689    movPtr->flags |= NEWGWORLD;
690    movPtr->flags |= NEED_PREROLL;
691    MoviePlayerWorldChanged( (ClientData) movPtr );
692    Tcl_SetObjResult( interp, Tcl_NewStringObj( Tk_PathName(movPtr->tkwin), -1 ) );
693    return TCL_OK;
694}
695/*
696 *----------------------------------------------------------------------
697 *
698 * MoviePlayerWidgetCmd --
699 *
700 *		Command to run for each widget.
701 *
702 * Results:
703 *		Normal TCL results
704 *
705 * Side effects:
706 *		Memory allocated and/or freed, Mac Movie structures modified
707 *
708 *----------------------------------------------------------------------
709 */
710
711static int
712MoviePlayerWidgetCmd( ClientData clientData,
713		     Tcl_Interp *interp,
714		     int objc,
715		     Tcl_Obj *CONST objv[] )
716{
717    MoviePlayer     *movPtr = (MoviePlayer *) clientData;
718    Movie	    aMovie = movPtr->aMovie;
719    short           resId;
720    short           movieResFile;
721    unsigned short  vol;
722    int             i;
723    int             undoLevel;
724    int		    sizeMayChange;
725    int             iarg;
726    int             cmdIndex;
727    int             optIndex;
728    int		    boolValue;
729    int		    intValue;
730    long            flags;
731    long	    mask;
732    long	    longValue;
733    double          theRate;
734    Tcl_Obj	    *resultObjPtr;
735    Tcl_Obj	    *listObjPtr;
736    Fixed           rate;
737    TimeValue       movTime;
738    TimeValue       movDuration;
739    FSSpec          fss;
740    PicHandle       thePic = NULL;
741    Movie           tmpMovie = NULL;
742    OSErr           err = noErr;
743    ComponentResult compErr = badComponentType;
744    int             result = TCL_OK;
745
746    if (objc < 2) {
747	Tcl_WrongNumArgs( interp, 1, objv, "command ?arg arg ...?" );
748	return TCL_ERROR;
749    }
750    QTTclDebugPrintf( interp, 2, "MoviePlayerWidgetCmd" );
751
752    Tcl_Preserve((ClientData) movPtr);
753    if (Tcl_GetIndexFromObj( interp, objv[1], allMovieCmds, "command", TCL_EXACT, &cmdIndex )
754	!= TCL_OK ) {
755	return TCL_ERROR;
756    }
757
758    /* Get the volume since the user may have changed it. */
759
760    vol = GetMovieVolume( aMovie );
761    movPtr->volume = vol;
762
763    /*
764     * Dispatch the movie command to the right branch.
765     */
766
767    switch (cmdIndex) {
768
769        case kMovieCmdAdd: {
770
771    	    /*
772    	     * Add tracks in parallel to the movie.
773    	     */
774
775    	    if (objc >= 3) {
776		Tcl_WrongNumArgs( interp, 2, objv, NULL );
777		result = TCL_ERROR;
778		goto error;
779    	    }
780	    if (aMovie != NULL) {
781		tmpMovie = NewMovieFromScrap(0);
782		if (tmpMovie != NULL) {
783		    result = LogUndoState( movPtr, interp );
784		    AddMovieSelection( aMovie, tmpMovie );
785		    /* // scales to fit...
786		     GetMovieBox( tmpMovie, &aRect );
787		     MacOffsetRect( &aRect, -aRect.left, -aRect.top );
788		     if (aRect.right > movPtr->mwidth) {
789		     movPtr->mwidth = aRect.right;
790		     }
791		     if (aRect.bottom > movPtr->mheight) {
792		     movPtr->mheight = aRect.bottom;
793		     }
794		     */
795		    DisposeMovie( tmpMovie );
796		} else {
797		    CheckAndSetErrorResult( interp, noErr );
798		    result = TCL_ERROR;
799		}
800		if (movPtr->aController != NULL) {
801		    MCDoAction( movPtr->aController,  mcActionMovieEdited, NULL );
802		    MCMovieChanged( movPtr->aController, aMovie );
803		} else {
804		    MoviePlayerWorldChanged( clientData );
805		}
806	    } else {
807		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
808		result = TCL_ERROR;
809	    }
810	    break;
811        }
812
813        case kMovieCmdCallBack: {
814	    if (objc < 3) {
815		Tcl_WrongNumArgs( interp, 2, objv, "time|cmd ?args?" );
816		goto error;
817	    }
818	    if (aMovie != NULL) {
819		if (ProcessCallBackCmd( movPtr, objc-2, objv+2 ) != TCL_OK) {
820		    result = TCL_ERROR;
821		}
822	    } else {
823		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
824		result = TCL_ERROR;
825	    }
826	    break;
827        }
828
829        case kMovieCmdCget: {
830
831    	    /*
832    	     * Just return an option.
833    	     */
834
835	    if (objc != 3) {
836		Tcl_WrongNumArgs( interp, 2, objv, "option" );
837		result = TCL_ERROR;
838		goto error;
839	    }
840	    resultObjPtr = Tk_GetOptionValue( interp, (char *) movPtr,
841					     movPtr->optionTable, objv[2], movPtr->tkwin );
842	    if (resultObjPtr == NULL) {
843		result = TCL_ERROR;
844	    } else {
845		Tcl_SetObjResult( interp, resultObjPtr );
846	    }
847	    break;
848        }
849
850        case kMovieCmdClear: {
851
852    	    /*
853    	     * Clear any movie selection.
854    	     */
855
856	    if (objc != 2) {
857		Tcl_WrongNumArgs( interp, 2, objv, NULL );
858		goto error;
859	    }
860	    if (aMovie != NULL) {
861		result = LogUndoState( movPtr, interp );
862		if (movPtr->aController && MCIsEditingEnabled( movPtr->aController )) {
863		    if (noErr != MCClear( movPtr->aController )) {
864			CheckAndSetErrorResult( interp, noErr );
865			result = TCL_ERROR;
866			goto error;
867		    }
868		} else {
869		    ClearMovieSelection( aMovie );
870		}
871		if (movPtr->aController != NULL) {
872		    MCDoAction( movPtr->aController,  mcActionMovieEdited, NULL );
873		    MCMovieChanged( movPtr->aController, aMovie );
874		} else {
875		    MoviePlayerWorldChanged( clientData );
876		}
877	    } else {
878		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
879		result = TCL_ERROR;
880	    }
881	    break;
882        }
883
884        case kMovieCmdConfigure: {
885
886    	    /*
887    	     * Configure an option.
888    	     */
889
890	    resultObjPtr = NULL;
891	    if (objc <= 3) {
892		resultObjPtr = Tk_GetOptionInfo( interp, (char *) movPtr,
893						movPtr->optionTable,
894						(objc == 2) ? (Tcl_Obj *) NULL : objv[2],
895						movPtr->tkwin );
896		if (resultObjPtr == NULL) {
897		    result = TCL_ERROR;
898		} else {
899		    Tcl_SetObjResult( interp, resultObjPtr );
900		}
901	    } else {
902
903            	/*
904            	 * Configure the widget; parse the command line arguments and look for defaults
905            	 * in the resource database.
906            	 */
907
908            	result = ConfigureMoviePlayer( interp, movPtr, objc - 2, objv + 2 );
909
910                /*
911                 * Only if we made a configuration that needs a redisplay.
912                 */
913
914		if ((result == TCL_OK) && (movPtr->flags & NEWGWORLD)) {
915		    MoviePlayerWorldChanged( (ClientData) movPtr );
916		}
917	    }
918	    break;
919        }
920
921        case kMovieCmdCompress: {
922
923	    /*
924	     * Compress the movie.
925	     */
926
927	    if (!Tk_IsMapped(movPtr->tkwin)) {
928		Tcl_SetObjResult( interp, Tcl_NewStringObj( "Movie must be displayed", -1 ) );
929		result = TCL_ERROR;
930		goto error;
931	    }
932	    if (objc < 3) {
933		Tcl_WrongNumArgs( interp, 2, objv, "filename" );
934		result = TCL_ERROR;
935		goto error;
936	    }
937	    result = ComressMovieCmd(interp, objc, objv, movPtr);
938	    if (result != TCL_OK) {
939		goto error;
940	    }
941	    break;
942        }
943
944        case kMovieCmdControllerinfo: {
945
946	    /*
947	     * Gets info about the state of the movie controller.
948	     */
949
950	    if (objc != 2) {
951		Tcl_WrongNumArgs( interp, 2, objv, NULL );
952		result = TCL_ERROR;
953		goto error;
954	    }
955	    if (aMovie != NULL) {
956		if (movPtr->aController != NULL) {
957
958		    MCGetControllerInfo( movPtr->aController, &flags );
959		    listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
960		    Tcl_ListObjAppendElement( interp, listObjPtr,
961					     Tcl_NewStringObj("-undoavailable", -1) );
962		    Tcl_ListObjAppendElement( interp, listObjPtr,
963					     Tcl_NewBooleanObj(flags & mcInfoUndoAvailable) );
964		    Tcl_ListObjAppendElement( interp, listObjPtr,
965					     Tcl_NewStringObj("-cutavailable", -1) );
966		    Tcl_ListObjAppendElement( interp, listObjPtr,
967					     Tcl_NewBooleanObj(flags & mcInfoCutAvailable) );
968		    Tcl_ListObjAppendElement( interp, listObjPtr,
969					     Tcl_NewStringObj("-copyavailable", -1) );
970		    Tcl_ListObjAppendElement( interp, listObjPtr,
971					     Tcl_NewBooleanObj(flags & mcInfoCopyAvailable) );
972		    Tcl_ListObjAppendElement( interp, listObjPtr,
973					     Tcl_NewStringObj("-pasteavailable", -1) );
974		    Tcl_ListObjAppendElement( interp, listObjPtr,
975					     Tcl_NewBooleanObj(flags & mcInfoPasteAvailable) );
976		    Tcl_ListObjAppendElement( interp, listObjPtr,
977					     Tcl_NewStringObj("-clearavailable", -1) );
978		    Tcl_ListObjAppendElement( interp, listObjPtr,
979					     Tcl_NewBooleanObj(flags & mcInfoClearAvailable) );
980		    Tcl_ListObjAppendElement( interp, listObjPtr,
981					     Tcl_NewStringObj("-hassound", -1) );
982		    Tcl_ListObjAppendElement( interp, listObjPtr,
983					     Tcl_NewBooleanObj(flags & mcInfoHasSound) );
984		    Tcl_ListObjAppendElement( interp, listObjPtr,
985					     Tcl_NewStringObj("-isplaying", -1) );
986		    Tcl_ListObjAppendElement( interp, listObjPtr,
987					     Tcl_NewBooleanObj(flags & mcInfoIsPlaying) );
988		    Tcl_ListObjAppendElement( interp, listObjPtr,
989					     Tcl_NewStringObj("-islooping", -1) );
990		    Tcl_ListObjAppendElement( interp, listObjPtr,
991					     Tcl_NewBooleanObj(flags & mcInfoIsLooping) );
992		    Tcl_ListObjAppendElement( interp, listObjPtr,
993					     Tcl_NewStringObj("-isinpalindrome", -1) );
994		    Tcl_ListObjAppendElement( interp, listObjPtr,
995					     Tcl_NewBooleanObj(flags & mcInfoIsInPalindrome) );
996		    Tcl_ListObjAppendElement( interp, listObjPtr,
997					     Tcl_NewStringObj("-editingenebled", -1) );
998		    Tcl_ListObjAppendElement( interp, listObjPtr,
999					     Tcl_NewBooleanObj(flags & mcInfoEditingEnabled) );
1000
1001		    Tcl_SetObjResult( interp, listObjPtr );
1002		} else {
1003		    Tcl_SetObjResult( interp,
1004				     Tcl_NewStringObj( "No movie controller", -1 ) );
1005		    result = TCL_ERROR;
1006		}
1007	    } else {
1008		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1009		result = TCL_ERROR;
1010	    }
1011	    break;
1012        }
1013
1014        case kMovieCmdCopy: {
1015
1016    	    /*
1017    	     * Copy the Movie selection. Any track scrap must be invalidated.
1018    	     */
1019
1020	    if (aMovie != NULL) {
1021		if (movPtr->aController && MCIsEditingEnabled( movPtr->aController )) {
1022		    tmpMovie = MCCopy( movPtr->aController );
1023		} else {
1024		    tmpMovie = CopyMovieSelection( aMovie );
1025		}
1026		if (tmpMovie != NULL) {
1027		    PutMovieOnScrap( tmpMovie, 0 );
1028		    DisposeMovie( tmpMovie );
1029		    gTrackScrap.movPtr = NULL;
1030		} else {
1031		    CheckAndSetErrorResult( interp, noErr );
1032		}
1033	    } else {
1034		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1035		result = TCL_ERROR;
1036	    }
1037	    break;
1038        }
1039
1040        case kMovieCmdCut: {
1041
1042    	    /*
1043    	     * Cut the Movie selection. Any track scrap must be invalidated.
1044    	     */
1045
1046	    if (aMovie != NULL) {
1047		result = LogUndoState( movPtr, interp );
1048		if (movPtr->aController && MCIsEditingEnabled( movPtr->aController )) {
1049		    tmpMovie = MCCut( movPtr->aController );
1050		} else {
1051		    tmpMovie = CutMovieSelection( aMovie );
1052		}
1053		if (tmpMovie != NULL) {
1054		    PutMovieOnScrap( tmpMovie, 0 );
1055		    DisposeMovie( tmpMovie );
1056		    gTrackScrap.movPtr = NULL;
1057		} else {
1058		    CheckAndSetErrorResult( interp, noErr );
1059		}
1060		if (movPtr->aController != NULL) {
1061		    MCDoAction( movPtr->aController,  mcActionMovieEdited, NULL );
1062		    MCMovieChanged( movPtr->aController, aMovie );
1063		}
1064	    } else {
1065		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1066		result = TCL_ERROR;
1067	    }
1068	    break;
1069        }
1070
1071        case kMovieCmdEffect: {
1072
1073    	    /*
1074    	     * Make an effect, from 0, 1, or 2 sources. Displays an effect dialog.
1075    	     */
1076
1077#if TARGET_API_MAC_CARBON
1078            Tcl_SetObjResult( interp, Tcl_NewStringObj( "No effects on Mac OS X yet", -1 ) );
1079            result = TCL_ERROR;
1080            break;
1081#endif
1082	    if (aMovie != NULL) {
1083
1084                /* Continues in 'EffectsRespondToDialogSelection'... */
1085                if (ProcessEffectCmd( interp, movPtr, objc - 2, objv + 2 )
1086		    != TCL_OK) {
1087		    result = TCL_ERROR;
1088		    goto error;
1089                }
1090	    } else {
1091		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1092		result = TCL_ERROR;
1093	    }
1094	    break;
1095        }
1096
1097        case kMovieCmdExport: {
1098
1099    	    /*
1100    	     * Export the Movie from a dialog. Return file path or empty.
1101    	     */
1102
1103    	    if (Tcl_IsSafe( interp )) {
1104		Tcl_SetObjResult( interp, Tcl_NewStringObj(
1105							   "\"export\" not allowed in a safe interpreter", -1 ) );
1106		result = TCL_ERROR;
1107            	goto error;
1108    	    }
1109	    if (!Tk_IsMapped(movPtr->tkwin)) {
1110		Tcl_SetObjResult( interp, Tcl_NewStringObj( "Movie must be displayed", -1 ) );
1111		result = TCL_ERROR;
1112		goto error;
1113	    }
1114	    if (aMovie != NULL) {
1115                if (ProcessExportCmd( interp, aMovie, objc - 2, objv + 2 ) != TCL_OK) {
1116		    result = TCL_ERROR;
1117		    goto error;
1118                }
1119	    } else {
1120		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1121		result = TCL_ERROR;
1122	    }
1123	    break;
1124        }
1125
1126        case kMovieCmdFieldofview: {
1127
1128    	    /*
1129    	     * Set or get fieldofview of QTVR pano.
1130    	     */
1131
1132	    if (objc > 3) {
1133		Tcl_WrongNumArgs( interp, 2, objv, "?fov?" );
1134		result = TCL_ERROR;
1135		goto error;
1136	    }
1137	    if (!gHasQTVRManager) {
1138		Tcl_SetObjResult( interp, Tcl_NewStringObj(
1139							   "Couldn't find the QTVR manager on this system", -1 ) );
1140		result = TCL_ERROR;
1141		goto error;
1142	    }
1143	    if (aMovie != NULL) {
1144		double   fov;
1145
1146		if (movPtr->qtvrInstance == NULL) {
1147		    Tcl_SetObjResult( interp, Tcl_NewStringObj(
1148							       "Couldn't identify this movie as a VR movie", -1 ) );
1149		    result = TCL_ERROR;
1150		    goto error;
1151		}
1152		if (objc == 3) {
1153		    if (Tcl_GetDoubleFromObj( interp, objv[2], &fov ) != TCL_OK) {
1154			Tcl_AddErrorInfo( interp,
1155					 "\n	(processing fieldofview command)" );
1156		    } else {
1157			ZoomInOrOutPanorama( movPtr->qtvrInstance, 0, (float) fov );
1158		    }
1159                } else {
1160                    fov = QTVRGetFieldOfView( movPtr->qtvrInstance );
1161                    Tcl_SetObjResult( interp, Tcl_NewDoubleObj(fov) );
1162                }
1163	    } else {
1164		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1165		result = TCL_ERROR;
1166            }
1167	    break;
1168        }
1169
1170        case kMovieCmdFlatten: {
1171
1172    	    /*
1173    	     * Flatten the movie into the datafork of a new file.
1174    	     * A separate mechanism is used for QTVR panos.
1175    	     */
1176
1177    	    if (Tcl_IsSafe( interp )) {
1178    	    	Tcl_SetObjResult( interp,
1179				 Tcl_NewStringObj("\"flatten\" not allowed in a safe interpreter", -1) );
1180		result = TCL_ERROR;
1181            	goto error;
1182    	    }
1183	    if (objc < 3) {
1184		Tcl_WrongNumArgs( interp, 2, objv, "fileName ?args?" );
1185		result = TCL_ERROR;
1186		goto error;
1187	    }
1188	    if (aMovie == NULL) {
1189		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1190		result = TCL_ERROR;
1191		goto error;
1192	    }
1193	    result = FlattenMovieCmd(interp, objc, objv, movPtr);
1194	    if (result != TCL_OK) {
1195		goto error;
1196	    }
1197	    break;
1198        }
1199
1200        case kMovieCmdGetrate: {
1201
1202    	    /*
1203    	     * Get the movie rate. Must be playing. Now obsolete! Use "rate" instead.
1204    	     */
1205
1206	    if (aMovie != NULL) {
1207		Tcl_SetObjResult( interp, Tcl_NewDoubleObj(
1208							   Fix2X( GetMovieRate(aMovie) ) ) );
1209	    } else {
1210		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1211		result = TCL_ERROR;
1212	    }
1213	    break;
1214        }
1215
1216        case kMovieCmdGetpreferredrate: {
1217
1218    	    /*
1219    	     * Get the movie preferred rate. Need not be playing. Now obsolete!
1220    	     */
1221
1222	    if (aMovie != NULL) {
1223		Tcl_SetObjResult( interp, Tcl_NewDoubleObj(
1224							   Fix2X( GetMoviePreferredRate(aMovie) ) ) );
1225	    } else {
1226		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1227		result = TCL_ERROR;
1228	    }
1229	    break;
1230        }
1231
1232        case kMovieCmdGetselection: {
1233
1234    	    /*
1235    	     * Get the movie selection.  Now obsolete, use "select".
1236    	     */
1237
1238	    if (aMovie != NULL) {
1239		GetMovieSelection(aMovie, &movTime, &movDuration);
1240		if (noErr != CheckAndSetErrorResult( interp, noErr )) {
1241                    result = TCL_ERROR;
1242                } else {
1243		    listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
1244		    Tcl_ListObjAppendElement( interp, listObjPtr,
1245					     Tcl_NewStringObj("-selectionTime", -1) );
1246		    Tcl_ListObjAppendElement( interp, listObjPtr,
1247					     Tcl_NewLongObj(movTime) );
1248		    Tcl_ListObjAppendElement( interp, listObjPtr,
1249					     Tcl_NewStringObj("-selectionDuration", -1) );
1250		    Tcl_ListObjAppendElement( interp, listObjPtr,
1251					     Tcl_NewLongObj(movDuration) );
1252		    Tcl_SetObjResult( interp, listObjPtr );
1253		}
1254	    } else {
1255		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1256		result = TCL_ERROR;
1257	    }
1258	    break;
1259        }
1260
1261        case kMovieCmdGettime: {
1262
1263    	    /*
1264    	     * Return the time elapsed for the movie.
1265    	     */
1266
1267	    if (aMovie != NULL) {
1268		listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
1269		Tcl_ListObjAppendElement( interp, listObjPtr,
1270					 Tcl_NewStringObj("-movietime", -1) );
1271		Tcl_ListObjAppendElement( interp, listObjPtr,
1272					 Tcl_NewLongObj(GetMovieTime( aMovie, NULL )) );
1273		Tcl_ListObjAppendElement( interp, listObjPtr,
1274					 Tcl_NewStringObj("-movieduration", -1) );
1275		Tcl_ListObjAppendElement( interp, listObjPtr,
1276					 Tcl_NewLongObj(GetMovieDuration( aMovie )) );
1277		Tcl_ListObjAppendElement( interp, listObjPtr,
1278					 Tcl_NewStringObj("-movietimescale", -1) );
1279		Tcl_ListObjAppendElement( interp, listObjPtr,
1280					 Tcl_NewLongObj(GetMovieTimeScale( aMovie )) );
1281		Tcl_ListObjAppendElement( interp, listObjPtr,
1282					 Tcl_NewStringObj("-postertime", -1) );
1283		Tcl_ListObjAppendElement( interp, listObjPtr,
1284					 Tcl_NewLongObj(GetMoviePosterTime( aMovie )) );
1285		Tcl_SetObjResult( interp, listObjPtr );
1286	    } else {
1287		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1288		result = TCL_ERROR;
1289	    }
1290	    break;
1291        }
1292
1293        case kMovieCmdHasChanged: {
1294
1295    	    /*
1296    	     * Return true if we edited the movie after it has been saved.
1297    	     */
1298
1299	    if (aMovie != NULL) {
1300		Tcl_SetObjResult( interp, Tcl_NewBooleanObj(
1301							    HasMovieChanged( aMovie ) ? true : false) );
1302    	    	if (noErr != CheckAndSetErrorResult( interp, noErr )) {
1303    	            result = TCL_ERROR;
1304    	            goto error;
1305    	        }
1306	    } else {
1307		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1308		result = TCL_ERROR;
1309	    }
1310	    break;
1311        }
1312
1313        case kMovieCmdHotspot: {
1314
1315    	    /*
1316    	     * The hotspot sub command.
1317    	     */
1318
1319	    if (aMovie != NULL) {
1320		if (IsQTVRMovie( aMovie ) && (movPtr->qtvrInstance != NULL)) {
1321		    if (TCL_OK != ProcessHotspotSubCmd( interp, aMovie,
1322						       movPtr->qtvrInstance, objc - 2, objv + 2 )) {
1323			result = TCL_ERROR;
1324                    }
1325                } else {
1326		    Tcl_SetObjResult( interp, Tcl_NewStringObj( "Not a QTVR movie", -1 ) );
1327		    result = TCL_ERROR;
1328                }
1329	    } else {
1330		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1331		result = TCL_ERROR;
1332	    }
1333	    break;
1334        }
1335
1336        case kMovieCmdIsdone: {
1337
1338    	    /*
1339    	     * Is the movie done?
1340    	     */
1341
1342	    if (aMovie != NULL) {
1343		Tcl_SetObjResult( interp, Tcl_NewBooleanObj(
1344							    IsMovieDone( aMovie ) ? true : false) );
1345	    } else {
1346		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1347		result = TCL_ERROR;
1348	    }
1349	    break;
1350        }
1351
1352        case kMovieCmdIsmovie: {
1353
1354    	    /*
1355    	     * Is the movie an ordinary movie and not a VR panoramic movie?
1356    	     */
1357
1358	    if (aMovie != NULL) {
1359		Tcl_SetObjResult( interp, Tcl_NewBooleanObj(
1360							    IsQTVRMovie( aMovie ) ? false : true) );
1361	    } else {
1362		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1363		result = TCL_ERROR;
1364	    }
1365	    break;
1366        }
1367
1368        case kMovieCmdIspanoramic: {
1369
1370    	    /*
1371    	     * Is the movie a VR panoramic movie?
1372    	     */
1373
1374	    if (aMovie != NULL) {
1375		Tcl_SetObjResult( interp, Tcl_NewBooleanObj(
1376							    IsQTVRMovie( aMovie ) ? true : false) );
1377	    } else {
1378		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1379		result = TCL_ERROR;
1380	    }
1381	    break;
1382        }
1383
1384        case kMovieCmdIsscrapmovie: {
1385
1386    	    /*
1387    	     * Is there a movie on the clipboard (scrap)?
1388    	     */
1389
1390    	    {
1391    	        Track       targetTrack = NULL;
1392
1393		if (aMovie != NULL) {
1394		    Tcl_SetObjResult( interp, Tcl_NewBooleanObj(
1395								IsScrapMovie( targetTrack ) ? true : false) );
1396		} else {
1397		    Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1398		    result = TCL_ERROR;
1399		}
1400	    }
1401	    break;
1402        }
1403
1404        case kMovieCmdIsvisual: {
1405
1406    	    /*
1407    	     * Is there there something for our eyes?
1408    	     */
1409
1410	    if (aMovie != NULL) {
1411		Tcl_SetObjResult( interp, Tcl_NewBooleanObj(
1412							    (GetMovieIndTrackType( aMovie, 1,
1413										  VisualMediaCharacteristic,
1414										  movieTrackCharacteristic ) == NULL) ? false : true) );
1415	    } else {
1416		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1417		result = TCL_ERROR;
1418	    }
1419	    break;
1420        }
1421
1422        case kMovieCmdNew: {
1423
1424    	    /*
1425    	     * Create a new movie.
1426    	     */
1427
1428	    if (objc != 3) {
1429		Tcl_WrongNumArgs( interp, 2, objv, "fileName" );
1430		result = TCL_ERROR;
1431		goto error;
1432	    }
1433	    if (movPtr->fileNamePtr != NULL) {
1434		Tcl_SetObjResult( interp,
1435			Tcl_NewStringObj( "There is already a file name associated with this movie", -1 ) );
1436		result = TCL_ERROR;
1437		goto error;
1438	    }
1439	    result = NewMovieCmd(interp, objc, objv, movPtr);
1440	    if (result != TCL_OK) {
1441		goto error;
1442	    }
1443	    /* get rid of the old one */
1444	    break;
1445	}
1446
1447        case kMovieCmdNextInterestingTime: {
1448	    TimeValue	interestingTime;
1449	    TimeValue	interestingDuration;
1450	    OSType	*mediaTypesPtr;
1451	    long	lType;
1452	    int		num;
1453	    short	numMediaTypes = 0;
1454	    Tcl_Obj 	*objPtr;
1455
1456	    if (objc == 3) {
1457		movTime = GetMovieTime( aMovie, NULL );
1458	    } else if (objc == 4) {
1459		if (Tcl_GetIntFromObj( interp, objv[3], &intValue ) != TCL_OK) {
1460		    Tcl_AddErrorInfo( interp, "\n	(processing time value)" );
1461		    result = TCL_ERROR;
1462		    goto error;
1463		}
1464		movTime = intValue;
1465	    } else {
1466		Tcl_WrongNumArgs( interp, 2, objv, "mediaTypeList ?movieTime?" );
1467		return TCL_ERROR;
1468	    }
1469
1470	    /*
1471	     * Get media types from list.
1472	     */
1473
1474	    if (Tcl_ListObjLength( interp, objv[2], &num ) != TCL_OK) {
1475		return TCL_ERROR;
1476	    }
1477	    numMediaTypes = (short) num;
1478	    mediaTypesPtr = (OSType *) ckalloc( numMediaTypes * sizeof(OSType) );
1479	    for (i = 0; i < numMediaTypes; i++) {
1480		if (Tcl_ListObjIndex( interp, objv[2], i, &objPtr ) != TCL_OK) {
1481		    return TCL_ERROR;
1482		}
1483		memcpy( &lType, Tcl_GetString( objPtr ), 4 );
1484		mediaTypesPtr[i] = EndianU32_NtoB( lType );
1485	    }
1486
1487	    listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
1488
1489	    GetMovieNextInterestingTime( aMovie,
1490					nextTimeMediaSample | nextTimeEdgeOK,
1491					numMediaTypes, mediaTypesPtr, movTime, fixed1,
1492					&interestingTime, &interestingDuration );
1493	    Tcl_ListObjAppendElement( interp, listObjPtr,
1494				     Tcl_NewStringObj("-sampletime", -1) );
1495	    Tcl_ListObjAppendElement( interp, listObjPtr,
1496				     Tcl_NewLongObj(interestingTime) );
1497	    Tcl_ListObjAppendElement( interp, listObjPtr,
1498				     Tcl_NewStringObj("-sampleduration", -1) );
1499	    Tcl_ListObjAppendElement( interp, listObjPtr,
1500				     Tcl_NewLongObj(interestingDuration) );
1501
1502	    GetMovieNextInterestingTime( aMovie,
1503					nextTimeMediaEdit | nextTimeEdgeOK,
1504					numMediaTypes, mediaTypesPtr, movTime, fixed1,
1505					&interestingTime, &interestingDuration );
1506	    Tcl_ListObjAppendElement( interp, listObjPtr,
1507				     Tcl_NewStringObj("-edittime", -1) );
1508	    Tcl_ListObjAppendElement( interp, listObjPtr,
1509				     Tcl_NewLongObj(interestingTime) );
1510	    Tcl_ListObjAppendElement( interp, listObjPtr,
1511				     Tcl_NewStringObj("-editduration", -1) );
1512	    Tcl_ListObjAppendElement( interp, listObjPtr,
1513				     Tcl_NewLongObj(interestingDuration) );
1514
1515	    GetMovieNextInterestingTime( aMovie,
1516					nextTimeSyncSample | nextTimeEdgeOK,
1517					numMediaTypes, mediaTypesPtr, movTime, fixed1,
1518					&interestingTime, &interestingDuration );
1519	    Tcl_ListObjAppendElement( interp, listObjPtr,
1520				     Tcl_NewStringObj("-syncsampletime", -1) );
1521	    Tcl_ListObjAppendElement( interp, listObjPtr,
1522				     Tcl_NewLongObj(interestingTime) );
1523	    Tcl_ListObjAppendElement( interp, listObjPtr,
1524				     Tcl_NewStringObj("-syncsampleduration", -1) );
1525	    Tcl_ListObjAppendElement( interp, listObjPtr,
1526				     Tcl_NewLongObj(interestingDuration) );
1527
1528	    err = GetMoviesError();
1529	    if (err!= noErr) {
1530		Tcl_DecrRefCount( listObjPtr );
1531		CheckAndSetErrorResult( interp, err );
1532		return TCL_ERROR;
1533	    }
1534	    Tcl_SetObjResult( interp, listObjPtr );
1535	    break;
1536        }
1537
1538        case kMovieCmdPan: {
1539
1540    	    /*
1541    	     * Set or get pan angle of QTVR pano.
1542    	     */
1543
1544	    if (objc > 3) {
1545		Tcl_WrongNumArgs( interp, 2, objv, "?angle?" );
1546		result = TCL_ERROR;
1547		goto error;
1548	    }
1549	    if (!gHasQTVRManager) {
1550		Tcl_SetObjResult( interp, Tcl_NewStringObj(
1551							   "Couldn't find the QTVR manager on this system", -1 ) );
1552		result = TCL_ERROR;
1553		goto error;
1554	    }
1555	    if (aMovie != NULL) {
1556		double   angle;
1557
1558		if (movPtr->qtvrInstance == NULL) {
1559		    Tcl_SetObjResult( interp, Tcl_NewStringObj(
1560							       "Couldn't identify this movie as a VR movie", -1 ) );
1561		    result = TCL_ERROR;
1562		    goto error;
1563		}
1564		if (objc == 3) {
1565		    if (Tcl_GetDoubleFromObj( interp, objv[2], &angle ) != TCL_OK) {
1566			Tcl_AddErrorInfo( interp,
1567					 "\n	(processing pan command)" );
1568		    } else {
1569			SetPanoramaByDegrees( movPtr->qtvrInstance, kDirLeft, (float) angle );
1570		    }
1571                } else {
1572		    Tcl_SetObjResult( interp, Tcl_NewDoubleObj(
1573							       QTVRGetPanAngle( movPtr->qtvrInstance ) ) );
1574                }
1575	    } else {
1576		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1577		result = TCL_ERROR;
1578            }
1579	    break;
1580        }
1581
1582        case kMovieCmdPanoInfo: {
1583
1584    	    /*
1585    	     * Return info about pano movie.
1586    	     */
1587
1588	    if (objc != 2) {
1589		Tcl_WrongNumArgs( interp, 2, objv, NULL );
1590		result = TCL_ERROR;
1591		goto error;
1592	    }
1593	    if (!gHasQTVRManager) {
1594		Tcl_SetObjResult( interp, Tcl_NewStringObj(
1595							   "Couldn't find the QTVR manager on this system", -1 ) );
1596		result = TCL_ERROR;
1597		goto error;
1598	    }
1599	    if (aMovie != NULL) {
1600                Tcl_Obj     *resObjPtr;
1601                UInt32      nodeID = kQTVRCurrentNode;
1602                //UInt32      nodeID = kQTVRDefaultNode;
1603
1604		if (movPtr->qtvrInstance == NULL) {
1605		    Tcl_SetObjResult( interp, Tcl_NewStringObj(
1606							       "Couldn't identify this movie as a VR movie", -1 ) );
1607		    result = TCL_ERROR;
1608		    goto error;
1609		}
1610
1611                if ( TCL_OK != PanoramaGetInfoNode( interp, aMovie, movPtr->qtvrInstance,
1612						   nodeID, &resObjPtr )) {
1613		    result = TCL_ERROR;
1614		    goto error;
1615		}
1616		Tcl_SetObjResult( interp, resObjPtr );
1617	    } else {
1618		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1619		result = TCL_ERROR;
1620            }
1621	    break;
1622        }
1623
1624        case kMovieCmdPaste: {
1625
1626    	    /*
1627    	     * Paste the movie or whatever that can be imported. Works differently
1628    	     * depending on if '-mcedit 1' (MCIsEditingEnabled) or not.
1629    	     */
1630
1631	    if (objc > 3) {
1632		Tcl_WrongNumArgs( interp, 2, objv, "?option?" );
1633		result = TCL_ERROR;
1634		goto error;
1635	    }
1636	    if (aMovie != NULL) {
1637		sizeMayChange = false;
1638		if ((movPtr->aController != NULL) &&
1639		    MCIsEditingEnabled( movPtr->aController )) {
1640		    long	modifiers = 0L;
1641		    char	*charPtr;
1642
1643		    if (objc == 3) {
1644
1645			/*
1646			 * Set modifier flags for the controller.
1647			 */
1648
1649			charPtr = Tcl_GetString( objv[2] );
1650			if (strcmp(charPtr, "scaled") == 0) {
1651			    modifiers |= shiftKey;
1652			    modifiers |= optionKey;
1653			    sizeMayChange = true;
1654			} else if (strcmp(charPtr, "replace") == 0) {
1655			    modifiers |= shiftKey;
1656			} else if (strcmp(charPtr, "parallel") == 0) {
1657			    modifiers |= optionKey;
1658			    sizeMayChange = true;
1659
1660#if TARGET_OS_MAC
1661			} else if (strcmp(charPtr, "dialog") == 0) {
1662			    modifiers |= controlKey;
1663			    modifiers |= optionKey;
1664			    sizeMayChange = true;
1665#endif
1666
1667			} else {
1668			    Tcl_SetObjResult( interp, Tcl_NewStringObj(
1669								       "unrecognized paste option", -1 ) );
1670			    result = TCL_ERROR;
1671			    goto error;
1672			}
1673		    }
1674		    result = LogUndoState( movPtr, interp );
1675		    compErr = MCSetUpEditMenu( movPtr->aController, modifiers, NULL );
1676		    if (compErr != noErr) {
1677			CheckAndSetErrorResult( interp, noErr );
1678			result = TCL_ERROR;
1679			goto error;
1680		    }
1681		    compErr = MCPaste( movPtr->aController, NULL );
1682		    if (compErr != noErr) {
1683			CheckAndSetErrorResult( interp, noErr );
1684			result = TCL_ERROR;
1685			goto error;
1686		    }
1687		} else {
1688		    if (objc == 3) {
1689			Tcl_SetObjResult( interp, Tcl_NewStringObj(
1690								   "Need a controller with enabled editing to accept paste options", -1 ) );
1691			result = TCL_ERROR;
1692			goto error;
1693		    }
1694		    tmpMovie = NewMovieFromScrap(0);
1695		    if (tmpMovie != NULL) {
1696			result = LogUndoState( movPtr, interp );
1697			PasteMovieSelection( aMovie, tmpMovie );
1698			if (noErr != CheckAndSetErrorResult( interp, noErr )) {
1699    	                    result = TCL_ERROR;
1700			    goto error;
1701    	                } else {
1702			    DisposeMovie(tmpMovie);
1703			}
1704		    } else {
1705			CheckAndSetErrorResult( interp, noErr );
1706			result = TCL_ERROR;
1707			goto error;
1708		    }
1709		}
1710
1711                /*
1712                 * Option here: we choose to only change the size if there
1713                 * was nothing visual before (mwidth/mheight = 0).
1714                 * Else it gets scaled.
1715                 */
1716
1717                if ((movPtr->mwidth == 0) || (movPtr->mheight == 0)) {
1718                    Rect            aRect;
1719
1720                    GetMovieBox( aMovie, &aRect );
1721                    movPtr->mwidth = aRect.right - aRect.left;
1722                    movPtr->mheight = aRect.bottom - aRect.top;
1723                    sizeMayChange = true;
1724                }
1725		if (movPtr->aController != NULL) {
1726		    MCDoAction( movPtr->aController,  mcActionMovieEdited, NULL );
1727		    MCMovieChanged( movPtr->aController, aMovie );
1728		    if (sizeMayChange) {
1729			MoviePlayerWorldChanged( clientData );
1730		    }
1731		} else {
1732		    MoviePlayerWorldChanged( clientData );
1733		}
1734	    } else {
1735		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1736		result = TCL_ERROR;
1737	    }
1738	    break;
1739        }
1740
1741        case kMovieCmdPicture: {
1742
1743    	    /*
1744    	     * Make a tk image from a time in a movie.
1745    	     */
1746
1747	    if (aMovie ) {
1748		int 		dstWidth = 0;
1749		int 		dstHeight = 0;
1750		char		*charPtr;
1751
1752		if (objc < 4) {
1753		    Tcl_WrongNumArgs( interp, 2, objv,
1754				     "time imageName ?-width width -height height?" );
1755		    result = TCL_ERROR;
1756		    goto error;
1757		}
1758
1759                /*
1760                 * If not visual then error.
1761                 */
1762
1763		if (GetMovieIndTrackType( aMovie, 1, VisualMediaCharacteristic,
1764					 movieTrackCharacteristic ) == NULL) {
1765		    Tcl_SetObjResult( interp, Tcl_NewStringObj(
1766							       "No visual media characteristics in movie", -1 ) );
1767		    result = TCL_ERROR;
1768		    goto error;
1769		}
1770		if (Tcl_GetIntFromObj( interp, objv[2], &intValue ) != TCL_OK) {
1771		    Tcl_AddErrorInfo( interp, "\n	(processing time value)" );
1772		    result = TCL_ERROR;
1773		    goto error;
1774		}
1775		movTime = intValue;
1776		thePic = GetMoviePict( aMovie, movTime );
1777		if (thePic == NULL) {
1778                    CheckAndSetErrorResult( interp, noErr );
1779		    result = TCL_ERROR;
1780		    goto error;
1781		}
1782
1783		/* Process any -width and -height options. */
1784		for (iarg = 4; iarg < objc; iarg = iarg + 2) {
1785		    charPtr = Tcl_GetString( objv[iarg] );
1786		    if (strcmp(charPtr, "-width") == 0) {
1787			if (Tcl_GetIntFromObj( interp, objv[iarg+1], &dstWidth )
1788			    != TCL_OK) {
1789			    Tcl_AddErrorInfo( interp, "\n	(processing -width value)" );
1790			    result = TCL_ERROR;
1791			    goto error;
1792			}
1793		    } else if (strcmp(charPtr, "-height") == 0) {
1794			if (Tcl_GetIntFromObj( interp, objv[iarg+1], &dstHeight )
1795			    != TCL_OK) {
1796			    Tcl_AddErrorInfo( interp, "\n	(processing -height value)" );
1797			    result = TCL_ERROR;
1798			    goto error;
1799			}
1800		    } else {
1801			Tcl_SetObjResult( interp, Tcl_NewStringObj(
1802								   "unrecognized option", -1 ) );
1803			result = TCL_ERROR;
1804			goto error;
1805		    }
1806		}
1807		result = ConvertPictureToTkPhoto( interp, thePic,
1808						 dstWidth, dstHeight, Tcl_GetString(objv[3]) );
1809		KillPicture( thePic );
1810	    } else {
1811		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1812		result = TCL_ERROR;
1813	    }
1814	    break;
1815        }
1816
1817        case kMovieCmdPlay: {
1818
1819    	    /*
1820    	     * Play the movie from its present position.
1821    	     */
1822
1823	    if (aMovie ) {
1824		if (!Tk_IsMapped(movPtr->tkwin) &&
1825		    (GetMovieIndTrackType( aMovie, 1, VisualMediaCharacteristic,
1826					  movieTrackCharacteristic ) != NULL)) {
1827		    Tcl_SetObjResult( interp,
1828				     Tcl_NewStringObj( "Movie not displayed", -1 ) );
1829		    result = TCL_ERROR;
1830		} else {
1831		    if (movPtr->aController != NULL) {
1832			rate = X2Fix(movPtr->preferredRate);
1833			MCDoAction( movPtr->aController, mcActionPlay, (void *) rate );
1834		    } else {
1835
1836			/*
1837			 * If at end, set movie time to beginning before playing.
1838			 */
1839
1840			if (IsMovieDone( aMovie )) {
1841			    if (GetMoviePreferredRate(aMovie) > 0) {
1842				SetMovieTimeValue( aMovie, 0 );
1843			    } else {
1844				SetMovieTimeValue( aMovie, GetMovieDuration(aMovie) );
1845			    }
1846			}
1847			StartMovie( aMovie );
1848		    }
1849		}
1850	    } else {
1851		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1852		result = TCL_ERROR;
1853	    }
1854	    break;
1855        }
1856
1857        case kMovieCmdPlayHints: {
1858	    if (aMovie != NULL) {
1859                int 	hintFlag = 0L;
1860
1861                flags = 0L;
1862                mask = 0L;
1863
1864                for (iarg = 2; iarg < objc; iarg += 2) {
1865		    if (Tcl_GetIndexFromObj( interp, objv[iarg], allMoviePlayHintsOpts,
1866					    "playhints option", TCL_EXACT, &optIndex ) != TCL_OK ) {
1867			result = TCL_ERROR;
1868			goto error;
1869		    }
1870		    if (iarg + 1 == objc) {
1871			resultObjPtr = Tcl_GetObjResult( interp );
1872			Tcl_AppendStringsToObj( resultObjPtr, "value for \"",
1873					       Tcl_GetString(objv[iarg]), "\"missing",
1874					       (char *) NULL );
1875			result = TCL_ERROR;
1876			goto error;
1877		    }
1878
1879                    /*
1880                     * All values are boolean.
1881                     */
1882                    if (Tcl_GetBooleanFromObj( interp, objv[iarg+1],
1883					      &boolValue ) != TCL_OK) {
1884                        Tcl_AddErrorInfo( interp,
1885					 "\n	(processing -playhints option)" );
1886                        result = TCL_ERROR;
1887                        goto error;
1888                        break;
1889                    }
1890
1891		    switch (optIndex) {
1892    	    	    	case kMoviePlayHintsOptScrubMode: {
1893                            hintFlag = hintsScrubMode;
1894                        }
1895    	    	    	case kMoviePlayHintsOptUseSoundInterp: {
1896                            hintFlag = hintsUseSoundInterp;
1897                        }
1898    	    	    	case kMoviePlayHintsOptAllowInterlace: {
1899                            hintFlag = hintsAllowInterlace;
1900                        }
1901    	    	    	case kMoviePlayHintsOptAllowBlacklining: {
1902                            hintFlag = hintsAllowBlacklining;
1903                        }
1904    	    	    	case kMoviePlayHintsOptDontPurge: {
1905                            hintFlag = hintsDontPurge;
1906                        }
1907    	    	    	case kMoviePlayHintsOptInactive: {
1908                            hintFlag = hintsInactive;
1909                        }
1910    	    	    	case kMoviePlayHintsOptHighQuality: {
1911                            hintFlag = hintsHighQuality;
1912                        }
1913                    }
1914                    if (boolValue) {
1915                        flags |= hintFlag;
1916                    } else {
1917                        flags &= ~hintFlag;
1918                    }
1919                    mask |= hintFlag;
1920                }
1921                SetMoviePlayHints( aMovie, flags, mask );
1922	    } else {
1923		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1924		result = TCL_ERROR;
1925	    }
1926	    break;
1927        }
1928
1929        case kMovieCmdRate: {
1930
1931    	    /*
1932    	     * Set or get the movie play rate.
1933    	     */
1934
1935	    if (aMovie != NULL) {
1936		if (objc == 2) {
1937		    Tcl_SetObjResult( interp, Tcl_NewDoubleObj(
1938							       Fix2X( GetMovieRate(aMovie) ) ) );
1939		} else if (objc == 3) {
1940		    if (Tcl_GetDoubleFromObj( interp, objv[2], &theRate ) != TCL_OK) {
1941			Tcl_AddErrorInfo( interp,
1942					 "\n	(processing rate command)" );
1943		    } else {
1944			rate = X2Fix(theRate);
1945			SetMovieRate( aMovie, rate );
1946		    }
1947		} else {
1948		    Tcl_WrongNumArgs( interp, 2, objv, "?rate?" );
1949		    result = TCL_ERROR;
1950		    goto error;
1951		}
1952		if (movPtr->aController != NULL) {
1953		    MCMovieChanged( movPtr->aController, aMovie );
1954		}
1955	    } else {
1956		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
1957		result = TCL_ERROR;
1958	    }
1959	    break;
1960        }
1961
1962        case kMovieCmdSave: {
1963
1964    	    /*
1965    	     * Save the movie. If there was a movie resource in the orig movie, save in
1966    	     * resource fork, else save in the data fork.
1967    	     */
1968
1969	    if (aMovie != NULL) {
1970		if (objc != 2) {
1971		    Tcl_WrongNumArgs( interp, 2, objv, NULL );
1972		    result = TCL_ERROR;
1973		    goto error;
1974		}
1975		if (movPtr->fileNamePtr == NULL) {
1976		    Tcl_SetObjResult( interp, Tcl_NewStringObj(
1977							       "Have no file name. Use \"saveas\" or \"flatten\" instead", -1 ) );
1978		    result = TCL_ERROR;
1979		    goto error;
1980    	        }
1981
1982		/*
1983		 * Translate file name to FSSpec.
1984		 */
1985
1986		err = QTTclNativePathNameToFSSpec( movPtr->interp,
1987						  Tcl_GetString( movPtr->fileNamePtr ), &fss );
1988
1989		if (err == fnfErr) {
1990		    Tcl_AppendStringsToObj( Tcl_GetObjResult( interp ),
1991					   "File not found: ",
1992					   Tcl_GetString( movPtr->fileNamePtr ),
1993					   (char *) NULL );
1994		    result = TCL_ERROR;
1995		    goto error;
1996		}
1997		if (err != noErr) {
1998		    Tcl_SetObjResult( interp, Tcl_NewStringObj(
1999							       "Unable to make a FSSpec from file name", -1 ) );
2000		    result = TCL_ERROR;
2001		    goto error;
2002		}
2003#if 0
2004extern OSErr
2005OpenMovieStorage(
2006  Handle         dataRef,
2007  OSType         dataRefType,
2008  long           flags,
2009  DataHandler *  outDataHandler)
2010#endif
2011		if (noErr != OpenMovieFile( &fss, &movieResFile, fsRdWrPerm )) {
2012                    CheckAndSetErrorResult( interp, noErr );
2013		    result = TCL_ERROR;
2014		    goto error;
2015		}
2016		if (movPtr->resourceNumber == -1) {
2017
2018    	            /* We have probably the movie in the data fork. */
2019    	            resId = movieInDataForkResID;
2020		} else {
2021
2022		    /* Save in the resource fork. */
2023		    resId = movPtr->resourceNumber;
2024		}
2025
2026		if (noErr != UpdateMovieResource( aMovie, movieResFile, resId, NULL )) {
2027                    CheckAndSetErrorResult( interp, noErr );
2028		    result = TCL_ERROR;
2029		}
2030#if 0
2031extern OSErr
2032CloseMovieStorage(DataHandler dh)
2033#endif
2034		CloseMovieFile(movieResFile);
2035
2036	    } else {
2037		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2038		result = TCL_ERROR;
2039	    }
2040	    break;
2041        }
2042
2043        case kMovieCmdSaveas: {
2044
2045    	    /*
2046    	     * Save the movie in a new file.
2047    	     */
2048
2049	    if (aMovie != NULL) {
2050		Handle	    dataRef = NULL;
2051		OSType	    dataRefType;
2052		DataHandler dataHandler = 0;
2053
2054		if (objc != 3) {
2055		    Tcl_WrongNumArgs( interp, 2, objv, "fileName" );
2056		    result = TCL_ERROR;
2057		    goto error;
2058		}
2059
2060		result = QTTclNewDataRefFromUTF8Obj(interp, objv[2], &dataRef, &dataRefType);
2061		if (result != TCL_OK) {
2062		    goto error;
2063		}
2064		flags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile |
2065			createMovieFileDontCreateMovie;
2066		err = CreateMovieStorage(dataRef, dataRefType, ksigMoviePlayer,
2067			smCurrentScript, flags, &dataHandler, NULL);
2068		if (noErr != err) {
2069                    CheckAndSetErrorResult( interp, noErr );
2070		    result = TCL_ERROR;
2071		    goto error;
2072		}
2073		resId = movieInDataForkResID;
2074		if (noErr != AddMovieToStorage(aMovie, dataHandler)) {
2075		    if (dataHandler) {
2076			err = CloseMovieStorage(dataHandler);
2077		    }
2078		    if (dataRef) {
2079			DisposeHandle(dataRef);
2080		    }
2081		    CheckAndSetErrorResult( interp, noErr );
2082		    result = TCL_ERROR;
2083		    goto error;
2084		}
2085		movPtr->resourceNumber = resId;
2086
2087		if (movPtr->fileNamePtr != NULL) {
2088		    Tcl_DecrRefCount( movPtr->fileNamePtr );
2089		}
2090		movPtr->fileNamePtr = objv[2];
2091		Tcl_IncrRefCount( movPtr->fileNamePtr );
2092		movPtr->filename = Tcl_GetString( objv[2] );
2093		if (dataHandler) {
2094		    err = CloseMovieStorage(dataHandler);
2095		}
2096		ClearMovieChanged( aMovie );
2097		if (dataRef) {
2098		    DisposeHandle(dataRef);
2099		}
2100	    } else {
2101		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2102		result = TCL_ERROR;
2103	    }
2104	    break;
2105        }
2106
2107        case kMovieCmdSelect: {
2108
2109    	    /*
2110    	     * Select a section of movie. A movie selection shall invalidate any track selection.
2111    	     */
2112
2113	    if (aMovie != NULL) {
2114		if (objc == 2) {
2115
2116            	    /*
2117            	     * Get the movie selection.
2118            	     */
2119
2120		    if (aMovie != NULL) {
2121			GetMovieSelection( aMovie, &movTime, &movDuration );
2122			if (noErr != CheckAndSetErrorResult( interp, noErr )) {
2123                            result = TCL_ERROR;
2124                            goto error;
2125                        }
2126			listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
2127			Tcl_ListObjAppendElement( interp, listObjPtr,
2128						 Tcl_NewLongObj(movTime) );
2129			Tcl_ListObjAppendElement( interp, listObjPtr,
2130						 Tcl_NewLongObj(movDuration) );
2131                        Tcl_SetObjResult( interp, listObjPtr );
2132		    } else {
2133			Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2134			result = TCL_ERROR;
2135		    }
2136
2137		} else if (objc == 3) {
2138                    char *strObjv2 = Tcl_GetString(objv[2]);
2139
2140		    if (strcmp(strObjv2, "clear") == 0) {
2141			result = LogUndoState( movPtr, interp );
2142			ClearMovieSelection( aMovie );
2143			if (movPtr->aController != NULL) {
2144			    MCDoAction( movPtr->aController,  mcActionMovieEdited, NULL );
2145			    MCMovieChanged( movPtr->aController, aMovie );
2146			} else {
2147			    MoviePlayerWorldChanged( clientData );
2148			}
2149		    } else if (strcmp(strObjv2, "all") == 0) {
2150			SetMovieSelection( aMovie, 0, GetMovieDuration( aMovie ) );
2151			if (noErr != CheckAndSetErrorResult( interp, noErr )) {
2152                            result = TCL_ERROR;
2153                            goto error;
2154                        }
2155    	                movPtr->trackSelect->trackID = 0;
2156		    } else if (strcmp(strObjv2, "end") == 0) {
2157			SetMovieSelection( aMovie, GetMovieTime( aMovie, NULL ), 0 );
2158			if (noErr != CheckAndSetErrorResult( interp, noErr )) {
2159                            result = TCL_ERROR;
2160                            goto error;
2161                        }
2162    	                movPtr->trackSelect->trackID = 0;
2163		    } else if (strcmp(strObjv2, "none") == 0) {
2164			SetMovieSelection( aMovie, GetMovieTime( aMovie, NULL ), 0 );
2165			if (noErr != CheckAndSetErrorResult( interp, noErr )) {
2166                            result = TCL_ERROR;
2167                            goto error;
2168                        }
2169    	                movPtr->trackSelect->trackID = 0;
2170		    } else {
2171                        if (GetMovieStartTimeFromObj( interp, aMovie, objv[2], &longValue ) != TCL_OK) {
2172                            goto error;
2173                        }
2174                        movTime = longValue;
2175			movDuration = 0;
2176			SetMovieSelection( aMovie, movTime, movDuration );
2177			if (noErr != CheckAndSetErrorResult( interp, noErr )) {
2178                            result = TCL_ERROR;
2179                            goto error;
2180                        }
2181    	                movPtr->trackSelect->trackID = 0;
2182                    }
2183		} else if (objc == 4) {
2184                    if (GetMovieStartTimeFromObj( interp, aMovie, objv[2], &longValue ) != TCL_OK) {
2185                        goto error;
2186                    }
2187                    movTime = longValue;
2188                    if (GetMovieDurationFromObj( interp, aMovie, objv[3], movTime, &longValue ) != TCL_OK) {
2189                        goto error;
2190                    }
2191                    movDuration = longValue;
2192		    SetMovieSelection( aMovie, movTime, movDuration );
2193		    if (noErr != CheckAndSetErrorResult( interp, noErr )) {
2194                        result = TCL_ERROR;
2195                        goto error;
2196                    }
2197    	            movPtr->trackSelect->trackID = 0;
2198		} else {
2199		    Tcl_WrongNumArgs( interp, 2, objv, "?startTime? ?duration?" );
2200		    result = TCL_ERROR;
2201                    goto error;
2202		}
2203		if (movPtr->aController != NULL) {
2204		    MCMovieChanged( movPtr->aController, aMovie );
2205		}
2206	    } else {
2207		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2208		result = TCL_ERROR;
2209	    }
2210	    break;
2211        }
2212
2213        case kMovieCmdSetpreferredrate: {
2214
2215    	    /*
2216    	     * Set the movie's preferred play rate. Now obsolete!
2217    	     */
2218
2219	    if (aMovie != NULL) {
2220		if (objc == 3) {
2221		    if (Tcl_GetDoubleFromObj( interp, objv[2], &theRate ) != TCL_OK) {
2222			Tcl_AddErrorInfo( interp,
2223					 "\n	(processing rate command)" );
2224		    } else {
2225			rate = X2Fix(theRate);
2226			movPtr->preferredRate = theRate;
2227			if (movPtr->aController != NULL) {
2228
2229			    /* For a controller, the movie's play rate is set from the play action. */
2230
2231			    SetMoviePreferredRate( aMovie, rate );
2232			} else {
2233			    SetMoviePreferredRate( aMovie, rate );
2234			}
2235		    }
2236		} else {
2237		    Tcl_WrongNumArgs( interp, 2, objv, "rate");
2238		    result = TCL_ERROR;
2239                    goto error;
2240		}
2241		if (movPtr->aController != NULL) {
2242		    MCMovieChanged( movPtr->aController, aMovie );
2243		}
2244	    } else {
2245		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2246		result = TCL_ERROR;
2247	    }
2248	    break;
2249        }
2250
2251        case kMovieCmdSetrate: {
2252
2253    	    /*
2254    	     * Set the movie play rate. Now obsolete!
2255    	     */
2256
2257	    if (aMovie != NULL) {
2258		if (objc == 3) {
2259		    if (Tcl_GetDoubleFromObj( interp, objv[2], &theRate ) != TCL_OK) {
2260			Tcl_AddErrorInfo( interp,
2261					 "\n	(processing rate command)" );
2262		    } else {
2263			rate = X2Fix(theRate);
2264			SetMovieRate( aMovie, rate );
2265		    }
2266		} else {
2267		    Tcl_WrongNumArgs( interp, 2, objv, "rate");
2268		    result = TCL_ERROR;
2269                    goto error;
2270		}
2271		if (movPtr->aController != NULL) {
2272		    MCMovieChanged( movPtr->aController, aMovie );
2273		}
2274	    } else {
2275		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2276		result = TCL_ERROR;
2277	    }
2278	    break;
2279        }
2280
2281        case kMovieCmdSettime: {
2282
2283    	    /*
2284    	     * Set the movie time. Now obsolete, use "time".
2285    	     */
2286
2287	    if (objc != 3) {
2288		Tcl_WrongNumArgs( interp, 2, objv, "time" );
2289		result = TCL_ERROR;
2290	    } else {
2291		if (aMovie != NULL) {
2292		    if (Tcl_GetIntFromObj( interp, objv[2], &intValue ) != TCL_OK) {
2293			Tcl_AddErrorInfo( interp, "\n	(processing time value)" );
2294			result = TCL_ERROR;
2295			goto error;
2296		    }
2297		    movTime = intValue;
2298		    SetMovieTimeValue( aMovie, movTime );
2299		    if (noErr != CheckAndSetErrorResult( interp, noErr )) {
2300                        result = TCL_ERROR;
2301                        goto error;
2302                    }
2303		    if (movPtr->aController != NULL) {
2304			MCMovieChanged(movPtr->aController, aMovie);
2305		    }
2306		} else {
2307		    Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2308		    result = TCL_ERROR;
2309		}
2310	    }
2311	    break;
2312        }
2313
2314        case kMovieCmdSize: {
2315
2316    	    /*
2317    	     * Return the movies "natural" size excluding any movie controller.
2318    	     */
2319
2320	    if (aMovie != NULL) {
2321		listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
2322		Tcl_ListObjAppendElement( interp, listObjPtr,
2323					 Tcl_NewLongObj(movPtr->mwidth) );
2324		Tcl_ListObjAppendElement( interp, listObjPtr,
2325					 Tcl_NewLongObj(movPtr->mheight) );
2326		Tcl_SetObjResult( interp, listObjPtr );
2327	    } else {
2328		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2329		result = TCL_ERROR;
2330	    }
2331	    break;
2332        }
2333
2334        case kMovieCmdUndocumeted1: {
2335
2336    	    /*
2337    	     * Sprite Subcommand (not implemented).
2338    	     */
2339
2340	    if (aMovie != NULL) {
2341		ProcessSpriteSubcmd(clientData, interp, objc-2, objv+2);
2342	    } else {
2343		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2344		result = TCL_ERROR;
2345	    }
2346	    break;
2347        }
2348
2349        case kMovieCmdStep: {
2350
2351	    /*
2352	     * Step through the movie.
2353	     */
2354
2355            long 	numSteps = 1;
2356            if (objc > 2) {
2357		if (Tcl_GetIntFromObj( interp, objv[2], &intValue ) != TCL_OK) {
2358		    Tcl_AddErrorInfo( interp, "\n	(processing numSteps value)" );
2359		    result = TCL_ERROR;
2360		    goto error;
2361		}
2362		numSteps = intValue;
2363            }
2364            if (aMovie != NULL) {
2365                if (movPtr->aController != NULL) {
2366                    MCDoAction( movPtr->aController, mcActionStep,
2367			       (void*) numSteps );
2368		    MCMovieChanged( movPtr->aController, aMovie );
2369                } else {
2370                    long 		i;
2371                    TimeValue 	intTime;
2372                    TimeValue 	intDur;
2373                    TimeValue 	curTime;
2374                    long 		num = numSteps;
2375
2376                    if (num < 0) {
2377                        num = 0 - num;
2378                    }
2379                    for (i = 0; i < num; i++) {
2380                        curTime = GetMovieTime( aMovie, NULL );
2381                        GetMovieNextInterestingTime( aMovie, nextTimeStep,
2382						    0, 0, curTime, numSteps, &intTime, &intDur );
2383                        SetMovieTimeValue( aMovie, intTime );
2384                    }
2385                }
2386            } else {
2387                Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2388                result = TCL_ERROR;
2389            }
2390	    break;
2391        }
2392
2393        case kMovieCmdStop: {
2394
2395    	    /*
2396    	     * Stop the movie from playing.
2397    	     */
2398
2399	    if (aMovie != NULL) {
2400		if (movPtr->aController != NULL) {
2401		    MCDoAction( movPtr->aController, mcActionPlay, 0 );
2402		    MCDoAction( movPtr->aController, mcActionSetLooping, (void *) false );
2403		} else {
2404		    StopMovie(aMovie);
2405		}
2406	    } else {
2407		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2408		result = TCL_ERROR;
2409	    }
2410	    break;
2411        }
2412
2413        case kMovieCmdTilt: {
2414
2415    	    /*
2416    	     * Set or get tilt angle of QTVR pano.
2417    	     */
2418
2419	    if (objc > 3) {
2420		Tcl_WrongNumArgs( interp, 2, objv, "?angle?" );
2421		result = TCL_ERROR;
2422		goto error;
2423	    }
2424	    if (!gHasQTVRManager) {
2425		Tcl_SetObjResult( interp, Tcl_NewStringObj(
2426							   "Couldn't find the QTVR manager on this system", -1 ) );
2427		result = TCL_ERROR;
2428		goto error;
2429	    }
2430	    if (aMovie != NULL) {
2431		double   angle;
2432
2433		if (movPtr->qtvrInstance == NULL) {
2434		    Tcl_SetObjResult( interp, Tcl_NewStringObj(
2435							       "Couldn't identify this movie as a VR movie", -1 ) );
2436		    result = TCL_ERROR;
2437		    goto error;
2438		}
2439		if (objc == 3) {
2440		    if (Tcl_GetDoubleFromObj( interp, objv[2], &angle ) != TCL_OK) {
2441			Tcl_AddErrorInfo( interp,
2442					 "\n	(processing tilt command)" );
2443		    } else {
2444			SetPanoramaByDegrees( movPtr->qtvrInstance, kDirUp, (float) angle );
2445		    }
2446                } else {
2447                    angle = QTVRGetTiltAngle( movPtr->qtvrInstance );
2448		    Tcl_SetObjResult( interp, Tcl_NewDoubleObj( angle ) );
2449                }
2450	    } else {
2451		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2452		result = TCL_ERROR;
2453            }
2454	    break;
2455        }
2456
2457        case kMovieCmdTime: {
2458
2459    	    /*
2460    	     * Set or get the movie time.
2461    	     */
2462
2463            if (objc == 2) {
2464		if (aMovie != NULL) {
2465		    Tcl_SetObjResult( interp, Tcl_NewLongObj(
2466							     GetMovieTime( aMovie, NULL ) ) );
2467		} else {
2468		    Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2469		    result = TCL_ERROR;
2470		}
2471            } else if (objc == 3) {
2472		if (aMovie != NULL) {
2473		    if (Tcl_GetIntFromObj( interp, objv[2], &intValue ) != TCL_OK) {
2474			Tcl_AddErrorInfo( interp, "\n	(processing time value)" );
2475			result = TCL_ERROR;
2476			goto error;
2477		    }
2478		    movTime = intValue;
2479		    SetMovieTimeValue( aMovie, movTime );
2480		    if (noErr != CheckAndSetErrorResult( interp, noErr )) {
2481                        result = TCL_ERROR;
2482                        goto error;
2483                    }
2484		    if (movPtr->aController != NULL) {
2485			MCMovieChanged( movPtr->aController, aMovie );
2486                    }
2487		} else {
2488		    Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2489		    result = TCL_ERROR;
2490		}
2491	    } else {
2492		Tcl_WrongNumArgs( interp, 2, objv, "?time?" );
2493		result = TCL_ERROR;
2494	    }
2495	    break;
2496        }
2497
2498        case kMovieCmdTimeCode: {
2499
2500    	    /*
2501    	     * The tracks sub command.
2502    	     */
2503
2504	    if (aMovie != NULL) {
2505		if (TCL_OK != TimeCodeCmd( interp, movPtr, objc - 2, objv + 2 )) {
2506		    result = TCL_ERROR;
2507                }
2508	    } else {
2509		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2510		result = TCL_ERROR;
2511	    }
2512	    break;
2513        }
2514
2515        case kMovieCmdTracks: {
2516
2517    	    /*
2518    	     * The tracks sub command.
2519    	     */
2520
2521	    if (aMovie != NULL) {
2522		if (TCL_OK != ProcessTracksObjCmd( clientData, interp, objc - 2, objv + 2 )) {
2523		    result = TCL_ERROR;
2524                }
2525	    } else {
2526		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2527		result = TCL_ERROR;
2528	    }
2529	    break;
2530        }
2531
2532        case kMovieCmdUndo: {
2533
2534    	    /*
2535    	     * Either checkpoint or undo a change.
2536    	     */
2537
2538	    if (aMovie != NULL) {
2539		if (objc == 3) {
2540		    if (strcmp(Tcl_GetString(objv[2]), "set") == 0) {
2541			result = LogUndoState( movPtr, interp );
2542		    } else {
2543			if (Tcl_GetIntFromObj( interp, objv[2], &intValue )
2544			    != TCL_OK) {
2545			    Tcl_AddErrorInfo( interp,
2546					     "\n	(processing undoLevel value)" );
2547			    result = TCL_ERROR;
2548			    goto error;
2549			}
2550			undoLevel = intValue;
2551			if (undoLevel < 0 || undoLevel >= movPtr->undoCount) {
2552			    Tcl_SetObjResult( interp, Tcl_NewStringObj(
2553								       "Invalid Undo Level", -1 ) );
2554			    result = TCL_ERROR;
2555			    goto error;
2556			}
2557			err = UseMovieEditState( aMovie, movPtr->editStates[undoLevel] );
2558			if (err != noErr) {
2559                            CheckAndSetErrorResult( interp, err );
2560			    result = TCL_ERROR;
2561			    goto error;
2562			}
2563			for (i = undoLevel; i < movPtr->undoCount; i++) {
2564			    DisposeMovieEditState( movPtr->editStates[i] );
2565			}
2566			movPtr->undoCount = undoLevel;
2567			if (movPtr->aController != NULL) {
2568			    MCDoAction( movPtr->aController,  mcActionMovieEdited, NULL );
2569			    MCMovieChanged( movPtr->aController, aMovie );
2570			}
2571		    }
2572                } else if (objc == 2) {
2573                    Tcl_SetObjResult( interp, Tcl_NewIntObj( movPtr->undoCount ) );
2574		} else {
2575		    Tcl_WrongNumArgs( interp, 2, objv, "set | level" );
2576		    result = TCL_ERROR;
2577		}
2578	    } else {
2579		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2580		result = TCL_ERROR;
2581	    }
2582	    break;
2583        }
2584
2585        case kMovieCmdUserData: {
2586
2587    	    /*
2588    	     * The userdata command.
2589    	     */
2590
2591	    if (aMovie != NULL) {
2592		UserData 		userData = 0;
2593
2594		userData = GetMovieUserData( aMovie );
2595		if (userData == NULL) {
2596		    CheckAndSetErrorResult( interp, noErr );
2597		    result = TCL_ERROR;
2598		    goto error;
2599		}
2600		if (objc == 2) {
2601		    if (TCL_OK != GetUserDataListCmd( userData, interp )) {
2602			result = TCL_ERROR;
2603		    }
2604		} else if ((objc > 2) && (objc % 2 == 0)) {
2605		    if (TCL_OK != SetUserDataListCmd( userData, interp, objc - 2, objv + 2 )) {
2606			result = TCL_ERROR;
2607		    }
2608                } else {
2609		    Tcl_WrongNumArgs( interp, 2, objv, "?options?" );
2610		    result = TCL_ERROR;
2611                }
2612	    } else {
2613		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2614		result = TCL_ERROR;
2615	    }
2616	    break;
2617        }
2618
2619        case kMovieCmdUndocumeted2: {
2620
2621    	    /*
2622    	     * Vector Subcommand.
2623    	     */
2624
2625	    if (aMovie != NULL) {
2626		ProcessVectorSubcmd(clientData, interp, objc-2, objv+2);
2627	    } else {
2628		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
2629		result = TCL_ERROR;
2630	    }
2631        }
2632    }
2633
2634error:
2635    Tcl_Release((ClientData) movPtr);
2636    return result;
2637}
2638
2639/*
2640 *----------------------------------------------------------------------
2641 *
2642 * ComressMovieCmd --
2643 *
2644 *	Compress the movie.
2645 *
2646 * Results:
2647 *	The return value is a standard Tcl result.  If TCL_ERROR is
2648 *	returned, then the interp's result contains an error message.
2649 *
2650 * Side effects:
2651 *	None.
2652 *
2653 *----------------------------------------------------------------------
2654 */
2655
2656static int
2657ComressMovieCmd(Tcl_Interp *interp,
2658	int objc, Tcl_Obj *CONST objv[],
2659	MoviePlayer *movPtr)
2660{
2661    Handle		dataRef = NULL;
2662    OSType		dataRefType;
2663    ComponentInstance	videoCodec;
2664    Boolean		canceled;
2665    OSErr		err = noErr;
2666    int			result = TCL_OK;
2667
2668    /*
2669     * Translate file name to Data Reference.
2670     */
2671    result = QTTclNewDataRefFromUTF8Obj(interp, objv[2],
2672	    &dataRef, &dataRefType);
2673    if (result != TCL_OK) {
2674	goto error;
2675    }
2676    videoCodec = NULL;
2677    if (objc == 4) {
2678
2679	/* Have a codec to work with. We do not need 'EndianU32_NtoB'! */
2680
2681	videoCodec = OpenDefaultComponent( MovieExportType,
2682					  MovieFileType );
2683	if (videoCodec == NULL) {
2684	    Tcl_SetObjResult( interp,
2685			     Tcl_NewStringObj( "Codec not found", -1 ) );
2686	    result = TCL_ERROR;
2687	    goto error;
2688	}
2689	MovieExportDoUserDialog( videoCodec, movPtr->aMovie, NULL,
2690				0, GetMovieDuration(movPtr->aMovie), &canceled );
2691	if (canceled) {
2692
2693	    /* This is actually not an error, just to clean up things. */
2694	    goto error;
2695	}
2696    }
2697    err = ConvertMovieToDataRef(movPtr->aMovie, 0, dataRef, dataRefType,
2698				MovieFileType, ksigMoviePlayer, 0, videoCodec);
2699    if (noErr != err) {
2700	CheckAndSetErrorResult( interp, err );
2701	result = TCL_ERROR;
2702	goto error;
2703    }
2704
2705error:
2706    if (dataRef) {
2707	DisposeHandle(dataRef);
2708    }
2709    if (videoCodec != NULL) {
2710	CloseComponent( videoCodec );
2711    }
2712    return result;
2713}
2714
2715/*
2716 *----------------------------------------------------------------------
2717 *
2718 * FlattenMovieCmd --
2719 *
2720 *	Flatten the movie into the datafork of a new file.
2721 *	A separate mechanism is used for QTVR panos.
2722 *
2723 * Results:
2724 *	The return value is a standard Tcl result.  If TCL_ERROR is
2725 *	returned, then the interp's result contains an error message.
2726 *
2727 * Side effects:
2728 *	New movie may be assiciated with movie widget.
2729 *
2730 *----------------------------------------------------------------------
2731 */
2732
2733static int
2734FlattenMovieCmd(Tcl_Interp *interp,
2735	int objc, Tcl_Obj *CONST objv[],
2736	MoviePlayer *movPtr)
2737{
2738    int			iarg;
2739    int			optIndex;
2740    int			boolValue;
2741    int			showDialog;
2742    long		flags;
2743    Tcl_Obj		*obj;
2744    Handle		dataRef = NULL;
2745    OSType		dataRefType;
2746    ComponentDescription    desc;
2747    MovieExportComponent    exporter = NULL;
2748    OSErr		err = noErr;
2749    int			result = TCL_OK;
2750
2751    /*
2752     * Set default sub options for flatten.
2753     */
2754
2755    flags = flattenAddMovieToDataFork | flattenForceMovieResourceBeforeMovieData;
2756    showDialog = false;
2757
2758    for (iarg = 3; iarg < objc; iarg += 2) {
2759	if (Tcl_GetIndexFromObj( interp, objv[iarg], allMovieFlattenOpts,
2760				"flatten option", TCL_EXACT, &optIndex ) != TCL_OK ) {
2761	    result = TCL_ERROR;
2762	    goto error;
2763	}
2764	if (iarg + 1 == objc) {
2765	    Tcl_AppendStringsToObj( Tcl_GetObjResult(interp),
2766		    "value for \"", Tcl_GetString(objv[iarg]), "\"missing",
2767		    (char *) NULL );
2768	    result = TCL_ERROR;
2769	    goto error;
2770	}
2771
2772	switch (optIndex) {
2773
2774	    case kMovieFlattenOptDialog: {
2775		if (Tcl_GetBooleanFromObj( interp, objv[iarg+1],
2776					  &showDialog ) != TCL_OK) {
2777		    Tcl_AddErrorInfo( interp,
2778				     "\n	(processing -dialog option)" );
2779		    result = TCL_ERROR;
2780		    goto error;
2781		}
2782		if (showDialog) {
2783		    flags |= showUserSettingsDialog;
2784		}
2785		break;
2786	    }
2787
2788	    case kMovieFlattenOptDontInterleave: {
2789		if (Tcl_GetBooleanFromObj( interp, objv[iarg+1],
2790					  &boolValue ) != TCL_OK) {
2791		    Tcl_AddErrorInfo( interp,
2792				     "\n	(processing -dontinterleave option)" );
2793		    result = TCL_ERROR;
2794		    goto error;
2795		}
2796		if (boolValue) {
2797		    flags |= flattenDontInterleaveFlatten;
2798		}
2799		break;
2800	    }
2801
2802	    case kMovieFlattenOptForceResourceBeforeData: {
2803		if (Tcl_GetBooleanFromObj( interp, objv[iarg+1],
2804					  &boolValue ) != TCL_OK) {
2805		    Tcl_AddErrorInfo( interp,
2806				     "\n	(processing -forceresourcebeforedata option)" );
2807		    result = TCL_ERROR;
2808		    goto error;
2809		}
2810		if (!boolValue) {
2811		    flags &= ~flattenForceMovieResourceBeforeMovieData;
2812		}
2813		break;
2814	    }
2815	}
2816    }
2817
2818    /*
2819     * Translate file name to Data Reference.
2820     */
2821    result = QTTclNewDataRefFromUTF8Obj(interp, objv[2],
2822	    &dataRef, &dataRefType);
2823    if (result != TCL_OK) {
2824	goto error;
2825    }
2826    if (showDialog || movPtr->qtvrInstance != NULL) {
2827	flags = createMovieFileDeleteCurFile | movieToFileOnlyExport | movieFileSpecValid;
2828	if (showDialog) {
2829	    flags |= showUserSettingsDialog;
2830	}
2831
2832	if (movPtr->qtvrInstance != NULL) {
2833	    desc.componentType = MovieExportType;
2834	    desc.componentSubType = MovieFileType;
2835	    desc.componentFlags = 0;
2836	    desc.componentFlagsMask = 0;
2837	    /* 'vrwe' */
2838	    desc.componentManufacturer = kQTVRFlattenerManufacturer;
2839	    exporter = OpenComponent( FindNextComponent( NULL, &desc ) );
2840	    if (exporter == NULL) {
2841		Tcl_SetObjResult( interp, Tcl_NewStringObj(
2842			"Failed finding a QTVR flattener component", -1 ) );
2843		result = TCL_ERROR;
2844		goto error;
2845	    }
2846	}
2847
2848	// export the movie into a file
2849	// @@@ This used to return the new FSSpec but doesn't!
2850	err = ConvertMovieToDataRef(movPtr->aMovie, /* the movie */
2851				NULL,		    /* all tracks */
2852				dataRef,	    /* the output data reference */
2853				dataRefType,	    /* the data ref type */
2854				MovieFileType,	    /* output file type */
2855				ksigMoviePlayer,    /* output file creator */
2856				flags,
2857				exporter);          /* export component */
2858	if (err == userCanceledErr) {
2859	    goto error;
2860	} else if (err != noErr) {
2861	    CheckAndSetErrorResult( interp, err );
2862	    result = TCL_ERROR;
2863	    goto error;
2864	}
2865	obj = Tcl_NewObj();
2866	result = QTTclNewUTF8ObjFromDataRef(interp, dataRef, dataRefType, &obj);
2867	if (result != TCL_OK) {
2868	    goto error;
2869	}
2870	Tcl_SetObjResult(interp, obj);
2871    } else {
2872        Movie newMovie = FlattenMovieDataToDataRef(movPtr->aMovie, flags,
2873		dataRef, dataRefType, ksigMoviePlayer,
2874		smSystemScript, createMovieFileDeleteCurFile);
2875        if (NULL != newMovie) {
2876            // we choose not to do anything with the returned movie
2877            DisposeMovie(newMovie);
2878        } else {
2879	    Tcl_SetObjResult( interp,
2880		    Tcl_NewStringObj("Failed FlattenMovieDataToDataRef", -1 ) );
2881	    result = TCL_ERROR;
2882	    goto error;
2883        }
2884	obj = Tcl_NewObj();
2885	result = QTTclNewUTF8ObjFromDataRef(interp, dataRef, dataRefType, &obj);
2886	if (result != TCL_OK) {
2887	    goto error;
2888	}
2889	Tcl_SetObjResult(interp, obj);
2890    }
2891
2892error:
2893    if (exporter) {
2894	CloseComponent(exporter);
2895    }
2896    if (dataRef) {
2897	DisposeHandle(dataRef);
2898    }
2899    return result;
2900}
2901
2902/*
2903 *----------------------------------------------------------------------
2904 *
2905 * NewMovieCmd --
2906 *
2907 *	Make new movie.
2908 *
2909 * Results:
2910 *	The return value is a standard Tcl result.  If TCL_ERROR is
2911 *	returned, then the interp's result contains an error message.
2912 *
2913 * Side effects:
2914 *	New movie may be assiciated with movie widget.
2915 *
2916 *----------------------------------------------------------------------
2917 */
2918
2919static int
2920NewMovieCmd(Tcl_Interp *interp,
2921	int objc, Tcl_Obj *CONST objv[],
2922	MoviePlayer *movPtr)
2923{
2924    int		i;
2925    long	flags;
2926    OSType	dataRefType;
2927    Handle	dataRef = NULL;
2928    DataHandler dataHandler = 0;
2929    Movie	aMovie = movPtr->aMovie;
2930    OSErr	err = noErr;
2931    int		result = TCL_OK;
2932
2933    /* get rid of the old one */
2934    RemoveMovieFromOpenMovies( movPtr );
2935
2936    if (aMovie != NULL) {
2937	for (i = 0; i < movPtr->undoCount; i++) {
2938	    DisposeMovieEditState(movPtr->editStates[i]);
2939	}
2940	movPtr->undoCount = 0;
2941	DisposeMovie( aMovie );
2942	aMovie = NULL;
2943	gMovieRefCount--;
2944    }
2945
2946    /*
2947     * Translate file name to Data Reference.
2948     */
2949    result = QTTclNewDataRefFromUTF8Obj(movPtr->interp, objv[2], &dataRef, &dataRefType);
2950    if (result != TCL_OK) {
2951	goto error;
2952    }
2953    flags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
2954
2955    /* Create a file. */
2956    err = CreateMovieStorage(dataRef, dataRefType, ksigMoviePlayer,
2957			     smCurrentScript, flags, &dataHandler, &(movPtr->aMovie));
2958    if (err != noErr) {
2959	CheckAndSetErrorResult( interp, err );
2960	movPtr->aMovie = NULL;
2961	result = TCL_ERROR;
2962	goto error;
2963    }
2964    aMovie = movPtr->aMovie;
2965    gMovieRefCount++;
2966
2967    /* Add the movie. */
2968    err = AddMovieToStorage(aMovie, dataHandler);
2969    if (err != noErr) {
2970	DataHDeleteFile(dataHandler);
2971	CheckAndSetErrorResult( interp, err );
2972	movPtr->aMovie = NULL;
2973	result = TCL_ERROR;
2974	goto error;
2975    }
2976    movPtr->resourceNumber = -1;
2977    err = CloseMovieStorage(dataHandler);
2978
2979    if (movPtr->fileNamePtr != NULL) {
2980	Tcl_DecrRefCount( movPtr->fileNamePtr );
2981    }
2982    movPtr->fileNamePtr = objv[2];
2983    Tcl_IncrRefCount( movPtr->fileNamePtr );
2984    movPtr->filename = Tcl_GetString( objv[2] );
2985
2986    Tcl_SetObjResult( interp, Tcl_NewIntObj( movPtr->resourceNumber ) );
2987    movPtr->flags |= NEWGWORLD;
2988
2989error:
2990    /* Close the storage. */
2991    if (dataHandler) {
2992	CloseMovieStorage(dataHandler);
2993    }
2994    if (dataRef) {
2995	DisposeHandle(dataRef);
2996    }
2997    return result;
2998}
2999
3000/*
3001 *----------------------------------------------------------------------
3002 *
3003 * ConfigureMoviePlayer --
3004 *
3005 *		This procedure is called to process an objv/objc list, plus
3006 *		the Tk option database, in order to configure (or
3007 *		reconfigure) a movie player widget. Many options are set via a
3008 *      movie controller so therefore it must be created just after finsished
3009 *      reading the movie.
3010 *
3011 * Results:
3012 *		The return value is a standard Tcl result.  If TCL_ERROR is
3013 *		returned, then the interp's result contains an error message.
3014 *
3015 * Side effects:
3016 *		Configuration information, such as text string, colors, font,
3017 *		etc. get set for movPtr;  old resources get freed, if there
3018 *		were any.
3019 *      Only if the configuration means that a (re)display is necessary,
3020 *      the NEWGWORLD flag is set. This triggers a call to 'MoviePlayerWorldChanged'.
3021 *
3022 *----------------------------------------------------------------------
3023 */
3024
3025static int
3026ConfigureMoviePlayer( Tcl_Interp *interp,
3027		     MoviePlayer *movPtr,
3028		     int objc,
3029		     Tcl_Obj *CONST objv[] )
3030{
3031    int         		ierror;
3032    int         		i;
3033    int         		result = TCL_OK;
3034    int 				mask = 0L;
3035    long        		optFlags;
3036    Tcl_Obj 			*errorResult = NULL;
3037    Tk_SavedOptions 	savedOptions;
3038    OSErr       		err = noErr;
3039
3040    QTTclDebugPrintf( interp, 2, "ConfigureMoviePlayer" );
3041
3042    /*
3043     * The following loop is potentially executed twice.  During the
3044     * first pass configuration options get set to their new values.
3045     * If there is an error in this pass, we execute a second pass
3046     * to restore all the options to their previous values.
3047     *
3048     * A 'continue' within this loop signals an error condition;
3049     * 'break' when everything went OK.
3050     */
3051
3052    for (ierror = 0; ierror <= 1; ierror++) {
3053	if (!ierror) {
3054	    /*
3055	     * First pass: set options to new values.
3056	     */
3057
3058	    if (Tk_SetOptions( interp, (char *) movPtr, movPtr->optionTable, objc,
3059			      objv, movPtr->tkwin, &savedOptions, &mask) != TCL_OK ) {
3060		continue;
3061	    }
3062	} else {
3063	    /*
3064	     * Second pass: restore options to old values.
3065	     */
3066
3067	    errorResult = Tcl_GetObjResult( interp );
3068	    Tcl_IncrRefCount( errorResult );
3069	    Tk_RestoreSavedOptions( &savedOptions );
3070	}
3071
3072	/*
3073	 * Check possible inconsistencies of the options. Return error if found any.
3074	 *
3075	 * Don't allow both file and url, at most one of them.
3076	 * Tk_DestroyWindow and DestroyMovie is made in calling procedure.
3077	 */
3078
3079	if (movPtr->fileNamePtr != NULL && movPtr->url != NULL) {
3080	    Tcl_SetObjResult( interp, Tcl_NewStringObj(
3081						       "Does not accept both -file and -url", -1 ) );
3082	    Tk_RestoreSavedOptions( &savedOptions );
3083	    return TCL_ERROR;
3084	}
3085
3086	/* Url only with a controller. */
3087
3088	if (movPtr->url != NULL && !movPtr->wantController) {
3089	    Tcl_SetObjResult( interp, Tcl_NewStringObj(
3090						       "Need a controller for -url", -1 ) );
3091	    Tk_RestoreSavedOptions( &savedOptions );
3092	    return TCL_ERROR;
3093	}
3094
3095	/*
3096	 * We cannot configure a movie that is async loading and that is not yet playable.
3097	 * It is configured when playable.
3098	 */
3099
3100	if ((movPtr->url != NULL) && (movPtr->state & kQTTclMovieStateAsyncLoading) &&
3101	    (movPtr->loadState < kMovieLoadStatePlayable)) {
3102	    Tk_RestoreSavedOptions( &savedOptions );
3103	    return TCL_ERROR;
3104	}
3105
3106        Tk_SetInternalBorder( movPtr->tkwin, movPtr->highlightWidth );
3107        if (movPtr->highlightWidth <= 0) {
3108            movPtr->highlightWidth = 0;
3109        }
3110        movPtr->inset = movPtr->padx + movPtr->highlightWidth;
3111
3112	/*
3113	 * Handle the -file option. Was there a new filename? If -file {}  ???
3114	 * Handle the -url option. Was there a new url?
3115	 */
3116
3117	if ((mask & MOV_CONF_FILE) || (mask & MOV_CONF_URL)) {
3118
3119	    /* Get rid of the old one in the global display list. */
3120	    RemoveMovieFromOpenMovies( movPtr );
3121
3122	    /*
3123	     * If there is a controller, remove since it may need a different one.
3124	     */
3125
3126	    /* Old edit states not valid anymore. */
3127	    if (movPtr->aMovie != NULL) {
3128		for (i = 0; i < movPtr->undoCount; i++) {
3129		    DisposeMovieEditState( movPtr->editStates[i] );
3130		}
3131		movPtr->undoCount = 0;
3132	    }
3133
3134	    /*
3135	     * We may have end up here during a 'configure -file newFile' call from the
3136	     * '-mccommand' tcl proc, which is called from inside 'MCIsPlayerEvent'.
3137	     * When we return to 'MCIsPlayerEvent' suddenly the movie doesn't exist
3138	     * anymore, and the thing crashes. Wait therefore to dispose off the movie.
3139	     */
3140
3141	    if (movPtr->insideMCCommand) {
3142		movPtr->tmpMovieRecordPtr = (TmpMovieRecord *) ckalloc( sizeof(TmpMovieRecord) );
3143		movPtr->tmpMovieRecordPtr->movie = movPtr->aMovie;
3144		movPtr->tmpMovieRecordPtr->movieController = movPtr->aController;
3145		SetMovieGWorld( movPtr->aMovie, gOffscreenGWorldPtr, NULL );
3146		movPtr->aMovie = NULL;
3147		movPtr->aController = NULL;
3148	    } else {
3149		if (movPtr->aController != NULL) {
3150		    DisposeMovieController( movPtr->aController );
3151		    movPtr->aController = NULL;
3152		}
3153		if (movPtr->aMovie != NULL) {
3154		    DisposeMovie( movPtr->aMovie );
3155		    movPtr->aMovie = NULL;
3156                    gMovieRefCount--;
3157		}
3158	    }
3159
3160	    /*
3161	     * In case we ended up here due to a 'configure -file/-url' command,
3162	     * the new movie's GWorld is not valid anymore, and the movie
3163	     * should NOT be put on display until we called 'SetMovieGWorld'.
3164	     */
3165
3166	    movPtr->grafPtr = NULL;
3167	    movPtr->state = 0;
3168
3169	    /*
3170	     * Retrieve the actual movie from the specified file or url.
3171	     */
3172
3173	    if (movPtr->fileNamePtr != NULL) {
3174		if ((result = GetMovie( movPtr )) != TCL_OK) {
3175		    continue;
3176		}
3177	    } else if (movPtr->url != NULL) {
3178		if ((result = GetMovieFromUrl( movPtr )) != TCL_OK) {
3179		    continue;
3180		}
3181	    }
3182
3183	    /*
3184	     * Whenever we open a new movie put it offscreen unitil we
3185	     * display it.
3186	     */
3187
3188	    SetMovieGWorld( movPtr->aMovie, gOffscreenGWorldPtr, NULL );
3189	    movPtr->flags |= NEWGWORLD;
3190	}
3191
3192	/*
3193	 * Set no progress procedure, QuickTime default, or a tcl procedure.
3194	 */
3195
3196     	if (mask & MOV_CONF_PROGRESS_PROC) {
3197	    if (movPtr->progressProc != NULL) {
3198#if TARGET_API_MAC_CARBON
3199		SetMovieProgressProc( movPtr->aMovie, NewMovieProgressUPP(MovieProgressFunction),
3200				     (long) movPtr );
3201#else
3202		SetMovieProgressProc( movPtr->aMovie, NewMovieProgressProc(MovieProgressFunction),
3203				     (long) movPtr );
3204#endif
3205	    } else if (movPtr->qtprogress) {
3206		SetMovieProgressProc( movPtr->aMovie, (MovieProgressUPP) -1, 0 );
3207	    } else {
3208		SetMovieProgressProc( movPtr->aMovie, NULL, 0 );
3209	    }
3210	}
3211
3212	/*
3213	 * Attach any controller. Many of the configure options need a controller to work,
3214	 * and therefore we cannot wait to make the controller until it is displayed.
3215	 * If we write 'movie .m', the controller is not added here, but later in
3216	 * 'MoviePlayerWorldChanged,.
3217	 */
3218
3219	AddOrRemoveMovieController( movPtr );
3220
3221	/*
3222	 * Do we need to set the controllers edit state? Be sure to also set
3223	 * the controller callback function to make movie and track selection
3224	 * mutually exclusive.
3225	 */
3226
3227	if ((movPtr->aController != NULL) && (mask & MOV_CONF_MCEDIT)) {
3228	    if (movPtr->mcEdit) {
3229		MCEnableEditing( movPtr->aController, true );
3230#if TARGET_API_MAC_CARBON
3231		MCSetActionFilterWithRefCon( movPtr->aController,
3232					    NewMCActionFilterWithRefConUPP( MovieControllerCallbackFunction ),
3233					    (long) movPtr );
3234#else
3235		MCSetActionFilterWithRefCon( movPtr->aController,
3236					    NewMCActionFilterWithRefConProc( MovieControllerCallbackFunction ),
3237					    (long) movPtr );
3238#endif
3239	    } else {
3240		MCEnableEditing( movPtr->aController, false );
3241	    }
3242	    MCMovieChanged( movPtr->aController, movPtr->aMovie );
3243	}
3244
3245	/*
3246	 * Set the loopstates if any. Set only if different from old state.
3247	 */
3248
3249	if (mask & MOV_CONF_LOOPSTATE) {
3250	    if (movPtr->aMovie != NULL) {
3251		if (movPtr->aController != NULL) {
3252		    MCDoAction( movPtr->aController, mcActionSetLooping,
3253			       (void *) movPtr->loopstate );
3254		    MCDoAction( movPtr->aController, mcActionSetLoopIsPalindrome,
3255			       (void *) false );
3256		} else {
3257		    if (movPtr->loopstate) {
3258			SetMovieLoopState( movPtr->aMovie, kQTTclNormalLooping );
3259		    } else {
3260			SetMovieLoopState( movPtr->aMovie, kQTTclNoLooping );
3261		    }
3262		}
3263	    } else {
3264		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
3265		continue;
3266	    }
3267	}
3268	if (mask & MOV_CONF_PALINDROME_LOOPSTATE) {
3269	    if (movPtr->aMovie != NULL) {
3270		if (movPtr->aController != NULL) {
3271		    if (movPtr->palindromeloopstate) {
3272			SetLoopingState( movPtr->aController, kQTTclPalindromeLooping );
3273		    } else {
3274			SetLoopingState( movPtr->aController, kQTTclNoLooping );
3275		    }
3276		} else {
3277		    if (movPtr->palindromeloopstate) {
3278			SetMovieLoopState( movPtr->aMovie, kQTTclPalindromeLooping );
3279		    } else {
3280			SetMovieLoopState( movPtr->aMovie, kQTTclNoLooping );
3281		    }
3282		}
3283	    } else {
3284		Tcl_SetObjResult( interp, Tcl_NewStringObj( "No movie", -1 ) );
3285		continue;
3286	    }
3287	}
3288
3289	/*
3290	 * Set the movie's volume if changed. If user interactively changes the volume,
3291	 * we cannot detect this.
3292	 * The 'MCMovieChanged' updates the volume button in the controller.
3293	 */
3294
3295	if ((movPtr->aMovie != NULL) && (mask & MOV_CONF_VOLUME)) {
3296	    SetMovieVolume( movPtr->aMovie, (short) movPtr->volume );
3297	    if (movPtr->aController != NULL) {
3298		MCMovieChanged( movPtr->aController, movPtr->aMovie );
3299	    }
3300	}
3301
3302	/*
3303	 * Set movie's preferred play rate.
3304	 */
3305
3306	if ((movPtr->aMovie != NULL) && (mask & MOV_CONF_PREFERRED_RATE)) {
3307	    SetMoviePreferredRate( movPtr->aMovie, X2Fix( movPtr->preferredRate ) );
3308	}
3309
3310	/*
3311	 * Here we set any callback function for the movie controller so that
3312	 * a tcl procedure can monitor user actions.
3313	 * It is also necessary if we allow resizing via the controller.
3314	 */
3315
3316	if ((movPtr->aMovie != NULL) && (movPtr->aController != NULL) &&
3317	    ((movPtr->mcCallbackProc != NULL) || movPtr->resizable)) {
3318#if TARGET_API_MAC_CARBON
3319	    MCSetActionFilterWithRefCon( movPtr->aController,
3320					NewMCActionFilterWithRefConUPP( MovieControllerCallbackFunction ),
3321					(long) movPtr );
3322#else
3323	    MCSetActionFilterWithRefCon( movPtr->aController,
3324					NewMCActionFilterWithRefConProc( MovieControllerCallbackFunction ),
3325					(long) movPtr );
3326#endif
3327	}
3328
3329	/*
3330	 * Get the QTVR instance for QTVR panos. Returns NULL for non QTVR movies.
3331	 * Special configuration for QTVR movies.
3332	 */
3333
3334	if ((movPtr->aMovie != NULL) && (movPtr->aController != NULL) &&
3335	    IsQTVRMovie( movPtr->aMovie )) {
3336	    movPtr->qtvrInstance = GetQTVRInstanceFromController( movPtr->aController );
3337	    if (movPtr->qtvrInstance != NULL) {
3338		ConfigureQTVRMovie( interp, movPtr,
3339				   mask & MOV_CONF_QTVR_QUALITY_STATIC,
3340				   mask & MOV_CONF_QTVR_QUALITY_MOTION );
3341	    }
3342	}
3343
3344	/*
3345	 * Is there a custom button in the movie controller bar?
3346	 */
3347
3348	if ((movPtr->aController != NULL) && (mask & MOV_CONF_CUSTOM_BUTTON)) {
3349	    if (movPtr->custombutton) {
3350		ShowControllerButton( movPtr->aController, mcFlagsUseCustomButton );
3351	    } else {
3352		HideControllerButton( movPtr->aController, mcFlagsUseCustomButton );
3353	    }
3354	    MCMovieChanged( movPtr->aController, movPtr->aMovie );
3355	}
3356
3357	/*
3358	 * Do we want the movie to be resizable via the controller.
3359	 */
3360
3361	if ((movPtr->aController != NULL) && (mask & MOV_CONF_RESIZEABLE)) {
3362	    Rect    r;
3363
3364#if TARGET_API_MAC_CARBON
3365	    MacSetRect( &r, 0, 0, 2000, 1800 );
3366#else
3367	    {
3368		CGrafPtr    wmPortPtr = NULL;
3369
3370		GetCWMgrPort( &wmPortPtr );
3371		r = wmPortPtr->portRect;
3372	    }
3373#endif
3374	    if (movPtr->resizable) {
3375		MCDoAction( movPtr->aController, mcActionSetGrowBoxBounds, &r );
3376	    } else {
3377		MacSetRect( &r, 0, 0, 0, 0 );
3378		MCDoAction( movPtr->aController, mcActionSetGrowBoxBounds, &r );
3379	    }
3380	    MCMovieChanged( movPtr->aController, movPtr->aMovie );
3381	}
3382
3383	/*
3384	 * Loading a remote movie into ram is unpredictable.
3385	 */
3386
3387	if ((movPtr->aMovie != NULL) && (movPtr->url == NULL) &&
3388	    (mask & MOV_CONF_LOAD_INTO_RAM)) {
3389	    if (movPtr->loadIntoRam) {
3390		optFlags = keepInRam;
3391	    } else {
3392		optFlags = unkeepInRam;
3393	    }
3394	    err = LoadMovieIntoRam( movPtr->aMovie, 0,
3395				   GetMovieDuration(movPtr->aMovie), optFlags );
3396	    if (err != noErr) {
3397		CheckAndSetErrorResult( movPtr->interp, noErr );
3398		return TCL_ERROR;
3399	    }
3400	}
3401
3402        /*
3403         * Changing any of the -highlight* needs a redisplay.
3404         */
3405
3406	if (mask & MOV_CONF_HIGHLIGHTS) {
3407	    movPtr->flags |= NEWGWORLD;
3408        }
3409
3410	/*
3411	 * If we came so far break out of the ierror loop.
3412	 */
3413
3414	break;
3415    }
3416
3417    if (ierror) {
3418	Tcl_SetObjResult(interp, errorResult);
3419	Tcl_DecrRefCount(errorResult);
3420	return TCL_ERROR;
3421    } else {
3422	/*
3423	 * If width or height changed, reschedule a new display.
3424	 */
3425
3426	if (mask & MOV_CONF_NEWGWORLD) {
3427	    movPtr->flags |= NEWGWORLD;
3428    	}
3429	Tk_FreeSavedOptions( &savedOptions );
3430	return TCL_OK;
3431    }
3432}
3433
3434/*
3435 *----------------------------------------------------------------------
3436 *
3437 * ConfigureQTVRMovie --
3438 *
3439 *		Processes the QTVR specific options.
3440 *
3441 * Results:
3442 *		The return value is a standard Tcl result.  If TCL_ERROR is
3443 *		returned, then the interp's result contains an error message.
3444 *
3445 * Side effects:
3446 *		Movie settings may be changed.
3447 *
3448 *----------------------------------------------------------------------
3449 */
3450
3451static int
3452ConfigureQTVRMovie( Tcl_Interp *interp,
3453		   MoviePlayer * movPtr,
3454		   int setQTVRQualityStatic,
3455		   int setQTVRQualityMotion )
3456{
3457    int         result = TCL_OK;
3458    SInt32      propertyValue;
3459
3460    if (!IsQTVRMovie( movPtr->aMovie )) {
3461        return result;
3462    }
3463    if (movPtr->qtvrInstance == NULL) {
3464        return result;
3465    }
3466
3467    /*
3468     * In QTVR panos we doesn't want tk to set our cursor. Doesn't work!
3469     * Handle quality settings.
3470     */
3471
3472    Tk_UndefineCursor( movPtr->tkwin );
3473
3474    if (movPtr->indQTVRQualityStatic == -1) {
3475
3476        /* First time: */
3477
3478        QTVRGetImagingProperty( movPtr->qtvrInstance, kQTVRStatic, kQTVRImagingQuality,
3479			       &propertyValue );
3480        if (propertyValue == codecMinQuality) {
3481            movPtr->indQTVRQualityStatic = MOV_QTVR_QUALITY_MIN;
3482        } else if (propertyValue == codecLowQuality) {
3483            movPtr->indQTVRQualityStatic = MOV_QTVR_QUALITY_LOW;
3484        } else if (propertyValue == codecNormalQuality) {
3485            movPtr->indQTVRQualityStatic = MOV_QTVR_QUALITY_NORMAL;
3486        } else if (propertyValue == codecHighQuality) {
3487            movPtr->indQTVRQualityStatic = MOV_QTVR_QUALITY_HIGH;
3488        } else if (propertyValue == codecMaxQuality) {
3489            movPtr->indQTVRQualityStatic = MOV_QTVR_QUALITY_MAX;
3490        }
3491    } else if (setQTVRQualityStatic) {
3492
3493        /* User configured this one: */
3494
3495       	switch (movPtr->indQTVRQualityStatic) {
3496	    case MOV_QTVR_QUALITY_MIN: {
3497		propertyValue = codecMinQuality;
3498		break;
3499	    }
3500	    case MOV_QTVR_QUALITY_LOW: {
3501		propertyValue = codecLowQuality;
3502		break;
3503	    }
3504	    case MOV_QTVR_QUALITY_NORMAL: {
3505		propertyValue = codecNormalQuality;
3506		break;
3507	    }
3508	    case MOV_QTVR_QUALITY_HIGH: {
3509		propertyValue = codecHighQuality;
3510		break;
3511	    }
3512	    case MOV_QTVR_QUALITY_MAX: {
3513		propertyValue = codecMaxQuality;
3514		break;
3515	    }
3516       	}
3517        QTVRSetImagingProperty( movPtr->qtvrInstance, kQTVRStatic, kQTVRImagingQuality,
3518			       propertyValue );
3519    }
3520
3521    if (movPtr->indQTVRQualityMotion == -1) {
3522
3523	/* First time: */
3524
3525	QTVRGetImagingProperty( movPtr->qtvrInstance, kQTVRMotion, kQTVRImagingQuality,
3526			       &propertyValue );
3527        if (propertyValue == codecMinQuality) {
3528            movPtr->indQTVRQualityMotion = MOV_QTVR_QUALITY_MIN;
3529        } else if (propertyValue == codecLowQuality) {
3530            movPtr->indQTVRQualityMotion = MOV_QTVR_QUALITY_LOW;
3531        } else if (propertyValue == codecNormalQuality) {
3532            movPtr->indQTVRQualityMotion = MOV_QTVR_QUALITY_NORMAL;
3533        } else if (propertyValue == codecHighQuality) {
3534            movPtr->indQTVRQualityMotion = MOV_QTVR_QUALITY_HIGH;
3535        } else if (propertyValue == codecMaxQuality) {
3536            movPtr->indQTVRQualityMotion = MOV_QTVR_QUALITY_MAX;
3537        }
3538    } else if (setQTVRQualityMotion) {
3539
3540        /* User configured this one: */
3541
3542       	switch (movPtr->indQTVRQualityMotion) {
3543	    case MOV_QTVR_QUALITY_MIN: {
3544		propertyValue = codecMinQuality;
3545		break;
3546	    }
3547	    case MOV_QTVR_QUALITY_LOW: {
3548		propertyValue = codecLowQuality;
3549		break;
3550	    }
3551	    case MOV_QTVR_QUALITY_NORMAL: {
3552		propertyValue = codecNormalQuality;
3553		break;
3554	    }
3555	    case MOV_QTVR_QUALITY_HIGH: {
3556		propertyValue = codecHighQuality;
3557		break;
3558	    }
3559	    case MOV_QTVR_QUALITY_MAX: {
3560		propertyValue = codecMaxQuality;
3561		break;
3562	    }
3563       	}
3564        QTVRSetImagingProperty( movPtr->qtvrInstance, kQTVRMotion, kQTVRImagingQuality,
3565			       propertyValue );
3566    }
3567
3568    /*
3569     * Callback function for QTVR movies.
3570     */
3571
3572    if ((movPtr->aController != NULL) && (movPtr->mcCallbackProc != NULL)) {
3573
3574#if TARGET_API_MAC_CARBON
3575	movPtr->funcQTVRIntercept = NewQTVRInterceptUPP( MyQTVRInterceptProc );
3576#else
3577	movPtr->funcQTVRIntercept = NewQTVRInterceptProc( MyQTVRInterceptProc );
3578#endif
3579	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRSetPanAngleSelector,
3580				 movPtr->funcQTVRIntercept, (long) movPtr, 0 );
3581	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRSetTiltAngleSelector,
3582				 movPtr->funcQTVRIntercept, (long) movPtr, 0 );
3583	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRSetFieldOfViewSelector,
3584				 movPtr->funcQTVRIntercept, (long) movPtr, 0 );
3585	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRTriggerHotSpotSelector,
3586				 movPtr->funcQTVRIntercept, (long) movPtr, 0 );
3587	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRMouseDownSelector,
3588				 movPtr->funcQTVRIntercept, (long) movPtr, 0 );
3589        /*
3590	 QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRMouseEnterSelector,
3591	 movPtr->funcQTVRIntercept, (long) movPtr, 0 );
3592	 QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRMouseLeaveSelector,
3593	 movPtr->funcQTVRIntercept, (long) movPtr, 0 );
3594	 QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRGetHotSpotTypeSelector,
3595	 movPtr->funcQTVRIntercept, (long) movPtr, 0 );
3596	 */
3597    } else {
3598	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRSetPanAngleSelector, NULL, 0, 0 );
3599	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRSetTiltAngleSelector, NULL, 0, 0 );
3600	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRSetFieldOfViewSelector, NULL, 0, 0 );
3601	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRMouseEnterSelector, NULL, 0, 0 );
3602	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRMouseLeaveSelector, NULL, 0, 0 );
3603	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRTriggerHotSpotSelector, NULL, 0, 0 );
3604	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRGetHotSpotTypeSelector, NULL, 0, 0 );
3605	QTVRInstallInterceptProc( movPtr->qtvrInstance, kQTVRMouseDownSelector, NULL, 0, 0 );
3606#if TARGET_API_MAC_CARBON
3607	DisposeQTVRInterceptUPP( movPtr->funcQTVRIntercept );
3608#else
3609	DisposeRoutineDescriptor( movPtr->funcQTVRIntercept );
3610#endif
3611	movPtr->funcQTVRIntercept = NULL;
3612    }
3613    if (movPtr->swing) {
3614        QTVREnableTransition( movPtr->qtvrInstance, kQTVRTransitionSwing, true );
3615    } else {
3616        QTVREnableTransition( movPtr->qtvrInstance, kQTVRTransitionSwing, false );
3617    }
3618
3619    /* Verify that the speed is an integer between 1 and 10. */
3620    if (movPtr->swingSpeed < 1) {
3621        movPtr->swingSpeed = 1;
3622    } else if (movPtr->swingSpeed > 10) {
3623        movPtr->swingSpeed = 10;
3624    }
3625    QTVRSetTransitionProperty( movPtr->qtvrInstance, kQTVRTransitionSwing,
3626			      kQTVRTransitionSpeed, movPtr->swingSpeed );
3627    QTVRSetTransitionProperty( movPtr->qtvrInstance, kQTVRTransitionSwing,
3628			      kQTVRTransitionDirection, -1 );
3629
3630    return result;
3631}
3632
3633/*
3634 *----------------------------------------------------------------------
3635 *
3636 * GetMovie --
3637 *
3638 *	Get a Movie given a filename, all info is stored in movPtr.
3639 *
3640 * Results:
3641 *	Normal TCL.
3642 *
3643 * Side effects:
3644 *	Movie Allocated
3645 *
3646 *----------------------------------------------------------------------
3647 */
3648
3649static int
3650GetMovie( MoviePlayer *movPtr )
3651{
3652    OSErr       err = noErr;
3653    Rect 	aRect;
3654    Fixed	thePlayRate;
3655    short       resId = 0;
3656    int		result = TCL_OK;
3657    OSType	dataRefType;
3658    Handle	dataRef = NULL;
3659
3660    QTTclDebugPrintf( movPtr->interp, 2, "GetMovie" );
3661
3662    /*
3663     * Translate file name to Data Reference.
3664     */
3665    result = QTTclNewDataRefFromUTF8Obj(movPtr->interp, movPtr->fileNamePtr,
3666	    &dataRef, &dataRefType);
3667    if (result != TCL_OK) {
3668	result = TCL_ERROR;
3669	goto bail;
3670    }
3671    err = NewMovieFromDataRef(&(movPtr->aMovie), newMovieActive,
3672                                &resId, dataRef, dataRefType);
3673    if (err != noErr) {
3674        CheckAndSetErrorResult( movPtr->interp, err );
3675	result = TCL_ERROR;
3676	goto bail;
3677    }
3678    QTTclDebugPrintf( movPtr->interp, 2, "\taMovie=%d", movPtr->aMovie );
3679
3680    movPtr->resourceNumber = resId;
3681    gMovieRefCount++;
3682
3683    /* This disables the movie so it wont play and wont show up. */
3684    //SetMovieActive( movPtr->aMovie, false );	// gives no sound sometimes
3685    SetMovieActive( movPtr->aMovie, true );
3686
3687    /* The movies natural size without any controller, and a few other options */
3688    GetMovieBox( movPtr->aMovie, &aRect );
3689    movPtr->mwidth = aRect.right - aRect.left;
3690    movPtr->mheight = aRect.bottom - aRect.top;
3691    thePlayRate = GetMoviePreferredRate( movPtr->aMovie );
3692    movPtr->preferredRate = Fix2X( thePlayRate );
3693    if (movPtr->volume == 255) {
3694	movPtr->volume = GetMoviePreferredVolume( movPtr->aMovie );
3695    }
3696
3697    /*
3698     * -1 sets the default progress, NULL removes any progress dialog.
3699     */
3700    SetMovieProgressProc( movPtr->aMovie, (MovieProgressUPP) -1L, 0 );
3701
3702bail:
3703    if (dataRef) {
3704	DisposeHandle(dataRef);
3705    }
3706    return result;
3707}
3708
3709/*
3710 *----------------------------------------------------------------------
3711 *
3712 * GetMovieFromUrl --
3713 *
3714 *		Get a Movie from the url (stored in movPtr).
3715 *
3716 * Results:
3717 *		Normal TCL.
3718 *
3719 * Side effects:
3720 *		Movie allocated.
3721 *
3722 *----------------------------------------------------------------------
3723 */
3724
3725static int
3726GetMovieFromUrl( MoviePlayer *movPtr )
3727{
3728    char			*charPtr;
3729    int				len = 0;
3730    short			flags = 0;
3731    Tcl_DString     ds;
3732    Rect			aRect;
3733    Handle			urlDataRef = NULL;
3734    CGrafPtr 		oldPort = NULL;
3735    GDHandle 		oldGDeviceH = NULL;
3736    QDErr       	err = noErr;
3737    int				result = TCL_OK;
3738
3739    QTTclDebugPrintf( movPtr->interp, 2, "GetMovieFromUrl" );
3740
3741    if (strlen(movPtr->url) == 0) {
3742	Tcl_SetObjResult( movPtr->interp, Tcl_NewStringObj(
3743							   "URL of zero length not valid", -1 ) );
3744	result = TCL_ERROR;
3745	goto bail;
3746    }
3747
3748    charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding, movPtr->url, -1, &ds);
3749    len = Tcl_DStringLength( &ds );
3750    urlDataRef = MySafeNewHandle( len + 1, 1 );
3751    BlockMoveData( charPtr, *urlDataRef, len );
3752
3753    /*
3754     * Fetch the movie. In blocking mode if no 'loadCommand',
3755     * else async. Need to use 'gMoviePlayerListPtr' and the state member to serve
3756     * it while loading. Note: async movie must? be active to be served? yes.
3757     */
3758
3759    movPtr->resourceNumber = -1;
3760    GetGWorld( &oldPort, &oldGDeviceH );
3761    SetGWorld( gOffscreenGWorldPtr, NULL );
3762
3763    if (movPtr->loadCommand != NULL) {
3764	flags = newMovieActive | newMovieAsyncOK;
3765    } else {
3766        flags = newMovieActive;
3767    }
3768
3769    err = NewMovieFromDataRef( &(movPtr->aMovie), flags, NULL, urlDataRef,
3770			      URLDataHandlerSubType );
3771    if (err != noErr) {
3772	CheckAndSetErrorResult( movPtr->interp, err );
3773	result = TCL_ERROR;
3774	goto bail;
3775    }
3776    gMovieRefCount++;
3777    if (movPtr->loadCommand != NULL) {
3778
3779	/* The movie should be served, with 'MoviesTask', but not displayed. */
3780	movPtr->state |= kQTTclMovieStateAsyncLoading;
3781    } else {
3782
3783	/* Set the rect to the movies natural size. */
3784	GetMovieBox( movPtr->aMovie, &aRect );
3785	movPtr->mwidth = aRect.right - aRect.left;
3786	movPtr->mheight = aRect.bottom - aRect.top;
3787    }
3788
3789bail:
3790
3791    if (urlDataRef != NULL) {
3792	DisposeHandle( urlDataRef );
3793    }
3794    Tcl_DStringFree( &ds );
3795    SetGWorld( oldPort, oldGDeviceH );
3796    return result;
3797}
3798
3799/*
3800 *----------------------------------------------------------------------
3801 *
3802 * MovieProgressFunction  --
3803 *
3804 *		Called by the Movie Toolbox when there is a long operation.
3805 *
3806 * Results:
3807 *		Mac Error Codes
3808 *
3809 * Side effects:
3810 *		Tcl command may be run.
3811 *
3812 *----------------------------------------------------------------------
3813 */
3814
3815static pascal OSErr
3816MovieProgressFunction( Movie theMovie, short message, short whatOperation,
3817		      Fixed percentDone, long refcon )
3818{
3819    MoviePlayer     *movPtr = (MoviePlayer *) refcon;
3820    int             result = TCL_OK;
3821    char            cmd[255];
3822    char            percent[255];
3823
3824    if (movPtr->progressProc != NULL) {
3825	strcpy( cmd, movPtr->progressProc );
3826	strcat( cmd, " " );
3827	strcat( cmd, Tcl_GetCommandName( movPtr->interp, movPtr->widgetCmd ) );
3828
3829	switch (message) {
3830	    case movieProgressOpen: {
3831		strcat(cmd, " open");
3832		break;
3833	    }
3834	    case movieProgressUpdatePercent: {
3835		strcat(cmd, " percent");
3836		break;
3837	    }
3838	    case movieProgressClose: {
3839		strcat(cmd, " close");
3840		break;
3841	    }
3842	    default:
3843		strcat(cmd, " other");
3844	}
3845
3846	switch (whatOperation) {
3847	    case progressOpFlatten: {
3848		strcat(cmd, " flatten");
3849		break;
3850	    }
3851	    case progressOpInsertTrackSegment: {
3852		strcat(cmd, " insertTrackSegment");
3853		break;
3854	    }
3855	    case progressOpInsertMovieSegment: {
3856		strcat(cmd, " insertMovieSegment");
3857		break;
3858	    }
3859	    case progressOpPaste: {
3860		strcat(cmd, " paste");
3861		break;
3862	    }
3863	    case progressOpAddMovieSelection: {
3864		strcat(cmd, " addMovieSelection");
3865		break;
3866	    }
3867	    case progressOpCopy: {
3868		strcat(cmd, " copy");
3869		break;
3870	    }
3871	    case progressOpCut: {
3872		strcat(cmd, " cut");
3873		break;
3874	    }
3875	    case progressOpLoadMovieIntoRam: {
3876		strcat(cmd, " loadMovieIntoRam");
3877		break;
3878	    }
3879	    case progressOpLoadTrackIntoRam: {
3880		strcat(cmd, " loadTrackIntoRam");
3881		break;
3882	    }
3883	    case progressOpLoadMediaIntoRam: {
3884		strcat(cmd, " loadMediaIntoRam");
3885		break;
3886	    }
3887	    case progressOpImportMovie: {
3888		strcat(cmd, " importMovie");
3889		break;
3890	    }
3891	    case progressOpExportMovie: {
3892		strcat(cmd, " exportMovie");
3893		break;
3894	    }
3895	    default: {
3896		strcat(cmd, " other");
3897	    }
3898	}
3899
3900	sprintf( percent, " %ld", Fix2Long( FixMul( Long2Fix(100), percentDone ) ) );
3901	strcat( cmd, percent );
3902	result = Tcl_Eval( movPtr->interp, cmd );
3903    }
3904    // This seems to create infinite loop in some circumstances!!!
3905    Tcl_DoOneEvent( TCL_DONT_WAIT | TCL_ALL_EVENTS );
3906
3907    if (result != TCL_OK) {
3908
3909        /*
3910         * Whenever the tcl procedure returns anything else than TCL_OK, this should
3911         * trigger a cancellation of the job. A press to a cancel button should return,
3912         * for instance, a 'break' code (return -code 3).
3913         */
3914
3915	return userCanceledErr;
3916    } else {
3917	return noErr;
3918    }
3919}
3920
3921/*
3922 *----------------------------------------------------------------------
3923 *
3924 * MovieControllerCallbackFunction  --
3925 *
3926 *		Called by the Movie Controller when there is an action.
3927 *
3928 * Results:
3929 *		Mac Error Codes
3930 *
3931 * Side effects:
3932 *		Tcl command may be run, or
3933 *
3934 *----------------------------------------------------------------------
3935 */
3936
3937static pascal Boolean
3938MovieControllerCallbackFunction( MovieController mc,
3939				short action,
3940				void *params,
3941				long refCon )
3942{
3943    MoviePlayer 	*movPtr = (MoviePlayer *) refCon;
3944    Tcl_Interp 		*interp = movPtr->interp;
3945    Movie           saveMovie = NULL;
3946    MovieController saveController = NULL;
3947    Rect			myMovieBounds;
3948    int 			result = TCL_OK;
3949    int             width, height;
3950    int				prevWidth, prevHeight;
3951    Boolean			isHandled = false;
3952    Boolean			isResized;
3953    Point			aPoint;
3954    Rect            aRect;
3955    TimeRecord		*timePtr;
3956    UInt32			aMessage;
3957    char 			cmd[255];
3958    char			strPara[255];
3959
3960    if ((action == mcActionIdle) || (movPtr->aMovie == NULL)) {
3961	return isHandled;
3962    }
3963
3964    /*
3965     * Signals that we does not exist anymore.
3966     */
3967
3968    if (movPtr->flags & MOVIE_DELETED) {
3969	return isHandled;
3970    }
3971
3972    /*
3973     * We shall not call the tcl proc recursively since this screws up any idle calls
3974     * on exiting this function.
3975     */
3976
3977    if (movPtr->insideMCCommand) {
3978	return isHandled;
3979    }
3980    QTTclDebugPrintf( movPtr->interp, 4, "MovieControllerCallbackFunction enter" );
3981
3982    Tcl_Preserve( (ClientData) movPtr );
3983    saveMovie = movPtr->aMovie;
3984    saveController = movPtr->aController;
3985
3986    /*
3987     * We can have either a tcl procedure or resizing via the controller or both.
3988     */
3989
3990    if (movPtr->resizable && (action == mcActionControllerSizeChanged) &&
3991	!(movPtr->flags & REDRAW_PENDING)) {
3992	MCGetControllerBoundsRect( movPtr->aController, &myMovieBounds );
3993	width = myMovieBounds.right - myMovieBounds.left;
3994	height = myMovieBounds.bottom - myMovieBounds.top;
3995
3996	/*
3997	 * This procedure gets called, for instance, on the initial display, and we
3998	 * must figure out if the size actually changed before requesting a new size.
3999	 */
4000
4001	if (movPtr->width == 0) {
4002	    prevWidth = movPtr->mwidth;
4003	} else {
4004	    prevWidth = movPtr->width;
4005	}
4006	if (movPtr->height == 0) {
4007	    prevHeight = movPtr->mheight + movPtr->controllerHeight;
4008	} else {
4009	    prevHeight = movPtr->height;
4010	}
4011	if ((prevWidth != width) || (prevHeight != height)) {
4012	    isResized = true;
4013	} else {
4014	    isResized = false;
4015	}
4016
4017	QTTclDebugPrintf( interp, 2, "\taction=%d, width=%d, height=%d, isResized=%d",
4018			 action, width, height, isResized );
4019
4020	/*
4021	 * We try to make an immediate resize here. The size is changed before this
4022	 * procedure returns. Schedule for a redisplay to tell tk as well.
4023	 */
4024
4025	Tk_ResizeWindow( movPtr->tkwin, width, height );
4026
4027	/*
4028	 * Need to set the width and height elements if resized interactively, but not
4029	 * if the user has 'configure'ed us to any width = 0, or height = 0.
4030	 */
4031
4032	if (width != movPtr->mwidth) {
4033	    movPtr->width = width;
4034	}
4035	if (height != movPtr->mheight + movPtr->controllerHeight) {
4036	    movPtr->height = height;
4037	}
4038
4039	if (isResized) {
4040	    movPtr->flags |= NEWGWORLD;
4041	    movPtr->flags |= UPDATEMOVIE;
4042	    MoviePlayerWorldChanged( (ClientData) movPtr );
4043        }
4044    }
4045
4046    /*
4047     * If we have '-mcedit' enabled, any selection must invalidate any track selection.
4048     */
4049
4050    if (movPtr->mcEdit && ( (action == mcActionSetSelectionBegin) ||
4051			   (action == mcActionSetSelectionDuration) )) {
4052	movPtr->trackSelect->trackID = 0;
4053    }
4054
4055    /*
4056     * Catch the event, get any parameters, and call the registered tcl callback procedure.
4057     * 'ProcName widget action {parameter}'
4058     */
4059
4060    if (movPtr->mcCallbackProc != NULL) {
4061       	CGrafPtr 	saveWorld = NULL;
4062       	GDHandle 	saveDevice = NULL;
4063       	GWorldPtr   destPort = NULL;
4064       	//Tcl_Obj		*listObjPtr;
4065
4066	/*
4067	 listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
4068	 Tcl_ListObjAppendElement( interp, listObjPtr,
4069	 Tcl_NewStringObj(movPtr->mcCallbackProc, -1) );
4070	 */
4071	strcpy( cmd, movPtr->mcCallbackProc );
4072	strcat( cmd, " " );
4073	strcat( cmd, Tcl_GetCommandName(interp, movPtr->widgetCmd) );
4074
4075	switch (action) {
4076	    case mcActionActivate: {
4077		strcat(cmd, " activate");
4078		break;
4079	    }
4080	    case mcActionCustomButtonClick: {
4081		strcat(cmd, " customButtonClick");
4082		aPoint = ((EventRecord *) params)->where;
4083		LocalToGlobal( &aPoint );
4084		sprintf( strPara, " {%d %d}", aPoint.h, aPoint.v );
4085		strcat(cmd, strPara);
4086		isHandled = true;
4087		break;
4088	    }
4089	    case mcActionDeactivate: {
4090		strcat(cmd, " deactivate");
4091		break;
4092	    }
4093	    case mcActionGoToTime: {
4094		timePtr = (TimeRecord *) params;
4095		strcat(cmd, " goToTime" );
4096		sprintf( strPara, " {%d %d %d}",
4097			(int) timePtr->value.hi, (int) timePtr->value.lo, (int) timePtr->scale );
4098		strcat( cmd, strPara );
4099		break;
4100	    }
4101	    case mcActionKey: {
4102		strcat(cmd, " key");
4103		aMessage = ((EventRecord *) params)->message;
4104		sprintf( strPara, " %c", (int) aMessage & charCodeMask );
4105		strcat(cmd, strPara);
4106		break;
4107	    }
4108	    case mcActionMouseDown: {
4109		strcat(cmd, " mouseDown");
4110		aPoint = ((EventRecord *) params)->where;
4111		LocalToGlobal( &aPoint );
4112		sprintf( strPara, " {%d %d}", aPoint.h, aPoint.v );
4113		strcat(cmd, strPara);
4114		break;
4115	    }
4116	    case mcActionPlay: {
4117		strcat(cmd, " play");
4118		sprintf( strPara, " %f", Fix2X((Fixed) params) );
4119		strcat( cmd, strPara );
4120		break;
4121	    }
4122	    case mcActionSetSelectionBegin: {
4123		timePtr = (TimeRecord *) params;
4124		strcat(cmd, " setSelectionBegin");
4125		sprintf( strPara, " %li", timePtr->value.lo );
4126		strcat( cmd, strPara );
4127		break;
4128	    }
4129	    case mcActionSetSelectionDuration: {
4130		timePtr = (TimeRecord *) params;
4131		strcat(cmd, " setSelectionDuration");
4132		sprintf( strPara, " %li", timePtr->value.lo );
4133		strcat( cmd, strPara );
4134		break;
4135	    }
4136	    case mcActionSetVolume: {
4137		strcat(cmd, " setVolume");
4138		sprintf( strPara, " %li", ((EventRecord *) params)->message );
4139		strcat( cmd, strPara );
4140		break;
4141	    }
4142	    default:
4143                goto bail;
4144	}
4145
4146	/*
4147	 * Perform the actual action. Any 'update' command in the tcl procedure
4148	 * ruins update events sent to redraw the controller, and we must therefore
4149	 * invalidate, make dirty, the controller rectangle!
4150         * Beware, we may have been destroyed in the callback!
4151	 */
4152
4153    	movPtr->insideMCCommand++;
4154	result = Tcl_Eval( interp, cmd );
4155    	movPtr->insideMCCommand--;
4156
4157        if (movPtr->flags & MOVIE_DELETED) {
4158            isHandled = true;
4159            goto bail;
4160        }
4161
4162       	GetGWorld( &saveWorld, &saveDevice );
4163    	GetMovieGWorld( saveMovie, &destPort, NULL );
4164    	SetGWorld( destPort, NULL );
4165        MCGetControllerBoundsRect( saveController, &aRect );
4166        aRect.top = aRect.bottom - movPtr->controllerHeight;
4167#if TARGET_API_MAC_CARBON
4168        // This seems to create infinite loop in some circumstances!!!
4169        //Tcl_DoOneEvent( TCL_DONT_WAIT | TCL_ALL_EVENTS );
4170        InvalWindowRect( GetWindowFromPort(destPort), &aRect );
4171#else
4172        InvalRect( &aRect );
4173#endif
4174       	SetGWorld( saveWorld, saveDevice );
4175
4176       	/*
4177       	 * If the original movie was destroyed or configure -file, then we put the
4178       	 * destruction as an idle call so that we don't crash.
4179       	 */
4180
4181       	if (movPtr->tmpMovieRecordPtr != NULL) {
4182            Tcl_DoWhenIdle( DisposeMovieAtIdle, (ClientData) movPtr );
4183       	}
4184    }
4185
4186    /*
4187     * We should return false to indicate that we only monitor the events here,
4188     * except if the callback script changed our movie (with configure -file),
4189     * or if script returned TCL_BREAK (break).
4190     */
4191
4192    if ((saveMovie != movPtr->aMovie) || (result == TCL_BREAK)) {
4193        isHandled = true;
4194
4195        /*
4196         * We are still processing the old movie, and therefore need to stop any updates of it.
4197         * Seems not to work, schedule redisplay of new.
4198         */
4199
4200      	if (movPtr->tmpMovieRecordPtr != NULL) {
4201	    MCGetControllerBoundsRect( movPtr->tmpMovieRecordPtr->movieController, &aRect );
4202#if TARGET_API_MAC_CARBON
4203            {
4204                GWorldPtr   destPort = NULL;
4205
4206		GetMovieGWorld( movPtr->tmpMovieRecordPtr->movie, &destPort, NULL );
4207                //ValidWindowRect( GetWindowFromPort(destPort), &aRect );
4208            }
4209#else
4210            ValidRect( &aRect );
4211#endif
4212        }
4213    	if (!(movPtr->flags & MOVIE_DELETED) && !(movPtr->flags & REDRAW_PENDING)) {
4214	    Tcl_DoWhenIdle( DisplayMovie, (ClientData) movPtr );
4215	    movPtr->flags |= REDRAW_PENDING;
4216    	}
4217
4218	QTTclDebugPrintf( interp, 2,
4219			 "\tMovieControllerCallbackFunction:: isHandled = true" );
4220    }
4221
4222bail:
4223    QTTclDebugPrintf( movPtr->interp, 4, "\tMovieControllerCallbackFunction exit" );
4224    Tcl_Release( (ClientData) movPtr );
4225
4226    return isHandled;
4227}
4228
4229/*
4230 *----------------------------------------------------------------------
4231 *
4232 * MyQTVRInterceptProc --
4233 *
4234 *		Used to monitor user action in a QTVR movie.
4235 *
4236 * Results:
4237 *		boolean
4238 *
4239 * Side effects:
4240 *		the intercepted QTVR procedure called.
4241 *
4242 *----------------------------------------------------------------------
4243 */
4244
4245pascal void
4246MyQTVRInterceptProc( QTVRInstance qtvrInst,
4247		    QTVRInterceptPtr msg,
4248		    SInt32 refCon,
4249		    Boolean *cancel )
4250{
4251    MoviePlayer 	*movPtr = (MoviePlayer *) refCon;
4252    Boolean         hasCancelled = false;
4253    CGrafPtr 	    saveWorld = NULL;
4254    GDHandle 	    saveDevice = NULL;
4255    GWorldPtr       destPort = NULL;
4256    Movie           saveMovie = NULL;
4257    Rect            aRect;
4258    int 			result = TCL_OK;
4259    char 			cmd[255];
4260    char			strPara[255];
4261    float           fValue;
4262    UInt32          uint32Value;
4263    OSType          typeValue;
4264
4265    *cancel = hasCancelled;
4266    if (movPtr->mcCallbackProc == NULL) {
4267        return;
4268    }
4269
4270    /*
4271     * Signals that we does not exist anymore.
4272     */
4273
4274    if (movPtr->flags & MOVIE_DELETED) {
4275	return;
4276    }
4277
4278    /*
4279     * We shall not call the tcl proc recursively since this screws up any idle calls
4280     * on exiting this function.
4281     */
4282
4283    if (movPtr->insideMCCommand) {
4284	return;
4285    }
4286    Tcl_Preserve((ClientData) movPtr);
4287
4288    saveMovie = movPtr->aMovie;
4289    strcpy( cmd, movPtr->mcCallbackProc );
4290    strcat( cmd, " " );
4291    strcat( cmd, Tcl_GetCommandName(movPtr->interp, movPtr->widgetCmd) );
4292
4293    switch (msg->selector) {
4294        case kQTVRSetPanAngleSelector:
4295            fValue = *((float *) msg->parameter[0]);
4296	    strcat(cmd, " pan");
4297	    sprintf( strPara, " %f", RadiansToDegrees( fValue ) );
4298	    strcat( cmd, strPara );
4299            break;
4300        case kQTVRSetTiltAngleSelector:
4301            fValue = *((float *) msg->parameter[0]);
4302	    strcat(cmd, " tilt");
4303	    sprintf( strPara, " %f", RadiansToDegrees( fValue ) );
4304	    strcat( cmd, strPara );
4305            break;
4306        case kQTVRSetFieldOfViewSelector:
4307            fValue = *((float *) msg->parameter[0]);
4308	    strcat(cmd, " fieldofview");
4309	    sprintf( strPara, " %f", RadiansToDegrees( fValue ) );
4310	    strcat( cmd, strPara );
4311            break;
4312        case kQTVRSetViewCenterSelector:
4313
4314            break;
4315        case kQTVRMouseEnterSelector:
4316
4317            break;
4318        case kQTVRMouseWithinSelector:
4319
4320            break;
4321        case kQTVRMouseLeaveSelector:
4322
4323            break;
4324        case kQTVRMouseDownSelector:
4325            /*
4326	     strcat(cmd, " mouseDown");
4327	     sprintf( strPara, " {%f %f %d %d %d}", *((float *) msg->parameter[0]),
4328	     *((float *) msg->parameter[1]), ((UInt32) msg->parameter[2]),
4329	     ((UInt32) msg->parameter[3]), ((UInt32) msg->parameter[4]) );
4330	     strcat(cmd, strPara); */
4331            break;
4332        case kQTVRMouseStillDownSelector:
4333
4334            break;
4335        case kQTVRMouseUpSelector:
4336
4337            break;
4338        case kQTVRTriggerHotSpotSelector:
4339            uint32Value = ((UInt32) msg->parameter[0]);
4340	    strcat(cmd, " triggerhotspot");
4341	    sprintf( strPara, " %li", uint32Value );
4342	    strcat( cmd, strPara );
4343            break;
4344        case kQTVRGetHotSpotTypeSelector:
4345            uint32Value = ((UInt32) msg->parameter[0]);
4346            typeValue = *((OSType *) msg->parameter[1]);
4347	    strcat(cmd, " hotspottype");
4348	    sprintf( strPara, " %li %li", uint32Value, typeValue );
4349	    strcat( cmd, strPara );
4350
4351            break;
4352        default:
4353            goto exit;
4354    }
4355
4356    movPtr->insideMCCommand++;
4357    result = Tcl_Eval( movPtr->interp, cmd );
4358    movPtr->insideMCCommand--;
4359
4360    /*
4361     * Any 'update' command in the tcl procedure
4362     * ruins update events sent to redraw the controller, and we must therefore
4363     * invalidate, make dirty, the controller rectangle!
4364     * Beware, we may have been destroyed in the callback! Note Preserve/Release.
4365     */
4366
4367    if (!(movPtr->flags & MOVIE_DELETED) && (movPtr->aController != NULL)) {
4368      	GetGWorld( &saveWorld, &saveDevice );
4369        GetMovieGWorld( movPtr->aMovie, &destPort, NULL );
4370        SetGWorld( destPort, NULL );
4371        MCGetControllerBoundsRect( movPtr->aController, &aRect );
4372        aRect.top = aRect.bottom - movPtr->controllerHeight;
4373#if TARGET_API_MAC_CARBON
4374        InvalWindowRect( GetWindowFromPort(destPort), &aRect );
4375#else
4376        InvalRect( &aRect );
4377#endif
4378      	SetGWorld( saveWorld, saveDevice );
4379    }
4380
4381    /*
4382     * If the original movie was destroyed or configure -file, then we put the
4383     * destruction as an idle call so that we don't crash.
4384     */
4385
4386    if (movPtr->tmpMovieRecordPtr) {
4387        Tcl_DoWhenIdle( DisposeMovieAtIdle, (ClientData) movPtr );
4388    }
4389
4390    /*
4391     * We should return false to indicate that we only monitor the events here,
4392     * except if the callback script changed our movie (with configure -file),
4393     * or if script returned TCL_BREAK (break).
4394     */
4395
4396    if ((saveMovie != movPtr->aMovie) || (result == TCL_BREAK)) {
4397        hasCancelled = true;
4398
4399        /*
4400         * We are still processing the old movie, and therefore need to stop any updates of it.
4401         * Seems not to work, schedule redisplay of new.
4402         */
4403
4404      	if (movPtr->tmpMovieRecordPtr) {
4405	    MCGetControllerBoundsRect( movPtr->tmpMovieRecordPtr->movieController, &aRect );
4406#if TARGET_API_MAC_CARBON
4407	    GetMovieGWorld( movPtr->tmpMovieRecordPtr->movie, &destPort, NULL );
4408            ValidWindowRect( GetWindowFromPort(destPort), &aRect );
4409#else
4410            ValidRect( &aRect );
4411#endif
4412        }
4413    	if (!(movPtr->flags & MOVIE_DELETED) && !(movPtr->flags & REDRAW_PENDING)) {
4414	    Tcl_DoWhenIdle( DisplayMovie, (ClientData) movPtr );
4415	    movPtr->flags |= REDRAW_PENDING;
4416    	}
4417
4418	QTTclDebugPrintf( movPtr->interp, 2, "\tsaveMovie=%d, movPtr->aMovie=%d",
4419			 saveMovie, movPtr->aMovie );
4420    }
4421
4422exit:
4423    Tcl_Release((ClientData) movPtr);
4424    *cancel = hasCancelled;
4425}
4426
4427/*
4428 *----------------------------------------------------------------------
4429 *
4430 * ControllerCallbackTimer --
4431 *
4432 *		Timer function for tcl callbacks from the controller.
4433 *  	(Presently unused; does not fix update problem!)
4434 *
4435 * Results:
4436 *		None.
4437 *
4438 * Side effects:
4439 *
4440 *
4441 *----------------------------------------------------------------------
4442 */
4443
4444static void
4445ControllerCallbackTimer ( ClientData clientData )
4446{
4447    int         result;
4448    ControllerTimerRecord     *timerPtr = (ControllerTimerRecord *) clientData;
4449
4450    /*
4451     * Perform the actual call to the registered tcl procedure.
4452     */
4453
4454    result = Tcl_Eval( timerPtr->movPtr->interp, timerPtr->tclCmd );
4455
4456    /* Release memory! */
4457    ckfree( timerPtr->tclCmd );
4458    ckfree( (char *) timerPtr );
4459}
4460
4461/*
4462 *----------------------------------------------------------------------
4463 *
4464 * LogUndoState --
4465 *
4466 *		Capture an UnDo state.
4467 *
4468 * Results:
4469 *  	Normal TCL results
4470 *
4471 * Side effects:
4472 *		Remembers where we are in a movie, memory is likely allocated.
4473 *		Result in interpreter as a list '-undostate number'.
4474 *
4475 *----------------------------------------------------------------------
4476 */
4477
4478int
4479LogUndoState( MoviePlayer *movPtr, Tcl_Interp *interp)
4480{
4481    MovieEditState  *tmparr = NULL;
4482    int             result = TCL_OK;
4483    int             i;
4484    Tcl_Obj			*listObjPtr;
4485
4486    /* Do we need to make the array bigger? */
4487
4488    if (movPtr->undoCount >= movPtr->editStatesSize) {
4489	tmparr = (MovieEditState *)
4490	ckalloc( sizeof(MovieEditState)*(UNDO_SIZE + movPtr->editStatesSize) );
4491	if (tmparr == NULL) {
4492	    Tcl_SetObjResult( interp, Tcl_NewStringObj( "Out of Memory", -1 ) );
4493	    return TCL_ERROR;
4494	}
4495	if (movPtr->editStates) {
4496	    for (i = 0; i < movPtr->editStatesSize; i++) {
4497		tmparr[i] = movPtr->editStates[i];
4498	    }
4499	    ckfree((char  *) movPtr->editStates);
4500	}
4501	movPtr->editStates = tmparr;
4502	movPtr->editStatesSize += UNDO_SIZE;
4503    }
4504
4505    movPtr->editStates[movPtr->undoCount] = NewMovieEditState( movPtr->aMovie );
4506    listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
4507    Tcl_ListObjAppendElement( interp, listObjPtr,
4508			     Tcl_NewStringObj("-undostate", -1) );
4509    Tcl_ListObjAppendElement( interp, listObjPtr,
4510			     Tcl_NewIntObj(movPtr->undoCount) );
4511    Tcl_AppendObjToObj( Tcl_GetObjResult( interp ), listObjPtr );
4512    movPtr->undoCount++;
4513
4514    return result;
4515}
4516
4517/*
4518 *----------------------------------------------------------------------
4519 *
4520 * AddImagesToMovie --
4521 *
4522 *		Add images to a media with a duration of duration.
4523 *
4524 * Results:
4525 *		Normal Tcl
4526 *
4527 * Side effects:
4528 *		Memory allocated, any error messages in the interpreter.
4529 *
4530 *----------------------------------------------------------------------
4531 */
4532
4533int
4534AddImagesToMovie( Tcl_Interp *interp,
4535		 Media myMedia,              /* the media (in) */
4536		 TimeValue durationPerFrame, /* the duration of one frame in media's time scale (in) */
4537		 int nPhotos,                /* number of photos in array (in) */
4538		 Tk_PhotoHandle *photoList,  /* the photo (in) */
4539		 int showDialog,             /* show dialog ? (in) */
4540		 CodecType codecType,        /* the compressor type to use (in) */
4541		 CodecQ spatialQuality,
4542		 CodecQ temporalQuality,
4543		 short colorDepth,
4544		 long keyFrameRate,
4545		 TimeValue *sampleTimePtr )  /* returns the position where the first sample was inserted
4546 * in the media, in media's time (out) */
4547{
4548    GWorldPtr 		        theGWorld = NULL;
4549    CGrafPtr 		        oldPort = NULL;
4550    GDHandle 		        oldGDeviceH = NULL;
4551    ImageDescriptionHandle  imageDesc = NULL;
4552    ImageSequence           sequenceID = 0;
4553    ComponentInstance       ci = NULL;
4554    ComponentResult         err = noErr;
4555    OSErr                   osErr = noErr;
4556    long 			        maxCompressedSize;
4557    long                    dataSize;
4558    Handle 			        compressedData = NULL;
4559    Ptr 			        compressedDataPtr = NULL;
4560    Ptr 			        nextDataPtr = NULL;
4561    TimeValue               thisSampleTime;
4562    short                   syncFlag = 0;
4563    PixMapHandle 	        pixels = NULL;
4564    int                     pixelDepth;
4565    Tk_PhotoImageBlock      photoBlock;
4566    unsigned char 	        *pixelPtr;
4567    unsigned char 	        *photoPixels;
4568    Rect 			        myRect;
4569    Tcl_Obj					*resultObjPtr;
4570    int 			        right, bottom;
4571    int                     iphoto;
4572    int                     nPhotosInBatch;
4573    int 			        i, j;
4574    int 			        result = TCL_OK;
4575    int                     useStandardCompress = true;  /* mainly for testing */
4576
4577    if (nPhotos <= 0) {
4578        return TCL_ERROR;
4579    }
4580    if ((nPhotos > 1) || showDialog) {
4581        useStandardCompress = true;
4582    }
4583
4584    GetGWorld( &oldPort, &oldGDeviceH );
4585    Tk_PhotoGetSize( photoList[0], &right, &bottom );
4586    myRect.top = 0;
4587    myRect.left = 0;
4588    myRect.right = right;
4589    myRect.bottom = bottom;
4590    err = QTNewGWorld( &theGWorld, 32, &myRect, NULL, NULL, kICMTempThenAppMemory );
4591    if (err != noErr) {
4592    	CheckAndSetErrorResult( interp, err );
4593	result = TCL_ERROR;
4594	goto done;
4595    }
4596
4597    SetGWorld( theGWorld, NULL );
4598    pixels = GetGWorldPixMap( theGWorld );
4599
4600    /*
4601     * Lock down the pixels so they don't move out from under us.
4602     */
4603
4604    LockPixels(pixels);
4605
4606    /*
4607     * If we are using CompressImage and has a specific colorDepth, it is necessary
4608     * to make a GWorld of that colorDepth.
4609     */
4610
4611    if (!showDialog && ((colorDepth != 0) && (colorDepth != 32))) {
4612        int     ans;
4613
4614        if (colorDepth <= 32) {
4615            pixelDepth = colorDepth;
4616        } else {
4617            pixelDepth = colorDepth - 32;
4618        }
4619        ans = DoesCodecSupport( codecType, kDoesCodecPixmapDepth, pixelDepth );
4620        if (ans == false) {
4621	    Tcl_SetObjResult( interp, Tcl_NewStringObj(
4622						       "Compressor does not support this colordepth", -1 ) );
4623	    result = TCL_ERROR;
4624	    goto done;
4625        }
4626        ans = DoesCodecSupport( codecType, kDoesCodecCompressDepth, colorDepth );
4627        if (ans == false) {
4628	    Tcl_SetObjResult( interp, Tcl_NewStringObj(
4629						       "Compressor does not support this colordepth", -1 ) );
4630	    result = TCL_ERROR;
4631	    goto done;
4632        }
4633    }
4634
4635    imageDesc = (ImageDescriptionHandle) NewHandle(4);
4636
4637    if (!useStandardCompress && !showDialog && (nPhotos > 1)) {
4638
4639        /*
4640         * Initialize the dialogless sequence compressor.
4641         */
4642
4643        err = CompressSequenceBegin(
4644				    &sequenceID,      	/* unique identifier for this sequence */
4645				    pixels,
4646				    NULL,             	/* tell ICM to allocate previous buffer */
4647				    &myRect,
4648				    &myRect,
4649				    colorDepth,
4650				    codecType,
4651				    (CompressorComponent) anyCodec,
4652				    spatialQuality,  	/* spatial quality */
4653				    temporalQuality,    /* temporal quality */
4654				    keyFrameRate,       /* key frame rate */
4655				    NULL,               /* default color table */
4656				    codecFlagUpdatePrevious,
4657				    imageDesc );
4658    	if (err != noErr) {
4659	    UnlockPixels(pixels);
4660	    CheckAndSetErrorResult( interp, err );
4661	    result = TCL_ERROR;
4662	    goto done;
4663    	}
4664        nPhotosInBatch = nPhotos;
4665    } else {
4666        nPhotosInBatch = 1;
4667    }
4668    if (!useStandardCompress) {
4669        err = GetMaxCompressionSize( pixels,
4670				    &myRect,
4671				    colorDepth,
4672				    spatialQuality,
4673				    codecType,
4674				    (CompressorComponent) anyCodec,
4675				    &maxCompressedSize );
4676        maxCompressedSize *= nPhotosInBatch;
4677        if (err != noErr) {
4678	    UnlockPixels( pixels );
4679	    CheckAndSetErrorResult( interp, err );
4680	    result = TCL_ERROR;
4681	    goto done;
4682        }
4683    	compressedData = MySafeNewHandle( maxCompressedSize, 0 );
4684      	if (osErr != noErr) {
4685    	    panic( "Out of memory in AddImagesToMovie" );
4686      	}
4687
4688        MoveHHi( compressedData );
4689        HLock( compressedData );
4690#if TARGET_API_MAC_CARBON
4691        compressedDataPtr = *compressedData;
4692#else
4693        compressedDataPtr = StripAddress( *compressedData );
4694#endif
4695        nextDataPtr = compressedDataPtr;
4696    }
4697    if (useStandardCompress) {
4698        err = OpenADefaultComponent( StandardCompressionType,
4699				    StandardCompressionSubType, &ci );
4700       	if (err != noErr) {
4701	    result = TCL_ERROR;
4702	    CheckAndSetErrorResult( interp, err );
4703	    goto done;
4704       	}
4705
4706        /*
4707         * If setting defaults of the compressor cmponent like this, we
4708         * can avoid having the dialog displayed.
4709         * Thanks to Kevin Marks at Apple for this one.
4710         */
4711
4712        if (!showDialog) {      /* not sure about this... */
4713            SCSpatialSettings       spatSet;
4714            SCTemporalSettings      tempSet;
4715            SCDataRateSettings      rateSet;
4716
4717            SCGetInfo( ci, scSpatialSettingsType, &spatSet );
4718            SCGetInfo( ci, scTemporalSettingsType, &tempSet );
4719            SCGetInfo( ci, scDataRateSettingsType, &rateSet );
4720
4721            spatSet.codecType = codecType;
4722            spatSet.codec = anyCodec;
4723            spatSet.depth = colorDepth;
4724            spatSet.spatialQuality = spatialQuality;
4725            tempSet.temporalQuality = temporalQuality;
4726            tempSet.keyFrameRate = keyFrameRate;
4727            rateSet.minSpatialQuality = spatialQuality;
4728            rateSet.minTemporalQuality = temporalQuality;
4729
4730            SCSetInfo( ci, scSpatialSettingsType, &spatSet );
4731            SCSetInfo( ci, scTemporalSettingsType, &tempSet );
4732            SCSetInfo( ci, scDataRateSettingsType, &rateSet );
4733        }
4734    }
4735
4736    /*
4737     * Loop over all photos we have got.
4738     */
4739
4740    for (iphoto = 0; iphoto < nPhotos; iphoto++) {
4741
4742        Tk_PhotoGetSize( photoList[iphoto], &right, &bottom );
4743        if ((right != myRect.right) && (bottom != myRect.bottom)) {
4744	    resultObjPtr = Tcl_GetObjResult( interp );
4745	    Tcl_AppendObjToObj( resultObjPtr,
4746			       Tcl_NewStringObj( "image number ", -1 ) );
4747	    Tcl_AppendObjToObj( resultObjPtr, Tcl_NewIntObj( iphoto + 1 ) );
4748	    Tcl_AppendObjToObj( resultObjPtr,
4749			       Tcl_NewStringObj( " has a different size than previous", -1 ) );
4750	    Tcl_SetObjResult( interp, resultObjPtr );
4751	    result = TCL_ERROR;
4752	    goto done;
4753        }
4754    	Tk_PhotoGetImage( photoList[iphoto], &photoBlock );
4755
4756        /*
4757         * Copy the pixels to the gworld.
4758         * The Mac pixmap stores them as "undefined, red, gree, blue", but tk 8.3 stores them
4759         * as "red, green, blue, alpha (transparency)".
4760         */
4761
4762    	for (i = 0; i < photoBlock.height; i++) {
4763	    pixelPtr = (unsigned char *)
4764	    (GetPixBaseAddr(pixels) + i * (0x3FFF & ((*pixels)->rowBytes)));
4765	    photoPixels = photoBlock.pixelPtr + i * photoBlock.pitch;
4766	    for (j  =0; j < photoBlock.width; j++) {
4767#if TK_MINOR_VERSION <= 2
4768		*pixelPtr = 0; pixelPtr++;
4769#else
4770		*pixelPtr = *(photoPixels + photoBlock.offset[3]); pixelPtr++;
4771#endif
4772		*pixelPtr = *(photoPixels + photoBlock.offset[0]); pixelPtr++;
4773		*pixelPtr = *(photoPixels + photoBlock.offset[1]); pixelPtr++;
4774		*pixelPtr = *(photoPixels + photoBlock.offset[2]); pixelPtr++;
4775		photoPixels += photoBlock.pixelSize;
4776	    }
4777    	}
4778
4779        /*
4780         * There are four possibilities here: with or without dialog,
4781         * and single image or sequence of images.
4782         */
4783
4784    	if (useStandardCompress || showDialog) {
4785            if (nPhotos == 1) {
4786            	if (showDialog) {
4787                    SCSetTestImagePixMap( ci, pixels, NULL, scPreferCropping );
4788                    InstallExtendedSCCallbackProcedures( ci, (long) interp );
4789                    SCDefaultPixMapSettings( ci, pixels, false );
4790                    err = SCRequestImageSettings( ci );
4791                    if (err == scUserCancelled) {
4792
4793                        /* Perhaps we should return something here and set the error code */
4794			goto done;
4795                    } else if (err != noErr) {
4796			result = TCL_ERROR;
4797			CheckAndSetErrorResult( interp, err );
4798			goto done;
4799		    }
4800                }
4801            	err = SCCompressImage( ci,
4802				      pixels,
4803				      &myRect,
4804				      &imageDesc,
4805				      &compressedData );
4806            } else {
4807
4808                /*
4809                 * Sequence.
4810                 */
4811
4812                if (iphoto == 0) {
4813		    if (showDialog) {
4814                        SCSetTestImagePixMap( ci, pixels, NULL, scPreferCropping );
4815                        InstallExtendedSCCallbackProcedures( ci, (long) interp );
4816                        SCDefaultPixMapSettings( ci, pixels, true );
4817                        err = SCRequestSequenceSettings( ci );
4818                        if (err == scUserCancelled) {
4819
4820                            /* Perhaps we should return something here and set the error code */
4821			    goto done;
4822                        } else if (err != noErr) {
4823			    result = TCL_ERROR;
4824			    CheckAndSetErrorResult( interp, err );
4825			    goto done;
4826                       	}
4827                    }
4828                    err = SCCompressSequenceBegin( ci, pixels, &myRect, &imageDesc );
4829		    if (err != noErr) {
4830			result = TCL_ERROR;
4831			CheckAndSetErrorResult( interp, err );
4832			goto done;
4833		    }
4834                }
4835                err = SCCompressSequenceFrame( ci, pixels, &myRect,
4836					      &compressedData,
4837					      &dataSize,
4838					      &syncFlag );
4839                (**imageDesc).dataSize = dataSize;
4840            }
4841    	} else {                    /* dialogless, old compress */
4842
4843            if (nPhotos == 1) {
4844		err = CompressImage( pixels,
4845				    &myRect,
4846				    spatialQuality,
4847				    codecType,
4848				    imageDesc,
4849				    compressedDataPtr );
4850            } else {
4851                err = CompressSequenceFrame( sequenceID,
4852					    pixels,
4853					    &myRect,
4854					    codecFlagUpdatePrevious,
4855					    nextDataPtr,
4856					    &dataSize,
4857					    NULL,
4858					    NULL );
4859                nextDataPtr += dataSize;
4860            }
4861        }
4862        if (err != noErr) {
4863	    result = TCL_ERROR;
4864	    CheckAndSetErrorResult( interp, err );
4865	    goto done;
4866       	}
4867	if ((nPhotosInBatch == 1) || (iphoto = nPhotosInBatch - 1)) {
4868	    err = AddMediaSample( myMedia,
4869				 compressedData,
4870				 0,                   		/* no offset in data */
4871				 (**imageDesc).dataSize,
4872				 durationPerFrame,         	/* frame duration */
4873				 (SampleDescriptionHandle) imageDesc,
4874				 nPhotosInBatch,          	/* one sample or the complete sequence */
4875				 syncFlag,                	/* key frame or not */
4876				 &thisSampleTime );
4877        }
4878
4879        /*
4880         * Important that we return the media position where the first frame was stored!
4881         */
4882
4883        if (iphoto == 0) {
4884            *sampleTimePtr = thisSampleTime;
4885        }
4886    	if (err != noErr) {
4887	    result = TCL_ERROR;
4888	    CheckAndSetErrorResult( interp, err );
4889	    goto done;
4890    	}
4891    } /* end loop over photos */
4892
4893done:
4894
4895    UnlockPixels(pixels);
4896    SetGWorld(oldPort, oldGDeviceH);
4897    if (nPhotos > 1) {
4898        if (useStandardCompress || showDialog) {
4899
4900            /* disposes imageDesc and compressedData handles */
4901            SCCompressSequenceEnd(ci);
4902        } else {
4903	    CDSequenceEnd(sequenceID);
4904        }
4905    }
4906    if (imageDesc) {
4907	DisposeHandle((Handle) imageDesc);
4908    }
4909    if (compressedData) {
4910	DisposeHandle(compressedData);
4911    }
4912    if (ci) {
4913        CloseComponent(ci);
4914    }
4915    if (theGWorld) {
4916	DisposeGWorld(theGWorld);
4917    }
4918    return result;
4919}
4920
4921/*
4922 *----------------------------------------------------------------------
4923 *
4924 * AddImageFileToMovie --
4925 *
4926 *		Add images to a media with a duration of duration.
4927 *
4928 * Results:
4929 *		Normal Tcl
4930 *
4931 * Side effects:
4932 *		Memory allocated, any error messages in the interpreter.
4933 *
4934 *----------------------------------------------------------------------
4935 */
4936
4937int
4938AddImageFileToMovie( Tcl_Interp *interp,
4939		    Media myMedia,              /* the media (in) */
4940		    TimeValue duration,         /* the duration of one frame in media's time scale (in) */
4941		    char *fileName,             /* file name (in) */
4942		    long optFlags,              /* flags specifying options set (in) */
4943		    int showDialog,             /* show dialog ? (in) */
4944		    CodecType codecType,        /* the compressor type to use (in) */
4945		    CodecQ spatialQuality,
4946		    CodecQ temporalQuality,
4947		    short colorDepth,
4948		    long keyFrameRate,
4949		    TimeValue *sampleTimePtr )  /* returns the position where the first sample was inserted
4950 * in the media, in media's time (out) */
4951{
4952    GWorldPtr 		        theGWorld = NULL;
4953    PixMapHandle 	        pixels = NULL;
4954    ComponentResult         err = noErr;
4955    ComponentInstance       gi = NULL;
4956    ComponentInstance       ci = NULL;
4957    ImageDescriptionHandle  srcImageDesc = NULL;
4958    ImageDescriptionHandle  dstImageDesc = NULL;
4959    Handle 			        compressedData = NULL;
4960    Rect                    bounds;
4961    SCSpatialSettings       spatSet;
4962    SCTemporalSettings      tempSet;
4963    SCDataRateSettings      rateSet;
4964    FSSpec 			        fss;
4965    CTabHandle              colTabHandle = NULL;
4966    short                   pixelDepth;
4967    int 			        result = TCL_OK;
4968
4969    err = QTTclNativePathNameToFSSpec( interp, fileName, &fss );
4970    if (err != noErr) {
4971	Tcl_SetObjResult( interp, Tcl_NewStringObj(
4972						   "Failed making FSSpec from filename", -1 ) );
4973	result = TCL_ERROR;
4974	goto done;
4975    }
4976
4977    /* This can be slow :-( */
4978    err = GetGraphicsImporterForFile( &fss, &gi );
4979    if (err != noErr) {
4980    	result = TCL_ERROR;
4981       	CheckAndSetErrorResult( interp, err );
4982    	goto done;
4983    }
4984    err = GraphicsImportGetImageDescription( gi, &srcImageDesc );
4985    if (err != noErr) {
4986    	result = TCL_ERROR;
4987       	CheckAndSetErrorResult( interp, err );
4988    	goto done;
4989    }
4990    err = GraphicsImportGetNaturalBounds( gi, &bounds );
4991    if (err != noErr) {
4992    	result = TCL_ERROR;
4993       	CheckAndSetErrorResult( interp, err );
4994    	goto done;
4995    }
4996
4997    /*
4998     * Try to be as faithful as possible to the original image. Depth and color table.
4999     */
5000
5001    if ((**srcImageDesc).depth > 32) {
5002        pixelDepth = (**srcImageDesc).depth - 32;
5003    } else {
5004        pixelDepth = (**srcImageDesc).depth;
5005    }
5006    if ((**srcImageDesc).clutID == 0) {
5007        err = GetImageDescriptionCTable( srcImageDesc, &colTabHandle );
5008        if (err != noErr) {
5009	    result = TCL_ERROR;
5010	    CheckAndSetErrorResult( interp, err );
5011	    goto done;
5012        }
5013        if (((**colTabHandle).ctSize == 0) || ((**colTabHandle).ctSeed < 0)) {
5014	    if (colTabHandle) {
5015		DisposeCTable(colTabHandle);
5016	    }
5017	    colTabHandle = NULL;
5018        }
5019    }
5020    err = QTNewGWorld( &theGWorld, pixelDepth, &bounds, colTabHandle, NULL,
5021		      kICMTempThenAppMemory );
5022    if (err != noErr) {
5023    	CheckAndSetErrorResult( interp, err );
5024	result = TCL_ERROR;
5025	goto done;
5026    }
5027    GraphicsImportSetGWorld( gi, theGWorld, NULL );
5028    err = GraphicsImportDraw( gi );
5029    if (err != noErr) {
5030    	CheckAndSetErrorResult( interp, err );
5031	result = TCL_ERROR;
5032	goto done;
5033    }
5034
5035    /*
5036     * Time to compress the thing. Set defaults from pixmap and image description.
5037     */
5038
5039    err = OpenADefaultComponent( StandardCompressionType, StandardCompressionSubType, &ci );
5040    if (err != noErr) {
5041    	CheckAndSetErrorResult( interp, err );
5042	result = TCL_ERROR;
5043	goto done;
5044    }
5045    pixels = GetGWorldPixMap( theGWorld );
5046    LockPixels( pixels );
5047    SCDefaultPixMapSettings( ci, pixels, false );
5048
5049    SCGetInfo( ci, scSpatialSettingsType, &spatSet );
5050    SCGetInfo( ci, scTemporalSettingsType, &tempSet );
5051    SCGetInfo( ci, scDataRateSettingsType, &rateSet );
5052
5053    spatSet.codecType = (**srcImageDesc).cType;
5054    spatSet.spatialQuality = (**srcImageDesc).spatialQuality;
5055
5056    /*
5057     * Override options from file with any command line options.
5058     */
5059
5060    if (optFlags & kCompressFlagColorDepth) {
5061        spatSet.depth = colorDepth;
5062    }
5063    if (optFlags & kCompressFlagCompressor) {
5064        spatSet.codecType = codecType;
5065    }
5066    if (optFlags & kCompressFlagKeyFrameRate) {
5067        tempSet.keyFrameRate = keyFrameRate;
5068    }
5069    if (optFlags & kCompressFlagSpatialQuality) {
5070        spatSet.spatialQuality = spatialQuality;
5071    }
5072    if (optFlags & kCompressFlagTemporalQuality) {
5073        tempSet.temporalQuality = temporalQuality;
5074    }
5075    SCSetInfo( ci, scSpatialSettingsType, &spatSet );
5076    SCSetInfo( ci, scTemporalSettingsType, &tempSet );
5077    SCSetInfo( ci, scDataRateSettingsType, &rateSet );
5078
5079    if (showDialog) {
5080        SCSetTestImagePixMap( ci, pixels, NULL, scPreferCropping );
5081        InstallExtendedSCCallbackProcedures( ci, (long) interp );
5082        SCDefaultPixMapSettings( ci, pixels, true );
5083        err = SCRequestSequenceSettings( ci );
5084        if (err == scUserCancelled) {
5085
5086            /* Perhaps we should return something here and set the error code??? */
5087            goto done;
5088        } else if (err != noErr) {
5089	    result = TCL_ERROR;
5090	    CheckAndSetErrorResult( interp, err );
5091	    goto done;
5092        }
5093    }
5094    err = SCCompressImage( ci,
5095			  pixels,
5096			  &bounds,
5097			  &dstImageDesc,
5098			  &compressedData );
5099    if (err != noErr) {
5100    	result = TCL_ERROR;
5101       	CheckAndSetErrorResult( interp, err );
5102    	goto done;
5103    }
5104    err = AddMediaSample( myMedia,
5105			 compressedData,
5106			 0,                            	/* no offset in data */
5107			 (**dstImageDesc).dataSize,
5108			 duration,                      	/* frame duration */
5109			 (SampleDescriptionHandle) dstImageDesc,
5110			 1,                           	/* one sample or the complete sequence */
5111			 0,                            	/* key frame or not */
5112			 sampleTimePtr );
5113    if (err != noErr) {
5114    	result = TCL_ERROR;
5115       	CheckAndSetErrorResult( interp, err );
5116    	goto done;
5117    }
5118
5119
5120done:
5121
5122    if (gi) {
5123        CloseComponent(gi);
5124    }
5125    if (ci) {
5126        CloseComponent(ci);
5127    }
5128    if (srcImageDesc) {
5129	DisposeHandle((Handle) srcImageDesc);
5130    }
5131    if (dstImageDesc) {
5132	DisposeHandle((Handle) dstImageDesc);
5133    }
5134    if (colTabHandle) {
5135	DisposeCTable(colTabHandle);
5136    }
5137    if (compressedData) {
5138	DisposeHandle(compressedData);
5139    }
5140    if (theGWorld) {
5141	DisposeGWorld(theGWorld);
5142    }
5143    return result;
5144}
5145
5146/*
5147 *----------------------------------------------------------------------
5148 *
5149 * MoviePlayerEventProc --
5150 *
5151 *		Deal with Movie Events, these events are TCL events.
5152 *  	Update (Expose) events for a movie with controller are dealt
5153 *		with in MCIsPlayerEvent in 'MoviePlayerMacEvent'.
5154 *
5155 * Results:
5156 *		None.
5157 *
5158 * Side effects:
5159 *		Depends on event.
5160 *
5161 *----------------------------------------------------------------------
5162 */
5163
5164static void
5165MoviePlayerEventProc(ClientData clientData, XEvent *eventPtr)
5166{
5167    MoviePlayer *movPtr = (MoviePlayer *) clientData;
5168
5169    QTTclDebugPrintf( movPtr->interp, 2,
5170		     "MoviePlayerEventProc: type=%d, flags=%1d%1d%1d%1d%1d%1d",
5171		     eventPtr->type, (movPtr->flags >> 5) & 0x1, (movPtr->flags >> 4) & 0x1,
5172		     (movPtr->flags >> 3) & 0x1, (movPtr->flags >> 2) & 0x1,
5173		     (movPtr->flags >> 1) & 0x1, (movPtr->flags >> 0) & 0x1 );
5174
5175    if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
5176	movPtr->flags |= UPDATEMOVIE;
5177	goto redraw;
5178    } else if (eventPtr->type == ConfigureNotify) {
5179	goto redraw;
5180    } else if (eventPtr->type == MapNotify) {
5181
5182#if TARGET_API_MAC_CARBON
5183        /*
5184         * Is this the right place to install the Carbon window event handler?
5185         */
5186
5187        InstallCarbonWindowEventHandler( movPtr );
5188#endif
5189	movPtr->flags |= NEWGWORLD;
5190	goto redraw;
5191    } else if (eventPtr->type == UnmapNotify) {
5192
5193	/*
5194	 * We do not want to serve it anymore; if async loaded it still gets served,
5195	 * but what about the GWorld?
5196	 */
5197
5198	movPtr->state &= ~kQTTclMovieStateOnDisplay;
5199	movPtr->grafPtr = NULL;
5200	return;
5201    } else if (eventPtr->type == FocusIn) {
5202        movPtr->flags |= GOT_FOCUS;
5203	goto redraw;
5204    } else if (eventPtr->type == FocusOut) {
5205        movPtr->flags &= ~GOT_FOCUS;
5206	goto redraw;
5207    } else if (eventPtr->type == DestroyNotify) {
5208        DestroyMovie( movPtr );
5209    }
5210    return;
5211
5212redraw:
5213
5214    if (!(movPtr->flags & MOVIE_DELETED) && !(movPtr->flags & REDRAW_PENDING)) {
5215	Tcl_DoWhenIdle( DisplayMovie, (ClientData) movPtr );
5216	movPtr->flags |= REDRAW_PENDING;
5217    }
5218}
5219
5220/*
5221 *----------------------------------------------------------------------
5222 *
5223 * MoviePlayerDeletedProc --
5224 *
5225 *		Deletes a MoviePlayer widget.
5226 *
5227 * Results:
5228 *		None.
5229 *
5230 * Side effects:
5231 *		DestroyMovie implicitly called.
5232 *
5233 *----------------------------------------------------------------------
5234 */
5235
5236static void
5237MoviePlayerDeletedProc( ClientData clientData )
5238{
5239    MoviePlayer 	*movPtr = (MoviePlayer *) clientData;
5240
5241    /*
5242     * This procedure could be invoked either because the window was
5243     * destroyed and the command was then deleted or because the command
5244     * was deleted, and then this procedure destroys the widget.  The
5245     * MOVIE_DELETED flag distinguishes these cases.
5246     */
5247
5248    if (!(movPtr->flags & MOVIE_DELETED)) {
5249	Tk_DestroyWindow( movPtr->tkwin );
5250    }
5251}
5252
5253/*
5254 *----------------------------------------------------------------------
5255 *
5256 * DestroyMovie --
5257 *
5258 *		Deletes a MoviePlayer Widget.  Most things cleaned up with
5259 *		Tk_FreeOptions but some things are freed up by me.
5260 *
5261 * Results:
5262 *		None.
5263 *
5264 * Side effects:
5265 *		Schedules or calls directly MoviePlayerFree to free all resources.
5266 *
5267 *----------------------------------------------------------------------
5268 */
5269
5270static void
5271DestroyMovie( MoviePlayer *movPtr )
5272{
5273    QTTclDebugPrintf( movPtr->interp, 2, "DestroyMovie: movPtr=%d", movPtr );
5274    movPtr->flags |= MOVIE_DELETED;
5275
5276#if TARGET_API_MAC_CARBON
5277    MovieDestroyCarbonWindowHandlerCleanup( movPtr );
5278#endif
5279
5280    if (movPtr->flags & REDRAW_PENDING) {
5281        Tcl_CancelIdleCall( DisplayMovie, (ClientData) movPtr );
5282    }
5283    Tcl_DeleteCommandFromToken( movPtr->interp, movPtr->widgetCmd );
5284
5285    /*
5286     * Remove from the display list.
5287     */
5288
5289    RemoveMovieFromOpenMovies( movPtr );
5290
5291    /*
5292     * This is the "protected" free procedure. Anything Tcl_Preserve'ed
5293     * is not freed until Tcl_Release'ed.
5294     */
5295
5296    Tcl_EventuallyFree( (ClientData) movPtr, MoviePlayerFree );
5297}
5298
5299/*
5300 *----------------------------------------------------------------------
5301 *
5302 * MoviePlayerFree --
5303 *
5304 *		Does the real job of freeing memory, Movie, and MovieController.
5305 *
5306 * Results:
5307 *		None.
5308 *
5309 * Side effects:
5310 *		Hopefully frees memory.
5311 *
5312 *----------------------------------------------------------------------
5313 */
5314
5315static void
5316MoviePlayerFree( char *clientData )
5317{
5318    MoviePlayer 	*movPtr = (MoviePlayer *) clientData;
5319    int     		i;
5320
5321    QTTclDebugPrintf( movPtr->interp, 2, "MoviePlayerFree: movPtr=%d", movPtr );
5322
5323    if (movPtr->trackSelect != NULL) {
5324	ckfree( (char *) movPtr->trackSelect );
5325	movPtr->trackSelect = NULL;
5326    }
5327
5328    /* Delete and free any async load async callbacks. */
5329    AsyncLoadFree( movPtr );
5330
5331    if (movPtr->aMovie != NULL) {
5332	for (i = 0; i < movPtr->undoCount; i++) {
5333	    DisposeMovieEditState( movPtr->editStates[i] );
5334	}
5335	movPtr->undoCount = 0;
5336	if (movPtr->editStates) {
5337	    ckfree( (char *) movPtr->editStates );
5338	}
5339	CallBackFree( movPtr );
5340
5341	/*
5342	 * If called from inside the -mccommand proc we destroy the actual movie at idle.
5343	 */
5344
5345        if (movPtr->insideMCCommand) {
5346            movPtr->tmpMovieRecordPtr = (TmpMovieRecord *) ckalloc( sizeof(TmpMovieRecord) );
5347            movPtr->tmpMovieRecordPtr->movie = movPtr->aMovie;
5348            movPtr->tmpMovieRecordPtr->movieController = movPtr->aController;
5349    	    SetMovieGWorld( movPtr->aMovie, gOffscreenGWorldPtr, NULL );
5350            movPtr->aMovie = NULL;
5351            movPtr->aController = NULL;
5352        } else {
5353            if (movPtr->aController != NULL) {
5354                DisposeMovieController( movPtr->aController );
5355		movPtr->aController = NULL;
5356	    }
5357	    if (movPtr->aMovie != NULL) {
5358            	QTTclDebugPrintf( movPtr->interp, 2, "\taMovie=%d", movPtr->aMovie );
5359		DisposeMovie( movPtr->aMovie );
5360		movPtr->aMovie = NULL;
5361                gMovieRefCount--;
5362	    }
5363        }
5364    }
5365
5366#ifdef _WIN32
5367    if (movPtr->hwnd != NULL) {
5368        /* There have been a previous crash problem from DestroyPortAssociation
5369         * but that was likely due to ExitMovies could be called before this. */
5370	/* IMPORTANT: Must be after movie is disposed!!! */
5371    	DestroyPortAssociation( (CGrafPtr) GetHWNDPort(movPtr->hwnd) );
5372        movPtr->hwnd = NULL;
5373    }
5374#endif
5375
5376    Tk_FreeConfigOptions( (char *) movPtr, movPtr->optionTable, movPtr->tkwin );
5377    ckfree( (char *) movPtr );
5378
5379    /*
5380     * If this is the last movie destroyed and the exit handler
5381     * has been called and not yet freed it must be done now.
5382     */
5383    if ((gExitState == kQTTclExitStateExitHandlerCalled) && (gMovieRefCount <= 0)) {
5384        ExitMoviePlayer( NULL );
5385    }
5386}
5387
5388/*
5389 *----------------------------------------------------------------------
5390 *
5391 * DisposeMovieAtIdle --
5392 *
5393 * 		When doing 'configure -file' from the '-mccommand' tcl proc it
5394 *		crashes for QTVR movies if not disposing movie at idle.
5395 *
5396 * Results:
5397 *		None.
5398 *
5399 * Side effects:
5400 *		Movie and controller disposed.
5401 *
5402 *----------------------------------------------------------------------
5403 */
5404
5405static void
5406DisposeMovieAtIdle( ClientData clientData )
5407{
5408    MoviePlayer 	*movPtr = (MoviePlayer *) clientData;
5409
5410    QTTclDebugPrintf( movPtr->interp, 2, "DisposeMovieAtIdle" );
5411
5412    if (gExitState > 0) {
5413        return;
5414    }
5415    if (movPtr->tmpMovieRecordPtr == NULL) {
5416        return;
5417    }
5418    if (movPtr->tmpMovieRecordPtr->movieController != NULL) {
5419        DisposeMovieController( movPtr->tmpMovieRecordPtr->movieController );
5420    }
5421    if (movPtr->tmpMovieRecordPtr->movie != NULL) {
5422    	QTTclDebugPrintf( movPtr->interp, 2, "\tmovPtr->tmpMovieRecordPtr->movie=%d",
5423			 movPtr->tmpMovieRecordPtr->movie );
5424	DisposeMovie( movPtr->tmpMovieRecordPtr->movie );
5425        gMovieRefCount--;
5426    }
5427    ckfree((char *) movPtr->tmpMovieRecordPtr);
5428    movPtr->tmpMovieRecordPtr = NULL;
5429}
5430
5431/*
5432 *----------------------------------------------------------------------
5433 *
5434 * MoviePlayerWorldChanged --
5435 *
5436 *		Something changed, arange for the movie to be redisplayed.
5437 *  	Find its geometry including any movie controller, and request
5438 *		this size from Tk.
5439 *
5440 * Results:
5441 *		None.
5442 *
5443 * Side effects:
5444 *		Movie widget displayed sometime in the future.
5445 *
5446 *----------------------------------------------------------------------
5447 */
5448
5449void
5450MoviePlayerWorldChanged (ClientData instanceData)
5451{
5452    MoviePlayer     *movPtr = (MoviePlayer *) instanceData;
5453    Rect            aRect;
5454
5455    QTTclDebugPrintf( movPtr->interp, 2, "MoviePlayerWorldChanged" );
5456    {
5457        GWorldPtr		    destPort = NULL;
5458
5459        GetMovieGWorld( movPtr->aMovie, &destPort, NULL );
5460        QTTclDebugPrintf( movPtr->interp, 4, "\tGetMovieGWorld=0x%.8x", destPort );
5461    }
5462    if (movPtr->tkwin && Tk_IsMapped(movPtr->tkwin) && !(movPtr->flags & REDRAW_PENDING)) {
5463	Tcl_DoWhenIdle( DisplayMovie, (ClientData) movPtr );
5464	movPtr->flags |= REDRAW_PENDING;
5465    }
5466
5467    if (movPtr->aMovie == NULL) {
5468	return;
5469    }
5470
5471    /*
5472     * Cache info about visual or not; Windows need to serve unmapped audio only movies.
5473     */
5474
5475    if (GetMovieIndTrackType( movPtr->aMovie, 1, VisualMediaCharacteristic,
5476			     movieTrackCharacteristic ) == NULL) {
5477	movPtr->isVisual = 0;
5478    } else {
5479	movPtr->isVisual = 1;
5480    }
5481
5482    if ((movPtr->state & kQTTclMovieStateAsyncLoading) &&
5483	(movPtr->loadState < kMovieLoadStatePlayable)) {
5484
5485	/*
5486	 * During the first stage of async loading we give it a default size (compare frame).
5487	 */
5488
5489	Tk_GeometryRequest( movPtr->tkwin, 1, 1 );
5490	Tk_SetInternalBorder( movPtr->tkwin, 0 );
5491
5492    } else {
5493
5494	/*
5495	 * It may happen that a movie controller is not added in 'ConfigureMoviePlayer',
5496	 * for instance if we start creating an empty movie with 'Movie .m', and then
5497	 * pastes data into it.
5498	 */
5499
5500	AddOrRemoveMovieController( movPtr );
5501
5502	/*
5503	 * Get the total size requested, with or without a controller.
5504	 */
5505
5506	aRect.left = 0;
5507	aRect.top = 0;
5508	aRect.right = movPtr->mwidth;
5509	aRect.bottom = movPtr->mheight;
5510	if (movPtr->wantController && movPtr->aController != NULL) {
5511	    MCGetControllerBoundsRect( movPtr->aController, &aRect );
5512	} else if (movPtr->aMovie != NULL) {		// testing...
5513	    GetMovieBox( movPtr->aMovie, &aRect );
5514	}
5515	MacOffsetRect( &aRect, (short) -aRect.left, (short) -aRect.top );
5516
5517	QTTclDebugPrintf( movPtr->interp, 2, "\taRect.right=%d, aRect.bottom=%d",
5518			 aRect.right, aRect.bottom );
5519
5520	/*
5521	 * If any requested particular size (different from the default 0),
5522	 * adjust the rectangle size here.
5523	 */
5524
5525	if (movPtr->width) {
5526	    aRect.right = movPtr->width;
5527	}
5528	if (movPtr->height) {
5529	    aRect.bottom = movPtr->height;
5530	}
5531
5532	/*
5533	 * What if any ->width or ->height equal to zero? Get natural size instead!
5534	 */
5535
5536	if (movPtr->width == 0) {
5537	    if (movPtr->wantController && movPtr->aController != NULL) {
5538		if (movPtr->mwidth == 0) {
5539		    aRect.right = CONTROLLER_WIDTH;		/* bad!!! */
5540		} else {
5541		    aRect.right = movPtr->mwidth;
5542		}
5543	    } else {
5544		aRect.right = movPtr->mwidth;
5545	    }
5546	}
5547	if (movPtr->height == 0) {
5548	    if (movPtr->wantController && movPtr->aController != NULL) {
5549		aRect.bottom = movPtr->mheight + movPtr->controllerHeight;
5550	    } else {
5551		aRect.bottom = movPtr->mheight;
5552	    }
5553	}
5554
5555	/* Padding not supported for the moment. */
5556	aRect.right += 2 * movPtr->padx + 2 * movPtr->highlightWidth;
5557	aRect.bottom += 2 * movPtr->pady + 2 * movPtr->highlightWidth;
5558
5559	Tk_GeometryRequest( movPtr->tkwin, aRect.right, aRect.bottom );
5560	//Tk_SetInternalBorder( movPtr->tkwin, movPtr->highlightWidth );
5561	movPtr->flags |= NEWGWORLD;
5562
5563	QTTclDebugPrintf( movPtr->interp, 2, "\treq size width=%d, height=%d",
5564			 aRect.right, aRect.bottom );
5565    }   // end if not kQTTclMovieStateAsyncLoading
5566}
5567
5568/*
5569 *----------------------------------------------------------------------
5570 *
5571 * CreateMoviePlayer --
5572 *
5573 *		Create a MoviePlayer Object.
5574 *
5575 * Results:
5576 *		Return pointer to movieplayer object
5577 *
5578 * Side effects:
5579 *		Memory allocated
5580 *
5581 *----------------------------------------------------------------------
5582 */
5583
5584static MoviePlayer *
5585CreateMoviePlayer( Tk_Window tkwin )
5586{
5587    MoviePlayer *movPtr;
5588
5589    movPtr = (MoviePlayer *) ckalloc( sizeof(MoviePlayer) );
5590    memset( (void *) movPtr, 0, sizeof(MoviePlayer) );
5591    movPtr->trackSelect = (TrackSelection *) ckalloc( sizeof(TrackSelection) );
5592    return movPtr;
5593}
5594
5595/*
5596 *----------------------------------------------------------------------
5597 *
5598 * AddOrRemoveMovieController --
5599 *
5600 *  	Attach any controller. Many of the configure options need a
5601 *		controller to work, and therefore we cannot wait to make the
5602 *		controller until it is displayed.
5603 *
5604 * Results:
5605 *		None
5606 *
5607 * Side effects:
5608 *		Controller created and attached, or disposed for the
5609 *		corresponding movie.
5610 *
5611 *----------------------------------------------------------------------
5612 */
5613
5614static void
5615AddOrRemoveMovieController( MoviePlayer *movPtr )
5616{
5617    Rect    aRect;
5618
5619    if (movPtr->aMovie != NULL) {
5620    	if (!movPtr->wantController && (movPtr->aController != NULL)) {
5621    	    DisposeMovieController( movPtr->aController );
5622	    movPtr->aController = NULL;
5623	    movPtr->flags |= NEWGWORLD;
5624	    movPtr->controllerHeight = 0;
5625        }
5626    	if (movPtr->wantController && (movPtr->aController == NULL)) {
5627            GWorldPtr		    theGWorldPtr = NULL;
5628
5629            /*
5630             * It appears to be a bug on Mac OS X that creating the controller
5631             * resets the movies GWorld to current port. Added workaround.
5632             */
5633
5634            GetMovieBox( movPtr->aMovie, &aRect );
5635            GetMovieGWorld( movPtr->aMovie, &theGWorldPtr, NULL );
5636	    movPtr->aController  = NewMovieController( movPtr->aMovie, &aRect,
5637						      mcTopLeftMovie );
5638            MCSetControllerPort( movPtr->aController, theGWorldPtr );
5639	    if (movPtr->mcEdit) {
5640		MCEnableEditing( movPtr->aController, true );
5641	    }
5642	    movPtr->controllerHeight = GetControllerBarHeight( movPtr->aController );
5643	    movPtr->flags |= NEWGWORLD;
5644        }
5645    }
5646}
5647
5648/*
5649 *----------------------------------------------------------------------
5650 *
5651 * AddMovieToOpenMovies --
5652 *
5653 *  	Add this movie to the global list of open movies.
5654 *
5655 * Results:
5656 *		None
5657 *
5658 * Side effects:
5659 *		Global movies list updated.
5660 *
5661 *----------------------------------------------------------------------
5662 */
5663
5664static void
5665AddMovieToOpenMovies( MoviePlayer *movPtr )
5666{
5667    MoviePlayerList     *movListPtr = NULL;
5668
5669#if TARGET_API_MAC_CARBON
5670    /*
5671     *  If this is the first movie we open on Carbon, be sure to set a timer
5672     *  to serve it and all that may follow.
5673     */
5674
5675    if (gCarbonMovieTimerRef == NULL) {
5676    	InstallMovieIdlingEventLoopTimer();
5677    }
5678#endif
5679
5680    QTTclDebugPrintf( movPtr->interp, 2, "AddMovieToOpenMovies: movPtr=%d", movPtr );
5681
5682    /*
5683     * Search to see if already in our list. If not, then add it.
5684     * gMoviePlayerListPtr always points to the first node, or NULL if none.
5685     */
5686
5687    movListPtr = gMoviePlayerListPtr;
5688    while ((movListPtr != NULL) && (movPtr != movListPtr->movPtr)) {
5689        movListPtr = movListPtr->next;
5690    }
5691    if (movListPtr == NULL) {
5692    	MoviePlayerList     *newMovListPtr = NULL;
5693
5694        /* Add as first in list (simplest!). */
5695        newMovListPtr = (MoviePlayerList *) ckalloc( sizeof(MoviePlayerList) );
5696        newMovListPtr->movPtr = movPtr;
5697        newMovListPtr->prev = NULL;
5698        newMovListPtr->next = gMoviePlayerListPtr;
5699        if (gMoviePlayerListPtr != NULL) {
5700            gMoviePlayerListPtr->prev = newMovListPtr;
5701        }
5702        gMoviePlayerListPtr = newMovListPtr;
5703    }
5704}
5705
5706static void
5707RemoveMovieFromOpenMovies( MoviePlayer *movPtr )
5708{
5709    MoviePlayerList     *movListPtr = NULL;
5710
5711    QTTclDebugPrintf( movPtr->interp, 2, "RemoveMovieFromOpenMovies: movPtr=%d", movPtr );
5712
5713    /*
5714     * Search to see if in our list. If then remove it.
5715     */
5716
5717    movListPtr = gMoviePlayerListPtr;
5718    while ((movListPtr != NULL) && (movPtr != movListPtr->movPtr)) {
5719        movListPtr = movListPtr->next;
5720    }
5721    if (movListPtr != NULL) {
5722	if (movListPtr->prev != NULL) {
5723	    movListPtr->prev->next = movListPtr->next;
5724	} else {
5725	    gMoviePlayerListPtr = movListPtr->next;
5726	}
5727	if (movListPtr->next != NULL) {
5728	    movListPtr->next->prev = movListPtr->prev;
5729	}
5730	ckfree( (char *) movListPtr );
5731    }
5732
5733#if TARGET_API_MAC_CARBON
5734    /*
5735     *  If this is the last movie we have on Carbon, be sure to remove the timer.
5736     */
5737
5738    if (gMoviePlayerListPtr == NULL) {
5739    	QTUninstallNextTaskNeededSoonerCallback(
5740						NewQTNextTaskNeededSoonerCallbackUPP( TaskNeededSoonerCallback ),
5741						(void *) gCarbonMovieTimerRef );
5742        RemoveEventLoopTimer( gCarbonMovieTimerRef );
5743        gCarbonMovieTimerRef = NULL;
5744    }
5745#endif
5746}
5747
5748/*
5749 *----------------------------------------------------------------------
5750 *
5751 * DisplayMovie --
5752 *
5753 *		Display a movie.
5754 *
5755 * Results:
5756 *		None.
5757 *
5758 * Side effects:
5759 *		With luck, the movie is displayed
5760 *
5761 *----------------------------------------------------------------------
5762 */
5763
5764static void
5765DisplayMovie( ClientData clientData )
5766{
5767    MoviePlayer         *movPtr = (MoviePlayer *) clientData;
5768    Tk_Window		tkwin = movPtr->tkwin;
5769    CGrafPtr 	        saveWorld = NULL;
5770    GDHandle 	        saveDevice = NULL;
5771    GWorldPtr           theGWorldPtr = NULL;
5772    Rect 		aRect;
5773    static RgnHandle    region = NULL;
5774    static RgnHandle    saveRegion = NULL;
5775    RgnHandle           regionHand = NULL;
5776
5777    /* The first time we allocate a region to help us with update events. */
5778    if (region == NULL) {
5779	region = NewRgn();
5780    }
5781    if (saveRegion == NULL) {
5782	saveRegion = NewRgn();
5783    }
5784    QTTclDebugPrintf( movPtr->interp, 2,
5785		     "DisplayMovie: flags=%1d%1d%1d%1d%d%d",
5786		     (movPtr->flags >> 5) & 0x1, (movPtr->flags >> 4) & 0x1,
5787		     (movPtr->flags >> 3) & 0x1, (movPtr->flags >> 2) & 0x1,
5788		     (movPtr->flags >> 1) & 0x1, (movPtr->flags >> 0) & 0x1 );
5789
5790    /*
5791     * Are we ready to display. Clear the redraw pending state.
5792     */
5793
5794    movPtr->flags &= ~REDRAW_PENDING;
5795    if ((movPtr->flags & MOVIE_DELETED) || !Tk_IsMapped(tkwin)) {
5796	return;
5797    }
5798    if ((movPtr->state & kQTTclMovieStateAsyncLoading) &&
5799	(movPtr->loadState < kMovieLoadStatePlayable)) {
5800
5801	/* If we are async loading and not yet playable. */
5802	return;
5803    }
5804    GetGWorld( &saveWorld, &saveDevice );
5805
5806    /*
5807     * Tk drawing: Draw the focus highlight if any.
5808     */
5809
5810    if (movPtr->highlightWidth > 0) {
5811        GC 		fgGC, bgGC;
5812        Pixmap	pixmap;
5813
5814        pixmap = (Pixmap) Tk_WindowId(tkwin);
5815        bgGC = Tk_GCForColor( movPtr->highlightBgColorPtr, pixmap );
5816        if (movPtr->flags & GOT_FOCUS) {
5817            fgGC = Tk_GCForColor( movPtr->highlightColorPtr, pixmap );
5818            TkpDrawHighlightBorder( tkwin, fgGC, bgGC, movPtr->highlightWidth, pixmap );
5819        } else {
5820            TkpDrawHighlightBorder( tkwin, bgGC, bgGC, movPtr->highlightWidth, pixmap );
5821        }
5822    }
5823
5824    if (movPtr->aMovie != NULL) {
5825	movPtr->display->request++;
5826
5827	theGWorldPtr = QTTclMacGetDrawablePort( Tk_WindowId( tkwin ) );
5828	SetGWorld( theGWorldPtr, NULL );
5829	GetClip( saveRegion );
5830	QTTclMacWinBounds( (TkWindow *) tkwin, &aRect );
5831
5832	aRect.top += movPtr->inset;
5833	aRect.bottom -= movPtr->inset;
5834	aRect.left += movPtr->inset;
5835	aRect.right -= movPtr->inset;
5836
5837	if (movPtr->flags & NEWGWORLD) {
5838
5839	    /*
5840	     * It is first when the movie is to be displayed we know which graphics
5841	     * port it should be associated with.
5842	     * The 'SetMovieGWorld' is costy so set it only if the graphics port
5843	     * actually changed. Bug: even if changed the address may be the same?
5844	     */
5845
5846	    if ((movPtr->grafPtr == NULL) || (movPtr->grafPtr != theGWorldPtr)) {
5847		SetMovieGWorld( movPtr->aMovie, theGWorldPtr, NULL );
5848		movPtr->grafPtr = theGWorldPtr;
5849            	QTTclDebugPrintf( movPtr->interp, 3, "\tSetMovieGWorld()" );
5850	    }
5851	    if (movPtr->aController != NULL) {
5852		MCSetControllerPort( movPtr->aController, theGWorldPtr );
5853	    }
5854
5855	    /*
5856	     * Be sure to set the movie active here, only to be really sure.
5857	     */
5858
5859	    SetMovieActive( movPtr->aMovie, true );
5860
5861	    /*
5862	     * We should determine if the toplevel is an actice window or not.
5863	     * If not, deactivate the controller. Once this initial state is set correctly,
5864	     * the controller seems to get these events and set its correct state automatically.
5865	     */
5866
5867#if TARGET_OS_MAC
5868            QTTclDebugPrintf( movPtr->interp, 3, "\tMyIsToplevelInFront %d",
5869			     MyIsToplevelInFront( tkwin ) );
5870
5871	    if (movPtr->aController != NULL) {
5872                if (MyIsToplevelInFront( tkwin )) {
5873                    MCDoAction( movPtr->aController, mcActionActivate, NULL );
5874                } else {
5875                    MCDoAction( movPtr->aController, mcActionDeactivate, NULL );
5876                }
5877		MCMovieChanged( movPtr->aController, movPtr->aMovie );
5878	    }
5879#endif      // TARGET_OS_MAC
5880
5881#if TARGET_OS_MAC	// Windows should manage this automatically through its subwindows.
5882
5883	    /*
5884	     * Setup clipping region. Need to check in the event loop if something happened that
5885	     * changed the clipping region. Once a clipping region has been set it is valid
5886	     * for the lifetime of the movie.
5887	     * The clipping regions for the window and its children are
5888	     * marked invalid.  (Make sure they are valid before drawing.)
5889	     */
5890
5891#if TARGET_API_MAC_CARBON
5892            TkMacOSXInvalClipRgns( ((TkWindow *) (movPtr->tkwin))->privatePtr->winPtr->parentPtr );
5893#else
5894	    TkMacInvalClipRgns( ((TkWindow *) (movPtr->tkwin))->privatePtr->winPtr->parentPtr );
5895#endif
5896	    regionHand = QTTclMacVisableClipRgn( ((TkWindow *) (movPtr->tkwin)) );
5897	    if (movPtr->aController != NULL) {
5898		MCSetClip( movPtr->aController, regionHand, NULL );
5899	    } else {
5900		SetMovieDisplayClipRgn( movPtr->aMovie, regionHand );
5901	    }
5902#endif      // TARGET_OS_MAC
5903
5904	    /*
5905	     * Add movie to our list of displayed movies only if it's not there already.
5906	     */
5907
5908	    AddMovieToOpenMovies( movPtr );
5909	    movPtr->state |= kQTTclMovieStateOnDisplay;
5910	    movPtr->flags &= ~NEWGWORLD;
5911	}	// end NEWGWORLD
5912
5913	if ((movPtr->flags & UPDATEMOVIE) && (movPtr->aController == NULL)) {
5914
5915	    /*
5916	     * 'MCIsPlayerEvent' takes care of update (Expose) events
5917	     * via the controller.
5918	     * InvalidateMovieRegion is the preferred way to treat update events
5919	     * when there is no controller. Perhaps we could get the exact region that
5920	     * needs to be updated and not the complete movie rectangle.
5921	     */
5922
5923#if !TARGET_API_MAC_CARBON
5924            /* The update event this generates is cleared in the mac event loop. */
5925	    RectRgn( region, &aRect );
5926	    InvalidateMovieRegion( movPtr->aMovie, region );
5927#endif
5928
5929#if TARGET_API_MAC_CARBON
5930            /*
5931             * This triggers a new update event which calls Display() again in an
5932             * infinite loop!!! Never do this! Does not seems to be neeed anyway.
5933             */
5934            //InvalWindowRect( GetWindowFromPort(theGWorldPtr), &aRect );
5935#endif
5936	}
5937	movPtr->flags &= ~UPDATEMOVIE;
5938
5939	/*
5940	 * Important, set the size and position of the movie in the actual window as
5941	 * indicated by TkMacWinBounds above.
5942	 */
5943
5944	if (movPtr->aController != NULL) {
5945	    MCSetControllerBoundsRect( movPtr->aController, &aRect );
5946	} else {
5947	    SetMovieBox( movPtr->aMovie, &aRect) ;
5948	}
5949
5950	if (movPtr->flags & NEED_PREROLL) {
5951	    // testing... PreRoll'ing should be made as late as possible. First time here.
5952	    /* this crashes on Windows for QT 5
5953	     PrerollMovie( movPtr->aMovie, GetMovieTime( movPtr->aMovie, NULL ),
5954	     GetMoviePreferredRate( movPtr->aMovie ) ); */
5955	    movPtr->flags &= ~NEED_PREROLL;
5956	}
5957	SetClip( saveRegion );
5958    }
5959    SetGWorld( saveWorld, saveDevice );
5960}
5961
5962/*
5963 *----------------------------------------------------------------------
5964 *
5965 * SetMoviePlayerRectBox --
5966 *
5967 *		Sets the movies display rectangle in mac window.
5968 *
5969 * Results:
5970 *		None.
5971 *
5972 * Side effects:
5973 *		None.
5974 *
5975 *----------------------------------------------------------------------
5976 */
5977
5978#if TARGET_OS_MAC
5979static void
5980SetMoviePlayerRectBox( MoviePlayer *movPtr )
5981{
5982    Rect	aRect;
5983    Rect	tkRect;
5984
5985    QTTclMacWinBounds( (TkWindow *) movPtr->tkwin, &tkRect );
5986    tkRect.left += movPtr->inset;
5987    tkRect.top += movPtr->inset;
5988    tkRect.right -= movPtr->inset;
5989    tkRect.bottom -= movPtr->inset;
5990    if (movPtr->aController != NULL) {
5991        MCGetControllerBoundsRect( movPtr->aController, &aRect );
5992        if (!MacEqualRect( &tkRect, &aRect )) {
5993            MCSetControllerBoundsRect( movPtr->aController, &tkRect );
5994        }
5995    } else {
5996        GetMovieBox( movPtr->aMovie, &aRect);
5997        if (!MacEqualRect( &tkRect, &aRect )) {
5998            SetMovieBox( movPtr->aMovie, &tkRect);
5999        }
6000    }
6001}
6002#endif
6003
6004/*
6005 *----------------------------------------------------------------------
6006 *
6007 * InstallMovieIdlingEventLoopTimer, TaskNeededSoonerCallback,
6008 * 		CarbonTimerNextTime --
6009 *
6010 *		Sets up Carbon timers to serve movies.
6011 *
6012 * Results:
6013 *		.
6014 *
6015 * Side effects:
6016 *		Timers installed etc.
6017 *
6018 *----------------------------------------------------------------------
6019 */
6020
6021#if TARGET_API_MAC_CARBON
6022static OSStatus
6023InstallMovieIdlingEventLoopTimer( void )
6024{
6025    OSStatus 		err;
6026
6027    err = InstallEventLoopTimer( GetMainEventLoop(),
6028				0, 											/* firedelay */
6029				kEventDurationMillisecond * kMinimumIdleDurationInMillis, /* interval */
6030				NewEventLoopTimerUPP( MoviePlayerCarbonTimer ),
6031				NULL,
6032				&gCarbonMovieTimerRef );
6033    if (err != noErr) {
6034	/*
6035	 * Install a callback that the Idle Manager will use when
6036	 * QuickTime needs to wake me up immediately.
6037	 */
6038
6039	err = QTInstallNextTaskNeededSoonerCallback(
6040						    NewQTNextTaskNeededSoonerCallbackUPP( TaskNeededSoonerCallback ),
6041						    1000, 			/* Millisecond timescale */
6042						    0, 				/* No flags */
6043						    (void *) gCarbonMovieTimerRef );
6044    }
6045    return err;
6046}
6047
6048static void
6049TaskNeededSoonerCallback( TimeValue duration,
6050			 unsigned long flags,
6051			 void *refcon )
6052{
6053    SetEventLoopTimerNextFireTime( (EventLoopTimerRef) refcon,
6054				  duration * kEventDurationMillisecond );
6055}
6056
6057static void
6058CarbonTimerNextTime( void )
6059{
6060    OSStatus		err = noErr;
6061    long			durationMillis;
6062
6063    err = QTGetTimeUntilNextTask( &durationMillis, 1000 );
6064    if (durationMillis == 0) {
6065	durationMillis = kMinimumIdleDurationInMillis;
6066    }
6067    // testing...
6068    /* This is only a temporary workaround for missing events
6069     * when movie is without a controller.
6070     */
6071    durationMillis = 50;
6072    // end testing...
6073
6074    SetEventLoopTimerNextFireTime( gCarbonMovieTimerRef,
6075				  durationMillis * kEventDurationMillisecond );
6076}
6077
6078#endif
6079
6080/*
6081 *----------------------------------------------------------------------
6082 *
6083 * MoviePlayerCarbonTimer --
6084 *
6085 *		Timer callback to serve movies under Carbon.
6086 *
6087 * Results:
6088 *		None.
6089 *
6090 * Side effects:
6091 *		Calls 'MoviePlayerMacEvent'.
6092 *
6093 *----------------------------------------------------------------------
6094 */
6095
6096#if TARGET_API_MAC_CARBON
6097static pascal void
6098MoviePlayerCarbonTimer( EventLoopTimerRef theTimer,
6099		       void *userData )
6100{
6101    /*
6102     * The first NULL (EventRecord *) just says idle movie.
6103     * The second NULL says idle all movies.
6104     */
6105
6106    MoviePlayerMacEvent( NULL, NULL );
6107}
6108#endif
6109
6110/*
6111 *----------------------------------------------------------------------
6112 *
6113 * InstallCarbonWindowEventHandler,
6114 *		MovieDestroyCarbonWindowHandlerCleanup --
6115 *
6116 *		Makes sure that there is always a Carbon window event handler.
6117 *		Removed when not useful anymore.
6118 *
6119 * Results:
6120 *		None.
6121 *
6122 * Side effects:
6123 *		May install or remove Carbon window event handler.
6124 *
6125 *----------------------------------------------------------------------
6126 */
6127
6128#if TARGET_API_MAC_CARBON
6129static void
6130InstallCarbonWindowEventHandler( MoviePlayer *movPtr )
6131{
6132    WindowRef			windowRef;		/* identical to WindowPtr */
6133    EventHandlerRef		handlerRef;
6134    OSStatus			err;
6135    Tcl_HashEntry   	*hashPtr = NULL;
6136    CarbonWindowHandlerEntry	*entryPtr;
6137    int					isNew;
6138    const EventTypeSpec kEvents[] = {
6139	{kEventClassWindow,		kEventWindowUpdate},
6140	{kEventClassWindow,		kEventWindowActivated},
6141	{kEventClassWindow,		kEventWindowDeactivated},
6142	{kEventClassMouse,		kEventMouseDown},
6143	{kEventClassMouse,		kEventMouseUp},
6144	{kEventClassMouse,		kEventMouseDragged}		/* Needed in controller,
6145	 * volume, and panos. */
6146    };
6147
6148    /*
6149     * We shall keep a single carbon window event handler for each toplevel
6150     * window that contains a displayed movie.
6151     */
6152
6153    if (gCarbonWindowHandlerHashPtr == NULL) {
6154        gCarbonWindowHandlerHashPtr = (Tcl_HashTable *) ckalloc( sizeof(Tcl_HashTable) );
6155	Tcl_InitHashTable( gCarbonWindowHandlerHashPtr, TCL_ONE_WORD_KEYS );
6156    }
6157    windowRef = GetWindowFromPort( TkMacOSXGetDrawablePort( Tk_WindowId(movPtr->tkwin)) );
6158
6159    /*
6160     * If already in our hash table, just add a refCount, else create it.
6161     */
6162
6163    hashPtr = Tcl_FindHashEntry( gCarbonWindowHandlerHashPtr, (char *) windowRef );
6164    if (hashPtr != NULL) {
6165	entryPtr = (CarbonWindowHandlerEntry *) Tcl_GetHashValue( hashPtr );
6166	entryPtr->refCount++;
6167
6168        QTTclDebugPrintf( movPtr->interp, 2,
6169			 "InstallCarbonWindowEventHandler: (exists) refCount=%d, windowRef=0x%.8x",
6170			 entryPtr->refCount, windowRef );
6171    } else {
6172	err = InstallWindowEventHandler( windowRef,
6173					NewEventHandlerUPP( MovieCarbonWindowEventHandler ),
6174					GetEventTypeCount(kEvents),
6175					&kEvents, 						/* const EventTypeSpec*  */
6176					(void *) movPtr, &handlerRef );
6177	if (err == noErr) {
6178            entryPtr = (CarbonWindowHandlerEntry *) ckalloc( sizeof(CarbonWindowHandlerEntry) );
6179            entryPtr->handlerRef = handlerRef;
6180            entryPtr->refCount = 1;
6181            hashPtr = Tcl_CreateHashEntry( gCarbonWindowHandlerHashPtr,
6182					  (char *) windowRef, &isNew );
6183            Tcl_SetHashValue( hashPtr, entryPtr );
6184        }
6185        QTTclDebugPrintf( movPtr->interp, 2,
6186			 "InstallCarbonWindowEventHandler: (new) refCount=%d, windowRef=0x%.8x",
6187			 entryPtr->refCount, windowRef );
6188    }
6189}
6190#endif
6191
6192#if TARGET_API_MAC_CARBON
6193static void
6194MovieDestroyCarbonWindowHandlerCleanup( MoviePlayer *movPtr )
6195{
6196    WindowRef			windowRef = NULL;		/* identical to WindowPtr */
6197    Tcl_HashEntry   	*hashPtr = NULL;
6198    CarbonWindowHandlerEntry	*entryPtr;
6199    OSStatus			err = noErr;
6200
6201    QTTclDebugPrintf( movPtr->interp, 2,
6202		     "MovieDestroyCarbonWindowHandlerCleanup: gCarbonWindowHandlerHashPtr=%d",
6203		     gCarbonWindowHandlerHashPtr );
6204    if (gCarbonWindowHandlerHashPtr == NULL) {
6205        return;
6206    }
6207    if (movPtr->tkwin == NULL) {
6208        return;
6209    }
6210    windowRef = GetWindowFromPort( TkMacOSXGetDrawablePort( Tk_WindowId(movPtr->tkwin)) );
6211    if (windowRef == NULL) {
6212	return;
6213    }
6214    hashPtr = Tcl_FindHashEntry( gCarbonWindowHandlerHashPtr, (char *) windowRef );
6215    if (hashPtr == NULL) {
6216	return;
6217    }
6218    entryPtr = Tcl_GetHashValue( hashPtr );
6219    entryPtr->refCount--;
6220
6221    QTTclDebugPrintf( movPtr->interp, 2, "\trefCount=%d, windowRef=0x%.8x",
6222		     entryPtr->refCount, windowRef );
6223
6224    if (entryPtr->refCount <= 0) {
6225
6226	/*
6227	 * No more movies in this mac window; remove event handler.
6228	 */
6229
6230	err = RemoveEventHandler( entryPtr->handlerRef );
6231	ckfree( (char *) entryPtr );
6232	Tcl_DeleteHashEntry( hashPtr );
6233    }
6234}
6235#endif
6236
6237/*
6238 *----------------------------------------------------------------------
6239 *
6240 * MovieCarbonWindowEventHandler --
6241 *
6242 *		Carbon window event handler for the Movie Player. Not related to
6243 *		the Carbon timers.
6244 *
6245 * Results:
6246 *		OSStatus if event handled or not.
6247 *
6248 * Side effects:
6249 *		Depending on the event.
6250 *
6251 *----------------------------------------------------------------------
6252 */
6253
6254#if TARGET_API_MAC_CARBON
6255static OSStatus
6256MovieCarbonWindowEventHandler( EventHandlerCallRef callRef,
6257			      EventRef eventRef,
6258			      void *userData )
6259{
6260    MoviePlayer 		*movPtr = (MoviePlayer *) userData;
6261    OSStatus			returnOSStatus = eventNotHandledErr;
6262    EventRecord			eventRec;
6263    WindowRef			windowRef = NULL;
6264    OSStatus			err = noErr;
6265
6266    /* Lots of temporary debug stuff. */
6267    UInt32				eventClass;
6268    UInt32				eventKind;
6269    char				tmp1[64];
6270    char				tmp2[64];
6271
6272    QTTclDebugPrintf( movPtr->interp, 3,
6273		     "MovieCarbonWindowEventHandler: movPtr->tkwin=%d", movPtr->tkwin );
6274
6275    /* We are being destroyed. */
6276    if (movPtr->flags & MOVIE_DELETED) {
6277        return returnOSStatus;
6278    }
6279    eventClass = GetEventClass( eventRef );
6280    eventKind = GetEventKind( eventRef );
6281    memset( tmp1, 0, 64 );
6282    memcpy( tmp1, &eventClass, 4 );
6283
6284    switch (eventClass) {
6285        case kEventClassWindow: {
6286
6287            err = GetEventParameter( eventRef, kEventParamDirectObject, typeWindowRef,
6288				    NULL, sizeof(windowRef), NULL, &windowRef );
6289
6290            switch (eventKind) {
6291                case kEventWindowDrawContent: strcpy( tmp2, "kEventWindowDrawContent" ); break;
6292                case kEventWindowUpdate: strcpy( tmp2, "kEventWindowUpdate" ); break;
6293                case kEventWindowActivated: strcpy( tmp2, "kEventWindowActivated" ); break;
6294                case kEventWindowDeactivated: strcpy( tmp2, "kEventWindowDeactivated" ); break;
6295            }
6296            break;
6297        }
6298        case kEventClassMouse: {
6299
6300            err = GetEventParameter( eventRef, kEventParamWindowRef, typeWindowRef,
6301				    NULL, sizeof(windowRef), NULL, &windowRef );
6302
6303            switch (eventKind) {
6304                case kEventMouseDown: strcpy( tmp2, "kEventMouseDown" ); break;
6305                case kEventMouseUp: strcpy( tmp2, "kEventMouseUp" ); break;
6306                case kEventMouseDragged: strcpy( tmp2, "kEventMouseDragged" ); break;
6307            }
6308            break;
6309        }
6310    }
6311
6312    QTTclDebugPrintf( movPtr->interp, 3, "\twindowRef=0x%.8x  %s  %s",
6313		     windowRef, tmp1, tmp2 );
6314
6315    if (err == noErr) {
6316	ConvertEventRefToEventRecord( eventRef, &eventRec );
6317    	MoviePlayerMacEvent( &eventRec, windowRef );
6318    }
6319    return returnOSStatus;
6320}
6321#endif
6322
6323/*
6324 *----------------------------------------------------------------------
6325 *
6326 * MoviePlayerMacEvent --
6327 *
6328 *		Processes mac events. Gets events until none of them are used by
6329 *		any movies or dialogs (Mac).
6330 *  	Update (Expose) events for a movie without a controller are
6331 *		dealt with here.
6332 *
6333 * Results:
6334 *		0 if the event was not handled, 1 if it was.
6335 *
6336 * Side effects:
6337 *		Events processed.
6338 *
6339 *----------------------------------------------------------------------
6340 */
6341
6342#if TARGET_OS_MAC
6343
6344static int
6345MoviePlayerMacEvent(
6346		    EventRecord *eventPtr,			/* If NULL just idle movie. */
6347		    WindowRef serveWindowRef )		/* If NULL serve all movies in our list,
6348 * else serve only movies in this mac
6349 * window. */
6350{
6351    MoviePlayer 	    *movPtr;
6352    Movie			    theMovie = NULL;
6353    MoviePlayerList     *movListPtr = NULL;
6354    MoviePlayerList     *nextMovListPtr = NULL;
6355    GWorldPtr 		    saveWorld = NULL;
6356    GDHandle 		    saveDevice = NULL;
6357    GWorldPtr		    destPort = NULL;
6358    Rect        	    aRect;
6359    int         	    eventDone = false;
6360    int         	    clipInvalid = false;
6361    long    			maxMilliSecsToUse = 0;
6362    static long 	    counter = 0;
6363    static RgnHandle    saveRegion = NULL;
6364    static RgnHandle    region = NULL;
6365    RgnHandle           rgnHandle = NULL;
6366#if TARGET_API_MAC_CARBON
6367    int                 shortcutLoop = false;
6368#else
6369    /* if true we get mac events in a shortcut loop; never on Mac OS X */
6370    int                 shortcutLoop = true;
6371#endif
6372
6373    if (saveRegion == NULL) {
6374	saveRegion = NewRgn();
6375    }
6376    if (region == NULL) {
6377	region = NewRgn();
6378    }
6379
6380    /*
6381     * We keep a counter here to enable regular calls only at a fraction rate.
6382     * This is used for queries to the status when a movie is loaded remotely.
6383     */
6384
6385    counter++;
6386
6387    /*
6388     * Mac: process the incoming event, if movie player event, poll for new
6389     * events, handle them, until we get an event not aimed for us. See 'shortcutLoop'.
6390     * See "Inside Macintosh: QuickTime Components", p. 2-46.
6391     * Potentially dangerous???
6392     */
6393
6394    do {
6395	eventDone = false;
6396
6397	/*
6398	 * See if the event is meant for the effects parameter dialog box.
6399	 */
6400
6401        if (eventPtr != NULL) {
6402            eventDone = EffectsHandleDialogEvents( eventPtr, 0 );
6403	}
6404
6405	if (!eventDone) {
6406	    GetGWorld( &saveWorld, &saveDevice );
6407
6408            movListPtr = gMoviePlayerListPtr;
6409            while (movListPtr != NULL) {
6410                movPtr = movListPtr->movPtr;
6411                nextMovListPtr = movListPtr->next;
6412		theMovie = movPtr->aMovie;
6413
6414		if (movPtr->state & kQTTclMovieStateOnDisplay) {
6415                    if (movPtr->flags & MOVIE_DELETED) {
6416                        goto nextMovie;
6417                    }
6418		    if (serveWindowRef != NULL) {
6419
6420			/*
6421			 * Serve only movies in specified mac window.
6422			 */
6423
6424			if (GetWindowFromPort( movPtr->grafPtr ) != serveWindowRef) {
6425			    goto nextMovie;
6426			}
6427		    }
6428		    GetMovieGWorld( theMovie, &destPort, NULL );
6429		    SetGWorld( destPort, NULL );
6430		    GetClip( saveRegion );
6431
6432		    /*
6433		     * The 'ClipRect' affects the clipping of the controller, and must
6434		     * encompass it to get it drawn correctly.
6435		     * 'MCSetClip/SetMovieDisplayClipRgn' then sets the clipping of the
6436		     * actual movie.
6437		     * Setting clip here is absolutely necessary to get the controller drawn!
6438		     * BUG: if another widget 'place'ed on top of controller => inf loop;
6439		     * has something to do with clipping, test 'clipInvalid = false;'.
6440		     */
6441
6442		    QTTclMacWinBounds( (TkWindow *) movPtr->tkwin, &aRect );
6443		    RectRgn( region, &aRect );
6444		    SetClip( region );
6445                    clipInvalid = false;
6446
6447		    /*
6448		     * We must find out if the present clip region for the movie is invalid,
6449		     * and set a new one below if this is the case.
6450		     */
6451
6452		    if (MyIsClipRegionInvalid( movPtr->tkwin )) {
6453                        clipInvalid = true;
6454                        QTTclDebugPrintf( movPtr->interp, 3, "\tMyIsClipRegionInvalid" );
6455                    }
6456                    TkMacOSXSetUpClippingRgn( (Drawable) Tk_WindowId( movPtr->tkwin ) );
6457                    if (clipInvalid) {
6458                        rgnHandle = QTTclMacVisableClipRgn( (TkWindow *) movPtr->tkwin );
6459                        SetMoviePlayerRectBox( movPtr );
6460                        if (rgnHandle == NULL) {
6461                            clipInvalid = false;
6462                        }
6463                    }
6464
6465		    if (movPtr->aController != NULL) {
6466			if (clipInvalid) {
6467			    MCSetClip( movPtr->aController, rgnHandle, NULL );
6468			}
6469                        if (eventPtr == NULL) {
6470
6471                            /* Says we shall only idle movie. */
6472                            MCIdle( movPtr->aController );
6473                        } else {
6474                            if (MCIsPlayerEvent( movPtr->aController, eventPtr )) {
6475                                eventDone = 1;
6476                                break;
6477                            }
6478                        }
6479		    } else {		/* without a controller */
6480			if (theMovie != NULL) {
6481
6482			    /*
6483			     * For a movie without a controller, the rate is not reset to 0
6484			     * when the movie ends. Needs to do that provided it is not looping.
6485			     */
6486
6487                            /* Not working on MacOSX's event model!!! */
6488
6489			    if (!IsMovieLooping( theMovie )) {
6490				if (IsMovieDone( theMovie )) {
6491				    if (GetMovieRate( theMovie ) > 0) {
6492					SetMovieRate( theMovie, 0 );
6493                                        QTTclDebugPrintf( movPtr->interp, 3,
6494							 "\tMovieDone, SetMovieRate 0" );
6495				    }
6496				}
6497			    }
6498			    if (clipInvalid) {
6499				SetMovieDisplayClipRgn( theMovie, rgnHandle );
6500			    }
6501			    MoviesTask( theMovie, maxMilliSecsToUse );
6502			}
6503		    }
6504		    if (movPtr->loadCommand &&
6505			(movPtr->loadState < kMovieLoadStateComplete)) {
6506			if (counter % 10 == 0) {
6507			    CheckMovieLoadState(movPtr);
6508			}
6509		    }
6510		    SetClip( saveRegion );
6511
6512		} else if (movPtr->url != NULL) {
6513
6514		    /*
6515		     * If loading a remote movie (-url) it needs to be served while loading,
6516		     * but not displayed.
6517		     */
6518
6519		    MoviesTask( theMovie, maxMilliSecsToUse );
6520		    if (movPtr->loadCommand) {
6521			if (counter % 10 == 0) {
6522			    CheckMovieLoadState( movPtr );
6523			}
6524		    }
6525		}
6526
6527	    nextMovie:
6528
6529                movListPtr = nextMovListPtr;
6530	    }	/* end loop over movie player list */
6531
6532	    SetGWorld( saveWorld, saveDevice );
6533	}	/* end if !eventDone */
6534
6535	/*
6536	 * We should get all events that are "movie player" events before returning.
6537	 * The first event that is not handled by us is passed on.
6538         * We don't do this on Carbon.
6539	 */
6540
6541#if !TARGET_API_MAC_CARBON
6542	if (shortcutLoop && eventDone) {
6543	    WaitNextEvent( everyEvent, eventPtr, 0, NULL );
6544	}
6545#endif
6546
6547    } while (shortcutLoop && eventDone);
6548
6549#if TARGET_API_MAC_CARBON
6550    CarbonTimerNextTime();
6551#endif
6552
6553    return eventDone;
6554}
6555
6556#endif 	// TARGET_OS_MAC
6557
6558#ifdef _WIN32
6559/*
6560 *----------------------------------------------------------------------
6561 *
6562 * QTWinProc --
6563 *
6564 *		This is the window callback procedure for Windows only.
6565 *
6566 * Results:
6567 *		Same as the original WinProc.
6568 *
6569 * Side effects:
6570 *		First the Mac event procedure called, then the original WinProc.
6571 *
6572 *----------------------------------------------------------------------
6573 */
6574
6575LRESULT CALLBACK
6576QTWinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
6577{
6578    LONG                origProc = 0L;
6579    HWND                tempH = NULL;
6580    MoviePlayerList     *movListPtr = NULL;
6581    MoviePlayer			*movPtr;
6582
6583    if (GetNativeWindowPort(hWnd)) {
6584	MSG	        msg;
6585	EventRecord	macEvent;
6586	LONG        thePoint = GetMessagePos();
6587	int         retVal;
6588
6589	msg.hwnd = hWnd;
6590	msg.message = message;
6591	msg.wParam = wParam;
6592	msg.lParam = lParam;
6593	msg.time = GetMessageTime();
6594	msg.pt.x = LOWORD(thePoint);
6595	msg.pt.y = HIWORD(thePoint);
6596
6597	/* Convert the message to a QTML event */
6598	WinEventToMacEvent( &msg, &macEvent );
6599	retVal = MoviePlayerMacEventWin( &macEvent );
6600    }
6601
6602    /*
6603     * Dispatch the event to the correct original winproc from tk.
6604     */
6605
6606    movListPtr = gMoviePlayerListPtr;
6607    while (movListPtr != NULL) {
6608        movPtr = movListPtr->movPtr;
6609        if (movPtr->hwnd != NULL) {
6610	    if (hWnd == movPtr->hwnd) {
6611		origProc = movPtr->winEventProc;
6612		break;
6613	    }
6614        }
6615        movListPtr = movListPtr->next;
6616    }
6617
6618    if (origProc) {
6619	return CallWindowProc( (WNDPROC) origProc, hWnd, message, wParam, lParam );
6620    } else {
6621	return 0;
6622    }
6623}
6624
6625/*
6626 *----------------------------------------------------------------------
6627 *
6628 * MoviePlayerMacEventWin --
6629 *
6630 *		Processes mac events.
6631 *		Incoming event just filtered through all active movies and
6632 *		dialogs (Windows). Just a stripped down version of
6633 *		'MoviePlayerMacEvent'.
6634 *
6635 * Results:
6636 *		0 if the event was not handled, 1 if it was.
6637 *
6638 * Side effects:
6639 *		Events processed.
6640 *
6641 *----------------------------------------------------------------------
6642 */
6643
6644static int
6645MoviePlayerMacEventWin( EventRecord *eventPtr )
6646{
6647    MoviePlayer         *movPtr;
6648    Movie		        theMovie = NULL;
6649    MoviePlayerList     *movListPtr = NULL;
6650    MoviePlayerList     *nextMovListPtr = NULL;
6651    int                 eventDone = 0;
6652    static long         counter = 0;
6653    long    			maxMilliSecsToUse = 0;
6654
6655    /*
6656     * We keep a counter here to enable regular calls only at a fraction rate.
6657     * This is used for queries to the status when a movie is loaded remotely.
6658     */
6659
6660    counter++;
6661    movListPtr = gMoviePlayerListPtr;
6662
6663    while (movListPtr != NULL) {
6664        nextMovListPtr = movListPtr->next;
6665        movPtr = movListPtr->movPtr;
6666	theMovie = movPtr->aMovie;
6667
6668	/*
6669	 * Unmapped audio only movies need to be served on Windows, in contrast to Mac.
6670	 * The 'isVisual' member variable is cached with info if audio only or not.
6671	 */
6672
6673	if ((movPtr->state & kQTTclMovieStateOnDisplay) || !(movPtr->isVisual)) {
6674
6675	    if (movPtr->aController != NULL) {
6676		if (MCIsPlayerEvent( movPtr->aController, eventPtr )) {
6677		    eventDone = 1;
6678		}
6679	    } else {
6680		if (theMovie != NULL) {
6681
6682		    /*
6683		     * For a movie without a controller, the rate is not reset to 0
6684		     * when the movie ends. Needs to do that provided it is not looping.
6685		     */
6686
6687		    if (!IsMovieLooping( theMovie )) {
6688			if (IsMovieDone( theMovie )) {
6689			    if (GetMovieRate( theMovie ) > 0) {
6690				SetMovieRate( theMovie, 0 );
6691			    }
6692			}
6693		    }
6694		    MoviesTask( theMovie, maxMilliSecsToUse );
6695		}
6696	    }
6697	    if (movPtr->loadCommand && (movPtr->loadState < kMovieLoadStateComplete)) {
6698		if (counter % 10 == 0) {
6699		    CheckMovieLoadState(movPtr);
6700		}
6701	    }
6702
6703	} else if (movPtr->url != NULL) {
6704
6705	    /*
6706	     * If loading a remote movie (-url) it needs to be served while loading,
6707	     * but not displayed.
6708	     */
6709
6710	    MoviesTask( theMovie, maxMilliSecsToUse );
6711
6712	    /* does this increase the download speed? */
6713	    if (movPtr->loadCommand != NULL) {
6714		if (counter % 10 == 0) {
6715		    CheckMovieLoadState( movPtr );
6716		}
6717	    }
6718	}
6719
6720	/* Take next movie in our linked list. */
6721	movListPtr = nextMovListPtr;
6722    }
6723    return 0;
6724}
6725#endif      // _WIN32
6726
6727/*
6728 *----------------------------------------------------------------------
6729 *
6730 * CheckMovieLoadState --
6731 *
6732 *		During an async opening of a remote movie, we need to
6733 *		regularly query the movie's load state. Calls to 'MoviesTask'
6734 *		is necessary to keep the network process alive.
6735 *
6736 * Results:
6737 *		None.
6738 *
6739 * Side effects:
6740 *		Any registered Tcl procedure gets called if the load state changes.
6741 *
6742 *----------------------------------------------------------------------
6743 */
6744
6745static void
6746CheckMovieLoadState( MoviePlayer *movPtr )
6747{
6748    Tcl_Interp 		*interp = movPtr->interp;
6749    long            loadState = 0;
6750    ComponentResult errStatus;
6751    int				resTcl = TCL_OK;
6752    Tcl_Obj			*listObjPtr = NULL;
6753    int				useDirectCallback = false;
6754
6755    loadState = GetMovieLoadState( movPtr->aMovie );
6756
6757    if (loadState == movPtr->loadState) {
6758
6759	/* Movies state did not change compared to previous call. */
6760        return;
6761    }
6762    QTTclDebugPrintf( interp, 2, "CheckMovieLoadState loadState=%d",
6763		     loadState );
6764
6765    /*
6766     * Start designing the Tcl callback.
6767     * Note that movPtr->loadCommand may already be a list.
6768     */
6769
6770    if (movPtr->loadCommand) {
6771        listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
6772        Tcl_ListObjAppendList( interp, listObjPtr,
6773			      Tcl_NewStringObj(movPtr->loadCommand, -1) );
6774        Tcl_ListObjAppendElement( interp, listObjPtr,
6775				 Tcl_NewStringObj( Tcl_GetCommandName( interp, movPtr->widgetCmd ), -1 ) );
6776    }
6777
6778    if ((loadState < 0) || (loadState == kMovieLoadStateError)) {
6779
6780        /*
6781         * We've got an error. Find a way to add both an error code and the message
6782         * to the tcl callback procedure. 'GetMovieStatus' returns errors
6783         * occuring in 'MoviesTask', which is where we are when establishing
6784         * network connections etc.
6785         */
6786
6787	/* Reset the load state. */
6788	movPtr->loadState = loadState;
6789
6790        errStatus = GetMovieStatus( movPtr->aMovie, NULL );
6791        QTTclDebugPrintf( interp, 2, "\t GetMovieStatus errStatus=%d",
6792			 errStatus );
6793
6794        if (movPtr->loadCommand) {
6795            Tcl_ListObjAppendElement( interp, listObjPtr, Tcl_NewStringObj("error", -1) );
6796	    if (errStatus != noErr) {
6797                Tcl_Obj		*errObjPtr;
6798
6799                errObjPtr = GetErrorObj( errStatus );
6800                Tcl_ListObjAppendElement( interp, listObjPtr, errObjPtr );
6801	    } else {
6802                Tcl_ListObjAppendElement( interp, listObjPtr, Tcl_NewLongObj( errStatus ) );
6803	    }
6804            if (useDirectCallback) {
6805                resTcl = Tcl_Eval( interp, Tcl_GetString( listObjPtr ) );
6806                Tcl_DoOneEvent( TCL_DONT_WAIT | TCL_ALL_EVENTS );
6807            }
6808	}
6809
6810    } else if (loadState != movPtr->loadState) {
6811
6812	/* Reset the load state. */
6813	movPtr->loadState = loadState;
6814
6815	if ((loadState >= kMovieLoadStatePlayable) &&
6816	    !(movPtr->state & kQTTclMovieStateAsyncHasWorld)) {
6817
6818	    /*
6819	     * Now its time to query the movies size, and add a controller.
6820	     * Many of the configure options need a controller to work,
6821	     * and therefore we cannot wait to make the controller until it is displayed.
6822	     */
6823
6824            if (movPtr->aMovie != NULL) {
6825		Rect	    aRect;
6826
6827		GetMovieBox( movPtr->aMovie, &aRect );
6828		MacOffsetRect( &aRect, (short) -aRect.left, (short) -aRect.top );
6829		movPtr->mwidth = aRect.right;
6830		movPtr->mheight = aRect.bottom;
6831
6832                /*
6833                 * All configuration options are taken from the struct now,
6834                 * and we therefore need to force all settings (except -file -url etc).
6835                 */
6836
6837            	resTcl = ConfigureMoviePlayer( interp, movPtr, 0, NULL );
6838                if (resTcl != TCL_OK) {
6839                    Tcl_ListObjAppendElement( interp, listObjPtr,
6840					     Tcl_NewStringObj("error", -1) );
6841                    Tcl_ListObjAppendElement( interp, listObjPtr,
6842					     Tcl_GetObjResult( interp ) );
6843
6844                    if (useDirectCallback) {
6845                        resTcl = Tcl_Eval( interp, Tcl_GetString( listObjPtr ) );
6846                        Tcl_DoOneEvent( TCL_DONT_WAIT | TCL_ALL_EVENTS );
6847                    }
6848                    return;
6849                } else {
6850                    MoviePlayerWorldChanged( (ClientData) movPtr );
6851                    movPtr->state |= kQTTclMovieStateAsyncHasWorld;
6852                }
6853            }
6854	}
6855	if (loadState >= kMovieLoadStateComplete) {
6856
6857	    /*
6858	     * We are finished with async loading. Clear flag.
6859	     */
6860
6861	    movPtr->state &= ~kQTTclMovieStateAsyncLoading;
6862	}
6863
6864	/*
6865	 * Load state changed, call Tcl proc.
6866	 * Must not be called before 'MoviePlayerWorldChanged'!
6867	 */
6868
6869        if (movPtr->loadCommand) {
6870            if (loadState < kMovieLoadStatePlayable) {
6871                Tcl_ListObjAppendElement( interp, listObjPtr,
6872					 Tcl_NewStringObj("loading", -1) );
6873            } else if (loadState < kMovieLoadStatePlaythroughOK) {
6874                Tcl_ListObjAppendElement( interp, listObjPtr,
6875					 Tcl_NewStringObj("playthroughok", -1) );
6876            } else if (loadState < kMovieLoadStateComplete) {
6877                Tcl_ListObjAppendElement( interp, listObjPtr,
6878					 Tcl_NewStringObj("playable", -1) );
6879            } else if (loadState >= kMovieLoadStateComplete) {
6880                Tcl_ListObjAppendElement( interp, listObjPtr,
6881					 Tcl_NewStringObj("complete", -1) );
6882            }
6883            if (useDirectCallback) {
6884                resTcl = Tcl_Eval( interp, Tcl_GetString( listObjPtr ) );
6885                Tcl_DoOneEvent( TCL_DONT_WAIT | TCL_ALL_EVENTS );
6886            }
6887	}
6888    }
6889    if (useDirectCallback) {
6890        Tcl_DecrRefCount( listObjPtr );
6891    }
6892
6893    /*
6894     * Design an async callback to be executed as soon as possible.
6895     */
6896
6897    if (!useDirectCallback && listObjPtr != NULL) {
6898        Tcl_HashTable 		*asyncLoadHashTablePtr = movPtr->asyncLoadHashTablePtr;
6899        Tcl_HashEntry   	*hashPtr = NULL;
6900        AsyncLoadHandlerEntry 	*asyncHandlerEntryPtr;
6901        int					isNew;
6902
6903        /* Init hash table. */
6904        if (asyncLoadHashTablePtr == NULL) {
6905            movPtr->asyncLoadHashTablePtr = (Tcl_HashTable *)
6906	    ckalloc( sizeof(Tcl_HashTable) );
6907            asyncLoadHashTablePtr = movPtr->asyncLoadHashTablePtr;
6908            Tcl_InitHashTable( asyncLoadHashTablePtr, TCL_ONE_WORD_KEYS );
6909        }
6910
6911        /* Create the entry in hash table. */
6912        asyncHandlerEntryPtr = (AsyncLoadHandlerEntry *)
6913	ckalloc( sizeof(AsyncLoadHandlerEntry) );
6914        hashPtr = Tcl_CreateHashEntry( asyncLoadHashTablePtr,
6915				      (char *) asyncHandlerEntryPtr, &isNew );
6916        Tcl_SetHashValue( hashPtr, (char *) asyncHandlerEntryPtr );
6917        asyncHandlerEntryPtr->movPtr = movPtr;
6918        asyncHandlerEntryPtr->commandObjPtr = listObjPtr;
6919        asyncHandlerEntryPtr->hashPtr = hashPtr;
6920
6921        asyncHandlerEntryPtr->timerToken = Tcl_CreateTimerHandler( 0,
6922								  AsyncLoadHandlerProc, (ClientData) asyncHandlerEntryPtr );
6923    }
6924}
6925
6926/*
6927 *----------------------------------------------------------------------
6928 *
6929 * AsyncLoadHandlerProc --
6930 *
6931 *		Callback when async loading movies.
6932 *
6933 * Results:
6934 *		.
6935 *
6936 * Side effects:
6937 *		.
6938 *
6939 *----------------------------------------------------------------------
6940 */
6941
6942static void
6943AsyncLoadHandlerProc(
6944		     ClientData clientData )		/* Pointer to AsyncLoadAsyncHandlerRecord structure. */
6945{
6946    AsyncLoadHandlerEntry 	*asyncHandlerEntryPtr = (AsyncLoadHandlerEntry *) clientData;
6947    Tcl_Interp 			*interp = asyncHandlerEntryPtr->movPtr->interp;
6948    int					cmdCode;
6949
6950    /* Since we may be destroyed in the callback keep storage alive. */
6951    Tcl_Preserve( (ClientData) asyncHandlerEntryPtr->movPtr );
6952
6953    if (interp != NULL) {
6954        QTTclDebugPrintf( interp, 2, "AsyncLoadHandlerProc" );
6955        Tcl_IncrRefCount( asyncHandlerEntryPtr->commandObjPtr );
6956        cmdCode = Tcl_EvalObjEx( interp, asyncHandlerEntryPtr->commandObjPtr, TCL_EVAL_GLOBAL );
6957    } else {
6958        /*
6959         * This should not happen, but by definition of how async
6960         * handlers are invoked, it's possible.  Better error
6961         * checking is needed here.
6962         */
6963    }
6964
6965    /* Remove it since it is a single shot call. */
6966    Tcl_DecrRefCount( asyncHandlerEntryPtr->commandObjPtr );
6967    Tcl_DeleteHashEntry( asyncHandlerEntryPtr->hashPtr );
6968
6969    Tcl_Release( (ClientData) asyncHandlerEntryPtr->movPtr );
6970
6971    ckfree( (char *) asyncHandlerEntryPtr );
6972}
6973
6974static void
6975AsyncLoadFree( MoviePlayer *movPtr )
6976{
6977    Tcl_HashTable 				*asyncLoadHashTablePtr = movPtr->asyncLoadHashTablePtr;
6978    Tcl_HashEntry 				*hashPtr;
6979    Tcl_HashSearch 				search;
6980    AsyncLoadHandlerEntry 		*entryPtr;
6981
6982    /* Loop through the list of async handler hash entries and kill. */
6983    if (asyncLoadHashTablePtr != NULL) {
6984	hashPtr = Tcl_FirstHashEntry( asyncLoadHashTablePtr, &search );
6985	while (hashPtr != NULL) {
6986	    entryPtr = (AsyncLoadHandlerEntry *) Tcl_GetHashValue( hashPtr );
6987            Tcl_DeleteTimerHandler( entryPtr->timerToken );
6988            Tcl_DecrRefCount( entryPtr->commandObjPtr );
6989	    Tcl_DeleteHashEntry( hashPtr );
6990	    ckfree( (char *) entryPtr );
6991	    hashPtr = Tcl_NextHashEntry( &search );
6992	}
6993        Tcl_DeleteHashTable( asyncLoadHashTablePtr );
6994        Tcl_Free( (char *) asyncLoadHashTablePtr );
6995        movPtr->asyncLoadHashTablePtr = NULL;
6996    }
6997}
6998
6999/*
7000 *----------------------------------------------------------------------
7001 *
7002 * MovieExitproc --
7003 *
7004 *		Registered exit handler. If no movies left call ExitMoviePlayer
7005 *		else set flag for later call to ExitMoviePlayer.
7006 *
7007 * Results:
7008 *		None.
7009 *
7010 * Side effects:
7011 *		Invokes ExitMoviePlayer or sets flag to schedule this.
7012 *
7013 *----------------------------------------------------------------------
7014 */
7015
7016static void
7017MovieExitProc( ClientData clientData )
7018{
7019    QTTclDebugPrintf( NULL, 2, "MovieExitProc" );
7020
7021    gExitState = kQTTclExitStateExitHandlerCalled;
7022    if (gMovieRefCount <= 0) {
7023        ExitMoviePlayer( clientData );
7024    }
7025}
7026
7027/*
7028 *----------------------------------------------------------------------
7029 *
7030 * ExitMoviePlayer --
7031 *
7032 *		Last bit of cleanup.
7033 *
7034 * Results:
7035 *		None.
7036 *
7037 * Side effects:
7038 *		Exits movie toolbox, probably frees memory.
7039 *
7040 *----------------------------------------------------------------------
7041 */
7042
7043static void
7044ExitMoviePlayer( ClientData clientData )
7045{
7046    QTTclDebugPrintf( NULL, 2, "ExitMoviePlayer" );
7047
7048    if (gExitState == kQTTclExitStateExitHandlerCalled) {
7049        gExitState = kQTTclExitStateAllFreed;
7050        ExitMovies();
7051
7052#ifdef _WIN32
7053	TerminateQTML();		/* This used to crash Windows when exit
7054	 * when this call came before DestroyNotify */
7055	TerminateQTVR();
7056#endif
7057
7058        ExportComponentSettingsFree();
7059        UserDataHashTablesFree();
7060        EffectsFree();
7061        TracksCommandFree();
7062    }
7063}
7064
7065/*
7066 *----------------------------------------------------------------------
7067 *
7068 * ProcessSpriteSubcmd --
7069 *
7070 *		Process the "sprite" subcommand.
7071 *
7072 * Results:
7073 *  	Normal TCL results
7074 *
7075 * Side effects:
7076 *		Depends on event.
7077 *
7078 *----------------------------------------------------------------------
7079 */
7080
7081static int
7082ProcessSpriteSubcmd( ClientData clientData,
7083		    Tcl_Interp *interp,
7084		    int objc,
7085		    Tcl_Obj *CONST objv[])
7086{
7087    int result = TCL_ERROR;
7088
7089    Tcl_SetObjResult( interp, Tcl_NewStringObj(
7090					       "sprite subcommand not implimented, yet", -1 ) );
7091    return result;
7092}
7093
7094/*
7095 *----------------------------------------------------------------------
7096 *
7097 * ProcessVectorSubcmd --
7098 *
7099 *		Process the "vector" subcommand.
7100 *
7101 * Results:
7102 *  	Normal TCL results
7103 *
7104 * Side effects:
7105 *		Depends on event.
7106 *
7107 *----------------------------------------------------------------------
7108 */
7109
7110static int
7111ProcessVectorSubcmd( ClientData clientData,
7112		    Tcl_Interp *interp,
7113		    int objc,
7114		    Tcl_Obj *CONST objv[])
7115{
7116    int result = TCL_ERROR;
7117
7118    Tcl_SetObjResult( interp, Tcl_NewStringObj(
7119					       "vector subcommand not implimented, yet", -1 ) );
7120    return result;
7121
7122}
7123
7124static void
7125InstallExtendedSCCallbackProcedures( ComponentInstance ci, long refCon )
7126{
7127    SCExtendedProcs     procStruct;
7128
7129    procStruct.filterProc = NewSCModalFilterUPP( MySCFilterDialogProc );
7130    procStruct.hookProc = NULL;
7131    procStruct.customName[0] = 0;
7132    procStruct.refcon = refCon;
7133    SCSetInfo( ci, scExtendedProcsType, &procStruct );
7134}
7135
7136#if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
7137
7138static pascal Boolean
7139MySCFilterDialogProc( DialogPtr dialogPtr, EventRecord * event, short *itemHit,
7140		     long refCon )
7141{
7142    Boolean         handled = false;
7143    WindowRef       eventWindow = NULL;
7144    WindowRef       dialogWindow = NULL;
7145
7146#if TARGET_API_MAC_CARBON
7147    dialogWindow = GetDialogWindow( dialogPtr );
7148#else
7149    dialogWindow = dialogPtr;
7150#endif
7151
7152    switch (event->what) {
7153
7154        case updateEvt:
7155            eventWindow = (WindowRef) event->message;
7156            if ((eventWindow != NULL) && (eventWindow != dialogWindow)) {
7157
7158		/*
7159		 * Handle update events to background windows here.
7160		 * First, translate mac event to a number of tcl events.
7161		 * If any tcl events generated, execute them until empty, and don't wait.
7162		 */
7163
7164		if (TkMacConvertEvent( event )) {
7165		    while ( Tcl_DoOneEvent( TCL_IDLE_EVENTS | TCL_DONT_WAIT | TCL_WINDOW_EVENTS ) )
7166		    /* empty */
7167			;
7168		}
7169            }
7170            break;
7171    }
7172    return handled;
7173}
7174
7175#endif // TARGET_OS_MAC && !TARGET_API_MAC_CARBON
7176
7177#if TARGET_API_MAC_CARBON
7178static Boolean
7179MySCFilterDialogProc( DialogPtr dialogPtr, EventRecord * event, short *itemHit,
7180		     long refCon )
7181{
7182    Boolean         handled = false;
7183
7184    return handled;
7185}
7186#endif // TARGET_API_MAC_CARBON
7187
7188#ifdef _WIN32
7189Boolean
7190MySCFilterDialogProc( DialogPtr dialogPtr, EventRecord * event, short *itemHit,
7191		     long refCon )
7192{
7193    Boolean         handled = false;
7194
7195    return handled;
7196}
7197#endif // _WIN32
7198
7199/*----------------------------------------------------------------------*/
7200