// ==========================================================================
// 							Class Implementation : COXZoomView
// ==========================================================================

// Source file : OXZoomView.cpp

// Version: 9.3

// This software along with its related components, documentation and files ("The Libraries")
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement").  Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office.  For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.                      
                          
// //////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "OXZoomVw.h"
#include <limits.h>
#include <afxpriv.h>

#ifndef __OXMFCIMPL_H__
#if _MFC_VER >= 0x0700
#if _MFC_VER >= 1400
#include <afxtempl.h>
#endif
#include <..\src\mfc\afximpl.h>
#else
#include <..\src\afximpl.h>
#endif
#define __OXMFCIMPL_H__
#endif

#ifndef SPI_GETWHEELSCROLLLINES
	#define SPI_GETWHEELSCROLLLINES   104
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif        

#ifndef WIN32
	#ifdef _DEBUG
		#define UNUSED(x)
	#else
		#define UNUSED(x) x
	#endif
#endif


/////////////////////////////////////////////////////////////////////////////
// COXZoomView

/////////////////////////////////////////////////////////////////////////////
// Definition of static members

#ifdef WIN32
	AFX_DATA const SIZE COXZoomView::sizeDefault = {0,0};
#else
	const SIZE AFXAPI_DATA COXZoomView::sizeDefault = {0,0};
#endif

// Data members -------------------------------------------------------------
// protected:
	// CSize m_totalLog;
	// --- total size in logical units (no rounding)
	
	// CRect m_rectLog;
	// --- bounding rectangle of document in logical units
	
	// CSize m_totalDev;
	// --- total size in device units, scaled

	// CSize m_pageDev;
	// --- per page scroll size in device units
	
	// CSize m_lineDev;
	// --- per line scroll size in device units

	// CSize m_sizeDev;
	// --- total size in device units, unscaled for MM_ANISOTROPIC

	// BOOL	m_bCenter;
	// --- Center output if larger than total size

	// BOOL	m_bInsideUpdate;
	// --- internal state for OnSize callback
	
	// int m_nZoomLevel;
	// --- percentage of zooming

	// BOOL m_bAlignToBottom;
	// --- align to bottom of window if it's bigger than necessary

	// int m_nPagePercent;
	// --- percentage of window to scroll in case of mouse click in a scroll-bar shaft
	
	// int m_nLinePercent;
	// --- percentage of window to scroll in case of mouse click in a scroll arrow

IMPLEMENT_DYNAMIC(COXZoomView, CView)

BEGIN_MESSAGE_MAP(COXZoomView, CView)
	//{{AFX_MSG_MAP(COXZoomView)
	ON_WM_SIZE()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
	ON_WM_MOUSEWHEEL()
	ON_WM_CREATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// Special mapping modes just for COXZoomView implementation
#define MM_NONE             0


// Member functions ---------------------------------------------------------

// public:

COXZoomView::COXZoomView() :
	m_nMapMode(MM_NONE),
	m_nZoomLevel(100),		// 100%
	m_align(ZV_TOPLEFT),
	m_totalLog(0,0),
	m_rectLog(0,0,0,0),
	m_totalDev(0,0),
	m_pageDev(0,0),
	m_lineDev(0,0),
	m_sizeDev(0,0),
	m_nPagePercent(0),
	m_nLinePercent(0),
	m_bUseRelativeScrollSizes(FALSE),
	m_bInsideUpdate(FALSE),
	m_bCenter(FALSE),
	m_bAlignToBottom(FALSE)
{
}

COXZoomView::~COXZoomView()
{
}

int COXZoomView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if(CView::OnCreate(lpCreateStruct)==-1)
		return -1;

	if(!Initialize())
	{
		TRACE(_T("COXZoomView::OnCreate: failed to initialize the object!\n"));
		return -1;
	}

	return 0;
}

BOOL COXZoomView::Initialize()
{
	BOOL bResult=FALSE;

#ifdef OXZOOMVIEW_USE_RULER
	bResult=AttachRuler();
#else
	bResult=TRUE;
#endif	//	OXZOOMVIEW_USE_RULER

	return bResult;
}


void COXZoomView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
	ASSERT_VALID(pDC);

	ASSERT(m_totalDev.cx >= 0 && m_totalDev.cx >= 0);
	SetMapMode(pDC);

	CPoint ptVpOrg(0, 0);       // assume no shift for printing
	if (!pDC->IsPrinting())
	{
		ASSERT(pDC->GetWindowOrg() == CPoint(0,0));
		
		ScaleViewport(pDC);
		// by default shift viewport origin in negative direction of scroll
		ptVpOrg = -GetDeviceScrollPosition();

		if (m_bCenter)
		{
			CRect rect;
			GetClientRect(&rect);

			// if client area is larger than total device size,
			// override scroll positions to place origin such that
			// output is centered in the window
			if (m_totalDev.cx < rect.Width())
				ptVpOrg.x = (rect.Width() - m_totalDev.cx) / 2;
			if (m_totalDev.cy < rect.Height())
				ptVpOrg.y = (rect.Height() - m_totalDev.cy) / 2;
		}
		else
		{
			// if no vertical SB, eventually align to bottom 
			if(m_bAlignToBottom)
			{
				CRect rect;
				int cy;
				GetClientRect(&rect);
			    cy = rect.Height() - m_totalDev.cy;
			    if(cy > 0)
			    	ptVpOrg.y = cy;
		    }
		}
		// Set the logical origin, do it yourself when printing !!!
		pDC->SetWindowOrg(m_rectLog.TopLeft());
	}
	pDC->SetViewportOrg(ptVpOrg);

	CView::OnPrepareDC(pDC, pInfo);     // For default Printing behavior
}

/////////////////////////////////////////////////////////////////////////////
// Set mode and scaling/scrolling sizes

void COXZoomView::SetDeviceScrollSizesRelative(int nMapMode, SIZE sizeTotal,
	int nPagePercent, int nLinePercent)
{
	// synthesize a rectangle from the size
	CRect rectLog(CPoint(0,0), sizeTotal);
	if(nMapMode != MM_TEXT)
		rectLog.bottom = -rectLog.bottom;
	// call overloaded function for real work
	SetDeviceScrollSizesRelative(nMapMode, rectLog, nPagePercent, nLinePercent);
}

void COXZoomView::SetDeviceScrollSizesRelative(SIZE sizeDevice, const CRect& rectDoc,
	int nPagePercent, int nLinePercent)
{
	ASSERT(sizeDevice.cy > 0);
	if(rectDoc.Height() < 0)
		sizeDevice.cy = -sizeDevice.cy;
	m_sizeDev = sizeDevice;
	SetDeviceScrollSizesRelative(MM_ANISOTROPIC, rectDoc, nPagePercent, nLinePercent);
}


void COXZoomView::SetDeviceScrollSizesRelative(int nMapMode, const CRect& rectDoc,
	int nPagePercent, int nLinePercent)
{
	ASSERT(nLinePercent > 0);
	ASSERT(nPagePercent >= nLinePercent);
	ASSERT(nLinePercent <= 100);
	ASSERT(nPagePercent <= 100);
	m_bUseRelativeScrollSizes = TRUE;
	m_nPagePercent = nPagePercent;
	m_nLinePercent = nLinePercent;
	m_rectLog = rectDoc;
	m_totalLog.cx = m_rectLog.Width();
	m_totalLog.cy = m_rectLog.Height();
	if(m_totalLog.cy < 0)
		m_totalLog.cy = -m_totalLog.cy;
	ASSERT(nMapMode > 0);
	ASSERT(nMapMode != MM_ISOTROPIC);
    
	int nOldMapMode = m_nMapMode;
	m_nMapMode = nMapMode;

	//BLOCK: convert logical coordinate space to device coordinates
	{
		CWindowDC dc(NULL);
		SetMapMode(&dc);
		ScaleViewport(&dc);
		m_totalDev = m_totalLog;
		dc.LPtoDP((LPPOINT)&m_totalDev);
		if (m_totalDev.cy < 0)
			m_totalDev.cy = -m_totalDev.cy;
	} // release DC here

	// now adjust device specific sizes
	ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);

	if (m_hWnd != NULL)
	{
		// window has been created, invalidate now
		UpdateBars();
		if (nOldMapMode != m_nMapMode)
			Invalidate(TRUE);
	}
}

// maintained for backward compatibility
void COXZoomView::SetScrollSizes(int nMapMode, SIZE sizeTotal,
	const SIZE& sizePage, const SIZE& sizeLine)
{
	m_bUseRelativeScrollSizes = FALSE;
	ASSERT(sizeTotal.cx >= 0 && sizeTotal.cy >= 0);
	ASSERT(nMapMode > 0);
	ASSERT(nMapMode != MM_ISOTROPIC && nMapMode != MM_ANISOTROPIC);
    
	int nOldMapMode = m_nMapMode;
	m_nMapMode = nMapMode;
	m_totalLog = sizeTotal;
	// synthesize a rectangle from the size
	m_rectLog = CRect(CPoint(0,0), sizeTotal);
	if(m_nMapMode != MM_TEXT)
		m_rectLog.bottom = -m_rectLog.bottom;

	//BLOCK: convert logical coordinate space to device coordinates
	{
		CWindowDC dc(NULL);
		ASSERT(m_nMapMode > 0);
		SetMapMode(&dc);
        // leave page and line sizes unaffected from zooming
		m_pageDev = sizePage;
		dc.LPtoDP((LPPOINT)&m_pageDev);
		m_lineDev = sizeLine;
		dc.LPtoDP((LPPOINT)&m_lineDev);
		// total size
		ScaleViewport(&dc);
		m_totalDev = m_totalLog;
		dc.LPtoDP((LPPOINT)&m_totalDev);
		if (m_totalDev.cy < 0)
			m_totalDev.cy = -m_totalDev.cy;
		if (m_pageDev.cy < 0)
			m_pageDev.cy = -m_pageDev.cy;
		if (m_lineDev.cy < 0)
			m_lineDev.cy = -m_lineDev.cy;
	} // release DC here

	// now adjust device specific sizes
	ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);
	if (m_pageDev.cx == 0)
		m_pageDev.cx = m_totalDev.cx / 10;
	if (m_pageDev.cy == 0)
		m_pageDev.cy = m_totalDev.cy / 10;
	if (m_lineDev.cx == 0)
		m_lineDev.cx = m_pageDev.cx / 10;
	if (m_lineDev.cy == 0)
		m_lineDev.cy = m_pageDev.cy / 10;

	if (m_hWnd != NULL)
	{
		// window has been created, invalidate now
		UpdateBars();
		if (nOldMapMode != m_nMapMode)
			Invalidate(TRUE);
	}
}

// Zoom Ops

void COXZoomView::ZoomToWindow()
{
	int nZoomLevel=GetZoomLevel();
	{
		CClientDC dc(this);
		ASSERT(m_nMapMode > 0);
		SetMapMode(&dc);

		CSize	sizeSb, sizeZoom, sizeClient;
		long temp;
		GetTrueClientSize(sizeClient,sizeSb);
    
		CRect rectClient;
		GetClientRect(rectClient);
		rectClient.right=rectClient.left+sizeClient.cx;
		rectClient.bottom=rectClient.top+sizeClient.cy;
		dc.DPtoLP(rectClient);
		sizeClient=rectClient.Size();
		if(sizeClient.cy < 0) 
    		sizeClient.cy = -sizeClient.cy;

		temp = (long)((float)100 * (float)sizeClient.cx / 
			(float)m_totalLog.cx);
		if(temp > (long)MAXZOOM)
    		sizeZoom.cx = MAXZOOM;
		else
    		sizeZoom.cx = (int)temp;
		temp = (long)((float)100 * (float)sizeClient.cy / 
			(float)m_totalLog.cy);
		if(temp > (long)MAXZOOM)
    		sizeZoom.cy = MAXZOOM;
		else
    		sizeZoom.cy = (int)temp;
			
		// use the lower value
		if(sizeZoom.cy < sizeZoom.cx)
    		nZoomLevel = sizeZoom.cy;
		else
    		nZoomLevel = sizeZoom.cx;
		if (nZoomLevel < MINZOOM)
    		nZoomLevel = MINZOOM;
	}

	SetZoomLevel(nZoomLevel);
}


void COXZoomView::ZoomToRectangle(CRect rectZoom, ZoomAlignment Align)
{
	int nZoomLevel=GetZoomLevel();
	{
		CClientDC dc(this);
		OnPrepareDC(&dc);

		// clip zoom rectangle to document size
		CRect rectDevDoc = m_rectLog;
		dc.LPtoDP(rectDevDoc);
		if(!rectZoom.IntersectRect(rectZoom, rectDevDoc))
			return;		// competely outside of the document, ignore
		dc.DPtoLP(rectZoom);

		// check again in logical coords
		if(0 == rectZoom.Width())
			return;
		if(0 == rectZoom.Height())
			return;

		CSize sizeZoom=rectZoom.Size();
		if(sizeZoom.cy < 0) 
    		sizeZoom.cy = -sizeZoom.cy;

		CSize	sizeSb, sizeClient;
		GetTrueClientSize(sizeClient,sizeSb);
    
		CRect rectClient;
		GetClientRect(rectClient);
		rectClient.right=rectClient.left+sizeClient.cx;
		rectClient.bottom=rectClient.top+sizeClient.cy;
	    SetMapMode(&dc);	// assume 100%
		dc.DPtoLP(rectClient);
		sizeClient=rectClient.Size();
		if(sizeClient.cy < 0) 
    		sizeClient.cy = -sizeClient.cy;

		long temp = (long)((float)100 * (float)sizeClient.cx / 
			(float)sizeZoom.cx);
		if(temp > (long)MAXZOOM)
    		sizeZoom.cx = MAXZOOM;
		else
    		sizeZoom.cx = (int)temp;
		temp = (long)((float)100 * (float)sizeClient.cy / 
			(float)sizeZoom.cy);
		if(temp > (long)MAXZOOM)
    		sizeZoom.cy = MAXZOOM;
		else
    		sizeZoom.cy = (int)temp;
		// use the lower value
		if(sizeZoom.cy < sizeZoom.cx)
    		nZoomLevel = sizeZoom.cy;
		else
    		nZoomLevel = sizeZoom.cx;
		if (nZoomLevel < MINZOOM)
    		nZoomLevel = MINZOOM;
	}

	SetZoomLevel(nZoomLevel);

	CPoint	ptAlign;
	if (m_hWnd != NULL)
	{
		if(Align == ZV_DEFAULT)
			Align = m_align;
		switch(Align)
		{
			case ZV_TOPLEFT:
				ptAlign.x = rectZoom.left;
				ptAlign.y = rectZoom.top;
				break;
					
			case ZV_BOTTOMLEFT:
				ptAlign.x = rectZoom.left;
				ptAlign.y = rectZoom.bottom;
				break;
					
			case ZV_CENTER:
				ptAlign.x = (rectZoom.left + rectZoom.right)/2;
				ptAlign.y = (rectZoom.top + rectZoom.bottom)/2;
				break;
		}
		ScrollToPosition(ptAlign, Align);
		UpdateBars();
		Invalidate(TRUE);
	}
}


int COXZoomView::SetZoomLevel(int nNewLevel, ZoomAlignment Align)
{
	int nOldLevel = m_nZoomLevel;
	if(Align == ZV_DEFAULT)
		Align = m_align;
	CPoint ptCurrent = GetScrollPosition(Align);
	if(nNewLevel < MINZOOM)
		nNewLevel = MINZOOM;
	if(nNewLevel > MAXZOOM)
		nNewLevel = MAXZOOM;
	m_nZoomLevel = nNewLevel;
	if(nOldLevel != nNewLevel)
	{
		//BLOCK: convert logical coordinate space to device coordinates
		{
			CWindowDC dc(NULL);
			ASSERT(m_nMapMode > 0);
			SetMapMode(&dc);
			// total size
			ScaleViewport(&dc);
			m_totalDev = m_totalLog;
			dc.LPtoDP((LPPOINT)&m_totalDev);
			if (m_totalDev.cy < 0)
				m_totalDev.cy = -m_totalDev.cy;
		} // release DC here
	
		ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);
	
		if (m_hWnd != NULL)
		{
			// window exists, invalidate now
			ScrollToPosition(ptCurrent, Align);
			UpdateBars(FALSE);
			Invalidate(TRUE);
		}
	}

#ifdef OXZOOMVIEW_USE_RULER
	m_ruler.ZoomRuler(m_nZoomLevel,TRUE);
	m_ruler.ZoomRuler(m_nZoomLevel,FALSE);
#endif	//	OXZOOMVIEW_USE_RULER

	return(nOldLevel);
}

// zoom helper functions

void COXZoomView::SetMapMode(CDC* pDC) const
{
	ASSERT(m_nMapMode > 0);
	pDC->SetMapMode(m_nMapMode);
    if(m_nMapMode == MM_ANISOTROPIC)
    {                     
    	ASSERT(m_sizeDev.cx >= 10);
    	ASSERT(m_sizeDev.cy >= 10 || m_sizeDev.cy <= -10);
    	ASSERT(m_sizeDev.cx < 32000);
    	ASSERT(m_sizeDev.cy < 32000 || m_sizeDev.cy >= -32000);
    	pDC->SetWindowExt(m_totalLog);
    	pDC->SetViewportExt(m_sizeDev);
    }
    else
    {
		// unlock mapping mode for zooming, extents of previous map mode are inherited
		pDC->SetMapMode(MM_ANISOTROPIC);
		if(m_nMapMode == MM_TEXT)
		{
			// current extents are 1,1 which are too small to zoom down
			pDC->ScaleWindowExt(1000, 1, 1000, 1);
			pDC->ScaleViewportExt(1000, 1, 1000, 1);
		}
    }
}

void COXZoomView::SetZoomAlign(ZoomAlignment align)
{
#ifdef _DEBUG
	if(align == ZV_DEFAULT)
		TRACE(_T("You cannot set the ZoomAlignment to ZV_DEFAULT"));
#endif
	if(align != ZV_DEFAULT)
		m_align = align;
}

/////////////////////////////////////////////////////////////////////////////
// Getting information

CPoint COXZoomView::GetScrollPosition(ZoomAlignment Align) const   // logical coordinates
{
	CPoint pt = GetDeviceScrollPosition();
	// pt may be negative if m_bCenter is set
	
	CRect	rectClient;
	GetClientRect(rectClient);
	DWORD dwStyle = GetStyle();
	BOOL bHasV, bHasH;
    bHasV = (dwStyle & WS_VSCROLL) != 0 || GetScrollBarCtrl(SB_VERT) != NULL;
    bHasH = (dwStyle & WS_HSCROLL) != 0 || GetScrollBarCtrl(SB_HORZ) != NULL;
    
	if(Align == ZV_DEFAULT)
		Align = m_align;
	switch (Align)
	{
		case ZV_BOTTOMLEFT:
			if(bHasV)
				pt.y += rectClient.Height();
			else
				pt.y = m_totalDev.cy - 1;
			break;
			    
		case ZV_CENTER:
			if(bHasV)
				pt.y += rectClient.Height()/2;
			else
				pt.y = m_totalDev.cy/2;
				
			if(bHasH)
				pt.x += rectClient.Width()/2;
			else
				pt.x = m_totalDev.cx/2;
			break;
	}
	
	ASSERT(m_nMapMode > 0); // must be set
	CWindowDC dc(NULL);
	SetMapMode(&dc);
	ScaleViewport(&dc);
	// CRect::TopLeft() doesn't work with const CRects
	dc.SetWindowOrg(m_rectLog.left, m_rectLog.top);
	dc.DPtoLP((LPPOINT)&pt);
	return pt;
}

void COXZoomView::ScrollToPosition(POINT pt, ZoomAlignment Align)    // logical coordinates
{
	ASSERT(m_nMapMode > 0);     // not allowed
	CWindowDC dc(NULL);
	SetMapMode(&dc);
	ScaleViewport(&dc);
	dc.SetWindowOrg(m_rectLog.TopLeft());
	dc.LPtoDP((LPPOINT)&pt);
	
	CRect	rectClient;
	GetClientRect(rectClient);

	// now in device coordinates
	if(Align == ZV_DEFAULT)
		Align = m_align;
	switch (Align)
	{
		case ZV_BOTTOMLEFT:
			pt.y -= rectClient.Height();
			break;
		case ZV_CENTER:
			pt.y -= rectClient.Height()/2;
			pt.x -= rectClient.Width()/2;
			break;
	}
	
	//limit if out of range
	int xMin, xMax, yMin, yMax;
	GetScrollRange(SB_HORZ, &xMin, &xMax);
	GetScrollRange(SB_VERT, &yMin, &yMax);
	ASSERT(xMin == 0 && yMin == 0);
	if (pt.x < 0)
		pt.x = 0;
	else if (pt.x > xMax)
		pt.x = xMax;
	if (pt.y < 0)
		pt.y = 0;
	else if (pt.y > yMax)
		pt.y = yMax;

	ScrollToDevicePosition(pt);
}

CPoint COXZoomView::GetDeviceScrollPosition() const
{
	CPoint pt(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT));
	ASSERT(pt.x >= 0 && pt.y >= 0);

	if (m_bCenter)
	{
		CRect rect;
		GetClientRect(&rect);

		// if client area is larger than total device size,
		// the scroll positions are overridden to place origin such that
		// output is centered in the window
		// GetDeviceScrollPosition() must reflect this

		if (m_totalDev.cx < rect.Width())
			pt.x = -((rect.Width() - m_totalDev.cx) / 2);
		if (m_totalDev.cy < rect.Height())
			pt.y = -((rect.Height() - m_totalDev.cy) / 2);
	}

	return pt;
}

void COXZoomView::GetDeviceScrollSizes(int& nMapMode, SIZE& sizeTotal,
			SIZE& sizePage, SIZE& sizeLine) const
{
	if (m_nMapMode <= 0)
		TRACE(_T("Warning: COXZoomView::GetDeviceScrollSizes returning invalid mapping mode\n"));
	nMapMode = m_nMapMode;
	sizeTotal = m_totalDev;
	sizePage = m_pageDev;
	sizeLine = m_lineDev;
}

void COXZoomView::ScrollToDevicePosition(POINT ptDev)
{
	ASSERT(ptDev.x >= 0);
	ASSERT(ptDev.y >= 0);
	int dx, dy;

	// Note: ScrollToDevicePosition can and is used to scroll out-of-range
	//  areas as far as COXZoomView is concerned -- specifically in
	//  the print-preview code.  Since OnScrollBy makes sure the range is
	//  valid, ScrollToDevicePosition does not vector through OnScrollBy.

	int xOrig = SetScrollPos(SB_HORZ, ptDev.x);
	int yOrig = SetScrollPos(SB_VERT, ptDev.y);
	dx = xOrig - ptDev.x;
	dy = yOrig - ptDev.y;
	ScrollWindow(dx, dy);
}

/////////////////////////////////////////////////////////////////////////////
// Other helpers
// retained for compatibility
void COXZoomView::FillOutsideRect(CDC* pDC, CBrush* pBrush)
{
	ASSERT_VALID(pDC);
	ASSERT_VALID(pBrush);
	// Fill Rect outside the image
	CRect rect;
	GetClientRect(rect);
	ASSERT(rect.left == 0 && rect.top == 0);
	rect.left = m_totalDev.cx;
	if (!rect.IsRectEmpty())
		pDC->FillRect(rect, pBrush);    // vertical strip along the side
	rect.left = 0;
	rect.right = m_totalDev.cx;
	rect.top = m_totalDev.cy;
	if (!rect.IsRectEmpty())
		pDC->FillRect(rect, pBrush);    // horizontal strip along the bottom
}


/////////////////////////////////////////////////////////////////////////////

void COXZoomView::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);
	UpdateBars(FALSE);
}

/////////////////////////////////////////////////////////////////////////////
// Tie to scrollbars and CWnd behaviour

void COXZoomView::GetScrollBarSizes(CSize& sizeSb)
{
	sizeSb.cx = sizeSb.cy = 0;
	DWORD dwStyle = GetStyle();

	if (GetScrollBarCtrl(SB_VERT) == NULL)
	{
		// vert scrollbars will impact client area of this window
		sizeSb.cx = GetSystemMetrics(SM_CXVSCROLL);
		if (dwStyle & WS_BORDER)
			sizeSb.cx -= GetSystemMetrics(SM_CXBORDER);
	}
	if (GetScrollBarCtrl(SB_HORZ) == NULL)
	{
		// horz scrollbars will impact client area of this window
		sizeSb.cy = GetSystemMetrics(SM_CYHSCROLL);
		if (dwStyle & WS_BORDER)
			sizeSb.cy -= GetSystemMetrics(SM_CYBORDER);
	}
}

BOOL COXZoomView::GetTrueClientSize(CSize& size, CSize& sizeSb)
	// return TRUE if enough room to add scrollbars if needed
{
	CRect rect;
	GetClientRect(&rect);
	ASSERT(rect.top == 0 && rect.left == 0);
	size.cx = rect.right;
	size.cy = rect.bottom;
	DWORD dwStyle = GetStyle();

	// first get the size of the scrollbars for this window
	GetScrollBarSizes(sizeSb);

	// first calculate the size of a potential scrollbar
	// (scroll bar controls do not get turned on/off)
	if (sizeSb.cx != 0 && (dwStyle & WS_VSCROLL))
	{
		// vert scrollbars will impact client area of this window
		size.cx += sizeSb.cx;   // currently on - adjust now
	}
	if (sizeSb.cy != 0 && (dwStyle & WS_HSCROLL))
	{
		// horz scrollbars will impact client area of this window
		size.cy += sizeSb.cy;   // currently on - adjust now
	}

	// return TRUE if enough room
	return (size.cx > sizeSb.cx && size.cy > sizeSb.cy);
}

// helper to return the state of the scrollbars without actually changing
//  the state of the scrollbars
void COXZoomView::GetScrollBarState(CSize sizeClient, CSize& needSb,
	CSize& sizeRange, CPoint& ptMove, BOOL bInsideClient)
{
	// get scroll bar sizes (the part that is in the client area)
	CSize sizeSb;
	GetScrollBarSizes(sizeSb);

	// enough room to add scrollbars
	sizeRange = m_totalDev - sizeClient;
		// > 0 => need to scroll
	ptMove = GetDeviceScrollPosition();
		// point to move to (start at current scroll pos)

	BOOL bNeedH = sizeRange.cx > 0;
	if (!bNeedH)
		ptMove.x = 0;                       // jump back to origin
	else if (bInsideClient)
		sizeRange.cy += sizeSb.cy;          // need room for a scroll bar

	BOOL bNeedV = sizeRange.cy > 0;
	if (!bNeedV)
		ptMove.y = 0;                       // jump back to origin 
		//ptMove.y =  m_totalDev.cy - sizeClient.cy;
	else if (bInsideClient)
		sizeRange.cx += sizeSb.cx;          // need room for a scroll bar

	if (bNeedV && !bNeedH && sizeRange.cx > 0)
	{
		ASSERT(bInsideClient);
		// need a horizontal scrollbar after all
		bNeedH = TRUE;
		sizeRange.cy += sizeSb.cy;
	}

	// if current scroll position will be past the limit, scroll to limit
	if (sizeRange.cx > 0 && ptMove.x >= sizeRange.cx)
		ptMove.x = sizeRange.cx;
	if (sizeRange.cy > 0 && ptMove.y >= sizeRange.cy)
		ptMove.y = sizeRange.cy;

	// now update the bars as appropriate
	needSb.cx = bNeedH;
	needSb.cy = bNeedV;

	// needSb, sizeRange, and ptMove area now all updated
}

void COXZoomView::UpdateBars(BOOL /*bSendRecalc*/)
{
	// UpdateBars may cause window to be resized - ignore those resizings
	if (m_bInsideUpdate)
		return;         // Do not allow recursive calls

	// Lock out recursion
	m_bInsideUpdate = TRUE;

	// update the horizontal to reflect reality
	// NOTE: turning on/off the scrollbars will cause 'OnSize' callbacks
	ASSERT(m_totalDev.cx >= 0 && m_totalDev.cy >= 0);

	CRect rectClient;
	BOOL bCalcClient = TRUE;

	// allow parent to do inside-out layout first
	CWnd* pParentWnd = GetParent();
	if (pParentWnd != NULL)
	{
		// if parent window responds to this message, use just
		//  client area for scroll bar calc -- not "true" client area
		if ((BOOL)pParentWnd->SendMessage(WM_RECALCPARENT, 0,
			(LPARAM)(LPCRECT)&rectClient) != 0)
		{
			// use rectClient instead of GetTrueClientSize for
			//  client size calculation.
			bCalcClient = FALSE;
		}
	}

	CSize sizeClient;
	CSize sizeSb;

	if (bCalcClient)
	{
		// get client rect
		if (!GetTrueClientSize(sizeClient, sizeSb))
		{
			// no room for scroll bars (common for zero sized elements)
			CRect rect;
			GetClientRect(&rect);
			if (rect.right > 0 && rect.bottom > 0)
			{
				// if entire client area is not invisible, assume we have
				//  control over our scrollbars
				EnableScrollBarCtrl(SB_BOTH, FALSE);
			}
			m_bInsideUpdate = FALSE;
			return;
		}
	}
	else
	{
		// let parent window determine the "client" rect
		sizeClient.cx = rectClient.right - rectClient.left;
		sizeClient.cy = rectClient.bottom - rectClient.top;
	}

	// enough room to add scrollbars
	CSize sizeRange;
	CPoint ptMove;
	CSize needSb;
    
    // if scrolling-sizes are relative to client area update them NOW
    if(m_bUseRelativeScrollSizes)
    {
    	m_pageDev.cx = MulDiv(m_nPagePercent, sizeClient.cx, 100);
    	m_pageDev.cy = MulDiv(m_nPagePercent, sizeClient.cy, 100);
    	m_lineDev.cx = MulDiv(m_nLinePercent, sizeClient.cx, 100);
    	m_lineDev.cy = MulDiv(m_nLinePercent, sizeClient.cy, 100);
    }
    
	// get the current scroll bar state given the true client area
	GetScrollBarState(sizeClient, needSb, sizeRange, ptMove, bCalcClient);
	if (needSb.cx)
		sizeClient.cy -= sizeSb.cy;
	if (needSb.cy)
		sizeClient.cx -= sizeSb.cx;

	// first scroll the window as needed
	ScrollToDevicePosition(ptMove); // will set the scroll bar positions too

	// this structure needed to update the scrollbar page range
	SCROLLINFO info;
	info.fMask = SIF_PAGE|SIF_RANGE;
	info.nMin = 0;

	// now update the bars as appropriate
	EnableScrollBarCtrl(SB_HORZ, needSb.cx);
	if (needSb.cx)
	{
		info.nPage = sizeClient.cx;
		info.nMax = m_totalDev.cx-1;
		if(!SetScrollInfo(SB_HORZ, &info, TRUE))
			SetScrollRange(SB_HORZ, 0, sizeRange.cx, TRUE);
	}
	EnableScrollBarCtrl(SB_VERT, needSb.cy);
	if (needSb.cy)
	{
		info.nPage = sizeClient.cy;
		info.nMax = m_totalDev.cy-1;
		if(!SetScrollInfo(SB_VERT, &info, TRUE))
			SetScrollRange(SB_VERT, 0, sizeRange.cy, TRUE);
	}

	// Remove recursion lockout
	m_bInsideUpdate = FALSE;
}

void COXZoomView::CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType)
{
	if (nAdjustType == adjustOutside)
	{
		// if the view is being used in-place, add scrollbar sizes
		//  (scollbars should appear on the outside when in-place editing)
		CSize sizeClient(
			lpClientRect->right - lpClientRect->left,
			lpClientRect->bottom - lpClientRect->top);

		CSize sizeRange = m_totalDev - sizeClient;
			// > 0 => need to scroll

		// get scroll bar sizes (used to adjust the window)
		CSize sizeSb;
		GetScrollBarSizes(sizeSb);

		// adjust the window size based on the state
		if (sizeRange.cy > 0)
		{   // vertical scroll bars take up horizontal space
			lpClientRect->right += sizeSb.cx;
		}
		if (sizeRange.cx > 0)
		{   // horizontal scroll bars take up vertical space
			lpClientRect->bottom += sizeSb.cy;
		}
	}
	else
	{
		// call default to handle other non-client areas
		::AdjustWindowRect(lpClientRect, GetStyle(), FALSE);
	}
}

/////////////////////////////////////////////////////////////////////////////
// COXZoomView scrolling

void COXZoomView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	ASSERT(pScrollBar == GetScrollBarCtrl(SB_HORZ));    // may be null
	UNUSED(pScrollBar);

	OnScroll(MAKEWORD(nSBCode, -1), nPos);
}

void COXZoomView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	ASSERT(pScrollBar == GetScrollBarCtrl(SB_VERT));    // may be null
	UNUSED(pScrollBar);

	OnScroll(MAKEWORD(-1, nSBCode), nPos);
}

BOOL COXZoomView::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll)
{
	// calc new x position
	int x = GetScrollPos(SB_HORZ);
	int xOrig = x;

	switch (LOBYTE(nScrollCode))
	{
	case SB_TOP:
		x = 0;
		break;
	case SB_BOTTOM:
		x = INT_MAX;
		break;
	case SB_LINEUP:
		x -= m_lineDev.cx;
		break;
	case SB_LINEDOWN:
		x += m_lineDev.cx;
		break;
	case SB_PAGEUP:
		x -= m_pageDev.cx;
		break;
	case SB_PAGEDOWN:
		x += m_pageDev.cx;
		break;
	case SB_THUMBTRACK:
		x = nPos;
		break;
	}

	// calc new y position
	int y = GetScrollPos(SB_VERT);
	int yOrig = y;

	switch (HIBYTE(nScrollCode))
	{
	case SB_TOP:
		y = 0;
		break;
	case SB_BOTTOM:
		y = INT_MAX;
		break;
	case SB_LINEUP:
		y -= m_lineDev.cy;
		break;
	case SB_LINEDOWN:
		y += m_lineDev.cy;
		break;
	case SB_PAGEUP:
		y -= m_pageDev.cy;
		break;
	case SB_PAGEDOWN:
		y += m_pageDev.cy;
		break;
	case SB_THUMBTRACK:
		y = nPos;
		break;
	}

	BOOL bResult = OnScrollBy(CSize(x - xOrig, y - yOrig), bDoScroll);
	if (bResult && bDoScroll)
		UpdateWindow();

	return bResult;
}

BOOL COXZoomView::OnScrollBy(CSize sizeScroll, BOOL bDoScroll)
{
	int xOrig, x;
	int yOrig, y;

	// don't scroll if there is no valid scroll range (ie. no scroll bar)
	CScrollBar* pBar;
	DWORD dwStyle = GetStyle();
	pBar = GetScrollBarCtrl(SB_VERT);
	if ((pBar != NULL && !pBar->IsWindowEnabled()) ||
		(pBar == NULL && !(dwStyle & WS_VSCROLL)))
	{
		// vertical scroll bar not enabled
		sizeScroll.cy = 0;
	}
	pBar = GetScrollBarCtrl(SB_HORZ);
	if ((pBar != NULL && !pBar->IsWindowEnabled()) ||
		(pBar == NULL && !(dwStyle & WS_HSCROLL)))
	{
		// horizontal scroll bar not enabled
		sizeScroll.cx = 0;
	}

	// adjust current x position
	xOrig = x = GetScrollPos(SB_HORZ);
	int xMax = GetScrollLimit(SB_HORZ);
	x += sizeScroll.cx;
	if (x < 0)
		x = 0;
	else if (x > xMax)
		x = xMax;

	// adjust current y position
	yOrig = y = GetScrollPos(SB_VERT);
	int yMax = GetScrollLimit(SB_VERT);
	y += sizeScroll.cy;
	if (y < 0)
		y = 0;
	else if (y > yMax)
		y = yMax;

	// did anything change?
	if (x == xOrig && y == yOrig)
		return FALSE;

	if (bDoScroll)
	{
		// do scroll and update scroll positions
		ScrollWindow(-(x-xOrig), -(y-yOrig));
		if (x != xOrig)
			SetScrollPos(SB_HORZ, x);
		if (y != yOrig)
			SetScrollPos(SB_VERT, y);
	}
	return TRUE;
}

BOOL COXZoomView::OnMouseWheel(UINT fFlags, short zDelta, CPoint point)
{
	// we don't handle anything but scrolling just now
	if (fFlags & (MK_SHIFT | MK_CONTROL))
		return FALSE;

	// if the parent is a splitter, it will handle the message
	if (GetParentSplitter(this, TRUE))
		return FALSE;

	// we can't get out of it--perform the scroll ourselves
	return DoMouseWheel(fFlags, zDelta, point);
}

// This function isn't virtual. If you need to override it,
// you really need to override OnMouseWheel() here or in
// CSplitterWnd.
BOOL COXZoomView::DoMouseWheel(UINT fFlags, short zDelta, CPoint point)
{
	UNUSED_ALWAYS(point);
	UNUSED_ALWAYS(fFlags);

	// if we have a vertical scroll bar, the wheel scrolls that
	// if we have _only_ a horizontal scroll bar, the wheel scrolls that
	// otherwise, don't do any work at all

	DWORD dwStyle = GetStyle();
	CScrollBar* pBar = GetScrollBarCtrl(SB_VERT);
	BOOL bHasVertBar = ((pBar != NULL) && pBar->IsWindowEnabled()) ||
					(dwStyle & WS_VSCROLL);

	pBar = GetScrollBarCtrl(SB_HORZ);
	BOOL bHasHorzBar = ((pBar != NULL) && pBar->IsWindowEnabled()) ||
					(dwStyle & WS_HSCROLL);

	if (!bHasVertBar && !bHasHorzBar)
		return FALSE;

	BOOL bResult = FALSE;
	UINT uWheelScrollLines=GetMouseScrollLines();
	int nToScroll;
	int nDisplacement;

	if (bHasVertBar)
	{
		nToScroll = ::MulDiv(-zDelta, uWheelScrollLines, WHEEL_DELTA);
		if (nToScroll == -1 || uWheelScrollLines == WHEEL_PAGESCROLL)
		{
			nDisplacement = m_pageDev.cy;
			if (zDelta > 0)
				nDisplacement = -nDisplacement;
		}
		else
		{
			nDisplacement = nToScroll * m_lineDev.cy;
			nDisplacement = min(nDisplacement, m_pageDev.cy);
		}
		bResult = OnScrollBy(CSize(0, nDisplacement), TRUE);
	}
	else if (bHasHorzBar)
	{
		nToScroll = ::MulDiv(-zDelta, uWheelScrollLines, WHEEL_DELTA);
		if (nToScroll == -1 || uWheelScrollLines == WHEEL_PAGESCROLL)
		{
			nDisplacement = m_pageDev.cx;
			if (zDelta > 0)
				nDisplacement = -nDisplacement;
		}
		else
		{
			nDisplacement = nToScroll * m_lineDev.cx;
			nDisplacement = min(nDisplacement, m_pageDev.cx);
		}
		bResult = OnScrollBy(CSize(nDisplacement, 0), TRUE);
	}

	if (bResult)
		UpdateWindow();

	return bResult;
}


static BOOL g_bGotScrollLines = FALSE;

UINT COXZoomView::GetMouseScrollLines()
{
	static UINT uCachedScrollLines;

	// if we've already got it and we're not refreshing,
	// return what we've already got

	if (g_bGotScrollLines)
		return uCachedScrollLines;

	// see if we can find the mouse window

	g_bGotScrollLines = TRUE;

	static UINT msgGetScrollLines;
	static WORD nRegisteredMessage;

	if (nRegisteredMessage == 0)
	{
		msgGetScrollLines = ::RegisterWindowMessage(MSH_SCROLL_LINES);
		if (msgGetScrollLines == 0)
			nRegisteredMessage = 1;     // couldn't register!  never try again
		else
			nRegisteredMessage = 2;     // it worked: use it
	}

	if (nRegisteredMessage == 2)
	{
		HWND hwMouseWheel = NULL;
		hwMouseWheel = ::FindWindow(MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE);
		if (hwMouseWheel && msgGetScrollLines)
		{
			uCachedScrollLines = (UINT)
				::SendMessage(hwMouseWheel, msgGetScrollLines, 0, 0);
			return uCachedScrollLines;
		}
	}

	// couldn't use the window -- try system settings
	uCachedScrollLines = 3; // reasonable default
	DWORD dwVersion = GetVersion();
	if (dwVersion < 0x80000000)              // Windows NT/2000/XP
	{
		HKEY hKey;
		if (RegOpenKeyEx(HKEY_CURRENT_USER,  _T("Control Panel\\Desktop"),
				0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
		{
			TCHAR szData[128];
			DWORD dwKeyDataType;
			DWORD dwDataBufSize = _countof(szData);

			if (RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType,
					(LPBYTE) &szData, &dwDataBufSize) == ERROR_SUCCESS)
			{
				uCachedScrollLines = _tcstoul(szData, NULL, 10);
			}
			RegCloseKey(hKey);
		}
	}
#if _MFC_VER > 0x0421
	else if (!afxData.bWin95)
#else
	else if (!afxData.bWin32s)
#endif
	{
		::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &uCachedScrollLines, 0);
	}

	return uCachedScrollLines;
}


/////////////////////////////////////////////////////////////////////////////
// COXZoomView diagnostics

#ifdef _DEBUG
void COXZoomView::Dump(CDumpContext& dc) const
{
	ASSERT_VALID(this);

	CView::Dump(dc);

	AFX_DUMP1(dc, "\nm_totalLog = ", m_totalLog);
	AFX_DUMP1(dc, "\nm_totalDev = ", m_totalDev);
	AFX_DUMP1(dc, "\nm_pageDev = ", m_pageDev);
	AFX_DUMP1(dc, "\nm_lineDev = ", m_lineDev);
	AFX_DUMP1(dc, "\nm_bCenter = ", m_bCenter);
	AFX_DUMP1(dc, "\nm_bInsideUpdate = ", m_bInsideUpdate);
	AFX_DUMP0(dc, "\nm_nMapMode = ");
	switch (m_nMapMode)
	{
	case MM_NONE:
		AFX_DUMP0(dc, "MM_NONE");
		break;
	case MM_TEXT:
		AFX_DUMP0(dc, "MM_TEXT");
		break;
	case MM_LOMETRIC:
		AFX_DUMP0(dc, "MM_LOMETRIC");
		break;
	case MM_HIMETRIC:
		AFX_DUMP0(dc, "MM_HIMETRIC");
		break;
	case MM_LOENGLISH:
		AFX_DUMP0(dc, "MM_LOENGLISH");
		break;
	case MM_HIENGLISH:
		AFX_DUMP0(dc, "MM_HIENGLISH");
		break;
	case MM_TWIPS:
		AFX_DUMP0(dc, "MM_TWIPS");
		break;
	case MM_ANISOTROPIC:
		AFX_DUMP0(dc, "MM_ANISOTROPIC");
		AFX_DUMP1(dc, "\nm_sizeDev = ", m_sizeDev);
		break;
	default:
		AFX_DUMP0(dc, "*unknown*");
		break;
	}
	AFX_DUMP1(dc, "\nm_nZoomLevel = ", m_nZoomLevel);
	AFX_DUMP1(dc, "\nm_bAlignToBottom = ", m_bAlignToBottom);
	AFX_DUMP1(dc, "\nm_bUseRelativeScrollSizes = ", m_bUseRelativeScrollSizes);
	if(m_bUseRelativeScrollSizes)
	{
		AFX_DUMP1(dc, "\nm_nPagePercent = ", m_nPagePercent);
		AFX_DUMP1(dc, "\nm_nLinePercent = ", m_nLinePercent);
	}
	AFX_DUMP0(dc, "\nm_align = ");
	switch (m_align)
	{
	case ZV_TOPLEFT:
		AFX_DUMP0(dc, "TOPLEFT");
		break;
	case ZV_BOTTOMLEFT:
		AFX_DUMP0(dc, "BOTTOMLEFT");
		break;
	case ZV_CENTER:
		AFX_DUMP0(dc, "CENTER");
		break;
	default:
		AFX_DUMP0(dc, "*illegal*");
		break;
    }
}

void COXZoomView::AssertValid() const
{
	CView::AssertValid();

	switch (m_nMapMode)
	{
	case MM_NONE:
	case MM_TEXT:
	case MM_LOMETRIC:
	case MM_HIMETRIC:
	case MM_LOENGLISH:
	case MM_HIENGLISH:
	case MM_TWIPS:
	case MM_ANISOTROPIC:
		break;
	case MM_ISOTROPIC:
		ASSERT(FALSE); // illegal mapping mode
	default:
		ASSERT(FALSE); // unknown mapping mode
	}
}
#endif //_DEBUG



/////////////////////////////////////////////////////////////////////////////
// CZoomRect
// little helper class for zooming to a rectangle
// DON'T comment om this !!

BOOL CZoomRect::TrackTheMouse(UINT uMessageEnd, CWnd *pWnd, CRect& rectZoom, CPoint ptAnchor)
{
	// return FALSE if mouse is already captured
	if(NULL != CWnd::GetCapture())
		return(FALSE);
	CRect	rectClip;
	MSG		msg;
	// capture the mouse and clip it to the client area
	pWnd->SetCapture();
	pWnd->GetClientRect(rectClip);
	pWnd->ClientToScreen(rectClip);
	::ClipCursor(rectClip);
	// preset member vars
	m_bSuccess = FALSE;
	m_bFirstMove = TRUE;
	m_ptOrig = ptAnchor;
	m_uMessageEnd = uMessageEnd;
	msg.message = WM_NULL;	// safe start value 
	// looks weird, but works real nice
	while(ProcessMsg(&msg, pWnd))
		RetrieveMsg(&msg); 
	// free mouse and clipping
	::ClipCursor(NULL);
	::ReleaseCapture();
	if(!m_bFirstMove)	// at least one WM_MOUSEMOVE processed
	{
		CClientDC dc(pWnd);
        DrawZoomRect(&dc);
	}
	// setup return rect and normalize it
	rectZoom.TopLeft() = m_ptOrig;
	rectZoom.BottomRight() = m_ptLast;
	int ntemp;
	if(rectZoom.left > rectZoom.right)
	{
		ntemp = rectZoom.left;
		rectZoom.left = rectZoom.right;
		rectZoom.right = ntemp;
	}
	if(rectZoom.top > rectZoom.bottom)
	{
		ntemp = rectZoom.top;
		rectZoom.top = rectZoom.bottom;
		rectZoom.bottom = ntemp;
	}
	return(m_bSuccess);
}

void CZoomRect::RetrieveMsg(MSG* pmsg)
{
	// get a message out of the queue
	while(!::PeekMessage(pmsg, 0, 0, 0, PM_REMOVE));
	return;
}

BOOL CZoomRect::ProcessMsg(MSG* pmsg, CWnd* pWnd)
{
	// ending message ?
	if(m_uMessageEnd == pmsg->message)
	{
		m_bSuccess = TRUE;
		CPoint pt(pmsg->lParam);
		UpdateRectangle(pWnd, pt);
		return(FALSE);
	}
	switch(pmsg->message)
	{
		case WM_MOUSEMOVE:
		{
			CPoint pt(pmsg->lParam);
			UpdateRectangle(pWnd, pt);
		}
		    break;
		case WM_CANCELMODE:
			return(FALSE);
		case WM_KEYDOWN:
			if(VK_ESCAPE == pmsg->wParam)
				return(FALSE);
			// else fall through
		default:
			::TranslateMessage(pmsg);
			::DispatchMessage(pmsg);
	}
	return(TRUE);		
}

void CZoomRect::UpdateRectangle(CWnd* pWnd, CPoint& pt)
{
	CClientDC dc(pWnd);
	// if this is the first move, there is no old rect to remove
	if(m_bFirstMove)
		m_bFirstMove = FALSE;
	else	// erase old rect
		DrawZoomRect(&dc);
	m_ptLast = pt;
	DrawZoomRect(&dc);
}

void CZoomRect::DrawZoomRect(CDC* pDC)
{
	int nOldRop = pDC->SetROP2(R2_NOTXORPEN);
	pDC->MoveTo(m_ptOrig);
	pDC->LineTo(m_ptLast.x, m_ptOrig.y);
	pDC->LineTo(m_ptLast);
	pDC->LineTo(m_ptOrig.x, m_ptLast.y);
	pDC->LineTo(m_ptOrig);
	pDC->SetROP2(nOldRop);
}

