//------------------------------------------------------------------------------
// File: app.cpp
//
// Desc: DirectShow sample code
//       - Main source file for VMRPlayer sample
//
// Copyright (c) 1994 - 2002 Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------

#include "project.h"
#include "vmrutil.h"

#include <winuser.h>
#include <initguid.h>

/* -------------------------------------------------------------------------
** Global variables that are initialized at run time and then stay constant.
** -------------------------------------------------------------------------
*/
HINSTANCE           hInst=0;
HICON               hIconVideoCd=0;
HWND                hwndApp=0;
HWND                g_hwndToolbar=0;
HWND                g_hwndStatusbar=0;
HWND                g_hwndTrackbar=0;
CMovie              *pMovie=0;
double              g_TrackBarScale = 1.0;
BOOL                g_bPlay = FALSE;
BOOL                g_fEnableAppImage=0;
BOOL                g_bSecondFileLoaded = FALSE;

int                 dyToolbar=0, dyStatusbar=0, dyTrackbar=0;
int                 FrameStepCount=0;

FLOAT               g_xPos = 0.25F, g_yPos = 0.25F;
FLOAT               g_xSize = 0.5F, g_ySize = 0.5F;
FLOAT               g_Alpha = 0.5F;

typedef struct
{
    FLOAT               xPos,  yPos;
    FLOAT               xSize, ySize;
    FLOAT               Alpha;
} STRM_PARAM;

const STRM_PARAM strParamInit[1] = {
    {0.0F, 0.0F, 1.0F, 1.0F, 1.0F}
};

STRM_PARAM strParam[2] = {
    {0.0F, 0.0F, 1.0F, 1.0F, 1.0F},
    {0.0F, 0.0F, 1.0F, 1.0F, 0.0F}
};


/* -------------------------------------------------------------------------
** True Globals - these may change during execution of the program.
** -------------------------------------------------------------------------
*/
TCHAR               g_achFileName[MAX_PATH];
OPENFILENAME        ofn;
DWORD               g_State = VCD_NO_CD;
LONG                lMovieOrgX, lMovieOrgY;
int                 g_TimeFormat = IDM_TIME;


/* -------------------------------------------------------------------------
** Constants
** -------------------------------------------------------------------------
*/
#define STRM_A  0
#define STRM_B  1

const TCHAR szClassName[] = TEXT("VMR9Player_CLASS\0");
const TCHAR g_szNULL[]    = TEXT("\0");
const TCHAR g_szEmpty[]   = TEXT("");
const TCHAR g_szMovieX[]  = TEXT("MovieOriginX\0");
const TCHAR g_szMovieY[]  = TEXT("MovieOriginY\0");

const int   dxBitmap        = 16;
const int   dyBitmap        = 15;
const int   dxButtonSep     = 8;
const TCHAR g_chNULL        = TEXT('\0');

#ifdef _WIN64
#define RESERVED    0,0,0,0,0,0  /* BYTE bReserved[6]  // padding for alignment */

#else
#define RESERVED    0,0          /* BYTE bReserved[2]  // padding for alignment */
#endif

const TBBUTTON tbButtons[DEFAULT_TBAR_SIZE] = {
    { IDX_SEPARATOR,    1,                    0,               TBSTYLE_SEP           },
    { IDX_1,            IDM_MOVIE_PLAY,       TBSTATE_ENABLED, TBSTYLE_BUTTON, RESERVED, 0, -1 },
    { IDX_2,            IDM_MOVIE_PAUSE,      TBSTATE_ENABLED, TBSTYLE_BUTTON, RESERVED, 0, -1 },
    { IDX_3,            IDM_MOVIE_STOP,       TBSTATE_ENABLED, TBSTYLE_BUTTON, RESERVED, 0, -1 },
    { IDX_SEPARATOR,    2,                    0,               TBSTYLE_SEP           },
    { IDX_4,            IDM_MOVIE_PREVTRACK,  TBSTATE_ENABLED, TBSTYLE_BUTTON, RESERVED, 0, -1 },
    { IDX_5,            IDM_MOVIE_SKIP_BACK,  TBSTATE_ENABLED, TBSTYLE_BUTTON, RESERVED, 0, -1 },
    { IDX_6,            IDM_MOVIE_SKIP_FORE,  TBSTATE_ENABLED, TBSTYLE_BUTTON, RESERVED, 0, -1 },
    { IDX_SEPARATOR,    3,                    0,               TBSTYLE_SEP           },
    { IDX_12,           IDM_MOVIE_STEP,       TBSTATE_ENABLED, TBSTYLE_BUTTON, RESERVED, 0, -1 }
};

const int CX_MOVIE_DEFAULT    = 352;
const int CY_MOVIE_DEFAULT    = 120;



/******************************Public*Routine******************************\
* WinMain
*
*
* Windows recognizes this function by name as the initial entry point
* for the program.  This function calls the application initialization
* routine, if no other instance of the program is running, and always
* calls the instance initialization routine.  It then executes a message
* retrieval and dispatch loop that is the top-level control structure
* for the remainder of execution.  The loop is terminated when a WM_QUIT
* message is received, at which time this function exits the application
* instance by returning the value passed by PostQuitMessage().
*
* If this function must abort before entering the message loop, it
* returns the conventional value FALSE.
*
\**************************************************************************/
int PASCAL
WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLineOld,
    int nCmdShow
    )
{
    USES_CONVERSION;
    LPTSTR lpCmdLine = A2T(lpCmdLineOld);

    HRESULT hres = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if(hres == S_FALSE)
    {
        CoUninitialize();
        return FALSE;
    }

    if(!hPrevInstance)
    {
        if(!InitApplication(hInstance))
        {
            return FALSE;
        }
    }

    /*
    ** Perform initializations that apply to a specific instance
    */
    if(!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    /* Verify that the VMR is present on this system */
    if(!VerifyVMR9())
        return FALSE;

    /* Look for options */
    while(lpCmdLine && (*lpCmdLine == '-' || *lpCmdLine == '/'))
    {
        if ((lpCmdLine[1] == 'P') || (lpCmdLine[1] == 'p'))
        {
            g_bPlay = TRUE;
            lpCmdLine += 2;
        }
        else
        {
            break;
        }

        while(lpCmdLine[0] == ' ')
        {
            lpCmdLine++;
        }
    }

    if(lpCmdLine != NULL && lstrlen(lpCmdLine) > 0)
    {
        ProcessOpen(lpCmdLine, g_bPlay);
        SetPlayButtonsEnableState();
    }

    /*
    ** Acquire and dispatch messages until a WM_QUIT message is received.
    */
    return DoMainLoop();
}


/*****************************Private*Routine******************************\
* DoMainLoop
*
* Process the main message loop
*
\**************************************************************************/
int
DoMainLoop(
    void
    )
{
    MSG       msg;
    HANDLE    ahObjects[1];   // handles that need to be waited on
    const int cObjects = 1;   // no of objects that we are waiting on
    HACCEL    haccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_ACCELERATOR));

    //
    // message loop lasts until we get a WM_QUIT message
    // upon which we shall return from the function
    //

    for(;;)
    {
        if(pMovie != NULL)
        {
            ahObjects[0] = pMovie->GetMovieEventHandle();
        }
        else
        {
            ahObjects[0] = NULL;
        }

        if(ahObjects[0] == NULL)
        {
            WaitMessage();
        }
        else
        {
            //
            // wait for any message sent or posted to this queue
            // or for a graph notification
            //
            DWORD result;

            result = MsgWaitForMultipleObjects(cObjects, ahObjects, FALSE,
                                               INFINITE, QS_ALLINPUT);
            if(result != (WAIT_OBJECT_0 + cObjects))
            {
                if(result == WAIT_OBJECT_0)
                    VideoCd_OnGraphNotify();

                continue;
            }
        }

        //
        // When here, we either have a message or no event handle
        // has been created yet.
        //
        // read all of the messages in this next loop
        // removing each message as we read it
        //

        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if(msg.message == WM_QUIT)
            {
                return (int) msg.wParam;
            }

            if(!TranslateAccelerator(hwndApp, haccel, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }

} // DoMainLoop


/*****************************Private*Routine******************************\
* InitApplication(HANDLE)
*
* This function is called at initialization time only if no other
* instances of the application are running.  This function performs
* initialization tasks that can be done once for any number of running
* instances.
*
* In this case, we initialize a window class by filling out a data
* structure of type WNDCLASS and calling the Windows RegisterClass()
* function.  Since all instances of this application use the same window
* class, we only need to do this when the first instance is initialized.
*
\**************************************************************************/
BOOL
InitApplication(
    HINSTANCE hInstance
    )
{
    WNDCLASS  wc;

    /*
    ** Fill in window class structure with parameters that describe the
    ** main window.
    */
    hIconVideoCd     = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_VIDEOCD_ICON));

    wc.style         = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
    wc.lpfnWndProc   = VideoCdWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = hIconVideoCd;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)NULL; // (COLOR_BTNFACE + 1);
    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MAIN_MENU);
    wc.lpszClassName = szClassName;

    OSVERSIONINFO OSVer;
    OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    BOOL bRet = GetVersionEx((LPOSVERSIONINFO) &OSVer);
    ASSERT(bRet);

    /*
    ** Register the window class and return success/failure code.
    */
    return RegisterClass(&wc);
}


/*****************************Private*Routine******************************\
* InitInstance
*
*
* This function is called at initialization time for every instance of
* this application.  This function performs initialization tasks that
* cannot be shared by multiple instances.
*
* In this case, we save the instance handle in a static variable and
* create and display the main program window.
*
\**************************************************************************/
BOOL
InitInstance(
    HINSTANCE hInstance,
    int nCmdShow
    )
{
    HWND    hwnd;
    RECT    rc;
    POINT   pt;

    /*
    ** Save the instance handle in static variable, which will be used in
    ** many subsequence calls from this application to Windows.
    */
    hInst = hInstance;

    if(! LoadWindowPos(&rc))
        rc.left = rc.top = CW_USEDEFAULT;

    /*
    ** Create a main window for this application instance.
    */
    hwnd = CreateWindow(szClassName, IdStr(STR_APP_TITLE),
                        WS_THICKFRAME | WS_POPUP | WS_CAPTION  |
                        WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
                        WS_CLIPCHILDREN,
                        rc.left, rc.top,
                        rc.right - rc.left, rc.bottom - rc.top,
                        NULL, NULL, hInstance, NULL);

    /*
    ** If window could not be created, return "failure"
    */
    if(NULL == hwnd)
    {
        return FALSE;
    }


    hwndApp = hwnd;
    nRecentFiles = GetRecentFiles(nRecentFiles, 3);

    pt.x = lMovieOrgX =  0;
    pt.y = lMovieOrgY =  0;

    // if we fail to get the working area (screen-tray), then assume
    // the screen is 640x480
    //
    if(!SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, FALSE))
    {
        rc.top    = rc.left = 0;
        rc.right  = 640;
        rc.bottom = 480;
    }

    if(!PtInRect(&rc, pt))
    {
        lMovieOrgX = lMovieOrgY = 0L;
    }

    /*
    ** Make the window visible; update its client area; and return "success"
    */
    SetPlayButtonsEnableState();
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    return TRUE;
}

/*****************************Private*Routine******************************\
* GetMoviePosition
*
* Place the movie in the centre of the client window.  We do not stretch the
* the movie yet !
*
\**************************************************************************/
void
GetMoviePosition(
    HWND hwnd,
    long* xPos,
    long* yPos,
    long* pcx,
    long* pcy
    )
{
    RECT rc;
    GetClientRect(hwnd, &rc);

    rc.top += (dyToolbar + dyTrackbar);
    rc.bottom -= dyStatusbar;

    *xPos = rc.left;
    *yPos = rc.top;
    *pcx = rc.right - rc.left;
    *pcy = rc.bottom - rc.top;
}


/*****************************Private*Routine******************************\
* RepositionMovie
*
\**************************************************************************/
void
RepositionMovie(HWND hwnd)
{
    if(pMovie)
    {
        long xPos, yPos, cx, cy;
        GetMoviePosition(hwnd, &xPos, &yPos, &cx, &cy);
        pMovie->PutMoviePosition(xPos, yPos, cx, cy);

        HDC hdcWin = GetDC(NULL);
        pMovie->RepaintVideo(hwnd, hdcWin);
        ReleaseDC(hwnd, hdcWin);
    }
}

/*****************************Private*Routine******************************\
* VideoCd_OnMove
*
\**************************************************************************/
void
VideoCd_OnMove(
    HWND hwnd,
    int x,
    int y
    )
{
    if(pMovie)
    {
        if(pMovie->GetStateMovie() != State_Running)
        {
            RepositionMovie(hwnd);
        }
        else
        {
            long xPos, yPos, cx, cy;

            // Reposition movie but don't invalidate the rect, since
            // the next video frame will handle the redraw.
            GetMoviePosition(hwnd, &xPos, &yPos, &cx, &cy);
            pMovie->PutMoviePosition(xPos, yPos, cx, cy);
        }
    }
}


/******************************Public*Routine******************************\
* VideoCdWndProc
*
\**************************************************************************/
LRESULT CALLBACK
VideoCdWndProc(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    switch(message)
    {
        HANDLE_MSG(hwnd, WM_CREATE,            VideoCd_OnCreate);
        HANDLE_MSG(hwnd, WM_PAINT,             VideoCd_OnPaint);
        HANDLE_MSG(hwnd, WM_COMMAND,           VideoCd_OnCommand);
        HANDLE_MSG(hwnd, WM_CLOSE,             VideoCd_OnClose);
        HANDLE_MSG(hwnd, WM_QUERYENDSESSION,   VideoCd_OnQueryEndSession);
        HANDLE_MSG(hwnd, WM_DESTROY,           VideoCd_OnDestroy);
        HANDLE_MSG(hwnd, WM_SIZE,              VideoCd_OnSize);
        HANDLE_MSG(hwnd, WM_SYSCOLORCHANGE,    VideoCd_OnSysColorChange);
        HANDLE_MSG(hwnd, WM_MENUSELECT,        VideoCd_OnMenuSelect);
        HANDLE_MSG(hwnd, WM_INITMENUPOPUP,     VideoCd_OnInitMenuPopup);
        HANDLE_MSG(hwnd, WM_HSCROLL,           VideoCd_OnHScroll);
        HANDLE_MSG(hwnd, WM_TIMER,             VideoCd_OnTimer);
        HANDLE_MSG(hwnd, WM_NOTIFY,            VideoCd_OnNotify);
        HANDLE_MSG(hwnd, WM_MOVE,              VideoCd_OnMove);

        case WM_RBUTTONDBLCLK:
        case WM_RBUTTONDOWN:
            VcdPlyerCaptureImage(CAPTURED_IMAGE_NAME);
            break;

        case WM_DISPLAYCHANGE:
            if(pMovie)
                pMovie->DisplayModeChanged();
            break;

        // Note: we do not use HANDLE_MSG here as we want to call
        // DefWindowProc after we have notifed the FilterGraph Resource Manager,
        // otherwise our window will not finish its activation process.

        case WM_ACTIVATE: 
            VideoCd_OnActivate(hwnd, wParam, lParam);

            // IMPORTANT - let this drop through to DefWindowProc

        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return 0L;
}


/*****************************Private*Routine******************************\
* VideoCd_OnCreate
*
\**************************************************************************/
BOOL
VideoCd_OnCreate(
    HWND hwnd,
    LPCREATESTRUCT lpCreateStruct
    )
{
    RECT rc;
    int Pane[2];

    InitCommonControls();

    /*
    ** Create the toolbar and statusbar.
    */
    g_hwndToolbar = CreateToolbarEx(hwnd,
                                    WS_VISIBLE | WS_CHILD |
                                    TBSTYLE_TOOLTIPS | CCS_NODIVIDER,
                                    ID_TOOLBAR, NUMBER_OF_BITMAPS,
                                    hInst, IDR_TOOLBAR, tbButtons,
                                    DEFAULT_TBAR_SIZE, dxBitmap, dyBitmap,
                                    dxBitmap, dyBitmap, sizeof(TBBUTTON));
    if(g_hwndToolbar == NULL)
        return FALSE;

    g_hwndStatusbar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | CCS_BOTTOM,
        TEXT("Example Text"), hwnd, ID_STATUSBAR);

    GetWindowRect(g_hwndToolbar, &rc);
    dyToolbar = rc.bottom - rc.top;

    GetWindowRect(g_hwndStatusbar, &rc);
    dyStatusbar = rc.bottom - rc.top;

    dyTrackbar = 30;

    GetClientRect(hwnd, &rc);
    Pane[0] = (rc.right - rc.left) / 2 ;
    Pane[1] = -1;
    SendMessage(g_hwndStatusbar, SB_SETPARTS, 2, (LPARAM)Pane);


    g_hwndTrackbar = CreateWindowEx(0, TRACKBAR_CLASS, TEXT("Trackbar Control"),
                                    WS_CHILD | WS_VISIBLE |
                                    TBS_AUTOTICKS | TBS_ENABLESELRANGE,
                                    LEFT_MARGIN, dyToolbar - 1,
                                    (rc.right - rc.left) - (2* LEFT_MARGIN),
                                    dyTrackbar, hwnd, (HMENU)ID_TRACKBAR,
                                    hInst, NULL);

    SetDurationLength((REFTIME)0);
    SetCurrentPosition((REFTIME)0);

    SetTimer(hwnd, StatusTimer, 500, NULL);

    if(g_hwndStatusbar == NULL || g_hwndTrackbar == NULL)
    {
        return FALSE;
    }

    // accept filemanager WM_DROPFILES messages
    DragAcceptFiles(hwnd, TRUE);

    return TRUE;
}


/*****************************Private*Routine******************************\
* VideoCd_OnActivate
*
\**************************************************************************/
void
VideoCd_OnActivate(
    HWND hwnd,
    WPARAM wParam,
    LPARAM lParam
    )
{
    if((UINT)LOWORD(wParam))
    {
        // we are being activated - tell the Filter graph (for Sound follows focus)
        if(pMovie)
        {
            pMovie->SetFocus();
        }
    }
}


/*****************************Private*Routine******************************\
* VideoCd_OnHScroll
*
\**************************************************************************/
void
VideoCd_OnHScroll(
    HWND hwnd,
    HWND hwndCtl,
    UINT code,
    int pos
    )
{
    static BOOL fWasPlaying = FALSE;
    static BOOL fBeginScroll = FALSE;

    if(pMovie == NULL)
    {
        return;
    }

    if(hwndCtl == g_hwndTrackbar)
    {
        REFTIME     rtCurrPos;
        REFTIME     rtTrackPos;
        REFTIME     rtDuration;

        pos = (int)SendMessage(g_hwndTrackbar, TBM_GETPOS, 0, 0);
        rtTrackPos = (REFTIME)pos * g_TrackBarScale;

        switch(code)
        {
            case TB_BOTTOM:
                rtDuration = pMovie->GetDuration();
                rtCurrPos = pMovie->GetCurrentPosition();
                VcdPlayerSeekCmd(rtDuration - rtCurrPos);
                SetCurrentPosition(pMovie->GetCurrentPosition());
                break;

            case TB_TOP:
                rtCurrPos = pMovie->GetCurrentPosition();
                VcdPlayerSeekCmd(-rtCurrPos);
                SetCurrentPosition(pMovie->GetCurrentPosition());
                break;

            case TB_LINEDOWN:
                VcdPlayerSeekCmd(10.0);
                SetCurrentPosition(pMovie->GetCurrentPosition());
                break;

            case TB_LINEUP:
                VcdPlayerSeekCmd(-10.0);
                SetCurrentPosition(pMovie->GetCurrentPosition());
                break;

            case TB_ENDTRACK:
                fBeginScroll = FALSE;
                if(fWasPlaying)
                {
                    VcdPlayerPauseCmd();
                    fWasPlaying = FALSE;
                }
                break;

            case TB_THUMBTRACK:
                if(!fBeginScroll)
                {
                    fBeginScroll = TRUE;
                    fWasPlaying = (g_State & VCD_PLAYING);
                    if(fWasPlaying)
                    {
                        VcdPlayerPauseCmd();
                    }
                }
                // Fall through to PAGEUP/PAGEDOWN processing

            case TB_PAGEUP:
            case TB_PAGEDOWN:
                rtCurrPos = pMovie->GetCurrentPosition();
                VcdPlayerSeekCmd(rtTrackPos - rtCurrPos);
                SetCurrentPosition(pMovie->GetCurrentPosition());
                break;
        }
    }
}


/*****************************Private*Routine******************************\
* VideoCd_OnTimer
*
\**************************************************************************/
void
VideoCd_OnTimer(
    HWND hwnd,
    UINT id
    )
{
    if(pMovie && pMovie->StatusMovie() == MOVIE_PLAYING)
    {
        switch(id)
        {
            case StatusTimer:
            {
                REFTIME rt = pMovie->GetCurrentPosition();
                SetCurrentPosition(rt);

                if(1)
                {
                    TCHAR   szFmt[64];
                    TCHAR   sz[64];
                    long cx, cy;

                    pMovie->GetNativeMovieSize(&cx, &cy);
                    wsprintf(sz, TEXT("%s"), FormatRefTime(szFmt, rt));

                    HDC hdc = GetDC(hwndApp);
                    HBITMAP hbmp = CreateCompatibleBitmap(hdc, 128, 128);
                    HBITMAP hbmpVmr = LoadBitmap(hInst, MAKEINTRESOURCE(IDR_VMR));
                    HDC hdcBmp = CreateCompatibleDC(hdc);
                    HDC hdcVMR = CreateCompatibleDC(hdc);
                    ReleaseDC(hwndApp, hdc);

                    HBITMAP hbmpold = (HBITMAP)SelectObject(hdcBmp, hbmp);
                    hbmpVmr = (HBITMAP)SelectObject(hdcVMR, hbmpVmr);
                    BitBlt(hdcBmp, 0, 0, 128, 128, hdcVMR, 0, 0, SRCCOPY);
                    DeleteObject(SelectObject(hdcVMR, hbmpVmr));
                    DeleteDC(hdcVMR);

                    RECT rc;
                    SetRect(&rc, 0, 0, 128, 32);
                    SetBkColor(hdcBmp, RGB(0, 0, 128));
                    SetTextColor(hdcBmp, RGB(255, 255, 255));

                    DrawText(hdcBmp, sz, lstrlen(sz), &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

                    VMR9AlphaBitmap bmpInfo;
                    ZeroMemory(&bmpInfo, sizeof(bmpInfo));
                    bmpInfo.dwFlags = VMRBITMAP_HDC | VMRBITMAP_SRCCOLORKEY;
                    bmpInfo.hdc = hdcBmp;
                    SetRect(&rc, 0, 0, 128, 128);
                    bmpInfo.rSrc = rc;

                    bmpInfo.rDest.left = g_xPos;
                    bmpInfo.rDest.top = g_yPos;
                    bmpInfo.rDest.right = g_xPos + g_xSize;
                    bmpInfo.rDest.bottom = g_yPos + g_ySize;

                    if(g_fEnableAppImage)
                    {
                        bmpInfo.fAlpha = g_Alpha;
                    }
                    else
                    {
                        bmpInfo.fAlpha = 0.0F;
                    }
                    bmpInfo.clrSrcKey = 0;

                    pMovie->SetAppImage(&bmpInfo);

                    DeleteObject(SelectObject(hdcBmp, hbmpold));
                    DeleteDC(hdcBmp);
                }
            }
            break;
        }
    }
}


/*****************************Private*Routine******************************\
* VideoCd_OnPaint
*
\**************************************************************************/
void
VideoCd_OnPaint(
    HWND hwnd
    )
{
    PAINTSTRUCT ps;
    HDC         hdc;
    RECT        rc1;
    RECT        rc2;

    /*
    ** Draw a frame around the movie playback area.
    */
    GetClientRect(hwnd, &rc2);

    hdc = BeginPaint(hwnd, &ps);

    if(pMovie)
    {
        long xPos, yPos, cx, cy;
        GetMoviePosition(hwnd, &xPos, &yPos, &cx, &cy);
        SetRect(&rc1, xPos, yPos, xPos + cx, yPos + cy);

        HRGN rgnClient = CreateRectRgnIndirect(&rc2);
        HRGN rgnVideo  = CreateRectRgnIndirect(&rc1);
        CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF);

        HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE);
        FillRgn(hdc, rgnClient, hbr);
        DeleteObject(hbr);
        DeleteObject(rgnClient);
        DeleteObject(rgnVideo);

        pMovie->RepaintVideo(hwnd, hdc);
    }
    else
    {
        FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1));
    }

    EndPaint(hwnd, &ps);
}


/*****************************Private*Routine******************************\
* VideoCd_OnCommand
*
\**************************************************************************/
void
VideoCd_OnCommand(
    HWND hwnd,
    int id,
    HWND hwndCtl,
    UINT codeNotify
    )
{
    switch(id)
    {
        case IDM_FILE_OPEN:
            VcdPlayerCloseCmd();
            VcdPlayerOpenCmd(STRM_A);
            break;

        case IDM_FILE_OPEN2:
            VcdPlayerOpenCmd(STRM_B);
            break;

        case IDM_FILE_CLOSE:
            VcdPlayerCloseCmd();
            QzFreeUnusedLibraries();
            break;

        case IDM_FILE_EXIT:
            PostMessage(hwnd, WM_CLOSE, 0, 0L);
            break;

        case IDM_MOVIE_PLAY:
            VcdPlayerPlayCmd();
            break;

        case IDM_MOVIE_STOP:
            VcdPlayerStopCmd();
            break;

        case IDM_MOVIE_PAUSE:
            VcdPlayerPauseCmd();
            break;

        case IDM_MOVIE_SKIP_FORE:
            VcdPlayerSeekCmd(1.0);
            break;

        case IDM_MOVIE_SKIP_BACK:
            VcdPlayerSeekCmd(-1.0);
            break;

        case IDM_MOVIE_PREVTRACK:
            if(pMovie)
                VcdPlayerSeekCmd(-pMovie->GetCurrentPosition());
            break;

        case IDM_MOVIE_STEP:
            VcdPlayerStepCmd();
            break;

        case IDM_APP_IMAGE:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_IMAGECTRL), hwnd, (DLGPROC)AppImgDlgProc);
            break;

        case IDM_STREAM_A:
            DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_IMAGECTRL), hwnd,
                          (DLGPROC)TransDlgProc, (LPARAM)STRM_A);
            break;

        case IDM_STREAM_B:
            DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_IMAGECTRL), hwnd,
                          (DLGPROC)TransDlgProc, (LPARAM)STRM_B);
            break;

        case IDM_CAPTURE_IMAGE:
            VcdPlyerCaptureImage(CAPTURED_IMAGE_NAME);
            break;

        case IDM_DISPLAY_CAPTURED_IMAGE:
            VcdPlyerDisplayCapturedImage(CAPTURED_IMAGE_NAME);
            break;

        case IDM_HELP_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX),
                hwnd,  (DLGPROC) AboutDlgProc);
            break;

        default:
            if(id > ID_RECENT_FILE_BASE
                && id <= (ID_RECENT_FILE_BASE + MAX_RECENT_FILES + 1))
            {
                ProcessOpen(aRecentFiles[id - ID_RECENT_FILE_BASE - 1]);
            }
            break;
    }

    SetPlayButtonsEnableState();
}


/******************************Public*Routine******************************\
* VideoCd_OnDestroy
*
\**************************************************************************/
void
VideoCd_OnDestroy(
    HWND hwnd
    )
{
    PostQuitMessage(0);
}


/******************************Public*Routine******************************\
* VideoCd_OnClose
*
\**************************************************************************/
void
VideoCd_OnClose(
    HWND hwnd
    )
{
    // stop accepting dropped filenames
    DragAcceptFiles(hwnd, FALSE);

    VcdPlayerCloseCmd();
    SaveWindowPos(hwnd);
    DestroyWindow(hwnd);
}


/*****************************Private*Routine******************************\
* VideoCd_OnQueryEndSession
*
\**************************************************************************/
BOOL
VideoCd_OnQueryEndSession(
    HWND hwnd
    )
{
    SaveWindowPos(hwnd);
    return TRUE;
}


/******************************Public*Routine******************************\
* VideoCd_OnSize
*
\**************************************************************************/
void
VideoCd_OnSize(
    HWND hwnd,
    UINT state,
    int dx,
    int dy
    )
{
    if(IsWindow(g_hwndStatusbar))
    {
        int Pane[2] = {dx/2-8, -1};

        SendMessage(g_hwndStatusbar, WM_SIZE, 0, 0L);
        SendMessage(g_hwndStatusbar, SB_SETPARTS, 2, (LPARAM)Pane);
    }

    if(IsWindow(g_hwndTrackbar))
    {
        SetWindowPos(g_hwndTrackbar, HWND_TOP, LEFT_MARGIN, dyToolbar - 1,
                     dx - (2 * LEFT_MARGIN), dyTrackbar, SWP_NOZORDER);
    }

    if(IsWindow(g_hwndToolbar))
    {
        SendMessage(g_hwndToolbar, WM_SIZE, 0, 0L);
    }

    RepositionMovie(hwnd);
}


/*****************************Private*Routine******************************\
* VideoCd_OnSysColorChange
*
\**************************************************************************/
void
VideoCd_OnSysColorChange(
    HWND hwnd
    )
{
    FORWARD_WM_SYSCOLORCHANGE(g_hwndToolbar, SendMessage);
    FORWARD_WM_SYSCOLORCHANGE(g_hwndStatusbar, SendMessage);
}


/*****************************Private*Routine******************************\
* VideoCd_OnInitMenuPopup
*
\**************************************************************************/
void
VideoCd_OnInitMenuPopup(
    HWND hwnd,
    HMENU hMenu,
    UINT item,
    BOOL fSystemMenu
    )
{
    UINT uFlags;

    switch(item)
    {
        case 0: // File menu
            if(g_State & (VCD_IN_USE | VCD_NO_CD | VCD_DATA_CD_LOADED))
                uFlags = (MF_BYCOMMAND | MF_GRAYED);
            else
                uFlags = (MF_BYCOMMAND | MF_ENABLED);

            EnableMenuItem(hMenu, IDM_FILE_CLOSE, uFlags);

            // Disable the "Open Second Stream" item if already opened
            if (g_bSecondFileLoaded)
                EnableMenuItem(hMenu, IDM_FILE_OPEN2, MF_BYCOMMAND | MF_GRAYED);
            else
                EnableMenuItem(hMenu, IDM_FILE_OPEN2, uFlags);
            break;

        case 1: // Properties menu
            if(g_State & (VCD_IN_USE | VCD_NO_CD | VCD_DATA_CD_LOADED))
                uFlags = (MF_BYCOMMAND | MF_GRAYED);
            else
                uFlags = (MF_BYCOMMAND | MF_ENABLED);

            EnableMenuItem(hMenu, IDM_APP_IMAGE, uFlags);
            EnableMenuItem(hMenu, IDM_STREAM_A, uFlags);
            EnableMenuItem(hMenu, IDM_CAPTURE_IMAGE, uFlags);
            EnableMenuItem(hMenu, IDM_DISPLAY_CAPTURED_IMAGE, uFlags);

            if (!g_bSecondFileLoaded)
                uFlags = (MF_BYCOMMAND | MF_GRAYED);
            else
                uFlags = (MF_BYCOMMAND | MF_ENABLED);

            EnableMenuItem(hMenu, IDM_STREAM_B, uFlags);
            break;

        case 2: // Help menu
            break;
    }
}


/*****************************Private*Routine******************************\
* VideoCd_OnGraphNotify
*
* This is where we get any notifications from the filter graph.
*
\**************************************************************************/
void
VideoCd_OnGraphNotify(
    void
    )
{
    long lEventCode = pMovie->GetMovieEventCode();

    switch(lEventCode)
    {
        case EC_STEP_COMPLETE:
            g_State &= ~VCD_STEPPING;
            SetPlayButtonsEnableState();
            break;

        case EC_COMPLETE:
        case EC_USERABORT:
        case EC_ERRORABORT:
            VcdPlayerStopCmd();
            SetPlayButtonsEnableState();
            break;

        default:
            break;
    }
}


/*****************************Private*Routine******************************\
* VideoCd_OnNotify
*
* This is where we get the text for the little tooltips
*
\**************************************************************************/
LRESULT
VideoCd_OnNotify(
    HWND hwnd,
    int idFrom,
    NMHDR FAR* pnmhdr
    )
{
    switch(pnmhdr->code)
    {
        case TTN_NEEDTEXT:
        {
            LPTOOLTIPTEXT   lpTt;

            lpTt = (LPTOOLTIPTEXT)pnmhdr;
            LoadString(hInst, (UINT) lpTt->hdr.idFrom, lpTt->szText,
                       NUMELMS(lpTt->szText));
        }
        break;
    }

    return 0;
}


/*****************************Private*Routine******************************\
* VideoCd_OnMenuSelect
*
\**************************************************************************/
void
VideoCd_OnMenuSelect(
    HWND hwnd,
    HMENU hmenu,
    int item,
    HMENU hmenuPopup,
    UINT flags
    )
{
    TCHAR szString[STR_MAX_STRING_LEN + 1];

    /*
    ** Is it time to end the menu help ?
    */
    if((flags == 0xFFFFFFFF) && (hmenu == NULL))
    {
        SendMessage(g_hwndStatusbar, SB_SIMPLE, 0, 0L);
    }

    /*
    ** Do we have a separator, popup or the system menu ?
    */
    else if(flags & MF_POPUP)
    {
        SendMessage(g_hwndStatusbar, SB_SIMPLE, 0, 0L);
    }
    else if(flags & MF_SYSMENU)
    {
        switch(item)
        {
            case SC_RESTORE:
                lstrcpy(szString, IdStr(STR_SYSMENU_RESTORE));
                break;

            case SC_MOVE:
                lstrcpy(szString, IdStr(STR_SYSMENU_MOVE));
                break;

            case SC_MINIMIZE:
                lstrcpy(szString, IdStr(STR_SYSMENU_MINIMIZE));
                break;

            case SC_MAXIMIZE:
                lstrcpy(szString, IdStr(STR_SYSMENU_MAXIMIZE));
                break;

            case SC_TASKLIST:
                lstrcpy(szString, IdStr(STR_SYSMENU_TASK_LIST));
                break;

            case SC_CLOSE:
                lstrcpy(szString, IdStr(STR_SYSMENU_CLOSE));
                break;
        }

        SendMessage(g_hwndStatusbar, SB_SETTEXT, SBT_NOBORDERS|255,
                   (LPARAM)(LPTSTR)szString);
        SendMessage(g_hwndStatusbar, SB_SIMPLE, 1, 0L);
        UpdateWindow(g_hwndStatusbar);
    }

    /*
    ** Hopefully it's one of ours
    */
    else
    {
        if((flags & MF_SEPARATOR))
        {
            szString[0] = g_chNULL;
        }
        else
        {
            lstrcpy(szString, IdStr(item + MENU_STRING_BASE));
        }

        SendMessage(g_hwndStatusbar, SB_SETTEXT, SBT_NOBORDERS|255,
                   (LPARAM)(LPTSTR)szString);
        SendMessage(g_hwndStatusbar, SB_SIMPLE, 1, 0L);
        UpdateWindow(g_hwndStatusbar);
    }
}


/******************************Public*Routine******************************\
* SetPlayButtonsEnableState
*
* Sets the play buttons enable state to match the state of the current
* cdrom device.  See below...
*
*
*                 VCD Player buttons enable state table
* -------------------------------------------------------------------
* ³E=Enabled D=Disabled      ³ Play ³ Pause ³ Eject ³ Stop  ³ Other ³
* -------------------------------------------------------------------
* ³Disk in use               ³  D   ³  D    ³  D    ³   D   ³   D   ³
* -------------------------------------------------------------------
* ³No video cd or data cdrom ³  D   ³  D    ³  E    ³   D   ³   D   ³
* -------------------------------------------------------------------
* ³Video cd (playing)        ³  D   ³  E    ³  E    ³   E   ³   E   ³
* -------------------------------------------------------------------
* ³Video cd (paused)         ³  E   ³  D    ³  E    ³   E   ³   E   ³
* -------------------------------------------------------------------
* ³Video cd (stopped)        ³  E   ³  D    ³  E    ³   D   ³   E   ³
* -------------------------------------------------------------------
*
*
\**************************************************************************/
void
SetPlayButtonsEnableState(
    void
    )
{
    BOOL    fEnable;
    BOOL    fVideoLoaded;

    /*
    ** Do we have a video cd loaded.
    */
    if(g_State & (VCD_NO_CD | VCD_DATA_CD_LOADED | VCD_IN_USE))
    {
        fVideoLoaded = FALSE;
    }
    else
    {
        fVideoLoaded = TRUE;
    }

    /*
    ** Do the play button
    */
    if(fVideoLoaded
        && ((g_State & VCD_STOPPED) || (g_State & VCD_PAUSED)))
    {
        fEnable = TRUE;
    }
    else
    {
        fEnable = FALSE;
    }
    SendMessage(g_hwndToolbar, TB_ENABLEBUTTON, IDM_MOVIE_PLAY, fEnable);

    /*
    ** Do the stop button
    */
    if(fVideoLoaded
        && ((g_State & VCD_PLAYING) || (g_State & VCD_PAUSED)))
    {
        fEnable = TRUE;
    }
    else
    {
        fEnable = FALSE;
    }
    SendMessage(g_hwndToolbar, TB_ENABLEBUTTON, IDM_MOVIE_STOP, fEnable);

    /*
    ** Do the pause button
    */
    if(fVideoLoaded && (g_State & VCD_PLAYING))
    {
        fEnable = TRUE;
    }
    else
    {
        fEnable = FALSE;
    }
    SendMessage(g_hwndToolbar, TB_ENABLEBUTTON, IDM_MOVIE_PAUSE, fEnable);


    fEnable = FALSE;
    if(fVideoLoaded && pMovie->CanMovieFrameStep())
    {
        if(!(g_State & VCD_STEPPING))
        {
            fEnable = TRUE;
        }
    }
    SendMessage(g_hwndToolbar, TB_ENABLEBUTTON, IDM_MOVIE_STEP, fEnable);

    /*
    ** Do the seeking buttons
    */
    if((g_State & VCD_PAUSED) || (!fVideoLoaded))
        fEnable = FALSE;
    else
        fEnable = TRUE;

    SendMessage(g_hwndToolbar, TB_ENABLEBUTTON,
        IDM_MOVIE_SKIP_FORE, fEnable);

    SendMessage(g_hwndToolbar, TB_ENABLEBUTTON,
        IDM_MOVIE_SKIP_BACK, fEnable);

    SendMessage(g_hwndToolbar, TB_ENABLEBUTTON,
        IDM_MOVIE_PREVTRACK, fEnable);
}


/*****************************Private*Routine******************************\
* GetAdjustedClientRect
*
* Calculate the size of the client rect and then adjusts it to take into
* account the space taken by the toolbar and status bar.
*
\**************************************************************************/
void
GetAdjustedClientRect(
    RECT *prc
    )
{
    RECT rcTool;

    GetClientRect(hwndApp, prc);

    GetWindowRect(g_hwndToolbar, &rcTool);
    prc->top += (rcTool.bottom - rcTool.top);

    GetWindowRect(g_hwndTrackbar, &rcTool);
    prc->top += (rcTool.bottom - rcTool.top);

    GetWindowRect(g_hwndStatusbar, &rcTool);
    prc->bottom -= (rcTool.bottom - rcTool.top);
}


/******************************Public*Routine******************************\
* IdStr
*
* Loads the given string resource ID into the passed storage.
*
\**************************************************************************/
LPCTSTR
IdStr(
    int idResource
    )
{
    static TCHAR chBuffer[ STR_MAX_STRING_LEN ];

    if(LoadString(hInst, idResource, chBuffer, STR_MAX_STRING_LEN) == 0)
        return g_szEmpty;

    return chBuffer;
}


/*****************************Private*Routine******************************\
* SetDurationLength
*
* Updates pane 0 on the status bar
*
\**************************************************************************/
void
SetDurationLength(
    REFTIME rt
    )
{
    TCHAR   szFmt[64];
    TCHAR   sz[64];

    g_TrackBarScale = 1.0;
    while(rt / g_TrackBarScale > 30000)
    {
        g_TrackBarScale *= 10;
    }

    SendMessage(g_hwndTrackbar, TBM_SETRANGE, TRUE,
        MAKELONG(0, (WORD)(rt / g_TrackBarScale)));

    SendMessage(g_hwndTrackbar, TBM_SETTICFREQ, (WPARAM)((int)(rt / g_TrackBarScale) / 9), 0);
    SendMessage(g_hwndTrackbar, TBM_SETPAGESIZE, 0, (LPARAM)((int)(rt / g_TrackBarScale) / 9));

    wsprintf(sz, TEXT("Length: %s\0"), FormatRefTime(szFmt, rt));
    SendMessage(g_hwndStatusbar, SB_SETTEXT, 0, (LPARAM)sz);
}


/*****************************Private*Routine******************************\
* SetCurrentPosition
*
* Updates pane 1 on the status bar
*
\**************************************************************************/
void
SetCurrentPosition(
    REFTIME rt
    )
{
    TCHAR   szFmt[64];
    TCHAR   sz[64];

    SendMessage(g_hwndTrackbar, TBM_SETPOS, TRUE, (LPARAM)(rt / g_TrackBarScale));

    wsprintf(sz, TEXT("Elapsed: %s\0"), FormatRefTime(szFmt, rt));
    SendMessage(g_hwndStatusbar, SB_SETTEXT, 1, (LPARAM)sz);
}


/*****************************Private*Routine******************************\
* FormatRefTime
*
* Formats the given RefTime into the passed in character buffer,
* returns a pointer to the character buffer.
*
\**************************************************************************/
TCHAR *
FormatRefTime(
    TCHAR *sz,
    REFTIME rt
    )
{
    // If we are not seeking in time then format differently

    if(pMovie && pMovie->GetTimeFormat() != TIME_FORMAT_MEDIA_TIME)
    {
        wsprintf(sz,TEXT("%s\0"),(LPCTSTR) CDisp((LONGLONG) rt,CDISP_DEC));
        return sz;
    }

    int hrs, mins, secs;

    rt += 0.49;

    hrs  =  (int)rt / 3600;
    mins = ((int)rt % 3600) / 60;
    secs = ((int)rt % 3600) % 60;

    wsprintf(sz, TEXT("%02d:%02d:%02d h:m:s\0"),hrs, mins, secs);
    return sz;
}


/*****************************Private*Routine******************************\
* InitStreamParams
*
\**************************************************************************/
void InitStreamParams(int i)
{
    CopyMemory(&strParam[i], strParamInit, sizeof(strParamInit));

    if(i == STRM_B)
    {
        strParam[STRM_B].Alpha = 0.5F;
    }
}


/*****************************Private*Routine******************************\
* UpdateAppImage
*
\**************************************************************************/
void
UpdateAppImage()
{
    VMR9AlphaBitmap bmpInfo;
    ZeroMemory(&bmpInfo, sizeof(bmpInfo));
    bmpInfo.rDest.left   = g_xPos;
    bmpInfo.rDest.top    = g_yPos;
    bmpInfo.rDest.right  = g_xPos + g_xSize;
    bmpInfo.rDest.bottom = g_yPos + g_ySize;
    bmpInfo.dwFlags      = VMRBITMAP_SRCCOLORKEY;
    bmpInfo.clrSrcKey    = 0;

    if(!g_fEnableAppImage)
    {
        bmpInfo.fAlpha = 0.0F;
    }
    else
    {
        bmpInfo.fAlpha = g_Alpha;
    }

    if (pMovie)
        pMovie->UpdateAppImage(&bmpInfo);
}


/*****************************Private*Routine******************************\
* AppImgDlgProc
*
\**************************************************************************/
BOOL CALLBACK
AppImgDlgProc(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    HWND hwndT;
    int pos;
    TCHAR sz[32];

    switch(uMsg)
    {
        case WM_INITDIALOG:

            hwndT = GetDlgItem(hwnd, IDC_XPOS_TRK);
            pos = int(1000 * g_xPos);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(1000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), g_xPos);
            SetWindowText(hwndT, sz);
            SetDlgItemText(hwnd, IDC_XPOS, sz);

            pos = int(1000 * g_yPos);
            hwndT = GetDlgItem(hwnd, IDC_YPOS_TRK);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(1000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), g_yPos);
            SetWindowText(hwndT, sz);
            SetDlgItemText(hwnd, IDC_YPOS, sz);

            pos = int(1000 * g_xSize);
            hwndT = GetDlgItem(hwnd, IDC_XSIZE_TRK);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(1000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), g_xSize);
            SetWindowText(hwndT, sz);
            SetDlgItemText(hwnd, IDC_XSIZE, sz);

            pos = int(1000 * g_ySize);
            hwndT = GetDlgItem(hwnd, IDC_YSIZE_TRK);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(1000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), g_ySize);
            SetDlgItemText(hwnd, IDC_YSIZE, sz);

            pos = int(1000 * g_Alpha);
            hwndT = GetDlgItem(hwnd, IDC_ALPHA_TRK2);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(1000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), g_Alpha);
            SetDlgItemText(hwnd, IDC_ALPHA, sz);

            Button_SetCheck(GetDlgItem(hwnd, IDC_IMAGE_ENABLE), g_fEnableAppImage);
            return TRUE;

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDOK:
                    EndDialog(hwnd, 1);
                    break;

                case IDC_IMAGE_ENABLE:
                    g_fEnableAppImage =
                    Button_GetCheck(GetDlgItem(hwnd, IDC_IMAGE_ENABLE));
                    UpdateAppImage();
                    break;
            }
            return TRUE;

        case WM_HSCROLL:
            {
                HWND hwndCtrl = (HWND)lParam;

                if(GetDlgItem(hwnd, IDC_ALPHA_TRK2) == hwndCtrl)
                {
                    pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                    g_Alpha = (FLOAT)pos / 1000.0F;
                    UpdateAppImage();
                    _stprintf(sz, TEXT("%.3f"), g_Alpha);
                    SetDlgItemText(hwnd, IDC_ALPHA, sz);
                }
                else if(GetDlgItem(hwnd, IDC_XPOS_TRK) == hwndCtrl)
                {
                        pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                        g_xPos = (FLOAT)pos / 1000.0F;
                        UpdateAppImage();
                        _stprintf(sz, TEXT("%.3f"), g_xPos);
                        SetDlgItemText(hwnd, IDC_XPOS, sz);
                }
                else if(GetDlgItem(hwnd, IDC_YPOS_TRK) == hwndCtrl)
                {
                        pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                        g_yPos = (FLOAT)pos / 1000.0F;
                        UpdateAppImage();
                        _stprintf(sz, TEXT("%.3f"), g_yPos);
                        SetDlgItemText(hwnd, IDC_YPOS, sz);
                }
                else if(GetDlgItem(hwnd, IDC_XSIZE_TRK) == hwndCtrl)
                {
                        pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                        g_xSize = (FLOAT)pos / 1000.0F;
                        UpdateAppImage();
                        _stprintf(sz, TEXT("%.3f"), g_xSize);
                        SetDlgItemText(hwnd, IDC_XSIZE, sz);
                }
                else if(GetDlgItem(hwnd, IDC_YSIZE_TRK) == hwndCtrl)
                {
                        pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                        g_ySize = (FLOAT)pos / 1000.0F;
                        UpdateAppImage();
                        _stprintf(sz, TEXT("%.3f"), g_ySize);
                        SetDlgItemText(hwnd, IDC_YSIZE, sz);
                }
            }
            return TRUE;

        default:
            return FALSE;
    }
}


/*****************************Private*Routine******************************\
* UpdatePinAlpha
*
\**************************************************************************/
void
UpdatePinAlpha(int strmID)
{
    STRM_PARAM* p = &strParam[strmID];

    if(pMovie && pMovie->m_pMixControl)
        pMovie->m_pMixControl->SetAlpha(strmID, p->Alpha);
}


/*****************************Private*Routine******************************\
* UpdatePinPos
*
\**************************************************************************/
void
UpdatePinPos(int strmID)
{
    STRM_PARAM* p = &strParam[strmID];
    VMR9NormalizedRect r = {p->xPos, p->yPos, p->xPos + p->xSize, p->yPos + p->ySize};

    if(pMovie && pMovie->m_pMixControl)
        pMovie->m_pMixControl->SetOutputRect(strmID, &r);
}


/*****************************Private*Routine******************************\
* TransDlgProc
*
\**************************************************************************/
BOOL CALLBACK
TransDlgProc(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    TCHAR sz[32];
    HWND hwndT;
    int pos;

    static int strmID;
    STRM_PARAM* p = &strParam[strmID];

    switch(uMsg)
    {
        case WM_INITDIALOG:

            strmID = (int)lParam;
            p = &strParam[strmID];

            SetWindowText(hwnd, TEXT("Video Transition Properties"));
            ShowWindow(GetDlgItem(hwnd, IDC_IMAGE_ENABLE), SW_HIDE);

            hwndT = GetDlgItem(hwnd, IDC_XPOS_TRK);
            pos = int(10000 * p->xPos) + 10000;
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(20000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), p->xPos);
            SetWindowText(hwndT, sz);
            SetDlgItemText(hwnd, IDC_XPOS, sz);

            pos = int(10000 * p->yPos) + 10000;
            hwndT = GetDlgItem(hwnd, IDC_YPOS_TRK);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(20000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), p->yPos);
            SetWindowText(hwndT, sz);
            SetDlgItemText(hwnd, IDC_YPOS, sz);

            pos = int(10000 * p->xSize) + 10000;
            hwndT = GetDlgItem(hwnd, IDC_XSIZE_TRK);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(20000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), p->xSize);
            SetWindowText(hwndT, sz);
            SetDlgItemText(hwnd, IDC_XSIZE, sz);

            pos = int(10000 * p->ySize) + 10000;
            hwndT = GetDlgItem(hwnd, IDC_YSIZE_TRK);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(20000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), p->ySize);
            SetWindowText(hwndT, sz);
            SetDlgItemText(hwnd, IDC_YSIZE, sz);

            pos = int(10000 * p->Alpha);
            hwndT = GetDlgItem(hwnd, IDC_ALPHA_TRK2);
            SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(10000)));
            SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
            _stprintf(sz, TEXT("%.3f"), p->Alpha);
            SetWindowText(hwndT, sz);
            SetDlgItemText(hwnd, IDC_ALPHA, sz);
            return TRUE;

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDOK:
                    EndDialog(hwnd, 1);
                    break;
            }
            return TRUE;

        case WM_HSCROLL:
            {
                HWND hwndCtrl = (HWND)lParam;

                if(GetDlgItem(hwnd, IDC_ALPHA_TRK2) == hwndCtrl)
                {
                    pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                    p->Alpha = (FLOAT)pos / 10000.0F;
                    UpdatePinAlpha(strmID);
                    _stprintf(sz, TEXT("%.3f"), p->Alpha);
                    SetDlgItemText(hwnd, IDC_ALPHA, sz);
                }
                else if(GetDlgItem(hwnd, IDC_XPOS_TRK) == hwndCtrl)
                {
                        pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                        p->xPos = ((FLOAT)pos - 10000.F) / 10000.0F;
                        UpdatePinPos(strmID);
                        _stprintf(sz, TEXT("%.3f"), p->xPos);
                        SetDlgItemText(hwnd, IDC_XPOS, sz);
                }
                else if(GetDlgItem(hwnd, IDC_YPOS_TRK) == hwndCtrl)
                {
                        pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                        p->yPos = ((FLOAT)pos - 10000.F) / 10000.0F;
                        UpdatePinPos(strmID);
                        _stprintf(sz, TEXT("%.3f"), p->yPos);
                        SetDlgItemText(hwnd, IDC_YPOS, sz);
                }
                else if(GetDlgItem(hwnd, IDC_XSIZE_TRK) == hwndCtrl)
                {
                        pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                        p->xSize = ((FLOAT)pos - 10000.F) / 10000.0F;
                        UpdatePinPos(strmID);
                        _stprintf(sz, TEXT("%.3f"), p->xSize);
                        SetDlgItemText(hwnd, IDC_XSIZE, sz);
                }
                else if(GetDlgItem(hwnd, IDC_YSIZE_TRK) == hwndCtrl)
                {
                        pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
                        p->ySize = ((FLOAT)pos - 10000.F) / 10000.0F;
                        UpdatePinPos(strmID);
                        _stprintf(sz, TEXT("%.3f"), p->ySize);
                        SetDlgItemText(hwnd, IDC_YSIZE, sz);
                }
            }
            return TRUE;

        default:
            return FALSE;
    }
}


LRESULT CALLBACK AboutDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_INITDIALOG:
            return TRUE;

        case WM_COMMAND:
            if(wParam == IDOK)
            {
                EndDialog(hWnd, TRUE);
                return TRUE;
            }
            break;
    }
    return FALSE;
}


