
// ---------------------------------------------------------------------
//
// PictKids.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 <stdlib.h>  // Required for abs function

#include <qtole.h>  // Interface to qtole dll
#include <qtw.h>    // Interface to QuickTime
#include "common.h" // Interface to common.c

#include "viewer.h"  // Interface to *.c files
#include "viewer.hr" // Defines used in *.rc files
#include "picture.h" // Interface to picture window
                     // child window processing


// Constants
// -----------------------
#define BANNER_TEXT_HEIGHT      9 // Banner text height in points
#define TEXT_EXTRA_SPACING      1 // Extra spacing in number text

// Message-Persistent Data
// -----------------------
static struct // Hungarian notation: g
  {WORD        wScrollBarWidth;     // Vertical scroll bar width
   WORD        wScrollBarHeight;    // Horizontal scroll bar height
   WORD        wBannerBarHeight;    // Banner bar height
   WORD        wZoomWndWidth;       // Width of zoom scroll bar window
   WORD        wMinWndHeight;       // Minimum height of window
   HBITMAP     hbmpGrowBox;         // Grow box bitmap
   BOOL        bLimitGrowBoxResize; // Flag to turn on max resize limits
                                    // using grow box
   POINT       ptMaxGrowBoxResize;  // Max size for grow box resizing

   RECT        rcResizeRect;   // Maximized wnd grow box resize rect
   POINT       ptCursorOffset; // Offset of cursor from edge of window
                               // used during maximized wnd grow box resize
  } g;


// Exported callback function
// ----------------------------
LONG __export CALLBACK PictureBannerWndProc  (HWND, UINT, WPARAM, LPARAM);

// Internal Function Declarations
// ------------------------------
static VOID    NEAR UpdateScrollingParms    (HWND, NPPICTUREDATA, LPRECT);
static HBITMAP NEAR GetGrowBoxBitmap        (HDC, NPPICTUREDATA);
static VOID    NEAR DrawTheFrameRect        (LPRECT);


// Function: ResizeKids - Resizes the child windows whenever the picture
//                        window is resized
// --------------------------------------------------------------------
// Parameters: HWND    hwndPicture       Handle of picture window
//             WORD    wClientWidth      Width of client rect
//             WORD    wClientHeight     Height of client rect
//
// Returns:    0L
// --------------------------------------------------------------------
VOID FAR ResizeKids
    ( HWND hwndPicture, WORD wClientWidth, WORD wClientHeight )

{
    NPPICTUREDATA   pPictureData; // -> picture data struct
    RECT            rcPicture;    // Picture rect in picture coordinates
    HDWP            hdwp;         // Handle to defer wnd pos struct
    int             xBrdr;        // Width of non resizable border
    int             yBrdr;        // Height of non resizable border

    // Note: All the extra terms involving xBrdr and yBrdr are used to make
    // the control boundaries match up correctly without heavy lines 
    // appearing between the controls

    xBrdr = GetSystemMetrics( SM_CXBORDER );
    yBrdr = GetSystemMetrics( SM_CYBORDER );

    if( !(pPictureData =
        (NPPICTUREDATA) GetWindowWord( hwndPicture, 0 ))) {
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NOPICDATA, VIEWER_STRING_CAPTION, MB_OK );
        return;
    }

    if( ( hdwp = BeginDeferWindowPos( 5 )) &&
        // Zoom window. This control has a constant size
        ( hdwp = DeferWindowPos( hdwp, pPictureData->zsZoomScroll.hwnd,
        NULL,
        -xBrdr,
        wClientHeight - g.wScrollBarHeight + yBrdr,
        g.wZoomWndWidth,
        g.wScrollBarHeight, 
        SWP_NOZORDER )) &&
        // Banner bar. This control has a constant height
        ( hdwp = DeferWindowPos( hdwp, pPictureData->hwndBanner, 
        NULL,
        0,
        0,
        wClientWidth,
        g.wBannerBarHeight,
        SWP_NOZORDER )) && 
        // Vertical scroll bar. This control has a constant width
        ( hdwp = DeferWindowPos( hdwp, pPictureData->spmsVScroll.hwnd,
        NULL, 
        wClientWidth - g.wScrollBarWidth + xBrdr,
        g.wBannerBarHeight - yBrdr,
        g.wScrollBarWidth,
        wClientHeight - g.wScrollBarHeight
        - g.wBannerBarHeight + 3 * yBrdr,
        SWP_NOZORDER )) && 
        // Horizontal scroll bar. This control has a constant height
        ( hdwp = DeferWindowPos( hdwp, pPictureData->spmsHScroll.hwnd,
        NULL, 
        g.wZoomWndWidth - 2 * xBrdr,
        wClientHeight - g.wScrollBarHeight + yBrdr,
        wClientWidth - g.wZoomWndWidth - 
        g.wScrollBarWidth + 4 * xBrdr,
        g.wScrollBarHeight,
        SWP_NOZORDER ))) {
        EndDeferWindowPos( hdwp );
    } 
    else { // Zoom window. This control has a constant size
        MoveWindow( pPictureData->zsZoomScroll.hwnd,
            -xBrdr, wClientHeight - g.wScrollBarHeight + yBrdr,
            g.wZoomWndWidth, g.wScrollBarHeight, TRUE );

        // Banner bar. This control has a constant height
        MoveWindow( pPictureData->hwndBanner, 0, 0,
            wClientWidth, g.wBannerBarHeight, TRUE );

        // Vertical scroll bar. This control has a constant width
        MoveWindow( pPictureData->spmsVScroll.hwnd,
            wClientWidth - g.wScrollBarWidth + xBrdr,
            g.wBannerBarHeight - yBrdr, g.wScrollBarWidth,
            wClientHeight - g.wScrollBarHeight -
            g.wBannerBarHeight + 3 * yBrdr, TRUE );

        // Horizontal scroll bar. This control has a constant height
        MoveWindow( pPictureData->spmsHScroll.hwnd,
            g.wZoomWndWidth - 2 * xBrdr,
            wClientHeight - g.wScrollBarHeight + yBrdr,
            wClientWidth - g.wZoomWndWidth -
            g.wScrollBarWidth + 4 * xBrdr,
            g.wScrollBarHeight, TRUE );
    }

    pPictureData->rcGrowBox.left = wClientWidth  - 
        g.wScrollBarWidth + 2 * xBrdr;
    pPictureData->rcGrowBox.top  = wClientHeight -
        g.wScrollBarHeight + 2 * yBrdr;
    pPictureData->rcGrowBox.right  = wClientWidth;
    pPictureData->rcGrowBox.bottom = wClientHeight;

    rcPicture.left   = rcPicture.top = 0;
    rcPicture.right  = wClientWidth;
    rcPicture.bottom = wClientHeight;
    PictureRectFromClient( &rcPicture );

    UpdateScrollingParms( hwndPicture, pPictureData, &rcPicture );

    // Force these to get painted now to avoid long delay while
    // waiting for the picture to be painted. This improves the 
    // appearance of the window during resizing
    UpdateWindow( pPictureData->zsZoomScroll.hwnd );
    UpdateWindow( pPictureData->hwndBanner );
    UpdateWindow( pPictureData->spmsHScroll.hwnd );
    UpdateWindow( pPictureData->spmsVScroll.hwnd );

    return;
}


// Function: CreateViewerKids - Creates the child windows for the picture
//                              window
// --------------------------------------------------------------------
// Parameters: HWND           hwndPicture     Handle of picture window
//             NPPICTUREDATA  pPictureData    -> to picture data struct
//
// Returns:    LONG           0l if OK
// --------------------------------------------------------------------
LONG FAR CreateViewerKids( HWND hwndPicture, NPPICTUREDATA pPictureData )

{
    WORD    wIDError; // Resource error string id

    g.wScrollBarWidth  = (WORD) GetSystemMetrics( SM_CXVSCROLL );
    g.wScrollBarHeight = (WORD) GetSystemMetrics( SM_CYHSCROLL );
    g.wBannerBarHeight = (WORD) GetSystemMetrics( SM_CYMENU );
    g.wMinWndHeight    = (WORD) ( GetSystemMetrics( SM_CYCAPTION ) +
        g.wBannerBarHeight + g.wScrollBarHeight + 
        3 * GetSystemMetrics( SM_CYVSCROLL ));


    wIDError = VIEWER_STRING_NOMEMORY;
    if( !( pPictureData->spmsHScroll.hwnd =
        CreateWindow( "scrollbar", NULL,
        WS_CHILD | WS_VISIBLE | SBS_HORZ,
        0, 0, 0, 0, hwndPicture,
        (HMENU) PICTURE_HORZ_SCROLL,
        ViewerQueryInstance(), NULL ))) {
        goto Failed;
    }

    if( !(pPictureData->spmsVScroll.hwnd =
        CreateWindow( "scrollbar", NULL,
        WS_CHILD | WS_VISIBLE | SBS_VERT,
        0, 0, 0, 0, hwndPicture,
        (HMENU) PICTURE_VERT_SCROLL,
        ViewerQueryInstance(), NULL ))) {
        goto Failed;
    }

    // Try initial width. This may be adjusted during create processing
    if( !( pPictureData->zsZoomScroll.hwnd =
        CreateWindow( PICTURE_ZOOM_CLASS, NULL,
        WS_CHILD | WS_VISIBLE | WS_BORDER,
        0, 0, 6 * GetSystemMetrics( SM_CXHSCROLL ),
        g.wScrollBarHeight, hwndPicture,
        (HMENU) PICTURE_ZOOM_SCROLL,
        ViewerQueryInstance(), NULL ))) {
        goto Failed;
    }
    else // Set the global variable here because dimensions may have
         // been adjusted. Use the query because GetClientRect() won't 
         // work until after first call to MoveWindow() and we need 
         // width before then.
    {
        g.wZoomWndWidth = ViewerQueryZoomWndWidth();
    }

    if( !( pPictureData->hwndBanner =
        CreateWindow( PICTURE_BANNER_CLASS, NULL,
        WS_CHILD | WS_VISIBLE,
        0, 0, 0, 0, hwndPicture,
        (HMENU) PICTURE_BANNER,
        ViewerQueryInstance(), NULL ))) {
        goto Failed;
    }
    // this window does nothing so disable it
    EnableWindow( pPictureData->hwndBanner, FALSE );

    return 0L;

 Failed:
    CommonTellUser( ViewerQueryResources(), wIDError, NULL, MB_OK );
    return MAKELONG( wIDError, 0 );
}


// Function: ZoomPicture - Resizes the Picture to the input width and
//                         height
// --------------------------------------------------------------------
// Parameters: HWND         hwndPicture     Handle of Picture window
//             WORD         wZoomIndex      Index of desired zoom
//
// Returns:    LONG         0L if successful
// --------------------------------------------------------------------
LONG FAR ZoomPicture( HWND hwndPicture, WORD wZoomIndex )

{
    NPPICTUREDATA  pPictureData; // -> picture data struct
    RECT           rcMaxPicture; // Current max picture display area
    WORD           wZoomMult;    // Zoom multiplier factor


    if( !(pPictureData =
        (NPPICTUREDATA) GetWindowWord( hwndPicture, 0 ))) {
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NOPICDATA, VIEWER_STRING_CAPTION, MB_OK );
        return VIEWER_STRING_NOPICDATA;
    }

    if( pPictureData->zsZoomScroll.wCurZoomIndex == wZoomIndex )
        return 0L;

    pPictureData->rcCurPictureRect.left = 0;
    pPictureData->rcCurPictureRect.top  = 0;

    wZoomMult = ViewerQueryZoomMultiplier( wZoomIndex );
    pPictureData->rcCurPictureRect.right = 
        MulDiv( pPictureData->idImageInfo.width, wZoomMult, 100 );
    pPictureData->rcCurPictureRect.bottom = 
        MulDiv( pPictureData->idImageInfo.height, wZoomMult, 100 );

    // Get client rect
    GetClientRect( hwndPicture, &rcMaxPicture );

    // Convert to Picture coordinates
    PictureRectFromClient( &rcMaxPicture );

    // This routine also offsets rcCurPictureRect when necessary
    UpdateScrollingParms( hwndPicture, pPictureData, &rcMaxPicture );

    // Update buckle ...
    pPictureData->zsZoomScroll.wCurZoomIndex = wZoomIndex;
    if( IsWindowVisible( pPictureData->zsZoomScroll.hwnd ))
        SendMessage( pPictureData->zsZoomScroll.hwnd,
        WM_ZOOM_MOVEBUCKLE, (WPARAM) wZoomIndex, 0L );

    // ... and paint
    InvalidateRect( hwndPicture, NULL, TRUE);

    if( pPictureData->hwndGetInfo )
        SendMessage( pPictureData->hwndGetInfo,
        WM_INFO_CURRENTSIZE, 0, 0L );

    return 0L;
}


// Function: ClientRectFromPicture - Returns the client rect in client
//                                   coordinates that corresponds to the
//                                   input picture rect in picture coords.
// --------------------------------------------------------------------
// Parameters: LPRECT       lprc     -> Picture rect on input
//
// Returns:    VOID         lprc     -> Client rect on output
// --------------------------------------------------------------------
VOID FAR ClientRectFromPicture( LPRECT lprc )

{
    lprc->right  -= lprc->left;
    lprc->bottom -= lprc->top;
    lprc->left = lprc->top = 0;

    lprc->right  += g.wScrollBarWidth - 
        GetSystemMetrics( SM_CXBORDER );
    lprc->bottom += g.wScrollBarHeight + g.wBannerBarHeight -
        GetSystemMetrics( SM_CYBORDER );

    return;
}

// Function: PictureRectFromClient - Returns the maximum picture rect
//                                   in picture coordinates corresponding
//                                   to the input client rect in
//                                   client coords
// --------------------------------------------------------------------
// Parameters: LPRECT       lprc   -> Client rect on input
//
// Returns:    VOID         lprc   -> Max Picture rect on output
// --------------------------------------------------------------------
VOID FAR PictureRectFromClient( LPRECT lprc )

{
    lprc->right  -= lprc->left;
    lprc->bottom -= lprc->top;

    lprc->left = lprc->top = 0;
    lprc->right  -= g.wScrollBarWidth - 
        GetSystemMetrics( SM_CXBORDER );
    lprc->bottom -= g.wScrollBarHeight + g.wBannerBarHeight -
        GetSystemMetrics( SM_CYBORDER );

    return;
}

// Function: ClientToPicture - Converts from client to picture coordinates
// --------------------------------------------------------------------
// Parameters: LPPOINT       lppt     -> points
//             WORD          wNum     Number of points to convert
//
// Returns:    VOID
// --------------------------------------------------------------------
VOID FAR ClientToPicture( LPPOINT lppt, WORD wNum )

{
    WORD    i; // Counter

    for(i=0; i < wNum; i++, lppt++)
        lppt->y -= g.wBannerBarHeight;

    return;
}

// Function: PictureToClient - Converts from picture to client coordinates
// --------------------------------------------------------------------
// Parameters: LPPOINT       lppt     -> points
//             WORD          wNum     number of points to convert
//
// Returns:    VOID
// --------------------------------------------------------------------
VOID FAR PictureToClient( LPPOINT lppt, WORD wNum )

{
    WORD    i; // Counter

    for(i=0; i < wNum; i++, lppt++)
        lppt->y += g.wBannerBarHeight;

    return;
}

// Function: SetMinMaxInfo - Processes the picture window
//                           WM_GETMINMAXINFO message
// --------------------------------------------------------------------
// Parameters: MINMAXINFO FAR*    lpmmi   -> to minmaxinfo struct
//
// Returns:    LONG               always 0L
// --------------------------------------------------------------------
LONG FAR SetMinMaxInfo( MINMAXINFO FAR* lpmmi )

{
    lpmmi->ptMinTrackSize.x = g.wZoomWndWidth +
        5 * GetSystemMetrics( SM_CXHSCROLL );
    lpmmi->ptMinTrackSize.y = g.wMinWndHeight;

    if( g.bLimitGrowBoxResize ) {
        lpmmi->ptMaxTrackSize = g.ptMaxGrowBoxResize;
    }

    return 0L;
}


// Function: UpdateScrollingParms - Update the scroll bar parameters after
//                                  size of picture is changed
// --------------------------------------------------------------------
// Parameters: HWND           hwndPicture      Handle of picture window
//             NPPICTUREDATA  pPictureData     -> picture data struct
//             LPRECT         lprcMaxPicture   -> to to picture display
//                                             area of wnd
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR UpdateScrollingParms( HWND hwndPicture,
                    NPPICTUREDATA pPictureData, LPRECT lprcMaxPicture )

{
    WORD      wCurPictWidth;  // Current picture width
    WORD      wCurPictHeight; // Current picture height
    WORD      wOldMax;        // Previous max scroll value
    WORD      wDisplayWidth;  // Display width, i.e. area available
                              // for display of picture
    WORD      wDisplayHeight; // Display height


    wCurPictWidth  = pPictureData->rcCurPictureRect.right -
        pPictureData->rcCurPictureRect.left;
    wCurPictHeight = pPictureData->rcCurPictureRect.bottom -
        pPictureData->rcCurPictureRect.top;
    wDisplayWidth  = lprcMaxPicture->right - lprcMaxPicture->left;
    wDisplayHeight = lprcMaxPicture->bottom - lprcMaxPicture->top;

    // leave these fixed
    pPictureData->spmsHScroll.wCurLine = g.wScrollBarWidth;
    pPictureData->spmsVScroll.wCurLine = g.wScrollBarHeight;

    // Adjust these for size of window
    pPictureData->spmsHScroll.wCurPage = wDisplayWidth - 1;
    pPictureData->spmsVScroll.wCurPage = wDisplayHeight - 1;

    // Set horizontal scrollbar parms
    if( wDisplayWidth >= wCurPictWidth ) {
        pPictureData->spmsHScroll.wCurPos  = 0;
        pPictureData->spmsHScroll.wCurMax  = 0;
        EnableWindow( pPictureData->spmsHScroll.hwnd, FALSE );
    }
    else {
        wOldMax = pPictureData->spmsHScroll.wCurMax;
        pPictureData->spmsHScroll.wCurMax  =
            wCurPictWidth - wDisplayWidth;
        // Scale current position for new display area
        if( wOldMax > 0 )
            pPictureData->spmsHScroll.wCurPos =
            MulDiv( pPictureData->spmsHScroll.wCurPos,
            pPictureData->spmsHScroll.wCurMax, wOldMax );
        else
            pPictureData->spmsHScroll.wCurPos = 0;

        EnableWindow( pPictureData->spmsHScroll.hwnd, TRUE );

        SetScrollRange( pPictureData->spmsHScroll.hwnd, SB_CTL,
            0, pPictureData->spmsHScroll.wCurMax, FALSE );
        SetScrollPos( pPictureData->spmsHScroll.hwnd, SB_CTL,
            pPictureData->spmsHScroll.wCurPos, TRUE );
    }

    // Set vertical scrollbar parms
    if( wDisplayHeight >= wCurPictHeight ) {
        pPictureData->spmsVScroll.wCurPos  = 0;
        pPictureData->spmsVScroll.wCurMax  = 0;
        EnableWindow( pPictureData->spmsVScroll.hwnd, FALSE );
    }
    else {
        wOldMax = pPictureData->spmsVScroll.wCurMax;
        pPictureData->spmsVScroll.wCurMax =
            wCurPictHeight - wDisplayHeight;
        // Scale current position for new display area
        if( wOldMax > 0 )
            pPictureData->spmsVScroll.wCurPos =
            MulDiv( pPictureData->spmsVScroll.wCurPos,
            pPictureData->spmsVScroll.wCurMax, wOldMax );
        else
            pPictureData->spmsVScroll.wCurPos = 0;

        EnableWindow( pPictureData->spmsVScroll.hwnd, TRUE );

        SetScrollRange( pPictureData->spmsVScroll.hwnd, SB_CTL,
            0, pPictureData->spmsVScroll.wCurMax, FALSE );
        SetScrollPos( pPictureData->spmsVScroll.hwnd, SB_CTL,
            pPictureData->spmsVScroll.wCurPos, TRUE );
    }


    // Define ->rcCurPictureRect.   The point (0, 0) is always the Upper-Left
    // corner of the display area. Therefore, rcCur.. .left and .top are
    // negative if the picture is scrolled.
    pPictureData->rcCurPictureRect.left =
        - ((int) pPictureData->spmsHScroll.wCurPos);
    pPictureData->rcCurPictureRect.top  =
        - ((int) pPictureData->spmsVScroll.wCurPos);
    pPictureData->rcCurPictureRect.right =
        pPictureData->rcCurPictureRect.left + wCurPictWidth;
    pPictureData->rcCurPictureRect.bottom =
        pPictureData->rcCurPictureRect.top  + wCurPictHeight;

    return;
}


// Function: ProcessHorzScroll - Process horizontal scroll bar messages
// --------------------------------------------------------------------
// Parameters: HWND    hwndPicture       Handle of picture window
//             WORD    wScrollCode       Scroll code as defined in windows.h
//             int     nCurBoxPos        Current position of scroll box
//
// Returns:    LONG    0L if OK
// --------------------------------------------------------------------
LONG FAR ProcessHorzScroll
    (HWND hwndPicture, WORD wScrollCode, int nCurBoxPos )

{
    NPPICTUREDATA   pPictureData; // -> picture data struct
    int             nPosition;    // Temp scroll position
    WORD            wWidth;       // Picture width
    int             nToScroll;    // Amount to scroll
    RECT            rcPictArea;   // Picture rect in client coordinates


    if( !(pPictureData =
        (NPPICTUREDATA) GetWindowWord( hwndPicture, 0 ))) {
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NOPICDATA, VIEWER_STRING_CAPTION, MB_OK );
        return VIEWER_STRING_NOPICDATA;
    }

    if( !IsWindowEnabled( pPictureData->spmsHScroll.hwnd ))
        return 0L;

    nPosition = pPictureData->spmsHScroll.wCurPos;

    switch( wScrollCode ) {
        case SB_PAGERIGHT:
            nPosition += pPictureData->spmsHScroll.wCurPage;
            break;
        case SB_LINERIGHT:
            nPosition += pPictureData->spmsHScroll.wCurLine;
            break;
        case SB_PAGELEFT:
            nPosition -= pPictureData->spmsHScroll.wCurPage;
            break;
        case SB_LINELEFT:
            nPosition -= pPictureData->spmsHScroll.wCurLine;
            break;
        case SB_LEFT:
            nPosition = 0;
            break;
        case SB_RIGHT:
            nPosition = pPictureData->spmsHScroll.wCurMax;
            break;
        case SB_THUMBPOSITION:
            nPosition = (WORD) nCurBoxPos;
            break;
        default:
            break;
    }

    nPosition = min( max( 0, nPosition ),
        (int) pPictureData->spmsHScroll.wCurMax );
    if( nPosition != (int) pPictureData->spmsHScroll.wCurPos ) {
        nToScroll = nPosition - pPictureData->spmsHScroll.wCurPos;
        pPictureData->spmsHScroll.wCurPos = nPosition;

        wWidth = pPictureData->rcCurPictureRect.right -
            pPictureData->rcCurPictureRect.left;
        pPictureData->rcCurPictureRect.left  = -nPosition;
        pPictureData->rcCurPictureRect.right = -nPosition + wWidth;

        SetScrollPos( pPictureData->spmsHScroll.hwnd, SB_CTL,
            pPictureData->spmsHScroll.wCurPos, TRUE );

        // Get picture rect in picture coordinates
        GetClientRect( hwndPicture, &rcPictArea );
        PictureRectFromClient( &rcPictArea);
        // Convert to client coordinates for ScrollWindow
        PictureToClient( (LPPOINT) &rcPictArea, 2 );

        if( ((WORD) abs( nToScroll )) <
            pPictureData->spmsHScroll.wCurPage ) {
            UpdateWindow( hwndPicture );
            ScrollWindow( hwndPicture, -nToScroll, 0,
                &rcPictArea, &rcPictArea );
        }
        else
            InvalidateRect( hwndPicture, &rcPictArea, TRUE );

        UpdateWindow( hwndPicture );
    }

    return 0L;
}


// Function: ProcessVertScroll - Process vertical scroll bar messages
// --------------------------------------------------------------------
// Parameters: HWND    hwndPicture       Handle of picture window
//             WORD    wScrollCode       Scroll code as defined in windows.h
//             int     nCurBoxPos        Current position of scroll box
//
// Returns:    LONG    0L if OK
// --------------------------------------------------------------------
LONG FAR ProcessVertScroll
    (HWND hwndPicture, WORD wScrollCode, int nCurBoxPos )

{
    NPPICTUREDATA   pPictureData; // -> picture data struct
    int             nPosition;    // Temp scroll position
    WORD            wHeight;      // Picture height
    int             nToScroll;    // Amount to scroll
    RECT            rcPictArea;   // Picture rect in client coordinates

    if( !(pPictureData =
        (NPPICTUREDATA) GetWindowWord( hwndPicture, 0 ))) {
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NOPICDATA, VIEWER_STRING_CAPTION, MB_OK );
        return VIEWER_STRING_NOPICDATA;
    }

    if( !IsWindowEnabled( pPictureData->spmsVScroll.hwnd ))
        return 0L;

    nPosition = pPictureData->spmsVScroll.wCurPos;

    switch( wScrollCode ) {
        case SB_PAGEDOWN:
            nPosition += pPictureData->spmsVScroll.wCurPage;
            break;
        case SB_LINEDOWN:
            nPosition += pPictureData->spmsVScroll.wCurLine;
            break;
        case SB_PAGEUP:
            nPosition -= pPictureData->spmsVScroll.wCurPage;
            break;
        case SB_LINEUP:
            nPosition -= pPictureData->spmsVScroll.wCurLine;
            break;
        case SB_TOP:
            nPosition = 0;
            break;
        case SB_BOTTOM:
            nPosition = pPictureData->spmsVScroll.wCurMax;
            break;
        case SB_THUMBPOSITION:
            nPosition = nCurBoxPos;
            break;
        default:
            break;
    }

    nPosition = min( max( 0, nPosition ),
        (int) pPictureData->spmsVScroll.wCurMax );
    if( nPosition != (int) pPictureData->spmsVScroll.wCurPos ) {
        nToScroll = nPosition - pPictureData->spmsVScroll.wCurPos;
        pPictureData->spmsVScroll.wCurPos = nPosition;

        wHeight = pPictureData->rcCurPictureRect.bottom -
            pPictureData->rcCurPictureRect.top;
        pPictureData->rcCurPictureRect.top    = -nPosition;
        pPictureData->rcCurPictureRect.bottom = -nPosition + wHeight;

        SetScrollPos( pPictureData->spmsVScroll.hwnd, SB_CTL,
            pPictureData->spmsVScroll.wCurPos, TRUE );

        // Get picture rect in picture coordinates
        GetClientRect( hwndPicture, &rcPictArea );
        PictureRectFromClient( &rcPictArea);
        // Convert to client coordinates for ScrollWindow
        PictureToClient( (LPPOINT) &rcPictArea, 2 );

        if( ((WORD) abs( nToScroll )) <
            pPictureData->spmsVScroll.wCurPage ) {
            UpdateWindow( hwndPicture );
            ScrollWindow( hwndPicture, 0, -nToScroll,
                &rcPictArea, &rcPictArea );
        }
        else
            InvalidateRect( hwndPicture, &rcPictArea, TRUE );

        UpdateWindow( hwndPicture );
    }

    return 0L;
}

// Function: ScrollToCorner - Scroll to upper_left or lower-right corner
// --------------------------------------------------------------------
// Parameters: HWND    hwndPicture       Handle of picture window
//             WORD    wScrollCode       Scroll code as defined in windows.h
//
// Returns:    LONG    0L if OK
// --------------------------------------------------------------------
LONG FAR ScrollToCorner(HWND hwndPicture, WORD vk_key )

{
    NPPICTUREDATA   pPictureData;  // -> picture data struct
    int             nHorzPosition; // Temp horz scroll position
    int             nVertPosition; // Temp vert scroll position
    WORD            wWidth;        // Picture width
    WORD            wHeight;       // Picture height
    int             nHorzToScroll; // Amount to scroll horz
    int             nVertToScroll; // Amount to scroll vert
    RECT            rcPictArea;    // Picture rect in client coordinates

    if( !(pPictureData =
        (NPPICTUREDATA) GetWindowWord( hwndPicture, 0 ))) {
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NOPICDATA, VIEWER_STRING_CAPTION, MB_OK );
        return VIEWER_STRING_NOPICDATA;
    }

    if( vk_key == VK_HOME )
        nVertPosition = nHorzPosition = 0;
    else {
        nHorzPosition = pPictureData->spmsHScroll.wCurMax;          
        nVertPosition = pPictureData->spmsVScroll.wCurMax;          
    }

    nHorzToScroll = nHorzPosition - (int) pPictureData->spmsHScroll.wCurPos;
    nVertToScroll = nVertPosition - (int) pPictureData->spmsVScroll.wCurPos;
    if( nHorzToScroll || nVertToScroll ) {
        pPictureData->spmsHScroll.wCurPos = nHorzPosition;
        pPictureData->spmsVScroll.wCurPos = nVertPosition;

        wWidth = pPictureData->rcCurPictureRect.right -
            pPictureData->rcCurPictureRect.left;
        pPictureData->rcCurPictureRect.left  = -nHorzPosition;
        pPictureData->rcCurPictureRect.right = -nHorzPosition + wWidth;

        wHeight = pPictureData->rcCurPictureRect.bottom -
            pPictureData->rcCurPictureRect.top;
        pPictureData->rcCurPictureRect.top    = -nVertPosition;
        pPictureData->rcCurPictureRect.bottom = -nVertPosition + wHeight;

        if( IsWindowEnabled( pPictureData->spmsHScroll.hwnd ))
            SetScrollPos( pPictureData->spmsHScroll.hwnd, SB_CTL,
            pPictureData->spmsHScroll.wCurPos, TRUE );
        if( IsWindowEnabled( pPictureData->spmsVScroll.hwnd ))
            SetScrollPos( pPictureData->spmsVScroll.hwnd, SB_CTL,
            pPictureData->spmsVScroll.wCurPos, TRUE );

        // Get picture rect in picture coordinates
        GetClientRect( hwndPicture, &rcPictArea );
        PictureRectFromClient( &rcPictArea);
        // Convert to client coordinates for ScrollWindow
        PictureToClient( (LPPOINT) &rcPictArea, 2 );

        if( (((WORD) abs( nHorzToScroll )) <
            pPictureData->spmsHScroll.wCurPage ) &&
            (((WORD) abs( nVertToScroll )) <
            pPictureData->spmsVScroll.wCurPage ))

        {
            UpdateWindow( hwndPicture );
            ScrollWindow( hwndPicture, -nHorzToScroll, -nVertToScroll,
                &rcPictArea, &rcPictArea );
        }
        else
            InvalidateRect( hwndPicture, &rcPictArea, TRUE );

        UpdateWindow( hwndPicture );
    }

    return 0L;

}


// Function: IsInGrowBox - Determines if the cursor is in the grow box
// --------------------------------------------------------------------
// Parameters: HWND         hwndPicture       Handle of picture wnd
//             POINT        ptCursor          Cursor position in client coords.
//
// Returns:    BOOL         TRUE if in grow box, else FALSE
// --------------------------------------------------------------------
BOOL FAR IsInGrowBox( HWND hwndPicture, POINT ptCursor )

{
    NPPICTUREDATA   pPictureData; // -> picture data struct

    if( !(pPictureData =
        (NPPICTUREDATA) GetWindowWord( hwndPicture, 0 )) ||
        !PtInRect( &pPictureData->rcGrowBox, ptCursor ))
        return FALSE;
    else
        return TRUE;
}


// Function: DestroyGrowBoxBitmap - Destroys the grow box bitnmap
// --------------------------------------------------------------------
// Parameters: VOID
//
// Returns:    VOID   
// --------------------------------------------------------------------
VOID FAR DestroyGrowBoxBitmap( VOID )

{
    if( g.hbmpGrowBox ) {
        DeleteObject( g.hbmpGrowBox );
        g.hbmpGrowBox = NULL;
    }

    return;
}


// Function: PaintTheGrowBox - Paints the grow box
// --------------------------------------------------------------------
// Parameters: HWND             hwndPicture     Handle of picture window
//             HDC              hdc             DC of picture window
//             NPPICTUREDATA    pPictureData    -> picture wnd data struct
//
// Returns:    VOID   
// --------------------------------------------------------------------
VOID FAR PaintTheGrowBox( HWND hwndPicture,
    HDC hdc, NPPICTUREDATA pPictureData )

{
    HDC        hmemDC;     // Memory dc
    HBITMAP    hbmpSave;   // Prev bitmap
    HBRUSH     hbrushSave; // Background brush

    if( !g.hbmpGrowBox && 
        !(g.hbmpGrowBox = GetGrowBoxBitmap( hdc, pPictureData )))
        return;

    if( IsZoomed( hwndPicture ) && IsZoomed( ViewerQueryFrameWindow())) {
        if( hbrushSave = SelectObject( hdc, GetStockObject( LTGRAY_BRUSH ))) {
            PatBlt( hdc,
                pPictureData->rcGrowBox.left,
                pPictureData->rcGrowBox.top,
                pPictureData->rcGrowBox.right - pPictureData->rcGrowBox.left,
                pPictureData->rcGrowBox.bottom - pPictureData->rcGrowBox.top,
                PATCOPY );

            SelectObject( hdc, hbrushSave );
        }
    }
    else {
        hmemDC = NULL;
        if( ( hmemDC = CreateCompatibleDC( hdc )) &&
            ( hbmpSave = SelectObject( hmemDC, g.hbmpGrowBox ))) {
            BitBlt( hdc, 
                pPictureData->rcGrowBox.left,
                pPictureData->rcGrowBox.top,
                pPictureData->rcGrowBox.right - pPictureData->rcGrowBox.left,
                pPictureData->rcGrowBox.bottom - pPictureData->rcGrowBox.top,
                hmemDC, 0, 0, SRCCOPY );

            SelectObject( hmemDC, hbmpSave );
        }

        if( hmemDC )
            DeleteDC( hmemDC );
    }

    return;
}


// Function: GetGrowBoxBitmap - Create the grow box mem bitmap. This routine
//                              saves a bitmap of the correct size so that
//                              bitblt can be used to draw the bitmap instead
//                              stetchblt which is much slower
// --------------------------------------------------------------------
// Parameters: HDC              hdc             DC of picture window
//             NPPICTUREDATA    pPictureData    -> picture wnd data struct
//
// Returns:    HBITMAP          hbmpGrowBox     Handle of grow box mem bitmap   
// --------------------------------------------------------------------
static HBITMAP NEAR GetGrowBoxBitmap( HDC hdc, NPPICTUREDATA pPictureData )

{
    HDC               hmemDC1;     // Mem dc
    HDC               hmemDC2;     // Mem dc
    BITMAP            bm;          // bitmap struct
    HBITMAP           hbmp;        // Handle of resource bitmap
    HBITMAP           hbmpSave1;   // Prev bitmap
    HBITMAP           hbmpSave2;   // Prev bitmap
    HBITMAP           hbmpGrowBox; // Handle of grow box bitmap
    int               nWidth;      // Width of final bitmap
    int               nHeight;     // Height of final bitmap
    HBRUSH            hbrushSave;  // Prev brush
    int               xOffset;     // x bitmap offset
    int               yOffset;     // y bitmap offset


    if( !( hbmp = LoadBitmap( ViewerQueryResources(), 
        MAKEINTRESOURCE( VIEWER_GROWBOX_BITMAP ))))
        return NULL;

    hmemDC1 = hmemDC2 = NULL;
    nWidth  = pPictureData->rcGrowBox.right - pPictureData->rcGrowBox.left;
    nHeight = pPictureData->rcGrowBox.bottom - pPictureData->rcGrowBox.top;

    if( ( hmemDC1 = CreateCompatibleDC( hdc )) &&
        ( hmemDC2 = CreateCompatibleDC( hdc )) &&
        ( hbmpGrowBox = CreateCompatibleBitmap
        ( hdc, nWidth, nHeight ))) {
        hbmpSave1 = SelectObject( hmemDC1, hbmp ); 
        hbmpSave2 = SelectObject( hmemDC2, hbmpGrowBox );

        // set the background to light gray
        hbrushSave = SelectObject( hmemDC2, GetStockObject( LTGRAY_BRUSH ));
        PatBlt( hmemDC2, 0, 0, nWidth, nHeight, PATCOPY );
        SelectObject( hmemDC2, hbrushSave );

        GetObject( hbmp, sizeof( BITMAP ), &bm );
        xOffset = ( nWidth - bm.bmWidth ) / 2;
        yOffset = ( nHeight - bm.bmHeight ) / 2;

        BitBlt( hmemDC2, max( 0, xOffset ), max( 0, yOffset ),
            min( bm.bmWidth, nWidth ),
            min( bm.bmHeight, nHeight ),
            hmemDC1, 
            max( 0, -xOffset ),
            max( 0, -yOffset ), SRCCOPY );

        SelectObject( hmemDC1, hbmpSave1 ); 
        SelectObject( hmemDC2, hbmpSave2 );
    }

    if( hmemDC1 )
        DeleteDC( hmemDC1 );
    if( hmemDC2 )
        DeleteDC( hmemDC2 );

    DeleteObject( hbmp );

    return hbmpGrowBox;
}


// Function: LimitTheDragSize - Limits the resized of the window so that it stays 
//                              inside the MDI client rect. Used with grow box
//                              processing
// --------------------------------------------------------------------
// Parameters: HWND             hwndPicture     hwnd of picture window
//             BOOL             bDragging       TRUE if dragging using grow box
//
// Returns:    VOID   
// --------------------------------------------------------------------
VOID FAR LimitTheDragSize( HWND hwndPicture, BOOL bDragging )

{
    RECT   rcClient;     // Client rect of MDI client
    RECT   rcwndPicture; // Window rect of picture wnd

    if( !( g.bLimitGrowBoxResize = bDragging ))
        return;

    if( !IsZoomed( hwndPicture )) {
        GetWindowRect( hwndPicture, &rcwndPicture );
        GetClientRect( ViewerQueryClientWindow(), &rcClient );

        MapWindowPoints( HWND_DESKTOP, ViewerQueryClientWindow(), 
            (LPPOINT) &rcwndPicture, 2 );

        g.ptMaxGrowBoxResize.x = rcClient.right - rcwndPicture.left;
        g.ptMaxGrowBoxResize.y = rcClient.bottom - rcwndPicture.top;
    }

    return;
}


// Function: InitMaxWndGrowBoxResize - Initializes the maximized window grow
//                                     box resizing
// --------------------------------------------------------------------
// Parameters: HWND        hwndPicture    Handle of picture wnd
//             POINT       ptCursor       Position of cursor
//
// Returns:    BOOL        TRUE if successful
// --------------------------------------------------------------------
BOOL FAR InitMaxWndGrowBoxResize( HWND hwndPicture, POINT ptCursor )

{
    RECT         rcClipCursor; // Clip cursor rect
    HDC          hdc;          // DC of desktop


    GetWindowRect( ViewerQueryFrameWindow(), &g.rcResizeRect );
    ClientToScreen( hwndPicture, &ptCursor );
    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;

    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 );

    return TRUE;
}


// Function: MoveTheFrameRect - Moves the resizing rect during maximized 
//                              window grow box resizing
// --------------------------------------------------------------------
// Parameters: HWND        hwndPicture    Handle of picture wnd
//             POINT       ptCursor       Position of cursor
//
// Returns:    VOID
// --------------------------------------------------------------------
VOID FAR MoveTheFrameRect( HWND hwndPicture, POINT ptCursor )

{
    DrawTheFrameRect( &g.rcResizeRect );

    ClientToScreen( hwndPicture, &ptCursor );
    g.rcResizeRect.right  = ptCursor.x + g.ptCursorOffset.x;
    g.rcResizeRect.bottom = ptCursor.y + g.ptCursorOffset.y;

    DrawTheFrameRect( &g.rcResizeRect );

    return;
}


// Function: EndMaxWndGrowBoxResize - Terminates the maximized window grow
//                                    box resizing
// --------------------------------------------------------------------
// Parameters: HWND        hwndPicture    Handle of picture wnd
//
// Returns:    VOID
// --------------------------------------------------------------------
VOID FAR EndMaxWndGrowBoxResize( HWND hwndPicture )

{
    DrawTheFrameRect( &g.rcResizeRect );
    DrawTheFrameRect( NULL ); // Does clean up

    ClipCursor( NULL );
    MoveWindow( ViewerQueryFrameWindow(),
        g.rcResizeRect.left, g.rcResizeRect.top,
        g.rcResizeRect.right - g.rcResizeRect.left,
        g.rcResizeRect.bottom - g.rcResizeRect.top, TRUE );

    return;
}


// Function: DrawTheFrameRect - Draws the resizing frame
// --------------------------------------------------------------------
// Parameters: RECT      lprcResizeRect     -> resize rect
//
// Returns:    VOID 
// --------------------------------------------------------------------
static VOID NEAR DrawTheFrameRect( LPRECT lprcResizeRect )

{
    HDC            hdc;             // DC of desktop
    HBRUSH         hbrushSave;      // Prev brush
    HBITMAP        hbitmapCheckers; // Handle of checkerboard bitmap

    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( ViewerQueryInstance(),
        MAKEINTRESOURCE( VIEWER_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 ));

            if( !( *lpfnFastWindowFrame ) ( hdc, lprcResizeRect, 
                wBorderWidth, wBorderHeight, PATINVERT )) { // Use PatBlt when FastWindowFrame fails
                ExcludeClipRect( hdc,
                    lprcResizeRect->left + wBorderWidth,
                    lprcResizeRect->top  + wBorderHeight,
                    lprcResizeRect->right  - wBorderWidth,
                    lprcResizeRect->bottom - wBorderHeight );

                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: PictureBannerWndProc - Window proc for the banner child window
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    Via DefWindowProc
// --------------------------------------------------------------------
LONG __export CALLBACK PictureBannerWndProc
    (HWND hwndBanner, UINT message, WPARAM wParam, LPARAM lParam)

{
    NPPICTUREDATA  pPictureData; // -> picture data struct
    PAINTSTRUCT    ps;           // Paint struct
    RECT           rcClient;     // Client rect of control
    UINT           nSaveAlign;   // Prev text align
    WORD           wBaseLine;    // Base line for text output
    HFONT          hFont;        // Handle of font
    HFONT          hsaveFont;    // Handle of prev font
    int            nHeight;      // Height of font


    if( message == WM_PAINT ) {
        if( !(pPictureData = (NPPICTUREDATA) GetWindowWord
            ( GetParent( hwndBanner ), 0 )))
            return 0L;

        if( !BeginPaint( hwndBanner, &ps ))
            return 0L;

        GetClientRect( hwndBanner, &rcClient );
        MoveTo( ps.hdc, 0, rcClient.bottom - 1 );
        LineTo( ps.hdc, rcClient.right, rcClient.bottom - 1 );

        // Don't report an error if this fails since it will just
        // use the system font instead
        nHeight = -MulDiv( BANNER_TEXT_HEIGHT,
            GetDeviceCaps( ps.hdc, LOGPIXELSY ), 72 );
        hsaveFont = NULL;
        if( hFont = MakeAnArialFont( ps.hdc, nHeight ))
            hsaveFont = SelectObject( ps.hdc, hFont );

        wBaseLine = 3 * rcClient.bottom / 4;
        nSaveAlign = SetTextAlign( ps.hdc, TA_LEFT | TA_BASELINE );
        TextOut( ps.hdc, 6, wBaseLine, pPictureData->szPictType,
            lstrlen( pPictureData->szPictType ));

        SetTextAlign( ps.hdc, TA_RIGHT | TA_BASELINE );
        SetTextCharacterExtra( ps.hdc, 
            GetTextCharacterExtra( ps.hdc ) + TEXT_EXTRA_SPACING );

        TextOut( ps.hdc, rcClient.right - 6, wBaseLine,
            pPictureData->szFileSize,
            lstrlen( pPictureData->szFileSize ));

        SetTextAlign( ps.hdc, nSaveAlign );

        if( hsaveFont )
            SelectObject( ps.hdc, hsaveFont ); 
        if( hFont )
            DeleteObject( hFont ); 

        EndPaint( hwndBanner, &ps );
        return 0L;
    }
    else
        return DefWindowProc( hwndBanner, message, wParam, lParam);
}


// 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
// --------------------------------------------------------------------
HFONT FAR 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( ViewerQueryResources(), VIEWER_STRING_FACENAME, 
        plf->lfFaceName, sizeof( plf->lfFaceName ));

    hFont = CreateFontIndirect( plf );

    LocalFree( (LOCALHANDLE) plf );

    return hFont;
}


// Function: RegisterChildControls - Registers the window classes for the
//                                   picture resizing and banner child windows
// --------------------------------------------------------------------
// Parameters: HINSTANCE     hInstance  Instance of application
//
// Returns:    BOOL          TRUE if OK
// --------------------------------------------------------------------
BOOL FAR RegisterChildControls( HINSTANCE hInstance )

{
    WNDCLASS     wc; // Window class information

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = PictureZoomWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = (HBRUSH) ( COLOR_BTNFACE + 1 );
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = PICTURE_ZOOM_CLASS;

    if( !RegisterClass( &wc ))
        return FALSE;

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = PictureBannerWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = GetStockObject( WHITE_BRUSH );
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = PICTURE_BANNER_CLASS;

    return RegisterClass( &wc );
}
