
// ---------------------------------------------------------------------
//
// MovieWnd.c - Movie Player - QuickTime for Windows
//
//              Version 1.0
//
//              (c) 1988-1992 Apple Computer, Inc. All Rights Reserved.
//
// ---------------------------------------------------------------------



// Includes
// --------
#include <Windows.H>   // Required by Windows
#include <commdlg.h>   // Required for PRINTDLG struct
#include <stdio.h>     // Required for sprintf routine
#include <sys\types.h> // Required for stat.h
#include <sys\stat.h>  // Required for _stat routine
#include <math.h>      // Required for abs()
#include <string.h>    // Required for memset

#include <qtw.h>   // Interface to QuickTime
#include <qtole.h> // Interface to qtole dll

#include "common.h" // Interface to common.c

#include "player.h"  // Interface to other *.c files
#include "player.hr" // Defines used in *.rc files


// Constants
// ---------
#define GETINFOMOVIEPROP      "Movie"


// Message-Persistent Data
// -----------------------
static struct // Hungarian notation: g
  {WORD        wMovieCount;            // Used during enumeration for
                                       // duplication checking
   BOOL        bCreating;              //   "    "       "
   HWND        hwndFirstDup;           //   "    "       "
   WORD        wMovieControllerHeight; // Height of movie controller
   BOOL        bUpdatingInfo;          // Updating info dialog flag
   char        szSelectFormat[30];     // Info dialog selection format string
   char        szNoSelection[20];      // Info dialog "no selection" string
   HICON       hmovieIcon;             // Movie frame icon
   HCURSOR     hcursor;                // Current cursor. We need to set cursor
                                       // during constrained resizing so class
                                       // cursor must be set to NULL;
   RECT        rcResizeRect;           // Rect used during constrained resizing
   POINT       ptCursorOffset;         // Offset of cursor from edge of wnd rect
   WORD        wSides;                 // Combined width of vertical size borders
   WORD        wTBBorder;              // Combined width of horizontal size borders
   WORD        wTopAndBottom;          // Difference in height between movie and
                                       // movie window. This includes the movie
                                       // controller
   WORD        wSoundOnlyDefWidth;     // Default width of a sound only movie
   WORD        wScaleWidth;            // Scale width used in constrained resize
   WORD        wScaleHeight;           // Scale height used in constrained resize
   BOOL        bFatResizeBorder;       // Flag that causes resize border to be
                                       // drawn 2 pixels thicker
   WNDPROC     lpOldGBProc;            // Original grow box proc
   WNDPROC     lpNewGBProc;            // Subclassed grow box proc
   HWND        hwndMaximizedMovie;     // Handle of maximized movie wnd
   BOOL        bCapturedGrowBox;       // TRUE if the grow box input has
                                       // been captured
  } g;

// Macros
// ----------------------
#define ISKEYDOWN( vKey )  (GetKeyState( (int) (vKey) ) < 0 )


// Exported callback functions
// ----------------------------
BOOL __export CALLBACK GetInfoDlgProc       (HWND, UINT, WPARAM, LPARAM);
BOOL __export CALLBACK CheckDupEnumProc     (HWND, LPARAM);

BOOL __export CALLBACK ActionFilter   (MovieController, UINT, LPVOID, LONG);

BOOL __export CALLBACK MovieChildEnumProc   (HWND, LPARAM);
LONG __export CALLBACK GBSubClassProc       (HWND, UINT, WPARAM, LPARAM);

// Internal Function Declarations
// ------------------------------
static LONG   NEAR PlayerMovieCreate        (HWND, LPARAM );
static LONG   NEAR PlayerEditCommands       (HWND, WPARAM, WORD);
static LONG   NEAR PlayerMovieCommands      (HWND, WPARAM, WORD);
static LONG   NEAR PrintFrame               (HWND, LPPRINTDLG);
static VOID   NEAR FillMovieInfo            (HWND, NPMOVIEDATA);
static LONG   NEAR StartTheMovie            (NPMOVIEDATA);
static LONG   NEAR StopTheMovie             (NPMOVIEDATA);
static VOID   NEAR UpdateInfoFileName       (NPMOVIEDATA);
static VOID   NEAR DisplayCurrentSelection  (NPMOVIEDATA);
static LONG   NEAR ResizeMovieAndWindow     (HWND, BOOL, NPMOVIEDATA, WORD, WORD);
static LONG   NEAR ResizeMovie              (NPMOVIEDATA, WORD, WORD);
static LONG   NEAR InitializePopupMenus     (HWND, HMENU, int);
static LONG   NEAR SetMinMaxInfo            (HWND, NPMOVIEDATA, MINMAXINFO FAR *);
static VOID   NEAR ActivateTheController    (HWND, NPMOVIEDATA, BOOL);
static LONG   NEAR PaintTheIcon             (HWND, NPMOVIEDATA);
static HFONT  NEAR MakeAnArialFont          (HDC, int);
static VOID   NEAR UpdateGBBoundsRect       (HWND, NPMOVIEDATA);
static WORD   NEAR InitializeResize         (HWND, NPMOVIEDATA, WORD, POINT);
static VOID   NEAR DrawTheFrameRect         (LPRECT, BOOL);
static VOID   NEAR MoveTheMovieResizeRect   (HWND, NPMOVIEDATA,
                                                          WORD, POINT, BOOL);
static VOID   NEAR GetProportionalDimensions (NPMOVIEDATA, PWORD, PWORD, BOOL);
static VOID   NEAR AdjustForMinProportionalSize(HWND, NPMOVIEDATA,
                                                      LPWORD, LPWORD, BOOL);
static BOOL   NEAR IsNormalSize             (LPRECT, NPMOVIEDATA);
static BOOL   NEAR SubclassTheGrowBox       (HWND, NPMOVIEDATA);
static BOOL   NEAR InitMaxWndGrowBoxResize  (HWND, POINT);
static VOID   NEAR MoveTheFrameResizeRect   (HWND, POINT);
static LONG   NEAR FixUpMovieTiling         (HWND, NPMOVIEDATA, LPWINDOWPOS);
static VOID   NEAR SetOptionsDefaults       (HWND, NPMOVIEDATA);
static VOID   NEAR PopulateOptionsStruct    (HWND, NPMOVIEDATA);


// Function: PlayerMovieWndProc - Player Movie Window Procedure
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    Via DefMDIChildProc
// --------------------------------------------------------------------
LONG __export CALLBACK PlayerMovieWndProc
    (HWND hwndMovie, UINT message, WPARAM wParam, LPARAM lParam)

{
    NPMOVIEDATA      pMovieData;      // Temp -> to movie data struct
    HMOVIEDATA       hMovieData;      // Temp handle of movie data
    WNDENUMPROC      lpfnEnumMovies;  // -> enumeration proc
    NPMOVIEDATA      pFirstMovieData; // Temp -> to movie data struct
                                      // used during duplication processing
    WORD             wMovieWidth;     // Movie normal width
    WORD             wMovieHeight;    // Movie normal height
    WORD             wWidth;          // Window width
    WORD             wHeight;         // Window height
    LRESULT          lRetVal;         // Return from DefMDIChildProc
    POINT            ptCursor;        // Cursor position in screen coords.
    LPQTOLE_OLEDATA  lpOleData;       // -> ole data

    static WORD      wResizeHitTestCode; // Equals the NC hit test code when
                                         // resizing with SHIFT or CONTROL


    pMovieData = (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 );
    if( pMovieData &&
        MCIsPlayerMessage( pMovieData->mcMovieController,
        hwndMovie, message, wParam, lParam ))
        return DefMDIChildProc( hwndMovie, message, wParam, lParam );

    switch( message ) {
        case WM_CREATE:
            // Load movie frame icon used by all instances
            if( !g.hmovieIcon )
                g.hmovieIcon = LoadIcon( PlayerQueryResources(),
                MAKEINTRESOURCE( PLAYER_MOVIE_ICON ));
            if( !g.hcursor )
                g.hcursor = LoadCursor( NULL, IDC_ARROW );

            return PlayerMovieCreate( hwndMovie, lParam );

        case WM_SIZE:
            if( !pMovieData )
                break; // break to DefMDIChildProc

            wMovieWidth  = LOWORD( lParam );
            if( HIWORD( lParam ) > g.wMovieControllerHeight )
                wMovieHeight = HIWORD( lParam ) - g.wMovieControllerHeight;
            else
                wMovieHeight = 0;

            if( wParam ) // Minimizing, maximizing, etc
            {
                if( wParam == SIZE_MAXIMIZED )
                // If subclass failed, remove grow box
                {
                    if( IsZoomed( PlayerQueryFrameWindow()) ||
                        !pMovieData->bGrowBoxSubclassed ) {
                        SetRectEmpty( &pMovieData->rcGrowBox );
                        MCDoAction( pMovieData->mcMovieController,
                            mcActionSetGrowBoxBounds, &pMovieData->rcGrowBox );
                    }
                    else if( pMovieData->wMinMaxEtc == SIZE_MINIMIZED ) { // Reset grow box bounds rect
                        UpdateGBBoundsRect( hwndMovie, pMovieData );
                    }

                    if( pMovieData->wMinMaxEtc == SIZE_MINIMIZED )
                        StartTheMovie( pMovieData );

                    g.hwndMaximizedMovie = hwndMovie;
                    ResizeMovie( pMovieData, wMovieWidth, wMovieHeight);
                }
                else if( wParam == SIZE_MINIMIZED ) {
                    SetRectEmpty( &pMovieData->rcGrowBox );
                    MCDoAction( pMovieData->mcMovieController,
                        mcActionSetGrowBoxBounds, &pMovieData->rcGrowBox );

                    StopTheMovie( pMovieData );

                    PaintTheIcon( hwndMovie, pMovieData );
                }

                pMovieData->wMinMaxEtc = wParam;
            }
            else if( pMovieData->wMinMaxEtc ) // restoring
            { // Reset grow box bounds rect
                UpdateGBBoundsRect( hwndMovie, pMovieData );

                if( pMovieData->wMinMaxEtc == SIZE_MAXIMIZED ) {
                    if( g.hwndMaximizedMovie == hwndMovie )
                        g.hwndMaximizedMovie = NULL;

                    ResizeMovie( pMovieData, wMovieWidth, wMovieHeight);
                }
                else if( pMovieData->wMinMaxEtc == SIZE_MINIMIZED ) {
                    StartTheMovie( pMovieData );
                }
                pMovieData->wMinMaxEtc = 0;
            }
            else if( pMovieData->bDisableSizeMsgProcessing ) { // Already processing create message, menu item or resizing
                                                               // to aspect ratio or even multiple of pixels according
                                                               // to key states in the last else under this message
                pMovieData->bDisableSizeMsgProcessing = FALSE;
            }
            else {
                ResizeMovie( pMovieData, wMovieWidth, wMovieHeight);
            }

            // Need this for movie controller to repaint correctly
            // when window is small
            if( wResizeHitTestCode ||
                ( PlayerQueryMDIAction() == PLAYER_WINDOW_TILE ))
                InvalidateRect( hwndMovie, NULL, FALSE );

            break; // break to DefMDIChildProc

        case WM_MOVE:
            if( !pMovieData ||
                ( pMovieData->wMinMaxEtc == SIZE_MAXIMIZED ) ||
                ( pMovieData->wMinMaxEtc == SIZE_MINIMIZED ))
                break; // break to DefMDIChildProc

            // Need to reset grow box bounds rect after a move because it is
            // the client rect of the MDI frame window expressed in the client
            // coordinates of the movie window
            UpdateGBBoundsRect( hwndMovie, pMovieData );

            break; // break to DefMDIChildProc

        case WM_WINDOWPOSCHANGING:
            if( pMovieData &&
                ( PlayerQueryMDIAction() == PLAYER_WINDOW_TILE )) {
                FixUpMovieTiling( hwndMovie,
                    pMovieData, (LPWINDOWPOS) lParam );
            }

            break; // break to DefMDIChildProc

        case WM_COMMAND:
            switch( wParam ) {
                case PLAYER_EDIT_COPY: // edit menu popup
                case PLAYER_EDIT_OPTIONS:
                case PLAYER_EDIT_CANCELSEL:
                    return PlayerEditCommands
                        ( hwndMovie, wParam, HIWORD( lParam ));

                case PLAYER_MOVIE_GETINFO: // movie menu popup
                case PLAYER_MOVIE_STOPATEND:
                case PLAYER_MOVIE_LOOP:
                case PLAYER_MOVIE_BACKANDFORTH:
                case PLAYER_MOVIE_PLAYSELONLY:
                case PLAYER_MOVIE_HALFSIZE:
                case PLAYER_MOVIE_NORMALSIZE:
                case PLAYER_MOVIE_DOUBLESIZE:
                case PLAYER_MOVIE_SHOWPOSTER:
                    return PlayerMovieCommands
                        ( hwndMovie, wParam, HIWORD( lParam ));

                default:
                    break; // break to DefMDIChildProc
            }

            break;

        // WM_USER messages

        case WM_PLAYER_PRINTFRAME:
            return PrintFrame( hwndMovie, (LPPRINTDLG) lParam );

        case WM_PLAYER_INITPOPUPS:
            return InitializePopupMenus
                ( hwndMovie, (HMENU) wParam, (int) LOWORD(lParam) );

        case WM_PLAYER_ACTIVATEMOVIE:
            if( pMovieData ) {
                SetMovieActive( pMovieData->mMovie, TRUE );
                SetFocus( hwndMovie );
            }

            return 0L;

        case WM_PLAYER_ACTIVATECONTROLLER:
            if( pMovieData && pMovieData->mcMovieController )
                ActivateTheController( hwndMovie, pMovieData, TRUE );

            return 0L;

        case WM_PLAYER_PLAYTHEMOVIE: // Send by frame window when it is
                                     // iconized or restored
            if( pMovieData &&
                ( pMovieData->wMinMaxEtc != SIZE_MINIMIZED )) {
                if( (BOOL) wParam )
                    StartTheMovie( pMovieData );
                else
                    StopTheMovie( pMovieData );
            }

            return 0L;

        case WM_PLAYER_UPDATEGBBOUNDSRECT:
            // Frame window sends this message when size changes
            if( !pMovieData ||
                ( pMovieData->wMinMaxEtc == SIZE_MINIMIZED ))
                return 0L;

            UpdateGBBoundsRect( hwndMovie, pMovieData );

            return 0L;

        // end WM_USER messages

        case WM_NCACTIVATE:
            if( pMovieData && pMovieData->mcMovieController ) { // Activate or deactivate the controller
                if( (BOOL) wParam ) { // Post message to introduce a delay so that
                                      // activation occurs after mouse click.
                                      // Activate is called directly if PostMessage fails
                    if( !PostMessage( hwndMovie,
                        WM_PLAYER_ACTIVATECONTROLLER, TRUE, 0L ))
                        ActivateTheController( hwndMovie, pMovieData, TRUE );
                }
                else {
                    ActivateTheController( hwndMovie, pMovieData, FALSE );
                }
            }

            break; // break to DefMDIChildProc

        case WM_GETMINMAXINFO:
            SetMinMaxInfo( hwndMovie, pMovieData, (MINMAXINFO FAR*) lParam );
            break; // break to DefMDIChildProc

        case WM_QUERYDRAGICON:
            return MAKELONG( g.hmovieIcon, 0 );

        case WM_NCHITTEST:
            lRetVal =  DefMDIChildProc
                ( hwndMovie, message, wParam, lParam );
            if( pMovieData && pMovieData->bSoundOnly ) {
                switch( LOWORD( lRetVal )) {
                    case HTTOP:
                    case HTBOTTOM:
                        return 0L;

                    case HTTOPLEFT:
                    case HTBOTTOMLEFT:
                        return HTLEFT;

                    case HTTOPRIGHT:
                    case HTBOTTOMRIGHT:
                        return HTRIGHT;

                    default:
                        return lRetVal;
                }
            }
            else
                return lRetVal;

        case WM_NCLBUTTONDOWN: // We must do all border drag resizing
                               // so we can do the constrained resizing
            if( pMovieData &&
                ( wResizeHitTestCode = InitializeResize
                ( hwndMovie, pMovieData, wParam, MAKEPOINT( lParam )))) {
                SetCapture( hwndMovie );
                MoveTheMovieResizeRect( hwndMovie, pMovieData,
                    wResizeHitTestCode, MAKEPOINT( lParam ), TRUE );
                return 0L;
            }

            break; // break to DefMDIChildProc

        case WM_MOUSEMOVE:
            if( wResizeHitTestCode ) {
                ptCursor = MAKEPOINT( lParam );
                ClientToScreen( hwndMovie, &ptCursor );
                MoveTheMovieResizeRect( hwndMovie, pMovieData,
                    wResizeHitTestCode, ptCursor, FALSE );
            }
            else if( g.bCapturedGrowBox ) // This move is initiated in the
                                          // grow box subclass proc.
            {
                ptCursor = MAKEPOINT( lParam );
                ClientToScreen( hwndMovie, &ptCursor );
                MoveTheFrameResizeRect( hwndMovie, ptCursor );
            }

            SetCursor( g.hcursor );
            return 0L;

        case WM_LBUTTONUP:
            if( wResizeHitTestCode ) {
                DrawTheFrameRect( &g.rcResizeRect, g.bFatResizeBorder );

                ReleaseCapture();
                DrawTheFrameRect( NULL, FALSE ); // This cleans up
                ClipCursor( NULL );
                g.hcursor = LoadCursor( NULL, IDC_ARROW );

                if( !pMovieData->bSoundOnly &&
                    ( ISKEYDOWN( VK_CONTROL ) || ISKEYDOWN( VK_SHIFT ))) {
                    wWidth  = g.rcResizeRect.right - g.rcResizeRect.left;
                    wHeight = g.rcResizeRect.bottom - g.rcResizeRect.top;

                    AdjustForMinProportionalSize
                        ( hwndMovie, pMovieData,
                        &wWidth, &wHeight, ISKEYDOWN( VK_CONTROL ));

                    g.rcResizeRect.right  = g.rcResizeRect.left + wWidth;
                    g.rcResizeRect.bottom = g.rcResizeRect.top + wHeight;
                }

                MapWindowPoints( HWND_DESKTOP, PlayerQueryClientWindow(),
                    (LPPOINT) &g.rcResizeRect, 2 );

                MoveWindow( hwndMovie,
                    g.rcResizeRect.left, g.rcResizeRect.top,
                    g.rcResizeRect.right - g.rcResizeRect.left,
                    g.rcResizeRect.bottom - g.rcResizeRect.top, TRUE );

                // This needs to be after the MoveWindow call
                wResizeHitTestCode = 0;
            }
            else if( g.bCapturedGrowBox ) // This move is initiated in the
                                          // grow box subclass proc.
            {
                g.bCapturedGrowBox = FALSE;
                DrawTheFrameRect( &g.rcResizeRect, FALSE );

                ReleaseCapture();
                DrawTheFrameRect( NULL, FALSE ); // This cleans up
                ClipCursor( NULL );

                MoveWindow( PlayerQueryFrameWindow(),
                    g.rcResizeRect.left, g.rcResizeRect.top,
                    g.rcResizeRect.right - g.rcResizeRect.left,
                    g.rcResizeRect.bottom - g.rcResizeRect.top, TRUE );
            }

            return 0L;

        case WM_PAINT:
            // We paint the icon but QTW takes care of all the painting
            // otherwise.
            if( pMovieData && IsIconic( hwndMovie ))
                return PaintTheIcon( hwndMovie, pMovieData );
            break; // break to DefMDIChildProc

        case WM_DESTROY:
            if( ( lpOleData = PlayerQueryOleData()) && lpOleData->lpqtoleServer )
                PopulateOptionsStruct( hwndMovie, pMovieData );
            break; // break to DefMDIChildProc

        case WM_NCDESTROY:
            if( !pMovieData )
                break;

            if( pMovieData->lpFilterProc ) {
                MCSetActionFilter
                    ( pMovieData->mcMovieController, NULL, 0L );
                FreeProcInstance( (FARPROC ) pMovieData->lpFilterProc );
                pMovieData->lpFilterProc = NULL;
            }

            if( pMovieData->hwndGetInfo )
                DestroyWindow( pMovieData->hwndGetInfo );

            if( pMovieData->mcMovieController )
                DisposeMovieController( pMovieData->mcMovieController );
            if( pMovieData->mMovie )
                DisposeMovie( pMovieData->mMovie );

            // Last instance destroys icon and grow box subclass proc
            if( PlayerQueryNumMovies() <= 1 ) {
                if( g.hmovieIcon )
                    DestroyIcon( g.hmovieIcon );
                g.hmovieIcon = NULL;

                // Free the grow box subclass proc
                if( g.lpNewGBProc ) {
                    FreeProcInstance( (FARPROC) g.lpNewGBProc );
                    g.lpNewGBProc = NULL;
                }
                g.lpOldGBProc = NULL;
            }
            else { // Check for duplicates
                g.wMovieCount  = 0;
                g.bCreating    = FALSE;
                g.hwndFirstDup = NULL;

                if( lpfnEnumMovies = (WNDENUMPROC) MakeProcInstance
                    ( (FARPROC) CheckDupEnumProc, PlayerQueryInstance())) {
                    EnumChildWindows( PlayerQueryClientWindow(),
                        lpfnEnumMovies, MAKELPARAM( hwndMovie, 0 ));
                    FreeProcInstance( (FARPROC) lpfnEnumMovies );
                }

                // if no dups, eliminate :1 on first
                // hwndFirstDup is set in CheckDupEnumProc
                if( ( g.wMovieCount == 1 ) && g.hwndFirstDup &&
                    ( pFirstMovieData = (NPMOVIEDATA)
                    GetWindowWord( g.hwndFirstDup, 0 ))) {
                    pFirstMovieData->wDuplicationIndex = 0;
                    SetWindowText( g.hwndFirstDup,
                        pFirstMovieData->szMovieName );
                }
            }

            // Tell OLE that window is closed. Movie has been disposed
            // qtole.dll used hwndObject so don't null the handle in struct
            pMovieData->qtoleOptions.mMovie = NULL;

            if( ( lpOleData = PlayerQueryOleData()) && lpOleData->lpqtoleServer )
                QTOLE_ClosingDocWnd( lpOleData,
                (LPQTOLE_OPTIONS) &pMovieData->qtoleOptions );

            LocalUnlock( hMovieData =
                (HMOVIEDATA) LocalHandle( pMovieData ));
            LocalFree( hMovieData );
            SetWindowWord( hwndMovie, 0, 0);

            // Update the movie count in the frame window globals
            SendMessage( PlayerQueryFrameWindow(),
                WM_PLAYER_MOVIEDELETED, (WPARAM) hwndMovie, 0L );

            break; // break to DefMDIChildProc

    }

    return DefMDIChildProc( hwndMovie, message, wParam, lParam );
}


// Function: PlayerMovieCreate - process WM_CREATE message
// --------------------------------------------------------------------
// Parameters: HWND        hwndMovie;      Handle of movie window
//             LPARAM      lParam;         lParam of WM_CREATE message
//
// Returns:    0 if OK, else returns -1 to kill app
// --------------------------------------------------------------------
static LONG NEAR PlayerMovieCreate( HWND hwndMovie, LPARAM lParam )

{
    NPMOVIEDATA        pMovieData;             // Temp -> to movie data struct
    HMOVIEDATA         hMovieData;             // Handle to movie data struct
    LPMDICREATESTRUCT  lpmdicreate;            // MDI create struct
    MovieFile          movMovieFile;           // Movie file handle
    char               szBuffer[MAX_PATH_LEN]; // Temp buffer
    struct _stat       statbuf;                // File statictics struct
    WORD               wIDString;              // Resource string id
    DWORD              dwBytes;                // File size
    OSErr              oserr;                  // Error return
    RECT               rcMovie;                // Movie rect
    RECT               rcWindow;               // Window rect
    RECT               rcController;           // Controller rect
    POINT              ptCorner;               // Upper Left corner of movie wnd
    WORD               wWidth;                 // Width of window
    WORD               wHeight;                // Height of window
    RECT               rcclientClient;         // Client rect of MDI client wnd
    int                nDiff;                  // Temp
    WNDENUMPROC        lpfnEnumMovies;         // -> to enumeration proc
    LPSTR              lpName;                 // Temp -> movie name
    LONG               lStyle;                 // Temp window style
    UserData           udUserData;             // Handle to user data
    HGLOBAL            ghMem;                  // Global mem used to get 'loop' user data
    LONG               lSizeData;              // Size of 'loop' user data
    LPSTR              lpUserData;             // -> 'loop' user data
    int                i;                      // Loop counter
    QTOLE_OPENWND      qtoleOpenWnd;           // Ole open wnd struct
    LPQTOLE_OLEDATA    lpOleData;              // -> ole data
    int                nLoop;


    pMovieData = (NPMOVIEDATA) NULL;

    if( !(hMovieData = (HMOVIEDATA)
        LocalAlloc( LPTR, sizeof( MOVIEDATASTRUCT )))) {
        CommonTellUser( PlayerQueryResources(),
            PLAYER_STRING_NOMEMORY, NULL, MB_OK );
        goto Failed;
    }
    pMovieData = (NPMOVIEDATA) LocalLock( hMovieData );
    SetWindowWord( hwndMovie, 0, (WORD) pMovieData );

    // mdi struct filled before call to MDI create in LaunchMovieWnd
    lpmdicreate = (LPMDICREATESTRUCT)
        ((LPCREATESTRUCT) lParam)->lpCreateParams;

    lstrcpy( pMovieData->szMoviePath, (LPSTR) lpmdicreate -> lParam );
    lstrcpy( pMovieData->szMovieName, (LPSTR) lpmdicreate -> szTitle );

    // Strip off and save extension so that name fits better
    // on title bar of window
    pMovieData->szMovieExt[0] = '\0';
    lpName = pMovieData->szMovieName;
    while( *lpName ) {
        if( *lpName == '.' ) {
            lstrcpy( pMovieData->szMovieExt, lpName );
            *lpName = '\0';
            SetWindowText( hwndMovie, pMovieData->szMovieName );
            break;
        }
        else
            lpName = AnsiNext( lpName );
    }

    if( ( oserr = OpenMovieFile( pMovieData->szMoviePath,
        &movMovieFile, OF_READ )) == 0 ) {
        oserr = NewMovieFromFile( &pMovieData->mMovie,
            movMovieFile, NULL, NULL, 0, NULL );
        CloseMovieFile( movMovieFile );
    }

    if( oserr ) {
        if( oserr == insufficientMemory )
            wIDString = PLAYER_STRING_NOMEMORY;
        else if( oserr == invalidDataRef )
            wIDString = PLAYER_STRING_INVALIDDATAREF;
        else
            wIDString = PLAYER_STRING_NEWMOVIEERR;

        CommonTellUser( PlayerQueryResources(),
            wIDString, PLAYER_STRING_CAPTION,
            MB_OK, (LPSTR) lpmdicreate -> szTitle );
        goto Failed;
    }

    // Check for duplicates. Fix up titles if necessary
    // Initialize globals used during enumeration
    g.wMovieCount = 1;
    g.bCreating =  TRUE;
    if( ( PlayerQueryNumMovies() > 0 ) &&
        ( lpfnEnumMovies = (WNDENUMPROC) MakeProcInstance
        ( (FARPROC) CheckDupEnumProc, PlayerQueryInstance()))) {
        EnumChildWindows( PlayerQueryClientWindow(),
            lpfnEnumMovies, MAKELPARAM( hwndMovie, 0 ));
        FreeProcInstance( (FARPROC) lpfnEnumMovies );

        // Fix up title if duplicate
        // Title of 1st dup is fixed up during enum
        if( g.wMovieCount > 1 ) {
            pMovieData->wDuplicationIndex = g.wMovieCount;
            wsprintf( szBuffer, "%s:%u",
                (LPSTR) pMovieData->szMovieName,
                pMovieData->wDuplicationIndex );
            SetWindowText( hwndMovie, szBuffer );
        }
    }

    // file size  Note: _stat path name is in OEM char set
    lstrcpy( szBuffer, pMovieData->szMoviePath );
    AnsiToOem( szBuffer, szBuffer );
    if( (_stat( szBuffer, &statbuf )) == 0 ) {
        if( statbuf.st_size < 1000L ) {
            dwBytes = (DWORD) statbuf.st_size;
            wIDString = PLAYER_STRING_SIZEBYTES;
        }
        else {
            dwBytes = (DWORD) ( statbuf.st_size / 1000L );
            wIDString = PLAYER_STRING_SIZEKBYTES;
        }

        LoadString( PlayerQueryResources(),
            wIDString, szBuffer, sizeof( szBuffer ));
        wsprintf( pMovieData->szFileSize, szBuffer, dwBytes );
    }

    pMovieData->bSoundOnly = FALSE;
    pMovieData->idMovieInfo.idSize = sizeof( ImageDescription );
    oserr = GetVideoInfo( pMovieData->mMovie, &pMovieData->idMovieInfo );
    if( oserr == noVideoTrackInMovie ) {
        pMovieData->bSoundOnly = TRUE;
    }
    else if( oserr ) {
        CommonTellUser( PlayerQueryResources(),
            PLAYER_STRING_NOINFO, PLAYER_STRING_CAPTION, MB_OK );
        goto Failed;
    }

    pMovieData->sdSoundInfo.descSize = sizeof( SoundDescription );
    pMovieData->oserrSoundInfo =
        GetSoundInfo( pMovieData->mMovie, &pMovieData->sdSoundInfo );


    // Set copy option default values
    SetOptionsDefaults( hwndMovie, pMovieData );

    // Instantiate the movie controller
    if( !pMovieData->bSoundOnly ) // Call only if there is video
    {
        GetMovieBox( pMovieData->mMovie, &rcMovie );
        OffsetRect( &rcMovie, -rcMovie.left, -rcMovie.top );

        // Now resize window if default option is half or double size
        if( pMovieData->qtoleOptions.bSizeHalf ) {
            rcMovie.right  /= 2;
            rcMovie.bottom /= 2;
        }
        else if( pMovieData->qtoleOptions.bSizeDouble ) {
            rcMovie.right  *= 2;
            rcMovie.bottom *= 2;
        }
    }

    // Movie with no video or no rect gets a default width to pass to
    // NewMovieController
    if( pMovieData->bSoundOnly || IsRectEmpty( &rcMovie )) {
        rcMovie.left = rcMovie.top = 0;
        rcMovie.right = 8 * GetSystemMetrics( SM_CXVSCROLL );
        rcMovie.bottom = 0;
    }

    if( !( pMovieData->mcMovieController =
        NewMovieController( pMovieData->mMovie, &rcMovie,
        mcTopLeftMovie | mcScaleMovieToFit, hwndMovie ))) {
        CommonTellUser( PlayerQueryResources(),
            PLAYER_STRING_NOCONTROLLER, PLAYER_STRING_CAPTION, MB_OK );
        goto Failed;
    }
    MCDoAction( pMovieData->mcMovieController, mcActionPlay, (LPVOID) 0 );

    // Subclass the movie controller grow box for use when a
    // movie window is maximized
    pMovieData->bGrowBoxSubclassed =
        SubclassTheGrowBox( hwndMovie, pMovieData );

    // Override loop mode depending on user data
    ghMem = NULL;
    nLoop = 0;
    if( ( udUserData = GetMovieUserData( pMovieData->mMovie )) &&
        ( CountUserDataType( udUserData,
        QTFOURCC( 'L', 'O', 'O', 'P' )) > 0 )) {
        pMovieData->qtoleOptions.bLoop           = TRUE;
        pMovieData->qtoleOptions.bLoopPalindrome = FALSE;
        if( ( ghMem = GlobalAlloc( GHND, 16L )) &&
            !GetUserData( udUserData, &ghMem,
            QTFOURCC( 'L', 'O', 'O', 'P' ), 1L, &lSizeData ) &&
            ( lSizeData > 0L )) {
            lpUserData = (LPSTR) GlobalLock( ghMem );
            for( i=0; i < lSizeData; i++ ) {
                if( *lpUserData++ != '\0' ) {
                    pMovieData->qtoleOptions.bLoop           = FALSE;
                    pMovieData->qtoleOptions.bLoopPalindrome = TRUE;
                    break;
                }
            }
            GlobalUnlock( ghMem );
        }
        if( ghMem )
            GlobalFree( ghMem );
    }

    // Now tell the movie controller about the loop mode
    MCDoAction( pMovieData->mcMovieController, mcActionSetLooping,
        (LPVOID) ( pMovieData->qtoleOptions.bLoop ||
        pMovieData->qtoleOptions.bLoopPalindrome ));
    MCDoAction( pMovieData->mcMovieController, mcActionSetLoopIsPalindrome,
        (LPVOID) pMovieData->qtoleOptions.bLoopPalindrome );

    // Assume that the controller is attached
    // Get the default UL corner of window
    GetWindowRect( hwndMovie, &rcWindow );
    MapWindowPoints( HWND_DESKTOP, PlayerQueryClientWindow(),
        (LPPOINT) &rcWindow, 2 );
    ptCorner = *((LPPOINT) &rcWindow.left );

    // This includes the movie AND the moviecontroller rect
    MCGetControllerBoundsRect
        ( pMovieData->mcMovieController, &rcController );
    rcWindow = rcController;
    // At this point rcController == client rect of window

    // Save controller height
    g.wMovieControllerHeight =
        ( rcWindow.bottom - rcWindow.top ) -
        ( rcMovie.bottom - rcMovie.top );

    // Remove maximize box if sound only movie
    if( pMovieData->bSoundOnly ) {
        lStyle = GetWindowLong( hwndMovie, GWL_STYLE );
        lStyle &= ~WS_MAXIMIZEBOX;
        SetWindowLong( hwndMovie, GWL_STYLE, lStyle );
    }

    AdjustWindowRect( &rcWindow,
        GetWindowLong( hwndMovie, GWL_STYLE ), FALSE );
    // rcWindow now contains the size of the window

    wWidth  = rcWindow.right  - rcWindow.left;
    wHeight = rcWindow.bottom - rcWindow.top;
    OffsetRect( &rcWindow, ptCorner.x - rcWindow.left,
        ptCorner.y - rcWindow.top );
    // rcWindow now contains the resized window positioned so that
    // the UL corner is at the MDI default position

    // Now stash some dimensions needed later. Only need to do this once
    if( pMovieData->bSoundOnly && !g.wSoundOnlyDefWidth ) {
        g.wSoundOnlyDefWidth = wWidth;
    }
    if( !g.wSides ) {
        g.wSides    = ( rcWindow.right - rcWindow.left ) -
            ( rcController.right - rcController.left );
        g.wTBBorder = ( rcWindow.bottom - rcWindow.top ) -
            ( rcController.bottom - rcController.top );
        g.wTopAndBottom = g.wMovieControllerHeight + g.wTBBorder;
    }


    // Now see if it will fit on screen.
    GetClientRect( PlayerQueryClientWindow(), &rcclientClient );
    if( rcWindow.right > rcclientClient.right ) { // extends beyond right border
                                                  // Try to shift it to the left
        nDiff = rcclientClient.right - wWidth;
        rcWindow.left  = max( 0, nDiff );
        rcWindow.right = rcWindow.left + wWidth;
    }

    if( rcWindow.bottom > rcclientClient.bottom ) { // extends beyond bottom
                                                    // Try to shift it up
        nDiff = rcclientClient.bottom - wHeight;
        rcWindow.top    = max( 0, nDiff );
        rcWindow.bottom = rcWindow.top + wHeight;
    }

    // Resize the window after disabling WM_SIZE processing
    // In the unusual case that the controller is smaller than
    // the smallest possible client, we stretch the controller
    pMovieData->bDisableSizeMsgProcessing = TRUE;
    MoveWindow( hwndMovie,
        rcWindow.left, rcWindow.top, wWidth, wHeight, FALSE );

    // If movie is smaller than minimum window size, resize movie to
    // fill the window
    GetClientRect( hwndMovie, &rcWindow );
    if( !EqualRect( &rcWindow, &rcController ))
        MCSetControllerBoundsRect
        ( pMovieData->mcMovieController, &rcWindow );

    // Size message turns this off, turn in on again for next
    // WM_SIZE message generated by standard "create a window" processing
    pMovieData->bDisableSizeMsgProcessing = TRUE;

    // Post message to activate the movie
    // Post message is used to avoid a extra paint of the movie which
    // causes a flash during the window creation
    // SendMessage is called if PostMessage fails
    if( !PostMessage( hwndMovie, WM_PLAYER_ACTIVATEMOVIE, 0, 0L ))
        SendMessage( hwndMovie, WM_PLAYER_ACTIVATEMOVIE, 0, 0L );

    // Set Action filter to look for selection changes and grow box resizing.
    // Do this after earlier resizing to avoid having to block filter messages
    // generated during creation
    if( pMovieData->lpFilterProc =
        (MCActionFilter) MakeProcInstance
        ( (FARPROC) ActionFilter, PlayerQueryInstance())) {
        MCSetActionFilter( pMovieData->mcMovieController,
            pMovieData->lpFilterProc, MAKELONG( pMovieData, hwndMovie ));
    }

    // Tell qtole.dll that movie has been opened
    if( ( lpOleData = PlayerQueryOleData()) && lpOleData->lpqtoleServer ) {
        qtoleOpenWnd.lStructSize  = sizeof( qtoleOpenWnd );
        qtoleOpenWnd.lVersion     = VERSION_1;
        qtoleOpenWnd.wObjectType  = MOVIE_OBJECT;
        qtoleOpenWnd.hwndObject   = hwndMovie;
        qtoleOpenWnd.lpObjectPath = pMovieData->szMoviePath;
        qtoleOpenWnd.lpObjectName = pMovieData->szMovieName;

        QTOLE_OpeningNewObjectWnd( lpOleData, &qtoleOpenWnd );
    }

    return  0L;

 Failed:
    SetWindowWord( hwndMovie, 0, 0 );
    if( pMovieData )
        LocalUnlock( hMovieData );
    if( hMovieData )
        LocalFree  ( hMovieData );

    return -1;

}

// Function: CheckDupEnumProc - Checks for duplicate pictures and
//                              fixes up titles if there are any
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    Always TRUE;
// --------------------------------------------------------------------
BOOL __export CALLBACK CheckDupEnumProc( HWND hwnd, LPARAM lParam )

// Look for duplicate pictures. Test is on path rather than just name

{
    char            szBuffer[50];     // Temp buffer
    HWND            hwndActiveMovie;  // Handle of active movie wnd
    NPMOVIEDATA     pMovieData;       // -> enum wnd movie data struct
    NPMOVIEDATA     pActiveMovieData; // -> active wnd movie data struct

    // Skip active movie
    if( ( hwndActiveMovie = (HWND) LOWORD( lParam )) == hwnd )
        return TRUE;

    if( !GetClassName( hwnd, szBuffer, sizeof( szBuffer )) ||
        lstrcmpi( szBuffer, PLAYER_MOVIE_CLASS ))
        return TRUE;

    pMovieData = (NPMOVIEDATA) GetWindowWord( hwnd, 0 );
    pActiveMovieData = (NPMOVIEDATA) GetWindowWord( hwndActiveMovie, 0 );
    if( !pMovieData || !pActiveMovieData )
        return TRUE;

    if( !lstrcmpi( pMovieData->szMoviePath,
        pActiveMovieData->szMoviePath )) { // Found a duplicate
        g.wMovieCount++;
        if( g.bCreating ) {
            if( pMovieData->wDuplicationIndex == 0 ) {
                pMovieData->wDuplicationIndex = 1;
                wsprintf( szBuffer, "%s:%u",
                    (LPSTR) pMovieData->szMovieName,
                    pMovieData->wDuplicationIndex );
                SetWindowText( hwnd, szBuffer );
            }
        }
        else {
            if( pMovieData->wDuplicationIndex >
                pActiveMovieData->wDuplicationIndex ) {
                pMovieData->wDuplicationIndex--;
                wsprintf( szBuffer, "%s:%u",
                    (LPSTR) pMovieData->szMovieName,
                    pMovieData->wDuplicationIndex );
                SetWindowText( hwnd, szBuffer );
            }
            if( pMovieData->wDuplicationIndex == 1 )
                g.hwndFirstDup = hwnd;
        }

        if( pMovieData->hwndGetInfo &&
            !PostMessage( pMovieData->hwndGetInfo,
            WM_PLAYER_INFO_UPDATEFILENAME, 0, 0L ))
            SendMessage( pMovieData->hwndGetInfo,
            WM_PLAYER_INFO_UPDATEFILENAME, 0, 0L );
    }

    return TRUE;
}


// Function: PlayerEditCommands - Process WM_COMMAND, Edit popup messages
// --------------------------------------------------------------------
// Parameters: HWND   hwndMovie;      Handle of movie window
//             WORD   wIDItem;        Menu or control id
//             WORD   wNotifyCode;    notification message
//
// Returns:    LONG   generally 0L
// --------------------------------------------------------------------
static LONG NEAR PlayerEditCommands
                    (HWND hwndMovie, WPARAM wIDItem, WORD wNotifyCode )

{
    NPMOVIEDATA      pMovieData;     // -> movie data struct
    TimeRecord       trZeroTime;     // Zero time
    PicHandle        phFrame;        // Pic handle of copied frame
    DIBHandle        hbmpDIB;        // Handle of DIB bitmap passed to clipboard
    OSErr            oserr;          // Errror return value
    WORD             wIDString;      // ID of error string
    HDC              hdc;            // Device context of window
    HDC              hdcMem;         // Memory dc
    ImageDescription idPicInfo;      // Image description struct
    HBITMAP          hbmpCompatible; // Mem bitmap
    HBITMAP          hbmpSave;       // Prev bitmap
    RECT             rcFrame;        // Rect of frame being copied
    TimeRecord       trMovieTime;    // Temp time record
    HPALETTE         hpal;           // Palette handle
    LPQTOLE_OLEDATA  lpOleData;      // -> ole data

    if( !(pMovieData = (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 ))) {
        CommonTellUser( PlayerQueryResources(),
            PLAYER_STRING_NOMOVIEDATA, PLAYER_STRING_CAPTION, MB_OK );
        return 0L;
    }

    switch( wIDItem ) {
        case PLAYER_EDIT_COPY:
            if( IsIconic( hwndMovie ))
                return 0L;

            // If ole, let ole do the copy
            if( ( lpOleData = PlayerQueryOleData()) && lpOleData->lpqtoleServer ) {
                PopulateOptionsStruct( hwndMovie, pMovieData );

                QTOLE_Copy( lpOleData, (LPQTOLE_OPTIONS) &pMovieData->qtoleOptions );

                // We must compensate for an error in certain drivers by
                // resetting the palette back the way we found it
                MCDoAction( pMovieData->mcMovieController,
                    mcActionSetFlags, (LPVOID) mcFlagsUseWindowPalette );
            }
            else // No ole so do copy here
            {
                hdcMem = NULL;
                hbmpCompatible = NULL;
                hbmpSave = NULL;
                phFrame  = NULL;

                if( !( hdc = GetDC( hwndMovie )) ||
                    !( hdcMem = CreateCompatibleDC( hdc )) ||
                    !( phFrame = GetMoviePict( pMovieData->mMovie,
                    GetMovieTime( pMovieData->mMovie, &trMovieTime )))) {
                    wIDString = PLAYER_STRING_COPYFAILED;
                    goto CopyFailed;
                }

                if( oserr = GetPictureInfo( phFrame, &idPicInfo )) {
                    wIDString = PLAYER_STRING_COPYFAILED;
                    goto CopyFailed;
                }

                if( !( hbmpCompatible = CreateCompatibleBitmap( hdc,
                    idPicInfo.width, idPicInfo.height ))) {
                    wIDString = PLAYER_STRING_NOMEMORY;
                    goto CopyFailed;
                }

                if( !( hbmpSave = SelectObject( hdcMem, hbmpCompatible ))) {
                    wIDString = PLAYER_STRING_COPYFAILED;
                    goto CopyFailed;
                }

                rcFrame.left = rcFrame.top = 0;
                rcFrame.right  = idPicInfo.width;
                rcFrame.bottom = idPicInfo.height;

                if( oserr = DrawPicture( hdcMem, phFrame, &rcFrame, NULL )) {
                    wIDString = PLAYER_STRING_COPYFAILED;
                    goto CopyFailed;
                }
                SelectObject( hdcMem, hbmpSave );
                hbmpSave = NULL;
                DeleteDC( hdcMem );
                hdcMem = NULL;

                OpenClipboard( hwndMovie );
                EmptyClipboard();
                if( !SetClipboardData( CF_BITMAP, hbmpCompatible )) {
                    wIDString = PLAYER_STRING_COPYFAILED;
                    goto CopyFailed;
                }
                hbmpCompatible = NULL; // Now belongs to clipboard

                // Now add palette if movie has a palette
                if( ( hpal = GetPicturePalette( phFrame )) &&
                    !SetClipboardData( CF_PALETTE, hpal ))
                    DeleteObject( hpal );
                hpal = NULL;

                // Now try to add DIBitmap to clipboard
                if( ( hbmpDIB = PictureToDIB( phFrame )) &&
                    !SetClipboardData( CF_DIB, hbmpDIB ))
                    GlobalFree( hbmpDIB );
                hbmpDIB = NULL;

                CloseClipboard();
                if( phFrame )
                    DisposePicture( phFrame );
                ReleaseDC( hwndMovie, hdc );

                return 0L;

 CopyFailed:
                CommonTellUser( PlayerQueryResources(),
                    wIDString, PLAYER_STRING_CAPTION, MB_OK );
                if( hbmpSave )
                    SelectObject( hdcMem, hbmpSave );
                if( hdc )
                    ReleaseDC( hwndMovie, hdc );
                if( hdcMem )
                    DeleteDC( hdcMem );
                if( hbmpCompatible )
                    DeleteObject( hbmpCompatible );
                if( phFrame )
                    DisposePicture( phFrame );
                if( hbmpDIB )
                    GlobalFree( hbmpDIB );
            }

            return 0L;

        case PLAYER_EDIT_OPTIONS:
            PopulateOptionsStruct( hwndMovie, pMovieData );

            if( PlayerGetOptions( hwndMovie, &pMovieData->qtoleOptions ))
                UpdateMovieForOptions( hwndMovie, pMovieData, FALSE );

            return 0L;

        case PLAYER_EDIT_CANCELSEL:
            trZeroTime.scale = GetMovieTimeScale( pMovieData->mMovie );
            trZeroTime.value.dwHi = 0L;

            trZeroTime.value.dwLo = (DWORD) -1L;
            trZeroTime.base       = TIMEBASE_DEFAULT;
            // Set beginning of selection to zero
            MCDoAction( pMovieData->mcMovieController,
                mcActionSetSelectionBegin, (LPVOID) &trZeroTime );
            // Set duration to zero
            trZeroTime.value.dwLo = 0L;
            trZeroTime.base       = NULL;
            MCDoAction( pMovieData->mcMovieController,
                mcActionSetSelectionDuration, (LPVOID) &trZeroTime );

            return 0L;
    }

    return 0L; // should never get here

}


// Function: PlayerMovieCommands - Process WM_COMMAND, Movie popup messages
// --------------------------------------------------------------------
// Parameters: HWND          hwndMovie;      Handle of movie window
//             WORD          wIDItem;        Menu or control id
//             WORD          wNotifyCode;    notification message
//
// Returns:    LONG   generally 0L
// --------------------------------------------------------------------
static LONG NEAR PlayerMovieCommands
                  ( HWND hwndMovie, WPARAM wIDItem, WORD wNotifyCode )

{
    NPMOVIEDATA   pMovieData;         // -> movie data struct
    DLGPROC       lpDlgProc;          // -> info dialog proc function
    HWND          hwndInfoDialog;     // HWND of info dialog box
    TimeRecord    trPosterTime;       // Poster time
    BOOL          bPlaySelectionOnly; // Flag


    if( !(pMovieData = (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 ))) {
        CommonTellUser( PlayerQueryResources(),
            PLAYER_STRING_NOMOVIEDATA, PLAYER_STRING_CAPTION, MB_OK );
        return 0L;
    }

    switch( wIDItem ) {
        case PLAYER_MOVIE_GETINFO:
            if( pMovieData->hwndGetInfo )
                SetFocus( pMovieData->hwndGetInfo );
            else if( !(lpDlgProc = (DLGPROC) MakeProcInstance
                ( (FARPROC) GetInfoDlgProc, PlayerQueryInstance())) ||
                !(hwndInfoDialog =
                CreateDialog( PlayerQueryResources(),
                MAKEINTRESOURCE( PLAYER_DLG_GETINFO ),
                PlayerQueryFrameWindow(), lpDlgProc ))) {
                CommonTellUser( PlayerQueryResources(),
                    PLAYER_STRING_NOMEMORY, NULL, MB_OK );
            }

            return 0L;

        case PLAYER_MOVIE_STOPATEND:
            MCDoAction( pMovieData->mcMovieController,
                mcActionSetLoopIsPalindrome, (LPVOID) FALSE );
            MCDoAction( pMovieData->mcMovieController,
                mcActionSetLooping, (LPVOID) FALSE );
            return 0L;

        case PLAYER_MOVIE_LOOP:
            MCDoAction( pMovieData->mcMovieController,
                mcActionSetLooping, (LPVOID) TRUE );
            MCDoAction( pMovieData->mcMovieController,
                mcActionSetLoopIsPalindrome, (LPVOID) FALSE );
            return 0L;

        case PLAYER_MOVIE_BACKANDFORTH:
            MCDoAction( pMovieData->mcMovieController,
                mcActionSetLooping, (LPVOID) TRUE );
            MCDoAction( pMovieData->mcMovieController,
                mcActionSetLoopIsPalindrome, (LPVOID) TRUE );
            return 0L;

        case PLAYER_MOVIE_PLAYSELONLY:
            MCDoAction( pMovieData->mcMovieController,
                mcActionGetPlaySelection,
                (LPVOID) &bPlaySelectionOnly );

            MCDoAction( pMovieData->mcMovieController,
                mcActionSetPlaySelection,
                (LPVOID) !bPlaySelectionOnly );
            return 0L;


        // For next 3 cases, if window is maximized or minimized, we need to
        // restore before resizing so that Windows knows that the window is
        // no longer maximized or minimized

        case PLAYER_MOVIE_HALFSIZE:
            if( ( pMovieData->wMinMaxEtc == SIZE_MAXIMIZED ) ||
                ( pMovieData->wMinMaxEtc == SIZE_MINIMIZED ))
                ShowWindow( hwndMovie, SW_SHOWNORMAL );

            return ResizeMovieAndWindow( hwndMovie, TRUE, pMovieData,
                pMovieData->idMovieInfo.width / 2,
                pMovieData->idMovieInfo.height / 2 );

        case PLAYER_MOVIE_NORMALSIZE:
            if( ( pMovieData->wMinMaxEtc == SIZE_MAXIMIZED ) ||
                ( pMovieData->wMinMaxEtc == SIZE_MINIMIZED ))
                ShowWindow( hwndMovie, SW_SHOWNORMAL );

            return ResizeMovieAndWindow( hwndMovie, TRUE, pMovieData,
                pMovieData->idMovieInfo.width,
                pMovieData->idMovieInfo.height );

        case PLAYER_MOVIE_DOUBLESIZE:
            if( ( pMovieData->wMinMaxEtc == SIZE_MAXIMIZED ) ||
                ( pMovieData->wMinMaxEtc == SIZE_MINIMIZED ))
                ShowWindow( hwndMovie, SW_SHOWNORMAL );

            return ResizeMovieAndWindow( hwndMovie, TRUE, pMovieData,
                pMovieData->idMovieInfo.width * 2,
                pMovieData->idMovieInfo.height * 2 );

        case PLAYER_MOVIE_SHOWPOSTER:
            trPosterTime.value.dwHi = 0L;
            trPosterTime.value.dwLo =
                GetMoviePosterTime( pMovieData->mMovie );
            trPosterTime.scale = GetMovieTimeScale( pMovieData->mMovie );
            trPosterTime.base  = TIMEBASE_DEFAULT;

            // Stop the movie
            MCDoAction( pMovieData->mcMovieController,
                mcActionPlay, (LPVOID) 0 );
            // Go to poster time
            MCDoAction( pMovieData->mcMovieController,
                mcActionGoToTime, (LPVOID) &trPosterTime );
            return 0L;
    }

    return 0L; // should never get here

}

//// the following are some utility routines

// Function: ActivateTheController - Activates or deactivates the movie
//                                   controller
// --------------------------------------------------------------------
// Parameters: HWND         hwndMovie       Handle of movie window
//             NPMOVIEDATA  pMovieData      -> to movie data struct
//             BOOL         bActivate       Activate flag
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR ActivateTheController
             ( HWND hwndMovie, NPMOVIEDATA pMovieData, BOOL bActivate )

{
    short          nVol; // Sound volume

    if( bActivate && ( hwndMovie != (HWND) SendMessage
        ( PlayerQueryClientWindow(), WM_MDIGETACTIVE, 0, 0L )))
        return;

    MCActivate( pMovieData->mcMovieController, hwndMovie, bActivate );

    MCDoAction( pMovieData->mcMovieController,
        mcActionSetKeysEnabled, (LPVOID) bActivate );
    if( bActivate ) // RealizePalette only if activating
    {
        MCDoAction( pMovieData->mcMovieController,
            mcActionSetFlags, (LPVOID) mcFlagsUseWindowPalette );
        // InvalidateRect is needed to cause repainting in certain
        // situations when the window is iconic
        InvalidateRect( hwndMovie, NULL, TRUE );
    }

    MCDoAction( pMovieData->mcMovieController,
        mcActionGetVolume, (LPVOID) &nVol );
    if( ( bActivate && ( nVol < 0 )) || ( !bActivate && ( nVol > 0 )))
        MCDoAction( pMovieData->mcMovieController,
        mcActionSetVolume, (LPVOID) -nVol );
}



// Function: StartTheMovie - Starts the movie
// --------------------------------------------------------------------
// Parameters: NPMOVIEDATA  pMovieData      -> movie data struct
//
// Returns:    LONG         always 0L
// --------------------------------------------------------------------
static LONG NEAR StartTheMovie( NPMOVIEDATA pMovieData )

{ // Reactivate the movie
    SetMovieActive( pMovieData->mMovie, TRUE );
  // Restart the movie if it was playing
    if( pMovieData->lfxSavePlayRate )
        MCDoAction( pMovieData->mcMovieController,
        mcActionPlay, (LPVOID) pMovieData->lfxSavePlayRate );

    return 0L;
}

// Function: StopTheMovie - Stops the movie
// --------------------------------------------------------------------
// Parameters: NPMOVIEDATA  pMovieData      -> movie data struct
//
// Returns:    LONG         always 0L
// --------------------------------------------------------------------
static LONG NEAR StopTheMovie( NPMOVIEDATA pMovieData )

{ // save the play rate
    MCDoAction( pMovieData->mcMovieController,
        mcActionGetPlayRate, (LPVOID) &pMovieData->lfxSavePlayRate );
  // Stop the movie
    MCDoAction( pMovieData->mcMovieController,
        mcActionPlay, (LPVOID) 0 );
  // Deactivate movie to prevent painting over the icon
    SetMovieActive( pMovieData->mMovie, FALSE );

    return 0L;
}


// Function: PaintTheIcon - processes the WM_PAINT message when iconic
// --------------------------------------------------------------------
// Parameters: HWND         hwndPicture     Handle of Picture window
//             NPMOVIEDATA  pMovieData      -> movie data struct
//
// Returns:    LONG         0L
// --------------------------------------------------------------------
static LONG NEAR PaintTheIcon( HWND hwndMovie, NPMOVIEDATA pMovieData )

{
    HCURSOR          hcursorSave;   // Saved cursor
    HBRUSH           hbrush;        // Temp background brush
    PicHandle        phPoster;      // Pic handle
    PAINTSTRUCT      ps;            // Paint struct
    RECT             rcPicRect;     // Proportional pic rect
    WORD             wIconWidth;    // Icon width
    WORD             wIconHeight;   // Icon height
    WORD             wProportional; // Temp

    static RECT      rcIcon = {
                         2, 2, 34, 34}; // Rect used in painting icon

    // Make sure the entire window rect is invalidated
    InvalidateRect( hwndMovie, NULL, FALSE );

    if( !BeginPaint( hwndMovie, &ps ))
        return 0L;

    if( !pMovieData->mMovie ) {
        EndPaint( hwndMovie, &ps );
        return 0L;
    }

    hcursorSave = SetCursor( LoadCursor( NULL, IDC_WAIT ));

    if( pMovieData->bSoundOnly ) { // Erase the entire background since there is no picture
        if( hbrush = CreateSolidBrush( GetSysColor( COLOR_APPWORKSPACE ))) {
            FillRect( ps.hdc, &ps.rcPaint, hbrush );
            DeleteObject( hbrush );
        }
        // Now draw the film strip icon
        if( g.hmovieIcon )
            DrawIcon( ps.hdc, 2, 2, g.hmovieIcon );
    }
    else {
        if( phPoster = GetMoviePosterPict( pMovieData->mMovie )) {
            rcPicRect = rcIcon;
            wIconWidth  = rcIcon.right - rcIcon.left;
            wIconHeight = rcIcon.bottom - rcIcon.top;
            if( pMovieData->idMovieInfo.width >
                pMovieData->idMovieInfo.height ) {
                wProportional = MulDiv( wIconHeight,
                    pMovieData->idMovieInfo.width,
                    pMovieData->idMovieInfo.height );
                rcPicRect.left  -= ( wProportional - wIconWidth ) / 2;
                rcPicRect.right = rcPicRect.left + wProportional;
            }
            else if( pMovieData->idMovieInfo.width <
                pMovieData->idMovieInfo.height ) {
                wProportional = MulDiv( wIconWidth,
                    pMovieData->idMovieInfo.height,
                    pMovieData->idMovieInfo.width );
                rcPicRect.top  -= ( wProportional - wIconHeight ) / 2;
                rcPicRect.bottom = rcPicRect.top + wProportional;
            }

            // Draw the poster frame
            if( DrawPicture( ps.hdc, phPoster, &rcPicRect, NULL )) {
                CommonTellUser( PlayerQueryResources(),
                    PLAYER_STRING_DRAWPICFAILED,
                    PLAYER_STRING_CAPTION, MB_OK );

                // Validate rect to prevent infinite loop
                ValidateRect( hwndMovie, NULL );
                DisposePicture( phPoster );
                SetCursor( hcursorSave );
                EndPaint( hwndMovie, &ps );
                return 0L;
            }
            else {
                DisposePicture( phPoster );
            }
        }

        // Now draw the film strip icon
        if( g.hmovieIcon )
            DrawIcon( ps.hdc, 2, 2, g.hmovieIcon );

        // Now paint background
        ExcludeClipRect( ps.hdc, rcIcon.left, rcIcon.top,
            rcIcon.right, rcIcon.bottom );
        // COLOR_APPWORKSPACE is standard MDI background color
        if( hbrush = CreateSolidBrush( GetSysColor( COLOR_APPWORKSPACE ))) {
            FillRect( ps.hdc, &ps.rcPaint, hbrush );
            DeleteObject( hbrush );
        }
    }

    SetCursor( hcursorSave );
    EndPaint( hwndMovie, &ps );

    return 0L;

}


// Function: PrintFrame - prints indicated number of copies of
//                        the selected frame at normal size
// --------------------------------------------------------------------
// Parameters: HWND         hwndMovie       Handle of movie window
//             LPPRINTDLG   lppd            -> to PRINTDLG struct created
//                                          print common dialog
//
// Returns:    LONG         0L if successful
// --------------------------------------------------------------------
static LONG NEAR PrintFrame( HWND hwndMovie, LPPRINTDLG lppd )

{
    NPMOVIEDATA     pMovieData;  // -> movie data struct
    WORD            i;           // Counter
    DOCINFO         diDocInfo;   // Document info struct
    PicHandle       phFrame;     // Pichandle of current frame
    BOOL            bError;      // Error flag
    int             nError;      // Error return
    OSErr           oserr;       // Error return from DrawPicture
    int             xRes;        // Horz printer resolution
    int             yRes;        // Vert printer resolution
    int             xOffset;     // Horz offset to center picture
    int             yOffset;     // Vert offset to center picture
    RECT            rcFrame;     // Frame rect
    WORD            wWidthPage;  // Width of printed page in pixels
    WORD            wHeightPage; // Height of printed page in pixels
    TimeRecord      trMovieTime; // Temp time record


    if( !(pMovieData = (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 ))) {
        CommonTellUser( PlayerQueryResources(),
            PLAYER_STRING_NOMOVIEDATA, PLAYER_STRING_CAPTION, MB_OK );
        // Turn off not reported flag
        return SP_ERROR &  ~SP_NOTREPORTED;
    }

    diDocInfo.cbSize      = sizeof( DOCINFO );
    diDocInfo.lpszDocName = pMovieData->szMovieName;
    diDocInfo.lpszOutput  = (LPSTR) NULL;

    if( !(phFrame = GetMoviePict( pMovieData->mMovie,
        GetMovieTime( pMovieData->mMovie, &trMovieTime )))) {
        CommonTellUser( PlayerQueryResources(),
            PLAYER_STRING_GETPICTFAILED, PLAYER_STRING_CAPTION, MB_OK );
        // Turn off not reported flag
        return SP_ERROR &  ~SP_NOTREPORTED;
    }

    wWidthPage  = GetDeviceCaps( lppd->hDC, HORZRES );
    wHeightPage = GetDeviceCaps( lppd->hDC, VERTRES );

    xRes = MulDiv( wWidthPage, 254,
        GetDeviceCaps( lppd->hDC, HORZSIZE ) * 10 );
    yRes = MulDiv( wHeightPage, 254,
        GetDeviceCaps( lppd->hDC, VERTSIZE ) * 10 );
    rcFrame.right  = MulDiv( pMovieData->idMovieInfo.width,
        xRes, HIWORD( pMovieData->idMovieInfo.hRes ));
    rcFrame.bottom = MulDiv( pMovieData->idMovieInfo.height,
        yRes, HIWORD( pMovieData->idMovieInfo.vRes ));
    rcFrame.left = rcFrame.top = 0;

    // Now make sure that frame fits on page
    if( rcFrame.right > (int) wWidthPage ) {
        rcFrame.bottom = MulDiv( rcFrame.bottom,
            wWidthPage, rcFrame.right );
        rcFrame.right = wWidthPage;
    }
    if( rcFrame.bottom > (int) wHeightPage ) {
        rcFrame.right = MulDiv( rcFrame.right,
            wHeightPage, rcFrame.bottom );
        rcFrame.bottom = wHeightPage;
    }

    // Now center rect on page
    xOffset = (wWidthPage  - rcFrame.right ) / 2;
    yOffset = (wHeightPage - rcFrame.bottom ) / 2;
    OffsetRect( &rcFrame, xOffset, yOffset );

    bError = FALSE;
    oserr  = 0L;
    nError = SP_ERROR;

    if( StartDoc( lppd->hDC, &diDocInfo ) > 0 ) {
        for( i=0; (i < lppd->nCopies) && !bError; i++ ) {
            if( StartPage( lppd->hDC ) > 0) {
                if( (oserr = DrawPicture
                    ( lppd->hDC, phFrame, &rcFrame, NULL )) != 0L ) {
                    AbortDoc( lppd->hDC );
                    bError = TRUE;
                }
                else if( (nError = EndPage( lppd->hDC )) < 0 )
                    bError = TRUE;
            }
            else
                bError = TRUE;
        }
    }
    else
        bError = TRUE;

    if( !bError )
        EndDoc( lppd->hDC );

    if( phFrame )
        DisposePicture( phFrame );

    return (LONG) nError;
}


// Function: SetMinMaxInfo - Processes the WM_GETMINMAXINFO message
// --------------------------------------------------------------------
// Parameters: HWND               hwndMovie     Handle of movie wnd
//             NPMOVIEDATA        pMovieData    -> to movie data
//             MINMAXINFO FAR*    lpmmi         -> to minmaxinfo struct
//
// Returns:    LONG               always 0L
// --------------------------------------------------------------------
static LONG NEAR SetMinMaxInfo
      ( HWND hwndMovie, NPMOVIEDATA pMovieData, MINMAXINFO FAR* lpmmi )

{
    RECT   rcWindow; // Movie window rect

    if( PlayerQueryMDIAction() == PLAYER_WINDOW_CASCADE ) {
        GetWindowRect( hwndMovie, &rcWindow );
        lpmmi->ptMaxTrackSize.x = rcWindow.right - rcWindow.left;
        lpmmi->ptMaxTrackSize.y = rcWindow.bottom - rcWindow.top;
        lpmmi->ptMinTrackSize = lpmmi->ptMaxTrackSize;
        lpmmi->ptMaxSize      = lpmmi->ptMaxTrackSize;
    }
    else {
        lpmmi->ptMinTrackSize.x = 3 * GetSystemMetrics( SM_CXSIZE );
        lpmmi->ptMinTrackSize.y = GetSystemMetrics( SM_CYCAPTION ) +
            g.wMovieControllerHeight +
            2 * GetSystemMetrics( SM_CYFRAME );
        // Disable vertical resizing if sound only movie
        if( pMovieData && pMovieData->bSoundOnly ) {
            lpmmi->ptMinTrackSize.y -= 2;
            lpmmi->ptMaxTrackSize.y = lpmmi->ptMinTrackSize.y;
        }
    }

    return 0L;
}


// Function: GetInfoDlgProc - Get Info dialog proc
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    As required by Microsoft Windows
// --------------------------------------------------------------------
BOOL __export CALLBACK GetInfoDlgProc
    ( HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam )

{
    NPMOVIEDATA   pMovieData; // -> movie data struct
    HWND          hwndMovie;  // HWND of movie window
    HWND          hwndCtl;    // Handle of control
    HDC           hdc;        // DC of dialog
    int           nHeight;    // Height of control text font

    switch( message ) {
        case WM_INITDIALOG:
            hwndMovie = (HWND) SendMessage
                ( PlayerQueryClientWindow(), WM_MDIGETACTIVE, 0, 0L );

            // Use property to associate movie window handle with
            // info dialog. This is necessary because MDI makes frame
            // window the parent of all dialogs no matter what handle
            // is used in the CreateDialog call
            SetProp( hdlg, GETINFOMOVIEPROP, (HANDLE) hwndMovie );

            if( pMovieData = (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 )) {
                pMovieData->hwndGetInfo = hdlg;
                // Cache selection strings to speed up updates
                if( !g.szSelectFormat[0] )
                    LoadString( PlayerQueryResources(),
                    PLAYER_STRING_SELECTION,
                    g.szSelectFormat, sizeof( g.szSelectFormat ));
                if( !g.szNoSelection[0] )
                    LoadString( PlayerQueryResources(),
                    PLAYER_STRING_NOSELECTION,
                    g.szNoSelection, sizeof( g.szNoSelection ));

                if( hdc = GetDC( hdlg )) {
                    nHeight = -MulDiv( 8, GetDeviceCaps( hdc, LOGPIXELSY ), 72 );
                    if( pMovieData->hfInfo = MakeAnArialFont( hdc, nHeight )) {
                        hwndCtl = GetWindow( hdlg, GW_CHILD );
                        while( hwndCtl ) {
                            if( GetDlgCtrlID( hwndCtl ) != IDOK )
                                SendMessage( hwndCtl, WM_SETFONT,
                                (WPARAM) pMovieData->hfInfo, 0 );
                            hwndCtl = GetWindow( hwndCtl, GW_HWNDNEXT );
                        }
                    }
                    ReleaseDC( hdlg, hdc );
                }

                FillMovieInfo( hdlg, pMovieData );
            }

            return TRUE;

        case WM_ACTIVATE:
            PlayerSetActiveModeless( wParam, hdlg );
            return TRUE;

        // WM_USER messages

        case WM_PLAYER_INFO_UPDATEFILENAME:
            hwndMovie = (HWND) GetProp( hdlg, GETINFOMOVIEPROP );
            if( IsWindow( hwndMovie ) && ( pMovieData =
                (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 )))
                UpdateInfoFileName( pMovieData );
            break;

        case WM_PLAYER_INFO_UPDATE:
            hwndMovie = (HWND) GetProp( hdlg, GETINFOMOVIEPROP );
            if( IsWindow( hwndMovie ) && ( pMovieData =
                (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 )))
                FillMovieInfo( hdlg, pMovieData );
            break;

        // End WM_USER messages

        case WM_COMMAND:
            return DestroyWindow( hdlg );

        case WM_DESTROY:
            hwndMovie = (HWND) GetProp( hdlg, GETINFOMOVIEPROP );
            if( IsWindow( hwndMovie ) && ( pMovieData =
                (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 ))) {
                pMovieData->hwndGetInfo = NULL;

                if( pMovieData->hfInfo )
                    DeleteObject( pMovieData->hfInfo );
                pMovieData->hfInfo = NULL;
            }

            RemoveProp( hdlg, GETINFOMOVIEPROP );
            break;
    }

    return FALSE;

}


// Function: FillMovieInfo - Fills dialog controls with movie data
// --------------------------------------------------------------------
// Parameters: HWND          hdlg            Handle of dialog wnd
//             NPMOVIEDATA   pMovieData      -> movie data struct
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR FillMovieInfo( HWND hdlg, NPMOVIEDATA pMovieData )

{
    char           szBuffer[MAX_PATH_LEN];          // Buffer
    char           szFormat[30];                    // Format buffer
    TimeValue      tvDuration;                      // Duration of movie
    double         fTimeScale;                      // Timescale
    RECT           rcMovie;                         // Current movie rect
    DWORD          dwColors;                        // Number of colors
    char           szCompressor[sizeof(DWORD) + 1]; // compressor type
    WORD           wIDString;                       // Colors string id
    DWORD          dwRate;                          // Sound sample rate
    DWORD          dwKRate;                         // Sound sample rate
    char           szNull[] = "";                   // Static null string

    g.bUpdatingInfo = TRUE;

    // Movie name: Append duplication index if > 0
    UpdateInfoFileName( pMovieData );

    // file size
    SetDlgItemText( hdlg, MOVIE_INFO_FILESIZE, pMovieData->szFileSize );

    // Get movie time scale
    if( ( fTimeScale = (double)
        GetMovieTimeScale( pMovieData->mMovie )) != 0. ) { // Do these only if timescale != zero to avoid divide by 0
                                                           // Movie duration
        tvDuration = GetMovieDuration( pMovieData->mMovie );
        LoadString( PlayerQueryResources(), PLAYER_STRING_DURATION,
            szFormat, sizeof( szFormat ));
        sprintf( szBuffer, szFormat, tvDuration / fTimeScale );
        SetDlgItemText( hdlg, MOVIE_INFO_DURATION, szBuffer );

        // Current selection
        DisplayCurrentSelection( pMovieData );
    }

    if( !pMovieData->bSoundOnly ) { // Current size
        LoadString( PlayerQueryResources(), PLAYER_STRING_WANDH,
            szFormat, sizeof( szFormat ));
        GetMovieBox( pMovieData->mMovie, &rcMovie );

        wsprintf( szBuffer, szFormat, rcMovie.right - rcMovie.left,
            rcMovie.bottom - rcMovie.top );
        SetDlgItemText( hdlg, MOVIE_INFO_CURSIZE, szBuffer );

        // Normal Width and Height
        wsprintf( szBuffer, szFormat,
            pMovieData->idMovieInfo.width,
            pMovieData->idMovieInfo.height );
        SetDlgItemText( hdlg, MOVIE_INFO_WANDH, szBuffer );
        wsprintf( szBuffer, szFormat, pMovieData->idMovieInfo.height );

        // Resolution
        if( HIWORD( pMovieData->idMovieInfo.hRes ) != 0 ) {
            LoadString( PlayerQueryResources(),
                PLAYER_STRING_RESOLUTION, szFormat, sizeof( szFormat ));
            wsprintf( szBuffer, szFormat,
                HIWORD( pMovieData->idMovieInfo.hRes ));
        }
        else {
            LoadString( PlayerQueryResources(),
                PLAYER_STRING_NORESOLUTION, szBuffer, sizeof( szBuffer ));
        }
        SetDlgItemText( hdlg, MOVIE_INFO_RESOLUTION, szBuffer );


        // Normal colors
        switch( pMovieData->idMovieInfo.depth ) {
            case 1:      // Black and White
                wIDString = PLAYER_STRING_CLRS_BANDW;      
                break;
            case 1 + 32: // 2 Grays
                wIDString = PLAYER_STRING_CLRS_2GRAYS;     
                break;
            case 2:      // 4 Colors
                wIDString = PLAYER_STRING_CLRS_4COLORS;    
                break;
            case 2 + 32: // 4 Grays
                wIDString = PLAYER_STRING_CLRS_4GRAYS;     
                break;
            case 4:      // 16 Colors
                wIDString = PLAYER_STRING_CLRS_16COLORS;   
                break;
            case 4 + 32: // 16 Grays
                wIDString = PLAYER_STRING_CLRS_16GRAYS;    
                break;
            case 8:      // 256 Colors
                wIDString = PLAYER_STRING_CLRS_256COLORS;  
                break;
            case 8 + 32: // 256 Grays
                wIDString = PLAYER_STRING_CLRS_256GRAYS;   
                break;
            case 16:     // Thousands of Colors
                wIDString = PLAYER_STRING_CLRS_THOUSANDS;  
                break;
            case 24:     // Millions of Colors
                wIDString = PLAYER_STRING_CLRS_MILLIONS;   
                break;
            case 32:     // Millions of Colors+
                wIDString = PLAYER_STRING_CLRS_MILLNSPLUS; 
                break;
            default:
                wIDString = 0xffff;
                if( pMovieData->idMovieInfo.depth < 32 ) {
                    LoadString( PlayerQueryResources(), PLAYER_STRING_COLORS,
                        szFormat, sizeof( szFormat ));
                    dwColors = 1L << pMovieData->idMovieInfo.depth;
                    wsprintf( szBuffer, szFormat, dwColors );
                }
                else {
                    szBuffer[0] = '\0';
                }
                break;
        }
        if( wIDString != 0xffff )
            LoadString( PlayerQueryResources(), wIDString,
            szBuffer, sizeof( szBuffer ));
        SetDlgItemText( hdlg, MOVIE_INFO_COLORS, szBuffer );

        // Compressor type. Use .name if not empty
        if( pMovieData->idMovieInfo.name[0] ) {
            SetDlgItemText( hdlg, MOVIE_INFO_COMPRESSOR,
                pMovieData->idMovieInfo.name );
        }
        else // Check Codec type for 'raw'
        { // Compressor type is stored as a DWORD. i.e. 'jpeg'
          // Spaces in "raw ", "rle " etc are necessary !!!
            if( pMovieData->idMovieInfo.CodecType ) {
                *((PDWORD) &szCompressor) = pMovieData->idMovieInfo.CodecType;
                szCompressor[ sizeof(DWORD) ] = '\0';
            }
            szBuffer[0] = '\0';
            if( !pMovieData->idMovieInfo.CodecType ||
                !lstrcmpi( szCompressor, "raw " ))
                wIDString = PLAYER_STRING_CODEC_NONE;
            else if( !lstrcmpi( szCompressor, "jpeg" ))
                wIDString = PLAYER_STRING_CODEC_PHOTO;
            else if( !lstrcmpi( szCompressor, "rle " ))
                wIDString = PLAYER_STRING_CODEC_ANIMATION;
            else if( !lstrcmpi( szCompressor, "smc " ))
                wIDString = PLAYER_STRING_CODEC_GRAPHICS;
            else if( !lstrcmpi( szCompressor, "rpza" ))
                wIDString = PLAYER_STRING_CODEC_VIDEO;
            else if( !lstrcmpi( szCompressor, "cvid" ))
                wIDString = PLAYER_STRING_CODEC_CVID;
            else
                wIDString = PLAYER_STRING_CODEC_NONE;

            LoadString( PlayerQueryResources(), wIDString,
                szBuffer, sizeof( szBuffer ));

            SetDlgItemText( hdlg, MOVIE_INFO_COMPRESSOR, szBuffer );
        }
    }
    else {
        LoadString( PlayerQueryResources(),
            PLAYER_STRING_NOVIDEO, szBuffer, sizeof( szBuffer ));
        SetDlgItemText( hdlg, MOVIE_INFO_CURSIZE,    szBuffer );
        SetDlgItemText( hdlg, MOVIE_INFO_WANDH,      szNull );
        SetDlgItemText( hdlg, MOVIE_INFO_RESOLUTION, szNull );
        SetDlgItemText( hdlg, MOVIE_INFO_COLORS,     szNull );
        SetDlgItemText( hdlg, MOVIE_INFO_COMPRESSOR, szNull );
    }

    // Sound info
    if( pMovieData->oserrSoundInfo == noSoundTrackInMovie ) {
        LoadString( PlayerQueryResources(),
            PLAYER_STRING_SND_NOSOUND, szBuffer, sizeof( szBuffer ));
        SetDlgItemText( hdlg, MOVIE_INFO_SND_NUMCHANNELS, szBuffer );
        SetDlgItemText( hdlg, MOVIE_INFO_SND_SOUNDQUALITY, szNull );
    }
    else if( pMovieData->oserrSoundInfo == soundSupportNotAvailable ) {
        LoadString( PlayerQueryResources(),
            PLAYER_STRING_SND_NOCARD, szBuffer, sizeof( szBuffer ));
        SetDlgItemText( hdlg, MOVIE_INFO_SND_NUMCHANNELS, szBuffer );
        SetDlgItemText( hdlg, MOVIE_INFO_SND_SOUNDQUALITY, szNull );
    }
    else if( pMovieData->oserrSoundInfo ) { // GetSoundInfo call failed so doen't write anything
    }
    else { // Num of channels
        if( pMovieData->sdSoundInfo.numChannels == 1 )
            LoadString( PlayerQueryResources(), PLAYER_STRING_SND_MONO,
            szBuffer, sizeof( szBuffer ));
        else
            LoadString( PlayerQueryResources(), PLAYER_STRING_SND_STEREO,
            szBuffer, sizeof( szBuffer ));
        SetDlgItemText( hdlg, MOVIE_INFO_SND_NUMCHANNELS, szBuffer );

        // Sample size and rate
        LoadString( PlayerQueryResources(),
            PLAYER_STRING_SND_SOUNDQUALITY, szFormat, sizeof( szFormat ));
        dwRate = ((DWORD) pMovieData->sdSoundInfo.sampleRate ) / 65536L;
        dwKRate = dwRate / 1000;
        dwRate -= dwKRate * 1000;
        wsprintf( szBuffer, szFormat,
            pMovieData->sdSoundInfo.sampleSize, dwKRate, dwRate );
        SetDlgItemText( hdlg, MOVIE_INFO_SND_SOUNDQUALITY, szBuffer );
    }

    // End sound info

    g.bUpdatingInfo = FALSE;

    return;
}


// Function: UpdateInfoFileName - Updates the file name with the current
//                                instance count in the info dialog
// --------------------- -----------------------------------------------
// Parameters: NPMOVIEDATA    pMovieData       -> to MOVIEDATA struct
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR UpdateInfoFileName( NPMOVIEDATA pMovieData )

{
    char      szBuffer[MAX_NAME_LEN + 10]; // Buffer
    char      szFormat[20];                // Format buffer

    lstrcpy( szFormat, "%s%s" );
    if( pMovieData->wDuplicationIndex > 0 )
        lstrcat( szFormat, ":%u" );
    wsprintf( szBuffer, szFormat, (LPSTR) pMovieData->szMovieName,
        (LPSTR) pMovieData->szMovieExt,
        pMovieData->wDuplicationIndex );
    AnsiUpper( szBuffer );
    SetDlgItemText( pMovieData-> hwndGetInfo,
        MOVIE_INFO_NAME, (LPSTR) szBuffer );

    return;
}


// Function:  DisplayCurrentSelection - Displays the current selection in
//                                      the info dialog
// --------------------------------------------------------------------
// Parameters: NPMOVIEDATA      pMovieData        -> movie data struct
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR DisplayCurrentSelection( NPMOVIEDATA pMovieData )

{
    char     szBuffer[100];   // Buffer
    double   fSelectionStart; // Beginning of selection
    double   fSelectionEnd;   // End of selection
    double   fTimeScale;      // Time scale of movie

    if( ( ( fTimeScale = (double)
        GetMovieTimeScale( pMovieData->mMovie )) != 0. ) &&
        ( pMovieData->trSelectionStart.value.dwLo != (DWORD) -1) &&
        ( pMovieData->trSelectionDuration.value.dwLo > 0L )) {
        fSelectionStart = pMovieData->trSelectionStart.value.dwLo /
            fTimeScale ;
        fSelectionEnd = fSelectionStart +
            pMovieData->trSelectionDuration.value.dwLo /
            fTimeScale ;

        sprintf( szBuffer, g.szSelectFormat, fSelectionStart, fSelectionEnd );
        SetDlgItemText( pMovieData-> hwndGetInfo,
            MOVIE_INFO_SELECTION, szBuffer );
    }
    else {
        SetDlgItemText( pMovieData-> hwndGetInfo,
            MOVIE_INFO_SELECTION, g.szNoSelection );
    }

    return;

}


// Function: ActionFilter - Used to filter the selection change message
// --------------------------------------------------------------------
// Parameters: MovieController  mcController      Movie controller
//             UNIT             psAction          action parameter
//             LPVOID           lpParm            DoAction parameter
//             LONG             lUserData         User defined data
//
// Returns:    BOOL             TRUE  if no further action
//                              FALSE if controller is to perform action
// --------------------------------------------------------------------
BOOL __export CALLBACK ActionFilter
    ( MovieController mcController, UINT psAction,
    LPVOID lpParm, LONG lUserData )

{
    NPMOVIEDATA         pMovieData; // -> movie data struct

    switch( psAction ) {
        case mcActionControllerSizeChanged:
            pMovieData = (NPMOVIEDATA) LOWORD( lUserData );
            if( !pMovieData->bSettingControllerSize ) {
                ResizeMovieAndWindow( (HWND) HIWORD( lUserData ),
                    FALSE, pMovieData, 0, 0 );
                if( pMovieData->hwndGetInfo && !g.bUpdatingInfo ) { // Need to post message here because FALSE second
                                                                    // parameter prevents ResizeMovie from being called
                                                                    // by ResizeMovieAndWindow.
                                                                    // WM_PLAYER_INFO_UPDATE is defined to be in
                                                                    // the range WM_COALESCE_FIRST to WM_COALESCE_LAST
                                                                    // which tells Windows to prevent duplicate messages
                                                                    // from appearing in the queue.
                    PostMessage( pMovieData->hwndGetInfo,
                        WM_PLAYER_INFO_UPDATE, 0, 0L );
                }
            }
            else {
                pMovieData->bSettingControllerSize = FALSE;
            }

            break;

        case mcActionSetSelectionBegin:
            pMovieData = (NPMOVIEDATA) LOWORD( lUserData );
            pMovieData->trSelectionStart = *( TimeRecord FAR *) lpParm;

            break;

        case mcActionSetSelectionDuration:
            pMovieData = (NPMOVIEDATA) LOWORD( lUserData );
            pMovieData->trSelectionDuration = *( TimeRecord FAR *) lpParm;

            if( pMovieData->hwndGetInfo )
                DisplayCurrentSelection( pMovieData );
            break;

        default:
            break;
    }

    return FALSE;
}


// Function: InitializePopupMenus - Called just before the popup menus
//                                  are displayed
// --------------------------------------------------------------------
// Parameters: HWND         hwndMovie       Handle of movie window
//             HMENU        hmenuPopup      Handle of popup menu
//             int          nPopupIndex     Index of popup
//
// Returns:    LONG         0L if successful
// --------------------------------------------------------------------
static LONG NEAR InitializePopupMenus
                ( HWND hwndMovie, HMENU hmenuPopup, int nPopupIndex )

{
    NPMOVIEDATA   pMovieData;         // -> movie data struct
    LONG          lControllerInfo;    // Controller info
    BOOL          bLooping;           // TRUE if looping
    BOOL          bPalindrome;        // TRUE if looping palindrome
    BOOL          bPlaySelectionOnly; // TRUE if playing selection only
    WORD          wSelectedSize;      // Nonzero if movie is a standard size
    RECT          rcMovie;            // Movie rect
    WORD          wMovieWidth;        // Width of movie
    WORD          wMovieHeight;       // Height of movie
    char          szTitle[64];        // OLE Client doc title
    char          szItemFormat1[32];  // Item format string
    char          szItem1[128];       // New item string
    char          szItemFormat2[32];  // Item format string
    char          szItem2[128];       // New item string



    if( !(pMovieData = (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 ))) {
        CommonTellUser( PlayerQueryResources(),
            PLAYER_STRING_NOMOVIEDATA, PLAYER_STRING_CAPTION, MB_OK );
        return 0L;
    }

    MCGetControllerInfo( pMovieData->mcMovieController,
        &lControllerInfo );

    // Decrement index if maximized since MDI adds a system menu item
    // which puts count off by one
    if( IsZoomed( hwndMovie ))
        nPopupIndex--;

    if( nPopupIndex == MENU_FILE_POS ) { // See if wnd is an activated client object
        if( QTOLE_IsActiveObjectWnd
            ( PlayerQueryOleData(), hwndMovie, szTitle )) {
            LoadString( PlayerQueryResources(),
                PLAYER_STRING_OLECLOSE, szItemFormat1, sizeof( szItemFormat1 ));
            wsprintf( szItem1, szItemFormat1, (LPSTR) szTitle );

            LoadString( PlayerQueryResources(),
                PLAYER_STRING_OLEEXIT, szItemFormat2, sizeof( szItemFormat2 ));
            wsprintf( szItem2, szItemFormat2, (LPSTR) szTitle );
        }
        else {
            LoadString( PlayerQueryResources(),
                PLAYER_STRING_CLOSE, szItem1, sizeof( szItem1 ));
            LoadString( PlayerQueryResources(),
                PLAYER_STRING_EXIT, szItem2, sizeof( szItem2 ));
        }

        DeleteMenu( hmenuPopup, PLAYER_FILE_CLOSE, MF_BYCOMMAND );
        InsertMenu( hmenuPopup, 1, MF_BYPOSITION,
            PLAYER_FILE_CLOSE, szItem1 );
        DeleteMenu( hmenuPopup, PLAYER_FILE_EXIT, MF_BYCOMMAND );
        InsertMenu( hmenuPopup, (UINT) -1, MF_BYPOSITION,
            PLAYER_FILE_EXIT, szItem2 );

        EnableMenuItem( hmenuPopup, PLAYER_FILE_PRINT,
            ( pMovieData->bSoundOnly ||
            (lControllerInfo & mcInfoIsPlaying)) ? MF_GRAYED: MF_ENABLED );
        EnableMenuItem( hmenuPopup, PLAYER_FILE_PRTSETUP,
            pMovieData->bSoundOnly ? MF_GRAYED: MF_ENABLED );
    }
    else if( nPopupIndex == MENU_EDIT_POS ) {
        EnableMenuItem( hmenuPopup, PLAYER_EDIT_COPY,
            ( IsIconic( hwndMovie ) ||
            ( lControllerInfo & mcInfoIsPlaying )) ? MF_GRAYED: MF_ENABLED );
        EnableMenuItem( hmenuPopup, PLAYER_EDIT_OPTIONS,
            ( IsIconic( hwndMovie ) ||
            ( lControllerInfo & mcInfoIsPlaying )) ? MF_GRAYED: MF_ENABLED );

    }
    else if( nPopupIndex == MENU_MOVIE_POS ) {
        bLooping    = (( lControllerInfo & mcInfoIsLooping ) != 0L );
        bPalindrome = (( lControllerInfo & mcInfoIsInPalindrome ) != 0L );

        CheckMenuItem( hmenuPopup, PLAYER_MOVIE_STOPATEND,
            ( bLooping || bPalindrome ) ? MF_UNCHECKED: MF_CHECKED );
        CheckMenuItem( hmenuPopup, PLAYER_MOVIE_LOOP,
            ( bLooping && !bPalindrome ) ? MF_CHECKED: MF_UNCHECKED );
        CheckMenuItem( hmenuPopup, PLAYER_MOVIE_BACKANDFORTH,
            ( bLooping && bPalindrome ) ? MF_CHECKED: MF_UNCHECKED );

        // Toggle play selection only
        MCDoAction( pMovieData->mcMovieController,
            mcActionGetPlaySelection, (LPVOID) &bPlaySelectionOnly );
        CheckMenuItem( hmenuPopup, PLAYER_MOVIE_PLAYSELONLY,
            ( bPlaySelectionOnly ) ? MF_CHECKED: MF_UNCHECKED );


        EnableMenuItem( hmenuPopup, PLAYER_MOVIE_HALFSIZE,
            pMovieData->bSoundOnly ? MF_GRAYED: MF_ENABLED );
        EnableMenuItem( hmenuPopup, PLAYER_MOVIE_NORMALSIZE,
            pMovieData->bSoundOnly ? MF_GRAYED: MF_ENABLED );
        EnableMenuItem( hmenuPopup, PLAYER_MOVIE_DOUBLESIZE,
            pMovieData->bSoundOnly ? MF_GRAYED: MF_ENABLED );

        // Set selected size parameter if movie is a standard size
        if( IsIconic( hwndMovie ))
            wSelectedSize = 0;
        else {
            GetMovieBox( pMovieData->mMovie, &rcMovie );
            wMovieWidth  = rcMovie.right - rcMovie.left;
            wMovieHeight = rcMovie.bottom - rcMovie.top;

            if( ( wMovieWidth == pMovieData->idMovieInfo.width ) &&
                ( wMovieHeight == pMovieData->idMovieInfo.height ))
                wSelectedSize = PLAYER_MOVIE_NORMALSIZE;
            else if( ( wMovieWidth == pMovieData->idMovieInfo.width * 2 ) &&
                ( wMovieHeight == pMovieData->idMovieInfo.height * 2 ))
                wSelectedSize = PLAYER_MOVIE_DOUBLESIZE;
            else if( ( wMovieWidth == pMovieData->idMovieInfo.width / 2 ) &&
                ( wMovieHeight == pMovieData->idMovieInfo.height / 2 ))
                wSelectedSize = PLAYER_MOVIE_HALFSIZE;
            else
                wSelectedSize = 0;
        }

        CheckMenuItem( hmenuPopup, PLAYER_MOVIE_HALFSIZE,
            ( !pMovieData->bSoundOnly &&
            ( wSelectedSize == PLAYER_MOVIE_HALFSIZE)) ?
            MF_CHECKED: MF_UNCHECKED );
        CheckMenuItem( hmenuPopup, PLAYER_MOVIE_NORMALSIZE,
            ( !pMovieData->bSoundOnly &&
            ( wSelectedSize == PLAYER_MOVIE_NORMALSIZE)) ?
            MF_CHECKED: MF_UNCHECKED );
        CheckMenuItem( hmenuPopup, PLAYER_MOVIE_DOUBLESIZE,
            ( !pMovieData->bSoundOnly &&
            ( wSelectedSize == PLAYER_MOVIE_DOUBLESIZE)) ?
            MF_CHECKED: MF_UNCHECKED );

        EnableMenuItem( hmenuPopup, PLAYER_MOVIE_SHOWPOSTER,
            pMovieData->bSoundOnly ? MF_GRAYED: MF_ENABLED );
    }

    return 0L;
}


// Function: ResizeMovieAndWindow - Resizes the movie and window to the
//                                  input width and height if flag is TRUE
//                                  Otherwise resize window to match movie
// --------------------------------------------------------------------
// Parameters: HWND         hwndMovie       Handle of Movie window
//             BOOL         bResizeMovie    TRUE if movie should be resized
//                                          This will be FALSE when the grow
//                                          box is used to resize since the
//                                          movie will already be resized
//             NPMOVIEDATA  pMovieData      -> to movie data struct
//             WORD         wMovieWidth     width of movie
//             WORD         wMovieHeight    height of movie
//
// Returns:    LONG         0L if successful
// --------------------------------------------------------------------
static LONG NEAR ResizeMovieAndWindow( HWND hwndMovie, BOOL bResizeMovie,
               NPMOVIEDATA pMovieData, WORD wMovieWidth, WORD wMovieHeight )

{
    RECT          rcWindow;     // Window rect
    RECT          rcWindowPrev; // Prev Window rect
    RECT          rcBounds;     // Bounds rect
    POINT         ptCorner;     // Upper-Left corner of window
    LONG          lError;       // Error return
    POINT         ptTemp[2];    // Temp array of points


    if( bResizeMovie &&
        ( lError = ResizeMovie( pMovieData, wMovieWidth, wMovieHeight )))
        return lError;

    // Now Get the bounds rect. In this app, this is the same as the
    // client rect of the window
    MCGetControllerBoundsRect( pMovieData->mcMovieController, &rcBounds );

    // Set about resizing window
    // Save the prev window rect in client window coordinates
    GetWindowRect( hwndMovie, &rcWindow );
    MapWindowPoints( HWND_DESKTOP, PlayerQueryClientWindow(),
        (LPPOINT) &rcWindow, 2 );
    rcWindowPrev = rcWindow;
    ptCorner = *((LPPOINT) &rcWindow );

    // Adjust corner. This will only happen if window has been
    // moved using the grow box
    if( !bResizeMovie && ( rcBounds.left || rcBounds.top )) {
        ptTemp[0].x = ptTemp[0].y = 0;
        ptTemp[1] = *((LPPOINT) &rcBounds );
        MapWindowPoints( hwndMovie, PlayerQueryClientWindow(), ptTemp, 2 );

        ptCorner.x += ptTemp[1].x - ptTemp[0].x;
        ptCorner.y += ptTemp[1].y - ptTemp[0].y;
    }

    rcWindow = rcBounds;
    AdjustWindowRect( &rcWindow,
        GetWindowLong( hwndMovie, GWL_STYLE ), FALSE );
    // rcWindow contains the new size of the window.
    // Now offset to the corrent UL corner
    OffsetRect( &rcWindow, ptCorner.x - rcWindow.left,
        ptCorner.y - rcWindow.top );

    // If window rect has changed, resize after
    // disabling the WM_SIZE processing
    if( !EqualRect( &rcWindow, &rcWindowPrev )) {
        pMovieData->bDisableSizeMsgProcessing = TRUE;
        MoveWindow( hwndMovie, rcWindow.left, rcWindow.top,
            rcWindow.right - rcWindow.left,
            rcWindow.bottom - rcWindow.top, TRUE );

        // If if happens that after resizing, the movie is smaller than
        // minimum movie window size, resize movie to fill the window
        GetClientRect( hwndMovie, &rcWindow );
        if( !EqualRect( &rcWindow, &rcBounds )) {
            pMovieData->bSettingControllerSize = TRUE;
            MCSetControllerBoundsRect
                ( pMovieData->mcMovieController, &rcWindow );
        }
    }

    return 0L;
}


// Function: ResizeMovie - Resizes the movie to the given size
// --------------------------------------------------------------------
// Parameters: NPMOVIEDATA  pMovieData      Pointer to movie data struct
//             WORD         wMovieWidth     width of movie
//             WORD         wMovieHeight    height of movie
//
// Returns:    LONG         0L if successful
// --------------------------------------------------------------------
static LONG NEAR ResizeMovie( NPMOVIEDATA pMovieData,
                                 WORD wMovieWidth, WORD wMovieHeight )

{
    RECT     rcBounds; // Controller bounds rect

    // set based on input movie dimensions.
    // Need to add in controller height for bounds rect
    rcBounds.left = rcBounds.top = 0;
    rcBounds.right  = wMovieWidth;
    rcBounds.bottom = wMovieHeight + g.wMovieControllerHeight;

    pMovieData->bSettingControllerSize = TRUE;
    MCSetControllerBoundsRect( pMovieData->mcMovieController, &rcBounds );

    if( pMovieData->hwndGetInfo && !g.bUpdatingInfo ) { // WM_PLAYER_INFO_UPDATE is defined to be in the range WM_COALESCE_FIRST
                                                        // to WM_COALESCE_LAST which tells Windows to prevent duplicate messages
                                                        // from appearing in the queue.
        PostMessage( pMovieData->hwndGetInfo, WM_PLAYER_INFO_UPDATE, 0, 0L );
    }

    return 0L;
}


// Function: UpdateGBBoundsRect - Updates the grow box bounds rect
// --------------------------------------------------------------------
// Parameters: HWND            hwndMovie     Handle of movie window
//             NPMOVIEDATA     pMovieData    -> movie data struct
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR UpdateGBBoundsRect( HWND hwndMovie, NPMOVIEDATA pMovieData )

{
    GetClientRect( PlayerQueryFrameWindow(), &pMovieData->rcGrowBox );
    MapWindowPoints( PlayerQueryFrameWindow(),
        hwndMovie, (LPPOINT) &pMovieData->rcGrowBox , 2 );
    MCDoAction( pMovieData->mcMovieController,
        mcActionSetGrowBoxBounds, &pMovieData->rcGrowBox );

    return;

}


// Function: MakeAnArialFont - Creates a logical font
// --------------------------------------------------------------------
// Parameters: HDC           hdc              Device context
//             int           nTextSize        Size of font
//
// Returns:    HFONT         hFont
//           Note: It is up to the caller to eventually delete this font
// --------------------------------------------------------------------
static HFONT NEAR MakeAnArialFont( HDC hdc, int nTextSize )

{
    HFONT       hFont; // Handle to created font
    PLOGFONT    plf;   // -> to font struct

    if( !(plf = (PLOGFONT) LocalAlloc( LPTR, sizeof( LOGFONT ))))
        return NULL;

    plf->lfHeight         = nTextSize;
    plf->lfWeight         = FW_LIGHT;
    plf->lfOutPrecision   = OUT_TT_ONLY_PRECIS;
    plf->lfPitchAndFamily = FF_SWISS;
    LoadString( PlayerQueryResources(), PLAYER_STRING_FACENAME,
        plf->lfFaceName, sizeof( plf->lfFaceName ));

    hFont = CreateFontIndirect( plf );

    LocalFree( (LOCALHANDLE) plf );

    return hFont;
}


// Function: InitializeResize - Tests for constrained resize and
//                              sets dragging rect if true
// --------------------------------------------------------------------
// Parameters: HWND          hwndMovie        Handle of movie wnd
//             NPMOVIEDATA   pMovieData       -> movie data struct
//             WORD          wHitTestCode     NC hit test code
//             POINT         ptCursor         Position of cursor in
//                                            screen coordinates
//
// Returns:    WORD          wHitTestCode if in border, else 0
// --------------------------------------------------------------------
static WORD NEAR InitializeResize
          ( HWND hwndMovie, NPMOVIEDATA pMovieData,
                                WORD wHitTestCode, POINT ptCursor )

{
    RECT        rcClipCursor;  // Rect used to clip motion of cursor
    MINMAXINFO  mmiMinMaxInfo; // Minmax info struct


    GetWindowRect( hwndMovie, &g.rcResizeRect );
    GetWindowRect( PlayerQueryClientWindow(), &rcClipCursor );

    SetMinMaxInfo( hwndMovie, pMovieData, &mmiMinMaxInfo );

    switch( wHitTestCode ) {
        case HTTOP:
            g.ptCursorOffset.x = 0;
            g.ptCursorOffset.y = g.rcResizeRect.top - ptCursor.y;

            rcClipCursor.bottom = g.rcResizeRect.bottom -
                mmiMinMaxInfo.ptMinTrackSize.y;
            break;
        case HTBOTTOM:
            g.ptCursorOffset.x = 0;
            g.ptCursorOffset.y = g.rcResizeRect.bottom - ptCursor.y;

            rcClipCursor.top = g.rcResizeRect.top +
                mmiMinMaxInfo.ptMinTrackSize.y;
            break;
        case HTLEFT:
            g.ptCursorOffset.x = g.rcResizeRect.left - ptCursor.x;
            g.ptCursorOffset.y = 0;

            rcClipCursor.right = g.rcResizeRect.right -
                mmiMinMaxInfo.ptMinTrackSize.x;
            break;
        case HTRIGHT:
            g.ptCursorOffset.x = g.rcResizeRect.right - ptCursor.x;
            g.ptCursorOffset.y = 0;

            rcClipCursor.left = g.rcResizeRect.left +
                mmiMinMaxInfo.ptMinTrackSize.x;
            break;
        case HTTOPLEFT:
            g.ptCursorOffset.x = g.rcResizeRect.left - ptCursor.x;
            g.ptCursorOffset.y = g.rcResizeRect.top - ptCursor.y;

            rcClipCursor.right  = g.rcResizeRect.right -
                mmiMinMaxInfo.ptMinTrackSize.x;
            if( !pMovieData->bSoundOnly )
                rcClipCursor.bottom = g.rcResizeRect.bottom -
                mmiMinMaxInfo.ptMinTrackSize.y;
            break;
        case HTBOTTOMRIGHT:
            g.ptCursorOffset.x = g.rcResizeRect.right - ptCursor.x;
            g.ptCursorOffset.y = g.rcResizeRect.bottom - ptCursor.y;

            rcClipCursor.left = g.rcResizeRect.left +
                mmiMinMaxInfo.ptMinTrackSize.x;
            if( !pMovieData->bSoundOnly )
                rcClipCursor.top  = g.rcResizeRect.top +
                mmiMinMaxInfo.ptMinTrackSize.y;
            break;
        case HTTOPRIGHT:
            g.ptCursorOffset.x = g.rcResizeRect.right - ptCursor.x;
            g.ptCursorOffset.y = g.rcResizeRect.top - ptCursor.y;

            rcClipCursor.left   = g.rcResizeRect.left +
                mmiMinMaxInfo.ptMinTrackSize.x;
            if( !pMovieData->bSoundOnly )
                rcClipCursor.bottom = g.rcResizeRect.bottom -
                mmiMinMaxInfo.ptMinTrackSize.y;
            break;
        case HTBOTTOMLEFT:
            g.ptCursorOffset.x = g.rcResizeRect.left - ptCursor.x;
            g.ptCursorOffset.y = g.rcResizeRect.bottom - ptCursor.y;

            rcClipCursor.right = g.rcResizeRect.right -
                mmiMinMaxInfo.ptMinTrackSize.x;
            if( !pMovieData->bSoundOnly )
                rcClipCursor.top   = g.rcResizeRect.top +
                mmiMinMaxInfo.ptMinTrackSize.y;
            break;

        default:
            return 0;
    }


    if( pMovieData->bSoundOnly ) {
        SetCursor( g.hcursor = LoadCursor( NULL, IDC_SIZEWE ));
    }
    else {
        SetCursor( g.hcursor = SetCursor( NULL ));
    }
    ClipCursor( &rcClipCursor );

    g.wScaleWidth  = max( pMovieData->idMovieInfo.width / 2, 1 );
    g.wScaleHeight = max( pMovieData->idMovieInfo.height / 2, 1 );

    return wHitTestCode;
}


// Function: MoveTheMovieResizeRect - Moves the resizing frame
// --------------------------------------------------------------------
// Parameters: HWND          hwndMovie       Handle of movie window
//             NPMOVIEDATA   pMovieData      -> movie data struct
//             WORD          wHitTestCode    Hit test code
//             POINT         ptCursor        Current cursor position in
//                                           screen coordinates
//             BOOL          bStarting       TRUE if this is the first
//                                           call of a drag
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR MoveTheMovieResizeRect
         ( HWND hwndMovie, NPMOVIEDATA pMovieData,
                WORD wHitTestCode, POINT ptCursor, BOOL bStarting )

{
    RECT     rcNewRect;    // resize rect in screen coordinates
    WORD     wMovieWidth;  // Movie width
    WORD     wMovieHeight; // Movie height

    ptCursor.x += g.ptCursorOffset.x;
    ptCursor.y += g.ptCursorOffset.y;

    rcNewRect = g.rcResizeRect;
    switch( wHitTestCode ) {
        case HTTOP:
            if( !ISKEYDOWN( VK_CONTROL ) && !ISKEYDOWN( VK_SHIFT )) {
                rcNewRect.top = ptCursor.y;
            }
            else {
                wMovieHeight = rcNewRect.bottom - ptCursor.y -
                    g.wTopAndBottom;
                if( ISKEYDOWN( VK_CONTROL )) {
                    wMovieHeight = g.wScaleHeight *
                        max( wMovieHeight / g.wScaleHeight, 1 );

                    if( abs( wMovieHeight -
                        pMovieData->idMovieInfo.height ) <= 2 ) {
                        wMovieWidth  = pMovieData->idMovieInfo.width;
                        wMovieHeight = pMovieData->idMovieInfo.height;
                    }
                    else {
                        wMovieWidth = MulDiv( wMovieHeight,
                            pMovieData->idMovieInfo.width,
                            pMovieData->idMovieInfo.height );
                    }
                }
                else {
                    wMovieWidth = MulDiv( wMovieHeight,
                        pMovieData->idMovieInfo.width,
                        pMovieData->idMovieInfo.height );
                }

                rcNewRect.right = rcNewRect.left + g.wSides + wMovieWidth;
                rcNewRect.top   = rcNewRect.bottom -
                    g.wTopAndBottom - wMovieHeight;
            }
            break;
        case HTBOTTOM:
            if( !ISKEYDOWN( VK_CONTROL ) && !ISKEYDOWN( VK_SHIFT )) {
                rcNewRect.bottom = ptCursor.y;
            }
            else {
                wMovieHeight = ptCursor.y - rcNewRect.top - g.wTopAndBottom;
                if( ISKEYDOWN( VK_CONTROL )) {
                    wMovieHeight = g.wScaleHeight *
                        max( wMovieHeight / g.wScaleHeight, 1 );
                    if( abs( wMovieHeight -
                        pMovieData->idMovieInfo.height ) <= 2 ) {
                        wMovieWidth  = pMovieData->idMovieInfo.width;
                        wMovieHeight = pMovieData->idMovieInfo.height;
                    }
                    else {
                        wMovieWidth = MulDiv( wMovieHeight,
                            pMovieData->idMovieInfo.width,
                            pMovieData->idMovieInfo.height );
                    }
                }
                else {
                    wMovieWidth = MulDiv( wMovieHeight,
                        pMovieData->idMovieInfo.width,
                        pMovieData->idMovieInfo.height );
                }

                rcNewRect.right  = rcNewRect.left + g.wSides + wMovieWidth;
                rcNewRect.bottom = rcNewRect.top +
                    g.wTopAndBottom + wMovieHeight;
            }
            break;
        case HTLEFT:
            if( pMovieData->bSoundOnly ||
                ( !ISKEYDOWN( VK_CONTROL ) && !ISKEYDOWN( VK_SHIFT ))) {
                rcNewRect.left = ptCursor.x;
            }
            else {
                wMovieWidth = rcNewRect.right - ptCursor.x - g.wSides;
                if( ISKEYDOWN( VK_CONTROL )) {
                    wMovieWidth = g.wScaleWidth *
                        max( wMovieWidth / g.wScaleWidth, 1 );
                    if( abs( wMovieWidth -
                        pMovieData->idMovieInfo.width ) <= 2 ) {
                        wMovieWidth  = pMovieData->idMovieInfo.width;
                        wMovieHeight = pMovieData->idMovieInfo.height;
                    }
                    else {
                        wMovieHeight = MulDiv( wMovieWidth,
                            pMovieData->idMovieInfo.height,
                            pMovieData->idMovieInfo.width );
                    }
                }
                else {
                    wMovieHeight = MulDiv( wMovieWidth,
                        pMovieData->idMovieInfo.height,
                        pMovieData->idMovieInfo.width );
                }

                rcNewRect.left   = rcNewRect.right - g.wSides - wMovieWidth;
                rcNewRect.bottom = rcNewRect.top +
                    g.wTopAndBottom + wMovieHeight;
            }
            break;
        case HTRIGHT:
            if( pMovieData->bSoundOnly ||
                ( !ISKEYDOWN( VK_CONTROL ) && !ISKEYDOWN( VK_SHIFT ))) {
                rcNewRect.right = ptCursor.x;
            }
            else {
                wMovieWidth = ptCursor.x - rcNewRect.left - g.wSides;
                if( ISKEYDOWN( VK_CONTROL )) {
                    wMovieWidth = g.wScaleWidth *
                        max( wMovieWidth / g.wScaleWidth, 1 );
                    if( abs( wMovieWidth -
                        pMovieData->idMovieInfo.width ) <= 2 ) {
                        wMovieWidth  = pMovieData->idMovieInfo.width;
                        wMovieHeight = pMovieData->idMovieInfo.height;
                    }
                    else {
                        wMovieHeight = MulDiv( wMovieWidth,
                            pMovieData->idMovieInfo.height,
                            pMovieData->idMovieInfo.width );
                    }
                }
                else {
                    wMovieHeight = MulDiv( wMovieWidth,
                        pMovieData->idMovieInfo.height,
                        pMovieData->idMovieInfo.width );
                }

                rcNewRect.right  = rcNewRect.left + g.wSides + wMovieWidth;
                rcNewRect.bottom = rcNewRect.top +
                    g.wTopAndBottom + wMovieHeight;
            }
            break;
        case HTTOPLEFT:
            if( pMovieData->bSoundOnly ) {
                rcNewRect.left = ptCursor.x;
            }
            else if( !ISKEYDOWN( VK_CONTROL ) && !ISKEYDOWN( VK_SHIFT )) {
                *((LPPOINT) &rcNewRect.left) = ptCursor;
            }
            else {
                wMovieWidth  = rcNewRect.right - ptCursor.x - g.wSides;
                wMovieHeight = rcNewRect.bottom - ptCursor.y -
                    g.wTopAndBottom;
                GetProportionalDimensions( pMovieData,
                    &wMovieWidth, &wMovieHeight, ISKEYDOWN( VK_CONTROL ));
                rcNewRect.top = rcNewRect.bottom - wMovieHeight -
                    g.wTopAndBottom;
                rcNewRect.left = rcNewRect.right - wMovieWidth - g.wSides;
            }
            break;
        case HTBOTTOMRIGHT:
            if( pMovieData->bSoundOnly ) {
                rcNewRect.right = ptCursor.x;
            }
            else if( !ISKEYDOWN( VK_CONTROL ) && !ISKEYDOWN( VK_SHIFT )) {
                *((LPPOINT) &rcNewRect.right) = ptCursor;
            }
            else {
                wMovieWidth  = ptCursor.x - rcNewRect.left - g.wSides;
                wMovieHeight = ptCursor.y - rcNewRect.top - g.wTopAndBottom;
                GetProportionalDimensions( pMovieData,
                    &wMovieWidth, &wMovieHeight, ISKEYDOWN( VK_CONTROL ));
                rcNewRect.bottom = rcNewRect.top + wMovieHeight +
                    g.wTopAndBottom;
                rcNewRect.right = rcNewRect.left + wMovieWidth + g.wSides;
            }
            break;
        case HTTOPRIGHT:
            if( pMovieData->bSoundOnly ) {
                rcNewRect.right = ptCursor.x;
            }
            else if( !ISKEYDOWN( VK_CONTROL ) && !ISKEYDOWN( VK_SHIFT )) {
                rcNewRect.right = ptCursor.x;
                rcNewRect.top   = ptCursor.y;
            }
            else {
                wMovieWidth  = ptCursor.x - rcNewRect.left - g.wSides;
                wMovieHeight = rcNewRect.bottom -
                    ptCursor.y - g.wTopAndBottom;
                GetProportionalDimensions( pMovieData,
                    &wMovieWidth, &wMovieHeight, ISKEYDOWN( VK_CONTROL ));
                rcNewRect.top = rcNewRect.bottom - wMovieHeight -
                    g.wTopAndBottom;
                rcNewRect.right = rcNewRect.left + wMovieWidth + g.wSides;
            }
            break;
        case HTBOTTOMLEFT:
            if( pMovieData->bSoundOnly ) {
                rcNewRect.left = ptCursor.x;
            }
            else if( !ISKEYDOWN( VK_CONTROL ) && !ISKEYDOWN( VK_SHIFT )) {
                rcNewRect.left   = ptCursor.x;
                rcNewRect.bottom = ptCursor.y;
            }
            else {
                wMovieWidth  = rcNewRect.right - ptCursor.x - g.wSides;
                wMovieHeight = ptCursor.y - rcNewRect.top - g.wTopAndBottom;
                GetProportionalDimensions( pMovieData,
                    &wMovieWidth, &wMovieHeight, ISKEYDOWN( VK_CONTROL ));
                rcNewRect.bottom = rcNewRect.top + wMovieHeight +
                    g.wTopAndBottom;
                rcNewRect.left = rcNewRect.right - wMovieWidth - g.wSides;
            }
            break;
    }

    if( bStarting ) {
        g.rcResizeRect = rcNewRect;
        g.bFatResizeBorder = IsNormalSize( &g.rcResizeRect, pMovieData );
        DrawTheFrameRect( &g.rcResizeRect, g.bFatResizeBorder );
    }
    else if( !EqualRect( &g.rcResizeRect, &rcNewRect )) {
        DrawTheFrameRect( &g.rcResizeRect, g.bFatResizeBorder );
        g.rcResizeRect = rcNewRect;
        g.bFatResizeBorder = IsNormalSize( &g.rcResizeRect, pMovieData );
        DrawTheFrameRect( &g.rcResizeRect, g.bFatResizeBorder );
    }

    return;
}


// Function: AdjustForMinProportionalSize - Adjusts the final dimensions of a movie
//                                          when a dimension is less than a minimum
//                                          allowed size
// --------------------------------------------------------------------
// Parameters: HWND             hwndMovie      Handle of movie wnd
//             NPMOVIEDATA      pMovieData     -> movie data struct
//             LPWORD           lpwWidth;      -> window width
//             LPWORD           lpwHeight;     -> window height
//             BOOL             bCtrlKeyDown   TRUE if CONTROL key is down
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR AdjustForMinProportionalSize
          ( HWND hwndMovie, NPMOVIEDATA pMovieData,
                 LPWORD lpwWidth, LPWORD lpwHeight, BOOL bCtrlKeyDown )

{
    MINMAXINFO  mmiMinMaxInfo; // Minmax info struct
    WORD        wMovieWidth;   // Movie width
    WORD        wMovieHeight;  // Movie height


    SetMinMaxInfo( hwndMovie, pMovieData, &mmiMinMaxInfo );
    // Add resizing borders
    mmiMinMaxInfo.ptMinTrackSize.x += g.wSides;
    mmiMinMaxInfo.ptMinTrackSize.y += g.wTBBorder;

    if( ( *lpwWidth > (WORD) mmiMinMaxInfo.ptMinTrackSize.x ) &&
        ( *lpwHeight > (WORD) mmiMinMaxInfo.ptMinTrackSize.y ))
        return;

    if( *lpwWidth < (WORD) mmiMinMaxInfo.ptMinTrackSize.x ) {
        *lpwWidth = mmiMinMaxInfo.ptMinTrackSize.x;
    }
    if( *lpwHeight < (WORD) mmiMinMaxInfo.ptMinTrackSize.y ) {
        *lpwHeight = mmiMinMaxInfo.ptMinTrackSize.y;
    }

    // Mult by 10 because numbers may be small
    wMovieWidth  = ( *lpwWidth - g.wSides ) * 10;
    wMovieHeight = ( *lpwHeight - g.wTopAndBottom ) * 10;
    GetProportionalDimensions
        ( pMovieData, &wMovieWidth, &wMovieHeight, bCtrlKeyDown );
    *lpwWidth  = ( wMovieWidth + 10 * g.wSides ) / 10;
    *lpwHeight = ( wMovieHeight + 10 * g.wTopAndBottom ) / 10;

    return;

}


// Function: GetProportionalDimensions - Scales the dimensions of a movie to
//                                       be in the same proportions as the
//                                       original movie dimensions
// --------------------------------------------------------------------
// Parameters: NPMOVIEDATA    pMovieData        -> movie data struct
//             PWORD          pwMovieWidth      -> current width
//             PWORD          pwMovieHeight     -> current height
//             BOOL           bCtrlKeyDown      TRUE if CONTROL key is down
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR GetProportionalDimensions
      ( NPMOVIEDATA pMovieData, PWORD pwMovieWidth,
                                PWORD pwMovieHeight, BOOL bCtrlKeyDown )

{
    WORD     wTempHeight; // Temp height

    wTempHeight = MulDiv( *pwMovieWidth,
        pMovieData->idMovieInfo.height,
        pMovieData->idMovieInfo.width );
    if( wTempHeight >= *pwMovieHeight ) {
        *pwMovieHeight = wTempHeight;
    }
    else {
        *pwMovieWidth = MulDiv( *pwMovieHeight,
            pMovieData->idMovieInfo.width,
            pMovieData->idMovieInfo.height );
    }

    if( bCtrlKeyDown ) {
        *pwMovieHeight = g.wScaleHeight *
            max( *pwMovieHeight / g.wScaleHeight, 1 );

        if( abs( *pwMovieHeight - pMovieData->idMovieInfo.height ) <= 2 ) {
            *pwMovieWidth  = pMovieData->idMovieInfo.width;
            *pwMovieHeight = pMovieData->idMovieInfo.height;
        }
        else {
            *pwMovieWidth = MulDiv( *pwMovieHeight,
                pMovieData->idMovieInfo.width,
                pMovieData->idMovieInfo.height );
        }
    }

    return;
}


// Function: IsNormalSize - Determines if movie is normal size
// --------------------------------------------------------------------
// Parameters: RECT          lprcResizeRect   -> resize rect
//             NPMOVIEDATA   pMovieData       -> movie data struct
//
// Returns:    BOOL     TRUE if input rect has the normal movie dimensions
// --------------------------------------------------------------------
static BOOL NEAR IsNormalSize
                   ( LPRECT lprcResizeRect, NPMOVIEDATA pMovieData )

{
    WORD    wMovieWidth;  // Current width of movie
    WORD    wMovieHeight; // Current height of movie

    wMovieWidth = lprcResizeRect->right -
        lprcResizeRect->left - g.wSides;
    wMovieHeight = lprcResizeRect->bottom -
        lprcResizeRect->top - g.wTopAndBottom;

    return ( wMovieWidth == pMovieData->idMovieInfo.width ) &&
        ( wMovieHeight == pMovieData->idMovieInfo.height );
}


// Function: DrawTheFrameRect - Draws the resizing frame
// --------------------------------------------------------------------
// Parameters: RECT      lprcResizeRect     -> resize rect
//             BOOL      bFatBorder         Flag to draw thicker border
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR DrawTheFrameRect( LPRECT lprcResizeRect, BOOL bFatBorder )

{
    HDC            hdc;             // DC of desktop
    HBRUSH         hbrushSave;      // Prev brush
    HBITMAP        hbitmapCheckers; // Handle of checkerboard bitmap
    WORD           wWBorder;        // Width of vertical border
    WORD           wHBorder;        // Width of horizontal border


    typedef  BOOL ( CALLBACK* FFRAMEPROC ) (HDC, LPRECT, int, int, DWORD);

    static  WORD   wBorderWidth;             // Width of vertical resize border
    static  WORD   wBorderHeight;            // Height of horizontal resize border
    static  FFRAMEPROC  lpfnFastWindowFrame; // -> FastWindowFrame()
    static  HBRUSH  hbrushCheckers;          // Handle to frame brush

    // FastWindowFrame() is an undocumented Windows function
    // described in "Undocumented Windows" by Andrew Schulman,
    // David Maxey and Matt Pietrek, Addison Wesley, 1992.

    if( lprcResizeRect == NULL ) // Clean up
    {
        if( hbrushCheckers ) {
            DeleteObject( hbrushCheckers );
            hbrushCheckers = NULL;
        }
        return;
    }

    if( !lpfnFastWindowFrame ) {
        lpfnFastWindowFrame = (FFRAMEPROC) GetProcAddress(
            GetModuleHandle( "GDI" ), "FASTWINDOWFRAME" );
        wBorderWidth  = GetSystemMetrics( SM_CXFRAME ) - 1;
        wBorderHeight = GetSystemMetrics( SM_CXFRAME ) - 1;
    }

    if( !hbrushCheckers &&
        ( hbitmapCheckers = LoadBitmap( PlayerQueryInstance(),
        MAKEINTRESOURCE( PLAYER_CHECKERS_BITMAP )))) {
        hbrushCheckers = CreatePatternBrush( hbitmapCheckers );
        DeleteObject( hbitmapCheckers );
    }

    if( hdc = GetDC( NULL )) {
        if( lpfnFastWindowFrame ) {
            if( hbrushCheckers )
                hbrushSave = SelectObject( hdc, hbrushCheckers );
            else
                hbrushSave = SelectObject( hdc,
                GetStockObject( GRAY_BRUSH ));

            wWBorder = wBorderWidth  + ( bFatBorder? 2: 0);
            wHBorder = wBorderHeight + ( bFatBorder? 2: 0);

            if( !( *lpfnFastWindowFrame ) ( hdc, lprcResizeRect,
                wWBorder, wHBorder, PATINVERT )) { // Use PatBlt when FastWindowFrame fails
                ExcludeClipRect( hdc,
                    lprcResizeRect->left + wWBorder,
                    lprcResizeRect->top  + wHBorder,
                    lprcResizeRect->right  - wWBorder,
                    lprcResizeRect->bottom - wHBorder );

                PatBlt( hdc, lprcResizeRect->left,
                    lprcResizeRect->top,
                    lprcResizeRect->right - lprcResizeRect->left,
                    lprcResizeRect->bottom - lprcResizeRect->top,
                    PATINVERT );
            }

            if( hbrushSave )
                SelectObject( hdc, hbrushSave );
        }
        else {
            DrawFocusRect( hdc, lprcResizeRect );
        }

        ReleaseDC( NULL, hdc );
    }

    return;
}


// Function: SubclassTheGrowBox - Subclass the movie controller grow box
// --------------------------------------------------------------------
// Parameters: HWND           hwndMovie    Handle of movie wnd
//             NPMOVIEDATA    pMovieData   -> movie data struct
//
// Returns:    BOOL    TRUE if successful
// --------------------------------------------------------------------
static BOOL NEAR SubclassTheGrowBox
                              ( HWND hwndMovie, NPMOVIEDATA pMovieData )

{
    WNDENUMPROC     lpfnEnumProc; // -> enumeration proc

    if( lpfnEnumProc = (WNDENUMPROC) MakeProcInstance
        ( (FARPROC) MovieChildEnumProc, PlayerQueryInstance())) {
        EnumChildWindows( hwndMovie, lpfnEnumProc,
            MAKELONG( pMovieData, 0 ));
        FreeProcInstance( (FARPROC) lpfnEnumProc );

        return pMovieData->bGrowBoxSubclassed;
    }

    return FALSE;
}


// Function: MovieChildEnumProc - Movie child enumeration proc
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    As required by Microsoft Windows
// --------------------------------------------------------------------
BOOL __export CALLBACK MovieChildEnumProc
                              ( HWND hwndChild, LPARAM lParam )

{
    char      szClassName[50]; // Class name buffer

    // The grow box class name can be found by using the enumeration
    // routine to list all the names
    #define GROWBOXCLASSNAME   "MCGrowBox"

    if( GetClassName( hwndChild,
        szClassName, sizeof( szClassName )) &&
        !lstrcmpi( GROWBOXCLASSNAME, szClassName )) {
        if( g.lpNewGBProc ||
            ( g.lpNewGBProc = (WNDPROC) MakeProcInstance
            ( (FARPROC) GBSubClassProc, PlayerQueryInstance()))) {
            if( g.lpOldGBProc = (WNDPROC) SetWindowLong
                ( hwndChild, GWL_WNDPROC, (LONG) g.lpNewGBProc )) {
                ((NPMOVIEDATA) LOWORD( lParam ))->
                    bGrowBoxSubclassed = TRUE;
            }
        }

        return FALSE;
    }

    return TRUE;
}


// Function: GBSubClassProc - Movie controller grow box subclass
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    As required by Microsoft Windows
// --------------------------------------------------------------------
LONG __export CALLBACK GBSubClassProc
    ( HWND hwndGrowBox, UINT message, WPARAM wParam, LPARAM lParam )

{
    POINT    ptCursor; // Cursor position

    if( g.hwndMaximizedMovie && ( message == WM_LBUTTONDOWN )) {
        ptCursor = MAKEPOINT( lParam );
        ClientToScreen( hwndGrowBox, &ptCursor );
        InitMaxWndGrowBoxResize( hwndGrowBox, ptCursor );

        SetCapture( g.hwndMaximizedMovie );
        g.bCapturedGrowBox = TRUE;

        return 0L;
    }

    return CallWindowProc
        ( g.lpOldGBProc, hwndGrowBox, message, wParam, lParam );
}


// Function: InitMaxWndGrowBoxResize - Initializes the maximized window grow
//                                     box resizing
// --------------------------------------------------------------------
// Parameters: HWND        hwndGrowBox    Handle of grow box
//             POINT       ptCursor       Position of cursor in screen coords.
//
// Returns:    BOOL        TRUE if successful
// --------------------------------------------------------------------
static BOOL NEAR InitMaxWndGrowBoxResize
                              ( HWND hwndGrowBox, POINT ptCursor )

{
    RECT         rcClipCursor; // Clip cursor rect
    HDC          hdc;          // DC of desktop

    GetWindowRect( PlayerQueryFrameWindow(), &g.rcResizeRect );
    g.ptCursorOffset.x = g.rcResizeRect.right  - ptCursor.x;
    g.ptCursorOffset.y = g.rcResizeRect.bottom - ptCursor.y;

    rcClipCursor.left = g.rcResizeRect.left +
        GetSystemMetrics( SM_CXMINTRACK ) - g.ptCursorOffset.x;
    rcClipCursor.top  = g.rcResizeRect.top +
        GetSystemMetrics( SM_CYMINTRACK ) - g.ptCursorOffset.y;

    // Offset the clip cursor rect to keep frame on screen
    if( hdc = GetDC( NULL )) {
        rcClipCursor.right =
            GetDeviceCaps( hdc, HORZRES ) - g.ptCursorOffset.x;
        rcClipCursor.bottom =
            GetDeviceCaps( hdc, VERTRES ) - g.ptCursorOffset.y;
        ReleaseDC( NULL, hdc );
    }
    else {
        rcClipCursor.right  = 0x7fff;
        rcClipCursor.bottom = 0x7fff;
    }
    ClipCursor( &rcClipCursor );

    DrawTheFrameRect( &g.rcResizeRect, FALSE );

    return TRUE;
}


// Function: MoveTheFrameResizeRect - Moves the resizing frame during
//                                    maximized wnd grow box resizing
// --------------------------------------------------------------------
// Parameters: HWND          hwndMovie     Handle of movie wnd
//             POINT         ptCursor      Current cursor position in
//                                         screen coordinates
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR MoveTheFrameResizeRect
                          ( HWND hwndMovie, POINT ptCursor )

{
    RECT     rcNewRect; // resize rect in screen coordinates

    ptCursor.x += g.ptCursorOffset.x;
    ptCursor.y += g.ptCursorOffset.y;

    rcNewRect = g.rcResizeRect;
    *((LPPOINT) &rcNewRect.right) = ptCursor;

    if( !EqualRect( &g.rcResizeRect, &rcNewRect )) {
        DrawTheFrameRect( &g.rcResizeRect, FALSE );
        g.rcResizeRect = rcNewRect;
        DrawTheFrameRect( &g.rcResizeRect, FALSE );
    }

    return;
}


// Function: FixUpMovieTiling - Sets the dimensions of the movie
//                              window when tiling
// --------------------------------------------------------------------
// Parameters: HWND             hwndMovie      Handle of movie wnd
//             NPMOVIEDATA      pMovieData     -> movie data struct
//             LPWINDOWPOS      lpwpWndPos     -> WINDOWPOS struct
//
// Returns:    LONG      Always 0L
// --------------------------------------------------------------------
static LONG NEAR FixUpMovieTiling
     ( HWND hwndMovie, NPMOVIEDATA pMovieData, LPWINDOWPOS lpwpWndPos )

{
    WORD          wMovieWidthMDI;  // Width of tile region
    WORD          wMovieHeightMDI; // Height of tile region
    WORD          wTemp;           // Temp
    MINMAXINFO    mmiMinMaxInfo;   // MinMax info struct


    if( pMovieData->bSoundOnly ) {
        SetMinMaxInfo( hwndMovie, pMovieData, &mmiMinMaxInfo );
        lpwpWndPos->cx =
            max( mmiMinMaxInfo.ptMinTrackSize.x,
            min( (int) g.wSoundOnlyDefWidth, lpwpWndPos->cx ));
        lpwpWndPos->cy = mmiMinMaxInfo.ptMaxTrackSize.y;

        return 0L;
    }

    wMovieWidthMDI  = lpwpWndPos->cx - g.wSides;
    wMovieHeightMDI = lpwpWndPos->cy - g.wTopAndBottom;

    if( ( wMovieWidthMDI >= pMovieData->idMovieInfo.width ) &&
        ( wMovieHeightMDI >= pMovieData->idMovieInfo.height )) {
        lpwpWndPos->cx = pMovieData->idMovieInfo.width + g.wSides;
        lpwpWndPos->cy = pMovieData->idMovieInfo.height +
            g.wTopAndBottom;
    }
    else // Try setting height and calc width
    {
        wTemp = MulDiv( wMovieHeightMDI,
            pMovieData->idMovieInfo.width,
            pMovieData->idMovieInfo.height );
        // If it fits, we are done
        if( wTemp <= wMovieWidthMDI ) {
            lpwpWndPos->cx = wTemp + g.wSides;
        }
        else // Set width and calc height
        {
            wTemp = MulDiv( wMovieWidthMDI,
                pMovieData->idMovieInfo.height,
                pMovieData->idMovieInfo.width );
            lpwpWndPos->cy = wTemp + g.wTopAndBottom;
        }

        // Now check for min allowed proportional size
        AdjustForMinProportionalSize( hwndMovie, pMovieData,
            &lpwpWndPos->cx, &lpwpWndPos->cy, FALSE );
    }

    return 0L;
}


// Function: SetOptionsDefaults - Set option defaults
// --------------------------------------------------------------------
// Parameters: HWND             hwndMovie      Movie hwnd
//             NPMOVIEDATA      pMovieData     -> movie data struct
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR SetOptionsDefaults
                              ( HWND hwndMovie, NPMOVIEDATA pMovieData )

{ // Preset copy struct parameters that do not change
    pMovieData->qtoleOptions.lStructSize     = sizeof( QTOLE_OPTIONSMOVIE );
    pMovieData->qtoleOptions.lVersion        = VERSION_1;
    pMovieData->qtoleOptions.wObjectType     = MOVIE_OBJECT;
    pMovieData->qtoleOptions.hwndObject      = hwndMovie;

    pMovieData->qtoleOptions.mMovie          = pMovieData->mMovie;
    pMovieData->qtoleOptions.bSoundOnlyMovie = pMovieData->bSoundOnly;
    pMovieData->qtoleOptions.lfxRate         = DEFAULT_RATE;

    pMovieData->qtoleOptions.sizeNormal.cx   = pMovieData->idMovieInfo.width;
    pMovieData->qtoleOptions.sizeNormal.cy   = pMovieData->idMovieInfo.height;

    pMovieData->qtoleOptions.tvMovieDuration =
        GetMovieDuration( pMovieData->mMovie );

    // This routine queries qtw.ini for defaults
    PlayerGetDefaultOptions( &pMovieData->qtoleOptions );

    if( pMovieData->qtoleOptions.bSizeHalf ) {
        pMovieData->qtoleOptions.sizeCurrent.cx =
            pMovieData->qtoleOptions.sizeNormal.cx / 2;
        pMovieData->qtoleOptions.sizeCurrent.cy =
            pMovieData->qtoleOptions.sizeNormal.cy / 2;
    }
    else if( pMovieData->qtoleOptions.bSizeDouble ) {
        pMovieData->qtoleOptions.sizeCurrent.cx =
            pMovieData->qtoleOptions.sizeNormal.cx * 2;
        pMovieData->qtoleOptions.sizeCurrent.cy =
            pMovieData->qtoleOptions.sizeNormal.cy * 2;
    }
    else // Set to normal size if not half or double
    {
        pMovieData->qtoleOptions.sizeCurrent =
            pMovieData->qtoleOptions.sizeNormal;
    }

    lstrcpy( pMovieData->qtoleOptions.szCaption, pMovieData->szMovieName );
    lstrcat( pMovieData->qtoleOptions.szCaption, pMovieData->szMovieExt );

    return;
}


// Function: UpdateMovieForOptions - Sets movie options according to values
//                                   set in options dialog
// --------------------------------------------------------------------
// Parameters: HWND          hwndMovie;      Movie window handle
//             NPMOVIEDATA   pMovieData;     Movie data structure
//             BOOL          bCalledByOLE    TRUE if called by ole callback func.
//
// Returns:    None
// --------------------------------------------------------------------
VOID FAR UpdateMovieForOptions
    ( HWND hwndMovie, NPMOVIEDATA pMovieData, BOOL bCalledByOLE )

{
    TimeRecord   trTime;

    if( pMovieData->qtoleOptions.bLoop )
        SendMessage( hwndMovie, WM_COMMAND, PLAYER_MOVIE_LOOP, 0L );
    else if( pMovieData->qtoleOptions.bLoopPalindrome )
        SendMessage( hwndMovie, WM_COMMAND, PLAYER_MOVIE_BACKANDFORTH, 0L );
    else
        SendMessage( hwndMovie, WM_COMMAND, PLAYER_MOVIE_STOPATEND, 0L );

    MCDoAction( pMovieData->mcMovieController,
        mcActionSetPlaySelection,
        (LPVOID) pMovieData->qtoleOptions.bPlaySelectionOnly );

    if( pMovieData->qtoleOptions.bSizeHalf )
        SendMessage( hwndMovie, WM_COMMAND, PLAYER_MOVIE_HALFSIZE, 0L );
    else if( pMovieData->qtoleOptions.bSizeNormal )
        SendMessage( hwndMovie, WM_COMMAND, PLAYER_MOVIE_NORMALSIZE, 0L );
    else if( pMovieData->qtoleOptions.bSizeDouble )
        SendMessage( hwndMovie, WM_COMMAND, PLAYER_MOVIE_DOUBLESIZE, 0L );
    else if( bCalledByOLE )
        ResizeMovieAndWindow( hwndMovie, TRUE, pMovieData,
        pMovieData->qtoleOptions.sizeCurrent.cx,
        pMovieData->qtoleOptions.sizeCurrent.cy );

    if( bCalledByOLE ) {
        trTime.value.dwHi = 0L;
        trTime.value.dwLo = pMovieData->qtoleOptions.tvDisplayFrame;
        trTime.scale = GetMovieTimeScale( pMovieData->mMovie );
        trTime.base  = TIMEBASE_DEFAULT;

        MCDoAction( pMovieData->mcMovieController,
            mcActionGoToTime, (LPVOID) &trTime );

        MCDoAction( pMovieData->mcMovieController,
            mcActionSetSelectionBegin,
            (LPVOID) &pMovieData->qtoleOptions.trSelStart );
        MCDoAction( pMovieData->mcMovieController,
            mcActionSetSelectionDuration,
            (LPVOID) &pMovieData->qtoleOptions.trSelDuration );
    }

    return;
}


// Function: PopulateOptionsStruct - Populates the options structure
// --------------------------------------------------------------------
// Parameters: HWND          hwndMovie      HWND of movie
//             NPMOVIEDATA   pMovieData     Movie data structure
//
// Returns:    None
// --------------------------------------------------------------------
static VOID NEAR PopulateOptionsStruct( HWND hwndMovie, NPMOVIEDATA pMovieData )

{
    BOOL           bLoop;           // Looping flag
    BOOL           bLoopPalindrome; // Loop palindrome flag
    RECT           rcMovie;         // Movie rectangle
    WORD           wMovieWidth;     // Movie width
    WORD           wMovieHeight;    // Movie height
    TimeRecord     trMovieTime;     // Movie time.. not used


    // Update these each time in case they get NULLed somewhere
    pMovieData->qtoleOptions.hwndObject = hwndMovie;
    pMovieData->qtoleOptions.mMovie     = pMovieData->mMovie;

    MCDoAction( pMovieData->mcMovieController,
        mcActionGetLooping, (LPVOID) &bLoop );
    MCDoAction( pMovieData->mcMovieController,
        mcActionGetLoopIsPalindrome, (LPVOID) &bLoopPalindrome );

    pMovieData->qtoleOptions.bLoop           = FALSE;
    pMovieData->qtoleOptions.bLoopPalindrome = FALSE;
    if( bLoop ) {
        if( bLoopPalindrome )
            pMovieData->qtoleOptions.bLoopPalindrome = TRUE;
        else
            pMovieData->qtoleOptions.bLoop = TRUE;
    }

    MCDoAction( pMovieData->mcMovieController,
        mcActionGetPlaySelection,
        (LPVOID) &pMovieData->qtoleOptions.bPlaySelectionOnly );
    if( pMovieData->qtoleOptions.bPlaySelectionOnly ) {
        pMovieData->qtoleOptions.trSelStart = pMovieData->trSelectionStart;
        pMovieData->qtoleOptions.trSelDuration =
            pMovieData->trSelectionDuration;
    }
    else {
        memset( &pMovieData->qtoleOptions.trSelStart,
            0, sizeof( TimeRecord ));
        memset( &pMovieData->qtoleOptions.trSelDuration,
            0, sizeof( TimeRecord ));
    }

    GetMovieBox( pMovieData->mMovie, &rcMovie );
    wMovieWidth  = rcMovie.right  - rcMovie.left;
    wMovieHeight = rcMovie.bottom - rcMovie.top;

    pMovieData->qtoleOptions.sizeCurrent.cx = wMovieWidth;
    pMovieData->qtoleOptions.sizeCurrent.cy = wMovieHeight;

    if( !pMovieData->qtoleOptions.bSoundOnlyMovie ) {
        if( !pMovieData->qtoleOptions.bCopyCurrentFrame ) {
            pMovieData->qtoleOptions.tvDisplayFrame =
                GetMoviePosterTime( pMovieData->mMovie );
        }
        else {
            pMovieData->qtoleOptions.tvDisplayFrame =
                GetMovieTime( pMovieData->mMovie, &trMovieTime );
        }
    }
    else {
        pMovieData->qtoleOptions.tvDisplayFrame = 0;
    }

    return;
}


//  This function is a query function called by other modules

// Function: PlayerQueryActiveMovieName - Query name of active movie
// --------------------------------------------------------------------
// Parameters: LPSTR      lpBuffer       string buffer
//
// Returns:    LPSTR      lpBuffer       Name of active movie
// --------------------------------------------------------------------
LPSTR FAR PlayerQueryActiveMovieName( LPSTR lpBuffer )

{
    HWND          hwndMovie;  // Handle to active window
    NPMOVIEDATA   pMovieData; // ->movie data struct

    hwndMovie = (HWND) SendMessage
        ( PlayerQueryClientWindow(), WM_MDIGETACTIVE, 0, 0L );
    *lpBuffer = 0;
    if( pMovieData = (NPMOVIEDATA) GetWindowWord( hwndMovie, 0 ))
        lstrcpy( lpBuffer, pMovieData->szMovieName );

    return lpBuffer;
}
