Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1548 lines
42 KiB

  1. #include "precomp.h"
  2. #include <uxtheme.h>
  3. #include <shstyle.h>
  4. #include "prevwnd.h"
  5. #include "guids.h"
  6. #include "resource.h"
  7. #define COLOR_PREVIEWBKGND COLOR_WINDOW
  8. #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
  9. #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
  10. /////////////////////////////////////////////////////////////////////////////
  11. // CZoomWnd
  12. CZoomWnd::CZoomWnd(CPreviewWnd *pPreview)
  13. {
  14. m_modeDefault = MODE_NOACTION;
  15. m_fPanning = FALSE;
  16. m_fCtrlDown = FALSE;
  17. m_fShiftDown = FALSE;
  18. m_fBestFit = TRUE;
  19. m_cxImage = 1;
  20. m_cyImage = 1;
  21. m_cxCenter = 1;
  22. m_cyCenter = 1;
  23. m_pImageData = NULL;
  24. m_cyHScroll = GetSystemMetrics(SM_CYHSCROLL);
  25. m_cxVScroll = GetSystemMetrics(SM_CXVSCROLL);
  26. m_iStrID = IDS_NOPREVIEW;
  27. m_hpal = NULL;
  28. m_pPreview = pPreview;
  29. m_pFront = NULL;
  30. m_pBack = NULL;
  31. m_pTaskScheduler = NULL;
  32. m_fTimerReady = FALSE;
  33. m_fFoundBackgroundColor = FALSE;
  34. m_iIndex = -1;
  35. }
  36. CZoomWnd::~CZoomWnd()
  37. {
  38. if (m_pImageData)
  39. m_pImageData->Release();
  40. if (m_pTaskScheduler)
  41. {
  42. // wait for any pending draw tasks since we're about to delete the buffers
  43. DWORD dwMode;
  44. m_pPreview->GetMode(&dwMode);
  45. TASKOWNERID toid;
  46. GetTaskIDFromMode(GTIDFM_DRAW, dwMode, &toid);
  47. m_pTaskScheduler->RemoveTasks(toid, ITSAT_DEFAULT_LPARAM, TRUE);
  48. m_pTaskScheduler->Release();
  49. }
  50. if (m_pBack)
  51. {
  52. DeleteBuffer(m_pBack);
  53. m_pBack = NULL;
  54. }
  55. // DeleteBuffer is going to check for NULL anyway
  56. DeleteBuffer(m_pFront);
  57. }
  58. LRESULT CZoomWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&)
  59. {
  60. // turn off The RTL layout extended style in GWL_EXSTYLE
  61. SHSetWindowBits(m_hWnd, GWL_EXSTYLE, WS_EX_LAYOUTRTL, 0);
  62. HDC hdc = GetDC();
  63. m_winDPIx = (float)(GetDeviceCaps(hdc,LOGPIXELSX));
  64. m_winDPIy = (float)(GetDeviceCaps(hdc,LOGPIXELSY));
  65. ReleaseDC(hdc);
  66. return 0;
  67. }
  68. DWORD CZoomWnd::GetBackgroundColor()
  69. {
  70. if (!m_fFoundBackgroundColor)
  71. {
  72. // First try the theme file
  73. HINSTANCE hinstTheme = SHGetShellStyleHInstance();
  74. if (hinstTheme)
  75. {
  76. WCHAR sz[20];
  77. if (LoadString(hinstTheme, IDS_PREVIEW_BACKGROUND_COLOR, sz, ARRAYSIZE(sz)))
  78. {
  79. int nColor;
  80. if (StrToIntEx(sz, STIF_SUPPORT_HEX, &nColor))
  81. {
  82. m_dwBackgroundColor = (DWORD)nColor;
  83. m_fFoundBackgroundColor = TRUE;
  84. }
  85. }
  86. FreeLibrary(hinstTheme);
  87. }
  88. if (!m_fFoundBackgroundColor)
  89. {
  90. m_dwBackgroundColor = GetSysColor(COLOR_PREVIEWBKGND);
  91. m_fFoundBackgroundColor = TRUE;
  92. }
  93. }
  94. return m_dwBackgroundColor;
  95. }
  96. LRESULT CZoomWnd::OnEraseBkgnd(UINT , WPARAM wParam, LPARAM , BOOL&)
  97. {
  98. RECT rcFill; // rect to fill with background color
  99. HDC hdc = (HDC)wParam;
  100. if (!m_pPreview->OnSetColor(hdc))
  101. SetBkColor(hdc, GetBackgroundColor());
  102. // There are four possible regions that might need to be erased:
  103. // +-----------------------+
  104. // | Erase Top |
  105. // +-------+-------+-------+
  106. // | | | |
  107. // | Erase | Image | Erase |
  108. // | Left | | Right |
  109. // +-------+-------+-------+
  110. // | Erase Bottom |
  111. // +-----------------------+
  112. if (m_pFront && m_pFront->hdc)
  113. {
  114. RECT rcImage = m_pFront->rc;
  115. HPALETTE hPalOld = NULL;
  116. if (m_pFront->hPal)
  117. {
  118. hPalOld = SelectPalette(hdc, m_pFront->hPal, FALSE);
  119. RealizePalette(hdc);
  120. }
  121. BitBlt(hdc, rcImage.left, rcImage.top, RECTWIDTH(rcImage), RECTHEIGHT(rcImage),
  122. m_pFront->hdc, 0,0, SRCCOPY);
  123. // erase the left region
  124. rcFill.left = 0;
  125. rcFill.top = rcImage.top;
  126. rcFill.right = rcImage.left;
  127. rcFill.bottom = rcImage.bottom;
  128. if (rcFill.right > rcFill.left)
  129. {
  130. ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcFill, NULL, 0, NULL);
  131. }
  132. // erase the right region
  133. rcFill.left = rcImage.right;
  134. rcFill.right = m_cxWindow;
  135. if (rcFill.right > rcFill.left)
  136. {
  137. ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcFill, NULL, 0, NULL);
  138. }
  139. // erase the top region
  140. rcFill.left = 0;
  141. rcFill.top = 0;
  142. rcFill.right = m_cxWindow;
  143. rcFill.bottom = rcImage.top;
  144. if (rcFill.bottom > rcFill.top)
  145. {
  146. ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcFill, NULL, 0, NULL);
  147. }
  148. // erase the bottom region
  149. rcFill.top = rcImage.bottom;
  150. rcFill.bottom = m_cyWindow;
  151. if (rcFill.bottom > rcFill.top)
  152. {
  153. ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcFill, NULL, 0, NULL);
  154. }
  155. HBRUSH hbr = GetSysColorBrush(COLOR_WINDOWTEXT);
  156. FrameRect(hdc, &rcImage, hbr);
  157. if (hPalOld)
  158. {
  159. SelectPalette(hdc, hPalOld, FALSE);
  160. }
  161. }
  162. return TRUE;
  163. }
  164. void CZoomWnd::FlushDrawMessages()
  165. {
  166. // first, remove any pending draw tasks
  167. DWORD dwMode;
  168. m_pPreview->GetMode(&dwMode);
  169. TASKOWNERID toid;
  170. GetTaskIDFromMode(GTIDFM_DRAW, dwMode, &toid);
  171. m_pTaskScheduler->RemoveTasks(toid, ITSAT_DEFAULT_LPARAM, TRUE);
  172. // make sure any posted messages get flushed and we free the associated data
  173. MSG msg;
  174. while (PeekMessage(&msg, m_hWnd, ZW_DRAWCOMPLETE, ZW_DRAWCOMPLETE, PM_REMOVE))
  175. {
  176. // NTRAID#NTBUG9-359356-2001/04/05-seank
  177. // If the queue is empty when PeekMessage is called and we have already
  178. // Posted a quit message then PeekMessage will return a WM_QUIT message
  179. // regardless of the filter min and max and subsequent calls to
  180. // GetMessage will hang indefinitely see SEANK or JASONSCH for more
  181. // info.
  182. if (msg.message == WM_QUIT)
  183. {
  184. PostQuitMessage(0);
  185. return;
  186. }
  187. Buffer * pBuf = (Buffer *)msg.wParam;
  188. DeleteBuffer(pBuf);
  189. }
  190. }
  191. HRESULT CZoomWnd::PrepareDraw()
  192. {
  193. // first, remove any pending draw tasks
  194. FlushDrawMessages();
  195. // we are now waiting for the "next task", even if we don't create a task with this ID.
  196. HRESULT hr = S_OK;
  197. BOOL bInvalidate = FALSE;
  198. if (m_pImageData)
  199. {
  200. if (m_pImageData->_iItem == m_iIndex)
  201. {
  202. SwitchBuffers(m_iIndex);
  203. bInvalidate = TRUE;
  204. }
  205. else
  206. {
  207. COLORREF clr;
  208. if (!m_pPreview->GetColor(&clr))
  209. clr = GetBackgroundColor();
  210. m_iStrID = IDS_DRAWFAILED;
  211. IRunnableTask * pTask;
  212. hr = CDrawTask::Create(m_pImageData, clr, m_rcCut, m_rcBleed, m_hWnd, ZW_DRAWCOMPLETE, &pTask);
  213. if (SUCCEEDED(hr))
  214. {
  215. DWORD dwMode;
  216. m_pPreview->GetMode(&dwMode);
  217. TASKOWNERID toid;
  218. GetTaskIDFromMode(GTIDFM_DRAW, dwMode, &toid);
  219. hr = m_pTaskScheduler->AddTask(pTask, toid, ITSAT_DEFAULT_LPARAM, ITSAT_DEFAULT_PRIORITY);
  220. if (SUCCEEDED(hr))
  221. {
  222. m_iStrID = IDS_DRAWING;
  223. }
  224. pTask->Release();
  225. }
  226. else
  227. {
  228. bInvalidate = TRUE;
  229. }
  230. }
  231. }
  232. else
  233. {
  234. bInvalidate = TRUE;
  235. }
  236. if (m_hWnd && bInvalidate)
  237. InvalidateRect(NULL);
  238. return hr;
  239. }
  240. HRESULT CZoomWnd::PrepareImageData(CDecodeTask *pImageData)
  241. {
  242. HRESULT hr = E_FAIL;
  243. if (pImageData)
  244. {
  245. SIZE sz;
  246. ULONG dpiX, dpiY;
  247. int cxImgPix, cyImgPix;
  248. float cxImgPhys, cyImgPhys;
  249. PTSZ ptszDest;
  250. pImageData->GetSize(&sz);
  251. pImageData->GetResolution(&dpiX, &dpiY);
  252. cxImgPhys = sz.cx/(float)dpiX;
  253. cyImgPhys = sz.cy/(float)dpiY;
  254. cxImgPix = (int)(cxImgPhys*m_winDPIx);
  255. cyImgPix = (int)(cyImgPhys*m_winDPIy);
  256. GetPTSZForBestFit(cxImgPix, cyImgPix, cxImgPhys, cyImgPhys, ptszDest);
  257. RECT rcCut, rcBleed;
  258. CalcCut(ptszDest, sz.cx, sz.cy, rcCut, rcBleed);
  259. COLORREF clr;
  260. if (!m_pPreview->GetColor(&clr))
  261. clr = GetBackgroundColor();
  262. IRunnableTask * pTask;
  263. hr = CDrawTask::Create(pImageData, clr, rcCut, rcBleed, m_hWnd, ZW_BACKDRAWCOMPLETE, &pTask);
  264. if (SUCCEEDED(hr))
  265. {
  266. DWORD dwMode;
  267. m_pPreview->GetMode(&dwMode);
  268. TASKOWNERID toid;
  269. GetTaskIDFromMode(GTIDFM_DRAW, dwMode, &toid);
  270. hr = m_pTaskScheduler->AddTask(pTask, toid, ITSAT_DEFAULT_LPARAM, ITSAT_DEFAULT_PRIORITY);
  271. pTask->Release();
  272. }
  273. }
  274. return hr;
  275. }
  276. BOOL CZoomWnd::SwitchBuffers(UINT iIndex)
  277. {
  278. BOOL fRet = FALSE;
  279. if (m_pBack && m_iIndex == iIndex)
  280. {
  281. // DeleteBuffer is going to check for NULL anyway
  282. DeleteBuffer(m_pFront);
  283. m_pFront = m_pBack;
  284. m_pBack = NULL;
  285. m_iIndex = -1;
  286. InvalidateRect(NULL);
  287. UpdateWindow();
  288. if (m_fTimerReady)
  289. {
  290. m_pPreview->OnDrawComplete();
  291. m_fTimerReady = FALSE;
  292. }
  293. fRet = TRUE;
  294. }
  295. return fRet;
  296. }
  297. LRESULT CZoomWnd::OnBackDrawComplete(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  298. {
  299. Buffer * pBuf = (Buffer *)wParam;
  300. if (m_pBack)
  301. {
  302. DeleteBuffer(m_pBack);
  303. m_pBack = NULL;
  304. }
  305. if (pBuf)
  306. {
  307. m_pBack = pBuf;
  308. }
  309. m_iIndex = PtrToInt((void *)lParam);
  310. return 0;
  311. }
  312. LRESULT CZoomWnd::OnDrawComplete(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  313. {
  314. Buffer * pBuf = (Buffer *)wParam;
  315. if (m_pFront)
  316. {
  317. DeleteBuffer(m_pFront);
  318. m_pFront = NULL;
  319. }
  320. if (pBuf)
  321. {
  322. m_pFront = pBuf;
  323. }
  324. else
  325. {
  326. m_iStrID = IDS_DRAWFAILED;
  327. }
  328. InvalidateRect(NULL);
  329. UpdateWindow();
  330. if (m_fTimerReady)
  331. {
  332. m_pPreview->OnDrawComplete();
  333. m_fTimerReady = FALSE;
  334. }
  335. return 0;
  336. }
  337. // OnPaint
  338. //
  339. // Handles WM_PAINT messages sent to the window
  340. LRESULT CZoomWnd::OnPaint(UINT , WPARAM , LPARAM , BOOL&)
  341. {
  342. PAINTSTRUCT ps;
  343. HDC hdcDraw = BeginPaint(&ps);
  344. // setup the destination DC:
  345. SetMapMode(hdcDraw, MM_TEXT);
  346. SetStretchBltMode(hdcDraw, COLORONCOLOR);
  347. if (m_hpal)
  348. {
  349. SelectPalette(hdcDraw, m_hpal, TRUE);
  350. RealizePalette(hdcDraw);
  351. }
  352. if (m_pFront)
  353. {
  354. if (m_Annotations.GetCount() > 0)
  355. {
  356. CPoint ptDeviceOrigin;
  357. ptDeviceOrigin.x = m_rcBleed.left - MulDiv(m_rcCut.left, RECTWIDTH(m_rcBleed), RECTWIDTH(m_rcCut));
  358. ptDeviceOrigin.y = m_rcBleed.top - MulDiv(m_rcCut.top, RECTHEIGHT(m_rcBleed), RECTHEIGHT(m_rcCut));
  359. SetMapMode(hdcDraw, MM_ANISOTROPIC);
  360. SetWindowOrgEx(hdcDraw, 0, 0, NULL);
  361. SetWindowExtEx(hdcDraw, RECTWIDTH(m_rcCut), RECTHEIGHT(m_rcCut), NULL);
  362. SetViewportOrgEx(hdcDraw, ptDeviceOrigin.x, ptDeviceOrigin.y, NULL);
  363. SetViewportExtEx(hdcDraw, RECTWIDTH(m_rcBleed), RECTHEIGHT(m_rcBleed), NULL);
  364. HRGN hrgn = CreateRectRgnIndirect(&m_rcBleed);
  365. if (hrgn != NULL)
  366. SelectClipRgn(hdcDraw, hrgn);
  367. m_Annotations.RenderAllMarks(hdcDraw);
  368. SelectClipRgn(hdcDraw, NULL);
  369. if (hrgn != NULL)
  370. DeleteObject(hrgn);
  371. SetMapMode(hdcDraw, MM_TEXT);
  372. SetViewportOrgEx(hdcDraw, 0, 0, NULL);
  373. SetWindowOrgEx(hdcDraw, 0, 0, NULL);
  374. }
  375. m_pPreview->OnDraw(hdcDraw);
  376. }
  377. else
  378. {
  379. TCHAR szBuf[80];
  380. LoadString(_Module.GetModuleInstance(), m_iStrID, szBuf, ARRAYSIZE(szBuf) );
  381. LOGFONT lf;
  382. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0);
  383. HFONT hFont = CreateFontIndirect(&lf);
  384. HFONT hFontOld;
  385. if (hFont)
  386. hFontOld = (HFONT)SelectObject(hdcDraw, hFont);
  387. if (!m_pPreview->OnSetColor(hdcDraw))
  388. {
  389. SetTextColor(hdcDraw, GetSysColor(COLOR_WINDOWTEXT));
  390. SetBkColor(hdcDraw, GetBackgroundColor());
  391. }
  392. RECT rc = { 0,0,m_cxWindow,m_cyWindow };
  393. ExtTextOut(hdcDraw, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
  394. DrawText(hdcDraw, szBuf, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  395. if (hFont)
  396. {
  397. SelectObject(hdcDraw, hFontOld);
  398. DeleteObject(hFont);
  399. }
  400. }
  401. EndPaint(&ps);
  402. return 0;
  403. }
  404. // OnSetCursor
  405. //
  406. // Handles WM_SETCURSOR messages sent to the window.
  407. //
  408. // This function is a total HackMaster job. I have overloaded its functionality to the point
  409. // of absurdity. Here's what the parameters mean:
  410. //
  411. // uMsg == WM_SETCURSOR
  412. // wParam Standard value sent during a WM_SETCURSOR messge.
  413. // lParam Standard value sent during a WM_SETCURSOR messge.
  414. //
  415. // uMsg == 0
  416. // wParam 0
  417. // lParam If this value is non-zero then it is a packed x,y cursor location.
  418. // If it's zero then we need to query the cursor location
  419. LRESULT CZoomWnd::OnSetCursor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  420. {
  421. // if this is a legitimate message but isn't intended for the client area, we ignore it.
  422. // we also ignore set cursor when we have no valid bitmap
  423. if (((WM_SETCURSOR == uMsg) && (HTCLIENT != LOWORD(lParam))) || (m_iStrID != IDS_LOADING && !m_pImageData))
  424. {
  425. bHandled = FALSE;
  426. return 0;
  427. }
  428. else if (0 == uMsg)
  429. {
  430. // Since this is one of our fake messages we need to do our own check to test for HTCLIENT.
  431. // we need to find the cursor location
  432. POINT pt;
  433. GetCursorPos(&pt);
  434. lParam = MAKELONG(pt.x, pt.y);
  435. if (HTCLIENT != SendMessage(WM_NCHITTEST, 0, lParam))
  436. {
  437. bHandled = FALSE;
  438. return 0;
  439. }
  440. }
  441. if (m_pPreview->OnSetCursor(uMsg, wParam, lParam))
  442. {
  443. bHandled = TRUE;
  444. return TRUE;
  445. }
  446. HINSTANCE hinst = _Module.GetModuleInstance();
  447. LPTSTR idCur;
  448. if (m_iStrID == IDS_LOADING && !m_pImageData)
  449. {
  450. idCur = IDC_WAIT;
  451. hinst = NULL;
  452. }
  453. else if (m_fPanning)
  454. {
  455. idCur = MAKEINTRESOURCE(IDC_CLOSEDHAND);
  456. }
  457. else if (m_fCtrlDown)
  458. {
  459. idCur = MAKEINTRESOURCE(IDC_OPENHAND);
  460. }
  461. else if (m_modeDefault == MODE_NOACTION)
  462. {
  463. hinst = NULL;
  464. idCur = IDC_ARROW;
  465. }
  466. else if ((m_modeDefault == MODE_ZOOMIN && m_fShiftDown == FALSE) || (m_modeDefault == MODE_ZOOMOUT && m_fShiftDown == TRUE))
  467. {
  468. idCur = MAKEINTRESOURCE(IDC_ZOOMIN);
  469. }
  470. else
  471. {
  472. idCur = MAKEINTRESOURCE(IDC_ZOOMOUT);
  473. }
  474. SetCursor(LoadCursor(hinst, idCur));
  475. return TRUE;
  476. }
  477. // OnKeyUp
  478. //
  479. // Handles WM_KEYUP messages sent to the window
  480. LRESULT CZoomWnd::OnKeyUp(UINT , WPARAM wParam, LPARAM , BOOL& bHandled)
  481. {
  482. if (VK_CONTROL == wParam)
  483. {
  484. m_fCtrlDown = FALSE;
  485. OnSetCursor(0,0,0, bHandled);
  486. }
  487. else if (VK_SHIFT == wParam)
  488. {
  489. m_fShiftDown = FALSE;
  490. OnSetCursor(0,0,0, bHandled);
  491. }
  492. bHandled = FALSE;
  493. return 0;
  494. }
  495. // OnKeyDown
  496. //
  497. // Handles WM_KEYDOWN messages sent to the window
  498. LRESULT CZoomWnd::OnKeyDown(UINT , WPARAM wParam, LPARAM , BOOL& bHandled)
  499. {
  500. // when we return, we want to call the DefWindowProc
  501. bHandled = FALSE;
  502. switch (wParam)
  503. {
  504. case VK_PRIOR:
  505. OnScroll(WM_VSCROLL, m_fCtrlDown?SB_TOP:SB_PAGEUP, 0, bHandled);
  506. break;
  507. case VK_NEXT:
  508. OnScroll(WM_VSCROLL, m_fCtrlDown?SB_BOTTOM:SB_PAGEDOWN, 0, bHandled);
  509. break;
  510. case VK_END:
  511. OnScroll(WM_HSCROLL, m_fCtrlDown?SB_BOTTOM:SB_PAGEDOWN, 0, bHandled);
  512. break;
  513. case VK_HOME:
  514. OnScroll(WM_HSCROLL, m_fCtrlDown?SB_TOP:SB_PAGEUP, 0, bHandled);
  515. break;
  516. case VK_CONTROL:
  517. case VK_SHIFT:
  518. // if m_fPanning is TRUE then we are already in the middle of an operation so we
  519. // should maintain the cursor for that operation
  520. if (!m_fPanning)
  521. {
  522. if (VK_CONTROL == wParam)
  523. {
  524. m_fCtrlDown = TRUE;
  525. }
  526. if (VK_SHIFT == wParam)
  527. {
  528. m_fShiftDown = TRUE;
  529. }
  530. // Update the cursor based on the key states set above only if we are over our window
  531. OnSetCursor(0,0,0, bHandled);
  532. }
  533. break;
  534. default:
  535. // if in run screen preview mode any key other than Shift and Control will dismiss the window
  536. if (NULL == GetParent())
  537. {
  538. DestroyWindow();
  539. }
  540. return 1; // return non-zero to indicate unprocessed message
  541. }
  542. return 0;
  543. }
  544. // OnMouseUp
  545. //
  546. // Handles WM_LBUTTONUP messages sent to the window
  547. LRESULT CZoomWnd::OnMouseUp(UINT , WPARAM , LPARAM , BOOL& bHandled)
  548. {
  549. if (m_fPanning)
  550. ReleaseCapture();
  551. m_fPanning = FALSE;
  552. bHandled = FALSE;
  553. return 0;
  554. }
  555. // OnMouseDown
  556. //
  557. // Handles WM_LBUTTONDOWN and WM_MBUTTONDOWN messages sent to the window
  558. LRESULT CZoomWnd::OnMouseDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  559. {
  560. if (m_pPreview->OnMouseDown(uMsg, wParam, lParam))
  561. return 0;
  562. // This stuff should be avoided if m_pImage is NULL.
  563. if (!m_pImageData)
  564. return 0;
  565. m_xPosMouse = GET_X_LPARAM(lParam);
  566. m_yPosMouse = GET_Y_LPARAM(lParam);
  567. ASSERT(m_fPanning == FALSE);
  568. // Holding the CTRL key makes a pan into a zoom and vise-versa.
  569. // The middle mouse button always pans regardless of default mode and key state.
  570. if ((wParam & MK_CONTROL) || (uMsg == WM_MBUTTONDOWN))
  571. {
  572. // REVIEW: check for pan being valid here? Should be more efficient than all the checks
  573. // I have to do in OnMouseMove.
  574. m_fPanning = TRUE;
  575. OnSetCursor(0,0,0,bHandled);
  576. SetCapture();
  577. }
  578. else if (m_modeDefault != MODE_NOACTION)
  579. {
  580. // Holding down the shift key turns a zoomin into a zoomout and vise-versa.
  581. // The "default" zoom mode is zoom in (if mode = pan and ctrl key is down we zoom in).
  582. BOOL bZoomIn = (m_modeDefault != MODE_ZOOMOUT) ^ ((wParam & MK_SHIFT)?1:0);
  583. // Find the point we want to stay centered on:
  584. m_cxCenter = MulDiv(m_xPosMouse-m_ptszDest.x, m_cxImgPix, m_ptszDest.cx);
  585. m_cyCenter = MulDiv(m_yPosMouse-m_ptszDest.y, m_cyImgPix, m_ptszDest.cy);
  586. bZoomIn?ZoomIn():ZoomOut();
  587. }
  588. bHandled = FALSE;
  589. return 0;
  590. }
  591. void CZoomWnd::Zoom(WPARAM wParam, LPARAM lParam)
  592. {
  593. switch (wParam&0xFF)
  594. {
  595. case IVZ_CENTER:
  596. break;
  597. case IVZ_POINT:
  598. {
  599. int x = GET_X_LPARAM(lParam);
  600. int y = GET_Y_LPARAM(lParam);
  601. if (x<0) x=0;
  602. else if (x>=m_cxImgPix) x = m_cxImgPix-1;
  603. if (y<0) y=0;
  604. else if (y>=m_cyImgPix) y = m_cyImgPix-1;
  605. m_cxCenter = x;
  606. m_cyCenter = y;
  607. }
  608. break;
  609. case IVZ_RECT:
  610. {
  611. LPRECT prc = (LPRECT)lParam;
  612. int x = (prc->left+prc->right)/2;
  613. int y = (prc->top+prc->bottom)/2;
  614. if (x<0) x=0;
  615. else if (x>=m_cxImgPix) x = m_cxImgPix-1;
  616. if (y<0) y=0;
  617. else if (y>=m_cyImgPix) y = m_cyImgPix-1;
  618. m_cxCenter = x;
  619. m_cyCenter = y;
  620. // TODO: This should really completely adjust the dest rect but I have to
  621. // check for any assumptions about aspect ratio before I allow this absolute
  622. // aspect ignoring zoom mode.
  623. }
  624. break;
  625. }
  626. if (wParam&IVZ_ZOOMOUT)
  627. {
  628. ZoomOut();
  629. SetMode(MODE_ZOOMOUT);
  630. }
  631. else
  632. {
  633. ZoomIn();
  634. SetMode(MODE_ZOOMIN);
  635. }
  636. }
  637. void CZoomWnd::ZoomIn()
  638. {
  639. DWORD dwMode;
  640. m_pPreview->GetMode(&dwMode);
  641. if (m_pImageData && (SLIDESHOW_MODE != dwMode))
  642. {
  643. m_fBestFit = FALSE;
  644. // first, the height is adjusted by the amount the mouse cursor moved.
  645. m_ptszDest.cy = (LONG)/*ceil*/(m_ptszDest.cy*1.200); // ceil is required in order to zoom in
  646. // on 4px high or less image
  647. // FEATURE: allow zooming beyond 16x the full size of the image
  648. // The use of the Cut and Bleed rectangles should eliminate the need for this
  649. // arbitrary zoom limit. The limit was originally added because GDI on win9x isn't ver good and
  650. // can't handle large images. Even on NT you would eventually zoom to the point where
  651. // it would take many seconds to blt the bitmap. Now we only blt the minimum required
  652. // area.
  653. if (m_ptszDest.cy >= m_cyImgPix*16)
  654. {
  655. m_ptszDest.cy = m_cyImgPix*16;
  656. }
  657. // next, a new width is calculated based on the original image dimensions and the new height
  658. m_ptszDest.cx = (LONG)(m_ptszDest.cy* (m_cxImgPhys*m_winDPIx)/(m_cyImgPhys*m_winDPIy));
  659. AdjustRectPlacement();
  660. }
  661. }
  662. void CZoomWnd::ZoomOut()
  663. {
  664. DWORD dwMode;
  665. m_pPreview->GetMode(&dwMode);
  666. if (m_pImageData && (SLIDESHOW_MODE != dwMode))
  667. {
  668. // if the destination rect already fits within the window, don't allow a zoom out.
  669. // This check is to prevent a redraw that would occur otherwise
  670. if ((m_ptszDest.cx <= MIN(m_cxWindow,m_cxImgPix)) &&
  671. (m_ptszDest.cy <= MIN(m_cyWindow,m_cyImgPix)))
  672. {
  673. m_fBestFit = TRUE;
  674. return;
  675. }
  676. // first, the height is adjusted by the amount the mouse cursor moved.
  677. m_ptszDest.cy = (LONG)/*floor*/(m_ptszDest.cy*0.833); // floor is default behavior
  678. // next, a new width is calculated based on the original image dimensions and the new height
  679. m_ptszDest.cx = (LONG)(m_ptszDest.cy* (m_cxImgPhys*m_winDPIx)/(m_cyImgPhys*m_winDPIy));
  680. AdjustRectPlacement();
  681. }
  682. }
  683. // OnMouseMove
  684. //
  685. // Handles WM_MOUSEMOVE messages sent to the control
  686. LRESULT CZoomWnd::OnMouseMove(UINT , WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  687. {
  688. // This is something of a hack since I never recieve the keyboard focus
  689. m_fCtrlDown = (BOOL)(wParam & MK_CONTROL);
  690. m_fShiftDown = (BOOL)(wParam & MK_SHIFT);
  691. // we only care about mouse move when the middle or left button is down
  692. // and we have a valid bitmap handle and we are panning
  693. if (!(wParam & (MK_LBUTTON|MK_MBUTTON)) || !m_fPanning || !m_pImageData)
  694. {
  695. m_pPreview->OnMouseMove(WM_MOUSEMOVE, wParam, lParam);
  696. bHandled = FALSE;
  697. return TRUE;
  698. }
  699. // we know we are panning when we reach this point
  700. ASSERT(m_fPanning);
  701. POINTS pt = MAKEPOINTS(lParam);
  702. PTSZ ptszDest;
  703. ptszDest.cx = m_ptszDest.cx;
  704. ptszDest.cy = m_ptszDest.cy;
  705. // only allow side-to-side panning if it's needed
  706. if (m_ptszDest.cx > m_cxWindow)
  707. {
  708. ptszDest.x = m_ptszDest.x + pt.x - m_xPosMouse;
  709. }
  710. else
  711. {
  712. ptszDest.x = m_ptszDest.x;
  713. }
  714. // only allow up-and-down panning if it's needed
  715. if (m_ptszDest.cy > m_cyWindow)
  716. {
  717. ptszDest.y = m_ptszDest.y + pt.y - m_yPosMouse;
  718. }
  719. else
  720. {
  721. ptszDest.y = m_ptszDest.y;
  722. }
  723. // if the image is now smaller than the window, center it
  724. // if the image is now panned when it shouldn't be, adjust the possition
  725. if (ptszDest.cx < m_cxWindow)
  726. ptszDest.x = (m_cxWindow-ptszDest.cx)/2;
  727. else
  728. {
  729. if (ptszDest.x < (m_cxWindow - ptszDest.cx))
  730. ptszDest.x = m_cxWindow - ptszDest.cx;
  731. if (ptszDest.x > 0)
  732. ptszDest.x = 0;
  733. }
  734. if (ptszDest.cy < m_cyWindow)
  735. ptszDest.y = (m_cyWindow-ptszDest.cy)/2;
  736. else
  737. {
  738. if (ptszDest.y < (m_cyWindow - ptszDest.cy))
  739. ptszDest.y = m_cyWindow - ptszDest.cy;
  740. if (ptszDest.y > 0)
  741. ptszDest.y = 0;
  742. }
  743. m_xPosMouse = pt.x;
  744. m_yPosMouse = pt.y;
  745. // ensure the scroll bars are correct
  746. SetScrollBars();
  747. // if anything has changed, we must invalidate the window to force a repaint
  748. if ((ptszDest.x != m_ptszDest.x) || (ptszDest.y != m_ptszDest.y) ||
  749. (ptszDest.cx != m_ptszDest.cx) || (ptszDest.y != m_ptszDest.y))
  750. {
  751. m_ptszDest = ptszDest;
  752. CalcCut();
  753. PrepareDraw();
  754. }
  755. // Update m_cxCenter and m_cyCenter so that a zoom after a pan will zoom in
  756. // on the correct area. This is majorly annoying otherwise. We want the
  757. // new center to be whatever is in the center of the window after we pan.
  758. m_cxCenter = MulDiv(m_cxWindow/2-m_ptszDest.x, m_cxImgPix, m_ptszDest.cx);
  759. m_cyCenter = MulDiv(m_cyWindow/2-m_ptszDest.y, m_cyImgPix, m_ptszDest.cy);
  760. return TRUE;
  761. }
  762. // OnSize
  763. //
  764. // Handles WM_SIZE messages set to the window
  765. LRESULT CZoomWnd::OnSize(UINT , WPARAM , LPARAM lParam, BOOL&)
  766. {
  767. m_cxWindow = GET_X_LPARAM(lParam);
  768. m_cyWindow = GET_Y_LPARAM(lParam);
  769. _UpdatePhysicalSize();
  770. if (m_fBestFit)
  771. {
  772. BestFit();
  773. }
  774. else
  775. {
  776. // The size of the rect doesn't change in this case, so just reposition
  777. AdjustRectPlacement();
  778. }
  779. return TRUE;
  780. }
  781. BOOL CZoomWnd::SetScheduler(IShellTaskScheduler * pTaskScheduler)
  782. {
  783. if (!m_pTaskScheduler)
  784. {
  785. m_pTaskScheduler = pTaskScheduler;
  786. m_pTaskScheduler->AddRef();
  787. return TRUE;
  788. }
  789. return FALSE;
  790. }
  791. // SetMode
  792. //
  793. // Sets the current mouse mode to one of the values specified in the MODE enumeration.
  794. // Currently there are two important modes, pan and zoom. The mode effects the default mouse
  795. // cursor when moving over the zoom window and the behavior of a click-and-drag with the
  796. // left mouse button. Holding the shift key effects the result of a click-and-drag but
  797. // does not effect m_mode, which is the default when the shift key isn't down.
  798. BOOL CZoomWnd::SetMode(MODE modeNew)
  799. {
  800. if (m_modeDefault == modeNew)
  801. return FALSE;
  802. m_modeDefault = modeNew;
  803. BOOL bDummy;
  804. OnSetCursor(0,0,0, bDummy);
  805. return TRUE;
  806. }
  807. // ActualSize
  808. //
  809. // Displays image zoomed to its full size
  810. void CZoomWnd::ActualSize()
  811. {
  812. m_fBestFit = FALSE;
  813. if (m_pImageData)
  814. {
  815. // actual size means same size as the image
  816. m_ptszDest.cx = (LONG)(m_cxImgPix);
  817. m_ptszDest.cy = (LONG)(m_cyImgPix);
  818. // we center the image in the window
  819. m_ptszDest.x = (LONG)((m_cxWinPhys-m_cxImgPhys)*m_winDPIx/2.0);
  820. m_ptszDest.y = (LONG)((m_cyWinPhys-m_cyImgPhys)*m_winDPIy/2.0);
  821. CalcCut();
  822. // Setting actual size is a zoom operation. Whenever we zoom we update our centerpoint.
  823. m_cxCenter = m_cxImgPix/2;
  824. m_cyCenter = m_cyImgPix/2;
  825. // turn scoll bars on/off as needed
  826. SetScrollBars();
  827. PrepareDraw();
  828. }
  829. }
  830. void CZoomWnd::GetPTSZForBestFit(int cxImgPix, int cyImgPix, float cxImgPhys, float cyImgPhys, PTSZ &ptszDest)
  831. {
  832. // Determine the limiting axis, if any.
  833. if (cxImgPhys <= m_cxWinPhys && cyImgPhys <= m_cyWinPhys)
  834. {
  835. // item fits centered within window
  836. ptszDest.x = (LONG)((m_cxWinPhys-cxImgPhys)*m_winDPIx/2.0);
  837. ptszDest.y = (LONG)((m_cyWinPhys-cyImgPhys)*m_winDPIy/2.0);
  838. ptszDest.cx = (LONG)(cxImgPix);
  839. ptszDest.cy = (LONG)(cyImgPix);
  840. }
  841. else if (cxImgPhys * m_cyWinPhys < m_cxWinPhys * cyImgPhys)
  842. {
  843. // height is the limiting factor
  844. int iNewWidth = (int)((m_cyWinPhys*cxImgPhys/cyImgPhys) * m_winDPIx);
  845. ptszDest.x = (m_cxWindow-iNewWidth)/2;
  846. ptszDest.y = 0;
  847. ptszDest.cx = iNewWidth;
  848. ptszDest.cy = m_cyWindow;
  849. }
  850. else
  851. {
  852. // width is the limiting factor
  853. int iNewHeight = (int)((m_cxWinPhys*cyImgPhys/cxImgPhys) * m_winDPIy);
  854. ptszDest.x = 0;
  855. ptszDest.y = (m_cyWindow-iNewHeight)/2;
  856. ptszDest.cx = m_cxWindow;
  857. ptszDest.cy = iNewHeight;
  858. }
  859. }
  860. // BestFit
  861. //
  862. // Computes the default location for the destination rectangle. This rectangle is a
  863. // best fit while maintaining aspect ratio within a window of the given width and height.
  864. // If the window is larger than the image, the image is centered, otherwise it is scaled
  865. // to fit within the window. The destination rectange is computed in the client coordinates
  866. // of the window whose width and height are passed as arguments (ie we assume the point 0,0
  867. // is the upper left corner of the window).
  868. //
  869. void CZoomWnd::BestFit()
  870. {
  871. m_fBestFit = TRUE;
  872. if (m_pImageData)
  873. {
  874. // if scroll bars are on, adjust the client size to what it will be once they are off
  875. DWORD dwStyle = GetWindowLong(GWL_STYLE);
  876. if (dwStyle & (WS_VSCROLL|WS_HSCROLL))
  877. {
  878. m_cxWindow += (dwStyle&WS_VSCROLL)?m_cxVScroll:0;
  879. m_cyWindow += (dwStyle&WS_HSCROLL)?m_cyHScroll:0;
  880. _UpdatePhysicalSize();
  881. }
  882. GetPTSZForBestFit(m_cxImgPix, m_cyImgPix, m_cxImgPhys, m_cyImgPhys, m_ptszDest);
  883. // this should turn off the scroll bars if they are on
  884. if (dwStyle & (WS_VSCROLL|WS_HSCROLL))
  885. {
  886. SetScrollBars();
  887. }
  888. CalcCut();
  889. // ensure the scroll bars are now off
  890. ASSERT(0 == (GetWindowLong(GWL_STYLE)&(WS_VSCROLL|WS_HSCROLL)));
  891. PrepareDraw();
  892. }
  893. }
  894. // AdjustRectPlacement
  895. //
  896. // This function determines the optimal placement of the destination rectangle. This may
  897. // include resizing the destination rectangle if it is smaller than the "best fit" rectangle
  898. // but it is primarily intended for repositioning the rectange due to a change in the window
  899. // size or destination rectangle size. The window is repositioned so that the centered point
  900. // remains in the center of the window.
  901. //
  902. void CZoomWnd::AdjustRectPlacement()
  903. {
  904. // if we have scroll bars ...
  905. DWORD dwStyle = GetWindowLong(GWL_STYLE);
  906. if (dwStyle&(WS_VSCROLL|WS_HSCROLL))
  907. {
  908. // .. and if removing scroll bars would allow the image to fit ...
  909. if ((m_ptszDest.cx < (m_cxWindow + ((dwStyle&WS_VSCROLL)?m_cxVScroll:0))) &&
  910. (m_ptszDest.cy < (m_cyWindow + ((dwStyle&WS_HSCROLL)?m_cyHScroll:0))))
  911. {
  912. // ... remove the scroll bars
  913. m_cxWindow += (dwStyle&WS_VSCROLL)?m_cxVScroll:0;
  914. m_cyWindow += (dwStyle&WS_HSCROLL)?m_cyHScroll:0;
  915. SetScrollBars();
  916. _UpdatePhysicalSize();
  917. }
  918. }
  919. // If the dest rect is smaller than the window ...
  920. if ((m_ptszDest.cx < m_cxWindow) && (m_ptszDest.cy < m_cyWindow))
  921. {
  922. // ... then it must be larger than the image. Otherwise we switch
  923. // to "best fit" mode.
  924. if ((m_ptszDest.cx < (LONG)m_cxImgPix) && (m_ptszDest.cy < (LONG)m_cyImgPix))
  925. {
  926. BestFit();
  927. return;
  928. }
  929. }
  930. // given the window size, client area size, and dest rect size calculate the
  931. // dest rect position. This position is then restrained by the limits below.
  932. m_ptszDest.x = (m_cxWindow/2) - MulDiv(m_cxCenter, m_ptszDest.cx, m_cxImgPix);
  933. m_ptszDest.y = (m_cyWindow/2) - MulDiv(m_cyCenter, m_ptszDest.cy, m_cyImgPix);
  934. // if the image is now narrower than the window ...
  935. if (m_ptszDest.cx < m_cxWindow)
  936. {
  937. // ... center the image
  938. m_ptszDest.x = (m_cxWindow-m_ptszDest.cx)/2;
  939. }
  940. else
  941. {
  942. // if the image is now panned when it shouldn't be, adjust the position
  943. if (m_ptszDest.x < (m_cxWindow - m_ptszDest.cx))
  944. m_ptszDest.x = m_cxWindow - m_ptszDest.cx;
  945. if (m_ptszDest.x > 0)
  946. m_ptszDest.x = 0;
  947. }
  948. // if the image is now shorter than the window ...
  949. if (m_ptszDest.cy < m_cyWindow)
  950. {
  951. // ... center the image
  952. m_ptszDest.y = (m_cyWindow-m_ptszDest.cy)/2;
  953. }
  954. else
  955. {
  956. // if the image is now panned when it shouldn't be, adjust the position
  957. if (m_ptszDest.y < (m_cyWindow - m_ptszDest.cy))
  958. m_ptszDest.y = m_cyWindow - m_ptszDest.cy;
  959. if (m_ptszDest.y > 0)
  960. m_ptszDest.y = 0;
  961. }
  962. CalcCut();
  963. SetScrollBars();
  964. PrepareDraw();
  965. }
  966. // CalcCut
  967. //
  968. // This function should be called anytime the Destination rectangle changes.
  969. // Based on the destination rectangle it determines what part of the image
  970. // will be visible, ptszCut, and where on the window to place the stretched
  971. // cut rectangle, ptszBleed.
  972. //
  973. void CZoomWnd::CalcCut()
  974. {
  975. if (m_pImageData)
  976. {
  977. CalcCut(m_ptszDest, m_cxImage, m_cyImage, m_rcCut, m_rcBleed);
  978. }
  979. }
  980. void CZoomWnd::CalcCut(PTSZ ptszDest, int cxImage, int cyImage, RECT &rcCut, RECT &rcBleed)
  981. {
  982. // If the expanded image doesn't occupy the entire window ...
  983. if ((ptszDest.cy <= m_cyWindow) || (ptszDest.cx <= m_cxWindow))
  984. {
  985. // draw the entire destination rectangle
  986. rcBleed.left = ptszDest.x;
  987. rcBleed.top = ptszDest.y;
  988. rcBleed.right = ptszDest.x + ptszDest.cx;
  989. rcBleed.bottom = ptszDest.y + ptszDest.cy;
  990. // cut the entire image
  991. rcCut.left = 0;
  992. rcCut.top = 0;
  993. rcCut.right = cxImage;
  994. rcCut.bottom = cyImage;
  995. }
  996. else
  997. {
  998. // NOTE: These calculations are written to retain as much
  999. // precision as possible. Loss of precision will result in
  1000. // undesirable drawing artifacts in the destination window.
  1001. // MulDiv is not used because it rounds the result when we
  1002. // really want the result to be floored.
  1003. // Given destination rectangle calculate the rectangle
  1004. // of the original image that will be visible.
  1005. // To do this we need to convert 2 points from window coordinates to image
  1006. // coordinates, those two points are (0,0) and (cxWindow, cyWindow). The
  1007. // (0,0) point needs to be floored and the (cxWindow, cyWindow) point needs
  1008. // to be ceilinged to handle partially visible pixels. Since we don't have
  1009. // a good way to do ceiling we just always add one.
  1010. rcCut.left = LONG(Int32x32To64(-ptszDest.x, cxImage) / ptszDest.cx);
  1011. rcCut.top = LONG(Int32x32To64(-ptszDest.y, cyImage) / ptszDest.cy);
  1012. rcCut.right = LONG(Int32x32To64(m_cxWindow-ptszDest.x, cxImage) / ptszDest.cx) + 1;
  1013. rcCut.bottom = LONG(Int32x32To64(m_cyWindow-ptszDest.y, cyImage) / ptszDest.cy) + 1;
  1014. // Make sure the +1 does extend past the image border or GDI+ will choke.
  1015. // If we were doing a TRUE "ceiling" this wouldn't be needed.
  1016. if (rcCut.right > cxImage) rcCut.right = cxImage;
  1017. if (rcCut.bottom > cyImage) rcCut.bottom = cyImage;
  1018. // Calculate where on the window to place the cut rectangle.
  1019. // Only a fraction of a zoomed pixel may be visible, hence the bleed factor.
  1020. // Basically we converted from window coordinates to image coordinates to find
  1021. // the Cut rectangle (what we need to draw), now we convert that Cut rectangle
  1022. // back to window coordinates so that we know exactly where we need to draw it.
  1023. rcBleed.left = ptszDest.x + LONG(Int32x32To64(rcCut.left, ptszDest.cx) / cxImage);
  1024. rcBleed.top = ptszDest.y + LONG(Int32x32To64(rcCut.top, ptszDest.cy) / cyImage);
  1025. rcBleed.right = ptszDest.x + LONG(Int32x32To64(rcCut.right, ptszDest.cx) / cxImage);
  1026. rcBleed.bottom = ptszDest.y + LONG(Int32x32To64(rcCut.bottom, ptszDest.cy) / cyImage);
  1027. }
  1028. }
  1029. void CZoomWnd::GetVisibleImageWindowRect(LPRECT prectImage)
  1030. {
  1031. CopyRect(prectImage, &m_rcBleed);
  1032. }
  1033. void CZoomWnd::GetImageFromWindow(LPPOINT ppoint, int cSize)
  1034. {
  1035. for(int i=0;i<cSize;i++)
  1036. {
  1037. ppoint[i].x -= m_ptszDest.x;
  1038. ppoint[i].y -= m_ptszDest.y;
  1039. ppoint[i].x = MulDiv(ppoint[i].x, m_cxImage, m_ptszDest.cx);
  1040. ppoint[i].y = MulDiv(ppoint[i].y, m_cyImage, m_ptszDest.cy);
  1041. }
  1042. }
  1043. void CZoomWnd::GetWindowFromImage(LPPOINT ppoint, int cSize)
  1044. {
  1045. for(int i=0;i<cSize;i++)
  1046. {
  1047. ppoint[i].x = MulDiv(ppoint[i].x, m_ptszDest.cx, m_cxImage);
  1048. ppoint[i].y = MulDiv(ppoint[i].y, m_ptszDest.cy, m_cyImage);
  1049. ppoint[i].x += m_ptszDest.x;
  1050. ppoint[i].y += m_ptszDest.y;
  1051. }
  1052. }
  1053. // StatusUpdate
  1054. //
  1055. // Sent when the image generation status has changed, once when the image is first
  1056. // being created and again if there is an error of any kind.
  1057. void CZoomWnd::StatusUpdate(int iStatus)
  1058. {
  1059. if (m_pImageData)
  1060. {
  1061. m_pImageData->Release();
  1062. m_pImageData = 0;
  1063. }
  1064. if (m_pFront)
  1065. {
  1066. DWORD dwMode;
  1067. m_pPreview->GetMode(&dwMode);
  1068. if (SLIDESHOW_MODE != dwMode)
  1069. {
  1070. DeleteBuffer(m_pFront);
  1071. m_pFront = NULL;
  1072. }
  1073. }
  1074. m_iStrID = iStatus;
  1075. // m_cxImage and m_cyImage should be reset to their initial values so that we don't
  1076. // accidentally draw scroll bars or something like that
  1077. m_cxImage = 1;
  1078. m_cyImage = 1;
  1079. // The dest rect should be reset too so that we don't allow some zoom
  1080. // or pan that isn't actually valid.
  1081. m_ptszDest.y = 0;
  1082. m_ptszDest.x = 0;
  1083. m_ptszDest.cx = m_cxWindow;
  1084. m_ptszDest.cy = m_cyWindow;
  1085. SetScrollBars();
  1086. if (m_hWnd)
  1087. {
  1088. PrepareDraw();
  1089. }
  1090. }
  1091. // SetImageData
  1092. //
  1093. // Called to pass in the pointer to the IShellImageData we draw. We hold a reference to this
  1094. // object so that we can use it to paint.
  1095. //
  1096. void CZoomWnd::SetImageData(CDecodeTask * pImageData, BOOL bUpdate)
  1097. {
  1098. if (bUpdate)
  1099. {
  1100. m_fTimerReady = TRUE;
  1101. if (m_pFront)
  1102. {
  1103. DWORD dwMode;
  1104. m_pPreview->GetMode(&dwMode);
  1105. if (SLIDESHOW_MODE != dwMode)
  1106. {
  1107. DeleteBuffer(m_pFront);
  1108. m_pFront = NULL;
  1109. }
  1110. }
  1111. }
  1112. if (m_pImageData)
  1113. {
  1114. m_pImageData->Release();
  1115. }
  1116. m_pImageData = pImageData;
  1117. if (m_pImageData)
  1118. {
  1119. m_pImageData->AddRef();
  1120. m_pImageData->ChangePage(m_Annotations);
  1121. SIZE sz;
  1122. ULONG dpiX;
  1123. ULONG dpiY;
  1124. pImageData->GetSize(&sz);
  1125. pImageData->GetResolution(&dpiX, &dpiY);
  1126. if (m_cxImage != sz.cx || m_cyImage != sz.cy || dpiX != m_imgDPIx || dpiY != m_imgDPIy)
  1127. {
  1128. bUpdate = TRUE;
  1129. }
  1130. if (bUpdate)
  1131. {
  1132. // cache the image dimensions to avoid checking m_pImageData against NULL all over the place
  1133. m_cxImage = sz.cx;
  1134. m_cyImage = sz.cy;
  1135. m_imgDPIx = (float)dpiX;
  1136. m_imgDPIy = (float)dpiY;
  1137. m_cxImgPhys = m_cxImage/m_imgDPIx;
  1138. m_cyImgPhys = m_cyImage/m_imgDPIy;
  1139. m_cxImgPix = (int)(m_cxImgPhys*m_winDPIx);
  1140. m_cyImgPix = (int)(m_cyImgPhys*m_winDPIy);
  1141. m_cxCenter = m_cxImgPix/2;
  1142. m_cyCenter = m_cyImgPix/2;
  1143. }
  1144. if (m_hWnd)
  1145. {
  1146. // REVIEW: should we keep the previous Actual Size/Best Fit setting?
  1147. if (bUpdate)
  1148. {
  1149. BestFit();
  1150. }
  1151. else
  1152. {
  1153. PrepareDraw();
  1154. }
  1155. }
  1156. return;
  1157. }
  1158. m_iStrID = IDS_LOADFAILED;
  1159. }
  1160. void CZoomWnd::SetPalette(HPALETTE hpal)
  1161. {
  1162. m_hpal = hpal;
  1163. }
  1164. void CZoomWnd::SetScrollBars()
  1165. {
  1166. SCROLLINFO si;
  1167. si.cbSize = sizeof(si);
  1168. si.fMask = SIF_ALL;
  1169. si.nMin = 0;
  1170. si.nMax = m_ptszDest.cx;
  1171. si.nPage = m_cxWindow+1;
  1172. si.nPos = 0-m_ptszDest.x;
  1173. si.nTrackPos = 0;
  1174. SetScrollInfo(SB_HORZ, &si, TRUE);
  1175. si.nMax = m_ptszDest.cy;
  1176. si.nPage = m_cyWindow+1;
  1177. si.nPos = 0-m_ptszDest.y;
  1178. SetScrollInfo(SB_VERT, &si, TRUE);
  1179. }
  1180. LRESULT CZoomWnd::OnScroll(UINT uMsg, WPARAM wParam, LPARAM , BOOL&)
  1181. {
  1182. int iScrollBar;
  1183. int iWindow; // width or height of the window
  1184. LONG * piTL; // pointer to top or left point
  1185. LONG iWH; // the width or height of the dest rect
  1186. if (!m_pImageData)
  1187. return 0;
  1188. // handle both which direction we're scrolling
  1189. if (WM_HSCROLL==uMsg)
  1190. {
  1191. iScrollBar = SB_HORZ;
  1192. iWindow = m_cxWindow;
  1193. piTL = &m_ptszDest.x;
  1194. iWH = m_ptszDest.cx;
  1195. }
  1196. else
  1197. {
  1198. iScrollBar = SB_VERT;
  1199. iWindow = m_cyWindow;
  1200. piTL = &m_ptszDest.y;
  1201. iWH = m_ptszDest.cy;
  1202. }
  1203. // Using the keyboard we can get scroll messages when we don't have scroll bars.
  1204. // Ignore these messages.
  1205. if (iWindow >= iWH)
  1206. {
  1207. // window is larger than the image, don't allow scrolling
  1208. return 0;
  1209. }
  1210. // handle all possible scroll cases
  1211. switch (LOWORD(wParam))
  1212. {
  1213. case SB_TOP:
  1214. *piTL = 0;
  1215. break;
  1216. case SB_PAGEUP:
  1217. *piTL += iWindow;
  1218. break;
  1219. case SB_LINEUP:
  1220. (*piTL)++;
  1221. break;
  1222. case SB_LINEDOWN:
  1223. (*piTL)--;
  1224. break;
  1225. case SB_PAGEDOWN:
  1226. *piTL -= iWindow;
  1227. break;
  1228. case SB_BOTTOM:
  1229. *piTL = iWindow-iWH;
  1230. break;
  1231. case SB_THUMBPOSITION:
  1232. case SB_THUMBTRACK:
  1233. *piTL = -HIWORD(wParam);
  1234. break;
  1235. case SB_ENDSCROLL:
  1236. return 0;
  1237. }
  1238. // apply limits
  1239. if (0 < *piTL)
  1240. *piTL = 0;
  1241. else if ((iWindow-iWH) > *piTL)
  1242. *piTL = iWindow-iWH;
  1243. CalcCut();
  1244. // adjust scrollbars
  1245. SetScrollPos(iScrollBar, -(*piTL), TRUE);
  1246. // calculate new center point relative to image
  1247. if (WM_HSCROLL==uMsg)
  1248. {
  1249. m_cxCenter = MulDiv((m_cxWindow/2)-m_ptszDest.x, m_cxImage, m_ptszDest.cx);
  1250. }
  1251. else
  1252. {
  1253. m_cyCenter = MulDiv((m_cyWindow/2)-m_ptszDest.y, m_cyImage, m_ptszDest.cy);
  1254. }
  1255. PrepareDraw();
  1256. return 0;
  1257. }
  1258. // OnWheelTurn
  1259. //
  1260. // Respondes to WM_MOUSEWHEEL messages sent to the parent window (then redirected here)
  1261. LRESULT CZoomWnd::OnWheelTurn(UINT , WPARAM wParam, LPARAM , BOOL&)
  1262. {
  1263. BOOL bZoomIn = ((short)HIWORD(wParam) > 0);
  1264. bZoomIn?ZoomIn():ZoomOut();
  1265. return TRUE;
  1266. }
  1267. LRESULT CZoomWnd::OnSetFocus(UINT , WPARAM , LPARAM , BOOL&)
  1268. {
  1269. HWND hwndParent = GetParent();
  1270. ::SetFocus(hwndParent);
  1271. return 0;
  1272. }
  1273. void CZoomWnd::CommitAnnotations()
  1274. {
  1275. if (m_pImageData)
  1276. {
  1277. IShellImageData * pSID;
  1278. if (SUCCEEDED(m_pImageData->Lock(&pSID)))
  1279. {
  1280. m_Annotations.CommitAnnotations(pSID);
  1281. m_pImageData->Unlock();
  1282. }
  1283. }
  1284. }
  1285. BOOL CZoomWnd::ScrollBarsPresent()
  1286. {
  1287. SCROLLINFO si = {0};
  1288. si.cbSize = sizeof(si);
  1289. si.fMask = SIF_ALL;
  1290. if ((GetScrollInfo(SB_HORZ, &si) && si.nPos) || (GetScrollInfo(SB_VERT, &si) && si.nPos) )
  1291. {
  1292. return TRUE;
  1293. }
  1294. return FALSE;
  1295. }
  1296. void CZoomWnd::_UpdatePhysicalSize()
  1297. {
  1298. m_cxWinPhys = (float)(m_cxWindow)/m_winDPIx;
  1299. m_cyWinPhys = (float)(m_cyWindow)/m_winDPIy;
  1300. }
  1301. LRESULT CZoomWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  1302. {
  1303. FlushDrawMessages();
  1304. if (m_pFront)
  1305. {
  1306. DeleteBuffer(m_pFront);
  1307. m_pFront = NULL;
  1308. }
  1309. return 0;
  1310. }