2020-09-30 17:12:32 +02:00

1115 lines
30 KiB
C++

#include "precomp.h"
#include "ZoomWnd.h"
#include "resource.h"
#define MIN(x,y) ((x<y)?x:y)
#define MAX(x,y) ((x>y)?x:y)
// CZoomWnd
CZoomWnd::CZoomWnd()
{
m_modeDefault = MODE_ZOOMIN;
m_fPanning = false;
m_fCtrlDown = false;
m_fShiftDown = false;
m_bBestFit = true;
m_cxImage = 1;
m_cyImage = 1;
m_cxCenter = 1;
m_cyCenter = 1;
m_hbitmap = 0;
m_pImgCtx = NULL;
m_cyHScroll = GetSystemMetrics( SM_CYHSCROLL );
m_cxVScroll = GetSystemMetrics( SM_CXVSCROLL );
m_iStrID = IDS_NOPREVIEW;
m_hpal = NULL;
m_fTransparent = false;
}
CZoomWnd::~CZoomWnd()
{
if ( m_pImgCtx )
{
// Do not disconnect, other zoom windows might be using this same interface
m_pImgCtx->Release();
}
}
// OnEraseBkgnd
// Handles WM_ERASEBKGND messages sent to the window
LRESULT CZoomWnd::OnEraseBkgnd(UINT , WPARAM wParam, LPARAM , BOOL& )
{
#define COLOR_PREVIEWBKGND COLOR_WINDOW
RECT rcFill; // rect to fill with background color
HDC hdc = (HDC)wParam;
if ( m_fTransparent )
{
// when we have a transparent image we are forced to erase the entire background.
// This can lead to visual flicker when panning or zooming transparent images
// but I don't know of any other way to handle this.
rcFill.left = 0;
rcFill.top = 0;
rcFill.right = m_cxWindow;
rcFill.bottom = m_cyWindow;
FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
}
else
{
// There are four possible regions that might need to be erased:
// +---+
// | Erase Top |
// +-------+-------+-------+
// | | | |
// | Erase | Image | Erase |
// | Left | | Right |
// +-------+-------+-------+
// | Erase Bottom |
// +---+
// erase the left region
rcFill.left = 0;
rcFill.top = m_ptszDest.y;
rcFill.right = m_ptszDest.x;
rcFill.bottom = m_ptszDest.y + m_ptszDest.cy;
if ( rcFill.right > rcFill.left )
FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
// erase the right region
rcFill.left = m_ptszDest.x + m_ptszDest.cx;
rcFill.right = m_cxWindow;
if ( rcFill.right > rcFill.left )
FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
// erase the top region
rcFill.left = 0;
rcFill.top = 0;
rcFill.right = m_cxWindow;
rcFill.bottom = m_ptszDest.y;
if ( rcFill.bottom > rcFill.top )
FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
// erase the bottom region
rcFill.top = m_ptszDest.y + m_ptszDest.cy;
rcFill.bottom = m_cyWindow;
if ( rcFill.bottom > rcFill.top )
FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
}
return TRUE;
#undef COLOR_PREVIEWBKGND
}
// OnPaint
// Handles WM_PAINT messages sent to the window
LRESULT CZoomWnd::OnPaint(UINT , WPARAM , LPARAM , BOOL& )
{
PAINTSTRUCT ps;
HDC hdcDraw = BeginPaint( &ps );
// setup the destination DC:
SetMapMode( hdcDraw, MM_TEXT );
SetStretchBltMode( hdcDraw, COLORONCOLOR );
if ( m_hpal )
{
SelectPalette(hdcDraw, m_hpal, TRUE);
RealizePalette(hdcDraw);
}
if ( m_pImgCtx )
{
m_pImgCtx->StretchBlt(
hdcDraw,
m_ptszDest.x,
m_ptszDest.y,
m_ptszDest.cx,
m_ptszDest.cy,
0,0,
m_cxImage,
m_cyImage,
SRCCOPY );
}
else if ( m_hbitmap)
{
// create the source DC:
HDC hdcBitmap = CreateCompatibleDC( hdcDraw );
SelectObject( hdcBitmap, m_hbitmap );
// and blit using the destination rectangle info
StretchBlt(
hdcDraw,
m_ptszDest.x,
m_ptszDest.y,
m_ptszDest.cx,
m_ptszDest.cy,
hdcBitmap,
0,0,
m_cxImage,
m_cyImage,
SRCCOPY );
DeleteDC( hdcBitmap );
}
else
{
RECT rc = { 0,0,m_cxWindow,m_cyWindow };
TCHAR szBuf[80];
LOGFONT lf;
HFONT hFont;
HFONT hFontOld;
LoadString( _Module.GetModuleInstance(),
m_iStrID,
szBuf,
sizeof(szBuf) );
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0);
hFont = CreateFontIndirect(&lf);
hFontOld = (HFONT)SelectObject(hdcDraw, hFont);
SetTextColor(hdcDraw, GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(hdcDraw, GetSysColor(COLOR_WINDOW));
FillRect(hdcDraw, &rc, (HBRUSH)(COLOR_WINDOW+1));
DrawText(hdcDraw, szBuf, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdcDraw, hFontOld);
DeleteObject(hFont);
}
EndPaint( &ps );
return 0;
}
// OnSetCursor
// Handles WM_SETCURSOR messages sent to the window.
// This function is a total HackMaster job. I have overloaded it's functionality to the point
// of abserdity. Here's what the parameters mean:
// uMsg == WM_SETCURSOR
// wParam Standard value sent during a WM_SETCURSOR messge.
// lParam Standard value sent during a WM_SETCURSOR messge.
// uMsg == 0
// wParam 0
// lParam If this value is non-zero then it is a packed x,y cursor location.
// If it's zero then we need to query the cursor location
LRESULT CZoomWnd::OnSetCursor(UINT uMsg, WPARAM, LPARAM lParam, BOOL& bHandled)
{
// if this is a legitimate message but isn't intended for the client area, we ignore it.
// we also ignore set cursor when we have no valid bitmap
if ( ((WM_SETCURSOR == uMsg) && (HTCLIENT != LOWORD(lParam))) || (!m_hbitmap && !m_pImgCtx) )
{
bHandled = FALSE;
return 0;
}
else if ( 0 == uMsg )
{
// Since this is one of our fake messages we need to do our own check to test for HTCLIENT.
// we need to find the cursor location
POINT pt;
GetCursorPos( &pt );
lParam = MAKELONG(pt.x, pt.y);
if ( HTCLIENT != SendMessage( WM_NCHITTEST, 0, lParam ) )
{
bHandled = FALSE;
return 0;
}
}
WORD idCur;
if ( m_fPanning )
{
idCur = IDC_CLOSEDHAND;
}
else if ( m_fCtrlDown )
{
idCur = IDC_OPENHAND;
}
else if ( (m_modeDefault == MODE_ZOOMIN) ^ m_fShiftDown )
{
idCur = IDC_ZOOMIN;
}
else
{
idCur = IDC_ZOOMOUT;
}
SetCursor( LoadCursor(_Module.GetModuleInstance(), MAKEINTRESOURCE(idCur)) );
return TRUE;
}
// OnKeyUp
// Handles WM_KEYUP messages sent to the window
LRESULT CZoomWnd::OnKeyUp(UINT , WPARAM wParam, LPARAM , BOOL& bHandled)
{
if ( VK_CONTROL == wParam )
{
m_fCtrlDown = false;
OnSetCursor( 0,0,0, bHandled );
}
else if (VK_SHIFT == wParam)
{
m_fShiftDown = false;
OnSetCursor( 0,0,0, bHandled );
}
bHandled = FALSE;
return 0;
}
// OnKeyDown
// Handles WM_KEYDOWN messages sent to the window
LRESULT CZoomWnd::OnKeyDown(UINT , WPARAM wParam, LPARAM , BOOL& bHandled)
{
// when we return, we want to call the DefWindowProc
bHandled = false;
switch ( wParam )
{
case VK_PRIOR:
OnScroll(WM_VSCROLL, m_fCtrlDown?SB_TOP:SB_PAGEUP, 0, bHandled);
break;
case VK_NEXT:
OnScroll(WM_VSCROLL, m_fCtrlDown?SB_BOTTOM:SB_PAGEDOWN, 0, bHandled);
break;
case VK_END:
OnScroll(WM_HSCROLL, m_fCtrlDown?SB_BOTTOM:SB_PAGEDOWN, 0, bHandled);
break;
case VK_HOME:
OnScroll(WM_HSCROLL, m_fCtrlDown?SB_TOP:SB_PAGEUP, 0, bHandled);
break;
case VK_CONTROL:
case VK_SHIFT:
// if m_fPanning is true then we are already in the middle of an operation so we
// should maintain the cursor for that operation
if ( !m_fPanning )
{
if ( VK_CONTROL == wParam )
{
m_fCtrlDown = true;
}
else if (VK_SHIFT == wParam)
{
m_fShiftDown = true;
}
// Update the cursor based on the key states set above only if we are over our window
OnSetCursor( 0,0,0, bHandled );
}
break;
default:
// if in run screen preview mode any key other than Shift and Control will dismiss the window
if ( NULL == GetParent() )
{
DestroyWindow();
}
return 1; // return non-zero to indicate unprocessed message
}
return 0;
}
// OnMouseUp
// Handles WM_LBUTTONUP messages sent to the window
LRESULT CZoomWnd::OnMouseUp(UINT , WPARAM , LPARAM , BOOL& bHandled)
{
if ( m_fPanning )
ReleaseCapture();
m_fPanning = false;
bHandled = FALSE;
return 0;
}
// OnMouseDown
// Handles WM_LBUTTONDOWN and WM_MBUTTONDOWN messages sent to the window
LRESULT CZoomWnd::OnMouseDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
m_xPosMouse = (short)LOWORD( lParam );
m_yPosMouse = (short)HIWORD( lParam );
ASSERT( m_fPanning == false );
// Holding the CTRL key makes a pan into a zoom and vise-versa.
// The middle mouse button always pans regardless of default mode and key state.
if ( (wParam & MK_CONTROL) || (uMsg == WM_MBUTTONDOWN) )
{
// REVIEW: check for pan being valid here? Should be more efficient than all the checks
// I have to do in OnMouseMove.
m_fPanning = true;
OnSetCursor(0,0,0,bHandled);
SetCapture();
}
else
{
// Holding down the shift key turns a zoomin into a zoomout and vise-versa.
// The "default" zoom mode is zoom in (if mode = pan and ctrl key is down we zoom in).
bool bZoomIn = (m_modeDefault != MODE_ZOOMOUT) ^ ((wParam & MK_SHIFT)?1:0);
// Find the point we want to stay centered on:
m_cxCenter = MulDiv( m_xPosMouse-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
m_cyCenter = MulDiv( m_yPosMouse-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
bZoomIn?ZoomIn():ZoomOut();
}
bHandled = FALSE;
return 0;
}
void CZoomWnd::Zoom( WPARAM wParam, LPARAM lParam )
{
switch (wParam&0xFF)
{
case IVZ_CENTER:
break;
case IVZ_POINT:
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
if ( x<0 ) x=0;
else if ( x>=m_cxImage ) x = m_cxImage-1;
if ( y<0 ) y=0;
else if ( y>=m_cyImage ) y = m_cyImage-1;
m_cxCenter = x;
m_cyCenter = y;
}
break;
case IVZ_RECT:
{
LPRECT prc = (LPRECT)lParam;
int x = (prc->left+prc->right)/2;
int y = (prc->top+prc->bottom)/2;
if ( x<0 ) x=0;
else if ( x>=m_cxImage ) x = m_cxImage-1;
if ( y<0 ) y=0;
else if ( y>=m_cyImage ) y = m_cyImage-1;
m_cxCenter = x;
m_cyCenter = y;
// TODO: This should really completely adjust the dest rect but I have to
// check for any assumptions about aspect ratio before I allow this absolute
// aspect ignoring zoom mode.
}
break;
}
if ( wParam&IVZ_ZOOMOUT )
ZoomOut();
else
ZoomIn();
}
void CZoomWnd::ZoomIn()
{
if ( m_pImgCtx || m_hbitmap )
{
m_bBestFit = false;
// first, the height is adjusted by the amount the mouse cursor moved.
m_ptszDest.cy = (LONG)/*ceil*/(m_ptszDest.cy*1.200); // ceil is required in order to zoom in
// on 4px high or less image
// we don't allow zooming beyond 16x the full size of the image
if ( m_ptszDest.cy >= m_cyImage*16 )
{
m_ptszDest.cy = m_cyImage*16;
}
// next, a new width is calculated based on the original image dimentions and the new height
m_ptszDest.cx = MulDiv( m_ptszDest.cy, m_cxImage, m_cyImage );
AdjustRectPlacement();
}
}
void CZoomWnd::ZoomOut()
{
if ( m_pImgCtx || m_hbitmap )
{
// if the destination rect already fits within the window, don't allow a zoom out.
// This check is to prevent a redraw that would occure otherwise
if ((m_ptszDest.cx <= MIN(m_cxWindow,m_cxImage)) &&
(m_ptszDest.cy <= MIN(m_cyWindow,m_cyImage)))
{
m_bBestFit = true;
return;
}
// first, the height is adjusted by the amount the mouse cursor moved.
m_ptszDest.cy = (LONG)/*floor*/(m_ptszDest.cy*0.833); // floor is default behavior
// next, a new width is calculated based on the original image dimentions and the new height
m_ptszDest.cx = MulDiv( m_ptszDest.cy, m_cxImage, m_cyImage );
AdjustRectPlacement();
}
}
// OnMouseMove
// Handles WM_MOUSEMOVE messages sent to the control
LRESULT CZoomWnd::OnMouseMove(UINT , WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// This is something of a hack since I never recieve the keyboard focus
if ( wParam & MK_CONTROL )
m_fCtrlDown = true;
else
m_fCtrlDown = false;
if ( wParam & MK_SHIFT )
m_fShiftDown = true;
else
m_fShiftDown = false;
// we only care about mouse move when the middle or left button is down
// and we have a valid bitmap handle and we are panning
if ( !(wParam & (MK_LBUTTON|MK_MBUTTON)) || !m_fPanning || (!m_hbitmap && !m_pImgCtx) )
{
bHandled = false;
return TRUE;
}
// we know we are panning when we reach this point
ASSERT( m_fPanning );
POINTS pt = MAKEPOINTS( lParam );
PTSZ ptszDest;
ptszDest.cx = m_ptszDest.cx;
ptszDest.cy = m_ptszDest.cy;
// only allow side-to-side panning if it's needed
if ( m_ptszDest.cx > m_cxWindow )
{
ptszDest.x = m_ptszDest.x + pt.x - m_xPosMouse;
}
else
{
ptszDest.x = m_ptszDest.x;
}
// only allow up-and-down panning if it's needed
if ( m_ptszDest.cy > m_cyWindow )
{
ptszDest.y = m_ptszDest.y + pt.y - m_yPosMouse;
}
else
{
ptszDest.y = m_ptszDest.y;
}
// if the image is now smaller than the window, center it
// if the image is now panned when it shouldn't be, adjust the possition
if ( ptszDest.cx < m_cxWindow )
ptszDest.x = (m_cxWindow-ptszDest.cx)/2;
else
{
if ( ptszDest.x < (m_cxWindow - ptszDest.cx) )
ptszDest.x = m_cxWindow - ptszDest.cx;
if ( ptszDest.x > 0 )
ptszDest.x = 0;
}
if ( ptszDest.cy < m_cyWindow )
ptszDest.y = (m_cyWindow-ptszDest.cy)/2;
else
{
if ( ptszDest.y < (m_cyWindow - ptszDest.cy) )
ptszDest.y = m_cyWindow - ptszDest.cy;
if ( ptszDest.y > 0 )
ptszDest.y = 0;
}
m_xPosMouse = pt.x;
m_yPosMouse = pt.y;
// ensure the scroll bars are correct
SetScrollBars();
// if anything has changed, we must invalidate the window to force a repaint
if ( (ptszDest.x != m_ptszDest.x) || (ptszDest.y != m_ptszDest.y) ||
(ptszDest.cx != m_ptszDest.cx) || (ptszDest.y != m_ptszDest.y ))
{
// REVIEW: Is it worth it to calculate rcInvalid? Should I use NULL instead?
RECT rcInvalid;
rcInvalid.left = MIN( ptszDest.x, m_ptszDest.x );
rcInvalid.top = MIN( ptszDest.y, m_ptszDest.y );
rcInvalid.right = MAX( ptszDest.x + ptszDest.cx, m_ptszDest.x + m_ptszDest.cx );
rcInvalid.bottom = MAX( ptszDest.y + ptszDest.cy, m_ptszDest.y + m_ptszDest.cy );
m_ptszDest = ptszDest;
InvalidateRect( &rcInvalid );
}
// Update m_cxCenter and m_cyCenter so that a zoom after a pan will zoom in
// on the correct area. This is majorly annoying otherwise. We want the
// new center to be whatever is in the center of the window after we pan.
m_cxCenter = MulDiv( m_cxWindow/2-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
m_cyCenter = MulDiv( m_cyWindow/2-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
return TRUE;
}
// OnSize
// Handles WM_SIZE messages set to the window
LRESULT CZoomWnd::OnSize(UINT , WPARAM , LPARAM lParam, BOOL& )
{
m_cxWindow = LOWORD(lParam);
m_cyWindow = HIWORD(lParam);
if ( m_bBestFit )
{
BestFit();
}
else
{
// The size of the rect doesn't change in this case, so just reposition
AdjustRectPlacement();
}
return TRUE;
}
// SetMode
// Sets the current mouse mode to one of the values specified in the MODE enumeration.
// Currently there are two modes, pan and zoom. The mode effects the default mouse
// cursor when moving over the zoom window and the behavior of a click-and-drag with the
// left mouse button. Holding the shift key effects the result of a click-and-drag but
// does not effect m_mode, which is the default when the shift key isn't down.
bool CZoomWnd::SetMode(MODE modeNew)
{
if ( m_modeDefault == modeNew )
return false;
m_modeDefault = modeNew;
return true;
}
// ActualSize
// Displays image zoomed to its full size
void CZoomWnd::ActualSize()
{
m_bBestFit = FALSE;
// actual size means same sixe as the image
m_ptszDest.cx = m_cxImage;
m_ptszDest.cy = m_cyImage;
// we center the image
m_ptszDest.x = (m_cxWindow-m_cxImage)/2;
m_ptszDest.y = (m_cyWindow-m_cyImage)/2;
// Setting actual size is a zoom operation. Whenever we zoom we update our centerpoint.
m_cxCenter = m_cxImage/2;
m_cyCenter = m_cyImage/2;
// turn scoll bars on/off as needed
SetScrollBars();
InvalidateRect( NULL );
}
// BestFit
// Computes the default location for the destination rectangle. This rectangle is a
// best fit while maintaining aspect ratio within a window of the given width and height.
// If the window is larger than the image, the image is centered, otherwise it is scaled
// to fit within the window. The destination rectange is computed in the client coordinates
// of the window whose width and height are passed as arguments (ie we assume the point 0,0
// is the upper left corner of the window).
void CZoomWnd::BestFit()
{
m_bBestFit = true;
// if scroll bars are on, adjust the client size to what it will be once they are off
DWORD dwStyle = GetWindowLong(GWL_STYLE);
if ( dwStyle & (WS_VSCROLL|WS_HSCROLL) )
{
m_cxWindow += (dwStyle&WS_VSCROLL)?m_cxVScroll:0;
m_cyWindow += (dwStyle&WS_HSCROLL)?m_cyHScroll:0;
}
// Determine the limiting axis, if any.
if ( m_cxImage <= m_cxWindow && m_cyImage <= m_cyWindow )
{
// item fits centered within window
m_ptszDest.x = (m_cxWindow-m_cxImage)/2;
m_ptszDest.y = (m_cyWindow-m_cyImage)/2;
m_ptszDest.cx = m_cxImage;
m_ptszDest.cy = m_cyImage;
}
else if ( m_cxImage * m_cyWindow < m_cxWindow * m_cyImage )
{
// height is the limiting factor
int iNewWidth = MulDiv(m_cyWindow, m_cxImage, m_cyImage);
m_ptszDest.x = (m_cxWindow-iNewWidth)/2;
m_ptszDest.y = 0;
m_ptszDest.cx = iNewWidth;
m_ptszDest.cy = m_cyWindow;
}
else
{
// width is the limiting factor
int iNewHeight = MulDiv(m_cxWindow, m_cyImage, m_cxImage);
m_ptszDest.x = 0;
m_ptszDest.y = (m_cyWindow-iNewHeight)/2;
m_ptszDest.cx = m_cxWindow;
m_ptszDest.cy = iNewHeight;
}
// this should turn off the scroll bars if they are on
if ( dwStyle & (WS_VSCROLL|WS_HSCROLL) )
{
SetScrollBars();
}
// ensure the scroll bars are now off
ASSERT( 0 == (GetWindowLong(GWL_STYLE)&(WS_VSCROLL|WS_HSCROLL)) );
InvalidateRect( NULL );
}
// AdjustRectPlacement
// This function determines the optimal placement of the destination rectangle. This may
// include resizing the destination rectangle if it is smaller than the "best fit" rectangle
// but it is primarily intended for repossitioning the rectange due to a change in the window
// size or destination rectangle size. The window is repositioned so that the centered point
// remains in the center of the window.
void CZoomWnd::AdjustRectPlacement()
{
// if we have scroll bars ...
DWORD dwStyle = GetWindowLong(GWL_STYLE);
if ( dwStyle&(WS_VSCROLL|WS_HSCROLL) )
{
// .. and if removing scroll bars would allow the image to fit ...
if ( (m_ptszDest.cx < (m_cxWindow + ((dwStyle&WS_VSCROLL)?m_cxVScroll:0))) &&
(m_ptszDest.cy < (m_cyWindow + ((dwStyle&WS_HSCROLL)?m_cyHScroll:0))) )
{
// ... remove the scroll bars
m_cxWindow += (dwStyle&WS_VSCROLL)?m_cxVScroll:0;
m_cyWindow += (dwStyle&WS_HSCROLL)?m_cyHScroll:0;
SetScrollBars();
}
}
// If the dest rect is smaller than the window ...
if ( (m_ptszDest.cx < m_cxWindow) && (m_ptszDest.cy < m_cyWindow) )
{
// ... then it must be larger than the image. Oterwise we switch
// to "best fit" mode.
if ( (m_ptszDest.cx < m_cxImage) && (m_ptszDest.cy < m_cyImage) )
{
BestFit();
return;
}
}
// given the window size, client area size, and dest rect size calculate the
// dest rect position. This position is then restrained by the limits below.
m_ptszDest.x = (m_cxWindow/2) - MulDiv( m_cxCenter, m_ptszDest.cx, m_cxImage);
m_ptszDest.y = (m_cyWindow/2) - MulDiv( m_cyCenter, m_ptszDest.cy, m_cyImage);
// if the image is now narrower than the window ...
if ( m_ptszDest.cx < m_cxWindow )
{
// ... center the image
m_ptszDest.x = (m_cxWindow-m_ptszDest.cx)/2;
}
else
{
// if the image is now panned when it shouldn't be, adjust the possition
if ( m_ptszDest.x < (m_cxWindow - m_ptszDest.cx) )
m_ptszDest.x = m_cxWindow - m_ptszDest.cx;
if ( m_ptszDest.x > 0 )
m_ptszDest.x = 0;
}
// if the image is now shorter than the window ...
if ( m_ptszDest.cy < m_cyWindow )
{
// ... center the image
m_ptszDest.y = (m_cyWindow-m_ptszDest.cy)/2;
}
else
{
// if the image is now panned when it shouldn't be, adjust the possition
if ( m_ptszDest.y < (m_cyWindow - m_ptszDest.cy) )
m_ptszDest.y = m_cyWindow - m_ptszDest.cy;
if ( m_ptszDest.y > 0 )
m_ptszDest.y = 0;
}
SetScrollBars();
InvalidateRect( NULL );
}
// StatusUpdate
// Sent when the image generation status has changed, once when the image is first
// being created and again if there is an error of any kind.
void CZoomWnd::StatusUpdate( int iStatus )
{
if ( m_pImgCtx )
{
m_pImgCtx->Release();
m_pImgCtx = 0;
}
m_hbitmap = 0;
m_fTransparent = false;
m_iStrID = iStatus;
if ( m_hWnd )
{
InvalidateRect(NULL);
}
}
// SetBitmap
// Called to pass in the handle to the bitmap we draw. We DO NOT own this bitmap, it
// is a duplicate handle. We use it to paint only, it is freed by the parent window.
void CZoomWnd::SetBitmap(HBITMAP hbitmap)
{
if ( m_pImgCtx )
{
m_pImgCtx->Release();
m_pImgCtx = 0;
}
m_fTransparent = false;
m_hbitmap = hbitmap;
if (m_hbitmap)
{
BITMAP bm;
if (GetObject(m_hbitmap, sizeof(bm), &bm))
{
// this message indicates a successful result
m_cxImage = bm.bmWidth;
m_cyImage = bm.bmHeight;
m_cxCenter = m_cxImage/2;
m_cyCenter = m_cyImage/2;
// this message could be recieved before the window is created.
// Make sure m_hWnd is valid before calling BestFit or InvalidateRect
if (m_hWnd)
{
BestFit();
InvalidateRect(NULL);
}
return;
}
}
m_iStrID = IDS_LOADFAILED;
}
// SetImgCtx
// Called to pass in the pointer to the IImgCtx we draw. We hold a reference to this
// object so that we can use it to paint.
void CZoomWnd::SetImgCtx(IImgCtx * pImg)
{
m_hbitmap = 0;
if ( m_pImgCtx )
{
m_pImgCtx->Release();
}
m_fTransparent = false;
m_pImgCtx = pImg;
if (m_pImgCtx)
{
SIZE size;
DWORD fState;
m_pImgCtx->AddRef();
if ( SUCCEEDED(m_pImgCtx->GetStateInfo(&fState, &size, FALSE)) )
{
ASSERT( fState & IMGLOAD_COMPLETE );
m_fTransparent = (0 == (fState & IMGTRANS_OPAQUE));
m_cxImage = size.cx;
m_cyImage = size.cy;
m_cxCenter = m_cxImage/2;
m_cyCenter = m_cyImage/2;
if (m_hWnd)
{
BestFit();
InvalidateRect(NULL);
}
}
return;
}
m_iStrID = IDS_LOADFAILED;
}
void CZoomWnd::SetPalette( HPALETTE hpal )
{
m_hpal = hpal;
}
void CZoomWnd::SetScrollBars()
{
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = m_ptszDest.cx;
si.nPage = m_cxWindow+1;
si.nPos = 0-m_ptszDest.x;
si.nTrackPos = 0;
SetScrollInfo( m_hWnd, SB_HORZ, &si, true );
si.nMax = m_ptszDest.cy;
si.nPage = m_cyWindow+1;
si.nPos = 0-m_ptszDest.y;
SetScrollInfo( m_hWnd, SB_VERT, &si, true );
}
LRESULT CZoomWnd::OnScroll(UINT uMsg, WPARAM wParam, LPARAM , BOOL& )
{
int iScrollBar;
int iWindow; // width or height of the window
LONG * piTL; // pointer to top or left point
LONG iWH; // the width or height of the dest rect
if ( (!m_hbitmap && !m_pImgCtx) )
return 0;
// handle both which direction we're scrolling
if ( WM_HSCROLL==uMsg )
{
iScrollBar = SB_HORZ;
iWindow = m_cxWindow;
piTL = &m_ptszDest.x;
iWH = m_ptszDest.cx;
}
else
{
iScrollBar = SB_VERT;
iWindow = m_cyWindow;
piTL = &m_ptszDest.y;
iWH = m_ptszDest.cy;
}
// Using the keyboard we can get scroll messages when we don't have scroll bars.
// Ignore these messages.
if ( iWindow >= iWH )
{
// window is larger than the image, don't allow scrolling
return 0;
}
// handle all possible scroll cases
switch ( LOWORD(wParam) )
{
case SB_TOP:
*piTL = 0;
break;
case SB_PAGEUP:
*piTL += iWindow;
break;
case SB_LINEUP:
(*piTL)++;
break;
case SB_LINEDOWN:
(*piTL)--;
break;
case SB_PAGEDOWN:
*piTL -= iWindow;
break;
case SB_BOTTOM:
*piTL = iWindow-iWH;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
*piTL = -HIWORD(wParam);
break;
case SB_ENDSCROLL:
return 0;
}
// apply limits
if ( 0 < *piTL )
*piTL = 0;
else if ( (iWindow-iWH) > *piTL )
*piTL = iWindow-iWH;
// adjust scrollbars
SetScrollPos(iScrollBar, -(*piTL), true);
// calculate new center point relative to image
if ( WM_HSCROLL==uMsg )
{
m_cxCenter = MulDiv( (m_cxWindow/2)-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
}
else
{
m_cyCenter = MulDiv( (m_cyWindow/2)-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
}
InvalidateRect( NULL );
return 0;
}
/*
LRESULT CZoomWnd::OnHScroll(UINT , WPARAM wParam, LPARAM , BOOL& )
{
if ( !m_hbitmap )
return 0;
// handle all possible scroll cases
switch ( LOWORD(wParam) )
{
case SB_TOP:
m_ptszDest.x = 0;
break;
case SB_PAGEUP:
m_ptszDest.x += m_cxWindow;
break;
case SB_LINEUP:
m_ptszDest.x++;
break;
case SB_LINEDOWN:
m_ptszDest.x--;
break;
case SB_PAGEDOWN:
m_ptszDest.x -= m_cxWindow;
break;
case SB_BOTTOM:
m_ptszDest.x = m_cxWindow-m_ptszDest.cx;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
m_ptszDest.x = -HIWORD(wParam);
break;
case SB_ENDSCROLL:
return 0;
}
// apply limits
if ( 0 < m_ptszDest.x )
m_ptszDest.x = 0;
else if ( m_cxWindow-m_ptszDest.cx > m_ptszDest.x )
m_ptszDest.x = m_cxWindow-m_ptszDest.cx;
// adjust scrollbars
SetScrollPos(SB_HORZ, -m_ptszDest.x, true);
// calculate new center point relative to image
m_cxCenter = MulDiv( (m_cxWindow/2)-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
// redraw
InvalidateRect( NULL );
return 0;
}
LRESULT CZoomWnd::OnVScroll(UINT , WPARAM wParam, LPARAM , BOOL& )
{
if ( !m_hbitmap )
return 0;
// handle all possible scroll cases
switch ( LOWORD(wParam) )
{
case SB_TOP:
m_ptszDest.y = 0;
break;
case SB_PAGEUP:
m_ptszDest.y += m_cyWindow;
break;
case SB_LINEUP:
m_ptszDest.y++;
break;
case SB_LINEDOWN:
m_ptszDest.y--;
break;
case SB_PAGEDOWN:
m_ptszDest.y -= m_cyWindow;
break;
case SB_BOTTOM:
m_ptszDest.y = m_cyWindow-m_ptszDest.cy;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
m_ptszDest.y = -HIWORD(wParam);
break;
case SB_ENDSCROLL:
return 0;
}
// apply limits
if ( 0 < m_ptszDest.y )
m_ptszDest.y = 0;
else if ( m_cyWindow-m_ptszDest.cy > m_ptszDest.y )
m_ptszDest.y = m_cyWindow-m_ptszDest.cy;
// adjust scrollbars
SetScrollPos(SB_VERT, -m_ptszDest.y, true);
// calculate new center point relative to image
m_cyCenter = MulDiv( (m_cyWindow/2)-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
// redraw
InvalidateRect( NULL );
return 0;
}
*/
// OnWheelTurn
// Respondes to WM_MOUSEWHEEL messages sent to the parent window (then redirected here)
LRESULT CZoomWnd::OnWheelTurn(UINT , WPARAM wParam, LPARAM , BOOL& )
{
bool bZoomIn = ((short)HIWORD(wParam) > 0);
bZoomIn?ZoomIn():ZoomOut();
return TRUE;
}
LRESULT CZoomWnd::OnSetFocus(UINT , WPARAM , LPARAM , BOOL& )
{
HWND hwndParent = GetParent();
::SetFocus( hwndParent );
return 0;
}