zoomwnd.cpp
上传用户:xhy777
上传日期:2007-02-14
资源大小:24088k
文件大小:31k
源码类别:

系统编程

开发平台:

Visual C++

  1. #include "precomp.h"
  2. #include "ZoomWnd.h"
  3. #include "resource.h"
  4. #define MIN(x,y)    ((x<y)?x:y)
  5. #define MAX(x,y)    ((x>y)?x:y)
  6. /////////////////////////////////////////////////////////////////////////////
  7. // CZoomWnd
  8. CZoomWnd::CZoomWnd()
  9. {
  10.     m_modeDefault = MODE_ZOOMIN;
  11.     m_fPanning = false;
  12.     m_fCtrlDown = false;
  13.     m_fShiftDown = false;
  14.     m_bBestFit = true;
  15.     m_cxImage = 1;
  16.     m_cyImage = 1;
  17.     m_cxCenter = 1;
  18.     m_cyCenter = 1;
  19.     m_hbitmap = 0;
  20.     m_pImgCtx = NULL;
  21.     m_cyHScroll = GetSystemMetrics( SM_CYHSCROLL );
  22.     m_cxVScroll = GetSystemMetrics( SM_CXVSCROLL );
  23.     m_iStrID = IDS_NOPREVIEW;
  24.     m_hpal = NULL;
  25.     m_fTransparent = false;
  26. }
  27. CZoomWnd::~CZoomWnd()
  28. {
  29.     if ( m_pImgCtx )
  30.     {
  31.         // Do not disconnect, other zoom windows might be using this same interface
  32.         m_pImgCtx->Release();
  33.     }
  34. }
  35. // OnEraseBkgnd
  36. //
  37. // Handles WM_ERASEBKGND messages sent to the window
  38. LRESULT CZoomWnd::OnEraseBkgnd(UINT , WPARAM wParam, LPARAM , BOOL& )
  39. {
  40. #define COLOR_PREVIEWBKGND COLOR_WINDOW
  41.     RECT rcFill;    // rect to fill with background color
  42.     HDC hdc = (HDC)wParam;
  43.     if ( m_fTransparent )
  44.     {
  45.         // when we have a transparent image we are forced to erase the entire background.
  46.         // This can lead to visual flicker when panning or zooming transparent images
  47.         // but I don't know of any other way to handle this.
  48.         rcFill.left = 0;
  49.         rcFill.top = 0;
  50.         rcFill.right = m_cxWindow;
  51.         rcFill.bottom = m_cyWindow;
  52.         
  53.         FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
  54.     }
  55.     else
  56.     {
  57.         // There are four possible regions that might need to be erased:
  58.         //      +-----------------------+
  59.         //      |       Erase Top       |
  60.         //      +-------+-------+-------+
  61.         //      |       |       |       |
  62.         //      | Erase | Image | Erase |
  63.         //      | Left  |       | Right |
  64.         //      +-------+-------+-------+
  65.         //      |     Erase Bottom      |
  66.         //      +-----------------------+
  67.         // erase the left region
  68.         rcFill.left = 0;
  69.         rcFill.top = m_ptszDest.y;
  70.         rcFill.right = m_ptszDest.x;
  71.         rcFill.bottom = m_ptszDest.y + m_ptszDest.cy;
  72.         if ( rcFill.right > rcFill.left )
  73.             FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
  74.         // erase the right region
  75.         rcFill.left = m_ptszDest.x + m_ptszDest.cx;
  76.         rcFill.right = m_cxWindow;
  77.         if ( rcFill.right > rcFill.left )
  78.             FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
  79.         // erase the top region
  80.         rcFill.left = 0;
  81.         rcFill.top = 0;
  82.         rcFill.right = m_cxWindow;
  83.         rcFill.bottom = m_ptszDest.y;
  84.         if ( rcFill.bottom > rcFill.top )
  85.             FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
  86.         // erase the bottom region
  87.         rcFill.top = m_ptszDest.y + m_ptszDest.cy;
  88.         rcFill.bottom = m_cyWindow;
  89.         if ( rcFill.bottom > rcFill.top )
  90.             FillRect( hdc, &rcFill, (HBRUSH)(COLOR_PREVIEWBKGND+1));
  91.     }
  92.     return TRUE;
  93. #undef COLOR_PREVIEWBKGND
  94. }
  95. // OnPaint
  96. //
  97. // Handles WM_PAINT messages sent to the window
  98. LRESULT CZoomWnd::OnPaint(UINT , WPARAM , LPARAM , BOOL& )
  99. {
  100.     PAINTSTRUCT ps;
  101.     HDC hdcDraw = BeginPaint( &ps );
  102.     // setup the destination DC:
  103.     SetMapMode( hdcDraw, MM_TEXT );
  104.     SetStretchBltMode( hdcDraw, COLORONCOLOR );
  105.     if ( m_hpal )
  106.     {
  107.         SelectPalette(hdcDraw, m_hpal, TRUE);
  108.         RealizePalette(hdcDraw);
  109.     }
  110.     if ( m_pImgCtx )
  111.     {
  112.         m_pImgCtx->StretchBlt(
  113.             hdcDraw,
  114.             m_ptszDest.x,
  115.             m_ptszDest.y,
  116.             m_ptszDest.cx,
  117.             m_ptszDest.cy,
  118.             0,0,
  119.             m_cxImage,
  120.             m_cyImage,
  121.             SRCCOPY );
  122.     }
  123.     else if ( m_hbitmap)
  124.     {
  125.         // create the source DC:
  126.         HDC hdcBitmap = CreateCompatibleDC( hdcDraw );
  127.         SelectObject( hdcBitmap, m_hbitmap );
  128.         // and blit using the destination rectangle info
  129.         StretchBlt(
  130.             hdcDraw,
  131.             m_ptszDest.x,
  132.             m_ptszDest.y,
  133.             m_ptszDest.cx,
  134.             m_ptszDest.cy,
  135.             hdcBitmap,
  136.             0,0,
  137.             m_cxImage,
  138.             m_cyImage,
  139.             SRCCOPY );
  140.         DeleteDC( hdcBitmap );
  141.     }
  142.     else
  143.     {
  144.         RECT rc = { 0,0,m_cxWindow,m_cyWindow };
  145.         TCHAR szBuf[80];
  146.         LOGFONT lf;
  147.         HFONT hFont;
  148.         HFONT hFontOld;
  149.         LoadString( _Module.GetModuleInstance(),
  150.                     m_iStrID,
  151.                     szBuf,
  152.                     sizeof(szBuf) );
  153.         SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0);
  154.         hFont = CreateFontIndirect(&lf);
  155.         hFontOld = (HFONT)SelectObject(hdcDraw, hFont);
  156.         SetTextColor(hdcDraw, GetSysColor(COLOR_WINDOWTEXT));
  157.         SetBkColor(hdcDraw, GetSysColor(COLOR_WINDOW));
  158.         FillRect(hdcDraw, &rc, (HBRUSH)(COLOR_WINDOW+1));
  159.         DrawText(hdcDraw, szBuf, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  160.         SelectObject(hdcDraw, hFontOld);
  161.         DeleteObject(hFont);
  162.     }
  163.     EndPaint( &ps );
  164.     return 0;
  165. }
  166. // OnSetCursor
  167. //
  168. // Handles WM_SETCURSOR messages sent to the window.
  169. //
  170. // This function is a total HackMaster job.  I have overloaded it's functionality to the point
  171. // of abserdity.  Here's what the parameters mean:
  172. //
  173. // uMsg == WM_SETCURSOR
  174. //      wParam  Standard value sent during a WM_SETCURSOR messge.
  175. //      lParam  Standard value sent during a WM_SETCURSOR messge.
  176. //
  177. // uMsg == 0
  178. //      wParam  0
  179. //      lParam  If this value is non-zero then it is a packed x,y cursor location.
  180. //              If it's zero then we need to query the cursor location
  181. LRESULT CZoomWnd::OnSetCursor(UINT uMsg, WPARAM, LPARAM lParam, BOOL& bHandled)
  182. {
  183.     // if this is a legitimate message but isn't intended for the client area, we ignore it.
  184.     // we also ignore set cursor when we have no valid bitmap
  185.     if ( ((WM_SETCURSOR == uMsg) && (HTCLIENT != LOWORD(lParam))) || (!m_hbitmap && !m_pImgCtx) )
  186.     {
  187.         bHandled = FALSE;
  188.         return 0;
  189.     }
  190.     else if ( 0 == uMsg )
  191.     {
  192.         // Since this is one of our fake messages we need to do our own check to test for HTCLIENT.
  193.         // we need to find the cursor location
  194.         POINT pt;
  195.         GetCursorPos( &pt );
  196.         lParam = MAKELONG(pt.x, pt.y);
  197.         if ( HTCLIENT != SendMessage( WM_NCHITTEST, 0, lParam ) )
  198.         {
  199.             bHandled = FALSE;
  200.             return 0;
  201.         }
  202.     }
  203.     WORD idCur;
  204.     if ( m_fPanning )
  205.     {
  206.         idCur = IDC_CLOSEDHAND;
  207.     }
  208.     else if ( m_fCtrlDown )
  209.     {
  210.         idCur = IDC_OPENHAND;
  211.     }
  212.     else if ( (m_modeDefault == MODE_ZOOMIN) ^ m_fShiftDown )
  213.     {
  214.         idCur = IDC_ZOOMIN;
  215.     }
  216.     else
  217.     {
  218.         idCur = IDC_ZOOMOUT;
  219.     }
  220.     SetCursor( LoadCursor(_Module.GetModuleInstance(), MAKEINTRESOURCE(idCur)) );
  221.     return TRUE;
  222. }
  223. // OnKeyUp
  224. //
  225. // Handles WM_KEYUP messages sent to the window
  226. LRESULT CZoomWnd::OnKeyUp(UINT , WPARAM wParam, LPARAM , BOOL& bHandled)
  227. {
  228.     if ( VK_CONTROL == wParam )
  229.     {
  230.         m_fCtrlDown = false;
  231.         OnSetCursor( 0,0,0, bHandled );
  232.     }
  233.     else if (VK_SHIFT == wParam)
  234.     {
  235.         m_fShiftDown = false;
  236.         OnSetCursor( 0,0,0, bHandled );
  237.     }
  238.     bHandled = FALSE;
  239.     return 0;
  240. }
  241.   
  242. // OnKeyDown
  243. //
  244. // Handles WM_KEYDOWN messages sent to the window
  245. LRESULT CZoomWnd::OnKeyDown(UINT , WPARAM wParam, LPARAM , BOOL& bHandled)
  246. {
  247.     // when we return, we want to call the DefWindowProc
  248.     bHandled = false;
  249.     switch ( wParam )
  250.     {
  251.     case VK_PRIOR:
  252.         OnScroll(WM_VSCROLL, m_fCtrlDown?SB_TOP:SB_PAGEUP, 0, bHandled);
  253.         break;
  254.     case VK_NEXT:
  255.         OnScroll(WM_VSCROLL, m_fCtrlDown?SB_BOTTOM:SB_PAGEDOWN, 0, bHandled);
  256.         break;
  257.     case VK_END:
  258.         OnScroll(WM_HSCROLL, m_fCtrlDown?SB_BOTTOM:SB_PAGEDOWN, 0, bHandled);
  259.         break;
  260.     case VK_HOME:
  261.         OnScroll(WM_HSCROLL, m_fCtrlDown?SB_TOP:SB_PAGEUP, 0, bHandled);
  262.         break;
  263.     case VK_CONTROL:
  264.     case VK_SHIFT:
  265.         // if m_fPanning is true then we are already in the middle of an operation so we
  266.         // should maintain the cursor for that operation
  267.         if ( !m_fPanning )
  268.         {
  269.             if ( VK_CONTROL == wParam )
  270.             {
  271.                 m_fCtrlDown = true;
  272.             }
  273.             else if (VK_SHIFT == wParam)
  274.             {
  275.                 m_fShiftDown = true;
  276.             }
  277.             // Update the cursor based on the key states set above only if we are over our window
  278.             OnSetCursor( 0,0,0, bHandled );
  279.         }
  280.         break;
  281.     default:
  282.         // if in run screen preview mode any key other than Shift and Control will dismiss the window
  283.         if ( NULL == GetParent() )
  284.         {
  285.             DestroyWindow();
  286.         }
  287.         return 1;   // return non-zero to indicate unprocessed message
  288.     }
  289.     return 0;
  290. }
  291. // OnMouseUp
  292. //
  293. // Handles WM_LBUTTONUP messages sent to the window
  294. LRESULT CZoomWnd::OnMouseUp(UINT , WPARAM , LPARAM , BOOL& bHandled)
  295. {
  296.     if ( m_fPanning )
  297.         ReleaseCapture();
  298.     m_fPanning = false;
  299.     bHandled = FALSE;
  300.     return 0;
  301. }
  302. // OnMouseDown
  303. //
  304. // Handles WM_LBUTTONDOWN and WM_MBUTTONDOWN messages sent to the window
  305. LRESULT CZoomWnd::OnMouseDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  306. {
  307.     m_xPosMouse = (short)LOWORD( lParam );
  308.     m_yPosMouse = (short)HIWORD( lParam );
  309.     ASSERT( m_fPanning == false );
  310.     // Holding the CTRL key makes a pan into a zoom and vise-versa.
  311.     // The middle mouse button always pans regardless of default mode and key state.
  312.     if ( (wParam & MK_CONTROL) || (uMsg == WM_MBUTTONDOWN) )
  313.     {
  314.         // REVIEW: check for pan being valid here?  Should be more efficient than all the checks
  315.         // I have to do in OnMouseMove.
  316.         m_fPanning = true;
  317.         OnSetCursor(0,0,0,bHandled);
  318.         SetCapture();
  319.     }
  320.     else
  321.     {
  322.         // Holding down the shift key turns a zoomin into a zoomout and vise-versa.
  323.         // The "default" zoom mode is zoom in (if mode = pan and ctrl key is down we zoom in).
  324.         bool bZoomIn = (m_modeDefault != MODE_ZOOMOUT) ^ ((wParam & MK_SHIFT)?1:0);
  325.         // Find the point we want to stay centered on:
  326.         m_cxCenter = MulDiv( m_xPosMouse-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
  327.         m_cyCenter = MulDiv( m_yPosMouse-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
  328.         bZoomIn?ZoomIn():ZoomOut();
  329.     }
  330.     bHandled = FALSE;
  331.     return 0;
  332. }
  333. void CZoomWnd::Zoom( WPARAM wParam, LPARAM lParam )
  334. {
  335.     switch (wParam&0xFF)
  336.     {
  337.     case IVZ_CENTER:
  338.         break;
  339.     case IVZ_POINT:
  340.         {
  341.             int x = LOWORD(lParam);
  342.             int y = HIWORD(lParam);
  343.             if ( x<0 ) x=0;
  344.             else if ( x>=m_cxImage ) x = m_cxImage-1;
  345.             if ( y<0 ) y=0;
  346.             else if ( y>=m_cyImage ) y = m_cyImage-1;
  347.             m_cxCenter = x;
  348.             m_cyCenter = y;
  349.         }
  350.         break;
  351.     case IVZ_RECT:
  352.         {
  353.             LPRECT prc = (LPRECT)lParam;
  354.             int x = (prc->left+prc->right)/2;
  355.             int y = (prc->top+prc->bottom)/2;
  356.             if ( x<0 ) x=0;
  357.             else if ( x>=m_cxImage ) x = m_cxImage-1;
  358.             if ( y<0 ) y=0;
  359.             else if ( y>=m_cyImage ) y = m_cyImage-1;
  360.             m_cxCenter = x;
  361.             m_cyCenter = y;
  362.             // TODO: This should really completely adjust the dest rect but I have to
  363.             // check for any assumptions about aspect ratio before I allow this absolute
  364.             // aspect ignoring zoom mode.
  365.         }
  366.         break;
  367.     }
  368.     if ( wParam&IVZ_ZOOMOUT )
  369.         ZoomOut();
  370.     else
  371.         ZoomIn();
  372. }
  373. void CZoomWnd::ZoomIn()
  374. {
  375.     if ( m_pImgCtx || m_hbitmap )
  376.     {
  377.         m_bBestFit = false;
  378.         // first, the height is adjusted by the amount the mouse cursor moved.
  379.         m_ptszDest.cy = (LONG)/*ceil*/(m_ptszDest.cy*1.200);  // ceil is required in order to zoom in
  380.                                                                 // on 4px high or less image
  381.         // we don't allow zooming beyond 16x the full size of the image
  382.         if ( m_ptszDest.cy >= m_cyImage*16 )
  383.         {
  384.             m_ptszDest.cy = m_cyImage*16;
  385.         }
  386.         // next, a new width is calculated based on the original image dimentions and the new height
  387.         m_ptszDest.cx = MulDiv( m_ptszDest.cy, m_cxImage, m_cyImage );
  388.         AdjustRectPlacement();
  389.     }
  390. }
  391. void CZoomWnd::ZoomOut()
  392. {
  393.     if ( m_pImgCtx || m_hbitmap )
  394.     {
  395.         // if the destination rect already fits within the window, don't allow a zoom out.
  396.         // This check is to prevent a redraw that would occure otherwise 
  397.         if ((m_ptszDest.cx <= MIN(m_cxWindow,m_cxImage)) &&
  398.             (m_ptszDest.cy <= MIN(m_cyWindow,m_cyImage)))
  399.         {
  400.             m_bBestFit = true;
  401.             return;
  402.         }
  403.         // first, the height is adjusted by the amount the mouse cursor moved.
  404.         m_ptszDest.cy = (LONG)/*floor*/(m_ptszDest.cy*0.833); // floor is default behavior
  405.         // next, a new width is calculated based on the original image dimentions and the new height
  406.         m_ptszDest.cx = MulDiv( m_ptszDest.cy, m_cxImage, m_cyImage );
  407.         AdjustRectPlacement();
  408.     }
  409. }
  410. // OnMouseMove
  411. //
  412. // Handles WM_MOUSEMOVE messages sent to the control
  413. LRESULT CZoomWnd::OnMouseMove(UINT , WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  414. {
  415.     // This is something of a hack since I never recieve the keyboard focus
  416.     if ( wParam & MK_CONTROL )
  417.         m_fCtrlDown = true;
  418.     else
  419.         m_fCtrlDown = false;    
  420.     if ( wParam & MK_SHIFT )
  421.         m_fShiftDown = true;
  422.     else
  423.         m_fShiftDown = false;
  424.     // we only care about mouse move when the middle or left button is down
  425.     // and we have a valid bitmap handle and we are panning
  426.     if ( !(wParam & (MK_LBUTTON|MK_MBUTTON)) || !m_fPanning || (!m_hbitmap && !m_pImgCtx) )
  427.     {
  428.         bHandled = false;
  429.         return TRUE;
  430.     }
  431.     // we know we are panning when we reach this point
  432.     ASSERT( m_fPanning );
  433.     POINTS pt = MAKEPOINTS( lParam );
  434.     PTSZ ptszDest;
  435.     ptszDest.cx = m_ptszDest.cx;
  436.     ptszDest.cy = m_ptszDest.cy;
  437.     // only allow side-to-side panning if it's needed
  438.     if ( m_ptszDest.cx > m_cxWindow )
  439.     {
  440.         ptszDest.x = m_ptszDest.x + pt.x - m_xPosMouse;
  441.     }
  442.     else
  443.     {
  444.         ptszDest.x = m_ptszDest.x;
  445.     }
  446.     // only allow up-and-down panning if it's needed
  447.     if ( m_ptszDest.cy > m_cyWindow )
  448.     {
  449.         ptszDest.y = m_ptszDest.y + pt.y - m_yPosMouse;
  450.     }
  451.     else
  452.     {
  453.         ptszDest.y = m_ptszDest.y;
  454.     }
  455.     // if the image is now smaller than the window, center it
  456.     // if the image is now panned when it shouldn't be, adjust the possition
  457.     if ( ptszDest.cx < m_cxWindow )
  458.         ptszDest.x = (m_cxWindow-ptszDest.cx)/2;
  459.     else
  460.     {
  461.         if ( ptszDest.x < (m_cxWindow - ptszDest.cx) )
  462.             ptszDest.x = m_cxWindow - ptszDest.cx;
  463.         if ( ptszDest.x > 0 )
  464.             ptszDest.x = 0;
  465.     }
  466.     if ( ptszDest.cy < m_cyWindow )
  467.         ptszDest.y = (m_cyWindow-ptszDest.cy)/2;
  468.     else
  469.     {
  470.         if ( ptszDest.y < (m_cyWindow - ptszDest.cy) )
  471.             ptszDest.y = m_cyWindow - ptszDest.cy;
  472.         if ( ptszDest.y > 0 )
  473.             ptszDest.y = 0;
  474.     }
  475.     m_xPosMouse = pt.x;
  476.     m_yPosMouse = pt.y;
  477.     // ensure the scroll bars are correct
  478.     SetScrollBars();
  479.     // if anything has changed, we must invalidate the window to force a repaint
  480.     if ( (ptszDest.x != m_ptszDest.x) || (ptszDest.y != m_ptszDest.y) ||
  481.          (ptszDest.cx != m_ptszDest.cx) || (ptszDest.y != m_ptszDest.y ))
  482.     {
  483.         // REVIEW: Is it worth it to calculate rcInvalid?  Should I use NULL instead?
  484.         RECT rcInvalid;
  485.         rcInvalid.left = MIN( ptszDest.x, m_ptszDest.x );
  486.         rcInvalid.top  = MIN( ptszDest.y, m_ptszDest.y );
  487.         rcInvalid.right = MAX( ptszDest.x + ptszDest.cx, m_ptszDest.x + m_ptszDest.cx );
  488.         rcInvalid.bottom = MAX( ptszDest.y + ptszDest.cy, m_ptszDest.y + m_ptszDest.cy );
  489.         m_ptszDest = ptszDest;
  490.         InvalidateRect( &rcInvalid );
  491.     }
  492.     // Update m_cxCenter and m_cyCenter so that a zoom after a pan will zoom in
  493.     // on the correct area.  This is majorly annoying otherwise.  We want the
  494.     // new center to be whatever is in the center of the window after we pan.
  495.     m_cxCenter = MulDiv( m_cxWindow/2-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
  496.     m_cyCenter = MulDiv( m_cyWindow/2-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
  497.     return TRUE;
  498. }
  499. // OnSize
  500. //
  501. // Handles WM_SIZE messages set to the window
  502. LRESULT CZoomWnd::OnSize(UINT , WPARAM , LPARAM lParam, BOOL& )
  503. {
  504.     m_cxWindow = LOWORD(lParam);
  505.     m_cyWindow = HIWORD(lParam);
  506.     if ( m_bBestFit )
  507.     {
  508.         BestFit();
  509.     }
  510.     else
  511.     {
  512.         // The size of the rect doesn't change in this case, so just reposition
  513.         AdjustRectPlacement();
  514.     }
  515.     return TRUE;
  516. }
  517. // SetMode
  518. //
  519. // Sets the current mouse mode to one of the values specified in the MODE enumeration.
  520. // Currently there are two modes, pan and zoom.  The mode effects the default mouse
  521. // cursor when moving over the zoom window and the behavior of a click-and-drag with the
  522. // left mouse button.  Holding the shift key effects the result of a click-and-drag but
  523. // does not effect m_mode, which is the default when the shift key isn't down.
  524. bool CZoomWnd::SetMode(MODE modeNew)
  525. {
  526.     if ( m_modeDefault == modeNew )
  527.         return false;
  528.     m_modeDefault = modeNew;
  529.     return true;
  530. }
  531. // ActualSize
  532. //
  533. // Displays image zoomed to its full size
  534. void CZoomWnd::ActualSize()
  535. {
  536.     m_bBestFit = FALSE;
  537.     // actual size means same sixe as the image
  538.     m_ptszDest.cx = m_cxImage;
  539.     m_ptszDest.cy = m_cyImage;
  540.     // we center the image
  541.     m_ptszDest.x = (m_cxWindow-m_cxImage)/2;
  542.     m_ptszDest.y = (m_cyWindow-m_cyImage)/2;
  543.     // Setting actual size is a zoom operation.  Whenever we zoom we update our centerpoint.
  544.     m_cxCenter = m_cxImage/2;
  545.     m_cyCenter = m_cyImage/2;
  546.     // turn scoll bars on/off as needed
  547.     SetScrollBars();
  548.     InvalidateRect( NULL );
  549. }
  550. // BestFit
  551. //
  552. // Computes the default location for the destination rectangle.  This rectangle is a
  553. // best fit while maintaining aspect ratio within a window of the given width and height.
  554. // If the window is larger than the image, the image is centered, otherwise it is scaled
  555. // to fit within the window.  The destination rectange is computed in the client coordinates
  556. // of the window whose width and height are passed as arguments (ie we assume the point 0,0
  557. // is the upper left corner of the window).
  558. //
  559. void CZoomWnd::BestFit()
  560. {
  561.     m_bBestFit = true;
  562.     // if scroll bars are on, adjust the client size to what it will be once they are off
  563.     DWORD dwStyle = GetWindowLong(GWL_STYLE);
  564.     if ( dwStyle & (WS_VSCROLL|WS_HSCROLL) )
  565.     {
  566.         m_cxWindow += (dwStyle&WS_VSCROLL)?m_cxVScroll:0;
  567.         m_cyWindow += (dwStyle&WS_HSCROLL)?m_cyHScroll:0;
  568.     }
  569.     // Determine the limiting axis, if any.
  570.     if ( m_cxImage <= m_cxWindow && m_cyImage <= m_cyWindow )
  571.     {
  572.         // item fits centered within window
  573.         m_ptszDest.x = (m_cxWindow-m_cxImage)/2;
  574.         m_ptszDest.y = (m_cyWindow-m_cyImage)/2;
  575.         m_ptszDest.cx = m_cxImage;
  576.         m_ptszDest.cy = m_cyImage;
  577.     }
  578.     else if ( m_cxImage * m_cyWindow < m_cxWindow * m_cyImage )
  579.     {
  580.         // height is the limiting factor
  581.         int iNewWidth = MulDiv(m_cyWindow, m_cxImage, m_cyImage);
  582.         m_ptszDest.x = (m_cxWindow-iNewWidth)/2;
  583.         m_ptszDest.y = 0;
  584.         m_ptszDest.cx = iNewWidth;
  585.         m_ptszDest.cy = m_cyWindow;
  586.     }
  587.     else
  588.     {
  589.         // width is the limiting factor
  590.         int iNewHeight = MulDiv(m_cxWindow, m_cyImage, m_cxImage);
  591.         m_ptszDest.x = 0;
  592.         m_ptszDest.y = (m_cyWindow-iNewHeight)/2;
  593.         m_ptszDest.cx = m_cxWindow;
  594.         m_ptszDest.cy = iNewHeight;
  595.     }
  596.     // this should turn off the scroll bars if they are on
  597.     if ( dwStyle & (WS_VSCROLL|WS_HSCROLL) )
  598.     {
  599.         SetScrollBars();
  600.     }
  601.     // ensure the scroll bars are now off
  602.     ASSERT( 0 == (GetWindowLong(GWL_STYLE)&(WS_VSCROLL|WS_HSCROLL)) );
  603.     InvalidateRect( NULL );
  604. }
  605. // AdjustRectPlacement
  606. //
  607. // This function determines the optimal placement of the destination rectangle.  This may
  608. // include resizing the destination rectangle if it is smaller than the "best fit" rectangle
  609. // but it is primarily intended for repossitioning the rectange due to a change in the window
  610. // size or destination rectangle size.  The window is repositioned so that the centered point
  611. // remains in the center of the window.
  612. //
  613. void CZoomWnd::AdjustRectPlacement()
  614. {
  615.     // if we have scroll bars ...
  616.     DWORD dwStyle = GetWindowLong(GWL_STYLE);
  617.     if ( dwStyle&(WS_VSCROLL|WS_HSCROLL) )
  618.     {
  619.         // .. and if removing scroll bars would allow the image to fit ...
  620.         if ( (m_ptszDest.cx < (m_cxWindow + ((dwStyle&WS_VSCROLL)?m_cxVScroll:0))) &&
  621.              (m_ptszDest.cy < (m_cyWindow + ((dwStyle&WS_HSCROLL)?m_cyHScroll:0))) )
  622.         {
  623.             // ... remove the scroll bars
  624.             m_cxWindow += (dwStyle&WS_VSCROLL)?m_cxVScroll:0;
  625.             m_cyWindow += (dwStyle&WS_HSCROLL)?m_cyHScroll:0;
  626.             SetScrollBars();
  627.         }
  628.     }
  629.     // If the dest rect is smaller than the window ...
  630.     if ( (m_ptszDest.cx < m_cxWindow) && (m_ptszDest.cy < m_cyWindow) )
  631.     {
  632.         // ... then it must be larger than the image.  Oterwise we switch
  633.         // to "best fit" mode.
  634.         if ( (m_ptszDest.cx < m_cxImage) && (m_ptszDest.cy < m_cyImage) )
  635.         {
  636.             BestFit();
  637.             return;
  638.         }
  639.     }
  640.     // given the window size, client area size, and dest rect size calculate the 
  641.     // dest rect position.  This position is then restrained by the limits below.
  642.     m_ptszDest.x = (m_cxWindow/2) - MulDiv( m_cxCenter, m_ptszDest.cx, m_cxImage);
  643.     m_ptszDest.y = (m_cyWindow/2) - MulDiv( m_cyCenter, m_ptszDest.cy, m_cyImage);
  644.     // if the image is now narrower than the window ...
  645.     if ( m_ptszDest.cx < m_cxWindow )
  646.     {
  647.         // ... center the image
  648.         m_ptszDest.x = (m_cxWindow-m_ptszDest.cx)/2;
  649.     }
  650.     else
  651.     {
  652.         // if the image is now panned when it shouldn't be, adjust the possition
  653.         if ( m_ptszDest.x < (m_cxWindow - m_ptszDest.cx) )
  654.             m_ptszDest.x = m_cxWindow - m_ptszDest.cx;
  655.         if ( m_ptszDest.x > 0 )
  656.             m_ptszDest.x = 0;
  657.     }
  658.     // if the image is now shorter than the window ...
  659.     if ( m_ptszDest.cy < m_cyWindow )
  660.     {
  661.         // ... center the image
  662.         m_ptszDest.y = (m_cyWindow-m_ptszDest.cy)/2;
  663.     }
  664.     else
  665.     {
  666.         // if the image is now panned when it shouldn't be, adjust the possition
  667.         if ( m_ptszDest.y < (m_cyWindow - m_ptszDest.cy) )
  668.             m_ptszDest.y = m_cyWindow - m_ptszDest.cy;
  669.         if ( m_ptszDest.y > 0 )
  670.             m_ptszDest.y = 0;
  671.     }
  672.     SetScrollBars();
  673.     InvalidateRect( NULL );
  674. }
  675. // StatusUpdate
  676. //
  677. // Sent when the image generation status has changed, once when the image is first
  678. // being created and again if there is an error of any kind.
  679. void CZoomWnd::StatusUpdate( int iStatus )
  680. {
  681.     if ( m_pImgCtx )
  682.     {
  683.         m_pImgCtx->Release();
  684.         m_pImgCtx = 0;
  685.     }
  686.     m_hbitmap = 0;
  687.     m_fTransparent = false;
  688.     m_iStrID = iStatus;
  689.     if ( m_hWnd )
  690.     {
  691.         InvalidateRect(NULL);
  692.     }
  693. }
  694. // SetBitmap
  695. //
  696. // Called to pass in the handle to the bitmap we draw.  We DO NOT own this bitmap, it
  697. // is a duplicate handle.  We use it to paint only, it is freed by the parent window.
  698. //
  699. void CZoomWnd::SetBitmap(HBITMAP hbitmap)
  700. {
  701.     if ( m_pImgCtx )
  702.     {
  703.         m_pImgCtx->Release();
  704.         m_pImgCtx = 0;
  705.     }
  706.     m_fTransparent = false;
  707.     m_hbitmap = hbitmap;
  708.     if (m_hbitmap)
  709.     {
  710.         BITMAP bm;
  711.         if (GetObject(m_hbitmap, sizeof(bm), &bm))
  712.         {
  713.             // this message indicates a successful result
  714.             m_cxImage = bm.bmWidth;
  715.             m_cyImage = bm.bmHeight;
  716.             m_cxCenter = m_cxImage/2;
  717.             m_cyCenter = m_cyImage/2;
  718.             // this message could be recieved before the window is created.  
  719.             // Make sure m_hWnd is valid before calling BestFit or InvalidateRect
  720.             if (m_hWnd)
  721.             {
  722.                 BestFit();
  723.                 InvalidateRect(NULL);
  724.             }
  725.             return;
  726.         }
  727.     }
  728.     m_iStrID = IDS_LOADFAILED;
  729. }
  730. // SetImgCtx
  731. //
  732. // Called to pass in the pointer to the IImgCtx we draw.  We hold a reference to this
  733. // object so that we can use it to paint.
  734. //
  735. void CZoomWnd::SetImgCtx(IImgCtx * pImg)
  736. {
  737.     m_hbitmap = 0;
  738.     if ( m_pImgCtx )
  739.     {
  740.         m_pImgCtx->Release();
  741.     }
  742.     m_fTransparent = false;
  743.     m_pImgCtx = pImg;
  744.     if (m_pImgCtx)
  745.     {
  746.         SIZE size;
  747.         DWORD fState;
  748.         m_pImgCtx->AddRef();
  749.         if ( SUCCEEDED(m_pImgCtx->GetStateInfo(&fState, &size, FALSE)) )
  750.         {
  751.             ASSERT( fState & IMGLOAD_COMPLETE );
  752.             m_fTransparent = (0 == (fState & IMGTRANS_OPAQUE));
  753.             m_cxImage = size.cx;
  754.             m_cyImage = size.cy;
  755.             m_cxCenter = m_cxImage/2;
  756.             m_cyCenter = m_cyImage/2;
  757.             if (m_hWnd)
  758.             {
  759.                 BestFit();
  760.                 InvalidateRect(NULL);
  761.             }
  762.         }
  763.         return;
  764.     }
  765.     m_iStrID = IDS_LOADFAILED;
  766. }
  767. void CZoomWnd::SetPalette( HPALETTE hpal )
  768. {
  769.     m_hpal = hpal;
  770. }
  771. void CZoomWnd::SetScrollBars()
  772. {
  773.     SCROLLINFO si;
  774.     si.cbSize = sizeof(si);
  775.     si.fMask = SIF_ALL;
  776.     si.nMin = 0;
  777.     si.nMax = m_ptszDest.cx;
  778.     si.nPage = m_cxWindow+1;
  779.     si.nPos = 0-m_ptszDest.x;
  780.     si.nTrackPos = 0;
  781.     SetScrollInfo( m_hWnd, SB_HORZ, &si, true );
  782.     si.nMax = m_ptszDest.cy;
  783.     si.nPage = m_cyWindow+1;
  784.     si.nPos = 0-m_ptszDest.y;
  785.     SetScrollInfo( m_hWnd, SB_VERT, &si, true );
  786. }
  787. LRESULT CZoomWnd::OnScroll(UINT uMsg, WPARAM wParam, LPARAM , BOOL& )
  788. {
  789.     int iScrollBar;
  790.     int iWindow;     // width or height of the window
  791.     LONG * piTL;     // pointer to top or left point
  792.     LONG   iWH;      // the width or height of the dest rect
  793.     if ( (!m_hbitmap && !m_pImgCtx) )
  794.         return 0;
  795.     // handle both which direction we're scrolling
  796.     if ( WM_HSCROLL==uMsg )
  797.     {
  798.         iScrollBar = SB_HORZ;
  799.         iWindow = m_cxWindow;
  800.         piTL = &m_ptszDest.x;
  801.         iWH = m_ptszDest.cx;
  802.     }
  803.     else
  804.     {
  805.         iScrollBar = SB_VERT;
  806.         iWindow = m_cyWindow;
  807.         piTL = &m_ptszDest.y;
  808.         iWH = m_ptszDest.cy;
  809.     }
  810.     // Using the keyboard we can get scroll messages when we don't have scroll bars.
  811.     // Ignore these messages.
  812.     if ( iWindow >= iWH )
  813.     {
  814.         // window is larger than the image, don't allow scrolling
  815.         return 0;
  816.     }
  817.     // handle all possible scroll cases
  818.     switch ( LOWORD(wParam) )
  819.     {
  820.     case SB_TOP:
  821.         *piTL = 0;
  822.         break;
  823.     case SB_PAGEUP:
  824.         *piTL += iWindow;
  825.         break;
  826.     case SB_LINEUP:
  827.         (*piTL)++;
  828.         break;
  829.     case SB_LINEDOWN:
  830.         (*piTL)--;
  831.         break;
  832.     case SB_PAGEDOWN:
  833.         *piTL -= iWindow;
  834.         break;
  835.     case SB_BOTTOM:
  836.         *piTL = iWindow-iWH;
  837.         break;
  838.     case SB_THUMBPOSITION:
  839.     case SB_THUMBTRACK:
  840.         *piTL = -HIWORD(wParam);
  841.         break;
  842.     case SB_ENDSCROLL:
  843.         return 0;
  844.     }
  845.     // apply limits
  846.     if ( 0 < *piTL )
  847.         *piTL = 0;
  848.     else if ( (iWindow-iWH) > *piTL )
  849.         *piTL = iWindow-iWH;
  850.     // adjust scrollbars 
  851.     SetScrollPos(iScrollBar, -(*piTL), true);
  852.     // calculate new center point relative to image
  853.     if ( WM_HSCROLL==uMsg )
  854.     {
  855.         m_cxCenter = MulDiv( (m_cxWindow/2)-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
  856.     }
  857.     else
  858.     {
  859.         m_cyCenter = MulDiv( (m_cyWindow/2)-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
  860.     }
  861.     InvalidateRect( NULL );
  862.     return 0;
  863. }
  864. /*
  865. LRESULT CZoomWnd::OnHScroll(UINT , WPARAM wParam, LPARAM , BOOL& )
  866. {
  867.     if ( !m_hbitmap )
  868.         return 0;
  869.     // handle all possible scroll cases
  870.     switch ( LOWORD(wParam) )
  871.     {
  872.     case SB_TOP:
  873.         m_ptszDest.x = 0;
  874.         break;
  875.     case SB_PAGEUP:
  876.         m_ptszDest.x += m_cxWindow;
  877.         break;
  878.     case SB_LINEUP:
  879.         m_ptszDest.x++;
  880.         break;
  881.     case SB_LINEDOWN:
  882.         m_ptszDest.x--;
  883.         break;
  884.     case SB_PAGEDOWN:
  885.         m_ptszDest.x -= m_cxWindow;
  886.         break;
  887.     case SB_BOTTOM:
  888.         m_ptszDest.x = m_cxWindow-m_ptszDest.cx;
  889.         break;
  890.     case SB_THUMBPOSITION:
  891.     case SB_THUMBTRACK:
  892.         m_ptszDest.x = -HIWORD(wParam);
  893.         break;
  894.     case SB_ENDSCROLL:
  895.         return 0;
  896.     }
  897.     // apply limits
  898.     if ( 0 < m_ptszDest.x )
  899.         m_ptszDest.x = 0;
  900.     else if ( m_cxWindow-m_ptszDest.cx > m_ptszDest.x )
  901.         m_ptszDest.x = m_cxWindow-m_ptszDest.cx;
  902.     // adjust scrollbars 
  903.     SetScrollPos(SB_HORZ, -m_ptszDest.x, true);
  904.     // calculate new center point relative to image
  905.     m_cxCenter = MulDiv( (m_cxWindow/2)-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
  906.     // redraw
  907.     InvalidateRect( NULL );
  908.     return 0;
  909. }
  910. LRESULT CZoomWnd::OnVScroll(UINT , WPARAM wParam, LPARAM , BOOL& )
  911. {
  912.     if ( !m_hbitmap )
  913.         return 0;
  914.     // handle all possible scroll cases
  915.     switch ( LOWORD(wParam) )
  916.     {
  917.     case SB_TOP:
  918.         m_ptszDest.y = 0;
  919.         break;
  920.     case SB_PAGEUP:
  921.         m_ptszDest.y += m_cyWindow;
  922.         break;
  923.     case SB_LINEUP:
  924.         m_ptszDest.y++;
  925.         break;
  926.     case SB_LINEDOWN:
  927.         m_ptszDest.y--;
  928.         break;
  929.     case SB_PAGEDOWN:
  930.         m_ptszDest.y -= m_cyWindow;
  931.         break;
  932.     case SB_BOTTOM:
  933.         m_ptszDest.y = m_cyWindow-m_ptszDest.cy;
  934.         break;
  935.     case SB_THUMBPOSITION:
  936.     case SB_THUMBTRACK:
  937.         m_ptszDest.y = -HIWORD(wParam);
  938.         break;
  939.     case SB_ENDSCROLL:
  940.         return 0;
  941.     }
  942.     // apply limits
  943.     if ( 0 < m_ptszDest.y )
  944.         m_ptszDest.y = 0;
  945.     else if ( m_cyWindow-m_ptszDest.cy > m_ptszDest.y )
  946.         m_ptszDest.y = m_cyWindow-m_ptszDest.cy;
  947.     // adjust scrollbars 
  948.     SetScrollPos(SB_VERT, -m_ptszDest.y, true);
  949.     // calculate new center point relative to image
  950.     m_cyCenter = MulDiv( (m_cyWindow/2)-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
  951.     // redraw
  952.     InvalidateRect( NULL );
  953.     return 0;
  954. }
  955. */
  956. // OnWheelTurn
  957. //
  958. // Respondes to WM_MOUSEWHEEL messages sent to the parent window (then redirected here)
  959. LRESULT CZoomWnd::OnWheelTurn(UINT , WPARAM wParam, LPARAM , BOOL& )
  960. {
  961.     bool bZoomIn = ((short)HIWORD(wParam) > 0);
  962.     bZoomIn?ZoomIn():ZoomOut();
  963.     return TRUE;
  964. }
  965. LRESULT CZoomWnd::OnSetFocus(UINT , WPARAM , LPARAM , BOOL& )
  966. {
  967.     HWND hwndParent = GetParent();
  968.     ::SetFocus( hwndParent );
  969.     return 0;
  970. }