Common Files/WinFramework.c

//////////
//
//  File:       WinFramework.c
//
//  Contains:   Code for the QuickTime sample code framework that is specific to Windows. 
//              This code handles windows, menus, events, and other low-level things. Put your
//              application-specific code into the file ComApplication.c. 
//
//  Written by: Tim Monroe
//              Based on the QTShell code written by Tim Monroe, which in turn was based on the MDIPlayer
//              code written by Brian S. Friedkin (Aug 5, 1996). This current version is now very far removed from
//              MDIPlayer.
//
//  Copyright:  © 1999 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <11>     07/31/00    rtm     reworked QTFrame_CalcWindowMinMaxInfo to use subsystem 4.0 metrics
//     <10>     07/07/00    rtm     removed QTFrame_CreateMacEditMenu; now we use MCGetMenuString
//     <9>      07/06/00    rtm     made changes to support new parameter to QTFrame_AdjustMenus; added the calls
//                                  QTFrame_CreateMacEditMenu and QTFrame_GetKeyboardModifiers
//     <8>      06/26/00    rtm     added SetFocus call to QTFrame_CreateMovieWindow
//     <7>      04/03/00    rtm     reworked QTFrame_CalcWindowMinMaxInfo; disabled movie window maximize button
//                                  in WM_CREATE message processing (implementing this is left as an exercise...)
//     <6>      01/14/00    rtm     reworked window-drawing code (WM_PAINT message) to support graphics files
//     <5>      01/05/00    rtm     minor tweaks to QuickTime initialization in WinMain
//     <4>      12/26/99    rtm     added WM_LBUTTONDOWN processing to QTFrame_MovieWndProc; minor reorganization
//                                  of QTFrame_MovieWndProc so that myMacEvent and myIsHandled are in scope for
//                                  that message
//     <3>      12/16/99    rtm     made minor change to QTFrame_MovieWndProc so that QTApp_HandleEvent is called
//                                  even if the window has no movie controller
//     <2>      11/29/99    rtm     modified "Save changes" dialog box to use Macintosh wordings prompted by move
//                                  to Navigation Services
//     <1>      11/05/99    rtm     first file; based on earlier sample code
//     
//////////
 
//////////
//
// header files
//
//////////
 
#include "WinFramework.h"
 
 
//////////
//
// global variables
//
//////////
 
BOOL                gShuttingDown = false;              // are we shutting down?
BOOL                gWeAreSizingWindow = false;         // are we resizing a window?
BOOL                gWeAreCreatingWindow = false;       // are we creating a window?
 
HANDLE              ghInst;                             // the instance of this application
HWND                ghWnd;                              // the MDI frame window; this window has the menu bar
HWND                ghWndMDIClient;                     // the MDI client window
 
char                gChildName[] = "QTShellChild";
char                gMovieType[] = "QuickTime Movie";
 
short               gAppResFile = kInvalidFileRefNum;   // file reference number for this application's resource file
FSSpec              gAppFSSpec;                         // file specification for the application itself
char                gAppName[20];                       // the name of this application
 
LPSTR               gCmdLine;                           // the command line passed to WinMain
 
extern Rect         gMCResizeBounds;                    // maximum size for any movie window
 
ModalFilterUPP      gModalFilterUPP = NULL;             // UPP to our custom dialog event filter
 
 
//////////
//
// WinMain
// The main function for this application.
//
// Set up the application's execution environment; make sure QuickTime (etc.) is installed,
// then start handling events. If we terminate before reaching the message loop, we should
// return 0.
//
//////////
 
int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR theCmdLine, int nCmdShow)
{
    HANDLE              myAccel;
    HWND                myWindowFrame;
    MSG                 myMsg;
    WNDCLASSEX          myWC;
    char                myFileName[MAX_PATH];
    DWORD               myLength;
    OSErr               myErr = noErr;
 
    ghInst = hInstance;
    gCmdLine = theCmdLine;
    
    if (hPrevInstance == NULL) {
        LoadString(hInstance, IDS_APPNAME, gAppName, sizeof(gAppName));
        
        // register the frame window class
        myWC.cbSize        = sizeof(WNDCLASSEX);
        myWC.style         = CS_HREDRAW | CS_VREDRAW;
        myWC.lpfnWndProc   = (WNDPROC)QTFrame_FrameWndProc;
        myWC.cbClsExtra    = 0;
        myWC.cbWndExtra    = 0;
        myWC.hInstance     = hInstance;
        myWC.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPICON));
        myWC.hCursor       = LoadCursor(NULL, IDC_ARROW);
        myWC.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        myWC.lpszMenuName  = gAppName;
        myWC.lpszClassName = gAppName;
        myWC.hIconSm       = LoadImage(hInstance, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 16, 16, 0);
                                     
        if (!RegisterClassEx(&myWC)) {
            if (!RegisterClass((LPWNDCLASS)&myWC.style))
                return(0);
        }
 
        // register the movie child window class
        myWC.cbSize        = sizeof(WNDCLASSEX);
        myWC.style         = 0;
        myWC.lpfnWndProc   = (WNDPROC)QTFrame_MovieWndProc;
        myWC.cbClsExtra    = 0;
        myWC.cbWndExtra    = 0;
        myWC.hInstance     = hInstance;
        myWC.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CHILDICON));
        // to avoid having QuickTime VR "fight" with the system over the cursor,
        // we set the client area cursor to NULL; this means that for QuickTime
        // movies, we'll need to change the cursor to an arrow manually; see the
        // handling of the WM_MOUSEMOVE message in QTFrame_MovieWndProc
        myWC.hCursor       = NULL;
        myWC.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        myWC.lpszMenuName  = NULL;
        myWC.lpszClassName = gChildName;
        myWC.hIconSm       = LoadImage(hInstance, MAKEINTRESOURCE(IDI_CHILDICON), IMAGE_ICON, 16, 16, 0);
                                     
        if (!RegisterClassEx(&myWC)) {
            if (!RegisterClass((LPWNDCLASS)&myWC.style))
                return(0);
        }
    }
 
    // load accelerators
    myAccel = LoadAccelerators(hInstance, gAppName);
 
    // initialize QuickTime Media Layer and QuickTime; alert the user and return 0 if unsuccessful
    myErr = InitializeQTML(0L);
    if (myErr != noErr) {
        MessageBox(NULL, "QuickTime is not installed on this computer. Exiting.", gAppName, MB_OK | MB_APPLMODAL);
        return(0);
    }
    
    myErr = EnterMovies();
    if (myErr != noErr) {
        MessageBox(NULL, "Could not initialize QuickTime. Exiting.", gAppName, MB_OK | MB_APPLMODAL);
        return(0);
    }
 
    // get the application's resource file, if it exists
    myLength = GetModuleFileName(NULL, myFileName, MAX_PATH);       // NULL means: the current process
    if (myLength != 0) {
        NativePathNameToFSSpec(myFileName, &gAppFSSpec, kFullNativePath);
 
        gAppResFile = FSpOpenResFile(&gAppFSSpec, fsRdWrPerm);
        if (gAppResFile != kInvalidFileRefNum)
            UseResFile(gAppResFile);
    }
    
    // do any application-specific initialization that must occur before the frame window is created
    QTApp_Init(kInitAppPhase_BeforeCreateFrameWindow);
    
    // create the main frame window
    myWindowFrame = CreateWindow(gAppName, gAppName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                                    CW_USEDEFAULT, 
                                    CW_USEDEFAULT,
                                    CW_USEDEFAULT, 
                                    CW_USEDEFAULT,
                                    NULL, 
                                    NULL, 
                                    hInstance, 
                                    NULL);
    ghWnd = myWindowFrame;
    
    // make sure we got a frame window
    if (myWindowFrame == NULL)
        return(0);
        
    // show the window
    ShowWindow(myWindowFrame, nCmdShow);
    UpdateWindow(myWindowFrame);
    
    // do any application-specific initialization that must occur after the frame window is created
    QTApp_Init(kInitAppPhase_AfterCreateFrameWindow);
    
    // get and process events until the user quits
    while (GetMessage(&myMsg, NULL, 0, 0)) {    
        if (!TranslateMDISysAccel(ghWndMDIClient, &myMsg)) {
            if (!TranslateAccelerator(myWindowFrame, myAccel, &myMsg)) {
                TranslateMessage(&myMsg);
                DispatchMessage(&myMsg);
            }
        }
    }
 
    // close the application's resource file, if it was previously opened
    if (gAppResFile != kInvalidFileRefNum)
        CloseResFile(gAppResFile);
 
    // terminate the QuickTime Media Layer
    ExitMovies();
    TerminateQTML();
 
    return(myMsg.wParam);           // returns the value from PostQuitMessage
}
 
 
//////////
//
// QTFrame_FrameWndProc
// The window procedure for the MDI frame window.
//
//////////
 
LRESULT CALLBACK QTFrame_FrameWndProc (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam)
{
    HWND                myChild;
 
    switch (theMessage) {
    
        case WM_CREATE: {
            CLIENTCREATESTRUCT      myClientStruct = {0};
 
            myClientStruct.hWindowMenu  = GetSubMenu(GetMenu(theWnd), WINDOWMENU);
            myClientStruct.idFirstChild = IDM_WINDOWCHILD;
            
            // create the MDI client filling the client area
            ghWndMDIClient = CreateWindow("mdiclient",
                                         NULL,
                                         WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
                                         0, 0, 0, 0,
                                         theWnd,
                                         (HMENU)0xCAC,
                                         ghInst,
                                         (LPVOID)&myClientStruct);
            
            // set initial menu state
            QTFrame_AdjustMenus(NULL, GetMenu(theWnd), 0L);
            
            if (ghWndMDIClient != NULL)
                ShowWindow(ghWndMDIClient, SW_SHOW);
            
            return(0);
        }
 
        case WM_ACTIVATE:
            // the MDI frame window is being activated or deactivated;
            // activate or deactivate any active child window by sending this message to DefMDIChildProc 
            myChild = (HWND)SendMessage(ghWndMDIClient, WM_MDIGETACTIVE, 0, 0L);
            if (IsWindow(myChild))
                SendMessage(myChild, WM_ACTIVATE, wParam, lParam);
            break;
 
        case WM_COMMAND: {
 
            switch (LOWORD(wParam)) {
                case IDM_FILENEW:
                case IDM_FILEOPEN:
                case IDM_FILECLOSE:
                case IDM_EXIT:
                    QTFrame_HandleFileMenuItem(NULL, LOWORD(wParam));
                    return(0);
 
                case IDM_FILESAVE:
                case IDM_FILESAVEAS:
                    // save the active child window
                    myChild = (HWND)SendMessage(ghWndMDIClient, WM_MDIGETACTIVE, 0, 0L);
                    if (IsWindow(myChild))
                        SendMessage(myChild, WM_COMMAND, wParam, lParam);
                    return(0);
 
                case IDM_WINDOWTILE:
                    SendMessage(ghWndMDIClient, WM_MDITILE, 0, 0L);
                    return(0);
 
                case IDM_WINDOWCASCADE:
                    SendMessage(ghWndMDIClient, WM_MDICASCADE, 0, 0L);
                    return(0);
 
                case IDM_WINDOWICONS:
                    SendMessage(ghWndMDIClient, WM_MDIICONARRANGE, 0, 0L);
                    return(0);
 
                case IDM_WINDOWCLOSEALL: {
                    WindowReference     myWindow, myNextWindow;
            
                    // walk the window list and destroy any open windows
                    myWindow = QTFrame_GetFrontMovieWindow();
                    while (myWindow != NULL) {
                        myNextWindow = QTFrame_GetNextMovieWindow(myWindow);
                        SendMessage(myWindow, WM_CLOSE, 0L, 0L);
                        myWindow = myNextWindow;
                    }
                    
                    return(0);
                }
 
                case IDM_ABOUT:
                    QTFrame_ShowAboutBox();
                    return(0);
 
                default:                
                    // pass this message to the active child window...
                    myChild = (HWND)SendMessage(ghWndMDIClient, WM_MDIGETACTIVE, 0, 0L);
                    if (IsWindow(myChild))
                        SendMessage(myChild, WM_COMMAND, wParam, lParam);
 
                    // ...then do any application-specific menu handling, if no movie windows are open...
                    if (myChild == NULL)
                        QTApp_HandleMenu((UInt16)LOWORD(wParam));
                    
                    // ...and then pass it to DefFrameProc
                    break;
            }
            break;
        }
 
        case WM_OPENDROPPEDFILES:
            // open any movie files that were dropped onto the application icon
            QTFrame_OpenCommandLineMovies(gCmdLine);
            return(0);
 
        case WM_INITMENU: {
            long        myModifiers = 0L;
            
            myModifiers = QTFrame_GetKeyboardModifiers();
            if (GetMenu(theWnd) == (HMENU)wParam)
                return(QTFrame_AdjustMenus((HWND)SendMessage(ghWndMDIClient, WM_MDIGETACTIVE, 0, 0L), (HMENU)wParam, myModifiers));
            return(1);
        }
        
        case WM_CLOSE:
            // if we're not already in the process of shutting down,
            // simulate the selection of the Quit menu command
            if (!gShuttingDown) {
                SendMessage(ghWnd, WM_COMMAND, IDM_EXIT, 0L);
                return(0);
            }
            break;
            
        case WM_DESTROY:
            // do any application-specific shutdown
            QTApp_Stop(kStopAppPhase_AfterDestroyWindows);
            PostQuitMessage(0);
            break;
    }
    
    return(DefFrameProc(theWnd, ghWndMDIClient, theMessage, wParam, lParam));
}
 
 
//////////
//
// QTFrame_MovieWndProc
// The window procedure for a movie window.
//
//////////
 
LRESULT CALLBACK QTFrame_MovieWndProc (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam)
{
    WPARAM              myWidth, myHeight;
    MovieController     myMC = NULL;
    Movie               myMovie = NULL;
    WindowObject        myWindowObject = NULL;
    MSG                 myMsg = {0};
    EventRecord         myMacEvent;
    Boolean             myIsHandled = false;
 
    // get the window object, movie, and movie controller for this window
    myWindowObject = QTFrame_GetWindowObjectFromWindow(theWnd);
    if (myWindowObject != NULL) {
        myMC = (**myWindowObject).fController;
        myMovie = (**myWindowObject).fMovie;
    }
 
    // give the movie controller this message first
    if ((!gShuttingDown) && (theMessage != WM_COMMAND)) {
        LONG            myPoints = GetMessagePos();
 
        myMsg.hwnd = theWnd;
        myMsg.message = theMessage;
        myMsg.wParam = wParam;
        myMsg.lParam = lParam;
        myMsg.time = GetMessageTime();
        myMsg.pt.x = LOWORD(myPoints);
        myMsg.pt.y = HIWORD(myPoints);
 
        // translate a Windows event to a Mac event
        WinEventToMacEvent(&myMsg, &myMacEvent);
 
        // let the application-specific code have a chance to intercept the event
        myIsHandled = QTApp_HandleEvent(&myMacEvent);
        
        // pass the Mac event to the movie controller, but only if the movie window isn't minimized
        if (!myIsHandled)
            if (myMC != NULL)
                if (!IsIconic(theWnd))
                    myIsHandled = MCIsPlayerEvent(myMC, (EventRecord *)&myMacEvent);
    }
 
    switch (theMessage) {
        case WM_CREATE: {
                LONG        myStyles;
                
                // create a new window object associated with the new window
                QTFrame_CreateWindowObject(theWnd);
            
                // disable the maximize button
                myStyles = GetWindowLong(theWnd, GWL_STYLE);
                myStyles &= ~WS_MAXIMIZEBOX;
                SetWindowLong(theWnd, GWL_STYLE, myStyles);
            }
            break;
 
        case WM_WINDOWPOSCHANGING:
            // don't show the window until we have created a movie and
            // can therefore properly size the window to contain the movie
            if (gWeAreCreatingWindow) {
                WINDOWPOS   *lpWindowPos = (WINDOWPOS*)lParam;
                
                lpWindowPos->flags &= ~SWP_SHOWWINDOW;
            }
            break;
 
        case WM_WINDOWPOSCHANGED:
            // if a movie window has become minimized, stop the movie
            if (IsIconic(theWnd))
                StopMovie(myMovie);
            break;
 
        case WM_SIZE:
            // resize the movie and controller to fit the window
            myWidth = LOWORD(lParam);
            myHeight = HIWORD(lParam);
            
            // we do NOT want to resize the movie controller if the window is minimized,
            // if there is no movie controller, or if we are in the middle of resizing the window
            if (!gWeAreSizingWindow && (myMC != NULL) && (wParam != SIZE_MINIMIZED)) {
                Rect        myRect;
                
                myRect.top = 0;
                myRect.left = 0;
                myRect.right = myWidth;
                myRect.bottom = myHeight;
                
                MCSetControllerBoundsRect(myMC, &myRect);
            }
            break;
 
        case WM_MOUSEMOVE:
            // for QuickTime movies (but NOT for QuickTime VR movies), set the cursor to the arrow cursor
            if (myWindowObject != NULL)
                if (!(**myWindowObject).fIsQTVRMovie)
                    SetCursor(LoadCursor(NULL, IDC_ARROW));
            break;
 
        case WM_PUMPMOVIE:
            // we receive this message only to task the movie
            break;
 
        case WM_LBUTTONDOWN:
            // do any application-specific mouse-button handling, but only if the message hasn't already been handled
            if (!myIsHandled)
                QTApp_HandleContentClick(theWnd, &myMacEvent);
            break;
 
        case WM_CHAR:
            // do any application-specific key press handling
            QTApp_HandleKeyPress((char)wParam);
            break;
 
        case WM_PAINT: {
            // do any application-specific drawing in the window
                PAINTSTRUCT     myPaintStruct;
 
                BeginPaint(theWnd, &myPaintStruct);
 
                // if the window contains an image, draw it using GraphicsImportDraw
                if (myWindowObject != NULL)
                    if ((**myWindowObject).fGraphicsImporter != NULL)
                        GraphicsImportDraw((**myWindowObject).fGraphicsImporter);
            
                QTApp_Draw(theWnd);
                
                EndPaint(theWnd, &myPaintStruct);
            }
            break;
 
        case WM_MDIACTIVATE:
            // activate or deactivate the movie controller in the specified window
            QTFrame_ActivateController(theWnd, (HWND)theWnd == (HWND)lParam);
            break;
 
        case WM_COMMAND: {
 
            switch (LOWORD(wParam)) {
                            
                case IDM_FILESAVE:
                case IDM_FILESAVEAS:
                    QTFrame_HandleFileMenuItem(theWnd, LOWORD(wParam));
                    break;
 
                case IDM_EDITUNDO:
                case IDM_EDITCUT:
                case IDM_EDITCOPY:
                case IDM_EDITPASTE:
                case IDM_EDITCLEAR:
                case IDM_EDITSELECTALL:
                case IDM_EDITSELECTNONE:
                    QTFrame_HandleEditMenuItem(theWnd, LOWORD(wParam));
                    break;
                    
                default:
                    // do any application-specific menu handling
                    QTApp_HandleMenu((UInt16)LOWORD(wParam));
                    break;
            }
            
            break;
        }   // case WM_COMMAND
 
        case WM_GETMINMAXINFO:
            QTFrame_CalcWindowMinMaxInfo(theWnd, (LPMINMAXINFO)lParam);
            return(0);
 
        case WM_CLOSE:
            // prepare to close the window, making sure that any changed data is saved or explicitly discarded;
            // we can still cancel the window closing here
            if (myWindowObject != NULL) {
            
                // if the window's data is "dirty", give the user a chance to save it
                if ((**myWindowObject).fIsDirty) {
                    int         myItem;
                    char        myText[256];
                    UINT        myAction;
        
                    // get the title of the window
                    GetWindowText(theWnd, myText, sizeof(myText));
        
                    // specify the action
                    myAction = gShuttingDown ? IDS_SAVEONQUIT : IDS_SAVEONCLOSE;
        
                    // display the "Save changes" dialog box
                    myItem = QTFrame_ShowCautionAlert(theWnd, myAction, MB_ICONEXCLAMATION, MB_YESNOCANCEL, gAppName, myText);
                    switch (myItem) {
                        case kSaveChanges:
                            // save the data in the window
                            QTFrame_UpdateMovieFile(theWnd);
                            break;
                            
                        case kCancelClose:
                            // do not close the window and do not quit the application
                            gShuttingDown = false;
                            return(0);
                        
                        case kDontSaveChanges:
                            // discard any unsaved changes (that is, don't do anything)
                            break;
                            
                        default:
                            // unexpected item selected; just return
                            return(0);
                    }
                }
            } // if (myWindowObject != NULL)
            
            // if we got to this point, it's okay to close and destroy the window
            SendMessage(ghWndMDIClient, WM_MDIDESTROY, (WPARAM)theWnd, 0L);
            break;
 
        case WM_DESTROY:
            // when we get this message,
            // the window has been removed from the screen and its associated data must be destroyed
            if (myWindowObject != NULL)
                QTFrame_CloseWindowObject(myWindowObject);
        
            SetWindowLong(theWnd, GWL_USERDATA, 0);
 
            // destroy the port association
            DestroyPortAssociation((CGrafPtr)GetHWNDPort(theWnd));
            
            break;
    }
 
    return(DefMDIChildProc(theWnd, theMessage, wParam, lParam));
}
 
 
//////////
//
// QTFrame_QuitFramework
// Do any framework-specific shut-down.
//
//////////
 
void QTFrame_QuitFramework (void)
{
    // set our global flag to indicate we're shutting down
    gShuttingDown = true;
    
    // do application-specific processing that must occur before movie windows are closed
    QTApp_Stop(kStopAppPhase_BeforeDestroyWindows);
    
    // close all open movie windows; note that the user can cancel the shutting down
    QTFrame_CloseMovieWindows();
    
    // close the frame window, if we're still shutting down
    if (gShuttingDown)
        SendMessage(ghWnd, WM_CLOSE, 0, 0L);
}
 
 
//////////
//
// QTFrame_OpenCommandLineMovies
// Parse the command line when the application first starts up and
// open as movie documents any files specified on the command line.
//
// Based on the routine ParseCmdLinePriv in GraphicImporter.c.
//
//////////
 
void QTFrame_OpenCommandLineMovies (LPSTR theCmdLine)
{
#pragma unused(theCmdLine)
    LPSTR               myCmdLine;
    FSSpec              myFSSpec;
    SHFILEINFO          myFileInfo;
    
    // get the command line for the current process
    myCmdLine = GetCommandLine();
 
    // parse the command line
    if (*myCmdLine) {
        LPSTR           myTempLine;
        
        // the string preceding any white space is the name of the module (that is, the application)
        myTempLine = strchr(myCmdLine, ' ');
        if (myTempLine) {
            myCmdLine = myTempLine;                 // skip the name of the application
            while (*myCmdLine == ' ')
                myCmdLine++;                        // skip spaces to end of string or to first command
 
            while (*myCmdLine != '\0') {
                char    myFileName[MAX_PATH];
                char    myTempName[MAX_PATH];
                char    myBuffName[MAX_PATH];
                int     myIndex;
                
                // read thru the remaining string to find file names
                for (myIndex = 0; *myCmdLine != '\0'; myIndex++, myCmdLine++) {
                    // if we encounter a space character, it might be a filename delimiter or a space in the filename;
                    // we'll try to open the filename we have so far to see whether it's a valid filename; if not, the
                    // space must be part of the filename we're constructing
                    if (*myCmdLine == ' ') {
                        HANDLE              myFindFile;
                        WIN32_FIND_DATA     myFile;
                    
                        myTempName[myIndex] = '\0';
                        strcpy(myBuffName, myTempName);
                        
                        myFindFile = FindFirstFile(myBuffName, &myFile);
                        if (myFindFile != INVALID_HANDLE_VALUE) {
                            // we found a file having the specified name; close our file search and
                            // break out of our character-gobbling loop (since we've got a valid filename)
                            FindClose(myFindFile);
                            break;
                        }
                    }
                
                    // if we made it here, *myCmdLine is part of the filename (possibly a space)
                    myFileName[myIndex] = myTempName[myIndex] = *myCmdLine;
                }
                
                if (*myCmdLine != '\0')
                    myCmdLine++;
                
                // add a terminating NULL character
                myFileName[myIndex] = '\0';
 
                // make sure the filename picks out a QuickTime movie
                SHGetFileInfo(myFileName, (DWORD)0, &myFileInfo, sizeof(myFileInfo), SHGFI_TYPENAME);
                if (strcmp(myFileInfo.szTypeName, gMovieType) != 0)
                    continue;
                
                // make an FSSpec record
                NativePathNameToFSSpec(myFileName, &myFSSpec, 0L);
 
                // open the file in a movie window
                QTFrame_OpenMovieInWindow(NULL, &myFSSpec);
            }
 
        } else
            myCmdLine += strlen(myCmdLine);         // point to NULL
    }
}
 
 
//////////
//
// QTFrame_CreateMovieWindow
// Create a new window to display the movie in.
//
//////////
 
WindowReference QTFrame_CreateMovieWindow (void)
{
    WindowReference         myWindow = NULL;
 
    gWeAreCreatingWindow = true;
    
    // create a new window to display the movie in
    myWindow = CreateWindowEx(WS_EX_MDICHILD,
                               gChildName,
                               "",
                               0,
                               CW_USEDEFAULT,
                               CW_USEDEFAULT,
                               CW_USEDEFAULT,
                               CW_USEDEFAULT,
                               ghWndMDIClient, 
                               NULL,
                               ghInst,
                               0);
    
    // CreateWindowEx sends a WM_CREATE message to the window being created;
    // we'll call QTFrame_CreateWindowObject when processing that message
    
    gWeAreCreatingWindow = false;
 
    // make sure the new window has the keyboard focus
    if (myWindow != NULL)
        SetFocus(myWindow);
    
    return(myWindow);
}
 
 
//////////
//
// QTFrame_DestroyMovieWindow
// Close the specified movie window.
//
//////////
 
void QTFrame_DestroyMovieWindow (WindowReference theWindow)
{
#pragma unused(theWindow)
 
    HWND                myChild = NULL;
    
    // close the active child window
    myChild = (HWND)SendMessage(ghWndMDIClient, WM_MDIGETACTIVE, 0, 0L);
    if (IsWindow(myChild))
        SendMessage(myChild, WM_CLOSE, 0L, 0L);
}
 
 
//////////
//
// QTFrame_GetDisplayName
// Given a full pathname, return the part that trails the rightmost path separator,
// in long file name format (not in 8.3 format).
//
//////////
 
void QTFrame_GetDisplayName (char *thePathName, char *theDispName)
{
    SHFILEINFO          myFileInfo;
    DWORD               myResult;
    
    myResult = SHGetFileInfo(thePathName, (DWORD)0, &myFileInfo, sizeof(myFileInfo), SHGFI_DISPLAYNAME);
    if (myResult != 0) {
        // SHGetFileInfo successful
        strcpy(theDispName, myFileInfo.szDisplayName);
    } else {
        // SHGetFileInfo not successful, so find the basename ourselves
        short   myLength = 0;
        short   myIndex;
 
        // get the length of the pathname
        myLength = strlen(thePathName);
        
        // find the position of the rightmost path separator in thePathName
        if (strchr(thePathName, kWinFilePathSeparator) != NULL) {
    
            myIndex = myLength - 1;
            while (thePathName[myIndex] != kWinFilePathSeparator)
                myIndex--;
                
            // calculate the length of the basename
            myLength = myLength - myIndex - 1;
    
        } else {
            // there is no rightmost path separator in thePathName;
            // set myIndex so that myIndex + 1 == 0, for the call to BlockMove below
            myIndex = -1;
        }
        
        // copy into theDispName the substring of thePathName from myIndex + 1 to the end
        BlockMove(&thePathName[myIndex + 1], theDispName, myLength);
        theDispName[myLength] = '\0';
    }
}
 
 
//////////
//
// QTFrame_ShowAboutBox 
// Display and manage the About dialog box.
//
//////////
 
void QTFrame_ShowAboutBox (void)
{
    DialogBox(ghInst, MAKEINTRESOURCE(IDD_ABOUT), ghWnd, (DLGPROC)QTFrame_DialogProcedure);
}
 
 
//////////
//
// QTFrame_ShowCautionAlert 
// Display and manage a caution alert.
//
// Based on ShowUserMessage by Stephen Chernicoff, in a WiniEdit application
// (as described in the book "From Mac to Windows" on CodeWarrior reference CD).
//
//////////
 
int QTFrame_ShowCautionAlert (HWND theWnd, UINT theID, UINT theIconStyle, UINT theButtonStyle, LPSTR theTitle, LPSTR theArgument)
{
    char            myTemplate[kAlertMessageMaxLength];
    char            myText[kAlertMessageMaxLength];
    UINT            myStyle;
    int             myItem;
    
    // beep, to get the user's attention (just like CautionAlert on MacOS)
    QTFrame_Beep();
    
    // load the message text template from a resource
    LoadString(ghInst, theID, myTemplate, sizeof(myTemplate));
    
    // insert argument into the message text template, to get the message text
    wsprintf(myText, myTemplate, theArgument);
    
    // set the dialog box style
    myStyle = theIconStyle | theButtonStyle | MB_APPLMODAL | MB_SETFOREGROUND;
 
    // display the dialog box
    myItem = MessageBox(theWnd, myText, theTitle, myStyle);
    
    return(myItem);
}
 
 
//////////
//
// QTFrame_DialogProcedure 
// Dialog callback procedure.
//
//////////
 
static UINT APIENTRY QTFrame_DialogProcedure (HWND theDialog, UINT theMessage, WPARAM wParam, LPARAM lParam)
{
    BOOL    isHandled = false;
 
    switch (theMessage) {
    
        case WM_INITDIALOG: {
            Point           myPoint;
            long            myWidth;
            long            myHeight;
            RECT            myRect;
            RECT            myDeskRect;
            HWND            myWindow;
            OPENFILENAME    *myOFNPtr = (OPENFILENAME *)lParam;
            
            myWindow = theDialog;
                
            // check whether theDialog is the Open File common dialog box
            
            // we need to do this because, for the Open File dialog box, theDialog isn't
            // the actual visible dialog box, but an invisible child of the visible dialog box;
            // for WM_INITDIALOG, lParam is the address of the structure passed to GetOpenFileName,
            // so we can just look for the value we previously put into the lCustData field
            // to make sure that we've got the correct dialog.
            if (myOFNPtr != NULL)
                if (myOFNPtr->lCustData == kOpenDialogCustomData)
                    myWindow = GetParent(theDialog);
                
            // center the dialog window on the screen
            GetWindowRect(myWindow, &myRect);
            myWidth = myRect.right - myRect.left;
            myHeight = myRect.bottom - myRect.top;
            GetWindowRect(GetDesktopWindow(), &myDeskRect);
            myPoint.h = (short)((myDeskRect.right + myDeskRect.left)/2 - myWidth/2);
            myPoint.v = (short)((myDeskRect.top + myDeskRect.bottom)/3 - myHeight/3);
            SetWindowPos(myWindow, 0, myPoint.h, myPoint.v, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
            
            // let Windows set the input focus
            isHandled = true;
            break;
        }
        
        case WM_CLOSE:
            EndDialog(theDialog, IDOK);
            isHandled = true;
            break;
        
        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case IDOK:
                    EndDialog(theDialog, IDOK);
                    isHandled = true;
                    break;
                default:
                    isHandled = false;
                    break;
            }
            break;
        
        default:
            isHandled = false;
            break;
    }
 
    return(isHandled);
}
 
 
//////////
//
// QTFrame_CalcWindowMinMaxInfo 
// Get minimum and maximum possible size of this window.
//
//////////
 
static void QTFrame_CalcWindowMinMaxInfo (HWND theWnd, LPMINMAXINFO lpMinMax)
{
    WindowObject                myWindowObject = NULL;
    Movie                       myMovie = NULL;
    MovieController             myMC = NULL;
    GraphicsImportComponent     myImporter = NULL;
    Rect                        myRect;
    ComponentResult             myErr = noErr;
    
    myWindowObject = QTFrame_GetWindowObjectFromWindow(theWnd);
    if (myWindowObject != NULL) {
        myMC = (**myWindowObject).fController;
        myMovie = (**myWindowObject).fMovie;
        myImporter = (**myWindowObject).fGraphicsImporter;
    }
    
    // we're expecting a window with either an image or a movie
    if ((myImporter == NULL) && (myMovie == NULL))
        return;
 
    if (myImporter != NULL) {
        myErr = GraphicsImportGetBoundsRect(myImporter, &myRect);
        if (myErr != noErr)
            return;
    }
    
    if (myMovie != NULL) {
        GetMovieBox(myMovie, &myRect);
        if (myMC != NULL)
            if (MCGetVisible(myMC))
                MCGetControllerBoundsRect(myMC, &myRect);
    }
    
    // we currently don't allow images or movies to be resized, so we return both 
    // min and max rectangles set to the current image or movie size, plus the 
    // appropriate window frame and caption sizes
    lpMinMax->ptMinTrackSize.x = myRect.right + (2 * GetSystemMetrics(SM_CXFRAME));
    lpMinMax->ptMinTrackSize.y = myRect.bottom + (2 * GetSystemMetrics(SM_CXFRAME)) + GetSystemMetrics(SM_CYCAPTION);
 
    lpMinMax->ptMaxTrackSize.x = lpMinMax->ptMinTrackSize.x;
    lpMinMax->ptMaxTrackSize.y = lpMinMax->ptMinTrackSize.y;
    
    lpMinMax->ptMaxSize.x = lpMinMax->ptMinTrackSize.x;
    lpMinMax->ptMaxSize.y = lpMinMax->ptMinTrackSize.y;
}
 
 
//////////
//
// QTFrame_GetKeyboardModifiers 
// Get the current keyboard modifiers; return a long word encoding those modifiers that's
// in the same format as the modifiers field of an event record.
//
// The Windows equivalent of the Option key is defined as the combination of Ctrl and Alt;
// so, if they are both down, we should only return the Option key; if only one of them is
// down, then we return that one.
//
// Based on code from Brian Friedkin.
//
//////////
 
static long QTFrame_GetKeyboardModifiers (void)
{
    long    myModifiers = 0L;
    BYTE    myKeyState[256];
 
    if (GetKeyboardState(&myKeyState[0])) {
        if (QTFrame_IsOptionKeyDown(myKeyState))
            myModifiers |= optionKey;
        else if (QTFrame_IsCommandKeyDown(myKeyState))
            myModifiers |= cmdKey;
        else if (QTFrame_IsControlKeyDown(myKeyState))
            myModifiers |= controlKey;
    
        if (QTFrame_IsShiftKeyDown(myKeyState))
            myModifiers |= shiftKey;
        if (QTFrame_IsAlphaLockKeyDown(myKeyState))
            myModifiers |= alphaLock;
    }
    
    return(myModifiers);
}