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.

3419 lines
99 KiB

  1. /*****************************************************************************
  2. *
  3. * (C) COPYRIGHT MICROSOFT CORPORATION, 2000
  4. *
  5. * TITLE: wizblob.cpp
  6. *
  7. * VERSION: 1.0
  8. *
  9. * AUTHOR: RickTu
  10. *
  11. * DATE: 10/18/00
  12. *
  13. * DESCRIPTION: Class which encapsulates the data which must be passed
  14. * around from page to page in the print photos wizard...
  15. *
  16. *****************************************************************************/
  17. #include "precomp.h"
  18. #pragma hdrstop
  19. #include "gphelper.h"
  20. static const int c_nDefaultThumbnailWidth = DEFAULT_THUMB_WIDTH;
  21. static const int c_nDefaultThumbnailHeight = DEFAULT_THUMB_HEIGHT;
  22. static const int c_nMaxThumbnailWidth = 120;
  23. static const int c_nMaxThumbnailHeight = 120;
  24. static const int c_nMinThumbnailWidth = 80;
  25. static const int c_nMinThumbnailHeight = 80;
  26. static const int c_nDefaultTemplatePreviewWidth = 48;
  27. static const int c_nDefaultTemplatePreviewHeight = 62;
  28. Gdiplus::Color g_wndColor;
  29. /*****************************************************************************
  30. Callback class for namespace walking code...
  31. <Notes>
  32. *****************************************************************************/
  33. class CWalkCallback : public INamespaceWalkCB
  34. {
  35. public:
  36. CWalkCallback();
  37. ~CWalkCallback();
  38. // IUnknown
  39. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  40. STDMETHOD_(ULONG,AddRef)(void);
  41. STDMETHOD_(ULONG,Release)(void);
  42. // INamespaceWalkCB
  43. STDMETHOD(FoundItem)(IShellFolder *psf, LPCITEMIDLIST pidl);
  44. STDMETHOD(EnterFolder)(IShellFolder *psf, LPCITEMIDLIST pidl);
  45. STDMETHOD(LeaveFolder)(IShellFolder *psf, LPCITEMIDLIST pidl);
  46. STDMETHOD(InitializeProgressDialog)(LPWSTR * ppszTitle, LPWSTR * ppszCancel);
  47. BOOL WereItemsRejected() {return _bItemsWereRejected;}
  48. private:
  49. LONG _cRef;
  50. BOOL _bItemsWereRejected;
  51. CImageFileFormatVerifier _Verify;
  52. };
  53. CWalkCallback::CWalkCallback()
  54. : _cRef(1),
  55. _bItemsWereRejected(FALSE)
  56. {
  57. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::CWalkCallback( this == 0x%x )"),this));
  58. DllAddRef();
  59. }
  60. CWalkCallback::~CWalkCallback()
  61. {
  62. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::~CWalkCallback( this == 0x%x )"),this));
  63. DllRelease();
  64. }
  65. ULONG CWalkCallback::AddRef()
  66. {
  67. ULONG ul = InterlockedIncrement(&_cRef);
  68. WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CWalkCallback::AddRef( new count is %d )"), ul));
  69. return ul;
  70. }
  71. ULONG CWalkCallback::Release()
  72. {
  73. ULONG ul = InterlockedDecrement(&_cRef);
  74. WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CWalkCallback::Release( new count is %d )"), ul));
  75. if (ul)
  76. return ul;
  77. WIA_TRACE((TEXT("deleting CWalkCallback( this == 0x%x ) object"),this));
  78. delete this;
  79. return 0;
  80. }
  81. HRESULT CWalkCallback::QueryInterface(REFIID riid, void **ppv)
  82. {
  83. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::QueryInterface()")));
  84. static const QITAB qit[] =
  85. {
  86. QITABENT(CWalkCallback, INamespaceWalkCB),
  87. {0, 0 },
  88. };
  89. HRESULT hr = QISearch(this, qit, riid, ppv);
  90. WIA_RETURN_HR(hr);
  91. }
  92. STDAPI DisplayNameOf(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD flags, LPTSTR psz, UINT cch)
  93. {
  94. *psz = 0;
  95. STRRET sr;
  96. HRESULT hr = psf->GetDisplayNameOf(pidl, flags, &sr);
  97. if (SUCCEEDED(hr))
  98. hr = StrRetToBuf(&sr, pidl, psz, cch);
  99. return hr;
  100. }
  101. HRESULT CWalkCallback::FoundItem( IShellFolder * psf, LPCITEMIDLIST pidl )
  102. {
  103. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::FoundItem()")));
  104. HRESULT hrRet = S_FALSE;
  105. if (psf && pidl)
  106. {
  107. WIA_TRACE((TEXT("FoundItem: psf & pidl are valid, binding to stream to check signature...")));
  108. IStream * pStream = NULL;
  109. HRESULT hr = psf->BindToObject( pidl, NULL, IID_IStream, (void **)&pStream );
  110. if (SUCCEEDED(hr) && pStream)
  111. {
  112. GUID guidType;
  113. BOOL bSupported = FALSE;
  114. WIA_TRACE((TEXT("FoundItem: checking for GDI+ decoder...")));
  115. bSupported = _Verify.IsSupportedImageFromStream(pStream, &guidType);
  116. //
  117. // We don't let EMF, WMF or .ico into the wizard
  118. //
  119. if (bSupported &&
  120. ((guidType != Gdiplus::ImageFormatWMF) &&
  121. (guidType != Gdiplus::ImageFormatEMF) &&
  122. (guidType != Gdiplus::ImageFormatIcon)))
  123. {
  124. WIA_TRACE((TEXT("FoundItem: GDI+ encoder found")));
  125. hrRet = S_OK;
  126. }
  127. else
  128. {
  129. _bItemsWereRejected = TRUE;
  130. }
  131. }
  132. if (pStream)
  133. {
  134. pStream->Release();
  135. }
  136. }
  137. if (hrRet != S_OK)
  138. {
  139. _bItemsWereRejected = TRUE;
  140. }
  141. WIA_RETURN_HR(hrRet);
  142. }
  143. HRESULT CWalkCallback::EnterFolder( IShellFolder * psf, LPCITEMIDLIST pidl )
  144. {
  145. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::EnterFolder()")));
  146. return S_OK;
  147. }
  148. HRESULT CWalkCallback::LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
  149. {
  150. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::LeaveFolder()")));
  151. return S_OK;
  152. }
  153. HRESULT CWalkCallback::InitializeProgressDialog(LPWSTR * ppszTitle, LPWSTR * ppszCancel)
  154. {
  155. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::InitializeProgressDialog()")));
  156. //
  157. // If we want to use the progress dialog, we need to specify
  158. // when we create the namespace walk object NSWF_SHOW_PROGRESS
  159. // and use CoTaskMemAlloc to create the strings...
  160. //
  161. if (ppszTitle)
  162. {
  163. *ppszTitle = NULL;
  164. }
  165. if (ppszCancel)
  166. {
  167. *ppszCancel = NULL;
  168. }
  169. return E_FAIL;
  170. }
  171. /*****************************************************************************
  172. MyItemDpaDestroyCallback
  173. Gets called as the HDPA is destroyed so that we can delete the objects
  174. stored in the DPA
  175. *****************************************************************************/
  176. INT MyItemDpaDestroyCallback( LPVOID pItem, LPVOID lpData )
  177. {
  178. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("MyItemDpaDestroyCallback( 0x%x, 0x%x )"),pItem,lpData));
  179. if (pItem)
  180. {
  181. delete (CListItem *)pItem;
  182. }
  183. return TRUE;
  184. }
  185. /*****************************************************************************
  186. CWizardInfoBlob -- constructor/desctructor
  187. <Notes>
  188. *****************************************************************************/
  189. CWizardInfoBlob::CWizardInfoBlob( IDataObject * pdo, BOOL bShowUI, BOOL bOnlyUseSelection )
  190. : _cRef(0),
  191. _hdpaItems(NULL),
  192. _nDefaultThumbnailImageListIndex(0),
  193. _bGdiplusInitialized(FALSE),
  194. _bAllPicturesAdded(FALSE),
  195. _bItemsWereRejected(FALSE),
  196. _pGdiplusToken(NULL),
  197. _pPreview(NULL),
  198. _pStatusPage(NULL),
  199. _pPhotoSelPage(NULL),
  200. _hDevMode(NULL),
  201. _hfontIntro(NULL),
  202. _iCurTemplate(-1),
  203. _bPreviewsAreDirty(TRUE),
  204. _bRepeat(FALSE),
  205. _hSmallIcon(NULL),
  206. _hLargeIcon(NULL),
  207. _uItemsInInitialSelection(0),
  208. _bAlreadyAddedPhotos(FALSE),
  209. _bWizardIsShuttingDown((LONG)FALSE),
  210. _hPhotoSelIsDone(NULL),
  211. _hStatusIsDone(NULL),
  212. _hPreviewIsDone(NULL),
  213. _hwndPreview(NULL),
  214. _hwndStatus(NULL),
  215. _hGdiPlusThread(NULL),
  216. _dwGdiPlusThreadId(0),
  217. _hGdiPlusMsgQueueCreated(NULL),
  218. _hCachedPrinterDC(NULL),
  219. _hOuterDlg(NULL),
  220. _bShowUI(bShowUI),
  221. _bOnlyUseSelection(bOnlyUseSelection),
  222. _iNumErrorsWhileRunningWizard(0),
  223. _iSelectedItem(0),
  224. _iCopiesOfEachItem(1),
  225. _bMinimumMemorySystem(FALSE),
  226. _bLargeMemorySystem(FALSE),
  227. _bForceSelectAll(FALSE)
  228. {
  229. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::CWizardInfoBlob()") ));
  230. //
  231. // Init the printer info stuff..
  232. //
  233. ZeroMemory( &_WizPrinterInfo, sizeof(_WizPrinterInfo) );
  234. //
  235. // Copy params and init
  236. //
  237. _pdo = pdo;
  238. _sizeThumbnails.cx = c_nDefaultThumbnailWidth;
  239. _sizeThumbnails.cy = c_nDefaultThumbnailHeight;
  240. _sizeTemplatePreview.cx = c_nDefaultTemplatePreviewWidth;
  241. _sizeTemplatePreview.cy = c_nDefaultTemplatePreviewHeight;
  242. _rcInitSize.left = 0;
  243. _rcInitSize.right = 0;
  244. _rcInitSize.bottom = 0;
  245. _rcInitSize.top = 0;
  246. //
  247. // This is disgusting -- but GDI+ needs to be initialized and shut down
  248. // ON THE SAME THREAD. So, we have to create a thread just for this and have
  249. // it sit around so that we're garaunteed this is the thread we'll do
  250. // startup/shutdown on.
  251. //
  252. _hGdiPlusMsgQueueCreated = CreateEvent( NULL, FALSE, FALSE, NULL );
  253. WIA_TRACE((TEXT("creating s_GdiPlusStartupShutdownThreadProc...")));
  254. _hGdiPlusThread = CreateThread( NULL, 0, s_GdiPlusStartupShutdownThreadProc, (LPVOID)this, 0, &_dwGdiPlusThreadId );
  255. if (_hGdiPlusMsgQueueCreated)
  256. {
  257. if (_hGdiPlusThread && _dwGdiPlusThreadId)
  258. {
  259. WIA_TRACE((TEXT("waiting for message queue to be created in s_GdiPlusStartupShutdownThreadProc...")));
  260. WiaUiUtil::MsgWaitForSingleObject( _hGdiPlusMsgQueueCreated, INFINITE );
  261. WIA_TRACE((TEXT("GdiPlusStartupShutdown thread initialized correctly.")));
  262. }
  263. else
  264. {
  265. WIA_ERROR((TEXT("_hGdiPlusThread = 0x%x, _dwGdiPlusThreadId = 0x%x"),_hGdiPlusThread,_dwGdiPlusThreadId));
  266. }
  267. CloseHandle( _hGdiPlusMsgQueueCreated );
  268. _hGdiPlusMsgQueueCreated = NULL;
  269. }
  270. else
  271. {
  272. WIA_ERROR((TEXT("Couldn't create _hGdiPlusMsgQueueCreated!")));
  273. }
  274. //
  275. // Make sure GDI+ is initialized
  276. //
  277. WIA_TRACE((TEXT("posting WIZ_MSG_STARTUP_GDI_PLUS to s_GdiPlusStartupShutdownThreadProc...")));
  278. HANDLE hGdiPlusInitialized = CreateEvent( NULL, FALSE, FALSE, NULL );
  279. PostThreadMessage( _dwGdiPlusThreadId, WIZ_MSG_STARTUP_GDI_PLUS, 0, (LPARAM)hGdiPlusInitialized );
  280. if (hGdiPlusInitialized)
  281. {
  282. WIA_TRACE((TEXT("waiting for GDI+ startup to finish...")));
  283. WiaUiUtil::MsgWaitForSingleObject( hGdiPlusInitialized, INFINITE );
  284. CloseHandle( hGdiPlusInitialized );
  285. WIA_TRACE((TEXT("GDI+ startup completed!")));
  286. }
  287. //
  288. // Set up window color for use later...
  289. //
  290. DWORD dw = GetSysColor( COLOR_WINDOW );
  291. Gdiplus::ARGB argb = Gdiplus::Color::MakeARGB( 255, GetRValue(dw), GetGValue(dw), GetBValue(dw) );
  292. g_wndColor.SetValue( argb );
  293. //
  294. // Get perf info (like how much memory we have)...
  295. //
  296. NTSTATUS NtStatus;
  297. SYSTEM_BASIC_INFORMATION BasicInfo;
  298. NtStatus = NtQuerySystemInformation( SystemBasicInformation,
  299. &BasicInfo,
  300. sizeof(BasicInfo),
  301. NULL
  302. );
  303. if (NT_SUCCESS(NtStatus))
  304. {
  305. DWORD dwMemSizeInK = BasicInfo.NumberOfPhysicalPages * (BasicInfo.PageSize / 1024);
  306. if (dwMemSizeInK < MINIMUM_MEMORY_SIZE)
  307. {
  308. WIA_TRACE((TEXT("we are running on a minimum memory system!")));
  309. _bMinimumMemorySystem = TRUE;
  310. }
  311. if (dwMemSizeInK > LARGE_MINIMUM_MEMORY_SIZE)
  312. {
  313. WIA_TRACE((TEXT("we are running on a minimum memory system!")));
  314. _bLargeMemorySystem = TRUE;
  315. }
  316. }
  317. //
  318. // If we're in UI mode, show the UI...
  319. //
  320. if (bShowUI)
  321. {
  322. //
  323. // Load icons for wizard...
  324. //
  325. _hSmallIcon = reinterpret_cast<HICON>(LoadImage( g_hInst, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR ));
  326. _hLargeIcon = reinterpret_cast<HICON>(LoadImage( g_hInst, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR ));
  327. //
  328. // Create events for background tasks to signal when they're done...
  329. //
  330. _hPhotoSelIsDone = CreateEvent( NULL, FALSE, FALSE, NULL );
  331. _hStatusIsDone = CreateEvent( NULL, FALSE, FALSE, NULL );
  332. _hPreviewIsDone = CreateEvent( NULL, FALSE, FALSE, NULL );
  333. //
  334. // Initialize template info from XML...
  335. //
  336. CComPtr<IXMLDOMDocument> spXMLDoc;
  337. if (SUCCEEDED(LoadXMLDOMDoc(TEXT("res://photowiz.dll/tmpldata.xml"), &spXMLDoc)))
  338. {
  339. _templates.Init(spXMLDoc);
  340. }
  341. }
  342. }
  343. CWizardInfoBlob::~CWizardInfoBlob()
  344. {
  345. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::~CWizardInfoBlob()") ));
  346. //
  347. // free the memory
  348. //
  349. if (_hDevMode)
  350. {
  351. delete [] _hDevMode;
  352. _hDevMode = NULL;
  353. }
  354. _csItems.Enter();
  355. if (_hdpaItems)
  356. {
  357. DPA_DestroyCallback( _hdpaItems, MyItemDpaDestroyCallback, NULL );
  358. _hdpaItems = NULL;
  359. }
  360. _csItems.Leave();
  361. if (_hfontIntro)
  362. {
  363. DeleteObject( (HGDIOBJ)_hfontIntro );
  364. _hfontIntro = NULL;
  365. }
  366. if (_hCachedPrinterDC)
  367. {
  368. DeleteDC( _hCachedPrinterDC );
  369. }
  370. //
  371. // Destroy icons for wizard
  372. //
  373. if (_hSmallIcon)
  374. {
  375. DestroyIcon( _hSmallIcon );
  376. _hSmallIcon = NULL;
  377. }
  378. if (_hLargeIcon)
  379. {
  380. DestroyIcon( _hLargeIcon );
  381. _hLargeIcon = NULL;
  382. }
  383. //
  384. // Close event handles...
  385. //
  386. if (_hPhotoSelIsDone)
  387. {
  388. CloseHandle( _hPhotoSelIsDone );
  389. _hPhotoSelIsDone = NULL;
  390. }
  391. if (_hStatusIsDone)
  392. {
  393. CloseHandle( _hStatusIsDone );
  394. _hStatusIsDone = NULL;
  395. }
  396. if (_hPreviewIsDone)
  397. {
  398. CloseHandle( _hPreviewIsDone );
  399. _hPreviewIsDone = NULL;
  400. }
  401. WIA_TRACE((TEXT("Attempting to shut down GDI+")));
  402. if (_hGdiPlusThread && _dwGdiPlusThreadId)
  403. {
  404. //
  405. // Lastly, shut down GDI+
  406. //
  407. WIA_TRACE((TEXT("Sending WIZ_MSG_SHUTDOWN_GDI_PLUS to s_GdiPlusStartupShutdownThreadProc")));
  408. PostThreadMessage( _dwGdiPlusThreadId, WIZ_MSG_SHUTDOWN_GDI_PLUS, 0, 0 );
  409. WIA_TRACE((TEXT("Sending WM_QUIT to s_GdiPlusStartupShutdownThreadProc")));
  410. PostThreadMessage( _dwGdiPlusThreadId, WM_QUIT, 0, 0 );
  411. //
  412. // Wait for thread to exit...
  413. //
  414. WIA_TRACE((TEXT("Waiting for s_GdiPlusStartupShutdownThreadProc to exit...")));
  415. WiaUiUtil::MsgWaitForSingleObject( _hGdiPlusThread, INFINITE );
  416. CloseHandle( _hGdiPlusThread );
  417. _hGdiPlusThread = NULL;
  418. _dwGdiPlusThreadId = 0;
  419. WIA_TRACE((TEXT("s_GdiPlusStartupShutdownThreadProc successfully shut down...")));
  420. }
  421. //
  422. // Keep this here (but commented out) for when I need to build
  423. // instrumented versions for test
  424. //
  425. //MessageBox( NULL, TEXT("Photowiz is now shut down."), TEXT("Photowiz"), MB_OK );
  426. }
  427. /*****************************************************************************
  428. CWizardInfoBlob::_DoHandleThreadMessage
  429. The is the subroutine that actually does the work for
  430. s_GdiPlusStartupShutdownThreadProc. The whole reason for this thread
  431. in the first place is that GDI+ demands to be initialized and shutdown
  432. on the SAME THREAD.
  433. *****************************************************************************/
  434. VOID CWizardInfoBlob::_DoHandleThreadMessage( LPMSG pMsg )
  435. {
  436. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::_DoHandleThreadMessage()") ));
  437. if (!pMsg)
  438. {
  439. WIA_ERROR((TEXT("pMSG is NULL, returning early!")));
  440. return;
  441. }
  442. switch (pMsg->message)
  443. {
  444. case WIZ_MSG_STARTUP_GDI_PLUS:
  445. WIA_TRACE((TEXT("Got WIZ_MSG_STARTUP_GDI_PLUS message")));
  446. {
  447. Gdiplus::GdiplusStartupInput StartupInput;
  448. _bGdiplusInitialized = (Gdiplus::GdiplusStartup(&_pGdiplusToken,&StartupInput,NULL) == Gdiplus::Ok);
  449. //
  450. // Signal that we've attempted to initialize GDI+
  451. //
  452. if (pMsg->lParam)
  453. {
  454. SetEvent( (HANDLE)pMsg->lParam );
  455. }
  456. }
  457. break;
  458. case WIZ_MSG_SHUTDOWN_GDI_PLUS:
  459. WIA_TRACE((TEXT("Got WIZ_MSG_SHUTDOWN_GDI_PLUS message")));
  460. if (_bGdiplusInitialized)
  461. {
  462. Gdiplus::GdiplusShutdown(_pGdiplusToken);
  463. _bGdiplusInitialized = FALSE;
  464. _pGdiplusToken = NULL;
  465. }
  466. break;
  467. case WIZ_MSG_COPIES_CHANGED:
  468. WIA_TRACE((TEXT("Got WIZ_MSG_COPIES_CHANGED( %d ) message"),pMsg->wParam));
  469. {
  470. if (_iCopiesOfEachItem != pMsg->wParam)
  471. {
  472. //
  473. // Stop all the preview generation background threads...
  474. //
  475. if (_pPreview)
  476. {
  477. _pPreview->StallBackgroundThreads();
  478. //
  479. // Now change number of copies of each image...
  480. //
  481. RemoveAllCopiesOfPhotos();
  482. AddCopiesOfPhotos( (UINT)pMsg->wParam );
  483. //
  484. // Let the preview threads get going again...
  485. //
  486. _pPreview->RestartBackgroundThreads();
  487. }
  488. _iCopiesOfEachItem = (INT)pMsg->wParam;
  489. }
  490. }
  491. break;
  492. }
  493. }
  494. /*****************************************************************************
  495. CWizardInfoBlob::ShutDownWizard
  496. Called to close all the background thread in the wizard.
  497. *****************************************************************************/
  498. VOID CWizardInfoBlob::ShutDownWizard()
  499. {
  500. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::ShutDownWizard()") ));
  501. //
  502. // Get the current value of _bWizardIsShuttingDown and set to TRUE
  503. // in an atomic way.
  504. //
  505. BOOL bPreviousValue = (BOOL)InterlockedExchange( &_bWizardIsShuttingDown, (LONG)TRUE );
  506. //
  507. // If we weren't already shutting down, then go ahead and shut down the wizard
  508. //
  509. if (!bPreviousValue)
  510. {
  511. //
  512. // This method will attempt to shut down the wizard in an orderly fashion.
  513. // To do that, all we really need to do at this point is shut down
  514. // all the background threads so that they don't do any callbacks
  515. // or callouts after this point.
  516. //
  517. //
  518. // Tell the photo selection page to shut down...
  519. //
  520. if (_pPhotoSelPage)
  521. {
  522. _pPhotoSelPage->ShutDownBackgroundThreads();
  523. }
  524. //
  525. // Tell the preview window to shut down...
  526. //
  527. if (_pPreview)
  528. {
  529. _pPreview->ShutDownBackgroundThreads();
  530. }
  531. //
  532. // Tell the status window we're sutting down...
  533. //
  534. if (_pStatusPage)
  535. {
  536. _pStatusPage->ShutDownBackgroundThreads();
  537. }
  538. //
  539. // Now, wait for all the background threads to complete...
  540. //
  541. INT i = 0;
  542. HANDLE ah[ 3 ];
  543. if (_hPhotoSelIsDone)
  544. {
  545. ah[i++] = _hPhotoSelIsDone;
  546. }
  547. if (_hStatusIsDone)
  548. {
  549. ah[i++] = _hStatusIsDone;
  550. }
  551. if (_hPreviewIsDone)
  552. {
  553. ah[i++] = _hPreviewIsDone;
  554. }
  555. WaitForMultipleObjects( i, ah, TRUE, INFINITE );
  556. }
  557. }
  558. /*****************************************************************************
  559. CWizardInfoBlob::UserPressedCancel
  560. Called whenever the user presses the cancel button to close the wizard.
  561. Returns the correct result as to whether the wizard should exit.
  562. *****************************************************************************/
  563. LRESULT CWizardInfoBlob::UserPressedCancel()
  564. {
  565. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::UserPressedCancel()") ));
  566. ShutDownWizard();
  567. return FALSE; // allow wizard to exit
  568. }
  569. /*****************************************************************************
  570. CWizardInfoBlob -- AddRef/Release
  571. <Notes>
  572. *****************************************************************************/
  573. VOID CWizardInfoBlob::AddRef()
  574. {
  575. LONG l = InterlockedIncrement( &_cRef );
  576. WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS, TEXT("CWizardInfoBlob::AddRef( new _cRef is %d )"),l ));
  577. }
  578. VOID CWizardInfoBlob::Release()
  579. {
  580. LONG l = InterlockedDecrement( &_cRef );
  581. WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS, TEXT("CWizardInfoBlob::Release( new _cRef is %d )"),l ));
  582. if (l > 0)
  583. {
  584. return;
  585. }
  586. WIA_TRACE((TEXT("Deleting CWizardInfoBlob object...")));
  587. delete this;
  588. }
  589. /*****************************************************************************
  590. CWizardInfoBlob::ShowError
  591. Unified error reporting...
  592. *****************************************************************************/
  593. INT CWizardInfoBlob::ShowError( HWND hwnd, HRESULT hr, UINT idText, BOOL bAskTryAgain, LPITEMIDLIST pidl )
  594. {
  595. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::ShowError( hr=0x%x, Id=%d )"),hr,idText));
  596. CSimpleString strTitle( IDS_ERROR_TITLE, g_hInst );
  597. CSimpleString strFormat;
  598. CSimpleString strError( TEXT("") );
  599. CSimpleString strMessage( TEXT("") );
  600. CSimpleString strFilename;
  601. //
  602. // Record that an error has occurred...
  603. //
  604. _iNumErrorsWhileRunningWizard++;
  605. //
  606. // Get an hwnd if not specified
  607. //
  608. if (!hwnd)
  609. {
  610. hwnd = _hOuterDlg;
  611. }
  612. //
  613. // Formulate information string
  614. //
  615. if (idText)
  616. {
  617. //
  618. // We were given a specific message string to display
  619. //
  620. strFormat.LoadString( idText, g_hInst );
  621. }
  622. else
  623. {
  624. //
  625. // Wants generic error working with file...
  626. //
  627. strFilename.LoadString( IDS_UNKNOWN_FILE, g_hInst );
  628. strFormat.LoadString( IDS_ERROR_WITH_FILE, g_hInst );
  629. }
  630. UINT idErrorText = 0;
  631. //
  632. // map certain hr values to strings we have...
  633. //
  634. switch (hr)
  635. {
  636. case E_OUTOFMEMORY:
  637. idErrorText = IDS_ERROR_NOMEMORY;
  638. break;
  639. case PPW_E_UNABLE_TO_ROTATE:
  640. idErrorText = IDS_ERROR_ROTATION;
  641. break;
  642. case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
  643. idErrorText = IDS_ERROR_FILENOTFOUND;
  644. break;
  645. case E_ACCESSDENIED:
  646. idErrorText = IDS_ERROR_ACCESSDENIED;
  647. break;
  648. case HRESULT_FROM_WIN32(ERROR_INVALID_PIXEL_FORMAT):
  649. idErrorText = IDS_ERROR_UNKNOWNFORMAT;
  650. break;
  651. case HRESULT_FROM_WIN32(ERROR_NOT_READY):
  652. case HRESULT_FROM_WIN32(ERROR_WRONG_DISK):
  653. idErrorText = IDS_ERROR_WRONG_DISK;
  654. break;
  655. case E_FAIL:
  656. case E_NOTIMPL:
  657. case E_ABORT:
  658. case HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER):
  659. case E_INVALIDARG:
  660. idErrorText = IDS_ERROR_GENERIC;
  661. break;
  662. }
  663. if (idErrorText)
  664. {
  665. strError.LoadString( idErrorText, g_hInst );
  666. }
  667. else
  668. {
  669. //
  670. // construct basic error string given the hr
  671. //
  672. LPTSTR pszMsgBuf = NULL;
  673. FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  674. NULL,
  675. hr,
  676. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  677. (LPTSTR)&pszMsgBuf,
  678. 0,
  679. NULL
  680. );
  681. if (pszMsgBuf)
  682. {
  683. strError.Assign( pszMsgBuf );
  684. LocalFree(pszMsgBuf);
  685. }
  686. }
  687. if (pidl)
  688. {
  689. //
  690. // Get the filename for this file (if passed in)
  691. //
  692. CComPtr<IShellFolder> psfParent;
  693. LPCITEMIDLIST pidlLast;
  694. hr = SHBindToParent( pidl, IID_PPV_ARG(IShellFolder, &psfParent), &pidlLast );
  695. if (SUCCEEDED(hr) && psfParent)
  696. {
  697. TCHAR szName[ MAX_PATH ];
  698. *szName = 0;
  699. if SUCCEEDED(DisplayNameOf( psfParent, pidlLast, SHGDN_INFOLDER, szName, MAX_PATH ))
  700. {
  701. strFilename.Assign(szName);
  702. }
  703. }
  704. }
  705. //
  706. // We have all the pieces, now format the message
  707. //
  708. if (strFilename.Length())
  709. {
  710. //
  711. // no message string was specified, so we're expected to put the file name
  712. // in the message...
  713. //
  714. strMessage.Format( strFormat, strFilename.String(), strError.String() );
  715. }
  716. else
  717. {
  718. //
  719. // We were given a particular error string, so no file name displayed...
  720. //
  721. strMessage.Format( strFormat, strError.String() );
  722. }
  723. UINT uFlags = bAskTryAgain ? (MB_CANCELTRYCONTINUE | MB_DEFBUTTON1) : MB_OK;
  724. return MessageBox( hwnd, strMessage, strTitle, uFlags | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND );
  725. }
  726. /*****************************************************************************
  727. CWizardInfoBlob::RemoveAllCopiesOfPhotos
  728. Traverses the photo list and removes all copies
  729. *****************************************************************************/
  730. VOID CWizardInfoBlob::RemoveAllCopiesOfPhotos()
  731. {
  732. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos()")));
  733. CAutoCriticalSection lock(_csItems);
  734. //
  735. // The easiest way to do this is just to create a new DPA with only
  736. // the root (non copies) items in it...
  737. //
  738. if (_hdpaItems)
  739. {
  740. HDPA hdpaNew = DPA_Create(DEFAULT_DPA_SIZE);
  741. if (hdpaNew)
  742. {
  743. INT iCount = DPA_GetPtrCount( _hdpaItems );
  744. CListItem * pListItem = NULL;
  745. for (INT i=0; i<iCount; i++)
  746. {
  747. pListItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i);
  748. if (pListItem)
  749. {
  750. if (pListItem->IsCopyItem())
  751. {
  752. WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - removing copy 0x%x"),pListItem));
  753. delete pListItem;
  754. }
  755. else
  756. {
  757. //
  758. // Add this page to the item list...
  759. //
  760. INT iRes = DPA_AppendPtr( hdpaNew, (LPVOID)pListItem );
  761. if (iRes == -1)
  762. {
  763. WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - Tried to add 0x%x to new HDPA, but got back -1, deleting..."),pListItem));
  764. delete pListItem;
  765. }
  766. else
  767. {
  768. WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - adding 0x%x to new HDPA"),pListItem));
  769. }
  770. }
  771. }
  772. }
  773. //
  774. // We've removed all the items that are core items, now
  775. // delete old list and keep new one. This is safe because
  776. // we already deleted any items that weren't moved over
  777. // from the old list -- so either an item has been deleted
  778. // or it's in the new list and will be deleted when that
  779. // is cleaned up...
  780. //
  781. WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - destroying old list...")));
  782. DPA_Destroy( _hdpaItems );
  783. WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - setting _hdpaItems to new list...")));
  784. _hdpaItems = hdpaNew;
  785. }
  786. }
  787. }
  788. /*****************************************************************************
  789. CWizardInfoBlob::AddCopiesOfPhotos
  790. Adds the specified number of copies of each photo
  791. *****************************************************************************/
  792. VOID CWizardInfoBlob::AddCopiesOfPhotos( UINT uCopies )
  793. {
  794. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::AddCopiesOfPhotos( %d )"),uCopies));
  795. CAutoCriticalSection lock(_csItems);
  796. //
  797. // This function assumes that it is getting a pure list -- i.e., only
  798. // root items and no copies. This is accomplished by calling RemoveAllCopiesOfPhotos
  799. // before calling this routine...
  800. //
  801. //
  802. // Loop through all the items, and add the requested number of copies...
  803. //
  804. if (_hdpaItems)
  805. {
  806. INT iCount = DPA_GetPtrCount( _hdpaItems );
  807. HDPA hdpaNew = DPA_Create(DEFAULT_DPA_SIZE);
  808. if (hdpaNew)
  809. {
  810. CListItem * pListItem = NULL;
  811. CListItem * pListItemCopy = NULL;
  812. for (INT i=0; i<iCount; i++)
  813. {
  814. pListItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i);
  815. if (pListItem)
  816. {
  817. //
  818. // Add this item to the new list...
  819. //
  820. INT iRes = DPA_AppendPtr( hdpaNew, (LPVOID)pListItem );
  821. if (iRes != -1)
  822. {
  823. WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- root item 0x%x added to new list"),pListItem));
  824. //
  825. // Now, add n-1 copies. We do n-1 because we've
  826. // already added the root item one.
  827. //
  828. for (UINT uCopy = 1; uCopy < uCopies; uCopy++)
  829. {
  830. pListItemCopy = new CListItem( pListItem->GetSubItem(), pListItem->GetSubFrame() );
  831. if (pListItemCopy)
  832. {
  833. //
  834. // Mark this new entry as a copy so it can be deleted as necessary...
  835. //
  836. pListItemCopy->MarkAsCopy();
  837. //
  838. // Maintain the selection state
  839. //
  840. pListItemCopy->SetSelectionState( pListItem->SelectedForPrinting() );
  841. //
  842. // Add the new item to the list...
  843. //
  844. iRes = DPA_AppendPtr( hdpaNew, (LPVOID)pListItemCopy );
  845. if (iRes == -1)
  846. {
  847. WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- error adding copy %d of 0x%x to new list, deleting..."),uCopy,pListItem));
  848. delete pListItemCopy;
  849. }
  850. else
  851. {
  852. WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- copy %d of 0x%x added to new list"),uCopy,pListItem));
  853. }
  854. }
  855. else
  856. {
  857. WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- couldn't allocated copy %d of 0x%x"),uCopy,pListItem));
  858. }
  859. }
  860. }
  861. else
  862. {
  863. WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- error adding root item 0x%x added to new list, deleting..."),pListItem));
  864. delete pListItem;
  865. }
  866. }
  867. }
  868. //
  869. // Now, swap the lists...
  870. //
  871. WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- deleting old list...")));
  872. DPA_Destroy( _hdpaItems );
  873. WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- using new list...")));
  874. _hdpaItems = hdpaNew;
  875. }
  876. }
  877. }
  878. /*****************************************************************************
  879. CWizardInfoBlob::AddAllPhotosFromDataObject
  880. Runs through the data object and create CListItems for each item
  881. in the data object...
  882. *****************************************************************************/
  883. VOID CWizardInfoBlob::AddAllPhotosFromDataObject()
  884. {
  885. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::AddAllPhotosFromDataObject()") ));
  886. if (!_pdo || _bAlreadyAddedPhotos)
  887. {
  888. return;
  889. }
  890. _bAlreadyAddedPhotos = TRUE;
  891. //
  892. // Get an instance of the Namespace walking object...
  893. //
  894. UINT cItemsWalk;
  895. CComPtr<INamespaceWalk> pNSW;
  896. HRESULT hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pNSW));
  897. if (SUCCEEDED(hr))
  898. {
  899. //
  900. // Walk the namespace but only pull from current folder...
  901. //
  902. CWalkCallback cb;
  903. DWORD dwFlags;
  904. if (_bOnlyUseSelection)
  905. {
  906. dwFlags = 0;
  907. }
  908. else
  909. {
  910. dwFlags = (NSWF_ONE_IMPLIES_ALL | NSWF_NONE_IMPLIES_ALL);
  911. }
  912. hr = pNSW->Walk(_pdo, dwFlags, 0, &cb);
  913. if (SUCCEEDED(hr))
  914. {
  915. //
  916. // Get the list of pidls, note, when we do
  917. // this we own them -- in other words, we
  918. // have to free the pidls when we're done
  919. // with them...
  920. //
  921. LPITEMIDLIST *ppidls = NULL;
  922. hr = pNSW->GetIDArrayResult(&cItemsWalk, &ppidls);
  923. if (SUCCEEDED(hr) && ppidls)
  924. {
  925. WIA_TRACE((TEXT("AddAllPhotosFromDataObject: pNSW->GetIDArrayResult() returned cItemsWalk = %d"),cItemsWalk));
  926. for (INT i = 0; i < (INT)cItemsWalk; i++)
  927. {
  928. AddPhoto( ppidls[i] );
  929. ILFree( ppidls[i] );
  930. }
  931. CoTaskMemFree(ppidls);
  932. }
  933. else
  934. {
  935. WIA_ERROR((TEXT("AddAllPhotosFromDataObject(): pNSW->GetIDArrayResult() failed w/hr=0x%x"),hr));
  936. }
  937. //
  938. // If only one item was given to us, then force select all
  939. // on by default. This way all frames of a multi-frame
  940. // image will be selected.
  941. //
  942. if (cItemsWalk == 1)
  943. {
  944. _bForceSelectAll = TRUE;
  945. }
  946. }
  947. else
  948. {
  949. WIA_ERROR((TEXT("AddAllPhotosFromDataObject(): pNSW->Walk() failed w/hr=0x%x"),hr));
  950. }
  951. //
  952. // Were any items rejected while walking the item tree?
  953. //
  954. _bItemsWereRejected = cb.WereItemsRejected();
  955. //
  956. // Now, detect the case where one item was selected, but we loaded
  957. // all the images in the folder. In this case, we want to pre-select
  958. // only that one image. That image will be the first pidl we got
  959. // back from the INamespaceWalk call...
  960. //
  961. //
  962. // How many items are in the dataobject? This is will give us how
  963. // many items were selected in the shell view...
  964. //
  965. if (_pdo)
  966. {
  967. // Request the IDA from the data object
  968. FORMATETC fmt = {0};
  969. fmt.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST);
  970. fmt.dwAspect = DVASPECT_CONTENT;
  971. fmt.lindex = -1;
  972. fmt.tymed = TYMED_HGLOBAL;
  973. STGMEDIUM medium = { 0 };
  974. hr = _pdo->GetData(&fmt, &medium);
  975. if (SUCCEEDED(hr))
  976. {
  977. LPIDA pida = (LPIDA)GlobalLock( medium.hGlobal );
  978. if (pida)
  979. {
  980. _uItemsInInitialSelection = pida->cidl;
  981. WIA_TRACE((TEXT("_uItemsInInitialSelection = %d"),_uItemsInInitialSelection));
  982. //
  983. // Now check if only one item was in dataobject...
  984. //
  985. if (cItemsWalk < _uItemsInInitialSelection)
  986. {
  987. WIA_TRACE((TEXT("Some items were rejected, setting _bItemsWereRejected to TRUE!")));
  988. _bItemsWereRejected = TRUE;
  989. }
  990. //
  991. // Now check if only one item was in dataobject...
  992. //
  993. if (pida->cidl == 1)
  994. {
  995. //
  996. // There are two situations where we get one object:
  997. //
  998. // A. When the user actually had 1 object selected.
  999. // In this case, we will get back a relative pidl
  1000. // that is an item.
  1001. //
  1002. // B. When the user actually had 0 objects selected.
  1003. // In this case, we will get back a relative pidl
  1004. // that is a folder.
  1005. //
  1006. // We need to set the initialselection count to 1 for A
  1007. // and 0 for B.
  1008. //
  1009. LPITEMIDLIST pidlItem = (LPITEMIDLIST)((LPBYTE)(pida) + pida->aoffset[1]);
  1010. LPITEMIDLIST pidlFolder = (LPITEMIDLIST)((LPBYTE)(pida) + pida->aoffset[0]);
  1011. //
  1012. // Build fully qualified IDList...
  1013. //
  1014. LPITEMIDLIST pidlFull = ILCombine( pidlFolder, pidlItem );
  1015. if (pidlFull)
  1016. {
  1017. CComPtr<IShellFolder> psfParent;
  1018. LPCITEMIDLIST pidlLast;
  1019. hr = SHBindToParent( pidlFull, IID_PPV_ARG(IShellFolder, &psfParent), &pidlLast );
  1020. if (SUCCEEDED(hr) && psfParent)
  1021. {
  1022. ULONG uAttr = SFGAO_FOLDER;
  1023. hr = psfParent->GetAttributesOf( 1, &pidlLast, &uAttr );
  1024. if (SUCCEEDED(hr) && (uAttr & SFGAO_FOLDER))
  1025. {
  1026. _uItemsInInitialSelection = 0;
  1027. }
  1028. }
  1029. ILFree(pidlFull);
  1030. }
  1031. }
  1032. }
  1033. GlobalUnlock( medium.hGlobal );
  1034. ReleaseStgMedium( &medium );
  1035. }
  1036. }
  1037. }
  1038. else
  1039. {
  1040. WIA_ERROR((TEXT("AddAllPhotosFromDataObject(): couldn't CoCreate( INamespaceWalk ), hr = 0x%x"),hr));
  1041. }
  1042. _bAllPicturesAdded = TRUE;
  1043. }
  1044. /*****************************************************************************
  1045. CWizardInfoBlob::AddAllPhotosFromList
  1046. Runs through the pidl array and create CListItems for each item...
  1047. *****************************************************************************/
  1048. VOID CWizardInfoBlob::AddPhotosFromList( LPITEMIDLIST *aidl, int cidl, BOOL bSelectAll )
  1049. {
  1050. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::AddAllPhotosFromList()") ));
  1051. if (_bAlreadyAddedPhotos)
  1052. {
  1053. return;
  1054. }
  1055. _bAlreadyAddedPhotos = TRUE;
  1056. for (int i=0;i<cidl;i++)
  1057. {
  1058. AddPhoto(aidl[i]);
  1059. }
  1060. _uItemsInInitialSelection = bSelectAll?0:1;
  1061. //
  1062. // If there was only one item passed to us, then force select
  1063. // all on by default (even if it's a multi-frame image)
  1064. //
  1065. if (cidl == 1)
  1066. {
  1067. _bForceSelectAll = TRUE;
  1068. }
  1069. _bAllPicturesAdded = TRUE;
  1070. }
  1071. /*****************************************************************************
  1072. CWizardInfoBlob::AddPhoto
  1073. Creates a CListItem for the given fully qualified pidl to the speified photo,
  1074. and then adds it to the HDPA.
  1075. *****************************************************************************/
  1076. HRESULT CWizardInfoBlob::AddPhoto( LPITEMIDLIST pidlFull )
  1077. {
  1078. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::AddPhoto()") ));
  1079. HRESULT hr = S_OK;
  1080. CAutoCriticalSection lock(_csItems);
  1081. if (!_hdpaItems)
  1082. {
  1083. _hdpaItems = DPA_Create(DEFAULT_DPA_SIZE);
  1084. }
  1085. if (_hdpaItems)
  1086. {
  1087. //
  1088. // Next, create CPhotoItem for this pidl.. and store in the DPA...
  1089. //
  1090. BOOL bItemAddedToDPA = FALSE;
  1091. CPhotoItem * pItem = new CPhotoItem( pidlFull );
  1092. if (pItem)
  1093. {
  1094. //
  1095. // Got the photo item, now create a list item for each page...
  1096. //
  1097. LONG lFrames;
  1098. hr = pItem->GetImageFrameCount(&lFrames);
  1099. if (SUCCEEDED(hr))
  1100. {
  1101. //
  1102. // Create a list item for each page...
  1103. //
  1104. INT iRes;
  1105. CListItem * pListItem = NULL;
  1106. for (LONG lCurFrame=0; lCurFrame < lFrames; lCurFrame++ )
  1107. {
  1108. //
  1109. // NOTE: The pListItem constructor does an addref on pItem
  1110. //
  1111. pListItem = new CListItem( pItem, lCurFrame );
  1112. iRes = -1;
  1113. if (pListItem)
  1114. {
  1115. //
  1116. // Add this page to the item list...
  1117. //
  1118. iRes = DPA_AppendPtr( _hdpaItems, (LPVOID)pListItem );
  1119. WIA_TRACE((TEXT("DPA_AppendPtr returned %d"),iRes));
  1120. }
  1121. if (iRes == -1)
  1122. {
  1123. //
  1124. // the list item wasn't correctly added to
  1125. // the DPA. So we need to delete the list
  1126. // item entry, but not the underlying photo item
  1127. // object. To do this, we increase the
  1128. // reference count artificially on the item,
  1129. // then delete pListItem (which will cause a
  1130. // Release() to happen on the underlying pItem).
  1131. // Then we knowck down the reference count by 1
  1132. // to get back to the value that was there (on
  1133. // the underlying pItem) before the pListItem
  1134. // was created.
  1135. //
  1136. pItem->AddRef();
  1137. delete pListItem;
  1138. pItem->ReleaseWithoutDeleting();
  1139. hr = E_OUTOFMEMORY;
  1140. WIA_ERROR((TEXT("Couldn't create a list item for this photo item")));
  1141. }
  1142. else
  1143. {
  1144. //
  1145. // record that there is a legitimate outstanding
  1146. // reference to the pItem
  1147. //
  1148. bItemAddedToDPA = TRUE;
  1149. }
  1150. }
  1151. }
  1152. if (!bItemAddedToDPA)
  1153. {
  1154. //
  1155. // An error occurred trying to load the file, since we skip'd
  1156. // adding the item to our list, we'll leak this pointer if we
  1157. // don't delete it here...
  1158. //
  1159. delete pItem;
  1160. }
  1161. }
  1162. else
  1163. {
  1164. WIA_ERROR((TEXT("Couldn't allocate a new CPhotoItem!")));
  1165. }
  1166. }
  1167. else
  1168. {
  1169. WIA_ERROR((TEXT("Couldn't create _hdpaItems!")));
  1170. }
  1171. WIA_RETURN_HR(hr);
  1172. }
  1173. /*****************************************************************************
  1174. CWizardInfoBlob::ToggleSelectionStateOnCopies
  1175. Finds the root item in the list, and then toggles selection state
  1176. to be the specified state on any copies that follow the item in the list...
  1177. *****************************************************************************/
  1178. VOID CWizardInfoBlob::ToggleSelectionStateOnCopies( CListItem * pRootItem, BOOL bState )
  1179. {
  1180. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::CountOfPhotos()")));
  1181. CAutoCriticalSection lock(_csItems);
  1182. if (_hdpaItems)
  1183. {
  1184. //
  1185. // First, try to find this root item in our list...
  1186. //
  1187. INT iCountOfItems = DPA_GetPtrCount(_hdpaItems);
  1188. INT iIndexOfRootItem = -1;
  1189. CListItem * pListItem;
  1190. for (INT i=0; i<iCountOfItems; i++)
  1191. {
  1192. pListItem = (CListItem *)DPA_FastGetPtr( _hdpaItems, i );
  1193. if (pListItem == pRootItem)
  1194. {
  1195. iIndexOfRootItem = i;
  1196. break;
  1197. }
  1198. }
  1199. //
  1200. // Now walk the copies, if there are any...
  1201. //
  1202. if (iIndexOfRootItem != -1)
  1203. {
  1204. INT iIndexOfFirstCopy = iIndexOfRootItem + 1;
  1205. if (iIndexOfFirstCopy < iCountOfItems)
  1206. {
  1207. for (INT i=iIndexOfFirstCopy; i < iCountOfItems; i++)
  1208. {
  1209. pListItem = (CListItem *)DPA_FastGetPtr( _hdpaItems, i );
  1210. //
  1211. // If we get back a NULL item, then bail...
  1212. //
  1213. if (!pListItem)
  1214. {
  1215. break;
  1216. }
  1217. //
  1218. // If we get a new root item, then we have traversed all the
  1219. // copies, so bail...
  1220. //
  1221. if (!pListItem->IsCopyItem())
  1222. {
  1223. break;
  1224. }
  1225. //
  1226. // This is a copy of the specified root item. Mark it
  1227. // to have the correct selection state...
  1228. //
  1229. pListItem->SetSelectionState(bState);
  1230. }
  1231. }
  1232. }
  1233. }
  1234. }
  1235. /*****************************************************************************
  1236. CWizardInfoBlob::CountOfPhotos
  1237. Returns number of photos
  1238. *****************************************************************************/
  1239. INT CWizardInfoBlob::CountOfPhotos( BOOL bIncludeCopies )
  1240. {
  1241. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::CountOfPhotos()")));
  1242. CAutoCriticalSection lock(_csItems);
  1243. if (_hdpaItems)
  1244. {
  1245. if (bIncludeCopies)
  1246. {
  1247. return (INT)DPA_GetPtrCount( _hdpaItems );
  1248. }
  1249. else
  1250. {
  1251. //
  1252. // actually walk the list and only count root (non-copy) items...
  1253. //
  1254. INT iCount = 0;
  1255. CListItem * pListItem = NULL;
  1256. for (INT i=0; i<(INT)DPA_GetPtrCount(_hdpaItems); i++)
  1257. {
  1258. pListItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i);
  1259. if (pListItem && (!pListItem->IsCopyItem()))
  1260. {
  1261. iCount++;
  1262. }
  1263. }
  1264. return iCount;
  1265. }
  1266. }
  1267. return 0;
  1268. }
  1269. /*****************************************************************************
  1270. CWizardInfoBlob::CountOfSelectedPhotos
  1271. returns the number of photos selected for printing
  1272. *****************************************************************************/
  1273. INT CWizardInfoBlob::CountOfSelectedPhotos( BOOL bIncludeCopies )
  1274. {
  1275. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::CountOfSelecetedPhotos()")));
  1276. INT iCount = 0;
  1277. CAutoCriticalSection lock(_csItems);
  1278. if (_hdpaItems)
  1279. {
  1280. INT iTotal = DPA_GetPtrCount( _hdpaItems );
  1281. CListItem * pItem = NULL;
  1282. for (INT i = 0; i < iTotal; i++)
  1283. {
  1284. pItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i);
  1285. if (pItem)
  1286. {
  1287. if (bIncludeCopies)
  1288. {
  1289. if (pItem->SelectedForPrinting())
  1290. {
  1291. iCount++;
  1292. }
  1293. }
  1294. else
  1295. {
  1296. if ((!pItem->IsCopyItem()) && pItem->SelectedForPrinting())
  1297. {
  1298. iCount++;
  1299. }
  1300. }
  1301. }
  1302. }
  1303. }
  1304. return iCount;
  1305. }
  1306. /*****************************************************************************
  1307. CWizardInfoBlob::GetIndexOfNextPrintableItem
  1308. Starting from iStartIndex, returns the index of the next item
  1309. that is mark as selected for printing...
  1310. *****************************************************************************/
  1311. INT CWizardInfoBlob::GetIndexOfNextPrintableItem( INT iStartIndex )
  1312. {
  1313. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetIndexOfNextPrintableItem( %d )"),iStartIndex));
  1314. INT iIndex = -1;
  1315. INT iCountOfAllItems = CountOfPhotos(TRUE);
  1316. CListItem * pItem = NULL;
  1317. if (iStartIndex != -1)
  1318. {
  1319. for( INT i = iStartIndex; i < iCountOfAllItems; i++ )
  1320. {
  1321. pItem = GetListItem( i, TRUE );
  1322. if (pItem)
  1323. {
  1324. if (pItem->SelectedForPrinting())
  1325. {
  1326. iIndex = i;
  1327. break;
  1328. }
  1329. }
  1330. }
  1331. }
  1332. return iIndex;
  1333. }
  1334. /*****************************************************************************
  1335. CWizardInfoBlob::GetListItem
  1336. Returns given item from the list of photos
  1337. *****************************************************************************/
  1338. CListItem * CWizardInfoBlob::GetListItem( INT iIndex, BOOL bIncludeCopies )
  1339. {
  1340. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetListItem( %d )"),iIndex));
  1341. CAutoCriticalSection lock(_csItems);
  1342. if (iIndex == -1)
  1343. {
  1344. return NULL;
  1345. }
  1346. if (_hdpaItems)
  1347. {
  1348. if (bIncludeCopies)
  1349. {
  1350. if ((iIndex >= 0) && (iIndex < DPA_GetPtrCount(_hdpaItems)))
  1351. {
  1352. return (CListItem *)DPA_FastGetPtr(_hdpaItems,iIndex);
  1353. }
  1354. }
  1355. else
  1356. {
  1357. //
  1358. // If we're not including copies, then we need to walk the
  1359. // whole list to find root items only. This is much slower,
  1360. // but will always find root items only.
  1361. //
  1362. CListItem * pListItem = NULL;
  1363. INT iRootIndex = 0;
  1364. for (INT i = 0; i < DPA_GetPtrCount(_hdpaItems); i++)
  1365. {
  1366. pListItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i);
  1367. if (pListItem && (!pListItem->IsCopyItem()))
  1368. {
  1369. if (iIndex == iRootIndex)
  1370. {
  1371. return pListItem;
  1372. }
  1373. else
  1374. {
  1375. iRootIndex++;
  1376. }
  1377. }
  1378. }
  1379. }
  1380. }
  1381. return NULL;
  1382. }
  1383. /*****************************************************************************
  1384. CWizardInfoBlob::GetTemplateByIndex
  1385. Gets a template given the index
  1386. *****************************************************************************/
  1387. HRESULT CWizardInfoBlob::GetTemplateByIndex(INT iIndex, CTemplateInfo ** ppTemplateInfo )
  1388. {
  1389. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetTemplateByIndex( iIndex = %d )"),iIndex));
  1390. if (ppTemplateInfo)
  1391. {
  1392. return _templates.GetTemplate( iIndex, ppTemplateInfo );
  1393. }
  1394. return E_INVALIDARG;
  1395. }
  1396. /*****************************************************************************
  1397. CWizardInfoBlob::TemplateGetPreviewBitmap
  1398. returns S_OK on sucess or COM error otherwise
  1399. *****************************************************************************/
  1400. HRESULT CWizardInfoBlob::TemplateGetPreviewBitmap(INT iIndex, const SIZE &sizeDesired, HBITMAP *phBmp)
  1401. {
  1402. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::TemplateGetPreviewBitmap( iIndex = %d )"),iIndex));
  1403. HRESULT hr = E_INVALIDARG;
  1404. IStream * pStream = NULL;
  1405. Gdiplus::Bitmap * pBmp = NULL;
  1406. CTemplateInfo * pTemplate = NULL;
  1407. hr = _templates.GetTemplate( iIndex, &pTemplate );
  1408. WIA_CHECK_HR(hr,"_templates.GetTemplate()");
  1409. if (SUCCEEDED(hr) && pTemplate)
  1410. {
  1411. hr = pTemplate->GetPreviewImageStream( &pStream );
  1412. WIA_CHECK_HR(hr,"pTemplate->GetPreviewImageStream( &pStream )");
  1413. if (SUCCEEDED(hr) && pStream)
  1414. {
  1415. // 48 62
  1416. hr = LoadAndScaleBmp(pStream, sizeDesired.cx, sizeDesired.cy, &pBmp);
  1417. WIA_CHECK_HR(hr,"LoadAndScaleBmp( pStream, size.cx, size.cy, pBmp )");
  1418. if (SUCCEEDED(hr) && pBmp)
  1419. {
  1420. DWORD dw = GetSysColor(COLOR_WINDOW);
  1421. Gdiplus::Color wndClr(255, GetRValue(dw), GetGValue(dw), GetBValue(dw));
  1422. hr = Gdiplus2HRESULT(pBmp->GetHBITMAP(wndClr, phBmp));
  1423. WIA_CHECK_HR(hr,"pBmp->GetHBITMAP( phBmp )");
  1424. delete pBmp;
  1425. }
  1426. pStream->Release();
  1427. }
  1428. }
  1429. WIA_RETURN_HR(hr);
  1430. }
  1431. /*****************************************************************************
  1432. CWizardInfoBlob::SetPrinterToUse
  1433. Sets the name of the printer to use to print...
  1434. *****************************************************************************/
  1435. HRESULT CWizardInfoBlob::SetPrinterToUse( LPCTSTR pszPrinterName )
  1436. {
  1437. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::SetPrinterToUse( '%s' )"),pszPrinterName ? pszPrinterName : TEXT("NULL POINTER!")));
  1438. if (!pszPrinterName || !(*pszPrinterName))
  1439. {
  1440. WIA_RETURN_HR( E_INVALIDARG );
  1441. }
  1442. _strPrinterName = pszPrinterName;
  1443. return S_OK;
  1444. }
  1445. /*****************************************************************************
  1446. CWizardInfoBlob::SetDevModeToUse
  1447. Sets the DEVMODE pointer to use to print...
  1448. *****************************************************************************/
  1449. HRESULT CWizardInfoBlob::SetDevModeToUse( PDEVMODE pDevMode )
  1450. {
  1451. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::SetDevModeToUse(0x%x)"),pDevMode));
  1452. HRESULT hr = E_INVALIDARG;
  1453. if (_hDevMode)
  1454. {
  1455. delete [] _hDevMode;
  1456. _hDevMode = NULL;
  1457. }
  1458. if (pDevMode)
  1459. {
  1460. UINT cbDevMode = (UINT)pDevMode->dmSize + (UINT)pDevMode->dmDriverExtra;
  1461. if( _hDevMode = (PDEVMODE) new BYTE[cbDevMode] )
  1462. {
  1463. WIA_TRACE((TEXT("CWizardInfoBlob::SetDevModeToUse - copying pDevMode(0x%x) to _hDevMode(0x%x)"),pDevMode,_hDevMode));
  1464. CopyMemory( _hDevMode, pDevMode, cbDevMode );
  1465. hr = S_OK;
  1466. }
  1467. else
  1468. {
  1469. hr = E_OUTOFMEMORY;
  1470. }
  1471. }
  1472. WIA_RETURN_HR(hr);
  1473. }
  1474. /*****************************************************************************
  1475. CWizardInfoBlob::GetDevModeToUse
  1476. Retrieves the devmode pointer to use
  1477. *****************************************************************************/
  1478. PDEVMODE CWizardInfoBlob::GetDevModeToUse()
  1479. {
  1480. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetDevModeToUse()")));
  1481. return _hDevMode;
  1482. }
  1483. /*****************************************************************************
  1484. CWizardInfoBlob::GetPrinterToUse
  1485. Returns the string that represent which printer to print to...
  1486. *****************************************************************************/
  1487. LPCTSTR CWizardInfoBlob::GetPrinterToUse()
  1488. {
  1489. if (_strPrinterName.Length())
  1490. {
  1491. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetPrinterToUse( returning: '%s' )"),_strPrinterName.String()));
  1492. return _strPrinterName.String();
  1493. }
  1494. return NULL;
  1495. }
  1496. /*****************************************************************************
  1497. CWizardInfoBlob::ConstructPrintToTemplate
  1498. When the wizard is invoked for "PrintTo" functionatlity, construct
  1499. a template that represents full page
  1500. *****************************************************************************/
  1501. VOID CWizardInfoBlob::ConstructPrintToTemplate()
  1502. {
  1503. WIA_PUSH_FUNCTION_MASK((TRACE_PRINTTO, TEXT("CWizardInfoBlob::ConstructPrintToTemplate()")));
  1504. //
  1505. // creates 1 template that is full page print...
  1506. //
  1507. _templates.InitForPrintTo();
  1508. }
  1509. /*****************************************************************************
  1510. CWizardInfoBlob::GetCountOfPrintedPages
  1511. Returns the number of pages that will be printed with the specified
  1512. template.
  1513. *****************************************************************************/
  1514. HRESULT CWizardInfoBlob::GetCountOfPrintedPages( INT iTemplateIndex, INT * pPageCount )
  1515. {
  1516. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetCountOfPrintedPages( iTemplateIndex = %d )"),iTemplateIndex));
  1517. HRESULT hr = E_FAIL;
  1518. //
  1519. // Check for bad params...
  1520. //
  1521. if ( !pPageCount ||
  1522. ((iTemplateIndex < 0) || (iTemplateIndex >= _templates.Count()))
  1523. )
  1524. {
  1525. WIA_RETURN_HR( E_INVALIDARG );
  1526. }
  1527. //
  1528. // Get template in question...
  1529. //
  1530. CTemplateInfo * pTemplate = NULL;
  1531. hr = _templates.GetTemplate( iTemplateIndex, &pTemplate );
  1532. if (SUCCEEDED(hr) && pTemplate)
  1533. {
  1534. //
  1535. // Is this a template that wants to repeat photos?
  1536. //
  1537. BOOL bRepeat = FALSE;
  1538. hr = pTemplate->GetRepeatPhotos( &bRepeat );
  1539. if (SUCCEEDED(hr))
  1540. {
  1541. //
  1542. // Get the count
  1543. //
  1544. if (!bRepeat)
  1545. {
  1546. INT iPhotosPerTemplate = pTemplate->PhotosPerPage();
  1547. INT iCountOfPhotos = CountOfSelectedPhotos(TRUE);
  1548. INT PagesRequired = iCountOfPhotos / iPhotosPerTemplate;
  1549. if (iCountOfPhotos % iPhotosPerTemplate)
  1550. {
  1551. PagesRequired++;
  1552. }
  1553. *pPageCount = PagesRequired;
  1554. }
  1555. else
  1556. {
  1557. *pPageCount = CountOfSelectedPhotos(TRUE);
  1558. }
  1559. }
  1560. }
  1561. WIA_RETURN_HR(hr);
  1562. }
  1563. /*****************************************************************************
  1564. CWizardInfoBlob::SetPreviewWnd
  1565. Stores the hwnd that is the preview and also calculates the center of
  1566. the window so all resizes will keep it in the right place in the window.
  1567. *****************************************************************************/
  1568. VOID CWizardInfoBlob::SetPreviewWnd( HWND hwnd )
  1569. {
  1570. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::SetPreviewWnd( hwnd = 0x%x )"),hwnd));
  1571. if (hwnd)
  1572. {
  1573. _hwndPreview = hwnd;
  1574. GetClientRect( _hwndPreview, &_rcInitSize );
  1575. MapWindowPoints( _hwndPreview, GetParent(_hwndPreview), (LPPOINT)&_rcInitSize, 2 );
  1576. //
  1577. // Find center of window
  1578. //
  1579. _Center.cx = MulDiv(_rcInitSize.right - _rcInitSize.left, 1, 2) + _rcInitSize.left;
  1580. _Center.cy = MulDiv(_rcInitSize.bottom - _rcInitSize.top, 1, 2) + _rcInitSize.top;
  1581. }
  1582. }
  1583. /*****************************************************************************
  1584. CWizardInfoBlob::GetIntroFont
  1585. Creates a font to be used for the intro text in the wizard...
  1586. *****************************************************************************/
  1587. HFONT CWizardInfoBlob::GetIntroFont(HWND hwnd)
  1588. {
  1589. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetIntroFont()")));
  1590. if ( !_hfontIntro )
  1591. {
  1592. TCHAR szBuffer[64];
  1593. NONCLIENTMETRICS ncm = { 0 };
  1594. LOGFONT lf;
  1595. ncm.cbSize = SIZEOF(ncm);
  1596. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
  1597. lf = ncm.lfMessageFont;
  1598. LoadString(g_hInst, IDS_TITLEFONTNAME, lf.lfFaceName, ARRAYSIZE(lf.lfFaceName));
  1599. lf.lfWeight = FW_BOLD;
  1600. LoadString(g_hInst, IDS_TITLEFONTSIZE, szBuffer, ARRAYSIZE(szBuffer));
  1601. lf.lfHeight = 0 - (GetDeviceCaps(NULL, LOGPIXELSY) * StrToInt(szBuffer) / 72);
  1602. _hfontIntro = CreateFontIndirect(&lf);
  1603. }
  1604. return _hfontIntro;
  1605. }
  1606. /*****************************************************************************
  1607. CWizardInfoBlob::UpdateCachedPrinterInfo
  1608. Update some cached information about the printer...
  1609. *****************************************************************************/
  1610. VOID CWizardInfoBlob::UpdateCachedPrinterInfo()
  1611. {
  1612. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::UpdateCachedPrinterInfo()")));
  1613. CAutoCriticalSection lock(_csPrinterInfo);
  1614. BOOL bDeleteDC = FALSE;
  1615. HDC hDC = GetCachedPrinterDC();
  1616. if (!hDC)
  1617. {
  1618. //
  1619. // For some reason, we don't have a stored DC. So, we need to create
  1620. // one so that we can get the info...
  1621. //
  1622. hDC = CreateDC( TEXT("WINSPOOL"), GetPrinterToUse(), NULL, GetDevModeToUse() );
  1623. bDeleteDC = TRUE;
  1624. }
  1625. if (hDC)
  1626. {
  1627. //
  1628. // Get DPI information
  1629. //
  1630. _WizPrinterInfo.DPI.cx = GetDeviceCaps( hDC, LOGPIXELSX );
  1631. _WizPrinterInfo.DPI.cy = GetDeviceCaps( hDC, LOGPIXELSY );
  1632. //
  1633. // Get size of printable area...
  1634. //
  1635. _WizPrinterInfo.rcDevice.left = 0;
  1636. _WizPrinterInfo.rcDevice.right = GetDeviceCaps( hDC, HORZRES );
  1637. _WizPrinterInfo.rcDevice.top = 0;
  1638. _WizPrinterInfo.rcDevice.bottom = GetDeviceCaps( hDC, VERTRES );
  1639. //
  1640. // Get physical size of printer's page
  1641. //
  1642. _WizPrinterInfo.PhysicalSize.cx = GetDeviceCaps( hDC, PHYSICALWIDTH );
  1643. _WizPrinterInfo.PhysicalSize.cy = GetDeviceCaps( hDC, PHYSICALHEIGHT );
  1644. //
  1645. // Get physical offset to printable area
  1646. //
  1647. _WizPrinterInfo.PhysicalOffset.cx = GetDeviceCaps( hDC, PHYSICALOFFSETX );
  1648. _WizPrinterInfo.PhysicalOffset.cy = GetDeviceCaps( hDC, PHYSICALOFFSETY );
  1649. //
  1650. // Say that we've got valid information now...
  1651. //
  1652. _WizPrinterInfo.bValid = TRUE;
  1653. }
  1654. if (bDeleteDC)
  1655. {
  1656. if (hDC)
  1657. {
  1658. DeleteDC( hDC );
  1659. }
  1660. }
  1661. }
  1662. /*****************************************************************************
  1663. CWizardInfoBlob::SetNumberOfCopies
  1664. When the number of copies of each pictures changes, do the work
  1665. here...
  1666. *****************************************************************************/
  1667. VOID CWizardInfoBlob::SetNumberOfCopies( UINT uCopies )
  1668. {
  1669. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::SetNumberOfCopies( %d )"),uCopies));
  1670. //
  1671. // We really want to do this on a background thread, so queue up a message
  1672. // to the only background thread we control -- the GDI+ startup & shutdown
  1673. // thread. We'll overload here to handle this task...
  1674. //
  1675. if (_dwGdiPlusThreadId)
  1676. {
  1677. PostThreadMessage( _dwGdiPlusThreadId, WIZ_MSG_COPIES_CHANGED, (WPARAM)uCopies, 0 );
  1678. }
  1679. }
  1680. /*****************************************************************************
  1681. _SetupDimensionsForPrinting
  1682. Computes all relevant information to printing to a page.
  1683. *****************************************************************************/
  1684. VOID CWizardInfoBlob::_SetupDimensionsForPrinting( HDC hDC, CTemplateInfo * pTemplate, RENDER_DIMENSIONS * pDim )
  1685. {
  1686. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::_SetupDimensionsForPrinting()")));
  1687. if (!pDim)
  1688. {
  1689. WIA_ERROR((TEXT("Printer: pDim is NULL!")));
  1690. return;
  1691. }
  1692. if (!pTemplate)
  1693. {
  1694. WIA_ERROR((TEXT("Printer: pTemplate is NULL!")));
  1695. }
  1696. //
  1697. // Make sure we have good values in the cached printer info structure
  1698. //
  1699. GetCachedPrinterInfo();
  1700. //
  1701. // Flush out old values...
  1702. //
  1703. ZeroMemory( pDim, sizeof(RENDER_DIMENSIONS) );
  1704. //
  1705. // Derive multiplier for horizontal & vertical measurements
  1706. // (NOMINAL --> printer), and compute rcDevice which is
  1707. // the printable area available (in device units -- pixels).
  1708. //
  1709. pDim->DPI = _WizPrinterInfo.DPI;
  1710. WIA_TRACE((TEXT("Printer: xDPI = %d, yDPI = %d"),pDim->DPI.cx,pDim->DPI.cy));
  1711. pDim->rcDevice = _WizPrinterInfo.rcDevice;
  1712. WIA_TRACE((TEXT("Printer: rcDevice( %d, %d, %d, %d )"),pDim->rcDevice.left, pDim->rcDevice.top, pDim->rcDevice.right, pDim->rcDevice.bottom ));
  1713. //
  1714. // Convert device coords into 1/10000 inch equivalents
  1715. //
  1716. pDim->NominalDevicePrintArea.cx = (INT)((DOUBLE)(((DOUBLE)pDim->rcDevice.right / (DOUBLE)pDim->DPI.cx) * (DOUBLE)NOMINAL_MULTIPLIER));
  1717. pDim->NominalDevicePrintArea.cy = (INT)((DOUBLE)(((DOUBLE)pDim->rcDevice.bottom / (DOUBLE)pDim->DPI.cy) * (DOUBLE)NOMINAL_MULTIPLIER));
  1718. WIA_TRACE((TEXT("Printer: DeviceNominal ( %d, %d )"),pDim->NominalDevicePrintArea.cx,pDim->NominalDevicePrintArea.cy));
  1719. //
  1720. // Get physical page size (in nominal coords)
  1721. //
  1722. pDim->NominalPhysicalSize.cx = (INT)((DOUBLE)(((DOUBLE)_WizPrinterInfo.PhysicalSize.cx / (DOUBLE)pDim->DPI.cx) * (DOUBLE)NOMINAL_MULTIPLIER));
  1723. pDim->NominalPhysicalSize.cy = (INT)((DOUBLE)(((DOUBLE)_WizPrinterInfo.PhysicalSize.cy / (DOUBLE)pDim->DPI.cy) * (DOUBLE)NOMINAL_MULTIPLIER));
  1724. WIA_TRACE((TEXT("Printer: NominalPhysicalSize (%d, %d)"),pDim->NominalPhysicalSize.cx,pDim->NominalPhysicalSize.cy));
  1725. //
  1726. // Get physical offset to printable area (in nominal coords)
  1727. //
  1728. pDim->NominalPhysicalOffset.cx = (INT)((DOUBLE)(((DOUBLE)_WizPrinterInfo.PhysicalOffset.cx / (DOUBLE)pDim->DPI.cx) * (DOUBLE)NOMINAL_MULTIPLIER));
  1729. pDim->NominalPhysicalOffset.cy = (INT)((DOUBLE)(((DOUBLE)_WizPrinterInfo.PhysicalOffset.cy / (DOUBLE)pDim->DPI.cx) * (DOUBLE)NOMINAL_MULTIPLIER));
  1730. WIA_TRACE((TEXT("Printer: NominalPhyscialOffset (%d, %d)"),pDim->NominalPhysicalOffset.cx,pDim->NominalPhysicalOffset.cy));
  1731. //
  1732. // Compute offset that will center the template in the printable
  1733. // area. Note, this can be a negative number if the paper size
  1734. // selected is too small to contain the template.
  1735. //
  1736. if (SUCCEEDED(pTemplate->GetNominalRectForImageableArea( &pDim->rcNominalTemplatePrintArea )))
  1737. {
  1738. if ((-1 == pDim->rcNominalTemplatePrintArea.left) &&
  1739. (-1 == pDim->rcNominalTemplatePrintArea.right) &&
  1740. (-1 == pDim->rcNominalTemplatePrintArea.top) &&
  1741. (-1 == pDim->rcNominalTemplatePrintArea.bottom))
  1742. {
  1743. WIA_TRACE((TEXT("Printer: NominalTemplateArea( use full printable area )")));
  1744. pDim->NominalPageOffset.cx = 0;
  1745. pDim->NominalPageOffset.cy = 0;
  1746. }
  1747. else
  1748. {
  1749. WIA_TRACE((TEXT("Printer: NominalTemplateArea(%d, %d)"),pDim->rcNominalTemplatePrintArea.right - pDim->rcNominalTemplatePrintArea.left,pDim->rcNominalTemplatePrintArea.bottom - pDim->rcNominalTemplatePrintArea.top));
  1750. pDim->NominalPageOffset.cx = (pDim->NominalDevicePrintArea.cx - (pDim->rcNominalTemplatePrintArea.right - pDim->rcNominalTemplatePrintArea.left)) / 2;
  1751. pDim->NominalPageOffset.cy = (pDim->NominalDevicePrintArea.cy - (pDim->rcNominalTemplatePrintArea.bottom - pDim->rcNominalTemplatePrintArea.top)) / 2;
  1752. }
  1753. }
  1754. WIA_TRACE((TEXT("Printer: NominalPageOffset(%d, %d)"),pDim->NominalPageOffset.cx,pDim->NominalPageOffset.cy));
  1755. //
  1756. // Compute clip rectangle for printable area on physical page (nominal coords)
  1757. //
  1758. pDim->rcNominalPageClip.left = pDim->NominalPhysicalOffset.cx;
  1759. pDim->rcNominalPageClip.top = pDim->NominalPhysicalOffset.cy;
  1760. pDim->rcNominalPageClip.right = pDim->rcNominalPageClip.left + pDim->NominalDevicePrintArea.cx;
  1761. pDim->rcNominalPageClip.bottom = pDim->rcNominalPageClip.top + pDim->NominalDevicePrintArea.cy;
  1762. WIA_TRACE((TEXT("Printer: rcNominalPageClip is (%d, %d, %d, %d)"), pDim->rcNominalPageClip.left, pDim->rcNominalPageClip.top, pDim->rcNominalPageClip.right, pDim->rcNominalPageClip.bottom ));
  1763. }
  1764. /*****************************************************************************
  1765. _SetupDimensionsForScreen
  1766. Computes all relevant information for drawing to the screen.
  1767. *****************************************************************************/
  1768. VOID CWizardInfoBlob::_SetupDimensionsForScreen( CTemplateInfo * pTemplate, HWND hwndScreen, RENDER_DIMENSIONS * pDim )
  1769. {
  1770. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::_SetupDimensionsForScreen()")));
  1771. if (!pDim)
  1772. {
  1773. WIA_ERROR((TEXT("Screen: pDim is NULL!")));
  1774. return;
  1775. }
  1776. //
  1777. // Before we do anything, check to see if we're in Portrait or Landscape
  1778. // and rotate the template accordingly...
  1779. //
  1780. PDEVMODE pDevMode = GetDevModeToUse();
  1781. if (pDevMode)
  1782. {
  1783. if (pDevMode->dmFields & DM_ORIENTATION)
  1784. {
  1785. if (pDevMode->dmOrientation == DMORIENT_PORTRAIT)
  1786. {
  1787. pTemplate->RotateForPortrait();
  1788. }
  1789. else if (pDevMode->dmOrientation == DMORIENT_LANDSCAPE)
  1790. {
  1791. pTemplate->RotateForLandscape();
  1792. }
  1793. }
  1794. }
  1795. _SetupDimensionsForPrinting( GetCachedPrinterDC(), pTemplate, pDim );
  1796. //
  1797. // Flush out old values, except for NominalPhysicalSize and
  1798. // NominalPhysicalOffset and NominalPageOffset which we want to keep...
  1799. //
  1800. pDim->rcDevice.left = 0;
  1801. pDim->rcDevice.top = 0;
  1802. pDim->rcDevice.right = 0;
  1803. pDim->rcDevice.bottom = 0;
  1804. pDim->DPI.cx = 0;
  1805. pDim->DPI.cy = 0;
  1806. RECT rcWnd = _rcInitSize;
  1807. WIA_TRACE((TEXT("Screen: _rcInitSize was (%d, %d, %d, %d)"),_rcInitSize.left,_rcInitSize.top,_rcInitSize.right,_rcInitSize.bottom));
  1808. //
  1809. // Get span of window to contain preview...
  1810. //
  1811. INT wScreen = rcWnd.right - rcWnd.left;
  1812. INT hScreen = rcWnd.bottom - rcWnd.top;
  1813. WIA_TRACE((TEXT("Screen: w = %d, h = %d"),wScreen,hScreen));
  1814. //
  1815. // Get DPI of screen
  1816. //
  1817. HDC hDC = GetDC( hwndScreen );
  1818. if (hDC)
  1819. {
  1820. pDim->DPI.cx = GetDeviceCaps( hDC, LOGPIXELSX );
  1821. pDim->DPI.cy = GetDeviceCaps( hDC, LOGPIXELSY );
  1822. ReleaseDC( hwndScreen, hDC );
  1823. }
  1824. //
  1825. // Scale printable area into window
  1826. //
  1827. SIZE sizePreview;
  1828. sizePreview = PrintScanUtil::ScalePreserveAspectRatio( wScreen, hScreen, pDim->NominalPhysicalSize.cx, pDim->NominalPhysicalSize.cy );
  1829. WIA_TRACE((TEXT("Screen: scaled print page is (%d, %d)"),sizePreview.cx,sizePreview.cy));
  1830. rcWnd.left = _rcInitSize.left;
  1831. rcWnd.top = _Center.cy - (sizePreview.cy / 2);
  1832. rcWnd.right = rcWnd.left + sizePreview.cx;
  1833. rcWnd.bottom = rcWnd.top + sizePreview.cy;
  1834. //
  1835. // Now change window size to be preview size...
  1836. //
  1837. WIA_TRACE((TEXT("Screen: moving window to (%d, %d) with size (%d, %d)"),rcWnd.left,rcWnd.top,sizePreview.cx,sizePreview.cy));
  1838. MoveWindow( hwndScreen, rcWnd.left, rcWnd.top, sizePreview.cx, sizePreview.cy, TRUE );
  1839. }
  1840. /*****************************************************************************
  1841. CWizardInfoBlob::_RenderFilenameOfPhoto
  1842. Draws the filename of the photo underneath the photo
  1843. *****************************************************************************/
  1844. VOID CWizardInfoBlob::_RenderFilenameOfPhoto( Gdiplus::Graphics * g, RECT * pPhotoDest, CListItem * pPhoto )
  1845. {
  1846. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::_RenderFilenameOfPhoto()")));
  1847. //
  1848. // check for bad params
  1849. //
  1850. if (!pPhotoDest || !g || !pPhoto)
  1851. {
  1852. return;
  1853. }
  1854. //
  1855. // the rectangle for the filename is the width of the photo & 2 text lines high, with a
  1856. // .05" gap from the bottom of the photo. All measurements are in nominal
  1857. // sizes, which means 1/10000 of an inch.
  1858. //
  1859. Gdiplus::Font font( L"arial", (Gdiplus::REAL)1100.0, Gdiplus::FontStyleRegular, Gdiplus::UnitWorld, NULL );
  1860. WIA_TRACE((TEXT("_RenderFilenameOfPhoto: height = %d pixels, emSize = %d"),(INT)font.GetHeight((Gdiplus::Graphics *)NULL), (INT)font.GetSize()));
  1861. Gdiplus::RectF rectText( (Gdiplus::REAL)pPhotoDest->left,
  1862. (Gdiplus::REAL)(pPhotoDest->bottom + 500),
  1863. (Gdiplus::REAL)(pPhotoDest->right - pPhotoDest->left),
  1864. (Gdiplus::REAL)font.GetHeight((Gdiplus::Graphics *)NULL) * (Gdiplus::REAL)2.0);
  1865. //Gdiplus::StringFormat sf( Gdiplus::StringFormatFlagsLineLimit );
  1866. Gdiplus::StringFormat sf( 0 );
  1867. sf.SetTrimming( Gdiplus::StringTrimmingEllipsisCharacter );
  1868. sf.SetAlignment( Gdiplus::StringAlignmentCenter );
  1869. CSimpleStringWide * pFilename = pPhoto->GetFilename();
  1870. if (pFilename)
  1871. {
  1872. WIA_TRACE((TEXT("_RenderFilenameOfPhoto: <%s> in (%d x %d) at (%d,%d), fontsize=%d"),CSimpleStringConvert::NaturalString(*pFilename).String(),(INT)rectText.Width,(INT)rectText.Height,(INT)rectText.X,(INT)rectText.Y,font.GetSize()));
  1873. g->DrawString( pFilename->String(), pFilename->Length(), &font, rectText, &sf, &Gdiplus::SolidBrush( Gdiplus::Color::Black ) );
  1874. delete pFilename;
  1875. }
  1876. }
  1877. /*****************************************************************************
  1878. CWizardInfoBlob::RenderPrintedPage
  1879. Draws photos to the printer according to which layout, which page and the
  1880. given printer hDC.
  1881. *****************************************************************************/
  1882. HRESULT CWizardInfoBlob::RenderPrintedPage( INT iTemplateIndex, INT iPage, HDC hDC, HWND hwndProgress, float fProgressStep, float * pPercent )
  1883. {
  1884. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::RenderPrintedPage( iPage = %d, iTemplateIndex = %d, hwndProgress = 0x%x )"),iPage,iTemplateIndex,hwndProgress));
  1885. HRESULT hr = S_OK;
  1886. RENDER_OPTIONS ro = {0};
  1887. //
  1888. // Check for bad params...
  1889. //
  1890. if ( (!hDC) ||
  1891. ((iTemplateIndex < 0) || (iTemplateIndex >= _templates.Count()))
  1892. )
  1893. {
  1894. WIA_RETURN_HR( E_INVALIDARG );
  1895. }
  1896. //
  1897. // Get the template in question...
  1898. //
  1899. CTemplateInfo * pTemplate = NULL;
  1900. hr = _templates.GetTemplate( iTemplateIndex, &pTemplate );
  1901. if (FAILED(hr))
  1902. {
  1903. WIA_RETURN_HR(hr);
  1904. }
  1905. if (!pTemplate)
  1906. {
  1907. WIA_RETURN_HR(E_OUTOFMEMORY);
  1908. }
  1909. UINT uFlagsOrientation = 0;
  1910. //
  1911. // Before we do anything, check to see if we're in Portrait or Landscape
  1912. // and rotate the template accordingly...
  1913. //
  1914. PDEVMODE pDevMode = GetDevModeToUse();
  1915. if (pDevMode)
  1916. {
  1917. if (pDevMode->dmFields & DM_ORIENTATION)
  1918. {
  1919. if (pDevMode->dmOrientation == DMORIENT_PORTRAIT)
  1920. {
  1921. pTemplate->RotateForPortrait();
  1922. }
  1923. else if (pDevMode->dmOrientation == DMORIENT_LANDSCAPE)
  1924. {
  1925. pTemplate->RotateForLandscape();
  1926. ro.Flags = RF_ROTATE_270;
  1927. }
  1928. }
  1929. }
  1930. //
  1931. // Is this a template that repeats photos?
  1932. //
  1933. BOOL bRepeat = FALSE;
  1934. pTemplate->GetRepeatPhotos( &bRepeat );
  1935. //
  1936. // Does this template want the filenames printed out under each photo?
  1937. //
  1938. BOOL bPrintFilename = FALSE;
  1939. pTemplate->GetPrintFilename( &bPrintFilename );
  1940. //
  1941. // Do we have any photos to print for this page?
  1942. //
  1943. INT iPhotosPerTemplate = pTemplate->PhotosPerPage();
  1944. INT iCountOfPhotos = CountOfSelectedPhotos(TRUE);
  1945. if ( (iPhotosPerTemplate == 0) ||
  1946. (iCountOfPhotos == 0) ||
  1947. ( (!bRepeat) && ((iCountOfPhotos - (iPage * iPhotosPerTemplate)) <= 0) )
  1948. )
  1949. {
  1950. WIA_RETURN_HR( S_FALSE );
  1951. }
  1952. //
  1953. // Get a handle to the printer we are going to use...
  1954. //
  1955. HANDLE hPrinter = NULL;
  1956. OpenPrinter( (LPTSTR)GetPrinterToUse(), &hPrinter, NULL );
  1957. //
  1958. // Compute the dimensions of the drawable area...
  1959. //
  1960. _SetupDimensionsForPrinting( hDC, pTemplate, &ro.Dim );
  1961. //
  1962. // Get index of photo to start with...
  1963. //
  1964. INT iPhoto;
  1965. if (!bRepeat)
  1966. {
  1967. iPhoto = iPage * iPhotosPerTemplate;
  1968. }
  1969. else
  1970. {
  1971. iPhoto = iPage;
  1972. }
  1973. //
  1974. // We always do scale to fit
  1975. //
  1976. ro.Flags |= RF_SCALE_TO_FIT;
  1977. //
  1978. // Get the control flags from the template...
  1979. //
  1980. BOOL bCanRotate = TRUE;
  1981. pTemplate->GetCanRotate( &bCanRotate );
  1982. if (bCanRotate)
  1983. {
  1984. ro.Flags |= RF_ROTATE_AS_NEEDED;
  1985. }
  1986. BOOL bCanCrop = TRUE;
  1987. pTemplate->GetCanCrop( &bCanCrop );
  1988. if (bCanCrop)
  1989. {
  1990. ro.Flags |= RF_CROP_TO_FIT;
  1991. }
  1992. BOOL bUseThumbnails = FALSE;
  1993. pTemplate->GetUseThumbnailsToPrint( &bUseThumbnails );
  1994. if (bUseThumbnails)
  1995. {
  1996. ro.Flags |= RF_USE_MEDIUM_QUALITY_DATA;
  1997. }
  1998. else
  1999. {
  2000. ro.Flags |= RF_USE_FULL_IMAGE_DATA;
  2001. }
  2002. //
  2003. // If we're in no UI mode, then don't fail if we can't rotate...
  2004. //
  2005. WIA_TRACE((TEXT("RenderPrintedPage: _bShowUI is 0x%x"),_bShowUI));
  2006. if (!_bShowUI)
  2007. {
  2008. ro.Flags |= RF_NO_ERRORS_ON_FAILURE_TO_ROTATE;
  2009. WIA_TRACE((TEXT("RenderPrintedPage: uFlags set to have RF_NO_ERRORS_ON_FAILURE (0x%x)"),ro.Flags));
  2010. }
  2011. //
  2012. // Compute offset to use...
  2013. //
  2014. INT xOffset = ro.Dim.NominalPageOffset.cx;
  2015. INT yOffset = ro.Dim.NominalPageOffset.cy;
  2016. //
  2017. // Set up GDI+ for printing...
  2018. //
  2019. Gdiplus::Graphics g( hDC, hPrinter );
  2020. hr = Gdiplus2HRESULT(g.GetLastStatus());
  2021. if (SUCCEEDED(hr))
  2022. {
  2023. //
  2024. // First, set up coordinates / transform
  2025. //
  2026. g.SetPageUnit( Gdiplus::UnitPixel );
  2027. hr = Gdiplus2HRESULT(g.GetLastStatus());
  2028. if (SUCCEEDED(hr))
  2029. {
  2030. g.SetPageScale( 1.0 );
  2031. hr = Gdiplus2HRESULT(g.GetLastStatus());
  2032. if (SUCCEEDED(hr))
  2033. {
  2034. //
  2035. // Set up transform so that we can draw in nominal
  2036. // template coordinates from here on out...
  2037. //
  2038. Gdiplus::Rect rectDevice( ro.Dim.rcDevice.left, ro.Dim.rcDevice.top, (ro.Dim.rcDevice.right - ro.Dim.rcDevice.left), (ro.Dim.rcDevice.bottom - ro.Dim.rcDevice.top) );
  2039. WIA_TRACE((TEXT("RenderPrintedPage: rectDevice is (%d, %d) with size (%d, %d)"),rectDevice.X,rectDevice.Y,rectDevice.Width,rectDevice.Height));
  2040. WIA_TRACE((TEXT("RenderPrintedPage: NominalDevicePrintArea is (%d, %d)"),ro.Dim.NominalDevicePrintArea.cx,ro.Dim.NominalDevicePrintArea.cy));
  2041. DOUBLE xScale = (DOUBLE)((DOUBLE)rectDevice.Width / (DOUBLE)ro.Dim.NominalDevicePrintArea.cx);
  2042. DOUBLE yScale = (DOUBLE)((DOUBLE)rectDevice.Height / (DOUBLE)ro.Dim.NominalDevicePrintArea.cy);
  2043. g.ScaleTransform( (Gdiplus::REAL) xScale, (Gdiplus::REAL) yScale );
  2044. hr = Gdiplus2HRESULT(g.GetLastStatus());
  2045. if (SUCCEEDED(hr))
  2046. {
  2047. #ifdef PRINT_BORDER_AROUND_PRINTABLE_AREA
  2048. Gdiplus::Rect rectPrintableArea( 0, 0, ro.Dim.NominalPrintArea.cx, ro.Dim.NominalPrintArea.cy );
  2049. Gdiplus::Color black(255,0,0,0);
  2050. Gdiplus::SolidBrush BlackBrush( black );
  2051. Gdiplus::Pen BlackPen( &BlackBrush, (Gdiplus::REAL)1.0 );
  2052. WIA_TRACE((TEXT("RenderPrintedPage: rectPrintableArea is (%d, %d) @ (%d, %d)"),rectPrintableArea.Width,rectPrintableArea.Height,rectPrintableArea.X,rectPrintableArea.Y));
  2053. g.DrawRectangle( &BlackPen, rectPrintableArea );
  2054. #endif
  2055. //
  2056. // Now loop through each image in the template, and draw it...
  2057. //
  2058. RECT rcNominal;
  2059. CListItem * pPhoto = NULL;;
  2060. INT iPhotoIndex = 0;
  2061. INT iPhotoIndexNext = 0;
  2062. //
  2063. // Get starting photo index...
  2064. //
  2065. for (INT i = iPhoto; i > 0; i--)
  2066. {
  2067. iPhotoIndex = GetIndexOfNextPrintableItem( iPhotoIndex );
  2068. iPhotoIndex++;
  2069. }
  2070. INT iRes = IDCONTINUE;
  2071. for (INT i = 0; (!IsWizardShuttingDown()) && (iRes == IDCONTINUE) && (i < iPhotosPerTemplate); i++)
  2072. {
  2073. if (SUCCEEDED(pTemplate->GetNominalRectForPhoto(i, &rcNominal)))
  2074. {
  2075. if ((-1 == rcNominal.left) &&
  2076. (-1 == rcNominal.right) &&
  2077. (-1 == rcNominal.top) &&
  2078. (-1 == rcNominal.bottom))
  2079. {
  2080. WIA_TRACE((TEXT("RenderPrintedPage: rcNominal is -1,-1,-1,-1 -- scaling to full page")));
  2081. rcNominal.left = 0;
  2082. rcNominal.top = 0;
  2083. rcNominal.right = ro.Dim.NominalDevicePrintArea.cx;
  2084. rcNominal.bottom = ro.Dim.NominalDevicePrintArea.cy;
  2085. WIA_TRACE((TEXT("RenderPrintedPage: rcNominal(%d) is ( %d, %d, %d, %d )"),i,rcNominal.left, rcNominal.top, rcNominal.right, rcNominal.bottom ));
  2086. }
  2087. else
  2088. {
  2089. WIA_TRACE((TEXT("RenderPrintedPage: rcNominal(%d) is ( %d, %d, %d, %d )"),i,rcNominal.left, rcNominal.top, rcNominal.right, rcNominal.bottom ));
  2090. rcNominal.left += xOffset;
  2091. rcNominal.right += xOffset;
  2092. rcNominal.top += yOffset;
  2093. rcNominal.bottom += yOffset;
  2094. }
  2095. //
  2096. // Get the photo object
  2097. //
  2098. if (!bRepeat)
  2099. {
  2100. iPhotoIndex = GetIndexOfNextPrintableItem( iPhotoIndex );
  2101. }
  2102. if ((!IsWizardShuttingDown()) && (iPhotoIndex != -1))
  2103. {
  2104. pPhoto = GetListItem( iPhotoIndex, TRUE );
  2105. if (pPhoto)
  2106. {
  2107. //
  2108. // Set up destination rectangle to draw into
  2109. //
  2110. Gdiplus::Rect dest( rcNominal.left, rcNominal.top, rcNominal.right - rcNominal.left, rcNominal.bottom - rcNominal.top );
  2111. WIA_TRACE((TEXT("RenderPrintedPage: rcPhotoDest(%d) is (%d x %d) a (%d, %d)"),i, dest.Width, dest.Height, dest.X, dest.Y ));
  2112. //
  2113. // supply the graphic objects to use...
  2114. //
  2115. ro.g = &g;
  2116. ro.pDest = &dest;
  2117. do
  2118. {
  2119. //
  2120. // This variable will be set to TRUE in status.cpp if the user cancels the
  2121. // print job.
  2122. //
  2123. extern BOOL g_bCancelPrintJob;
  2124. //
  2125. // Draw the image!
  2126. //
  2127. hr = pPhoto->Render( &ro );
  2128. //
  2129. // Check to see if we've been cancelled.
  2130. // If we have, we are going to break out
  2131. // before displaying any errors.
  2132. //
  2133. if (g_bCancelPrintJob)
  2134. {
  2135. iRes = IDCANCEL;
  2136. break;
  2137. }
  2138. if (FAILED(hr))
  2139. {
  2140. iRes = ShowError( NULL, hr, 0, TRUE, pPhoto->GetPIDL() );
  2141. if (iRes == IDCONTINUE)
  2142. {
  2143. hr = S_FALSE;
  2144. }
  2145. }
  2146. else
  2147. {
  2148. iRes = IDCONTINUE;
  2149. }
  2150. } while ( iRes == IDTRYAGAIN );
  2151. //
  2152. // Print the filename if warranted
  2153. //
  2154. if (bPrintFilename)
  2155. {
  2156. _RenderFilenameOfPhoto( &g, &rcNominal, pPhoto );
  2157. }
  2158. //
  2159. // Update the percentage complete if needed
  2160. //
  2161. if (pPercent)
  2162. {
  2163. *pPercent += (float)(fProgressStep / (float)iPhotosPerTemplate);
  2164. if (hwndProgress)
  2165. {
  2166. INT iPercent = (INT)(*pPercent);
  2167. WIA_TRACE((TEXT("RenderPrinterPage: iPercent = %d"),iPercent));
  2168. PostMessage( hwndProgress, PBM_SETPOS, (WPARAM)iPercent, 0 );
  2169. }
  2170. }
  2171. }
  2172. else
  2173. {
  2174. hr = E_OUTOFMEMORY;
  2175. break;
  2176. }
  2177. if (!bRepeat)
  2178. {
  2179. iPhotoIndex++;
  2180. }
  2181. }
  2182. }
  2183. }
  2184. }
  2185. }
  2186. }
  2187. }
  2188. else
  2189. {
  2190. WIA_ERROR((TEXT("RenderPrintedPage: couldn't create graphics, hr = 0x%x"),hr));
  2191. }
  2192. if (hPrinter)
  2193. {
  2194. ClosePrinter( hPrinter );
  2195. }
  2196. WIA_RETURN_HR(hr);
  2197. }
  2198. /*****************************************************************************
  2199. CWizardInfo::RenderPreview
  2200. Given a template index and an HWND, sizes the HWND to be aspect correct
  2201. for the chosen printer/paper, and then returns an HBITMAP of a preview
  2202. for this template that will fit in the window.
  2203. *****************************************************************************/
  2204. HBITMAP CWizardInfoBlob::RenderPreview( INT iTemplateIndex, HWND hwndScreen )
  2205. {
  2206. WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::RenderPreview( iTemplateIndex = %d )"),iTemplateIndex));
  2207. HBITMAP hbmp = NULL;
  2208. RENDER_OPTIONS ro = {0};
  2209. if ((iTemplateIndex < 0) || (iTemplateIndex > _templates.Count()))
  2210. {
  2211. return NULL;
  2212. }
  2213. //
  2214. // Get the correct template...
  2215. //
  2216. CTemplateInfo * pTemplate = NULL;
  2217. HRESULT hr = _templates.GetTemplate( iTemplateIndex, &pTemplate );
  2218. if (FAILED(hr) || (!pTemplate))
  2219. {
  2220. return NULL;
  2221. }
  2222. //
  2223. // Tell the render engine we're rendering to the screen
  2224. //
  2225. ro.Flags |= RF_SET_QUALITY_FOR_SCREEN;
  2226. //
  2227. // Before we do anything, check to see if we're in Portrait or Landscape
  2228. // and rotate the template accordingly...
  2229. //
  2230. PDEVMODE pDevMode = GetDevModeToUse();
  2231. if (pDevMode)
  2232. {
  2233. if (pDevMode->dmFields & DM_ORIENTATION)
  2234. {
  2235. if (pDevMode->dmOrientation == DMORIENT_PORTRAIT)
  2236. {
  2237. pTemplate->RotateForPortrait();
  2238. }
  2239. else if (pDevMode->dmOrientation == DMORIENT_LANDSCAPE)
  2240. {
  2241. pTemplate->RotateForLandscape();
  2242. ro.Flags |= RF_ROTATE_270;
  2243. }
  2244. }
  2245. }
  2246. //
  2247. // Do we have any photos to print for this page?
  2248. //
  2249. INT iPhotosPerTemplate = pTemplate->PhotosPerPage();
  2250. INT iCountOfPhotos = CountOfSelectedPhotos(TRUE);
  2251. //
  2252. // Compute the dimensions of the drawable area...
  2253. //
  2254. _SetupDimensionsForScreen( pTemplate, hwndScreen, &ro.Dim );
  2255. //
  2256. // Does this template want the filenames printed out under each photo?
  2257. //
  2258. BOOL bPrintFilename = FALSE;
  2259. pTemplate->GetPrintFilename( &bPrintFilename );
  2260. //
  2261. // Get the control flags from the template...
  2262. //
  2263. ro.Flags |= RF_SCALE_TO_FIT;
  2264. BOOL bCanRotate = TRUE;
  2265. pTemplate->GetCanRotate( &bCanRotate );
  2266. if (bCanRotate)
  2267. {
  2268. ro.Flags |= RF_ROTATE_AS_NEEDED;
  2269. }
  2270. BOOL bCanCrop = TRUE;
  2271. pTemplate->GetCanCrop( &bCanCrop );
  2272. if (bCanCrop)
  2273. {
  2274. ro.Flags |= RF_CROP_TO_FIT;
  2275. }
  2276. //
  2277. // Is this a template that repeats photos?
  2278. //
  2279. BOOL bRepeat = FALSE;
  2280. pTemplate->GetRepeatPhotos( &bRepeat );
  2281. //
  2282. // Does the template use thumbnail data for printing? Match that for display
  2283. //
  2284. BOOL bUseThumbnails = TRUE;
  2285. pTemplate->GetUseThumbnailsToPrint( &bUseThumbnails );
  2286. if (bUseThumbnails)
  2287. {
  2288. ro.Flags |= RF_USE_THUMBNAIL_DATA;
  2289. }
  2290. else
  2291. {
  2292. ro.Flags |= RF_USE_FULL_IMAGE_DATA;
  2293. }
  2294. //
  2295. // Compute offset to use...
  2296. //
  2297. INT xOffset = ro.Dim.NominalPageOffset.cx + ro.Dim.NominalPhysicalOffset.cx;
  2298. INT yOffset = ro.Dim.NominalPageOffset.cy + ro.Dim.NominalPhysicalOffset.cy;
  2299. WIA_TRACE((TEXT("RenderPreview: Offset is (%d, %d)"),xOffset,yOffset));
  2300. //
  2301. // Get clip rectangle...
  2302. //
  2303. Gdiplus::Rect clip( ro.Dim.rcNominalPageClip.left,
  2304. ro.Dim.rcNominalPageClip.top,
  2305. ro.Dim.rcNominalPageClip.right - ro.Dim.rcNominalPageClip.left,
  2306. ro.Dim.rcNominalPageClip.bottom - ro.Dim.rcNominalPageClip.top
  2307. );
  2308. //
  2309. // Get size of preview window
  2310. //
  2311. RECT rcWnd = {0};
  2312. GetClientRect( hwndScreen, &ro.Dim.rcDevice );
  2313. Gdiplus::Rect rectWindow( 0, 0, ro.Dim.rcDevice.right - ro.Dim.rcDevice.left, ro.Dim.rcDevice.bottom - ro.Dim.rcDevice.top );
  2314. ro.Dim.bDeviceIsScreen = TRUE;
  2315. //
  2316. // Need to create a new preview bitmap for this template...
  2317. //
  2318. BITMAPINFO BitmapInfo;
  2319. ZeroMemory( &BitmapInfo, sizeof(BITMAPINFO) );
  2320. BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  2321. BitmapInfo.bmiHeader.biWidth = rectWindow.Width;
  2322. BitmapInfo.bmiHeader.biHeight = rectWindow.Height;
  2323. BitmapInfo.bmiHeader.biPlanes = 1;
  2324. BitmapInfo.bmiHeader.biBitCount = 24;
  2325. BitmapInfo.bmiHeader.biCompression = BI_RGB;
  2326. //
  2327. // Create the DIB section
  2328. //
  2329. PBYTE pBitmapData = NULL;
  2330. HDC hdc = CreateCompatibleDC( NULL );
  2331. hbmp = CreateDIBSection( hdc, &BitmapInfo, DIB_RGB_COLORS, (LPVOID*)&pBitmapData, NULL, 0 );
  2332. if (hdc && hbmp)
  2333. {
  2334. //
  2335. // Select the DIB section into the DC
  2336. //
  2337. SelectObject( hdc, hbmp );
  2338. //
  2339. // Create Graphics object around memory DC
  2340. //
  2341. Gdiplus::Graphics g( hdc );
  2342. if (Gdiplus::Ok == g.GetLastStatus())
  2343. {
  2344. //
  2345. // First, draw bounding rectangle
  2346. //
  2347. g.SetPageUnit( Gdiplus::UnitPixel );
  2348. g.SetPageScale( 1.0 );
  2349. Gdiplus::Color white(255,255,255,255);
  2350. Gdiplus::SolidBrush WhiteBrush( white );
  2351. //
  2352. // Clear out the contents
  2353. //
  2354. g.FillRectangle( &WhiteBrush, rectWindow );
  2355. //
  2356. // Frame the outside
  2357. //
  2358. Gdiplus::Color OutsideColor(255,64,64,64);
  2359. Gdiplus::Pen OutsidePen( OutsideColor );
  2360. rectWindow.Width--;
  2361. rectWindow.Height--;
  2362. g.DrawRectangle( &OutsidePen, rectWindow );
  2363. //
  2364. // Set up transform so that we can draw in nominal
  2365. // template coordinates from here on out...
  2366. //
  2367. g.ScaleTransform( (Gdiplus::REAL)((DOUBLE)rectWindow.Width / (DOUBLE)ro.Dim.NominalPhysicalSize.cx), (Gdiplus::REAL)((DOUBLE)rectWindow.Height / (DOUBLE)ro.Dim.NominalPhysicalSize.cy) );
  2368. //
  2369. // Set clip rectangle...
  2370. //
  2371. //WIA_TRACE((TEXT("RenderPreview: setting clip rect to (%d, %d) with size (%d, %d)"),clip.X,clip.Y,clip.Width,clip.Height));
  2372. //g.SetClip( clip, Gdiplus::CombineModeReplace );
  2373. //
  2374. // Now loop through each image in the template, and draw it...
  2375. //
  2376. RECT rcNominal;
  2377. INT iPhotoIndex = 0;
  2378. CListItem * pPhoto = NULL;
  2379. if (bRepeat)
  2380. {
  2381. iPhotoIndex = GetIndexOfNextPrintableItem( 0 );
  2382. }
  2383. INT iRes = IDCONTINUE;
  2384. hr = S_OK;
  2385. for (INT i = 0; (iRes == IDCONTINUE) && (!IsWizardShuttingDown()) && (i < iPhotosPerTemplate); i++)
  2386. {
  2387. if (SUCCEEDED(pTemplate->GetNominalRectForPhoto(i, &rcNominal)))
  2388. {
  2389. if ((-1 == rcNominal.left) &&
  2390. (-1 == rcNominal.right) &&
  2391. (-1 == rcNominal.top) &&
  2392. (-1 == rcNominal.bottom))
  2393. {
  2394. WIA_TRACE((TEXT("RenderPreview: rcNominal is -1,-1,-1,-1 -- scaling to full page")));
  2395. rcNominal = ro.Dim.rcNominalPageClip;
  2396. WIA_TRACE((TEXT("RenderPreview: rcNominal(%d) is ( %d, %d, %d, %d )"),i,rcNominal.left, rcNominal.top, rcNominal.right, rcNominal.bottom ));
  2397. }
  2398. else
  2399. {
  2400. WIA_TRACE((TEXT("RenderPreview: rcNominal(%d) is ( %d, %d, %d, %d )"),i,rcNominal.left, rcNominal.top, rcNominal.right, rcNominal.bottom ));
  2401. rcNominal.left += xOffset;
  2402. rcNominal.right += xOffset;
  2403. rcNominal.top += yOffset;
  2404. rcNominal.bottom += yOffset;
  2405. }
  2406. //
  2407. // Get the photo object
  2408. //
  2409. if (!bRepeat)
  2410. {
  2411. iPhotoIndex = GetIndexOfNextPrintableItem( iPhotoIndex );
  2412. }
  2413. if ((!IsWizardShuttingDown()) && (iPhotoIndex != -1))
  2414. {
  2415. pPhoto = GetListItem( iPhotoIndex, TRUE );
  2416. if (pPhoto)
  2417. {
  2418. //
  2419. // Set up the destination rectangle to draw into
  2420. //
  2421. Gdiplus::Rect dest( rcNominal.left, rcNominal.top, rcNominal.right - rcNominal.left, rcNominal.bottom - rcNominal.top );
  2422. WIA_TRACE((TEXT("RenderPreview: rcPhoto(%d) is (%d x %d) at (%d, %d)"),i, dest.Width, dest.Height, dest.X, dest.Y ));
  2423. //
  2424. // Supply the GDI/GDI+ objects to use...
  2425. //
  2426. ro.g = &g;
  2427. ro.pDest = &dest;
  2428. //
  2429. // Save the flags before trying to do throttling...
  2430. //
  2431. ULONG uFlagsSave = ro.Flags;
  2432. //
  2433. // throttle back to thumbnails if we're on a low-end system
  2434. // and it's a large file...
  2435. //
  2436. if (_bMinimumMemorySystem)
  2437. {
  2438. //
  2439. // We're on a low memory system...is this a
  2440. // large file? We say anything over 1MB
  2441. // is large.
  2442. //
  2443. if (pPhoto->GetFileSize() > (LONGLONG)LARGE_IMAGE_SIZE)
  2444. {
  2445. WIA_TRACE((TEXT("RenderPreview: throttling back to thumbnail data because not enough memory!")));
  2446. //
  2447. // Clear out old render quality flags
  2448. //
  2449. ro.Flags &= (~RF_QUALITY_FLAGS_MASK);
  2450. ro.Flags |= RF_USE_THUMBNAIL_DATA;
  2451. }
  2452. }
  2453. //
  2454. // Is this a really large file? We say anything
  2455. // greater than 5MB is really large
  2456. //
  2457. if (pPhoto->GetFileSize() > (LONGLONG)REALLY_LARGE_IMAGE_SIZE)
  2458. {
  2459. //
  2460. // Unless we have a really large memory
  2461. // system, then throttle back on this file
  2462. // and only show the thumbnail
  2463. //
  2464. if (!_bLargeMemorySystem)
  2465. {
  2466. WIA_TRACE((TEXT("RenderPreview: throttling back to thumbnail data because of really large file!")));
  2467. //
  2468. // Clear out old render quality flags
  2469. //
  2470. ro.Flags &= (~RF_QUALITY_FLAGS_MASK);
  2471. ro.Flags |= RF_USE_THUMBNAIL_DATA;
  2472. }
  2473. }
  2474. //
  2475. // Now that we have everything set up, try to draw the image...
  2476. //
  2477. do
  2478. {
  2479. //
  2480. // Draw the image!
  2481. //
  2482. hr = pPhoto->Render( &ro );
  2483. if (FAILED(hr))
  2484. {
  2485. iRes = ShowError( NULL, hr, 0, TRUE, pPhoto->GetPIDL() );
  2486. hr = S_FALSE;
  2487. }
  2488. else
  2489. {
  2490. iRes = IDCONTINUE;
  2491. }
  2492. } while ( iRes == IDTRYAGAIN );
  2493. //
  2494. // Restore flags...
  2495. //
  2496. ro.Flags = uFlagsSave;
  2497. //
  2498. // Print the filename if warranted
  2499. //
  2500. if (bPrintFilename)
  2501. {
  2502. _RenderFilenameOfPhoto( &g, &rcNominal, pPhoto );
  2503. }
  2504. }
  2505. else
  2506. {
  2507. hr = E_FAIL;
  2508. break;
  2509. }
  2510. if (!bRepeat)
  2511. {
  2512. iPhotoIndex++;
  2513. }
  2514. }
  2515. }
  2516. }
  2517. //
  2518. // Last -- draw a dashed rectangle that represents
  2519. // the printable area on the bitmap if the template
  2520. // won't fit.
  2521. //
  2522. if ((ro.Dim.NominalPageOffset.cx < 0) || (ro.Dim.NominalPageOffset.cy < 0))
  2523. {
  2524. //Gdiplus::Pen DashedPen( black, (Gdiplus::REAL)1.0 );
  2525. //DashedPen.SetDashStyle( Gdiplus::DashStyleDash );
  2526. Gdiplus::Color InsideColor(255,180,180,180);
  2527. Gdiplus::Pen InsidePen( InsideColor );
  2528. g.DrawRectangle( &InsidePen, clip );
  2529. }
  2530. }
  2531. else
  2532. {
  2533. WIA_ERROR((TEXT("RenderPreview: couldn't get a Graphics from the bmp, Status = %d"),g.GetLastStatus()));
  2534. }
  2535. }
  2536. else
  2537. {
  2538. if (hbmp)
  2539. {
  2540. DeleteObject(hbmp);
  2541. hbmp = NULL;
  2542. }
  2543. WIA_ERROR((TEXT("RenderPreview: couldn't create DIB section")));
  2544. }
  2545. if (hdc)
  2546. {
  2547. DeleteDC( hdc );
  2548. }
  2549. return hbmp;
  2550. }