
// ---------------------------------------------------------------------
//
// FrameWnd.c - Picture Viewer - QuickTime for Windows
//
//              Version 1.0
//
//              (c) 1988-1992 Apple Computer, Inc. All Rights Reserved.
//
// ---------------------------------------------------------------------


// Includes
// --------
#define NOMINMAX
#include <Windows.H>  // Required by Windows
#include <commdlg.h>  // Header file for common dlgs
#include <dlgs.h>     // Header file for common dlgs ids
#include <cderr.h>    // Header file for error ids
#include <memory.h>   // Needed for memset() function
#include <shellapi.h> // Drag and drop stuff

#include <qtole.h> // Interface to qtole dll

#include "common.h" // Interface to common.c

#include "viewer.h"  // Interface to other *.c files
#include "viewer.hr" // Defines used in *.rc files
#include "picture.h" // Interface to other *.c files

// Message-Persistent Data
// -----------------------
static struct // Hungarian notation: g
  { HWND      hwndClient;      // MDI client window
    WORD      wNumPictures;    // Number of picture wnds
    BOOL      bUserAbortPrint; // User abort print flag
    HWND      hwndCancelPrt;   // Handle of print cancel dlg
    HBITMAP   hAboutBitmap;    // Temp static storage of bitmap
                               // displayed in about dialogs
  } g;


// Exported callback functions
// ----------------------------
BOOL __export CALLBACK AboutDlgProc       (HWND, UINT, WPARAM, LPARAM);
BOOL __export CALLBACK CloseEnumProc      (HWND, LPARAM);
BOOL __export CALLBACK PaletteEnumProc    (HWND, LPARAM);
BOOL __export CALLBACK PrintCancelDlgProc (HWND, UINT, WPARAM, LPARAM);
int  __export CALLBACK PrintAbortProc     (HDC, int);
UINT __export CALLBACK PrintDlgHookProc   (HWND, UINT, WPARAM, LPARAM);

// Internal Function Declarations
// ------------------------------
static LONG NEAR ViewerFrameCreate       (HWND);
static LONG NEAR ViewerFileCommands      (HWND, WPARAM, WORD);
static LONG NEAR ViewerWindowCommands    (HWND, WPARAM, WORD);
static LONG NEAR ViewerHelpCommands      (HWND, WPARAM, WORD);
static LONG NEAR LaunchPictureWnd        (LPSTR, LPSTR);
static VOID NEAR ViewerEnableMenus       (HWND, BOOL);
static VOID NEAR TellUserCommonDlgError  (DWORD);
static LONG NEAR ProcessDroppedFiles     (HWND, WPARAM );
static VOID NEAR DestroyHelpInstance     (HWND);

// -----------------------------------------------------------------------


// Function: ViewerFrameWndProc - Viewer Frame Window Procedure
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    Via DefFrameProc
// --------------------------------------------------------------------
LONG __export CALLBACK ViewerFrameWndProc
    (HWND hwndFrame, UINT message, WPARAM wParam, LPARAM lParam)

{
    WNDENUMPROC       lpfnEnumPictures; // -> callback funcion for
                                        // enumeration of pictures
    HWND              hwndPicture;      // Temp handle of active picture wnd
    LPQTOLE_OLEDATA   lpOleData;        // -> to OLE data


    switch( message ) {
        case WM_CREATE:
            return ViewerFrameCreate( hwndFrame );

        case WM_PALETTECHANGED:
            if( g.wNumPictures &&
                ( lpfnEnumPictures = (WNDENUMPROC) MakeProcInstance
                ( (FARPROC) PaletteEnumProc, ViewerQueryInstance()))) {
                StoreCurrentSystemPalette( NULL );

                // Tell the picture wnds to repaint themselves
                EnumChildWindows( g.hwndClient, lpfnEnumPictures,
                    (LPARAM) wParam);
                FreeProcInstance( (FARPROC) lpfnEnumPictures );
            }

            return 0L;

        case WM_INITMENUPOPUP:
            // Set check marks and enable menu items in popups
            if( !(BOOL) HIWORD( lParam ) &&
                ( hwndPicture = (HWND) SendMessage
                ( g.hwndClient, WM_MDIGETACTIVE, 0, 0L )) &&
                IsWindow( hwndPicture ))
                SendMessage( hwndPicture,
                WM_VIEWER_INITPOPUPS, wParam, lParam );

            return 0L;

        case WM_COMMAND:
            switch( wParam ) {
                case VIEWER_FILE_OPEN: // file menu popup
                case VIEWER_FILE_CLOSE:
                case VIEWER_FILE_PRTSETUP:
                case VIEWER_FILE_PRINT:
                case VIEWER_FILE_EXIT:
                    return ViewerFileCommands
                        ( hwndFrame, wParam, HIWORD (lParam));

                case VIEWER_WINDOW_TILE: // window menu popup
                case VIEWER_WINDOW_CASCADE:
                case VIEWER_WINDOW_ARRANGE:
                    return ViewerWindowCommands
                        ( hwndFrame, wParam, HIWORD (lParam));


                case VIEWER_HELP_VIEWERHELP: // help menu popup
                case VIEWER_HELP_USINGHELP:
                case VIEWER_HELP_ABOUTVIEWER:
                    return ViewerHelpCommands
                        ( hwndFrame, wParam, HIWORD (lParam));

                default:
                    if( ( hwndPicture = (HWND) SendMessage
                        ( g.hwndClient, WM_MDIGETACTIVE, 0, 0L )) &&
                        IsWindow( hwndPicture ))
                        SendMessage( hwndPicture,
                        WM_COMMAND, wParam, lParam );

                    break; // break to DefFrameProc
            }

            break;

        // WM_USER messages

        case WM_VIEWER_CMDLINE:
            return LaunchPictureWnd( (LPSTR) lParam, NULL );

        case WM_VIEWER_PICTUREDELETED:
            // Decrement picture count. This is incremented in LaunchPictureWnd
            // when picture is created
            if( --g.wNumPictures <= 0 )
                ViewerEnableMenus( hwndFrame, FALSE );
            return 0L;

        // These next messages are posted by the ole callback function in PictUtl.c
        case WM_VIEWER_OLE_OPTIONSDLG:
            ViewerGetOptions( NULL, (LPQTOLE_OPTIONSPICTURE) lParam );
            return 0L;

        case WM_VIEWER_OLE_PLAYOBJECT:
            QTOLE_PlayObject( ViewerQueryOleData(), lParam );
            return 0L;


        // end WM_USER messages


        // Standard drag and drop processing. Allows for multiple pictures but
        // does not worry about position of drop
        case WM_DROPFILES:
            return ProcessDroppedFiles( hwndFrame, wParam );

        case WM_QUERYENDSESSION:
        case WM_CLOSE:
            if( g.wNumPictures && 
                ( lpfnEnumPictures = (WNDENUMPROC) MakeProcInstance
                ( (FARPROC) CloseEnumProc, ViewerQueryInstance()))) { // Give all pictures a chance to stop the close
                EnumChildWindows( g.hwndClient, lpfnEnumPictures, 0L );
                FreeProcInstance( (FARPROC) lpfnEnumPictures );

                // If someone didn't want to close, don't kill the app
                if( NULL != GetWindow( g.hwndClient, GW_CHILD ))
                    return 0L;
            }

            // Tell qtole.dll that we are closing the server
            // Don't close if QTOLE_ClosingServerWnd returns FALSE;
            if( ( lpOleData = ViewerQueryOleData()) && 
                lpOleData->lpqtoleServer &&
                !QTOLE_ClosingServerWnd( lpOleData, message ))
                return 0L;

            break; // break to DefFrameProc


        case WM_NCDESTROY:
            DragAcceptFiles( hwndFrame, FALSE );

            // Destroy help instance
            DestroyHelpInstance( hwndFrame );

            // NULL the global hwnds in viewmain.c
            ViewerNoMoreWindow();

            PostQuitMessage( 0 );
            break;

    }

    return DefFrameProc
        ( hwndFrame, g.hwndClient, message, wParam, lParam );
}


// Function: ViewerFrameCreate - process WM_CREATE message
// --------------------------------------------------------------------
// Parameters: HWND hwndFrame;         Frame window
//
// Returns:    0L if OK, else returns -1L to kill app
// --------------------------------------------------------------------
static LONG NEAR ViewerFrameCreate( HWND hwndFrame )

{
    CLIENTCREATESTRUCT  clientcreate;  // MDI client create struct
    char                szCaption[50]; // caption buffer;


    szCaption[0] = '\0';
    if( LoadString( ViewerQueryResources(), VIEWER_STRING_CAPTION,
        szCaption, sizeof( szCaption )))
        SetWindowText( hwndFrame, szCaption );
    // No point in trying for an error message here since it probably won't
    // load either.

    // disable menu items until a picture is created
    g.wNumPictures = 0;
    ViewerEnableMenus( hwndFrame, FALSE );

    DragAcceptFiles( hwndFrame, TRUE );

    clientcreate.hWindowMenu =
        GetSubMenu( GetMenu( hwndFrame ), MENU_WINDOW_POS );
    clientcreate.idFirstChild = VIEWER_CLIENT_FIRSTCHILD;

    if( !(g.hwndClient = CreateWindow( "MDICLIENT", NULL,
        WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE |
        WS_HSCROLL | WS_VSCROLL ,
        0, 0, 0, 0, hwndFrame, ( HMENU) 1,
        ViewerQueryInstance(), (LPVOID) &clientcreate )))
        return -1L; // return -1 to kill app
    else
        return  0L;
}


// Function: ViewerFileCommands - Process WM_COMMAND, File popup messages
// --------------------------------------------------------------------
// Parameters: HWND   hwndFrame;      Frame window
//             WORD   wIDItem;        Menu or control id
//             WORD   wNotifyCode;    notification message
//
// Returns:    LONG   generally 0L
// --------------------------------------------------------------------
static LONG NEAR ViewerFileCommands
                    (HWND hwndFrame, WPARAM wIDItem, WORD wNotifyCode )

{
    HWND          hwndPicture;                 // Handle of picture window
    OPENFILENAME  ofn;                         // OPENFILENAME struct
    UINT          cbString;                    // Length of filter string
    char          szPicturePath[MAX_PATH_LEN]; // Picture file path.
                                               // Must be at least 256 bytes
    char          szPictureName[MAX_NAME_LEN]; // Picture file name
    char          szFilter[MAX_PATH_LEN];      // Picture file filter str
    LPSTR         lpTemp;                      // Temp -> to string
    LPSTR         lpSave;                      // Temp -> to string
    char          chReplace;                   // String separator used in filter string
    DWORD         dwError;                     // Common dlg error return
    PRINTDLG      pd;                          // Print common dlg struct
    ABORTPROC     lpPrintAbortProc;            // -> to Print abort proc
    DLGPROC       lpPrtCancelProc;             // -> to Print cancel dlg proc
    WORD          wIDError;                    // Resource error string id
    int           nError;                      // Error return

    static DWORD  dwFilterIndex = 1; // Filter index. Saved between 
                                     // invocations of dialog

    typedef UINT ( CALLBACK * PRINTDLGHOOKPROC ) (HWND, UINT, WPARAM, LPARAM );


    switch( wIDItem ) {
        case VIEWER_FILE_OPEN:
            memset( &ofn, 0, sizeof( OPENFILENAME ));
            szPicturePath[0] = '\0';

            if( !(cbString = LoadString( ViewerQueryResources(),
                VIEWER_STRING_OPENPICTFILTER,
                szFilter, sizeof( szFilter ))))
                return 0L;

            // See 3.1 SDK manuals, vol 1 for examples of common dlg processing
            lpTemp = szFilter + lstrlen( szFilter );
            chReplace = *AnsiPrev( szFilter, lpTemp );
            lpTemp = szFilter;
            while( *lpTemp ) {
                if( *lpTemp == chReplace ) { // Need to increment pointer past chReplace before
                                             // setting to NULL. Otherwise AnsiNext thinks it is
                                             // at end of string and quits
                    lpSave = lpTemp;
                    lpTemp = AnsiNext( lpTemp );
                    *lpSave = '\0';
                }
                else
                    lpTemp = AnsiNext( lpTemp );
            }

            ofn.lStructSize    = sizeof( OPENFILENAME );
            ofn.hwndOwner      = hwndFrame;
            ofn.lpstrFilter    = szFilter;
            ofn.nFilterIndex   = dwFilterIndex;
            ofn.lpstrFile      = szPicturePath;
            ofn.nMaxFile       = sizeof( szPicturePath );
            ofn.lpstrFileTitle = szPictureName;
            ofn.nMaxFileTitle  = sizeof( szPictureName );
            ofn.Flags =
                OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;

            if( GetOpenFileName( &ofn )) { // Save the current filter index
                dwFilterIndex = ofn.nFilterIndex;
                LaunchPictureWnd( szPicturePath, szPictureName );
            }
            else if( dwError = CommDlgExtendedError()) { // Tell the user about the error
                TellUserCommonDlgError( dwError );
            }

            return 0L;

        case VIEWER_FILE_CLOSE:
            if( ( hwndPicture = (HWND) SendMessage
                ( g.hwndClient, WM_MDIGETACTIVE, 0, 0L )) &&
                IsWindow( hwndPicture ))
                SendMessage( hwndPicture, WM_CLOSE, 0, 0L );

            return 0L;

        case VIEWER_FILE_PRTSETUP:
            memset( &pd, 0, sizeof( PRINTDLG ));

            pd.lStructSize = sizeof( PRINTDLG );
            pd.hwndOwner   = hwndFrame;
            pd.Flags       = PD_PRINTSETUP;

            if( ( PrintDlg( &pd ) == 0 ) &&
                ( dwError = CommDlgExtendedError())) { // Tell the user about the error
                TellUserCommonDlgError( dwError );
            }

            return 0L;

        case VIEWER_FILE_PRINT:
            // Customize the common dlg to eliminate some options that are 
            // not useful. This requires a new template and a hook proc
            memset( &pd, 0, sizeof( PRINTDLG ));

            pd.lStructSize = sizeof( PRINTDLG );
            pd.hwndOwner   = hwndFrame;
            pd.Flags       = PD_RETURNDC |
                PD_ENABLEPRINTHOOK | PD_ENABLEPRINTTEMPLATE;
            pd.hInstance   = ViewerQueryResources();
            pd.lpPrintTemplateName = MAKEINTRESOURCE( CUSTOM_DLG_COMN_PRINT );
            if( !( pd.lpfnPrintHook =  (PRINTDLGHOOKPROC) 
                MakeProcInstance( (FARPROC) PrintDlgHookProc,
                ViewerQueryInstance()))) {
                CommonTellUser( ViewerQueryResources(), 
                    VIEWER_STRING_NOMEMORY, NULL, MB_OK );
                return 0L;
            }

            if( PrintDlg( &pd ) != 0 ) {
                lpPrtCancelProc = (DLGPROC) MakeProcInstance
                    ( (FARPROC) PrintCancelDlgProc, ViewerQueryInstance() );

                lpPrintAbortProc = (ABORTPROC) MakeProcInstance
                    ( (FARPROC) PrintAbortProc, ViewerQueryInstance() );

                if( !lpPrtCancelProc || !lpPrintAbortProc ) {
                    CommonTellUser( ViewerQueryResources(), 
                        VIEWER_STRING_NOMEMORY, NULL, MB_OK );
                    return 0L;
                }

                g.bUserAbortPrint = FALSE;
                nError   = 0;
                wIDError = 0;

                if( !(g.hwndCancelPrt =
                    CreateDialog( ViewerQueryResources(),
                    MAKEINTRESOURCE( VIEWER_DLG_PRINTCANCEL ),
                    hwndFrame, lpPrtCancelProc ))) {
                    wIDError = VIEWER_STRING_CANCELDLG;
                }
                else {
                    EnableWindow( hwndFrame, FALSE ); // disable frame window

                    if( SetAbortProc( pd.hDC, lpPrintAbortProc ) <= 0 )
                        wIDError = VIEWER_STRING_ABORTPROC;
                    else {
                        if( hwndPicture = (HWND) SendMessage
                            ( g.hwndClient, WM_MDIGETACTIVE, 0, 0L ))
                            nError = (int) SendMessage( hwndPicture,
                            WM_VIEWER_PRINTPICTURE, 0,
                            (LPARAM) (LPPRINTDLG) &pd );
                        if( ( nError < 0 ) &&
                            ( nError & SP_NOTREPORTED ) &&
                            !g.bUserAbortPrint ) {
                            switch( nError ) {
                                case SP_APPABORT:
                                case SP_USERABORT:
                                    break;
                                case SP_OUTOFDISK:
                                    wIDError = VIEWER_STRING_PRT_OUTOFDISK;
                                    break;
                                case SP_OUTOFMEMORY:
                                    wIDError = VIEWER_STRING_PRT_NOMEMORY;
                                    break;
                                case SP_ERROR: // fall through
                                default:
                                    wIDError = VIEWER_STRING_PRT_GENERROR;
                                    break;
                            }
                        }
                    }

                    // reenable frame window
                    EnableWindow( hwndFrame, TRUE ); 

                    if( g.hwndCancelPrt )
                        DestroyWindow( g.hwndCancelPrt );
                }

                if( wIDError )
                    CommonTellUser( ViewerQueryResources(), wIDError,
                    VIEWER_STRING_PRT_CAPTION, MB_OK );

                FreeProcInstance( (FARPROC) lpPrtCancelProc );
                FreeProcInstance( (FARPROC) lpPrintAbortProc );

                if( pd.hDC )
                    DeleteDC( pd.hDC );
                if( pd.hDevMode != NULL )
                    GlobalFree( pd.hDevMode );
                if( pd.hDevNames != NULL )
                    GlobalFree( pd.hDevNames );
            }
            else if( dwError = CommDlgExtendedError()) { // Tell the user about the error
                TellUserCommonDlgError( dwError );
            }

            return 0L;

        case VIEWER_FILE_EXIT:
            SendMessage( hwndFrame, WM_CLOSE, 0, 0L );
            return 0L;
    }

    return 0L; // should never get here

}


// Function: ViewerWindowCommands - Process WM_COMMAND, Window popup messages
// --------------------------------------------------------------------
// Parameters: HWND   hwndFrame;      Frame window
//             WORD   wIDItem;        Menu or control id
//             WORD   wNotifyCode;    notification message
//
// Returns:    LONG   generally 0L
// --------------------------------------------------------------------
static LONG NEAR ViewerWindowCommands
                    (HWND hwndFrame, WPARAM wIDItem, WORD wNotifyCode )

// This is standard MDI stuff. 
{
    switch( wIDItem ) {
        case VIEWER_WINDOW_TILE:
            SendMessage( g.hwndClient, WM_MDITILE, 0, 0L );
            return 0L;

        case VIEWER_WINDOW_CASCADE:
            SendMessage( g.hwndClient, WM_MDICASCADE, 0, 0L );
            return 0L;

        case VIEWER_WINDOW_ARRANGE:
            SendMessage( g.hwndClient, WM_MDIICONARRANGE, 0, 0L );
            return 0L;
    }

    return 0L; // should never get here

}


// Function: ViewerHelpCommands - Process WM_COMMAND, Help popup messages
// --------------------------------------------------------------------
// Parameters: HWND   hwndFrame;      Frame window
//             WORD   wIDItem;        Menu or control id
//             WORD   wNotifyCode;    notification message
//
// Returns:    LONG   generally 0L
// --------------------------------------------------------------------
static LONG NEAR ViewerHelpCommands
                    (HWND hwndFrame, WPARAM wIDItem, WORD wNotifyCode )

{
    char       szHelp[MAX_PATH_LEN]; // Help file path
    DLGPROC    lpDlgProc;            // -> dialog proc

    switch( wIDItem ) {
        case VIEWER_HELP_VIEWERHELP:
            CommonGetLocalizedHelpFile
                ( VIEWER_ROOT_NAME, szHelp, ViewerQueryInstance() );

            if( szHelp[0] )
                WinHelp( hwndFrame, (LPCSTR) szHelp, HELP_CONTENTS, 0L );
            else
                CommonTellUser( ViewerQueryResources(),
                VIEWER_STRING_NOHELPFILE,
                VIEWER_STRING_CAPTION, MB_OK );

            return 0L;

        case VIEWER_HELP_USINGHELP:
            WinHelp( hwndFrame, (LPCSTR) "WINHELP.HLP", HELP_CONTENTS, 0L );
            return 0L;

        case VIEWER_HELP_ABOUTVIEWER:
            if( ( g.hAboutBitmap = LoadBitmap( ViewerQueryResources(),
                MAKEINTRESOURCE( VIEWER_VIEWER_BITMAP ))) &&
                ( lpDlgProc = (DLGPROC) MakeProcInstance
                ( (FARPROC) AboutDlgProc, ViewerQueryInstance()))) {
                DialogBox( ViewerQueryResources(),
                    MAKEINTRESOURCE( VIEWER_DLG_ABOUTVIEWER ),
                    hwndFrame, lpDlgProc );
                FreeProcInstance( (FARPROC) lpDlgProc );
            }
            else
                CommonTellUser( ViewerQueryResources(),
                VIEWER_STRING_NOMEMORY,
                VIEWER_STRING_CAPTION, MB_OK );
            if( g.hAboutBitmap )
                DeleteObject( g.hAboutBitmap );
            g.hAboutBitmap = NULL;

            return 0L;
    }

    return 0L; // should never get here

}


// Function: CloseEnumProc - Close all enumerate proc
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    Always TRUE to enumerate all windows
// --------------------------------------------------------------------
BOOL __export CALLBACK CloseEnumProc( HWND hwnd, LPARAM lParam )

{
    char    szClassName[40]; // Temp buffer for class name

    // Check class name since there are several classes of child windows
    // These include scroll bars etc that appear in picture windows
    if( !GetClassName( hwnd, szClassName, sizeof( szClassName )) ||
        lstrcmpi( szClassName, VIEWER_PICTURE_CLASS ))
        return TRUE;

    // If someone doesn't want to quit, stop enumeration
    if( !SendMessage( hwnd, WM_QUERYENDSESSION, 0, 0L ))
        return FALSE;

    SendMessage( GetParent( hwnd ), WM_MDIDESTROY, (WPARAM) hwnd, 0L );

    return TRUE;
}

// Function: PaletteEnumProc - Tells all child wnds to repaint because
//                             of palette change
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    Always TRUE to enumerate all windows
// --------------------------------------------------------------------
BOOL __export CALLBACK PaletteEnumProc( HWND hwnd, LPARAM lParam )

{
    char    szClassName[40]; // Temp buffer for class name

    // Check class name since there are several classes of child windows
    // These include scroll bars etc that appear in picture windows
    if( !GetClassName( hwnd, szClassName, sizeof( szClassName )) ||
        lstrcmpi( szClassName, VIEWER_PICTURE_CLASS ))
        return TRUE;

    if( hwnd != (HWND) LOWORD( lParam ))
        InvalidateRect( hwnd, NULL, FALSE );

    return TRUE;
}


// Function: LaunchPictureWnd - tell hwndClient to launch a picture wnd
// --------------------------------------------------------------------
// Parameters: LPSTR    lpPicturePath        Path of picture file
//             LPSTR    lpName               File name of picture
//
// Returns:    LONG     generally 0L
// --------------------------------------------------------------------
static LONG NEAR LaunchPictureWnd( LPSTR lpPicturePath, LPSTR lpName )

{
    MDICREATESTRUCT       mdicreate;   // mdi create struct
    HWND                  hwndPicture; // Temp handle of window

    if( !lpPicturePath[0] )
        return -1L;

    if( lpName == NULL ) {
        lpName = lpPicturePath + lstrlen( lpPicturePath );
        lpName = AnsiPrev( lpPicturePath, lpName );
        while( *lpName && (*lpName != '\\') && (lpName != lpPicturePath))
            lpName = AnsiPrev( lpPicturePath, lpName );
        if( *lpName == '\\' )
            lpName = AnsiNext( lpName );
    }

    mdicreate.szClass = VIEWER_PICTURE_CLASS;
    mdicreate.szTitle = lpName;
    mdicreate.hOwner  = ViewerQueryInstance();
    mdicreate.x       = CW_USEDEFAULT;
    mdicreate.y       = CW_USEDEFAULT;
    mdicreate.cx      = CW_USEDEFAULT;
    mdicreate.cy      = CW_USEDEFAULT;
    mdicreate.style   = MDIS_ALLCHILDSTYLES | WS_CLIPCHILDREN;
    mdicreate.lParam  = (LPARAM) lpPicturePath;

    if( !( hwndPicture = (HWND) SendMessage
        ( g.hwndClient, WM_MDICREATE, 0,
        (LPARAM) (LPMDICREATESTRUCT) &mdicreate ))) {
        return -1L;
    }
    else {
        if(++g.wNumPictures == 1 )
            ViewerEnableMenus( ViewerQueryFrameWindow(), TRUE );

        return 0L;
    }
}


// Function: ProcessDroppedFiles - Process the WM_DROPFILES message
// --------------------------------------------------------------------
// Parameters: HWND     hwndFrame         Frame window handle
//             WPARAM   wParam            Message wParam
//
// Returns:    LONG     Always 0L;
// --------------------------------------------------------------------
static LONG NEAR ProcessDroppedFiles( HWND hwndFrame, WPARAM wParam )

{
    int           i;                        // Temp counter
    int           nNumFiles;                // Temp number of dropped files
    UINT          uBytes;                   // Temp len of drop file path
    char          szDropFile[MAX_PATH_LEN]; // Temp drop file path

    if( nNumFiles = DragQueryFile( (HDROP) wParam, 0xffff,
        (LPSTR) NULL, 0 )) { // Create processing requires that frame window has
                             // nonzero dimensions so first restore iconic window
        if( IsIconic( hwndFrame ))
            ShowWindow( hwndFrame, SW_SHOWNORMAL );

        for( i=0; i < nNumFiles; i++ ) {
            uBytes = DragQueryFile( (HDROP) wParam, i,
                (LPSTR) szDropFile, sizeof( szDropFile ));
            if( uBytes > 0 )
                LaunchPictureWnd( szDropFile, NULL );
        }
    }

    DragFinish( (HDROP) wParam );

    return 0L;
}

// Function: DestroyHelpInstance - Tell windows that instance is done with
//                                 help. This is called as a function so that
//                                 szHelp[] is not an automatic var. in the
//                                 winproc
// --------------------------------------------------------------------
// Parameters: HWND     hwndFrame         Frame window handle
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR DestroyHelpInstance( HWND hwndFrame )

{
    char          szHelp[MAX_PATH_LEN]; // Help file name

    WinHelp( hwndFrame, CommonGetLocalizedHelpFile
        ( VIEWER_ROOT_NAME, szHelp, ViewerQueryResources() ),
        HELP_QUIT, NULL );
    return;
}

// Function: AboutDlgProc - About dialog proc
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    As required by Microsoft Windows
// --------------------------------------------------------------------
BOOL __export CALLBACK AboutDlgProc
    ( HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam )

{
    BITMAP       bm;          // Temp bitmap struct
    HWND         hwndCntrl;   // Handle of rect control that is painted
                              // over with bitmap
    HDC          hdestDC;     // hdc of control rect
    HDC          hmemDC;      // Memory dc
    RECT         rcdestRect;  // Rect of control
    HBITMAP      hbitmapSave; // return from select object
    PAINTSTRUCT  ps;          // Paint struct


    switch( msg ) {
        case WM_COMMAND:
            EndDialog( hdlg, 0 );
            return TRUE;

        case WM_PAINT:
            // Don't bother with error messages since only effect is that
            // bitmap will not appear in dialog
            if( !BeginPaint( hdlg, &ps ))
                return FALSE;
            EndPaint( hdlg, &ps );

            if( !g.hAboutBitmap || 
                !( hwndCntrl = GetDlgItem( hdlg, VIEWER_ABOUT_BMPFRAME )))
                return FALSE;

            InvalidateRect( hwndCntrl, NULL, TRUE );
            UpdateWindow( hwndCntrl );

            if( !(hdestDC = GetDC( hwndCntrl )))
                return FALSE;

            if( hmemDC = CreateCompatibleDC( hdestDC )) {
                if( hbitmapSave = SelectObject( hmemDC, g.hAboutBitmap )) {
                    GetObject( g.hAboutBitmap, sizeof( BITMAP ), &bm );
                    GetClientRect( hwndCntrl, &rcdestRect );

                    BitBlt( hdestDC, 
                        ( rcdestRect.right - bm.bmWidth ) / 2,
                        ( rcdestRect.bottom - bm.bmHeight ) / 2, 
                        bm.bmWidth, bm.bmHeight, hmemDC, 0, 0, SRCCOPY );

                    SelectObject( hmemDC, hbitmapSave );
                }

                DeleteDC( hmemDC );
            }

            ReleaseDC( hwndCntrl, hdestDC );

            return FALSE;

        default:
            return FALSE;
    }

    return FALSE;
}


// Function: PrintAbortProc - Print abort proc
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    As required by Microsoft Windows
// --------------------------------------------------------------------
int __export CALLBACK PrintAbortProc( HDC hdc, int nCode )

{
    MSG     msg; // Message struct

    while( !g.bUserAbortPrint &&
        PeekMessage( &msg, NULL, NULL, NULL, PM_REMOVE )) {
        if( !g.hwndCancelPrt || !IsDialogMessage( g.hwndCancelPrt, &msg )) {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }

    return !g.bUserAbortPrint;
}

// Function: PrintCancelDlgProc - User cancel printing dlg proc
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    As required by Microsoft Windows
// --------------------------------------------------------------------
BOOL __export CALLBACK PrintCancelDlgProc
    ( HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam )

{
    char    szName[50]; // Buffer for picture name

    switch( msg ) {
        case WM_INITDIALOG:
            SetFocus( GetDlgItem( hdlg, IDCANCEL ));
            SetDlgItemText( hdlg, PRINT_CANCEL_PICTURENAME,
                ViewerQueryActivePictureName( szName ));
            return TRUE;

        case WM_COMMAND:
            return ( g.bUserAbortPrint = TRUE );

        case WM_DESTROY:
            g.hwndCancelPrt = NULL;
            break;
    }

    return FALSE;
}


// Function: PrintDlgHookProc - Custom print common dlg hook function
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    As required by Microsoft Windows
// --------------------------------------------------------------------
UINT __export CALLBACK PrintDlgHookProc
                (HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam )

{ // The dialog ids are defined in dlgs.h
  // Array of ids of controls to be hidden
    static int nIDs[] = {grp1, rad1, rad2, rad3, stc2, edt1, 
                         stc3, edt2, chx1, chx2, pshHelp };
    UINT   i; // Temp counter

    if( message == WM_INITDIALOG ) {
        for( i=0; i < ( sizeof( nIDs ) / sizeof( nIDs[0] )); i++ )
            ShowWindow( GetDlgItem( hdlg, nIDs[i] ), SW_HIDE );

        return TRUE;
    }
    else
        return FALSE;
}


// Function: ViewerEnableMenus - Disables menu items when there
//                               are no pictures
// --------------------------------------------------------------------
// Parameters: HWND       hwndFrame      Handle of frame window
//             BOOL       bEnable        Enabling flag
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR ViewerEnableMenus( HWND hwndFrame, BOOL bEnable )

{
    UINT    fFlag;      // Temp enabling flag
    HMENU   hmenuFrame; // Handle to main menu of frame window
    UINT    i;          // Index

    static UINT uMenuIDs[] = {VIEWER_FILE_CLOSE,
                              VIEWER_FILE_PRINT,

                              VIEWER_EDIT_COPYPICTURE,
                              VIEWER_EDIT_OPTIONS,
                              VIEWER_EDIT_CANCELSEL,

                              VIEWER_IMAGE_GETINFO,
                              VIEWER_IMAGE_HALFSIZE,
                              VIEWER_IMAGE_NORMALSIZE,
                              VIEWER_IMAGE_DOUBLESIZE,

                              VIEWER_WINDOW_TILE,
                              VIEWER_WINDOW_CASCADE,
                              VIEWER_WINDOW_ARRANGE
                             };

    if( !( hmenuFrame = GetMenu( hwndFrame )))
        return;

    fFlag = (bEnable ? MF_ENABLED : MF_GRAYED ) | MF_BYCOMMAND;
    for( i=0; i < sizeof( uMenuIDs ) / sizeof( uMenuIDs[0] ); i++ )
        EnableMenuItem( hmenuFrame, uMenuIDs[i], fFlag );

    DrawMenuBar( hwndFrame );

    return;
}


// Function: TellUserCommonDlgError - Tell the user about the common dlg
//                                    error
// --------------------------------------------------------------------
// Parameters: DWORD       dwError      error code returned by common dlg
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR TellUserCommonDlgError( DWORD dwError )

{
    WORD          wIDErrorString; // String id

    // Not much here now, can make this as explicit as desired
    // Now returns text of error id, i.e. "CDERR_INITIALIZATION"
    // Not all messages are explicitly included

    switch( dwError ) {
        case CDERR_FINDRESFAILURE: 
            wIDErrorString = VIEWER_STRING_CDLG_FINDRESFAIL;
            break;
        case CDERR_INITIALIZATION:
            wIDErrorString = VIEWER_STRING_CDLG_INITFAIL;
            break;
        case CDERR_LOADRESFAILURE:
            wIDErrorString = VIEWER_STRING_CDLG_LOADRESFAIL;
            break;
        case CDERR_LOCKRESFAILURE:
            wIDErrorString = VIEWER_STRING_CDLG_LOCKRESFAIL;
            break;
        case CDERR_MEMALLOCFAILURE:
            wIDErrorString = VIEWER_STRING_CDLG_MEMALLOCFAIL;
            break;
        case CDERR_MEMLOCKFAILURE:
            wIDErrorString = VIEWER_STRING_CDLG_MEMLOCKFAIL;
            break;
        case CDERR_STRUCTSIZE:
            wIDErrorString = VIEWER_STRING_CDLG_STRUCTSIZE;
            break;
        case FNERR_INVALIDFILENAME:
            wIDErrorString = VIEWER_STRING_CDLG_BADFILENAME;
            break;
        case PDERR_INITFAILURE:
            wIDErrorString = VIEWER_STRING_CDLG_PRTINITFAIL;
            break;
        case PDERR_LOADDRVFAILURE:
            wIDErrorString = VIEWER_STRING_CDLG_LOADDRVFAIL;
            break;
        case PDERR_NODEFAULTPRN:
            wIDErrorString = VIEWER_STRING_CDLG_NODEFPRINTER;
            break;
        case PDERR_NODEVICES:
            wIDErrorString = VIEWER_STRING_CDLG_NODEVICES;
            break;
        case PDERR_PRINTERNOTFOUND:
            wIDErrorString = VIEWER_STRING_CDLG_NOFINDPNTR;
            break;
        case PDERR_SETUPFAILURE:
            wIDErrorString = VIEWER_STRING_CDLG_SETUPFAIL;
            break;
        default:
            wIDErrorString = VIEWER_STRING_CDLG_GENFAILURE;
            break;
    }

    if( wIDErrorString == VIEWER_STRING_CDLG_GENFAILURE )
        CommonTellUser( ViewerQueryResources(),
        VIEWER_STRING_CDLG_GENFAILURE,
        VIEWER_STRING_CDLG_CAP, MB_OK, dwError );
    else
        CommonTellUser( ViewerQueryResources(),
        VIEWER_STRING_CDLG_FORMAT, VIEWER_STRING_CDLG_CAP,
        MB_OK, wIDErrorString );
    return;

}


//  The remaining functions are the query functions called by other modules

// Function: ViewerQueryClientWindow - Query Client Window Handle
// --------------------------------------------------------------------
// Parameters: None.
//
// Returns:    HWND g.hwndClient;        MDI client window handle
// --------------------------------------------------------------------
HWND FAR ViewerQueryClientWindow( VOID )

{
    return g.hwndClient;
}

// Function: ViewerQueryNumPictures - Query number of pictures
// --------------------------------------------------------------------
// Parameters: None.
//
// Returns:    HWND g.wNumPictures;    Number of pictures
// --------------------------------------------------------------------
WORD FAR ViewerQueryNumPictures( VOID )

{
    return g.wNumPictures;
}
