
#include "stdafx.h"
#include "global.h"
#include "pbrush.h"
#include "pbrusdoc.h"
#include "pbrusfrm.h"
#include "pbrusvw.h"
#include "minifwnd.h"
#include "bmobject.h"
#include "imgsuprt.h"
#include "imgwnd.h"
#include "imgcolor.h"
#include "imgbrush.h"
#include "imgwell.h"
#include "imgtools.h"
#include "tedit.h"
#include "t_text.h"
#include "t_fhsel.h"
#include "toolbox.h"
#include "undo.h"
#include "props.h"
#include "cmpmsg.h"
#include "imgdlgs.h"
#include "ferr.h"
#include "thumnail.h"

#ifdef _DEBUG
#undef THIS_FILE
static CHAR BASED_CODE THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNAMIC(CImgWnd, CWnd)

#include "memtrace.h"

/***************************************************************************/
// helper fns

static CTedit *_GetTextEdit()
{
    if (CImgTool::GetCurrentID() == IDMX_TEXTTOOL)
        {
        CTextTool* pTextTool = (CTextTool*)CImgTool::GetCurrent();

        if ((pTextTool != NULL) &&
            pTextTool->IsKindOf(RUNTIME_CLASS( CTextTool )))
            {
            CTedit* pTextEdit = pTextTool->GetTextEditField();

            if ((pTextEdit != NULL) &&
                pTextEdit->IsKindOf(RUNTIME_CLASS( CTedit )))
                {
                    return pTextEdit;
                }
            }
        }
    return NULL;
}

BOOL IsUserEditingText()
    {
    return (_GetTextEdit() != NULL);
    }

BOOL TextToolProcessed( UINT nMessage )
    {
    CTedit *pTextEdit = _GetTextEdit();
    if (pTextEdit)
        {
        pTextEdit->SendMessage( WM_COMMAND, nMessage );
        return TRUE;
        }
    return FALSE;
    }

/***************************************************************************/

BEGIN_MESSAGE_MAP(CImgWnd, CWnd)
    ON_WM_CREATE()
#if 0
    ON_WM_DESTROY()
#endif
    ON_WM_SETFOCUS()
    ON_WM_KILLFOCUS()
    ON_WM_PAINT()
    ON_WM_SIZE()
    ON_WM_HSCROLL()
    ON_WM_VSCROLL()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONDBLCLK()
    ON_WM_LBUTTONUP()
    ON_WM_RBUTTONDOWN()
    ON_WM_RBUTTONDBLCLK()
    ON_WM_RBUTTONUP()
    ON_WM_MOUSEMOVE()
    ON_WM_KEYDOWN()
    ON_WM_KEYUP()
    ON_WM_TIMER()
    ON_WM_CANCELMODE()
    ON_WM_WINDOWPOSCHANGING()
    ON_WM_DESTROYCLIPBOARD()
    ON_WM_PALETTECHANGED()
    ON_WM_SETCURSOR()
    ON_WM_MOUSEWHEEL ()
END_MESSAGE_MAP()

/***************************************************************************/

CRect  rcDragBrush;

CImgBrush  theBackupBrush;

CImgWnd*  g_pMouseImgWnd  = NULL;
CImgWnd*  g_pDragBrushWnd = NULL;

// Current Image Viewer
CImgWnd*          CImgWnd::c_pImgWndCur     = NULL;
CDragger*         CImgWnd::c_pResizeDragger = NULL;
CTracker::STATE   CImgWnd::c_dragState      = CTracker::nil;

// Mouse Tracking Information
MTI   mti;
BOOL  bIgnoreMouse;

/***************************************************************************/

CImgWnd::CImgWnd(IMG* pImg)
    {
    m_pNextImgWnd = NULL;
    m_nZoom       = 1;
    m_nZoomPrev   = 4;
    m_xScroll     = 0;
    m_yScroll     = 0;
    m_LineX       = 1;
    m_LineY       = 1;
    m_ptDispPos.x = -1;
    m_ptDispPos.y = -1;
    c_pImgWndCur  = this;
    m_pwndThumbNailView = NULL;
    m_wClipboardFormat = 0;
    m_hPoints     = NULL;
    m_WheelDelta = 0;
    ASSERT(pImg != NULL);
    m_pImg = pImg;
    m_pImg->m_nLastChanged = -1;
    }

/***************************************************************************/

CImgWnd::CImgWnd(CImgWnd *pImgWnd)
    {
    m_pImg        = pImgWnd->m_pImg;
    m_pNextImgWnd = pImgWnd->m_pNextImgWnd;
    m_nZoom       = pImgWnd->m_nZoom;
    m_nZoomPrev   = pImgWnd->m_nZoomPrev;
    m_xScroll     = pImgWnd->m_xScroll,
    m_yScroll     = pImgWnd->m_yScroll;
    m_ptDispPos   = pImgWnd->m_ptDispPos;
    m_pwndThumbNailView = NULL;
    m_wClipboardFormat = 0;
    m_hPoints     = NULL;
    }

/***************************************************************************/

CImgWnd::~CImgWnd()
    {
    if (c_pImgWndCur == this)
        c_pImgWndCur = NULL;

    if (g_pMouseImgWnd == this)
        g_pMouseImgWnd = NULL;

    HideBrush();
    fDraggingBrush = FALSE;

    g_bBrushVisible = FALSE;

    if (g_pDragBrushWnd == this)
        {
        g_pDragBrushWnd = NULL;
        }

    if (m_hPoints)
        {
        ::GlobalFree( m_hPoints );
        m_hPoints = NULL;
        }
    }

/***************************************************************************/

BOOL CImgWnd::Create( DWORD dwStyle, const RECT& rect,
                      CWnd* pParentWnd, UINT nID)
    {
    static CString  sImgWndClass;

    if (sImgWndClass.IsEmpty())
        sImgWndClass = AfxRegisterWndClass( CS_DBLCLKS );

    ASSERT( ! sImgWndClass.IsEmpty() );

    dwStyle |= WS_CLIPSIBLINGS;

    return CWnd::Create( sImgWndClass, NULL, dwStyle, rect, pParentWnd, nID );
    }

/***************************************************************************/

int CImgWnd::OnCreate( LPCREATESTRUCT lpCreateStruct )
    {
    if (m_pImg)
        AddImgWnd( m_pImg, this );

    return CWnd::OnCreate(lpCreateStruct);
    }

/***************************************************************************/

#if 0
void CImgWnd::OnDestroy()
    {
    if (c_pImgWndCur == this)
        c_pImgWndCur = NULL;

    HideBrush();
    fDraggingBrush = FALSE;

    CWnd::OnDestroy();
    }
#endif

/***************************************************************************/

void CImgWnd::OnPaletteChanged(CWnd *pPaletteWnd)
{
#if 0
    // obviously this never gets hit or somebody would have realized by now...
    CImgWnd::OnPaletteChanged(pPaletteWnd);
#endif
    Invalidate();
}


/***************************************************************************/

BOOL CImgWnd::OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message)
{
        if (nHitTest==HTCLIENT && pWnd->m_hWnd==m_hWnd)
        {
                // We do our own cursor stuff in our own client area, but not in the
                // text box
                return(TRUE);
        }

        return((BOOL)Default());
}

/***************************************************************************/

void CImgWnd::OnWindowPosChanging( WINDOWPOS FAR* lpwndpos )
    {
    CWnd::OnWindowPosChanging( lpwndpos );
    }

/***************************************************************************/
// Image View Painting Functions
//

void CImgWnd::OnPaint()
    {
    CPaintDC dc(this);

    if (dc.m_hDC == NULL)
        {
        theApp.SetGdiEmergency();
        return;
        }

    if (m_pImg == NULL)
        return;

    if (g_pMouseImgWnd == this)
        CImgTool::HideDragger( this );

    CPalette* ppalOld = SetImgPalette( &dc, FALSE );

    DrawBackground( &dc, (CRect*)&dc.m_ps.rcPaint );
    DrawImage     ( &dc, (CRect*)&dc.m_ps.rcPaint );
    DrawTracker   ( &dc, (CRect*)&dc.m_ps.rcPaint );

    if (g_pMouseImgWnd == this)
        CImgTool::ShowDragger( this );

    if (m_pwndThumbNailView != NULL)
        m_pwndThumbNailView->RefreshImage();

    if (ppalOld)
        dc.SelectPalette( ppalOld, FALSE );
    }

/***************************************************************************/

BOOL CImgWnd::OnCmdMsg( UINT nID, int nCode, void* pExtra,
                        AFX_CMDHANDLERINFO* pHandlerInfo )
    {
    if (nCode == CN_COMMAND)
        {
        switch (nID)
            {
            case IDMX_VS_PAGEUP:
                SendMessage( WM_VSCROLL, SB_PAGEUP, 0L );
                return TRUE;


            case IDMX_VS_PAGEDOWN:
                SendMessage( WM_VSCROLL, SB_PAGEDOWN, 0L );
                return TRUE;


            case IDMX_HS_PAGEUP:
                SendMessage( WM_HSCROLL, SB_PAGEUP, 0L );
                return TRUE;


            case IDMX_HS_PAGEDOWN:
                SendMessage( WM_HSCROLL, SB_PAGEDOWN, 0L );
                return TRUE;

            }

        CImgTool* pImgTool = CImgTool::FromID( nID );

        if (pImgTool != NULL)
            {
            pImgTool->Select();
            return TRUE;
            }
        }
    return CWnd::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
    }


void CImgWnd::GetDrawRects(const CRect* pPaintRect, const CRect* pReqDestRect,
                CRect& srcRect, CRect& destRect)
{
        // Find the sub-rectangle of srcRect that corresponds to
        // the pPaintRect sub-rectangle of destRect.
        srcRect         = *pPaintRect;
        srcRect.right  += m_nZoom - 1;
        srcRect.bottom += m_nZoom - 1;

        ClientToImage( srcRect );

        srcRect.left = max(0, srcRect.left);
        srcRect.top  = max(0, srcRect.top );

        srcRect.right  = min(m_pImg->cxWidth , srcRect.right );
        srcRect.bottom = min(m_pImg->cyHeight, srcRect.bottom);

        if (pReqDestRect == NULL)
        {
                destRect = srcRect;
                ImageToClient( destRect );
        }
        else
        {
                destRect = *pReqDestRect;
        }
}


/***************************************************************************/
// Draw the actual image 'a bitmap'.  Drawing is
// optimized to only deal with the pixels inside paintRect.  This function
// reduces flashing by drawing the image and optional grid in an off-screen
// bitmap and then transfering that bitmap to the screen.
//
void CImgWnd::DrawImage( CDC* pDC, const CRect* pPaintRect,
                                         CRect* pDestRect, BOOL bDoGrid )
    {
    ASSERT(    pDC != NULL );
    ASSERT( m_pImg != NULL );

    CRect destRect;
    CRect srcRect;

    GetDrawRects(pPaintRect, pDestRect, srcRect, destRect);

    if (srcRect.Width() <= 0 || srcRect.Height() <= 0)
        {
        // Nothing to paint...
        return;
        }

    if (! IsGridVisible() && m_nZoom == 1)
        {
        // Optimize the easy case...  (Can't speed up magnified views
        // because of the bogus hack we have to do in StretchCopy.)

        if (theApp.m_pPalette
        && ((m_pImg->cPlanes * m_pImg->cBitCount) == 1))
            {
            pDC->SetTextColor( PALETTEINDEX( 0 ) );
            pDC->SetBkColor  ( PALETTEINDEX( 1 ) );
            }


        BitBlt(pDC->m_hDC, destRect.left   , destRect.top,
                           destRect.Width(), destRect.Height(),
              m_pImg->hDC,  srcRect.left   , srcRect.top, SRCCOPY);

        return;
        }

    CDC tempDC;
    CBitmap      tempBitmap;
    CBitmap* pOldTempBitmap;

    if (! tempDC.CreateCompatibleDC(pDC)
    ||  ! tempBitmap.CreateCompatibleBitmap(pDC, destRect.Width() + 1,
                                                 destRect.Height() + 1))
        {
        theApp.SetGdiEmergency(FALSE);
        return;
        }

    pOldTempBitmap = tempDC.SelectObject(&tempBitmap);

    ASSERT(pOldTempBitmap != NULL);

    CPalette* pOldPalette = SetImgPalette( &tempDC, FALSE ); // Background ??

    // If we're zoomed in, use COLORONCOLOR for easy pixel-by-pixel editing
    // Otherwise use HALFTONE for nice appearance
    if (m_nZoom < 2)
    {
        tempDC.SetStretchBltMode(HALFTONE);
    }
    else
    {
        tempDC.SetStretchBltMode(COLORONCOLOR);
    }


    if (m_pImg->cPlanes * m_pImg->cBitCount == 1)
        {
        tempDC.SetTextColor( RGB( 0x00, 0x00, 0x00 ));
        tempDC.SetBkColor  ( RGB( 0xFF, 0xFF, 0xFF ));
        }

    // Bitmaps...
    StretchCopy(tempDC.m_hDC, 0, 0, destRect.Width(), destRect.Height(),
                 m_pImg->hDC,       srcRect.left, srcRect.top,
                                    srcRect.Width(), srcRect.Height());
    // Draw the grid...
    if (IsGridVisible() && bDoGrid)
        DrawGrid( &tempDC, srcRect, destRect );

    // Transfer to the screen...
    pDC->BitBlt(destRect.left, destRect.top, destRect.Width(),
                               destRect.Height(), &tempDC, 0, 0, SRCCOPY);
    // Cleanup...
    if (pOldPalette)
        tempDC.SelectPalette( pOldPalette, FALSE ); // Background ??

    tempDC.SelectObject(pOldTempBitmap);
    }

/***************************************************************************/
// Draw a border and bevel around the image and fill the rest of
// the window with gray.  If pPaintRect is not NULL, painting is
// optimized to only draw with the rectangle.
//
void CImgWnd::DrawBackground(CDC* pDC, const CRect* pPaintRect)
    {
    ASSERT( pDC != NULL );

    CRect clientRect;

    if (pPaintRect == NULL)
        {
        // Draw everything...
        GetClientRect( &clientRect );
        pPaintRect = &clientRect;
        }

    CRect srcRect;
    CRect imageRect;

    GetDrawRects(pPaintRect, NULL, srcRect, imageRect);

    // Erase area around image, border, and bevel...
    CBrush* pOldBrush = pDC->SelectObject( GetSysBrush( COLOR_APPWORKSPACE ) );

    if (imageRect.top > pPaintRect->top)
        {
        // Top...

        pDC->PatBlt(pPaintRect->left, pPaintRect->top, pPaintRect->Width(),
                      imageRect.top - pPaintRect->top, PATCOPY);
        }

    if (imageRect.left > pPaintRect->left)
        {
        // Left...

        pDC->PatBlt(pPaintRect->left, imageRect.top,
            imageRect.left - pPaintRect->left, imageRect.Height(), PATCOPY);
        }

    if (imageRect.right < pPaintRect->right)
        {
        // Right...

        pDC->PatBlt(imageRect.right, imageRect.top,
            pPaintRect->right - imageRect.right, imageRect.Height(), PATCOPY);
        }

    if (imageRect.bottom < pPaintRect->bottom)
        {
        // Bottom...

        pDC->PatBlt(pPaintRect->left, imageRect.bottom, pPaintRect->Width(),
            pPaintRect->bottom - imageRect.bottom, PATCOPY);
        }

    pDC->SelectObject(pOldBrush);
    }

/***************************************************************************/

void CImgWnd::SetImg(IMG* pImg)
    {
    m_pNextImgWnd = pImg->m_pFirstImgWnd;
    pImg->m_pFirstImgWnd = this;
    m_pImg = pImg;
    }

/***************************************************************************/

CPalette* CImgWnd::SetImgPalette( CDC* pdc, BOOL bForce )
    {
    CPalette* ppal = NULL;

        // If we do not realize as a background brush when in-place, we can get
        // an infinite recursion of the container and us trying to realize the
        // palette
        if (theApp.m_pwndInPlaceFrame)
        {
                bForce = TRUE;
        }

    if (theApp.m_pPalette
    &&  theApp.m_pPalette->m_hObject)
        {
        ppal = pdc->SelectPalette( theApp.m_pPalette, bForce );

        pdc->RealizePalette();
        }
    return ppal;
    }

/***************************************************************************/

HPALETTE CImgWnd::SetImgPalette( HDC hdc, BOOL bForce )
    {
    HPALETTE hpal = NULL;

        // If we do not realize as a background brush when in-place, we can get
        // an infinite recursion of the container and us trying to realize the
        // palette
        if (theApp.m_pwndInPlaceFrame)
        {
                bForce = TRUE;
        }

    if (theApp.m_pPalette
    &&  theApp.m_pPalette->m_hObject)
        {
        hpal = ::SelectPalette( hdc, (HPALETTE)theApp.m_pPalette->m_hObject, bForce );

        ::RealizePalette( hdc );
        }
    return hpal;
    }

/***************************************************************************/

void CImgWnd::SetZoom(int nZoom)
    {
    if (m_nZoom > 1)
        m_nZoomPrev = m_nZoom;

    CommitSelection(TRUE);

    if (nZoom > 1)
        {
        // deselect the text tool if it's around
        CImgTool* pImgTool = CImgTool::GetCurrent();

        if (pImgTool != NULL && CImgTool::GetCurrentID() == IDMX_TEXTTOOL)
            {
            CImgTool::Select(IDMB_PENCILTOOL);
            }
        }

    HideBrush();
    SetupRubber( m_pImg );
    EraseTracker();
    theImgBrush.m_pImg = NULL;
    DrawTracker();

    CPBView* pView = (CPBView*)GetParent();

    if (pView != NULL && pView->IsKindOf( RUNTIME_CLASS( CPBView ) ))
        if (nZoom == 1)
            pView->HideThumbNailView();
        else
            pView->ShowThumbNailView();

        Invalidate(FALSE);

    m_nZoom = nZoom;
    }

/***************************************************************************/

void CImgWnd::SetScroll(int xPos, int yPos)
    {
    if (xPos > 0)
        xPos = 0;
    else
        if (xPos < -m_pImg->cxWidth)
            xPos = -m_pImg->cxWidth;

    if (yPos > 0)
        yPos = 0;
    else
        if (yPos < -m_pImg->cyHeight)
            yPos = -m_pImg->cyHeight;

    m_xScroll = xPos;
    m_yScroll = yPos;

    Invalidate( FALSE );

    CheckScrollBars();
    }

/***************************************************************************/

void CImgWnd::CheckScrollBars()
    {
    // Tacky recursion blocker is required because this is called from
    // the OnSize handler and turning scroll bars on or off changes
    // the size of our window...
    static BOOL  bInHere = FALSE;

    if (bInHere)
        return;

    bInHere = TRUE;

    int cxVScrollBar = GetSystemMetrics( SM_CXVSCROLL );
    int cyHScrollBar = GetSystemMetrics( SM_CYHSCROLL );

    // Figure the client area size if there were no scroll bars...
    CRect clientRect;

    GetClientRect( &clientRect );

    int cxWidth  = clientRect.Width();
    int cyHeight = clientRect.Height();

    BOOL hHasHBar = ((GetStyle() & WS_HSCROLL) != 0);
    BOOL bHasVBar = ((GetStyle() & WS_VSCROLL) != 0);

    if (hHasHBar)
        cyHeight += cyHScrollBar;

    if (bHasVBar)
        cxWidth += cxVScrollBar;

    // Figure the size of the thing we are scrolling (the subject)...
    CSize subjectSize;

    GetImgSize( m_pImg, subjectSize );

    int iTrackerSize = 2 * CTracker::HANDLE_SIZE;

    subjectSize.cx = (subjectSize.cx * m_nZoom ) + iTrackerSize;
    subjectSize.cy = (subjectSize.cy * m_nZoom ) + iTrackerSize;

    m_LineX = (subjectSize.cx + 31) / 32;
    m_LineY = (subjectSize.cy + 31) / 32;

    // Nasty loop takes care of case where we only need a vertical
    // scroll bar because we added a horizontal scroll bar and
    // vice versa...  (Will only ever loop twice.)
    BOOL bNeedHBar = FALSE;
    BOOL bNeedVBar = FALSE;
    BOOL bChange;

    do  {
        bChange = FALSE;

        if (! bNeedVBar && subjectSize.cy > cyHeight)
            {
            bChange   = TRUE;
            bNeedVBar = TRUE;
            cxWidth  -= cxVScrollBar;
            }

        if (! bNeedHBar && subjectSize.cx > cxWidth)
            {
            bChange   = TRUE;
            bNeedHBar = TRUE;
            cyHeight -= cyHScrollBar;
            }
        } while (bChange);

    SetRedraw( FALSE );

    SCROLLINFO si;

    si.cbSize = sizeof( si );
    si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
    si.nMin   = 0;

    // We subtract 1 because subjectSize is the size we want, so the range
    // should be 0 to subjectSize-1
    si.nMax   = (subjectSize.cx - 1) / m_nZoom;
    si.nPage  = cxWidth / m_nZoom;
    si.nPos   = -m_xScroll;
    SetScrollInfo( SB_HORZ, &si, FALSE );

    si.nMax   = (subjectSize.cy - 1) / m_nZoom;
    si.nPage  = cyHeight / m_nZoom;
    si.nPos   = -m_yScroll;
    SetScrollInfo( SB_VERT, &si, FALSE );

        si.fMask = SIF_POS;
        GetScrollInfo( SB_HORZ, &si );
        if ( -m_xScroll != si.nPos )
                m_xScroll = -si.nPos ;
        GetScrollInfo( SB_VERT, &si );
        if ( -m_yScroll != si.nPos )
                m_yScroll = -si.nPos;

    SetRedraw ( TRUE  );
    Invalidate( FALSE );

    bInHere = FALSE;
    }

/***************************************************************************/

void CImgWnd::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar*)
    {
    OnScroll(FALSE, nSBCode, nPos);
    }

/***************************************************************************/

void CImgWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar*)
    {
    OnScroll(TRUE, nSBCode, nPos);
    }

/***************************************************************************/

void CImgWnd::OnScroll(BOOL bVert, UINT nSBCode, UINT nPos)
    {
    SCROLLINFO ScrollInfo;

    ScrollInfo.cbSize = sizeof( ScrollInfo );
    ScrollInfo.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;

    GetScrollInfo( (bVert? SB_VERT: SB_HORZ), &ScrollInfo );
        int iScroll = ScrollInfo.nPage/4;
    int iNewPos = ScrollInfo.nPos;

    switch (nSBCode)
        {
        case SB_TOP:
            iNewPos = 0;
            break;

        case SB_BOTTOM:
            iNewPos = ScrollInfo.nMax;
            break;

        case SB_LINEDOWN:
            iNewPos += iScroll;
            break;

        case SB_LINEUP:
            iNewPos -= iScroll;
            break;

        case SB_PAGEDOWN:
            iNewPos += iScroll * 4;
            break;

        case SB_PAGEUP:
            iNewPos -= iScroll * 4;
            break;

        case SB_THUMBPOSITION:
        case SB_THUMBTRACK:
            iNewPos = nPos;
            break;
        }

    if (iNewPos < ScrollInfo.nMin)
        iNewPos = 0;
    else
        if (iNewPos > ScrollInfo.nMax-(int)ScrollInfo.nPage+1)
            iNewPos = ScrollInfo.nMax-(int)ScrollInfo.nPage+1;

    iScroll = -(iNewPos - ScrollInfo.nPos);
        Invalidate(FALSE);

    if (bVert)
        m_yScroll = -iNewPos;
    else
        m_xScroll = -iNewPos;

    ScrollInfo.fMask = SIF_POS;
    ScrollInfo.nPos  = iNewPos;
    SetScrollInfo( (bVert? SB_VERT: SB_HORZ), &ScrollInfo, TRUE );
    }

/***************************************************************************/
BOOL CImgWnd::OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
    {
    //
    // Don't handle zoom and datazoom.
    //

    if (nFlags & (MK_SHIFT | MK_CONTROL))
        {
        return FALSE;
        }

    int nBar;
    int *pScroll;

    if (GetWindowLong(GetSafeHwnd(), GWL_STYLE) & WS_VSCROLL)
    {
        nBar = SB_VERT;
        pScroll = &m_yScroll;
    }
    else
    {
        nBar = SB_HORZ;
        pScroll = &m_xScroll;
    }

    SCROLLINFO ScrollInfo;

    ScrollInfo.cbSize = sizeof( ScrollInfo );
    ScrollInfo.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;

    GetScrollInfo( nBar, &ScrollInfo );
    m_WheelDelta -= zDelta;
    if (abs(m_WheelDelta) >= WHEEL_DELTA)
        {
        int iScroll = ScrollInfo.nPage/4 * (m_WheelDelta/WHEEL_DELTA);
        int iNewPos = ScrollInfo.nPos + iScroll;


        if (iNewPos < ScrollInfo.nMin)
           iNewPos = 0;
        else if (iNewPos > ScrollInfo.nMax-(int)ScrollInfo.nPage+1)
           iNewPos = ScrollInfo.nMax-(int)ScrollInfo.nPage+1;

        Invalidate(FALSE);

        *pScroll = -iNewPos;

        ScrollInfo.fMask = SIF_POS;
        ScrollInfo.nPos  = iNewPos;
        SetScrollInfo( nBar, &ScrollInfo, TRUE );
        m_WheelDelta= m_WheelDelta % WHEEL_DELTA;
        CImgTool* pImgTool = CImgTool::GetCurrent();
        mti.ptPrev = mti.pt;
        mti.pt = pt;
        pImgTool->OnMove (this, &mti);
    }


    return TRUE;
    }
/***************************************************************************/

void CImgWnd::PrepareForBrushChange(BOOL bPickup, BOOL bErase)
    {
    if (theImgBrush.m_pImg != NULL
    &&  theImgBrush.m_bFirstDrag)
        {
        if (bPickup)
            PickupSelection();

        SetUndo(m_pImg);

        theImgBrush.m_bLastDragWasFirst = TRUE;
        theImgBrush.m_bFirstDrag        = FALSE;
        theImgBrush.m_rcDraggedFrom     = rcDragBrush;

        if (CImgTool::GetCurrentID() == IDMX_TEXTTOOL)
            {
            HideBrush();
            bErase = FALSE;
            }

        if (bErase)
            {
            // Clear the background...
            HideBrush();

            FillImgRect( m_pImg->hDC, &theImgBrush.m_rcDraggedFrom, crRight );

            CommitImgRect(m_pImg, &theImgBrush.m_rcDraggedFrom);
            InvalImgRect (m_pImg, &theImgBrush.m_rcDraggedFrom);

            FinishUndo(theImgBrush.m_rcDraggedFrom);

            MoveBrush(theImgBrush.m_rcSelection);
            }
        }
    }

/***************************************************************************/

void CImgWnd::OnCancelMode()
    {
    CmdCancel();
    }

/***************************************************************************/

void CImgWnd::CmdCancel()
    {
    // This will:
    //  Erase the size indicator on the status bar.
    //  Reset the mouse cursor to an arrow.
    //  Release the capture.
    //  Cancel (and undo) any drawing operation in progress.
    //  Cancel the Pick Color command if it's active.
    //  If there's a selection, will set to whole image and select prev tool
    ClearStatusBarSize();

    mti.fLeft = mti.fRight = FALSE;

    if (c_pResizeDragger != NULL)
        {
        EndResizeOperation();
        bIgnoreMouse = TRUE;
        return;
        }

    CImgTool* pImgTool = CImgTool::GetCurrent();

    if (GetCapture() == this || pImgTool->IsMultPtOpInProgress())
        {
        // Cancel dragging or multi-point operation in progress
        BOOL bWasMakingSelection = theImgBrush.m_bMakingSelection;

        ZoomedInDP(WM_CANCEL, 0, CPoint(0, 0));

        SetCursor(LoadCursor(NULL, IDC_ARROW + 11));

        if (! bWasMakingSelection)
            CancelPainting();

        bIgnoreMouse = TRUE;
        }
    else
        if (pImgTool->IsToggle()
        ||  CImgTool::GetCurrentID() == IDMB_PICKTOOL
        ||  CImgTool::GetCurrentID() == IDMB_PICKRGNTOOL
        ||  CImgTool::GetCurrentID() == IDMZ_BRUSHTOOL
        ||  CImgTool::GetCurrentID() == IDMB_POLYGONTOOL
        ||  CImgTool::GetCurrentID() == IDMX_TEXTTOOL)
            {
            pImgTool->OnCancel( this );
            }

    if (GetKeyState( VK_LBUTTON ) < 0 || GetKeyState( VK_RBUTTON) < 0 )
        bIgnoreMouse = TRUE;

    SetToolCursor();
    }

/***************************************************************************/

void CImgWnd::CmdSel2Bsh()
    {
    if (! g_bCustomBrush)
        {
        if (theImgBrush.m_pImg == NULL)
            {
            // No selection, turn the whole image into a brush!
            MakeBrush( m_pImg->hDC, CRect( 0, 0, m_pImg->cxWidth,
                                                 m_pImg->cyHeight ) );
            }

        if (theImgBrush.m_bFirstDrag)
            {
            // Time to pick up the bits!
            ASSERT(theImgBrush.m_pImg == m_pImg);

            PickupSelection();
            }

        InvalImgRect(theImgBrush.m_pImg, NULL); // erase the selection tracker
        CImgTool::Select(IDMZ_BRUSHTOOL);
        SetCombineMode(combineMatte);

        g_bCustomBrush = TRUE;

        theImgBrush.m_pImg = NULL;
        theImgBrush.CenterHandle();
        }
    else
        if (CImgTool::GetCurrentID() == IDMZ_BRUSHTOOL)
            {
            CImgTool::GetCurrent()->OnCancel(this);
            }
    }

/***************************************************************************/
// Coordinate Translation and Calculation Functions
//
//
// Convert a point or rect in image view client coordinates to image
// coordinates taking magnification and scrolling into account.
//
void CImgWnd::ClientToImage(CPoint& point)
    {
    int iHandleSize = CTracker::HANDLE_SIZE;

    point.x = (point.x - iHandleSize) / m_nZoom - m_xScroll;
    point.y = (point.y - iHandleSize) / m_nZoom - m_yScroll;
    }

/***************************************************************************/

void CImgWnd::ClientToImage(CRect& rect)
    {
    ClientToImage(rect.TopLeft());
    ClientToImage(rect.BottomRight());
    }

/***************************************************************************/
// Convert a point or rect in image coordinates to image view client
// coordinates taking magnification and scrolling into account.
//
void CImgWnd::ImageToClient(CPoint& point)
    {
    int iHandleSize = CTracker::HANDLE_SIZE;

    point.x = (point.x + m_xScroll) * m_nZoom + iHandleSize;
    point.y = (point.y + m_yScroll) * m_nZoom + iHandleSize;
    }

/***************************************************************************/

void CImgWnd::ImageToClient(CRect& rect)
    {
    ImageToClient(rect.TopLeft());
    ImageToClient(rect.BottomRight());
    }

/***************************************************************************/
// Return a rectangle in image view coordinates surrounding the image
// taking magnification, scrolling, and the grid into account.

void CImgWnd::GetImageRect( CRect& imageRect )
    {
    imageRect.SetRect( 0, 0, m_pImg->cxWidth, m_pImg->cyHeight );

    ImageToClient( imageRect );

    if (IsGridVisible())
        {
        imageRect.right  += 1;
        imageRect.bottom += 1;
        }
    }

/***************************************************************************/

CRect CImgWnd::GetDrawingRect( void )
    {
    CRect rectImage;
    CRect rectClient;

    GetImageRect (  rectImage );
    GetClientRect( &rectClient );

    rectImage &= rectClient;
    rectImage.InflateRect( CTracker::HANDLE_SIZE, CTracker::HANDLE_SIZE );

    return ( rectImage );
    }

/***************************************************************************/

void CImgWnd::OnSetFocus(CWnd* pOldWnd)
    {
    if (m_pImg == NULL)
        {
        // Time to die...  (Our img was deleted, so we'll be disappearing
        // soon.  Don't bother to do any of the rest of this function...
        return;
        }

        Invalidate();
        BringWindowToTop(); // so updates happen here first

    if (c_pImgWndCur != this
    &&  c_pImgWndCur != NULL)
        c_pImgWndCur->EraseTracker();

    c_pImgWndCur = this;

    SelectImg( m_pImg );
        UpdateWindow();

    CWnd::OnSetFocus( pOldWnd );

    DrawTracker();
    }

/***************************************************************************/

void CImgWnd::OnKillFocus(CWnd* pNewWnd)
    {
        Invalidate();

    if (theImgBrush.m_pImg == NULL)
        HideBrush();

    if (GetCapture() == this)
        CmdCancel();

    CWnd::OnKillFocus(pNewWnd);
    }

/***************************************************************************/

void CImgWnd::OnSize(UINT nType, int cx, int cy)
    {
    CheckScrollBars();

    CWnd::OnSize(nType, cx, cy);
    }

/***************************************************************************/

BOOL CImgWnd::OnMouseDown(UINT nFlags)
    {
    if (GetFocus() != this)
        {
        SetFocus();
        SetActiveWindow();
        }

    if ((nFlags & (MK_LBUTTON | MK_RBUTTON)) == (MK_LBUTTON | MK_RBUTTON))
        {
        ClearStatusBarSize();

        BOOL bWasMakingSelection = theImgBrush.m_bMakingSelection;

        ZoomedInDP(WM_CANCEL, 0, CPoint(0, 0));

        SetCursor(LoadCursor(NULL, IDC_ARROW + 11));

        if (! bWasMakingSelection)
            CancelPainting();

        bIgnoreMouse = TRUE;
        return FALSE;
        }

    return TRUE;
    }

/***************************************************************************/

BOOL CImgWnd::OnMouseMessage( UINT nFlags )
    {
    if (bIgnoreMouse /*|| GetFocus() != this*/)
        {
        if ((nFlags & (MK_LBUTTON | MK_RBUTTON)) == 0)
            {
            bIgnoreMouse = FALSE;
            SetToolCursor();
            }
        else
            {
            SetCursor( LoadCursor( NULL, IDC_ARROW ) );
            }

        return FALSE;
        }

    if ((CImgTool::GetCurrentID() != IDMB_PICKTOOL)
    &&  (CImgTool::GetCurrentID() != IDMB_PICKRGNTOOL))
        SetupRubber(m_pImg);

    const MSG* pMsg = GetCurrentMessage();
    mti.fCtrlDown = (nFlags & MK_CONTROL);
    ZoomedInDP( pMsg->message, (DWORD)pMsg->wParam, CPoint( (DWORD)pMsg->lParam ) );

    return TRUE;
    }

/***************************************************************************/

void CImgWnd::OnLButtonDown( UINT nFlags, CPoint point )
    {
    CWnd::OnLButtonDown( nFlags, point );

    if (OnMouseDown( nFlags ))
        {
        OnMouseMessage( nFlags );
        }
    }

/***************************************************************************/

void CImgWnd::OnLButtonDblClk(UINT nFlags, CPoint point)
    {
    CRect rect;
    GetImageRect(rect);

    // When inside the image, a double click is the same as a single click
    OnLButtonDown(nFlags, point);
    }

/***************************************************************************/

void CImgWnd::OnLButtonUp(UINT nFlags, CPoint point)
    {
    CWnd::OnLButtonUp(nFlags, point);

    OnMouseMessage(nFlags);
    }

/***************************************************************************/

void CImgWnd::OnRButtonDown(UINT nFlags, CPoint point)
    {
    CWnd::OnRButtonDown(nFlags, point);

    if (OnMouseDown(nFlags))
        {
        OnMouseMessage(nFlags);
        }
    }


/***************************************************************************/

void CImgWnd::OnRButtonDblClk(UINT nFlags, CPoint point)
    {
    // A right button double click is the same as a right button single click
    OnRButtonDown(nFlags, point);
    }


/***************************************************************************/

void CImgWnd::OnRButtonUp(UINT nFlags, CPoint point)
    {
    CWnd::OnRButtonUp(nFlags, point);

    OnMouseMessage(nFlags);
    }


/***************************************************************************/

void CImgWnd::OnMouseMove(UINT nFlags, CPoint point)
    {
    CWnd::OnMouseMove(nFlags, point);

    if (g_pMouseImgWnd != this
    &&  g_pMouseImgWnd != NULL)
        {
        CImgTool::GetCurrent()->OnLeave( g_pMouseImgWnd, &mti );
        g_pMouseImgWnd = NULL;
        }

    ClientToImage( point );
    m_ptDispPos = point;

    if (g_pMouseImgWnd == NULL)
        {
        MTI mtiEnter;

        mtiEnter.pt        = point;
        mtiEnter.ptDown    = point;
        mtiEnter.ptPrev    = point;
        mtiEnter.fLeft     = FALSE;
        mtiEnter.fRight    = FALSE;
        mtiEnter.fCtrlDown = FALSE;

        CImgTool::GetCurrent()->OnEnter( g_pMouseImgWnd, &mtiEnter );

        g_pMouseImgWnd  = this;
        }
    OnMouseMessage( nFlags );
    }

/***************************************************************************/

void CImgWnd::OnTimer(UINT nIDEvent)
    {
    OnMouseMessage( 0 );
    }

/***************************************************************************/

void CImgWnd::SetToolCursor()
    {
    UINT nCursorID = CImgTool::GetCurrent()->GetCursorID();
    HCURSOR hCursor = NULL;

    if (nCursorID != 0)
        {
        hCursor = LoadCursor(nCursorID < 32512 ?
            AfxGetResourceHandle() : NULL, MAKEINTRESOURCE( nCursorID ));
        }

    SetCursor(hCursor);
    }


/***************************************************************************/

void CImgWnd::EndResizeOperation()
    {
    ReleaseCapture();
    delete c_pResizeDragger;
    c_pResizeDragger = NULL;
    c_dragState = CTracker::nil;
    ClearStatusBarSize();
    }


/***************************************************************************/

void CImgWnd::ResizeMouseHandler(unsigned code, CPoint imagePt)
    {
    CRect imageRect = c_pResizeDragger->m_rect;
    ClientToImage(imageRect);

    switch (code)
        {
        case WM_CANCEL:
            EndResizeOperation();
            return;

        case WM_LBUTTONUP:
            // resizing whole bitmap
            if  (m_pImg != theImgBrush.m_pImg
            &&   m_pwndThumbNailView)
                {
                m_pwndThumbNailView->Invalidate();
                }

            EndResizeOperation();

            if (theImgBrush.m_pImg == NULL)
                {
                // User was resizing the whole image...
                CPBView* pView = (CPBView*)((CFrameWnd*)AfxGetMainWnd())->GetActiveView();
                CPBDoc*  pDoc  = (pView == NULL)? NULL: pView->GetDocument();

                if (pDoc != NULL)
                    {
                    theUndo.BeginUndo( TEXT("Property Edit") );

                    if (GetKeyState( VK_SHIFT ) < 0)
                        pDoc->m_pBitmapObj->SetIntProp( P_Shrink, 1 );

                    theApp.m_sizeBitmap = imageRect.Size();

                    pDoc->m_pBitmapObj->SetSizeProp( P_Size, theApp.m_sizeBitmap );
                    pDoc->m_pBitmapObj->SetIntProp ( P_Shrink, 0 );

                    theUndo.EndUndo();
                    }
                }
            else
                {
                // User was resizing the selection...
                HideBrush();
                theImgBrush.SetSize( imageRect.Size(), TRUE );
                MoveBrush( imageRect );
                }
            return;

        case WM_MOUSEMOVE:
            switch (c_dragState)
                {
                default:
                    ASSERT(FALSE);

                case CTracker::resizingTop:
                    imageRect.top = imagePt.y;
                    if (imageRect.top >= imageRect.bottom)
                        imageRect.top = imageRect.bottom - 1;
                    break;

                case CTracker::resizingLeft:
                    imageRect.left = imagePt.x;
                    if (imageRect.left >= imageRect.right)
                        imageRect.left = imageRect.right - 1;
                    break;

                case CTracker::resizingRight:
                    imageRect.right = imagePt.x;
                    if (imageRect.right <= imageRect.left)
                        imageRect.right = imageRect.left + 1;
                    break;

                case CTracker::resizingBottom:
                    imageRect.bottom = imagePt.y;
                    if (imageRect.bottom <= imageRect.top)
                        imageRect.bottom = imageRect.top + 1;
                    break;

                case CTracker::resizingTopLeft:
                    imageRect.left = imagePt.x;
                    imageRect.top = imagePt.y;
                    if (imageRect.top >= imageRect.bottom)
                        imageRect.top = imageRect.bottom - 1;
                    if (imageRect.left >= imageRect.right)
                        imageRect.left = imageRect.right - 1;
                    break;

                case CTracker::resizingTopRight:
                    imageRect.top = imagePt.y;
                    imageRect.right = imagePt.x;
                    if (imageRect.top >= imageRect.bottom)
                        imageRect.top = imageRect.bottom - 1;
                    if (imageRect.right <= imageRect.left)
                        imageRect.right = imageRect.left + 1;
                    break;

                case CTracker::resizingBottomLeft:
                    imageRect.left = imagePt.x;
                    imageRect.bottom = imagePt.y;
                    if (imageRect.left >= imageRect.right)
                        imageRect.left = imageRect.right - 1;
                    if (imageRect.bottom <= imageRect.top)
                        imageRect.bottom = imageRect.top + 1;
                    break;

                case CTracker::resizingBottomRight:
                    imageRect.right = imagePt.x;
                    imageRect.bottom = imagePt.y;
                    if (imageRect.right <= imageRect.left)
                        imageRect.right = imageRect.left + 1;
                    if (imageRect.bottom <= imageRect.top)
                        imageRect.bottom = imageRect.top + 1;
                    break;
                }

            if (theImgBrush.m_pImg == NULL && m_pImg->m_bTileGrid)
                {
                // Snap to tile grid...

                int cxTile = m_pImg->m_cxTile;
                if (cxTile != 1 && cxTile <= m_pImg->cxWidth)
                    {
                    imageRect.right = ((imageRect.right + cxTile / 2) /
                        cxTile) * cxTile;
                    }

                int cyTile = m_pImg->m_cyTile;
                if (cyTile != 1 && cyTile <= m_pImg->cyHeight)
                    {
                    imageRect.bottom = ((imageRect.bottom + cyTile / 2) /
                        cyTile) * cyTile;
                    }
                }

            SetStatusBarSize(imageRect.Size());

            ImageToClient(imageRect);

            c_pResizeDragger->Move(imageRect, TRUE);
            break;
        }
    }


/***************************************************************************/

void CImgWnd::StartSelectionDrag(unsigned code, CPoint newPt)
    {
    theImgBrush.CopyTo(theBackupBrush);

    newPt.x /= m_nZoom;
    newPt.y /= m_nZoom;

    mti.pt = mti.ptDown = mti.ptPrev = newPt;

    SetCapture();
    SetCombineMode(theImgBrush.m_bOpaque ? combineReplace : combineMatte);

    if (theImgBrush.m_bFirstDrag)
        {
        ASSERT(theImgBrush.m_pImg == m_pImg);

        PickupSelection();
        }
    else
        if (! theImgBrush.m_bOpaque)
            theImgBrush.RecalcMask( crRight );

    theImgBrush.TopLeftHandle();
    theImgBrush.m_dragOffset = mti.pt - theImgBrush.m_rcSelection.TopLeft();

    EraseTracker();

    if (GetKeyState(VK_CONTROL) < 0)
        {
        // Copy the selection and start moving...

        if (theImgBrush.m_bFirstDrag)
            {
            // The first time, the bits are already in
            // the bitmap, so just copy them to the
            // selection (which has already been done).

            theImgBrush.m_bFirstDrag = FALSE;
            theImgBrush.m_bLastDragWasFirst = TRUE;
            }
        else
            {
            CommitSelection(TRUE);
            }

        theImgBrush.m_bMoveSel = TRUE;
        }
    else
        if (GetKeyState(VK_SHIFT) < 0)
            {
            // Start a smear operation...
            HideBrush();

            if (theImgBrush.m_bLastDragWasFirst)
                CommitSelection(TRUE);

            SetUndo(m_pImg);
            theImgBrush.m_bSmearSel = TRUE;
            theImgBrush.m_bFirstDrag = FALSE;
            theImgBrush.m_bLastDragWasFirst = TRUE;
            }
        else
            {
            // Start a move operation...
            theImgBrush.m_bMoveSel = TRUE;
            }

    g_bCustomBrush = TRUE;
    }

/***************************************************************************/

void CImgWnd::CancelSelectionDrag()
    {
    if (!theImgBrush.m_bSmearSel && !theImgBrush.m_bMoveSel)
        {
        TRACE(TEXT("Extraneous CancelSelectionDrag!\n"));
        return;
        }

    ReleaseCapture();

    theImgBrush.m_rcSelection = theImgBrush.m_rcDraggedFrom;

    theImgBrush.m_bMoveSel = theImgBrush.m_bSmearSel = FALSE;
    g_bCustomBrush = FALSE;
    SetCombineMode(combineColor);

    theBackupBrush.CopyTo(theImgBrush);
    rcDragBrush = theImgBrush.m_rcSelection;
    rcDragBrush.right += 1;
    rcDragBrush.bottom += 1;

    CancelPainting();

    InvalImgRect(theImgBrush.m_pImg, NULL); // draw selection tracker

    // "Opaque" mode may have changed...
    if ((CImgTool::GetCurrentID() == IDMB_PICKTOOL)
    ||  (CImgTool::GetCurrentID() == IDMB_PICKRGNTOOL))
        g_pImgToolWnd->InvalidateOptions(FALSE);

    // Cancel all the way now...
    theImgBrush.m_pImg = NULL;
    }


/***************************************************************************/

void CImgWnd::SelectionDragHandler(unsigned code, CPoint newPt)
    {
    switch (code)
        {
        case WM_CANCEL:
            CancelSelectionDrag();
            break;

        case WM_MOUSEMOVE:
            if (theImgBrush.m_bMoveSel)
                PrepareForBrushChange(FALSE);

            mti.ptPrev = mti.pt;
            mti.pt = newPt;

            theImgBrush.m_rcSelection.OffsetRect(
                        -theImgBrush.m_rcSelection.TopLeft()
                           + (CSize)mti.pt - theImgBrush.m_dragOffset);

            // Make sure the selection stays at least along the edge
            // of the actual image so we don't lose the tracker...
            if (theImgBrush.m_rcSelection.left > m_pImg->cxWidth)
                theImgBrush.m_rcSelection.OffsetRect(-theImgBrush.m_rcSelection.left + m_pImg->cxWidth, 0);
            if (theImgBrush.m_rcSelection.top > m_pImg->cyHeight)
                theImgBrush.m_rcSelection.OffsetRect(0, -theImgBrush.m_rcSelection.top + m_pImg->cyHeight);
            if (theImgBrush.m_rcSelection.right < 0)
                theImgBrush.m_rcSelection.OffsetRect(-theImgBrush.m_rcSelection.right, 0);
            if (theImgBrush.m_rcSelection.bottom < 0)
                theImgBrush.m_rcSelection.OffsetRect(0, -theImgBrush.m_rcSelection.bottom);

            if (theImgBrush.m_bSmearSel)
                DrawBrush(m_pImg, theImgBrush.m_rcSelection.TopLeft(), TRUE);
            else
                ShowBrush(theImgBrush.m_rcSelection.TopLeft());
            break;

        case WM_LBUTTONUP:
            theImgBrush.m_bLastDragWasASmear = theImgBrush.m_bSmearSel;

            if (theImgBrush.m_bSmearSel)
                {
                IMG* pImg = m_pImg;

                CommitSelection(FALSE);

                FinishUndo(CRect(0, 0, pImg->cxWidth, pImg->cyHeight));
                }
            ReleaseCapture();

            theImgBrush.m_bMoveSel = theImgBrush.m_bSmearSel = FALSE;

            g_bCustomBrush = FALSE;
            SetCombineMode(combineColor);

            InvalImgRect(theImgBrush.m_pImg, NULL); // draw selection tracker
            break;
        }
    }

/******************************************************************************/

BOOL CImgWnd::PtInTracker( CPoint cptLocation )
    {
    CRect selRect = theImgBrush.m_rcSelection;
    BOOL  bPtInTracker = FALSE;

    selRect.left   *= m_nZoom;
    selRect.top    *= m_nZoom;
    selRect.right  *= m_nZoom;
    selRect.bottom *= m_nZoom;

    selRect.InflateRect( CTracker::HANDLE_SIZE, CTracker::HANDLE_SIZE );

    bPtInTracker = selRect.PtInRect( cptLocation );

    return bPtInTracker;
    }

/******************************************************************************/

void CImgWnd::OnRButtonDownInSel(CPoint *pcPointDown)
    {
    CMenu cMenuPopup;
    BOOL bRC = cMenuPopup.LoadMenu(IDR_SELECTION_POPUP);

    ASSERT(bRC);
    if (bRC)
        {
        CMenu *pcContextMenu = cMenuPopup.GetSubMenu(0);
        ASSERT(pcContextMenu != NULL);
        if (pcContextMenu != NULL)
            {
            CPoint cPointDown = *pcPointDown;
            ImageToClient(cPointDown);
            ClientToScreen(&cPointDown);

            CRect cRectClient;
            GetClientRect(&cRectClient);
            ClientToScreen(&cRectClient);

            // the frame actually has a clue about what items to enable...
            CWnd *notify = GetParentFrame();

            if (!notify)
                notify = GetParent(); // oh well...

            pcContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
                cPointDown.x, cPointDown.y, notify, &cRectClient);
            }
        }
    }

/***************************************************************************/

void CImgWnd::ZoomedInDP(unsigned code, unsigned mouseKeys, CPoint newPt)
    {
    CPoint clientPt = newPt;
    CPoint imagePt  = clientPt;

    ClientToImage( imagePt );

    if (c_pResizeDragger != NULL)
        {
        ResizeMouseHandler( code, imagePt );
        return;
        }

    int iHandleSize = CTracker::HANDLE_SIZE;

    newPt.x -= iHandleSize + m_xScroll * m_nZoom;
    newPt.y -= iHandleSize + m_yScroll * m_nZoom;

//  AdjustPointForGrid(&newPt);

    IMG* pImg = m_pImg;

    int cxImage = pImg->cxWidth;
    int cyImage = pImg->cyHeight;

    CRect imageRect;

    GetImageRect( imageRect );

    // Check for selection manipulations...
    if (GetCapture() != this
    &&  c_pImgWndCur == this
    &&  theImgBrush.m_pImg == m_pImg)
        {
        CRect selRect = theImgBrush.m_rcSelection;
        BOOL bPtInTracker = FALSE;

        selRect.left   *= m_nZoom;
        selRect.top    *= m_nZoom;
        selRect.right  *= m_nZoom;
        selRect.bottom *= m_nZoom;

        selRect.InflateRect( CTracker::HANDLE_SIZE, CTracker::HANDLE_SIZE );

        bPtInTracker = PtInTracker(newPt);

        if (bPtInTracker)
            {
            // Mouse is within the outer border of the tracker...


            // We don't set the rubber for every mouse message when the
            // selection tool is active, but we'd better set it up now!
            if (pRubberImg != m_pImg)
                SetupRubber(m_pImg);

            ClearStatusBarPosition();

            CTracker::STATE state;

            selRect.InflateRect( -CTracker::HANDLE_SIZE,
                                 -CTracker::HANDLE_SIZE );

            state = CTracker::HitTest(selRect, newPt, CTracker::nil);

            if (bPtInTracker && state == CTracker::nil)
                {
                // Actually inside the selection...
                SetCursor(theApp.LoadCursor(IDCUR_MOVE));

                if (code == WM_LBUTTONDOWN || code == WM_LBUTTONDBLCLK)
                    {
                    StartSelectionDrag(code, newPt);
                    }
                else
                    {
                    if (code == WM_RBUTTONDOWN || code == WM_RBUTTONDBLCLK)
                        // some of the menu commands don't work for free form selections
                        OnRButtonDownInSel( &imagePt );
                    }
                }
            else
                {
                // In the tracker frame...

                SetCursor(HCursorFromTrackerState(state));

                if (code == WM_LBUTTONDOWN || code == WM_LBUTTONDBLCLK)
                    {
                    // Start a resize operation...
                    SetCapture();
                    PrepareForBrushChange();

                    ASSERT(c_pResizeDragger == NULL);
                    CRect rect = theImgBrush.m_rcSelection;
                    ImageToClient(rect);

                    c_pResizeDragger = new CDragger(this, &rect);
                    ASSERT(c_pResizeDragger != NULL);
                    c_dragState = state;
                    }
                }

            return;
            }
        }

    if (! imageRect.PtInRect( clientPt )
    &&    code         != WM_CANCEL
    &&    GetCapture() == NULL)
        {
        // The mouse is not inside the image and we're not in any
        // special mode, so hide the brush...
        if (g_pDragBrushWnd    == this
        &&  theImgBrush.m_pImg == NULL)
            HideBrush();

        CRect selRect = imageRect;

        selRect.InflateRect( CTracker::HANDLE_SIZE, CTracker::HANDLE_SIZE );

        if (theImgBrush.m_pImg != NULL || ! selRect.PtInRect( clientPt ))
            {
            // The mouse is not in the whole image tracker
            if (WM_LBUTTONDOWN == code)
            {
               if (CImgTool::GetCurrentID() != IDMX_TEXTTOOL)
               {
                  CmdCancel ();
               }

            }
            else
            {
               SetCursor( LoadCursor(NULL, IDC_ARROW ));
            }

            return;
            }

        // The mouse is in the whole image tracker, so set the cursor
        // as appropriate
        CTracker::STATE state = CTracker::nil;

        if (c_pImgWndCur == this)
            state = CTracker::HitTest(imageRect, clientPt, CTracker::nil);

        switch (state)
            {
            case CTracker::resizingTop:
            case CTracker::resizingLeft:
            case CTracker::resizingTopLeft:
            case CTracker::resizingTopRight:
            case CTracker::resizingBottomLeft:
               state = CTracker::nil;
               break;
            }

        SetCursor( HCursorFromTrackerState( state ) );

        // Handle mouse messages for tracker...
        if (state != CTracker::nil
        &&  (code == WM_LBUTTONDOWN || code == WM_LBUTTONDBLCLK))
            {
            SetCapture();

            ASSERT( c_pResizeDragger == NULL );

            c_pResizeDragger = new CDragger( this, &imageRect );

            ASSERT( c_pResizeDragger != NULL );

            c_dragState = state;
            }

        return;
        }

    newPt.x /= m_nZoom;
    newPt.y /= m_nZoom;

    if (! CImgTool::IsDragging())
        SetStatusBarPosition( m_ptDispPos );

    // Moving the selection??

    if (theImgBrush.m_bMoveSel
    ||  theImgBrush.m_bSmearSel)
        {
        SelectionDragHandler( code, newPt );

        return;
        }

    AdjustPointForGrid( &newPt );

    // Dispatch the event off to the current tool...

    CImgTool* pImgTool = CImgTool::GetCurrent();

    switch (code)
        {
        case WM_CANCEL:
            ReleaseCapture();
            pImgTool->OnCancel(this);
            mti.fLeft = mti.fRight = FALSE;
            break;

        case WM_LBUTTONDOWN:
        case WM_LBUTTONDBLCLK:
        case WM_RBUTTONDOWN:
        case WM_RBUTTONDBLCLK:
            // We don't set the rubber for every mouse message when the
            // selection tool is active, but we'd better set it up now!
            if (pRubberImg != m_pImg)
                SetupRubber( m_pImg );

            mti.fLeft  = (code == WM_LBUTTONDOWN || code == WM_LBUTTONDBLCLK);
            mti.fRight = (code == WM_RBUTTONDOWN || code == WM_RBUTTONDBLCLK);

            mti.pt = mti.ptDown = mti.ptPrev = newPt;

            // if in the polygon tool, double clicks will end operation

            if (CImgTool::GetCurrentID() == IDMB_POLYGONTOOL
            &&  ((code == WM_LBUTTONDBLCLK) || (code == WM_RBUTTONDBLCLK)))
                {
                mti.ptPrev = mti.pt;
                mti.pt     = newPt;

                pImgTool->EndMultiptOperation(); // end the multipt operation
                pImgTool->OnEndDrag( this, &mti );

                mti.fLeft  = FALSE;
                mti.fRight = FALSE;

                break;
                }

            SetCapture();

            if (CImgTool::GetCurrentID() != IDMB_PICKRGNTOOL)
                HideBrush();

            if (pImgTool->IsUndoable())
                SetUndo(m_pImg);

            pImgTool->OnStartDrag( this, &mti );
            break;

        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
            mti.ptPrev = mti.pt;
            mti.pt     = newPt;

            if (GetCapture() != this)
                break;

            ReleaseCapture();

            pImgTool->CanEndMultiptOperation( &mti );
            pImgTool->OnEndDrag( this, &mti );

            if (code == WM_LBUTTONUP)
                {
                mti.fLeft = FALSE;
                }
            if (code == WM_RBUTTONUP)
                {
                mti.fRight = FALSE;
                }
            break;

        case WM_MOUSEMOVE:
            mti.ptPrev = mti.pt;
            mti.pt = newPt;

            if (mti.fLeft || mti.fRight)
                pImgTool->OnDrag(this, &mti);
            else
                pImgTool->OnMove(this, &mti);
            break;

        case WM_TIMER:
            pImgTool->OnTimer( this, &mti );
            break;
        }

    UpdateWindow(); // For immediate feedback in active window
    SetToolCursor();
    }

/***************************************************************************/

void CImgWnd::FinishUndo(const CRect& rectUndo)
    {
        if ( EnsureUndoSize(m_pImg) )
        m_pImg->m_pBitmapObj->FinishUndo(&rectUndo);
        else
        {
        TRACE(TEXT("Problem: Can NOT ensure undo capability!\n"));
        MessageBeep(0);
        }
    }

/***************************************************************************/

void CImgWnd::CancelPainting()
    {
    if (g_hUndoImgBitmap == NULL)
        return; // nothing to cancel!

    IMG*     pimg;
    HDC      hTempDC;
    HBITMAP  hOldBM;
    HPALETTE hOldPalette = NULL;

    pimg = m_pImg;

    if ((hTempDC = CreateCompatibleDC( pimg->hDC )) == NULL)
        {
        TRACE(TEXT("Not enough memory to undo!\n"));
        MessageBeep(0);
        return;
        }

    HideBrush();

    if (g_hUndoPalette)
        {
        if (pimg->m_hPalOld)
            {
            ::SelectPalette( pimg->hDC, pimg->m_hPalOld, FALSE );
            pimg->m_hPalOld = NULL;
            }

        if (pimg->m_pPalette)
            pimg->m_pPalette->DeleteObject();

        pimg->m_pPalette->Attach( g_hUndoPalette );
        g_hUndoPalette = NULL;

        pimg->m_hPalOld = ::SelectPalette( pimg->hDC,
                                 (HPALETTE)pimg->m_pPalette->GetSafeHandle(), FALSE );
        ::RealizePalette( pimg->hDC );
        }

    hOldBM = (HBITMAP)SelectObject( hTempDC, g_hUndoImgBitmap );

    if (pimg->m_pPalette)
        {
        hOldPalette = ::SelectPalette( hTempDC,
                            (HPALETTE)pimg->m_pPalette->GetSafeHandle(), FALSE );
        ::RealizePalette( hTempDC );
        }

    ASSERT( hOldBM != NULL );

    BitBlt( pimg->hDC, 0, 0, pimg->cxWidth, pimg->cyHeight, hTempDC, 0, 0, SRCCOPY );

    if (hOldPalette != NULL)
        ::SelectPalette( hTempDC,  hOldPalette, FALSE );

    SelectObject( hTempDC, hOldBM );
    DeleteDC    ( hTempDC );

    InvalImgRect ( m_pImg, NULL );
    CommitImgRect( m_pImg, NULL );
    }

#ifdef  GRIDOPTIONS
/***************************************************************************/

void CImgWnd::CmdGridOptions()
    {
    CImgGridDlg dlg;

    dlg.m_bPixelGrid = theApp.m_bShowGrid;
    dlg.m_bTileGrid  = m_pImg->m_bTileGrid;
    dlg.m_nWidth     = m_pImg->m_cxTile;
    dlg.m_nHeight    = m_pImg->m_cyTile;

    if (dlg.DoModal() != IDOK)
        return;

    // Hide the current is dependant on the state of the grid...

    BOOL bOldShowGrid = theApp.m_bShowGrid;
    theApp.m_bShowGrid = dlg.m_bPixelGrid;
    m_pImg->m_bTileGrid = dlg.m_bTileGrid;
    m_pImg->m_cxTile = dlg.m_nWidth;
    m_pImg->m_cyTile = dlg.m_nHeight;

    InvalImgRect(m_pImg, NULL);

    if (bOldShowGrid != theApp.m_bShowGrid)
        {
        if (c_pImgWndCur != NULL)
            c_pImgWndCur->Invalidate(FALSE); // Redraw tracker
        }
    }
#endif  // GRIDOPTIONS

/***************************************************************************/

void CImgWnd::CmdShowGrid()
    {
    // Hide the current cross hair since the width of the lines
    // is dependant on the state of the grid...
    theApp.m_bShowGrid = ! theApp.m_bShowGrid;

    InvalImgRect(m_pImg, NULL);

    if (c_pImgWndCur != NULL)
        c_pImgWndCur->Invalidate(FALSE); // Redraw tracker
    }

/***************************************************************************/
// Draw a grid over the image already in the bitmap in pDC.  Drawing
// is optimized by restricting it to destRect.
//
void CImgWnd::DrawGrid(CDC* pDC, const CRect& srcRect, CRect& destRect)
    {
    ASSERT(pDC != NULL);
    ASSERT(m_pImg != NULL);

    pDC->SetTextColor(RGB(192, 192, 192));
    pDC->SetBkColor(RGB(128, 128, 128));

    CBrush* pOldBrush = pDC->SelectObject(GetHalftoneBrush());

    CRect gridRect(0, 0, m_pImg->cxWidth * m_nZoom + 1,
        m_pImg->cyHeight * m_nZoom + 1);

    for (int x = gridRect.left; x <= gridRect.right; x += m_nZoom)
        pDC->PatBlt(x, gridRect.top, 1, gridRect.Height(), PATCOPY);

    for (int y = gridRect.top; y <= gridRect.bottom; y += m_nZoom)
        pDC->PatBlt(gridRect.left, y, gridRect.Width(), 1, PATCOPY);

    if (m_pImg->m_bTileGrid)
        {
        pDC->SetTextColor(RGB(0, 0, 255));
        pDC->SetBkColor(RGB(0, 0, 128));

        int nWidth = destRect.Width();
        int nHeight = destRect.Height();
        int nStep;

        if (m_pImg->m_cxTile > 1 && m_pImg->m_cxTile <= m_pImg->cxWidth)
            {
            nStep = m_nZoom * m_pImg->m_cxTile;
            for (x = (m_pImg->m_cxTile - srcRect.left % m_pImg->m_cxTile -
                m_pImg->m_cxTile) * m_nZoom; x <= nWidth; x += nStep)
                {
                pDC->PatBlt(x, 0, 1, nHeight, PATCOPY);
                }
            }

        if (m_pImg->m_cyTile > 1 && m_pImg->m_cyTile <= m_pImg->cyHeight)
            {
            nStep = m_nZoom * m_pImg->m_cyTile;
            for (y = (m_pImg->m_cyTile - srcRect.top % m_pImg->m_cyTile -
            m_pImg->m_cyTile) * m_nZoom; y <= nHeight; y += nStep)
                {
                pDC->PatBlt(0, y, nWidth, 1, PATCOPY);
                }
            }
        }

    pDC->SelectObject(pOldBrush);

    destRect.right += 1;
    destRect.bottom += 1;
    }

#ifdef  GRIDOPTIONS
/***************************************************************************/

void CImgWnd::CmdShowTileGrid()
    {
    extern BOOL  g_bDefaultTileGrid;
    // If neither grid is visible, show both.  Otherwise leave the pixel
    // grid alone and toggle the tile grid.

    if (! theApp.m_bShowGrid)
        {
        m_pImg->m_bTileGrid = TRUE;
        theApp.m_bShowGrid = TRUE;
        }
    else
        {
        m_pImg->m_bTileGrid = !m_pImg->m_bTileGrid;
        }

    g_bDefaultTileGrid = m_pImg->m_bTileGrid;

    InvalImgRect(m_pImg, NULL);

    if (c_pImgWndCur != NULL)
        c_pImgWndCur->Invalidate(FALSE); // Redraw tracker
    }
#endif  // GRIDOPTIONS

/***************************************************************************/

void CImgWnd::MoveBrush( const CRect& newSelRect )
    {
    if (! theImgBrush.m_pImg)
        return;

    theImgBrush.m_rcSelection = newSelRect;
    InvalImgRect( theImgBrush.m_pImg, NULL );


    theImgBrush.m_handle.cx = theImgBrush.m_handle.cy = 0;

    BOOL bOldCustomBrush = g_bCustomBrush;

    g_bCustomBrush = TRUE;

    int wOldCombineMode = wCombineMode;

    SetCombineMode( theImgBrush.m_bOpaque ? combineReplace : combineMatte );
    ShowBrush( theImgBrush.m_rcSelection.TopLeft() );

    g_bCustomBrush = bOldCustomBrush;

    SetCombineMode( wOldCombineMode );
    }

/***************************************************************************/

BOOL CImgWnd::MakeBrush( HDC hSourceDC, CRect rcSource )
    {
    int       cxWidth;
    int       cyHeight;
    int       iToolID = CImgTool::GetCurrentID();


    if (rcSource.IsRectEmpty())
        {
        ASSERT( FALSE );

        return FALSE;
        }

    theImgBrush.m_size = rcSource.Size();

    cxWidth  = theImgBrush.m_size.cx;
    cyHeight = theImgBrush.m_size.cy;

    if (theImgBrush.m_hbmOld)
        ::SelectObject( theImgBrush.m_dc.m_hDC, theImgBrush.m_hbmOld );

    if (theImgBrush.m_hbmMaskOld)
        ::SelectObject( theImgBrush.m_dc.m_hDC, theImgBrush.m_hbmMaskOld );

    theImgBrush.m_hbmOld     = NULL;
    theImgBrush.m_hbmMaskOld = NULL;

    theImgBrush.m_dc.DeleteDC();
    theImgBrush.m_bitmap.DeleteObject();
    theImgBrush.m_maskDC.DeleteDC();
    theImgBrush.m_maskBitmap.DeleteObject();

    CDC* pdcSource = CDC::FromHandle( hSourceDC );
    CDC* pdcBitmap = CDC::FromHandle( m_pImg->hDC );

    if (! theImgBrush.m_bitmap.CreateCompatibleBitmap( pdcBitmap, cxWidth, cyHeight )
    ||  ! theImgBrush.m_dc.CreateCompatibleDC        ( pdcBitmap )
    ||  ! theImgBrush.m_maskBitmap.CreateBitmap      (            cxWidth, cyHeight, 1, 1, NULL)
    ||  ! theImgBrush.m_maskDC.CreateCompatibleDC    ( pdcBitmap ))
        {
        theApp.SetGdiEmergency();
        return FALSE;
        }

    theImgBrush.m_pImg       = m_pImg;
    theImgBrush.m_hbmOld     = (HBITMAP)((theImgBrush.m_dc.SelectObject(
                                         &theImgBrush.m_bitmap ))->GetSafeHandle());
    theImgBrush.m_hbmMaskOld = (HBITMAP)((theImgBrush.m_maskDC.SelectObject(
                                         &theImgBrush.m_maskBitmap ))->GetSafeHandle());

    CPalette* pcOldPalette = SetImgPalette( &theImgBrush.m_dc, FALSE );

    if (iToolID == IDMB_PICKRGNTOOL)
        {
        // Using StretchBlt to ensure palette mapping occurs
        TRY {
            CBrush cBrushWhite( PALETTERGB( 0xff, 0xff, 0xff ) );
            CRect  cRectTmp( 0, 0, cxWidth, cyHeight );

            theImgBrush.m_dc.FillRect( &cRectTmp, &cBrushWhite );
            }
        CATCH(CResourceException, e)
            {
            theApp.SetGdiEmergency();
            return FALSE;
            }
        END_CATCH

        if (theImgBrush.m_cRgnPolyFreeHandSel.GetSafeHandle())
            theImgBrush.m_dc.FillRgn( &theImgBrush.m_cRgnPolyFreeHandSel,
                                      CBrush::FromHandle( (HBRUSH)::GetStockObject( BLACK_BRUSH ) ) );

        theImgBrush.m_dc.StretchBlt( 0, 0, cxWidth, cyHeight,
                                        pdcSource,
                                        rcSource.left, rcSource.top,
                                        cxWidth, cyHeight, SRCERASE);
        }
    else
        {
        // Using StretchBlt to ensure palette mapping occurs
        theImgBrush.m_dc.StretchBlt( 0, 0, cxWidth, cyHeight,
                                           pdcSource,
                                           rcSource.left, rcSource.top,
                                           cxWidth, cyHeight, SRCCOPY );
        }

    theImgBrush.RecalcMask( crRight );

    if (pcOldPalette)
        theImgBrush.m_dc.SelectPalette( pcOldPalette, FALSE );

    theImgBrush.m_rcSelection = rcSource;

    rcSource.right  += 1;
    rcSource.bottom += 1;

    InvalImgRect( m_pImg, NULL ); // Redraw selection tracker

    rcDragBrush     = rcSource;
    g_bBrushVisible = TRUE;
    g_pDragBrushWnd = this;

    theImgBrush.m_bFirstDrag        = TRUE;
    theImgBrush.m_bLastDragWasFirst = FALSE;

    return TRUE;
    }

/***************************************************************************/

void CImgWnd::CmdClear()
    {
    if (TextToolProcessed( ID_EDIT_CLEAR ))
        return;

    HPALETTE hOldPalette = NULL;
    HBRUSH hNewBrush, hOldBrush;
    IMG* pImg = m_pImg;

    if ((hNewBrush = CreateSolidBrush( crRight )) == NULL)
        {
        theApp.SetGdiEmergency();
        return;
        }

    HideBrush();

    CRect clearRect;

    if (theImgBrush.m_pImg == NULL)
        clearRect.SetRect(0, 0, pImg->cxWidth, pImg->cyHeight);
    else
        {
        clearRect         = rcDragBrush;
        clearRect.right  -= 1;
        clearRect.bottom -= 1;
        }

    BOOL bUndo = FALSE;

    if (!theImgBrush.m_pImg || theImgBrush.m_bFirstDrag
            || theImgBrush.m_bCuttingFromImage)
        {
        bUndo = TRUE;
        SetUndo(m_pImg);

        if (CImgTool::GetCurrentID() == IDMB_PICKRGNTOOL)
            {
            int     iPoints;
            CPoint* pptArray;
            BOOL    bData = ((CFreehandSelectTool*)CImgTool::GetCurrent())->CopyPointsToMemArray( &pptArray, &iPoints );
            if (bData && iPoints)
                {

                HRGN hrgn = ::CreatePolygonRgn( pptArray, iPoints, ALTERNATE );

                if (hrgn)
                    ::FillRgn( pImg->hDC, hrgn, hNewBrush );

                delete [] pptArray;
                }
            else
                {
                DeleteObject ( hNewBrush );
                theApp.SetMemoryEmergency();
                return;
                }
            }
        else
            {
            hOldBrush = (HBRUSH)SelectObject(pImg->hDC, hNewBrush);

            PatBlt( pImg->hDC, clearRect.left,
                            clearRect.top,
                            clearRect.Width(),
                            clearRect.Height(), PATCOPY );
            SelectObject( pImg->hDC, hOldBrush );
            }
        }

    InvalImgRect ( m_pImg, &clearRect );
    CommitImgRect( m_pImg, &clearRect );

    if (bUndo)
        FinishUndo(clearRect);

    DirtyImg     ( m_pImg );
    DeleteObject ( hNewBrush );

    // If we have a selection, nuke it since it's useless now...
    if (theImgBrush.m_pImg != NULL)
        {
        if (theImgBrush.m_bLastDragWasFirst)
            {
            theImgBrush.m_bLastDragWasFirst = FALSE;
            FinishUndo(theImgBrush.m_rcDraggedFrom);
            }

        theImgBrush.m_handle.cx = 0;
        theImgBrush.m_handle.cy = 0;
        theImgBrush.m_bMoveSel = theImgBrush.m_bSmearSel = FALSE;
        g_bCustomBrush = FALSE;
        SetCombineMode(combineColor);

        InvalImgRect(theImgBrush.m_pImg, NULL);  // redraw selection
        theImgBrush.m_pImg = NULL;
        }
    }


/***************************************************************************/

void CImgWnd::CmdFlipBshH()
    {
    IMG* pImg = m_pImg;

    HideBrush();

    CRect flipRect;

    if (theImgBrush.m_pImg == NULL && !g_bCustomBrush)
        {
        flipRect.SetRect(0, 0, pImg->cxWidth, pImg->cyHeight);
        }
    else
        {
        flipRect = rcDragBrush;
        flipRect.right -= 1;
        flipRect.bottom -= 1;
        }

    if (  theImgBrush.m_pImg != NULL
    &&  ! theImgBrush.m_bFirstDrag || g_bCustomBrush)
        {
        CPalette* ppal = SetImgPalette( &theImgBrush.m_dc, FALSE );
        //
        // Don't do halftone blts when just moving bits around
        //
        theImgBrush.m_dc.SetStretchBltMode (COLORONCOLOR);

        StretchCopy(theImgBrush.m_dc.m_hDC, 0, 0,
                    theImgBrush.m_size.cx,
                    theImgBrush.m_size.cy,
                    theImgBrush.m_dc.m_hDC,
                    theImgBrush.m_size.cx - 1, 0,
                   -theImgBrush.m_size.cx,
                    theImgBrush.m_size.cy);

        StretchCopy(theImgBrush.m_maskDC.m_hDC, 0, 0,
                    theImgBrush.m_size.cx,
                    theImgBrush.m_size.cy,
                    theImgBrush.m_maskDC.m_hDC,
                    theImgBrush.m_size.cx - 1, 0,
                   -theImgBrush.m_size.cx,
                    theImgBrush.m_size.cy);

        if (ppal)
            theImgBrush.m_dc.SelectPalette( ppal, FALSE ); // Background ??

        MoveBrush(theImgBrush.m_rcSelection);
        }
    else
        {
        SetUndo(m_pImg);
        SetStretchBltMode (pImg->hDC, COLORONCOLOR);
        StretchCopy(pImg->hDC, flipRect.left,
                               flipRect.top,
                               flipRect.Width(),
                               flipRect.Height(),
                    pImg->hDC, flipRect.left + flipRect.Width() - 1,
                               flipRect.top,
                              -flipRect.Width(),
                               flipRect.Height());

        InvalImgRect (m_pImg, &flipRect);
        CommitImgRect(m_pImg, &flipRect);
        FinishUndo   (flipRect);
        DirtyImg     (m_pImg);
        }
    }

/***************************************************************************/

void CImgWnd::CmdFlipBshV()
    {
    IMG* pImg = m_pImg;

    HideBrush();

    CRect flipRect;

    if (theImgBrush.m_pImg == NULL && !g_bCustomBrush)
        {
        flipRect.SetRect(0, 0, pImg->cxWidth, pImg->cyHeight);
        }
    else
        {
        flipRect = rcDragBrush;
        flipRect.right -= 1;
        flipRect.bottom -= 1;
        }

    if (  theImgBrush.m_pImg != NULL
    &&  ! theImgBrush.m_bFirstDrag || g_bCustomBrush)
        {
        CPalette* ppal = SetImgPalette( &theImgBrush.m_dc, FALSE ); // Background ??
        theImgBrush.m_dc.SetStretchBltMode (COLORONCOLOR);
        StretchCopy(theImgBrush.m_dc.m_hDC, 0, 0,
                    theImgBrush.m_size.cx,
                    theImgBrush.m_size.cy,
                    theImgBrush.m_dc.m_hDC, 0,
                    theImgBrush.m_size.cy - 1,
                    theImgBrush.m_size.cx,
                   -theImgBrush.m_size.cy);

        StretchCopy(theImgBrush.m_maskDC.m_hDC, 0, 0,
                    theImgBrush.m_size.cx,
                    theImgBrush.m_size.cy,
                    theImgBrush.m_maskDC.m_hDC, 0,
                    theImgBrush.m_size.cy - 1,
                    theImgBrush.m_size.cx,
                   -theImgBrush.m_size.cy);

        if (ppal)
            theImgBrush.m_dc.SelectPalette( ppal, FALSE ); // Background ??

        MoveBrush(theImgBrush.m_rcSelection);
        }
    else
        {
        SetUndo(m_pImg);
        SetStretchBltMode (pImg->hDC, COLORONCOLOR);
        StretchCopy(pImg->hDC, flipRect.left,    flipRect.top,
                               flipRect.Width(), flipRect.Height(),
                    pImg->hDC, flipRect.left,    flipRect.top + flipRect.Height() - 1,
                               flipRect.Width(), -flipRect.Height());

        InvalImgRect (m_pImg, &flipRect);
        CommitImgRect(m_pImg, &flipRect);
        FinishUndo   (flipRect);
        DirtyImg     (m_pImg);
        }
    }

/***************************************************************************/

void CImgWnd::CmdDoubleBsh()
    {
    if (!g_bCustomBrush && theImgBrush.m_pImg == NULL)
        {
        MessageBeep(0);
        return;
        }

    PrepareForBrushChange(TRUE, FALSE);

    CRect rc  =           theImgBrush.m_rcSelection;
    rc.left  -=           theImgBrush.m_size.cx / 2;
    rc.right  = rc.left + theImgBrush.m_size.cx * 2;
    rc.top   -=           theImgBrush.m_size.cy / 2;
    rc.bottom = rc.top  + theImgBrush.m_size.cy * 2;

    HideBrush();

    theImgBrush.SetSize( CSize( theImgBrush.m_size.cx * 2,
                                theImgBrush.m_size.cy * 2 ) );
    MoveBrush(rc);

    if (g_bCustomBrush)
        theImgBrush.CenterHandle();
    }

/***************************************************************************/

void CImgWnd::CmdHalfBsh()
    {
    if (! g_bCustomBrush
    &&  ! theImgBrush.m_pImg)
        {
        MessageBeep(0);
        return;
        }

    PrepareForBrushChange( TRUE, FALSE );

    CRect rc  =            theImgBrush.m_rcSelection;
    rc.left  +=            theImgBrush.m_size.cx / 4;
    rc.right  = rc.left + (theImgBrush.m_size.cx + 1) / 2;
    rc.top   +=            theImgBrush.m_size.cy / 4;
    rc.bottom = rc.top  + (theImgBrush.m_size.cy + 1) / 2;

    HideBrush();

    theImgBrush.SetSize( CSize( (theImgBrush.m_size.cx + 1) / 2,
                                (theImgBrush.m_size.cy + 1) / 2 ) );
    MoveBrush( rc );

    if (g_bCustomBrush)
        theImgBrush.CenterHandle();
    }

/***************************************************************************/

CPalette* CImgWnd::FixupDibPalette( LPSTR lpDib, CPalette* ppalDib )
    {
    CPBView* pView = (CPBView*)GetParent();
    CPBDoc*  pDoc  = pView->GetDocument();

    if (pDoc == NULL || lpDib == NULL || ppalDib == NULL || pDoc->m_pBitmapObj->m_pImg == NULL)
        return ppalDib;

    IMG* pImg         = pDoc->m_pBitmapObj->m_pImg;
    int  iColorBits   = pImg->cBitCount * pImg->cPlanes;
    BOOL bFixupDib    = TRUE;
    BOOL bSwapPalette = TRUE;

    // only if dealing with palettes
    if (iColorBits != 8)
        return ppalDib;

    CPalette* ppalPic = theApp.m_pPalette;
    CPalette* ppalNew = NULL;
        BOOL      bMergedPalette = FALSE;

    if (ppalPic)
        {
        int iAdds;

        if ( ppalNew = MergePalettes( ppalPic, ppalDib, iAdds ) )
                        bMergedPalette = TRUE;

        if (ppalNew)
            {
            if (! iAdds)
                {
                bSwapPalette = FALSE;
                                if ( bMergedPalette )
                                        {
                                        delete ppalNew;
                                        ppalNew = FALSE;
                                        bMergedPalette = FALSE;
                                        }
                ppalNew = ppalPic;
                }
            }
        else
            {
            bSwapPalette = FALSE;
                        if ( bMergedPalette )
                                {
                                delete ppalNew;
                                ppalNew = FALSE;
                                bMergedPalette = FALSE;
                                }
            ppalNew = ppalPic;
            }
        }
    else
        {
                if ( bMergedPalette )
                        {
                        delete ppalNew;
                        ppalNew = FALSE;
                        bMergedPalette = FALSE;
                        }
        ppalNew   = ppalDib;
        bFixupDib = FALSE;
        }

    if (bFixupDib)
        {
        LOGPALETTE256    palette;
        COLORREF         crCurColor;
        UINT             uColorIndex;
        int              iDibColors   = DIBNumColors( lpDib );
        BOOL             bWinStyleDIB = IS_WIN30_DIB( lpDib );
        LPBITMAPINFO     lpDibInfo    = (LPBITMAPINFO)lpDib;
        LPBITMAPCOREINFO lpCoreInfo   = (LPBITMAPCOREINFO)lpDib;

        palette.palVersion    = 0x300;
        palette.palNumEntries = (WORD)ppalNew->GetPaletteEntries( 0, 256,
                                                              &palette.palPalEntry[0] );
                                ppalNew->GetPaletteEntries( 0, palette.palNumEntries,
                                                              &palette.palPalEntry[0] );
        for (int iLoop = 0; iLoop < iDibColors; iLoop++)
            {
            if (bWinStyleDIB)
                {
                crCurColor = PALETTERGB( lpDibInfo->bmiColors[iLoop].rgbRed,
                                         lpDibInfo->bmiColors[iLoop].rgbGreen,
                                         lpDibInfo->bmiColors[iLoop].rgbBlue );
                }
            else
                {
                crCurColor = PALETTERGB( lpCoreInfo->bmciColors[iLoop].rgbtRed,
                                         lpCoreInfo->bmciColors[iLoop].rgbtGreen,
                                         lpCoreInfo->bmciColors[iLoop].rgbtBlue );
                }
            uColorIndex = ppalNew->GetNearestPaletteIndex( crCurColor );

            if (bWinStyleDIB)
                {
                lpDibInfo->bmiColors[iLoop].rgbRed   = palette.palPalEntry[uColorIndex].peRed;
                lpDibInfo->bmiColors[iLoop].rgbGreen = palette.palPalEntry[uColorIndex].peGreen;
                lpDibInfo->bmiColors[iLoop].rgbBlue  = palette.palPalEntry[uColorIndex].peBlue;
                }
            else
                {
                lpCoreInfo->bmciColors[iLoop].rgbtRed   = palette.palPalEntry[uColorIndex].peRed;
                lpCoreInfo->bmciColors[iLoop].rgbtGreen = palette.palPalEntry[uColorIndex].peGreen;
                lpCoreInfo->bmciColors[iLoop].rgbtBlue  = palette.palPalEntry[uColorIndex].peBlue;
                }
            }
        if (! bSwapPalette)
                        {
                        if ( bMergedPalette )
                                {
                                delete ppalNew;
                                bMergedPalette = FALSE;
                                }
            ppalNew = NULL;
                        }
        }

    if (bSwapPalette)
        {
        if (pImg->m_hPalOld)
            {
            ::SelectPalette( pImg->hDC, pImg->m_hPalOld, FALSE );
            pImg->m_hPalOld = NULL;
            }

        if (pImg->m_pPalette)
            delete pImg->m_pPalette;

        pImg->m_pPalette = ppalNew;
        pImg->m_hPalOld  = ::SelectPalette( pImg->hDC,
                                     (HPALETTE)ppalNew->GetSafeHandle(), FALSE );
        ::RealizePalette( pImg->hDC );
        InvalImgRect( pImg, NULL );

        // Return NULL since we swapped the new palette into the pImg!
        ppalNew = NULL;

        theApp.m_pPalette = pImg->m_pPalette;

        //
        // now that we changed the app palette update the DIB Section
        // color table too.
        //
        DWORD rgb[256];
        int i,n;

        n = theApp.m_pPalette->GetPaletteEntries(0, 256, (LPPALETTEENTRY)rgb);
        for (i=0; i<n; i++)
            rgb[i] = RGB(GetBValue(rgb[i]),GetGValue(rgb[i]),GetRValue(rgb[i]));
        SetDIBColorTable(pImg->hDC, 0, n, (LPRGBQUAD)rgb);
        }

        // Delete any orphaned ppalDib pointers.
        if ( ppalDib && ppalDib != ppalNew )
                delete ppalDib;

    return ppalNew;
    }

/***************************************************************************/

void CImgWnd::ShowBrush(CPoint ptHandle)
    {
    IMG * pimg = m_pImg;

    HideBrush();

    COLORREF crRealLeftColor;
    COLORREF crRealRightColor;

    int nStrokeWidth = CImgTool::GetCurrent()->GetStrokeWidth();
    int nStrokeShape = CImgTool::GetCurrent()->GetStrokeShape();

    if (CImgTool::GetCurrentID() == IDMB_ERASERTOOL)
        {
        crRealRightColor = crRight;
        crRealLeftColor  = crLeft;

        crLeft = crRight;
        }

    g_pDragBrushWnd = this;

    if (g_bCustomBrush)
        {
        int nCombineMode = (theImgBrush.m_bOpaque) ? combineReplace : combineMatte;

        rcDragBrush.SetRect(ptHandle.x, ptHandle.y,
                            ptHandle.x + theImgBrush.m_size.cx,
                            ptHandle.y + theImgBrush.m_size.cy);
        rcDragBrush -= (CPoint)theImgBrush.m_handle;

        theImgBrush.m_rcSelection = rcDragBrush;
        rcDragBrush.right  += 1;
        rcDragBrush.bottom += 1;

        if (CImgTool::GetCurrentID() == IDMX_TEXTTOOL)
            {
//          extern CTextTool g_textTool;
//          g_textTool.Render(CDC::FromHandle(pimg->hDC),
//                          rcDragBrush, TRUE, FALSE);
            }
        else
            {
            switch (nCombineMode)
                {
                case combineColor:
                    theImgBrush.BltColor(pimg, rcDragBrush.TopLeft(), crLeft);
                    break;

                case combineMatte:
                    theImgBrush.BltMatte(pimg, rcDragBrush.TopLeft());
                    break;

                case combineReplace:
                    theImgBrush.BltReplace(pimg, rcDragBrush.TopLeft());
                    break;
                }
            }

        InvalImgRect(m_pImg, &rcDragBrush);
        }
    else
        {
        DrawImgLine(m_pImg, ptHandle, ptHandle, crLeft,
                                          nStrokeWidth, nStrokeShape, FALSE);
        rcDragBrush.left   = ptHandle.x - nStrokeWidth / 2;
        rcDragBrush.top    = ptHandle.y - nStrokeWidth / 2;
        rcDragBrush.right  = rcDragBrush.left + nStrokeWidth;
        rcDragBrush.bottom = rcDragBrush.top  + nStrokeWidth;
        }

    if (CImgTool::GetCurrentID() == IDMB_ERASERTOOL)
        {
        crLeft  = crRealLeftColor;
        crRight = crRealRightColor;
        }

    g_bBrushVisible = TRUE;
    }

/***************************************************************************/

void CImgWnd::CmdSmallBrush()
    {
    if (CImgTool::GetCurrent()->GetStrokeWidth() != 0)
        CImgTool::GetCurrent()->SetStrokeWidth(1);
    }

/***************************************************************************/

void CImgWnd::CmdSmallerBrush()
    {
    if (theImgBrush.m_pImg != NULL || g_bCustomBrush)
        {
        CmdHalfBsh();
        return;
        }

    UINT nStrokeWidth = CImgTool::GetCurrent()->GetStrokeWidth();

    if (nStrokeWidth > 1)
        CImgTool::GetCurrent()->SetStrokeWidth(nStrokeWidth - 1);
    }

/***************************************************************************/

void CImgWnd::CmdLargerBrush()
    {
    if (theImgBrush.m_pImg != NULL || g_bCustomBrush)
        {
        CmdDoubleBsh();
        return;
        }

    UINT nStrokeWidth = CImgTool::GetCurrent()->GetStrokeWidth();

    CImgTool::GetCurrent()->SetStrokeWidth(nStrokeWidth + 1);
    }

/***************************************************************************/

void CImgWnd::CmdOK()
    {
    if (GetCapture() != NULL)
        {
        MessageBeep(0);
        return;
        }
    }

/***************************************************************************/
// Draw the tracker for this view (if it's the active one) into pDC.
// If pDC is NULL, one will be provided.  Optimize drawing by limiting
// it to pPaintRect.  If pPaintRect is NULL, draw the whole tracker.
//
void CImgWnd::DrawTracker( CDC* pDC, const CRect* pPaintRect )
    {
    BOOL bDrawTrackerRgn = FALSE;

    if (c_pImgWndCur != this
    ||  theImgBrush.m_bMoveSel
    ||  theImgBrush.m_bSmearSel
    ||  theImgBrush.m_bMakingSelection)
        {
        // This is not the active view, or the user is doing something
        // to prevent the tracker from appearing.
        return;
        }

    BOOL bReleaseDC = FALSE;
    CRect clientRect;

    if (pDC == NULL)
        {
        pDC = GetDC();

        if (pDC == NULL)
            {
            theApp.SetGdiEmergency(FALSE);
            return;
            }
        bReleaseDC = TRUE;
        }

   if (pPaintRect == NULL)
        {
        GetClientRect(&clientRect);
        pPaintRect = &clientRect;
        }

    CRect trackerRect;

    GetImageRect( trackerRect );

    trackerRect.InflateRect( CTracker::HANDLE_SIZE, CTracker::HANDLE_SIZE );

    CTracker::EDGES edges = (CTracker::EDGES)(CTracker::right | CTracker::bottom);

    if (CImgTool::GetCurrentID() == IDMB_PICKRGNTOOL)
        {
        bDrawTrackerRgn = TRUE;
        }

    if (m_pImg == theImgBrush.m_pImg)
        {
        edges = CTracker::all;

        trackerRect = theImgBrush.m_rcSelection;

        trackerRect.left   *= m_nZoom;
        trackerRect.top    *= m_nZoom;
        trackerRect.right  *= m_nZoom;
        trackerRect.bottom *= m_nZoom;

        trackerRect.InflateRect( CTracker::HANDLE_SIZE,
                                 CTracker::HANDLE_SIZE);
        trackerRect.OffsetRect(  CTracker::HANDLE_SIZE + m_xScroll * m_nZoom,
                                 CTracker::HANDLE_SIZE + m_yScroll * m_nZoom);

        if (IsGridVisible())
            {
            trackerRect.right  += 1;
            trackerRect.bottom += 1;
            }
        }

    CTracker::DrawBorder (pDC, trackerRect, edges );
    CTracker::DrawHandles(pDC, trackerRect, edges );

    if (bReleaseDC)
        ReleaseDC(pDC);
    }

/***************************************************************************/
// Erase the tracker from this window.  Handles whole image as well
// as selection trackers.
//
void CImgWnd::EraseTracker()
    {
    if (m_pImg == NULL)
        return;

    CClientDC dc(this);

    if (dc.m_hDC == NULL)
        {
        theApp.SetGdiEmergency(FALSE);
        return;
        }

    CRect trackerRect;

    if (m_pImg == theImgBrush.m_pImg)
        {
        // Tracker is a selection within the image

        trackerRect = theImgBrush.m_rcSelection;
        ImageToClient(trackerRect);
        trackerRect.InflateRect(CTracker::HANDLE_SIZE, CTracker::HANDLE_SIZE);

        if (IsGridVisible())
            {
            trackerRect.right += 1;
            trackerRect.bottom += 1;
            }

        InvalidateRect( &trackerRect, FALSE );
        }
    else
        {
        // Tracker is around entire image

        GetImageRect(trackerRect);
        trackerRect.InflateRect(CTracker::HANDLE_SIZE, CTracker::HANDLE_SIZE);
        DrawBackground(&dc, &trackerRect);
        }
    }

/***************************************************************************/

void CImgWnd::CmdTglOpaque()
    {
    HideBrush();
    theImgBrush.m_bOpaque = !theImgBrush.m_bOpaque;
    theImgBrush.RecalcMask( crRight );

    MoveBrush( theImgBrush.m_rcSelection );

    if ((CImgTool::GetCurrentID() == IDMB_PICKTOOL)
    ||  (CImgTool::GetCurrentID() == IDMB_PICKRGNTOOL))
        g_pImgToolWnd->InvalidateOptions( FALSE );
    }

/***************************************************************************/

void CImgWnd::CmdInvertColors()
    {
    IMG* pImg = m_pImg;

    HideBrush();

    CRect invertRect;
    if (theImgBrush.m_pImg == NULL && !g_bCustomBrush)
        {
        invertRect.SetRect(0, 0, pImg->cxWidth, pImg->cyHeight);
        }
    else
        {
        invertRect = rcDragBrush;
        invertRect.right -= 1;
        invertRect.bottom -= 1;
        }

    if (theImgBrush.m_pImg != NULL && ! theImgBrush.m_bFirstDrag || g_bCustomBrush)
        {
        CPalette* ppal = SetImgPalette( &theImgBrush.m_dc, FALSE );

        theImgBrush.m_dc.PatBlt(0, 0, theImgBrush.m_size.cx,
                                      theImgBrush.m_size.cy, DSTINVERT);

        if (ppal)
            theImgBrush.m_dc.SelectPalette( ppal, FALSE ); // Background ??

        theImgBrush.RecalcMask( crRight );
        MoveBrush( theImgBrush.m_rcSelection );
        }
    else
        {
        SetUndo( m_pImg );

        PatBlt( pImg->hDC, invertRect.left, invertRect.top,
                invertRect.Width(), invertRect.Height(), DSTINVERT );

        InvalImgRect ( m_pImg, &invertRect );
        CommitImgRect( m_pImg, &invertRect );
        FinishUndo( invertRect );
        DirtyImg( m_pImg );
        }
    }

/***************************************************************************/

void CImgWnd::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
    {
    CWnd::OnKeyDown( nChar, nRepCnt, nFlags );
    }

/***************************************************************************/

void CImgWnd::OnKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags )
    {
    CWnd::OnKeyUp( nChar, nRepCnt, nFlags );
    }

/***************************************************************************/
