
// ---------------------------------------------------------------------
//
// ZoomCtrl.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
                    // Needed for struct defs in picture.h
#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

// Consts
// ---------
// These are used instead of GetSysColors because user may change 
// system colors using control panel in which case, our colors would
// no longer match the Windows button colors which do not change
#define BTNFACECOLOR        (RGB( 192, 192, 192 ))
#define BTNSHADOWCOLOR      (RGB( 128, 128, 128 ))
#define BTNHIGHLIGHTCOLOR   (RGB( 255, 255, 255 ))

#define TEXT_HEIGHT           7 // Text height in points 
#define TEXT_EXTRA_SPACING    2 // Extra spacing between characters in
                                // percent text


// This struct defines the bounds of the zones used by the zoom control
typedef struct // Hungarian notation: zns
  {POINT      ptBuckle;         // position of UL corner of buckle
                                // in the corresponding zone
    WORD       wZoomMultiplier; // Zoom scaling multiplier
    char       szPercent[8];    // percent text i.e. "150%   "
                                // This string is padded with with blanks
} ZONES, * NPZONES;

// Message-Persistent Data
// -----------------------
static struct // Hungarian notation: g
  {                                     // These bitmaps and dimensions are used in the zoom control
                                        // They are shared by all instances
    RECT        rcBar;                  // Rect defining belt in control
    RECT        rcYard;                 // Rect defining area in which mouse
                                        // dragging works. When the mouse is
                                        // dragged outside this rect, the
                                        // buckle does not follow
    HBITMAP     hbmpActiveBuckle;       // Handle to active buckle bitmap
    WORD        wBuckleWidth;           // Width of buckle bitmap
    WORD        wBuckleHeight;          // Height of buckle bitmap
    ZONES       znsZones[NUM_OF_ZOOMS]; // Array of zone structs
    POINT       ptText;                 // Upper-Left corner of % text
    int         nTextHeight;            // % Text height
    WORD        wWidthWnd;              // Adjusted window width after  
                                        // correcting for font size
  } g;



// Internal Function Declarations
// ------------------------------
static LONG NEAR BuildBuckleStuff     (HWND);
static LONG NEAR UpdateBuckleAndText  (HWND, HDC, NPPICTUREDATA, WORD,
                                                   HBITMAP, BOOL, BOOL);
static VOID NEAR Draw3DRect           (HDC, int, int, int, int);
static LONG NEAR DrawBuckle           (HDC, NPPICTUREDATA, NPZONES,
                                                    HBITMAP, BOOL, BOOL);
static WORD NEAR PointToZoomIndex     (PPOINT, WORD);


// Function: PictureZoomWndProc - Window proc for the picture resizing
//                                scroll bar
// --------------------------------------------------------------------
// Parameters: As required by Microsoft Windows
//
// Returns:    Via DefWindowProc
// --------------------------------------------------------------------
LONG __export CALLBACK PictureZoomWndProc
    (HWND hwndZoom, UINT message, WPARAM wParam, LPARAM lParam)

{                               // These statics are only used during mouse capture
    static BOOL     bDragging;  // Dragging flag
    static WORD     wZoomIndex; // Current zoom index during dragging

    NPPICTUREDATA   pPictureData;  // -> picture data struct
    PAINTSTRUCT     ps;            // Paint struct
    HDC             hdc;           // Control dc
    RECT            rcZoom;        // Client rect of control
    HWND            hwndPicture;   // Handle of picture window
    HBRUSH          hbrush;        // Handle of brush
    HBRUSH          hbrushSave;    // Handle of prev brush
    NPZONES         pznsZone;      // -> zone struct
    POINT           ptMouse;       // Mouse position
    WORD            wNewZoomIndex; // New zoom index
    RECT            rcBuckle;      // Buckle rect


    if( !(pPictureData = (NPPICTUREDATA)
        GetWindowWord( hwndPicture = GetParent( hwndZoom ), 0 )))
        return DefWindowProc( hwndZoom, message, wParam, lParam);

    switch( message ) {
        case WM_CREATE:
            // Set statics: rcBar, zones and bitmaps for buckle
            // This data is shared by all instances
            if( !g.hbmpActiveBuckle && BuildBuckleStuff( hwndZoom ))
                return -1L;

            // Create a bitmap that will save the area under the buckle
            // Each instance gets one of these
            if( !( hdc = GetDC( hwndZoom ))) {
                CommonTellUser( ViewerQueryResources(),
                    VIEWER_STRING_NODC, VIEWER_STRING_CAPTION, MB_OK );
                return 0L;
            }

            if( !(pPictureData->zsZoomScroll.hbmpNotBuckle =
                CreateCompatibleBitmap( hdc,
                g.wBuckleWidth, g.wBuckleHeight ))) {
                CommonTellUser( ViewerQueryResources(),
                    VIEWER_STRING_NOMEMORY, NULL, MB_OK );

                ReleaseDC( hwndZoom, hdc );
                return -1L;
            }

            ReleaseDC( hwndZoom, hdc );
            return 0L;

        case WM_KEYDOWN:
            switch( wParam ) {
                case VK_ADD:
                case VK_SUBTRACT:
                    wZoomIndex =
                        pPictureData->zsZoomScroll.wCurZoomIndex +
                        ((wParam == VK_ADD) ? 1 : -1);
                    if( (wZoomIndex < IMAGE_SIZE_FIRST ) ||
                        (wZoomIndex >= IMAGE_SIZE_FIRST + NUM_OF_ZOOMS))
                        return 0L;

                    if( !(hdc = GetDC( hwndZoom ))) {
                        CommonTellUser( ViewerQueryResources(),
                            VIEWER_STRING_NODC,
                            VIEWER_STRING_CAPTION, MB_OK );
                        return 0L;
                    }

                    UpdateBuckleAndText( hwndZoom, hdc, pPictureData,
                        wZoomIndex, g.hbmpActiveBuckle, FALSE, TRUE );
                    ReleaseDC( hwndZoom, hdc );

                    ZoomPicture( hwndPicture, wZoomIndex );

                    return 0L;

                default:
                    break;
            }
            break;

        case WM_LBUTTONDOWN:
            ptMouse = MAKEPOINT( lParam );
            if( !PtInRect( &g.rcBar, ptMouse ))
                return 0L;

            wZoomIndex = PointToZoomIndex( &ptMouse,
                pPictureData->zsZoomScroll.wCurZoomIndex );

            if( wZoomIndex != pPictureData->zsZoomScroll.wCurZoomIndex ) {
                if( !(hdc = GetDC( hwndZoom ))) {
                    CommonTellUser( ViewerQueryResources(),
                        VIEWER_STRING_NODC,
                        VIEWER_STRING_CAPTION, MB_OK );
                    return 0L;
                }

                UpdateBuckleAndText( hwndZoom, hdc, pPictureData,
                    wZoomIndex, g.hbmpActiveBuckle, FALSE, TRUE );
                ReleaseDC( hwndZoom, hdc );
            }

            SetCapture( hwndZoom );
            bDragging = TRUE;

            return 0L;

        case WM_LBUTTONUP:
            if( bDragging ) {
                ReleaseCapture();
                ZoomPicture( hwndPicture, wZoomIndex );
            }
            bDragging = FALSE;
            return 0L;

        case WM_MOUSEMOVE:
            if( !bDragging )
                return 0L;

            ptMouse = MAKEPOINT( lParam );
            if( PtInRect( &g.rcYard, ptMouse )) {
                if( wZoomIndex == ( wNewZoomIndex = 
                    PointToZoomIndex( &ptMouse, wZoomIndex )))
                    return 0L;
            }
            else {
                wNewZoomIndex = pPictureData->zsZoomScroll.wCurZoomIndex;
            }

            wZoomIndex = wNewZoomIndex;

            if( !(hdc = GetDC( hwndZoom ))) {
                CommonTellUser( ViewerQueryResources(),
                    VIEWER_STRING_NODC, VIEWER_STRING_CAPTION, MB_OK );
                return 0L;
            }

            UpdateBuckleAndText( hwndZoom, hdc, pPictureData, wZoomIndex,
                g.hbmpActiveBuckle, FALSE, TRUE );
            ReleaseDC( hwndZoom, hdc );

            return 0L;

        // WM_USER messages

        case WM_ZOOM_MOVEBUCKLE:
            if( !(hdc = GetDC( hwndZoom ))) {
                CommonTellUser( ViewerQueryResources(),
                    VIEWER_STRING_NODC, VIEWER_STRING_CAPTION, MB_OK );
                return 0L;
            }

            UpdateBuckleAndText( hwndZoom, hdc, pPictureData,
                pPictureData->zsZoomScroll.wCurZoomIndex,
                g.hbmpActiveBuckle, FALSE, TRUE );
            ReleaseDC( hwndZoom, hdc );
            return 0L;

        // End WM_USER messages

        case WM_PAINT:
            pznsZone = g.znsZones +
                pPictureData->zsZoomScroll.wCurZoomIndex -
                IMAGE_SIZE_FIRST;

            // Force repaint of the buckle to prevent the
            // repainting logic from getting off
            *((LPPOINT) &rcBuckle.left) = 
                pPictureData->zsZoomScroll.ptBucklePos;
            rcBuckle.right  = rcBuckle.left + g.wBuckleWidth;
            rcBuckle.bottom = rcBuckle.top  + g.wBuckleHeight;
            InvalidateRect( hwndZoom, &rcBuckle, TRUE );

            if( !BeginPaint( hwndZoom, &ps ))
                return 0L;

            GetClientRect( hwndZoom, &rcZoom );

            // Draw the main rect
            if( !( hbrush = CreateSolidBrush( BTNFACECOLOR ))) {
                EndPaint( hwndZoom, &ps );
                return 0L;
            }
            FillRect( ps.hdc, &rcZoom, hbrush );
            DeleteObject( hbrush );

            // Draw the belt
            if( !( hbrush = CreateSolidBrush( BTNSHADOWCOLOR ))) {
                EndPaint( hwndZoom, &ps );
                return 0L;
            }
            hbrushSave = SelectObject( ps.hdc, hbrush );
            Rectangle( ps.hdc, g.rcBar.left, g.rcBar.top,
                g.rcBar.right, g.rcBar.bottom );
            DeleteObject( SelectObject( ps.hdc, hbrushSave ));

            // Draw the buckle
            UpdateBuckleAndText( hwndZoom, ps.hdc, pPictureData,
                pPictureData->zsZoomScroll.wCurZoomIndex,
                g.hbmpActiveBuckle, TRUE, TRUE );

            EndPaint( hwndZoom, &ps );

            return 0L;

        case WM_DESTROY:
            // Each instance has one of these
            if( pPictureData->zsZoomScroll.hbmpNotBuckle ) {
                DeleteObject( pPictureData->zsZoomScroll.hbmpNotBuckle );
                pPictureData->zsZoomScroll.hbmpNotBuckle = NULL;
            }

            // All instances share these
            if( ViewerQueryNumPictures() <= 1 ) {
                if( g.hbmpActiveBuckle )
                    DeleteObject( g.hbmpActiveBuckle );
                g.hbmpActiveBuckle   = NULL;
            }

            pPictureData->zsZoomScroll.hwnd = NULL;

            return 0L;

        default:
            break;
    }

    return DefWindowProc( hwndZoom, message, wParam, lParam);

}


// Function: UpdateBuckleAndText - Draws the buckle and text
// --------------------------------------------------------------------
// Parameters: HWND           hwndZoom          Window handle of control
//             HDC            hdc               DC of control
//             NPPICTUREDATA  pPictureData      -> picture data struct
//             WORD           wZoomIndex        Current zoom index
//             HBITMAP        hbmpBuckle        Handle of buckle bitmap
//             BOOL           bRepainting       Repainting flag
//             BOOL           bUpdateNoBuckle   Update no buckle bitmap flag
//
// Returns:    LONG             0L if OK
// --------------------------------------------------------------------
static LONG NEAR UpdateBuckleAndText( HWND hwndZoom,
                   HDC hdc, NPPICTUREDATA pPictureData,
                         WORD wZoomIndex, HBITMAP hbmpBuckle,
                                BOOL bRepainting, BOOL bUpdateNoBuckle )

{
    NPZONES    pznsZone;   // -> zone struct
    HFONT      hFont;      // Handle to font used for % text
    HFONT      hsaveFont;  // Prev font
    COLORREF   dwcrSave;   // Prev color
    UINT       uSaveAlign; // Prev text alignment


    pznsZone = g.znsZones + wZoomIndex - IMAGE_SIZE_FIRST;

    // Draw the buckle in the new position
    if( DrawBuckle(hdc, pPictureData, pznsZone, hbmpBuckle,
        bRepainting, bUpdateNoBuckle ))
        return 0L;

    // Get the font. Don't bother with error message. If this
    // fails, the system font will be used
    if( hFont = MakeAnArialFont( hdc, g.nTextHeight ))
        hsaveFont = SelectObject( hdc, hFont );

    SetTextCharacterExtra( hdc, 
        GetTextCharacterExtra( hdc ) + TEXT_EXTRA_SPACING );

    dwcrSave = SetBkColor( hdc, BTNFACECOLOR );

    // Draw the % text
    uSaveAlign = SetTextAlign( hdc, TA_LEFT | TA_BASELINE );
    TextOut( hdc, g.ptText.x, g.ptText.y, pznsZone->szPercent,
        lstrlen( pznsZone->szPercent ));
    SetTextAlign( hdc, uSaveAlign );

    if( hFont )
        DeleteObject( SelectObject( hdc, hsaveFont ));
    SetBkColor( hdc, dwcrSave );

    return 0L;
}


// Function: DrawBuckle - Draws the buckle
// --------------------------------------------------------------------
// Parameters: HDC              hdc              hdc of control
//             NPPICTUREDATA    pPictureData     -> to picturedata struct
//             NPZONES          pznsZone         -> zone struct. Contains
//                                               offsets used to position buckle
//             HBITMAP          hbmpBuckle       Buckle bitmap
//             BOOL             bRepainting      TRUE if WM_PAINT message
//             BOOL             bUpdateNoBuckle  TRUE if area under buckle
//                                               should be saved. This is
//                                               false if only changing
//                                               activation state of buckle
//
// Returns:    LONG             0L if OK
// --------------------------------------------------------------------
static LONG NEAR DrawBuckle( HDC hdc, NPPICTUREDATA pPictureData,
                         NPZONES pznsZone, HBITMAP hbmpBuckle,
                                 BOOL bRepainting, BOOL bUpdateNoBuckle )

{
    HDC           hdcMem;   // Memory device context
    HBITMAP       hbmpSave; // Prev bitmap


    if( !(hdcMem = CreateCompatibleDC( hdc ))) {
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NODC, VIEWER_STRING_CAPTION, MB_OK );
        return VIEWER_STRING_NODC;
    }

    hbmpSave = SelectObject( hdcMem,
        pPictureData->zsZoomScroll.hbmpNotBuckle );
    // Use no buckle bitmap to remove buckle
    if( !bRepainting ) {
        BitBlt( hdc, pPictureData->zsZoomScroll.ptBucklePos.x,
            pPictureData->zsZoomScroll.ptBucklePos.y,
            g.wBuckleWidth, g.wBuckleHeight,
            hdcMem, 0, 0, SRCCOPY );
    }

    // Save area under new position of buckle
    if( bUpdateNoBuckle ) {
        BitBlt( hdcMem, 0, 0, g.wBuckleWidth, g.wBuckleHeight,
            hdc, pznsZone->ptBuckle.x, pznsZone->ptBuckle.y, SRCCOPY );
        pPictureData->zsZoomScroll.ptBucklePos = pznsZone->ptBuckle;
    }

    // Draw buckle
    SelectObject( hdcMem, hbmpBuckle );
    BitBlt( hdc, pznsZone->ptBuckle.x, pznsZone->ptBuckle.y,
        g.wBuckleWidth, g.wBuckleHeight, hdcMem, 0, 0, SRCCOPY );

    SelectObject( hdcMem, hbmpSave );
    DeleteDC( hdcMem );

    return 0L;
}


// Function: BuildBuckleStuff - processes WM_CREATE message for
//                              zoom wnd control
// --------------------------------------------------------------------
// Parameters: HWND         hwndZoom       Window handle of zoom control
//
// Returns:    LONG         0L if OK
// --------------------------------------------------------------------
static LONG NEAR BuildBuckleStuff( HWND hwndZoom )

{
    RECT     rcClient;         // Client rect of window
    POINT    ptBuckle;         // Temp UL corner of buckle
    WORD     wBar;             // Length of bar
    WORD     wBar1;            // Temp length of bar
    WORD     wBar2;            // Temp length of bar
    WORD     wBuckleP1;        // Width of buckle + 1 pixel
    HDC      hdc;              // Device context of control
    HDC      hdcMem;           // Memory dc
    HBITMAP  hbmpSave;         // Prev bitmap
    char     szFormat[6];      // Resource string format for % text
    int      i;                // Temp index
    int      j;                // Temp index
    WORD     wBarHeight;       // Bar height
    WORD     wBuckleExtension; // Half the amount by which buckle height exceeds
                               // bar height
    WORD     wMaxTextExtent;   // Max text extent of % text
    WORD     wTextExtent;      // Text extent of % text
    HFONT    hFont;            // Font used to display % text
    HFONT    hsaveFont;        // Prev font
    TEXTMETRIC tm;             // Text metrics struct
    BOOL     bGotTextMetrics;  // TRUE if GetTextMetrics worked 
    WORD     wAveChar;         // Average width of % text char
    int      nMaxTextHeight;   // Max text height


    static WORD wZoomMults[NUM_OF_ZOOMS] = // Array of zoom multipliers
          { 25, 50, 75, 100, 150, 200, 400 }; // that define the scaling


    GetClientRect( hwndZoom, &rcClient );

    // rcYard defines the area within which the control responds to
    // mouse dragging
    g.rcYard = rcClient;
    g.rcYard.left   -= (WORD) GetSystemMetrics( SM_CXVSCROLL );
    g.rcYard.right  += (WORD) GetSystemMetrics( SM_CXVSCROLL );
    g.rcYard.top    -= (WORD) GetSystemMetrics( SM_CYHSCROLL );
    g.rcYard.bottom += (WORD) GetSystemMetrics( SM_CYHSCROLL );

    // Set rect for slider bar
    g.rcBar.left   = rcClient.bottom / 3;
    g.rcBar.right  = g.rcBar.left + (65 * rcClient.right / 100 );
    g.rcBar.top    = max( 3 * rcClient.bottom / 10, 3 );
    g.rcBar.bottom = rcClient.bottom - g.rcBar.top;
    wBar = g.rcBar.right - g.rcBar.left;
    wBarHeight = g.rcBar.bottom - g.rcBar.top;

    wBuckleExtension = ( rcClient.bottom - wBarHeight - 2 ) / 2;
    if( wBuckleExtension > 2 )
        wBuckleExtension = 2;
    g.wBuckleHeight = wBarHeight + 2 * wBuckleExtension;

    // Set buckle width and then adjust bar or buckle length so that
    // bar length is integral number of buckles long + 2 pixels

    g.wBuckleWidth = ( wBar - NUM_OF_ZOOMS - 1 ) / NUM_OF_ZOOMS;
    wBar1 = ( g.wBuckleWidth + 1 ) * NUM_OF_ZOOMS + 1;
    wBar2 = ( (g.wBuckleWidth + 1) + 1 ) * NUM_OF_ZOOMS + 1;
    if( abs( wBar - wBar1 ) >= abs( wBar - wBar2 )) {
        g.wBuckleWidth += 1;
        g.rcBar.right = g.rcBar.left + wBar2;
    }
    else {
        g.rcBar.right = g.rcBar.left + wBar1;
    }


    if( !(hdc = GetDC( hwndZoom ))) {
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NODC, VIEWER_STRING_CAPTION, MB_OK );
        return VIEWER_STRING_NODC;
    }

    // Set zones
    // Initialize text strings
    LoadString( ViewerQueryResources(), VIEWER_STRING_PERCENT,
        szFormat, sizeof( szFormat ));
    wMaxTextExtent = 0;
    // Set text height
    g.nTextHeight = -MulDiv( TEXT_HEIGHT,
        GetDeviceCaps( hdc, LOGPIXELSY ), 72 );
    // Limit text height to 2/3 of the window height
    nMaxTextHeight = -MulDiv( 2, rcClient.bottom, 3 );
    // use max because values are < 0
    g.nTextHeight = max( g.nTextHeight, nMaxTextHeight );

    // Select font into dc so GetTextExtent will work
    if( hFont = MakeAnArialFont( hdc, g.nTextHeight ))
        hsaveFont = SelectObject( hdc, hFont );

    SetTextCharacterExtra( hdc, 
        GetTextCharacterExtra( hdc ) + TEXT_EXTRA_SPACING );

    for( i=0; i < NUM_OF_ZOOMS; i++ ) { // Save multipliers for Query function
        g.znsZones[i].wZoomMultiplier = wZoomMults[i]; 
        wsprintf( g.znsZones[i].szPercent, szFormat,
            g.znsZones[i].wZoomMultiplier );
                                        // Get text extent before padding with blanks
        wTextExtent = LOWORD( GetTextExtent( hdc, g.znsZones[i].szPercent, 
            lstrlen( g.znsZones[i].szPercent )));
        if( wMaxTextExtent < wTextExtent ) {
            wMaxTextExtent = wTextExtent;
            wAveChar = wMaxTextExtent / lstrlen( g.znsZones[i].szPercent );
        }

        // Now pad with blanks to erase previous text
        for(j=lstrlen( g.znsZones[i].szPercent );
            j < sizeof( g.znsZones[i].szPercent ) - 1; j++ )
            g.znsZones[i].szPercent[j] = ' ';
        g.znsZones[i].szPercent[j] = '\0';
    }

    bGotTextMetrics = GetTextMetrics( hdc, &tm );

    if( hFont )
        DeleteObject( SelectObject( hdc, hsaveFont ));

    // Initialize buckle positions and zone edges
    wBuckleP1  = g.wBuckleWidth + 1;
    ptBuckle.x = g.rcBar.left + 1;
    ptBuckle.y = g.rcBar.top - wBuckleExtension;
    for( i=0; i < NUM_OF_ZOOMS; i++ ) {
        g.znsZones[i].ptBuckle = ptBuckle;
        ptBuckle.x += wBuckleP1;
    }

    if( !(hdcMem = CreateCompatibleDC( hdc ))) {
        ReleaseDC( hwndZoom, hdc );
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NODC, VIEWER_STRING_CAPTION, MB_OK );
        return VIEWER_STRING_NODC;
    }

    // build buckle
    if( !( g.hbmpActiveBuckle = CreateCompatibleBitmap
        ( hdc, g.wBuckleWidth, g.wBuckleHeight ))) {
        DeleteDC( hdcMem );
        ReleaseDC( hwndZoom, hdc );
        CommonTellUser( ViewerQueryResources(),
            VIEWER_STRING_NOMEMORY, NULL, MB_OK );
        return VIEWER_STRING_NOMEMORY;
    }

    hbmpSave = SelectObject( hdcMem, g.hbmpActiveBuckle );

    Draw3DRect( hdcMem, 0, 0, g.wBuckleWidth, g.wBuckleHeight );

    SelectObject( hdcMem,  hbmpSave );
    DeleteDC( hdcMem );

    // Set reference point for TextOut
    g.ptText.x = g.rcBar.right + 4;
    if( bGotTextMetrics ) {
        g.ptText.y = max( g.rcBar.bottom, 
            ( rcClient.bottom +
            tm.tmAscent - tm.tmInternalLeading ) / 2 );
    }
    else {
        g.ptText.y = g.rcBar.bottom;
    }

    // Now make sure text will fit in window, adjust width if it doesn't
    g.wWidthWnd = g.ptText.x + wMaxTextExtent + ( 3 * wAveChar / 2 );

    ReleaseDC( hwndZoom, hdc );

    return 0L;
}

// Function: Draw3DRect - Draws the 3D stuff on a given rect
// --------------------------------------------------------------------
// Parameters: HDC              hdc          Device context
//             int              left         Rect.left
//             int              top          Rect.top
//             int              right        Rect.right
//             int              bottom       Rect.bottom
//
// Returns:    VOID
// --------------------------------------------------------------------
static VOID NEAR Draw3DRect( HDC hdc,
                        int nleft, int ntop, int nright, int nbottom )

{
    HPEN     hpenSave;     // Prev pen
    HPEN     hpenHighLite; // HighLite pen
    HPEN     hpenShadow;   // Shadow pen
    HBRUSH   hbrush;       // Brush
    HBRUSH   hbrushSave;   // Prev brush


    // Don't bother with error messages. Failure here will not cause a
    // crash. The rects just won't look right.

    if( hbrush = CreateSolidBrush( BTNFACECOLOR )) {
        hbrushSave = SelectObject( hdc, hbrush );
        Rectangle( hdc, nleft, ntop, nright, nbottom );
        DeleteObject( SelectObject( hdc, hbrushSave ));
    }

    if( hpenShadow = CreatePen( PS_SOLID, 1, BTNSHADOWCOLOR)) {
        hpenSave = SelectObject( hdc, hpenShadow );
        MoveTo( hdc, nright - 2, 1 );
        LineTo( hdc, nright - 2, nbottom - 2 );
        LineTo( hdc, 0, nbottom - 2);
        DeleteObject( SelectObject( hdc, hpenSave ));
    }

    if( hpenHighLite = CreatePen( PS_SOLID, 1, BTNHIGHLIGHTCOLOR )) {
        hpenSave = SelectObject( hdc, hpenHighLite );
        MoveTo( hdc, 1, nbottom - 3 );
        LineTo( hdc, 1, 1 );
        LineTo( hdc, nright - 2, 1 );
        DeleteObject( SelectObject( hdc, hpenSave ));
    }

    return;
}


// Function: PointToZoomIndex - Finds index of zoon cooresponding to mouse
//                              position
// --------------------------------------------------------------------
// Parameters: PPOINT       pptMouse         -> current mouse position
//             WORD         wCurZoneIndex    Current zone index
//
// Returns:    WORD         wZoomIndex
// --------------------------------------------------------------------
static WORD NEAR PointToZoomIndex( PPOINT pptMouse, WORD wCurZoneIndex )

{
    int        nxPos;    // Horizontal position of mouse
    WORD       iCur;     // Current zone -> offset
    WORD       i;        // Counter
    int        nWidthP1; // Width of buckle + 1 pixel

    nxPos = pptMouse->x;
    nWidthP1 = g.wBuckleWidth + 1;
    iCur = wCurZoneIndex - IMAGE_SIZE_FIRST;

    // Check if left of first zone
    if( nxPos <= g.znsZones[0].ptBuckle.x + nWidthP1)
        return IMAGE_SIZE_FIRST;

    // Check if in current zone
    if( ( nxPos >= g.znsZones[iCur].ptBuckle.x - 1 ) &&
        ( nxPos <= g.znsZones[iCur].ptBuckle.x + nWidthP1 ))
        return wCurZoneIndex;

    // Check rest of zones
    for(i=0; i < NUM_OF_ZOOMS; i++ ) {
        if(( i != iCur ) &&
            ( nxPos <= g.znsZones[i].ptBuckle.x + nWidthP1 ))
            return ( i + IMAGE_SIZE_FIRST );
    }

    return ( IMAGE_SIZE_FIRST + NUM_OF_ZOOMS - 1 );
}


//  The remaining functions are the query functions called by other modules

// Function: ViewerQueryZoomMultiplier - Query the zoom multiplier for the
//                                       current zoom index
// --------------------------------------------------------------------
// Parameters: WORD           wZoomIndex    Current zoom index
//
// Returns:    WORD           wZoomMult     Corresponding zoom muliplier
// --------------------------------------------------------------------
WORD FAR ViewerQueryZoomMultiplier( WORD wZoomIndex )

{
    return g.znsZones[wZoomIndex - IMAGE_SIZE_FIRST].wZoomMultiplier;
}


// Function: ViewerQueryZoomIndex - Query the zoom index for the
//                                  current zoom multiplier 
// --------------------------------------------------------------------
// Parameters: WORD           wZoomMult     Current zoom muliplier
//
// Returns:    WORD           wZoomIndex    Corresponding zoom index
// --------------------------------------------------------------------
WORD FAR ViewerQueryZoomIndex( WORD wZoomMultiplier )

{
    int      i;

    for( i=0; i < NUM_OF_ZOOMS - 1; i++ ) {
        if( wZoomMultiplier <= g.znsZones[i].wZoomMultiplier )
            break;
    }

    return ( i + IMAGE_SIZE_FIRST );
}


// Function: ViewerQueryZoomWndWidth - Query the final width of the 
//                                     zoom window after adjustment for
//                                     font (see BuildBuckleStuff())
// --------------------------------------------------------------------
// Parameters: VOID
//
// Returns:    WORD    wWidthWnd     final width of control
// --------------------------------------------------------------------
WORD FAR ViewerQueryZoomWndWidth( VOID )

{
    return g.wWidthWnd;
}
