/***************************************************************************** * * (C) COPYRIGHT MICROSOFT CORPORATION, 2000 * * TITLE: wizblob.cpp * * VERSION: 1.0 * * AUTHOR: RickTu * * DATE: 10/18/00 * * DESCRIPTION: Class which encapsulates the data which must be passed * around from page to page in the print photos wizard... * *****************************************************************************/ #include "precomp.h" #pragma hdrstop #include "gphelper.h" static const int c_nDefaultThumbnailWidth = DEFAULT_THUMB_WIDTH; static const int c_nDefaultThumbnailHeight = DEFAULT_THUMB_HEIGHT; static const int c_nMaxThumbnailWidth = 120; static const int c_nMaxThumbnailHeight = 120; static const int c_nMinThumbnailWidth = 80; static const int c_nMinThumbnailHeight = 80; static const int c_nDefaultTemplatePreviewWidth = 48; static const int c_nDefaultTemplatePreviewHeight = 62; Gdiplus::Color g_wndColor; /***************************************************************************** Callback class for namespace walking code... *****************************************************************************/ class CWalkCallback : public INamespaceWalkCB { public: CWalkCallback(); ~CWalkCallback(); // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj); STDMETHOD_(ULONG,AddRef)(void); STDMETHOD_(ULONG,Release)(void); // INamespaceWalkCB STDMETHOD(FoundItem)(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHOD(EnterFolder)(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHOD(LeaveFolder)(IShellFolder *psf, LPCITEMIDLIST pidl); STDMETHOD(InitializeProgressDialog)(LPWSTR * ppszTitle, LPWSTR * ppszCancel); BOOL WereItemsRejected() {return _bItemsWereRejected;} private: LONG _cRef; BOOL _bItemsWereRejected; CImageFileFormatVerifier _Verify; }; CWalkCallback::CWalkCallback() : _cRef(1), _bItemsWereRejected(FALSE) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::CWalkCallback( this == 0x%x )"),this)); DllAddRef(); } CWalkCallback::~CWalkCallback() { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::~CWalkCallback( this == 0x%x )"),this)); DllRelease(); } ULONG CWalkCallback::AddRef() { ULONG ul = InterlockedIncrement(&_cRef); WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CWalkCallback::AddRef( new count is %d )"), ul)); return ul; } ULONG CWalkCallback::Release() { ULONG ul = InterlockedDecrement(&_cRef); WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS,TEXT("CWalkCallback::Release( new count is %d )"), ul)); if (ul) return ul; WIA_TRACE((TEXT("deleting CWalkCallback( this == 0x%x ) object"),this)); delete this; return 0; } HRESULT CWalkCallback::QueryInterface(REFIID riid, void **ppv) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::QueryInterface()"))); static const QITAB qit[] = { QITABENT(CWalkCallback, INamespaceWalkCB), {0, 0 }, }; HRESULT hr = QISearch(this, qit, riid, ppv); WIA_RETURN_HR(hr); } STDAPI DisplayNameOf(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD flags, LPTSTR psz, UINT cch) { *psz = 0; STRRET sr; HRESULT hr = psf->GetDisplayNameOf(pidl, flags, &sr); if (SUCCEEDED(hr)) hr = StrRetToBuf(&sr, pidl, psz, cch); return hr; } HRESULT CWalkCallback::FoundItem( IShellFolder * psf, LPCITEMIDLIST pidl ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::FoundItem()"))); HRESULT hrRet = S_FALSE; if (psf && pidl) { WIA_TRACE((TEXT("FoundItem: psf & pidl are valid, binding to stream to check signature..."))); IStream * pStream = NULL; HRESULT hr = psf->BindToObject( pidl, NULL, IID_IStream, (void **)&pStream ); if (SUCCEEDED(hr) && pStream) { GUID guidType; BOOL bSupported = FALSE; WIA_TRACE((TEXT("FoundItem: checking for GDI+ decoder..."))); bSupported = _Verify.IsSupportedImageFromStream(pStream, &guidType); // // We don't let EMF, WMF or .ico into the wizard // if (bSupported && ((guidType != Gdiplus::ImageFormatWMF) && (guidType != Gdiplus::ImageFormatEMF) && (guidType != Gdiplus::ImageFormatIcon))) { WIA_TRACE((TEXT("FoundItem: GDI+ encoder found"))); hrRet = S_OK; } else { _bItemsWereRejected = TRUE; } } if (pStream) { pStream->Release(); } } if (hrRet != S_OK) { _bItemsWereRejected = TRUE; } WIA_RETURN_HR(hrRet); } HRESULT CWalkCallback::EnterFolder( IShellFolder * psf, LPCITEMIDLIST pidl ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::EnterFolder()"))); return S_OK; } HRESULT CWalkCallback::LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::LeaveFolder()"))); return S_OK; } HRESULT CWalkCallback::InitializeProgressDialog(LPWSTR * ppszTitle, LPWSTR * ppszCancel) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("CWalkCallback::InitializeProgressDialog()"))); // // If we want to use the progress dialog, we need to specify // when we create the namespace walk object NSWF_SHOW_PROGRESS // and use CoTaskMemAlloc to create the strings... // if (ppszTitle) { *ppszTitle = NULL; } if (ppszCancel) { *ppszCancel = NULL; } return E_FAIL; } /***************************************************************************** MyItemDpaDestroyCallback Gets called as the HDPA is destroyed so that we can delete the objects stored in the DPA *****************************************************************************/ INT MyItemDpaDestroyCallback( LPVOID pItem, LPVOID lpData ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB,TEXT("MyItemDpaDestroyCallback( 0x%x, 0x%x )"),pItem,lpData)); if (pItem) { delete (CListItem *)pItem; } return TRUE; } /***************************************************************************** CWizardInfoBlob -- constructor/desctructor *****************************************************************************/ CWizardInfoBlob::CWizardInfoBlob( IDataObject * pdo, BOOL bShowUI, BOOL bOnlyUseSelection ) : _cRef(0), _hdpaItems(NULL), _nDefaultThumbnailImageListIndex(0), _bGdiplusInitialized(FALSE), _bAllPicturesAdded(FALSE), _bItemsWereRejected(FALSE), _pGdiplusToken(NULL), _pPreview(NULL), _pStatusPage(NULL), _pPhotoSelPage(NULL), _hDevMode(NULL), _hfontIntro(NULL), _iCurTemplate(-1), _bPreviewsAreDirty(TRUE), _bRepeat(FALSE), _hSmallIcon(NULL), _hLargeIcon(NULL), _uItemsInInitialSelection(0), _bAlreadyAddedPhotos(FALSE), _bWizardIsShuttingDown((LONG)FALSE), _hPhotoSelIsDone(NULL), _hStatusIsDone(NULL), _hPreviewIsDone(NULL), _hwndPreview(NULL), _hwndStatus(NULL), _hGdiPlusThread(NULL), _dwGdiPlusThreadId(0), _hGdiPlusMsgQueueCreated(NULL), _hCachedPrinterDC(NULL), _hOuterDlg(NULL), _bShowUI(bShowUI), _bOnlyUseSelection(bOnlyUseSelection), _iNumErrorsWhileRunningWizard(0), _iSelectedItem(0), _iCopiesOfEachItem(1), _bMinimumMemorySystem(FALSE), _bLargeMemorySystem(FALSE), _bForceSelectAll(FALSE) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::CWizardInfoBlob()") )); // // Init the printer info stuff.. // ZeroMemory( &_WizPrinterInfo, sizeof(_WizPrinterInfo) ); // // Copy params and init // _pdo = pdo; _sizeThumbnails.cx = c_nDefaultThumbnailWidth; _sizeThumbnails.cy = c_nDefaultThumbnailHeight; _sizeTemplatePreview.cx = c_nDefaultTemplatePreviewWidth; _sizeTemplatePreview.cy = c_nDefaultTemplatePreviewHeight; _rcInitSize.left = 0; _rcInitSize.right = 0; _rcInitSize.bottom = 0; _rcInitSize.top = 0; // // This is disgusting -- but GDI+ needs to be initialized and shut down // ON THE SAME THREAD. So, we have to create a thread just for this and have // it sit around so that we're garaunteed this is the thread we'll do // startup/shutdown on. // _hGdiPlusMsgQueueCreated = CreateEvent( NULL, FALSE, FALSE, NULL ); WIA_TRACE((TEXT("creating s_GdiPlusStartupShutdownThreadProc..."))); _hGdiPlusThread = CreateThread( NULL, 0, s_GdiPlusStartupShutdownThreadProc, (LPVOID)this, 0, &_dwGdiPlusThreadId ); if (_hGdiPlusMsgQueueCreated) { if (_hGdiPlusThread && _dwGdiPlusThreadId) { WIA_TRACE((TEXT("waiting for message queue to be created in s_GdiPlusStartupShutdownThreadProc..."))); WiaUiUtil::MsgWaitForSingleObject( _hGdiPlusMsgQueueCreated, INFINITE ); WIA_TRACE((TEXT("GdiPlusStartupShutdown thread initialized correctly."))); } else { WIA_ERROR((TEXT("_hGdiPlusThread = 0x%x, _dwGdiPlusThreadId = 0x%x"),_hGdiPlusThread,_dwGdiPlusThreadId)); } CloseHandle( _hGdiPlusMsgQueueCreated ); _hGdiPlusMsgQueueCreated = NULL; } else { WIA_ERROR((TEXT("Couldn't create _hGdiPlusMsgQueueCreated!"))); } // // Make sure GDI+ is initialized // WIA_TRACE((TEXT("posting WIZ_MSG_STARTUP_GDI_PLUS to s_GdiPlusStartupShutdownThreadProc..."))); HANDLE hGdiPlusInitialized = CreateEvent( NULL, FALSE, FALSE, NULL ); PostThreadMessage( _dwGdiPlusThreadId, WIZ_MSG_STARTUP_GDI_PLUS, 0, (LPARAM)hGdiPlusInitialized ); if (hGdiPlusInitialized) { WIA_TRACE((TEXT("waiting for GDI+ startup to finish..."))); WiaUiUtil::MsgWaitForSingleObject( hGdiPlusInitialized, INFINITE ); CloseHandle( hGdiPlusInitialized ); WIA_TRACE((TEXT("GDI+ startup completed!"))); } // // Set up window color for use later... // DWORD dw = GetSysColor( COLOR_WINDOW ); Gdiplus::ARGB argb = Gdiplus::Color::MakeARGB( 255, GetRValue(dw), GetGValue(dw), GetBValue(dw) ); g_wndColor.SetValue( argb ); // // Get perf info (like how much memory we have)... // NTSTATUS NtStatus; SYSTEM_BASIC_INFORMATION BasicInfo; NtStatus = NtQuerySystemInformation( SystemBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL ); if (NT_SUCCESS(NtStatus)) { DWORD dwMemSizeInK = BasicInfo.NumberOfPhysicalPages * (BasicInfo.PageSize / 1024); if (dwMemSizeInK < MINIMUM_MEMORY_SIZE) { WIA_TRACE((TEXT("we are running on a minimum memory system!"))); _bMinimumMemorySystem = TRUE; } if (dwMemSizeInK > LARGE_MINIMUM_MEMORY_SIZE) { WIA_TRACE((TEXT("we are running on a minimum memory system!"))); _bLargeMemorySystem = TRUE; } } // // If we're in UI mode, show the UI... // if (bShowUI) { // // Load icons for wizard... // _hSmallIcon = reinterpret_cast(LoadImage( g_hInst, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR )); _hLargeIcon = reinterpret_cast(LoadImage( g_hInst, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR )); // // Create events for background tasks to signal when they're done... // _hPhotoSelIsDone = CreateEvent( NULL, FALSE, FALSE, NULL ); _hStatusIsDone = CreateEvent( NULL, FALSE, FALSE, NULL ); _hPreviewIsDone = CreateEvent( NULL, FALSE, FALSE, NULL ); // // Initialize template info from XML... // CComPtr spXMLDoc; if (SUCCEEDED(LoadXMLDOMDoc(TEXT("res://photowiz.dll/tmpldata.xml"), &spXMLDoc))) { _templates.Init(spXMLDoc); } } } CWizardInfoBlob::~CWizardInfoBlob() { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::~CWizardInfoBlob()") )); // // free the memory // if (_hDevMode) { delete [] _hDevMode; _hDevMode = NULL; } _csItems.Enter(); if (_hdpaItems) { DPA_DestroyCallback( _hdpaItems, MyItemDpaDestroyCallback, NULL ); _hdpaItems = NULL; } _csItems.Leave(); if (_hfontIntro) { DeleteObject( (HGDIOBJ)_hfontIntro ); _hfontIntro = NULL; } if (_hCachedPrinterDC) { DeleteDC( _hCachedPrinterDC ); } // // Destroy icons for wizard // if (_hSmallIcon) { DestroyIcon( _hSmallIcon ); _hSmallIcon = NULL; } if (_hLargeIcon) { DestroyIcon( _hLargeIcon ); _hLargeIcon = NULL; } // // Close event handles... // if (_hPhotoSelIsDone) { CloseHandle( _hPhotoSelIsDone ); _hPhotoSelIsDone = NULL; } if (_hStatusIsDone) { CloseHandle( _hStatusIsDone ); _hStatusIsDone = NULL; } if (_hPreviewIsDone) { CloseHandle( _hPreviewIsDone ); _hPreviewIsDone = NULL; } WIA_TRACE((TEXT("Attempting to shut down GDI+"))); if (_hGdiPlusThread && _dwGdiPlusThreadId) { // // Lastly, shut down GDI+ // WIA_TRACE((TEXT("Sending WIZ_MSG_SHUTDOWN_GDI_PLUS to s_GdiPlusStartupShutdownThreadProc"))); PostThreadMessage( _dwGdiPlusThreadId, WIZ_MSG_SHUTDOWN_GDI_PLUS, 0, 0 ); WIA_TRACE((TEXT("Sending WM_QUIT to s_GdiPlusStartupShutdownThreadProc"))); PostThreadMessage( _dwGdiPlusThreadId, WM_QUIT, 0, 0 ); // // Wait for thread to exit... // WIA_TRACE((TEXT("Waiting for s_GdiPlusStartupShutdownThreadProc to exit..."))); WiaUiUtil::MsgWaitForSingleObject( _hGdiPlusThread, INFINITE ); CloseHandle( _hGdiPlusThread ); _hGdiPlusThread = NULL; _dwGdiPlusThreadId = 0; WIA_TRACE((TEXT("s_GdiPlusStartupShutdownThreadProc successfully shut down..."))); } // // Keep this here (but commented out) for when I need to build // instrumented versions for test // //MessageBox( NULL, TEXT("Photowiz is now shut down."), TEXT("Photowiz"), MB_OK ); } /***************************************************************************** CWizardInfoBlob::_DoHandleThreadMessage The is the subroutine that actually does the work for s_GdiPlusStartupShutdownThreadProc. The whole reason for this thread in the first place is that GDI+ demands to be initialized and shutdown on the SAME THREAD. *****************************************************************************/ VOID CWizardInfoBlob::_DoHandleThreadMessage( LPMSG pMsg ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::_DoHandleThreadMessage()") )); if (!pMsg) { WIA_ERROR((TEXT("pMSG is NULL, returning early!"))); return; } switch (pMsg->message) { case WIZ_MSG_STARTUP_GDI_PLUS: WIA_TRACE((TEXT("Got WIZ_MSG_STARTUP_GDI_PLUS message"))); { Gdiplus::GdiplusStartupInput StartupInput; _bGdiplusInitialized = (Gdiplus::GdiplusStartup(&_pGdiplusToken,&StartupInput,NULL) == Gdiplus::Ok); // // Signal that we've attempted to initialize GDI+ // if (pMsg->lParam) { SetEvent( (HANDLE)pMsg->lParam ); } } break; case WIZ_MSG_SHUTDOWN_GDI_PLUS: WIA_TRACE((TEXT("Got WIZ_MSG_SHUTDOWN_GDI_PLUS message"))); if (_bGdiplusInitialized) { Gdiplus::GdiplusShutdown(_pGdiplusToken); _bGdiplusInitialized = FALSE; _pGdiplusToken = NULL; } break; case WIZ_MSG_COPIES_CHANGED: WIA_TRACE((TEXT("Got WIZ_MSG_COPIES_CHANGED( %d ) message"),pMsg->wParam)); { if (_iCopiesOfEachItem != pMsg->wParam) { // // Stop all the preview generation background threads... // if (_pPreview) { _pPreview->StallBackgroundThreads(); // // Now change number of copies of each image... // RemoveAllCopiesOfPhotos(); AddCopiesOfPhotos( (UINT)pMsg->wParam ); // // Let the preview threads get going again... // _pPreview->RestartBackgroundThreads(); } _iCopiesOfEachItem = (INT)pMsg->wParam; } } break; } } /***************************************************************************** CWizardInfoBlob::ShutDownWizard Called to close all the background thread in the wizard. *****************************************************************************/ VOID CWizardInfoBlob::ShutDownWizard() { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::ShutDownWizard()") )); // // Get the current value of _bWizardIsShuttingDown and set to TRUE // in an atomic way. // BOOL bPreviousValue = (BOOL)InterlockedExchange( &_bWizardIsShuttingDown, (LONG)TRUE ); // // If we weren't already shutting down, then go ahead and shut down the wizard // if (!bPreviousValue) { // // This method will attempt to shut down the wizard in an orderly fashion. // To do that, all we really need to do at this point is shut down // all the background threads so that they don't do any callbacks // or callouts after this point. // // // Tell the photo selection page to shut down... // if (_pPhotoSelPage) { _pPhotoSelPage->ShutDownBackgroundThreads(); } // // Tell the preview window to shut down... // if (_pPreview) { _pPreview->ShutDownBackgroundThreads(); } // // Tell the status window we're sutting down... // if (_pStatusPage) { _pStatusPage->ShutDownBackgroundThreads(); } // // Now, wait for all the background threads to complete... // INT i = 0; HANDLE ah[ 3 ]; if (_hPhotoSelIsDone) { ah[i++] = _hPhotoSelIsDone; } if (_hStatusIsDone) { ah[i++] = _hStatusIsDone; } if (_hPreviewIsDone) { ah[i++] = _hPreviewIsDone; } WaitForMultipleObjects( i, ah, TRUE, INFINITE ); } } /***************************************************************************** CWizardInfoBlob::UserPressedCancel Called whenever the user presses the cancel button to close the wizard. Returns the correct result as to whether the wizard should exit. *****************************************************************************/ LRESULT CWizardInfoBlob::UserPressedCancel() { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::UserPressedCancel()") )); ShutDownWizard(); return FALSE; // allow wizard to exit } /***************************************************************************** CWizardInfoBlob -- AddRef/Release *****************************************************************************/ VOID CWizardInfoBlob::AddRef() { LONG l = InterlockedIncrement( &_cRef ); WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS, TEXT("CWizardInfoBlob::AddRef( new _cRef is %d )"),l )); } VOID CWizardInfoBlob::Release() { LONG l = InterlockedDecrement( &_cRef ); WIA_PUSH_FUNCTION_MASK((TRACE_REF_COUNTS, TEXT("CWizardInfoBlob::Release( new _cRef is %d )"),l )); if (l > 0) { return; } WIA_TRACE((TEXT("Deleting CWizardInfoBlob object..."))); delete this; } /***************************************************************************** CWizardInfoBlob::ShowError Unified error reporting... *****************************************************************************/ INT CWizardInfoBlob::ShowError( HWND hwnd, HRESULT hr, UINT idText, BOOL bAskTryAgain, LPITEMIDLIST pidl ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::ShowError( hr=0x%x, Id=%d )"),hr,idText)); CSimpleString strTitle( IDS_ERROR_TITLE, g_hInst ); CSimpleString strFormat; CSimpleString strError( TEXT("") ); CSimpleString strMessage( TEXT("") ); CSimpleString strFilename; // // Record that an error has occurred... // _iNumErrorsWhileRunningWizard++; // // Get an hwnd if not specified // if (!hwnd) { hwnd = _hOuterDlg; } // // Formulate information string // if (idText) { // // We were given a specific message string to display // strFormat.LoadString( idText, g_hInst ); } else { // // Wants generic error working with file... // strFilename.LoadString( IDS_UNKNOWN_FILE, g_hInst ); strFormat.LoadString( IDS_ERROR_WITH_FILE, g_hInst ); } UINT idErrorText = 0; // // map certain hr values to strings we have... // switch (hr) { case E_OUTOFMEMORY: idErrorText = IDS_ERROR_NOMEMORY; break; case PPW_E_UNABLE_TO_ROTATE: idErrorText = IDS_ERROR_ROTATION; break; case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): idErrorText = IDS_ERROR_FILENOTFOUND; break; case E_ACCESSDENIED: idErrorText = IDS_ERROR_ACCESSDENIED; break; case HRESULT_FROM_WIN32(ERROR_INVALID_PIXEL_FORMAT): idErrorText = IDS_ERROR_UNKNOWNFORMAT; break; case HRESULT_FROM_WIN32(ERROR_NOT_READY): case HRESULT_FROM_WIN32(ERROR_WRONG_DISK): idErrorText = IDS_ERROR_WRONG_DISK; break; case E_FAIL: case E_NOTIMPL: case E_ABORT: case HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER): case E_INVALIDARG: idErrorText = IDS_ERROR_GENERIC; break; } if (idErrorText) { strError.LoadString( idErrorText, g_hInst ); } else { // // construct basic error string given the hr // LPTSTR pszMsgBuf = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pszMsgBuf, 0, NULL ); if (pszMsgBuf) { strError.Assign( pszMsgBuf ); LocalFree(pszMsgBuf); } } if (pidl) { // // Get the filename for this file (if passed in) // CComPtr psfParent; LPCITEMIDLIST pidlLast; hr = SHBindToParent( pidl, IID_PPV_ARG(IShellFolder, &psfParent), &pidlLast ); if (SUCCEEDED(hr) && psfParent) { TCHAR szName[ MAX_PATH ]; *szName = 0; if SUCCEEDED(DisplayNameOf( psfParent, pidlLast, SHGDN_INFOLDER, szName, MAX_PATH )) { strFilename.Assign(szName); } } } // // We have all the pieces, now format the message // if (strFilename.Length()) { // // no message string was specified, so we're expected to put the file name // in the message... // strMessage.Format( strFormat, strFilename.String(), strError.String() ); } else { // // We were given a particular error string, so no file name displayed... // strMessage.Format( strFormat, strError.String() ); } UINT uFlags = bAskTryAgain ? (MB_CANCELTRYCONTINUE | MB_DEFBUTTON1) : MB_OK; return MessageBox( hwnd, strMessage, strTitle, uFlags | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND ); } /***************************************************************************** CWizardInfoBlob::RemoveAllCopiesOfPhotos Traverses the photo list and removes all copies *****************************************************************************/ VOID CWizardInfoBlob::RemoveAllCopiesOfPhotos() { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos()"))); CAutoCriticalSection lock(_csItems); // // The easiest way to do this is just to create a new DPA with only // the root (non copies) items in it... // if (_hdpaItems) { HDPA hdpaNew = DPA_Create(DEFAULT_DPA_SIZE); if (hdpaNew) { INT iCount = DPA_GetPtrCount( _hdpaItems ); CListItem * pListItem = NULL; for (INT i=0; iIsCopyItem()) { WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - removing copy 0x%x"),pListItem)); delete pListItem; } else { // // Add this page to the item list... // INT iRes = DPA_AppendPtr( hdpaNew, (LPVOID)pListItem ); if (iRes == -1) { WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - Tried to add 0x%x to new HDPA, but got back -1, deleting..."),pListItem)); delete pListItem; } else { WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - adding 0x%x to new HDPA"),pListItem)); } } } } // // We've removed all the items that are core items, now // delete old list and keep new one. This is safe because // we already deleted any items that weren't moved over // from the old list -- so either an item has been deleted // or it's in the new list and will be deleted when that // is cleaned up... // WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - destroying old list..."))); DPA_Destroy( _hdpaItems ); WIA_TRACE((TEXT("CWizardInfoBlob::RemoveAllCopiesOfPhotos - setting _hdpaItems to new list..."))); _hdpaItems = hdpaNew; } } } /***************************************************************************** CWizardInfoBlob::AddCopiesOfPhotos Adds the specified number of copies of each photo *****************************************************************************/ VOID CWizardInfoBlob::AddCopiesOfPhotos( UINT uCopies ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::AddCopiesOfPhotos( %d )"),uCopies)); CAutoCriticalSection lock(_csItems); // // This function assumes that it is getting a pure list -- i.e., only // root items and no copies. This is accomplished by calling RemoveAllCopiesOfPhotos // before calling this routine... // // // Loop through all the items, and add the requested number of copies... // if (_hdpaItems) { INT iCount = DPA_GetPtrCount( _hdpaItems ); HDPA hdpaNew = DPA_Create(DEFAULT_DPA_SIZE); if (hdpaNew) { CListItem * pListItem = NULL; CListItem * pListItemCopy = NULL; for (INT i=0; iGetSubItem(), pListItem->GetSubFrame() ); if (pListItemCopy) { // // Mark this new entry as a copy so it can be deleted as necessary... // pListItemCopy->MarkAsCopy(); // // Maintain the selection state // pListItemCopy->SetSelectionState( pListItem->SelectedForPrinting() ); // // Add the new item to the list... // iRes = DPA_AppendPtr( hdpaNew, (LPVOID)pListItemCopy ); if (iRes == -1) { WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- error adding copy %d of 0x%x to new list, deleting..."),uCopy,pListItem)); delete pListItemCopy; } else { WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- copy %d of 0x%x added to new list"),uCopy,pListItem)); } } else { WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- couldn't allocated copy %d of 0x%x"),uCopy,pListItem)); } } } else { WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- error adding root item 0x%x added to new list, deleting..."),pListItem)); delete pListItem; } } } // // Now, swap the lists... // WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- deleting old list..."))); DPA_Destroy( _hdpaItems ); WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- using new list..."))); _hdpaItems = hdpaNew; } } } /***************************************************************************** CWizardInfoBlob::AddAllPhotosFromDataObject Runs through the data object and create CListItems for each item in the data object... *****************************************************************************/ VOID CWizardInfoBlob::AddAllPhotosFromDataObject() { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::AddAllPhotosFromDataObject()") )); if (!_pdo || _bAlreadyAddedPhotos) { return; } _bAlreadyAddedPhotos = TRUE; // // Get an instance of the Namespace walking object... // UINT cItemsWalk; CComPtr pNSW; HRESULT hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pNSW)); if (SUCCEEDED(hr)) { // // Walk the namespace but only pull from current folder... // CWalkCallback cb; DWORD dwFlags; if (_bOnlyUseSelection) { dwFlags = 0; } else { dwFlags = (NSWF_ONE_IMPLIES_ALL | NSWF_NONE_IMPLIES_ALL); } hr = pNSW->Walk(_pdo, dwFlags, 0, &cb); if (SUCCEEDED(hr)) { // // Get the list of pidls, note, when we do // this we own them -- in other words, we // have to free the pidls when we're done // with them... // LPITEMIDLIST *ppidls = NULL; hr = pNSW->GetIDArrayResult(&cItemsWalk, &ppidls); if (SUCCEEDED(hr) && ppidls) { WIA_TRACE((TEXT("AddAllPhotosFromDataObject: pNSW->GetIDArrayResult() returned cItemsWalk = %d"),cItemsWalk)); for (INT i = 0; i < (INT)cItemsWalk; i++) { AddPhoto( ppidls[i] ); ILFree( ppidls[i] ); } CoTaskMemFree(ppidls); } else { WIA_ERROR((TEXT("AddAllPhotosFromDataObject(): pNSW->GetIDArrayResult() failed w/hr=0x%x"),hr)); } // // If only one item was given to us, then force select all // on by default. This way all frames of a multi-frame // image will be selected. // if (cItemsWalk == 1) { _bForceSelectAll = TRUE; } } else { WIA_ERROR((TEXT("AddAllPhotosFromDataObject(): pNSW->Walk() failed w/hr=0x%x"),hr)); } // // Were any items rejected while walking the item tree? // _bItemsWereRejected = cb.WereItemsRejected(); // // Now, detect the case where one item was selected, but we loaded // all the images in the folder. In this case, we want to pre-select // only that one image. That image will be the first pidl we got // back from the INamespaceWalk call... // // // How many items are in the dataobject? This is will give us how // many items were selected in the shell view... // if (_pdo) { // Request the IDA from the data object FORMATETC fmt = {0}; fmt.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST); fmt.dwAspect = DVASPECT_CONTENT; fmt.lindex = -1; fmt.tymed = TYMED_HGLOBAL; STGMEDIUM medium = { 0 }; hr = _pdo->GetData(&fmt, &medium); if (SUCCEEDED(hr)) { LPIDA pida = (LPIDA)GlobalLock( medium.hGlobal ); if (pida) { _uItemsInInitialSelection = pida->cidl; WIA_TRACE((TEXT("_uItemsInInitialSelection = %d"),_uItemsInInitialSelection)); // // Now check if only one item was in dataobject... // if (cItemsWalk < _uItemsInInitialSelection) { WIA_TRACE((TEXT("Some items were rejected, setting _bItemsWereRejected to TRUE!"))); _bItemsWereRejected = TRUE; } // // Now check if only one item was in dataobject... // if (pida->cidl == 1) { // // There are two situations where we get one object: // // A. When the user actually had 1 object selected. // In this case, we will get back a relative pidl // that is an item. // // B. When the user actually had 0 objects selected. // In this case, we will get back a relative pidl // that is a folder. // // We need to set the initialselection count to 1 for A // and 0 for B. // LPITEMIDLIST pidlItem = (LPITEMIDLIST)((LPBYTE)(pida) + pida->aoffset[1]); LPITEMIDLIST pidlFolder = (LPITEMIDLIST)((LPBYTE)(pida) + pida->aoffset[0]); // // Build fully qualified IDList... // LPITEMIDLIST pidlFull = ILCombine( pidlFolder, pidlItem ); if (pidlFull) { CComPtr psfParent; LPCITEMIDLIST pidlLast; hr = SHBindToParent( pidlFull, IID_PPV_ARG(IShellFolder, &psfParent), &pidlLast ); if (SUCCEEDED(hr) && psfParent) { ULONG uAttr = SFGAO_FOLDER; hr = psfParent->GetAttributesOf( 1, &pidlLast, &uAttr ); if (SUCCEEDED(hr) && (uAttr & SFGAO_FOLDER)) { _uItemsInInitialSelection = 0; } } ILFree(pidlFull); } } } GlobalUnlock( medium.hGlobal ); ReleaseStgMedium( &medium ); } } } else { WIA_ERROR((TEXT("AddAllPhotosFromDataObject(): couldn't CoCreate( INamespaceWalk ), hr = 0x%x"),hr)); } _bAllPicturesAdded = TRUE; } /***************************************************************************** CWizardInfoBlob::AddAllPhotosFromList Runs through the pidl array and create CListItems for each item... *****************************************************************************/ VOID CWizardInfoBlob::AddPhotosFromList( LPITEMIDLIST *aidl, int cidl, BOOL bSelectAll ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::AddAllPhotosFromList()") )); if (_bAlreadyAddedPhotos) { return; } _bAlreadyAddedPhotos = TRUE; for (int i=0;iGetImageFrameCount(&lFrames); if (SUCCEEDED(hr)) { // // Create a list item for each page... // INT iRes; CListItem * pListItem = NULL; for (LONG lCurFrame=0; lCurFrame < lFrames; lCurFrame++ ) { // // NOTE: The pListItem constructor does an addref on pItem // pListItem = new CListItem( pItem, lCurFrame ); iRes = -1; if (pListItem) { // // Add this page to the item list... // iRes = DPA_AppendPtr( _hdpaItems, (LPVOID)pListItem ); WIA_TRACE((TEXT("DPA_AppendPtr returned %d"),iRes)); } if (iRes == -1) { // // the list item wasn't correctly added to // the DPA. So we need to delete the list // item entry, but not the underlying photo item // object. To do this, we increase the // reference count artificially on the item, // then delete pListItem (which will cause a // Release() to happen on the underlying pItem). // Then we knowck down the reference count by 1 // to get back to the value that was there (on // the underlying pItem) before the pListItem // was created. // pItem->AddRef(); delete pListItem; pItem->ReleaseWithoutDeleting(); hr = E_OUTOFMEMORY; WIA_ERROR((TEXT("Couldn't create a list item for this photo item"))); } else { // // record that there is a legitimate outstanding // reference to the pItem // bItemAddedToDPA = TRUE; } } } if (!bItemAddedToDPA) { // // An error occurred trying to load the file, since we skip'd // adding the item to our list, we'll leak this pointer if we // don't delete it here... // delete pItem; } } else { WIA_ERROR((TEXT("Couldn't allocate a new CPhotoItem!"))); } } else { WIA_ERROR((TEXT("Couldn't create _hdpaItems!"))); } WIA_RETURN_HR(hr); } /***************************************************************************** CWizardInfoBlob::ToggleSelectionStateOnCopies Finds the root item in the list, and then toggles selection state to be the specified state on any copies that follow the item in the list... *****************************************************************************/ VOID CWizardInfoBlob::ToggleSelectionStateOnCopies( CListItem * pRootItem, BOOL bState ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::CountOfPhotos()"))); CAutoCriticalSection lock(_csItems); if (_hdpaItems) { // // First, try to find this root item in our list... // INT iCountOfItems = DPA_GetPtrCount(_hdpaItems); INT iIndexOfRootItem = -1; CListItem * pListItem; for (INT i=0; iIsCopyItem()) { break; } // // This is a copy of the specified root item. Mark it // to have the correct selection state... // pListItem->SetSelectionState(bState); } } } } } /***************************************************************************** CWizardInfoBlob::CountOfPhotos Returns number of photos *****************************************************************************/ INT CWizardInfoBlob::CountOfPhotos( BOOL bIncludeCopies ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::CountOfPhotos()"))); CAutoCriticalSection lock(_csItems); if (_hdpaItems) { if (bIncludeCopies) { return (INT)DPA_GetPtrCount( _hdpaItems ); } else { // // actually walk the list and only count root (non-copy) items... // INT iCount = 0; CListItem * pListItem = NULL; for (INT i=0; i<(INT)DPA_GetPtrCount(_hdpaItems); i++) { pListItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i); if (pListItem && (!pListItem->IsCopyItem())) { iCount++; } } return iCount; } } return 0; } /***************************************************************************** CWizardInfoBlob::CountOfSelectedPhotos returns the number of photos selected for printing *****************************************************************************/ INT CWizardInfoBlob::CountOfSelectedPhotos( BOOL bIncludeCopies ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::CountOfSelecetedPhotos()"))); INT iCount = 0; CAutoCriticalSection lock(_csItems); if (_hdpaItems) { INT iTotal = DPA_GetPtrCount( _hdpaItems ); CListItem * pItem = NULL; for (INT i = 0; i < iTotal; i++) { pItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i); if (pItem) { if (bIncludeCopies) { if (pItem->SelectedForPrinting()) { iCount++; } } else { if ((!pItem->IsCopyItem()) && pItem->SelectedForPrinting()) { iCount++; } } } } } return iCount; } /***************************************************************************** CWizardInfoBlob::GetIndexOfNextPrintableItem Starting from iStartIndex, returns the index of the next item that is mark as selected for printing... *****************************************************************************/ INT CWizardInfoBlob::GetIndexOfNextPrintableItem( INT iStartIndex ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetIndexOfNextPrintableItem( %d )"),iStartIndex)); INT iIndex = -1; INT iCountOfAllItems = CountOfPhotos(TRUE); CListItem * pItem = NULL; if (iStartIndex != -1) { for( INT i = iStartIndex; i < iCountOfAllItems; i++ ) { pItem = GetListItem( i, TRUE ); if (pItem) { if (pItem->SelectedForPrinting()) { iIndex = i; break; } } } } return iIndex; } /***************************************************************************** CWizardInfoBlob::GetListItem Returns given item from the list of photos *****************************************************************************/ CListItem * CWizardInfoBlob::GetListItem( INT iIndex, BOOL bIncludeCopies ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetListItem( %d )"),iIndex)); CAutoCriticalSection lock(_csItems); if (iIndex == -1) { return NULL; } if (_hdpaItems) { if (bIncludeCopies) { if ((iIndex >= 0) && (iIndex < DPA_GetPtrCount(_hdpaItems))) { return (CListItem *)DPA_FastGetPtr(_hdpaItems,iIndex); } } else { // // If we're not including copies, then we need to walk the // whole list to find root items only. This is much slower, // but will always find root items only. // CListItem * pListItem = NULL; INT iRootIndex = 0; for (INT i = 0; i < DPA_GetPtrCount(_hdpaItems); i++) { pListItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i); if (pListItem && (!pListItem->IsCopyItem())) { if (iIndex == iRootIndex) { return pListItem; } else { iRootIndex++; } } } } } return NULL; } /***************************************************************************** CWizardInfoBlob::GetTemplateByIndex Gets a template given the index *****************************************************************************/ HRESULT CWizardInfoBlob::GetTemplateByIndex(INT iIndex, CTemplateInfo ** ppTemplateInfo ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetTemplateByIndex( iIndex = %d )"),iIndex)); if (ppTemplateInfo) { return _templates.GetTemplate( iIndex, ppTemplateInfo ); } return E_INVALIDARG; } /***************************************************************************** CWizardInfoBlob::TemplateGetPreviewBitmap returns S_OK on sucess or COM error otherwise *****************************************************************************/ HRESULT CWizardInfoBlob::TemplateGetPreviewBitmap(INT iIndex, const SIZE &sizeDesired, HBITMAP *phBmp) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::TemplateGetPreviewBitmap( iIndex = %d )"),iIndex)); HRESULT hr = E_INVALIDARG; IStream * pStream = NULL; Gdiplus::Bitmap * pBmp = NULL; CTemplateInfo * pTemplate = NULL; hr = _templates.GetTemplate( iIndex, &pTemplate ); WIA_CHECK_HR(hr,"_templates.GetTemplate()"); if (SUCCEEDED(hr) && pTemplate) { hr = pTemplate->GetPreviewImageStream( &pStream ); WIA_CHECK_HR(hr,"pTemplate->GetPreviewImageStream( &pStream )"); if (SUCCEEDED(hr) && pStream) { // 48 62 hr = LoadAndScaleBmp(pStream, sizeDesired.cx, sizeDesired.cy, &pBmp); WIA_CHECK_HR(hr,"LoadAndScaleBmp( pStream, size.cx, size.cy, pBmp )"); if (SUCCEEDED(hr) && pBmp) { DWORD dw = GetSysColor(COLOR_WINDOW); Gdiplus::Color wndClr(255, GetRValue(dw), GetGValue(dw), GetBValue(dw)); hr = Gdiplus2HRESULT(pBmp->GetHBITMAP(wndClr, phBmp)); WIA_CHECK_HR(hr,"pBmp->GetHBITMAP( phBmp )"); delete pBmp; } pStream->Release(); } } WIA_RETURN_HR(hr); } /***************************************************************************** CWizardInfoBlob::SetPrinterToUse Sets the name of the printer to use to print... *****************************************************************************/ HRESULT CWizardInfoBlob::SetPrinterToUse( LPCTSTR pszPrinterName ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::SetPrinterToUse( '%s' )"),pszPrinterName ? pszPrinterName : TEXT("NULL POINTER!"))); if (!pszPrinterName || !(*pszPrinterName)) { WIA_RETURN_HR( E_INVALIDARG ); } _strPrinterName = pszPrinterName; return S_OK; } /***************************************************************************** CWizardInfoBlob::SetDevModeToUse Sets the DEVMODE pointer to use to print... *****************************************************************************/ HRESULT CWizardInfoBlob::SetDevModeToUse( PDEVMODE pDevMode ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::SetDevModeToUse(0x%x)"),pDevMode)); HRESULT hr = E_INVALIDARG; if (_hDevMode) { delete [] _hDevMode; _hDevMode = NULL; } if (pDevMode) { UINT cbDevMode = (UINT)pDevMode->dmSize + (UINT)pDevMode->dmDriverExtra; if( _hDevMode = (PDEVMODE) new BYTE[cbDevMode] ) { WIA_TRACE((TEXT("CWizardInfoBlob::SetDevModeToUse - copying pDevMode(0x%x) to _hDevMode(0x%x)"),pDevMode,_hDevMode)); CopyMemory( _hDevMode, pDevMode, cbDevMode ); hr = S_OK; } else { hr = E_OUTOFMEMORY; } } WIA_RETURN_HR(hr); } /***************************************************************************** CWizardInfoBlob::GetDevModeToUse Retrieves the devmode pointer to use *****************************************************************************/ PDEVMODE CWizardInfoBlob::GetDevModeToUse() { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetDevModeToUse()"))); return _hDevMode; } /***************************************************************************** CWizardInfoBlob::GetPrinterToUse Returns the string that represent which printer to print to... *****************************************************************************/ LPCTSTR CWizardInfoBlob::GetPrinterToUse() { if (_strPrinterName.Length()) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetPrinterToUse( returning: '%s' )"),_strPrinterName.String())); return _strPrinterName.String(); } return NULL; } /***************************************************************************** CWizardInfoBlob::ConstructPrintToTemplate When the wizard is invoked for "PrintTo" functionatlity, construct a template that represents full page *****************************************************************************/ VOID CWizardInfoBlob::ConstructPrintToTemplate() { WIA_PUSH_FUNCTION_MASK((TRACE_PRINTTO, TEXT("CWizardInfoBlob::ConstructPrintToTemplate()"))); // // creates 1 template that is full page print... // _templates.InitForPrintTo(); } /***************************************************************************** CWizardInfoBlob::GetCountOfPrintedPages Returns the number of pages that will be printed with the specified template. *****************************************************************************/ HRESULT CWizardInfoBlob::GetCountOfPrintedPages( INT iTemplateIndex, INT * pPageCount ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetCountOfPrintedPages( iTemplateIndex = %d )"),iTemplateIndex)); HRESULT hr = E_FAIL; // // Check for bad params... // if ( !pPageCount || ((iTemplateIndex < 0) || (iTemplateIndex >= _templates.Count())) ) { WIA_RETURN_HR( E_INVALIDARG ); } // // Get template in question... // CTemplateInfo * pTemplate = NULL; hr = _templates.GetTemplate( iTemplateIndex, &pTemplate ); if (SUCCEEDED(hr) && pTemplate) { // // Is this a template that wants to repeat photos? // BOOL bRepeat = FALSE; hr = pTemplate->GetRepeatPhotos( &bRepeat ); if (SUCCEEDED(hr)) { // // Get the count // if (!bRepeat) { INT iPhotosPerTemplate = pTemplate->PhotosPerPage(); INT iCountOfPhotos = CountOfSelectedPhotos(TRUE); INT PagesRequired = iCountOfPhotos / iPhotosPerTemplate; if (iCountOfPhotos % iPhotosPerTemplate) { PagesRequired++; } *pPageCount = PagesRequired; } else { *pPageCount = CountOfSelectedPhotos(TRUE); } } } WIA_RETURN_HR(hr); } /***************************************************************************** CWizardInfoBlob::SetPreviewWnd Stores the hwnd that is the preview and also calculates the center of the window so all resizes will keep it in the right place in the window. *****************************************************************************/ VOID CWizardInfoBlob::SetPreviewWnd( HWND hwnd ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::SetPreviewWnd( hwnd = 0x%x )"),hwnd)); if (hwnd) { _hwndPreview = hwnd; GetClientRect( _hwndPreview, &_rcInitSize ); MapWindowPoints( _hwndPreview, GetParent(_hwndPreview), (LPPOINT)&_rcInitSize, 2 ); // // Find center of window // _Center.cx = MulDiv(_rcInitSize.right - _rcInitSize.left, 1, 2) + _rcInitSize.left; _Center.cy = MulDiv(_rcInitSize.bottom - _rcInitSize.top, 1, 2) + _rcInitSize.top; } } /***************************************************************************** CWizardInfoBlob::GetIntroFont Creates a font to be used for the intro text in the wizard... *****************************************************************************/ HFONT CWizardInfoBlob::GetIntroFont(HWND hwnd) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::GetIntroFont()"))); if ( !_hfontIntro ) { TCHAR szBuffer[64]; NONCLIENTMETRICS ncm = { 0 }; LOGFONT lf; ncm.cbSize = SIZEOF(ncm); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); lf = ncm.lfMessageFont; LoadString(g_hInst, IDS_TITLEFONTNAME, lf.lfFaceName, ARRAYSIZE(lf.lfFaceName)); lf.lfWeight = FW_BOLD; LoadString(g_hInst, IDS_TITLEFONTSIZE, szBuffer, ARRAYSIZE(szBuffer)); lf.lfHeight = 0 - (GetDeviceCaps(NULL, LOGPIXELSY) * StrToInt(szBuffer) / 72); _hfontIntro = CreateFontIndirect(&lf); } return _hfontIntro; } /***************************************************************************** CWizardInfoBlob::UpdateCachedPrinterInfo Update some cached information about the printer... *****************************************************************************/ VOID CWizardInfoBlob::UpdateCachedPrinterInfo() { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::UpdateCachedPrinterInfo()"))); CAutoCriticalSection lock(_csPrinterInfo); BOOL bDeleteDC = FALSE; HDC hDC = GetCachedPrinterDC(); if (!hDC) { // // For some reason, we don't have a stored DC. So, we need to create // one so that we can get the info... // hDC = CreateDC( TEXT("WINSPOOL"), GetPrinterToUse(), NULL, GetDevModeToUse() ); bDeleteDC = TRUE; } if (hDC) { // // Get DPI information // _WizPrinterInfo.DPI.cx = GetDeviceCaps( hDC, LOGPIXELSX ); _WizPrinterInfo.DPI.cy = GetDeviceCaps( hDC, LOGPIXELSY ); // // Get size of printable area... // _WizPrinterInfo.rcDevice.left = 0; _WizPrinterInfo.rcDevice.right = GetDeviceCaps( hDC, HORZRES ); _WizPrinterInfo.rcDevice.top = 0; _WizPrinterInfo.rcDevice.bottom = GetDeviceCaps( hDC, VERTRES ); // // Get physical size of printer's page // _WizPrinterInfo.PhysicalSize.cx = GetDeviceCaps( hDC, PHYSICALWIDTH ); _WizPrinterInfo.PhysicalSize.cy = GetDeviceCaps( hDC, PHYSICALHEIGHT ); // // Get physical offset to printable area // _WizPrinterInfo.PhysicalOffset.cx = GetDeviceCaps( hDC, PHYSICALOFFSETX ); _WizPrinterInfo.PhysicalOffset.cy = GetDeviceCaps( hDC, PHYSICALOFFSETY ); // // Say that we've got valid information now... // _WizPrinterInfo.bValid = TRUE; } if (bDeleteDC) { if (hDC) { DeleteDC( hDC ); } } } /***************************************************************************** CWizardInfoBlob::SetNumberOfCopies When the number of copies of each pictures changes, do the work here... *****************************************************************************/ VOID CWizardInfoBlob::SetNumberOfCopies( UINT uCopies ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::SetNumberOfCopies( %d )"),uCopies)); // // We really want to do this on a background thread, so queue up a message // to the only background thread we control -- the GDI+ startup & shutdown // thread. We'll overload here to handle this task... // if (_dwGdiPlusThreadId) { PostThreadMessage( _dwGdiPlusThreadId, WIZ_MSG_COPIES_CHANGED, (WPARAM)uCopies, 0 ); } } /***************************************************************************** _SetupDimensionsForPrinting Computes all relevant information to printing to a page. *****************************************************************************/ VOID CWizardInfoBlob::_SetupDimensionsForPrinting( HDC hDC, CTemplateInfo * pTemplate, RENDER_DIMENSIONS * pDim ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::_SetupDimensionsForPrinting()"))); if (!pDim) { WIA_ERROR((TEXT("Printer: pDim is NULL!"))); return; } if (!pTemplate) { WIA_ERROR((TEXT("Printer: pTemplate is NULL!"))); } // // Make sure we have good values in the cached printer info structure // GetCachedPrinterInfo(); // // Flush out old values... // ZeroMemory( pDim, sizeof(RENDER_DIMENSIONS) ); // // Derive multiplier for horizontal & vertical measurements // (NOMINAL --> printer), and compute rcDevice which is // the printable area available (in device units -- pixels). // pDim->DPI = _WizPrinterInfo.DPI; WIA_TRACE((TEXT("Printer: xDPI = %d, yDPI = %d"),pDim->DPI.cx,pDim->DPI.cy)); pDim->rcDevice = _WizPrinterInfo.rcDevice; WIA_TRACE((TEXT("Printer: rcDevice( %d, %d, %d, %d )"),pDim->rcDevice.left, pDim->rcDevice.top, pDim->rcDevice.right, pDim->rcDevice.bottom )); // // Convert device coords into 1/10000 inch equivalents // pDim->NominalDevicePrintArea.cx = (INT)((DOUBLE)(((DOUBLE)pDim->rcDevice.right / (DOUBLE)pDim->DPI.cx) * (DOUBLE)NOMINAL_MULTIPLIER)); pDim->NominalDevicePrintArea.cy = (INT)((DOUBLE)(((DOUBLE)pDim->rcDevice.bottom / (DOUBLE)pDim->DPI.cy) * (DOUBLE)NOMINAL_MULTIPLIER)); WIA_TRACE((TEXT("Printer: DeviceNominal ( %d, %d )"),pDim->NominalDevicePrintArea.cx,pDim->NominalDevicePrintArea.cy)); // // Get physical page size (in nominal coords) // pDim->NominalPhysicalSize.cx = (INT)((DOUBLE)(((DOUBLE)_WizPrinterInfo.PhysicalSize.cx / (DOUBLE)pDim->DPI.cx) * (DOUBLE)NOMINAL_MULTIPLIER)); pDim->NominalPhysicalSize.cy = (INT)((DOUBLE)(((DOUBLE)_WizPrinterInfo.PhysicalSize.cy / (DOUBLE)pDim->DPI.cy) * (DOUBLE)NOMINAL_MULTIPLIER)); WIA_TRACE((TEXT("Printer: NominalPhysicalSize (%d, %d)"),pDim->NominalPhysicalSize.cx,pDim->NominalPhysicalSize.cy)); // // Get physical offset to printable area (in nominal coords) // pDim->NominalPhysicalOffset.cx = (INT)((DOUBLE)(((DOUBLE)_WizPrinterInfo.PhysicalOffset.cx / (DOUBLE)pDim->DPI.cx) * (DOUBLE)NOMINAL_MULTIPLIER)); pDim->NominalPhysicalOffset.cy = (INT)((DOUBLE)(((DOUBLE)_WizPrinterInfo.PhysicalOffset.cy / (DOUBLE)pDim->DPI.cx) * (DOUBLE)NOMINAL_MULTIPLIER)); WIA_TRACE((TEXT("Printer: NominalPhyscialOffset (%d, %d)"),pDim->NominalPhysicalOffset.cx,pDim->NominalPhysicalOffset.cy)); // // Compute offset that will center the template in the printable // area. Note, this can be a negative number if the paper size // selected is too small to contain the template. // if (SUCCEEDED(pTemplate->GetNominalRectForImageableArea( &pDim->rcNominalTemplatePrintArea ))) { if ((-1 == pDim->rcNominalTemplatePrintArea.left) && (-1 == pDim->rcNominalTemplatePrintArea.right) && (-1 == pDim->rcNominalTemplatePrintArea.top) && (-1 == pDim->rcNominalTemplatePrintArea.bottom)) { WIA_TRACE((TEXT("Printer: NominalTemplateArea( use full printable area )"))); pDim->NominalPageOffset.cx = 0; pDim->NominalPageOffset.cy = 0; } else { WIA_TRACE((TEXT("Printer: NominalTemplateArea(%d, %d)"),pDim->rcNominalTemplatePrintArea.right - pDim->rcNominalTemplatePrintArea.left,pDim->rcNominalTemplatePrintArea.bottom - pDim->rcNominalTemplatePrintArea.top)); pDim->NominalPageOffset.cx = (pDim->NominalDevicePrintArea.cx - (pDim->rcNominalTemplatePrintArea.right - pDim->rcNominalTemplatePrintArea.left)) / 2; pDim->NominalPageOffset.cy = (pDim->NominalDevicePrintArea.cy - (pDim->rcNominalTemplatePrintArea.bottom - pDim->rcNominalTemplatePrintArea.top)) / 2; } } WIA_TRACE((TEXT("Printer: NominalPageOffset(%d, %d)"),pDim->NominalPageOffset.cx,pDim->NominalPageOffset.cy)); // // Compute clip rectangle for printable area on physical page (nominal coords) // pDim->rcNominalPageClip.left = pDim->NominalPhysicalOffset.cx; pDim->rcNominalPageClip.top = pDim->NominalPhysicalOffset.cy; pDim->rcNominalPageClip.right = pDim->rcNominalPageClip.left + pDim->NominalDevicePrintArea.cx; pDim->rcNominalPageClip.bottom = pDim->rcNominalPageClip.top + pDim->NominalDevicePrintArea.cy; WIA_TRACE((TEXT("Printer: rcNominalPageClip is (%d, %d, %d, %d)"), pDim->rcNominalPageClip.left, pDim->rcNominalPageClip.top, pDim->rcNominalPageClip.right, pDim->rcNominalPageClip.bottom )); } /***************************************************************************** _SetupDimensionsForScreen Computes all relevant information for drawing to the screen. *****************************************************************************/ VOID CWizardInfoBlob::_SetupDimensionsForScreen( CTemplateInfo * pTemplate, HWND hwndScreen, RENDER_DIMENSIONS * pDim ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::_SetupDimensionsForScreen()"))); if (!pDim) { WIA_ERROR((TEXT("Screen: pDim is NULL!"))); return; } // // Before we do anything, check to see if we're in Portrait or Landscape // and rotate the template accordingly... // PDEVMODE pDevMode = GetDevModeToUse(); if (pDevMode) { if (pDevMode->dmFields & DM_ORIENTATION) { if (pDevMode->dmOrientation == DMORIENT_PORTRAIT) { pTemplate->RotateForPortrait(); } else if (pDevMode->dmOrientation == DMORIENT_LANDSCAPE) { pTemplate->RotateForLandscape(); } } } _SetupDimensionsForPrinting( GetCachedPrinterDC(), pTemplate, pDim ); // // Flush out old values, except for NominalPhysicalSize and // NominalPhysicalOffset and NominalPageOffset which we want to keep... // pDim->rcDevice.left = 0; pDim->rcDevice.top = 0; pDim->rcDevice.right = 0; pDim->rcDevice.bottom = 0; pDim->DPI.cx = 0; pDim->DPI.cy = 0; RECT rcWnd = _rcInitSize; WIA_TRACE((TEXT("Screen: _rcInitSize was (%d, %d, %d, %d)"),_rcInitSize.left,_rcInitSize.top,_rcInitSize.right,_rcInitSize.bottom)); // // Get span of window to contain preview... // INT wScreen = rcWnd.right - rcWnd.left; INT hScreen = rcWnd.bottom - rcWnd.top; WIA_TRACE((TEXT("Screen: w = %d, h = %d"),wScreen,hScreen)); // // Get DPI of screen // HDC hDC = GetDC( hwndScreen ); if (hDC) { pDim->DPI.cx = GetDeviceCaps( hDC, LOGPIXELSX ); pDim->DPI.cy = GetDeviceCaps( hDC, LOGPIXELSY ); ReleaseDC( hwndScreen, hDC ); } // // Scale printable area into window // SIZE sizePreview; sizePreview = PrintScanUtil::ScalePreserveAspectRatio( wScreen, hScreen, pDim->NominalPhysicalSize.cx, pDim->NominalPhysicalSize.cy ); WIA_TRACE((TEXT("Screen: scaled print page is (%d, %d)"),sizePreview.cx,sizePreview.cy)); rcWnd.left = _rcInitSize.left; rcWnd.top = _Center.cy - (sizePreview.cy / 2); rcWnd.right = rcWnd.left + sizePreview.cx; rcWnd.bottom = rcWnd.top + sizePreview.cy; // // Now change window size to be preview size... // WIA_TRACE((TEXT("Screen: moving window to (%d, %d) with size (%d, %d)"),rcWnd.left,rcWnd.top,sizePreview.cx,sizePreview.cy)); MoveWindow( hwndScreen, rcWnd.left, rcWnd.top, sizePreview.cx, sizePreview.cy, TRUE ); } /***************************************************************************** CWizardInfoBlob::_RenderFilenameOfPhoto Draws the filename of the photo underneath the photo *****************************************************************************/ VOID CWizardInfoBlob::_RenderFilenameOfPhoto( Gdiplus::Graphics * g, RECT * pPhotoDest, CListItem * pPhoto ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::_RenderFilenameOfPhoto()"))); // // check for bad params // if (!pPhotoDest || !g || !pPhoto) { return; } // // the rectangle for the filename is the width of the photo & 2 text lines high, with a // .05" gap from the bottom of the photo. All measurements are in nominal // sizes, which means 1/10000 of an inch. // Gdiplus::Font font( L"arial", (Gdiplus::REAL)1100.0, Gdiplus::FontStyleRegular, Gdiplus::UnitWorld, NULL ); WIA_TRACE((TEXT("_RenderFilenameOfPhoto: height = %d pixels, emSize = %d"),(INT)font.GetHeight((Gdiplus::Graphics *)NULL), (INT)font.GetSize())); Gdiplus::RectF rectText( (Gdiplus::REAL)pPhotoDest->left, (Gdiplus::REAL)(pPhotoDest->bottom + 500), (Gdiplus::REAL)(pPhotoDest->right - pPhotoDest->left), (Gdiplus::REAL)font.GetHeight((Gdiplus::Graphics *)NULL) * (Gdiplus::REAL)2.0); //Gdiplus::StringFormat sf( Gdiplus::StringFormatFlagsLineLimit ); Gdiplus::StringFormat sf( 0 ); sf.SetTrimming( Gdiplus::StringTrimmingEllipsisCharacter ); sf.SetAlignment( Gdiplus::StringAlignmentCenter ); CSimpleStringWide * pFilename = pPhoto->GetFilename(); if (pFilename) { 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())); g->DrawString( pFilename->String(), pFilename->Length(), &font, rectText, &sf, &Gdiplus::SolidBrush( Gdiplus::Color::Black ) ); delete pFilename; } } /***************************************************************************** CWizardInfoBlob::RenderPrintedPage Draws photos to the printer according to which layout, which page and the given printer hDC. *****************************************************************************/ HRESULT CWizardInfoBlob::RenderPrintedPage( INT iTemplateIndex, INT iPage, HDC hDC, HWND hwndProgress, float fProgressStep, float * pPercent ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::RenderPrintedPage( iPage = %d, iTemplateIndex = %d, hwndProgress = 0x%x )"),iPage,iTemplateIndex,hwndProgress)); HRESULT hr = S_OK; RENDER_OPTIONS ro = {0}; // // Check for bad params... // if ( (!hDC) || ((iTemplateIndex < 0) || (iTemplateIndex >= _templates.Count())) ) { WIA_RETURN_HR( E_INVALIDARG ); } // // Get the template in question... // CTemplateInfo * pTemplate = NULL; hr = _templates.GetTemplate( iTemplateIndex, &pTemplate ); if (FAILED(hr)) { WIA_RETURN_HR(hr); } if (!pTemplate) { WIA_RETURN_HR(E_OUTOFMEMORY); } UINT uFlagsOrientation = 0; // // Before we do anything, check to see if we're in Portrait or Landscape // and rotate the template accordingly... // PDEVMODE pDevMode = GetDevModeToUse(); if (pDevMode) { if (pDevMode->dmFields & DM_ORIENTATION) { if (pDevMode->dmOrientation == DMORIENT_PORTRAIT) { pTemplate->RotateForPortrait(); } else if (pDevMode->dmOrientation == DMORIENT_LANDSCAPE) { pTemplate->RotateForLandscape(); ro.Flags = RF_ROTATE_270; } } } // // Is this a template that repeats photos? // BOOL bRepeat = FALSE; pTemplate->GetRepeatPhotos( &bRepeat ); // // Does this template want the filenames printed out under each photo? // BOOL bPrintFilename = FALSE; pTemplate->GetPrintFilename( &bPrintFilename ); // // Do we have any photos to print for this page? // INT iPhotosPerTemplate = pTemplate->PhotosPerPage(); INT iCountOfPhotos = CountOfSelectedPhotos(TRUE); if ( (iPhotosPerTemplate == 0) || (iCountOfPhotos == 0) || ( (!bRepeat) && ((iCountOfPhotos - (iPage * iPhotosPerTemplate)) <= 0) ) ) { WIA_RETURN_HR( S_FALSE ); } // // Get a handle to the printer we are going to use... // HANDLE hPrinter = NULL; OpenPrinter( (LPTSTR)GetPrinterToUse(), &hPrinter, NULL ); // // Compute the dimensions of the drawable area... // _SetupDimensionsForPrinting( hDC, pTemplate, &ro.Dim ); // // Get index of photo to start with... // INT iPhoto; if (!bRepeat) { iPhoto = iPage * iPhotosPerTemplate; } else { iPhoto = iPage; } // // We always do scale to fit // ro.Flags |= RF_SCALE_TO_FIT; // // Get the control flags from the template... // BOOL bCanRotate = TRUE; pTemplate->GetCanRotate( &bCanRotate ); if (bCanRotate) { ro.Flags |= RF_ROTATE_AS_NEEDED; } BOOL bCanCrop = TRUE; pTemplate->GetCanCrop( &bCanCrop ); if (bCanCrop) { ro.Flags |= RF_CROP_TO_FIT; } BOOL bUseThumbnails = FALSE; pTemplate->GetUseThumbnailsToPrint( &bUseThumbnails ); if (bUseThumbnails) { ro.Flags |= RF_USE_MEDIUM_QUALITY_DATA; } else { ro.Flags |= RF_USE_FULL_IMAGE_DATA; } // // If we're in no UI mode, then don't fail if we can't rotate... // WIA_TRACE((TEXT("RenderPrintedPage: _bShowUI is 0x%x"),_bShowUI)); if (!_bShowUI) { ro.Flags |= RF_NO_ERRORS_ON_FAILURE_TO_ROTATE; WIA_TRACE((TEXT("RenderPrintedPage: uFlags set to have RF_NO_ERRORS_ON_FAILURE (0x%x)"),ro.Flags)); } // // Compute offset to use... // INT xOffset = ro.Dim.NominalPageOffset.cx; INT yOffset = ro.Dim.NominalPageOffset.cy; // // Set up GDI+ for printing... // Gdiplus::Graphics g( hDC, hPrinter ); hr = Gdiplus2HRESULT(g.GetLastStatus()); if (SUCCEEDED(hr)) { // // First, set up coordinates / transform // g.SetPageUnit( Gdiplus::UnitPixel ); hr = Gdiplus2HRESULT(g.GetLastStatus()); if (SUCCEEDED(hr)) { g.SetPageScale( 1.0 ); hr = Gdiplus2HRESULT(g.GetLastStatus()); if (SUCCEEDED(hr)) { // // Set up transform so that we can draw in nominal // template coordinates from here on out... // 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) ); WIA_TRACE((TEXT("RenderPrintedPage: rectDevice is (%d, %d) with size (%d, %d)"),rectDevice.X,rectDevice.Y,rectDevice.Width,rectDevice.Height)); WIA_TRACE((TEXT("RenderPrintedPage: NominalDevicePrintArea is (%d, %d)"),ro.Dim.NominalDevicePrintArea.cx,ro.Dim.NominalDevicePrintArea.cy)); DOUBLE xScale = (DOUBLE)((DOUBLE)rectDevice.Width / (DOUBLE)ro.Dim.NominalDevicePrintArea.cx); DOUBLE yScale = (DOUBLE)((DOUBLE)rectDevice.Height / (DOUBLE)ro.Dim.NominalDevicePrintArea.cy); g.ScaleTransform( (Gdiplus::REAL) xScale, (Gdiplus::REAL) yScale ); hr = Gdiplus2HRESULT(g.GetLastStatus()); if (SUCCEEDED(hr)) { #ifdef PRINT_BORDER_AROUND_PRINTABLE_AREA Gdiplus::Rect rectPrintableArea( 0, 0, ro.Dim.NominalPrintArea.cx, ro.Dim.NominalPrintArea.cy ); Gdiplus::Color black(255,0,0,0); Gdiplus::SolidBrush BlackBrush( black ); Gdiplus::Pen BlackPen( &BlackBrush, (Gdiplus::REAL)1.0 ); WIA_TRACE((TEXT("RenderPrintedPage: rectPrintableArea is (%d, %d) @ (%d, %d)"),rectPrintableArea.Width,rectPrintableArea.Height,rectPrintableArea.X,rectPrintableArea.Y)); g.DrawRectangle( &BlackPen, rectPrintableArea ); #endif // // Now loop through each image in the template, and draw it... // RECT rcNominal; CListItem * pPhoto = NULL;; INT iPhotoIndex = 0; INT iPhotoIndexNext = 0; // // Get starting photo index... // for (INT i = iPhoto; i > 0; i--) { iPhotoIndex = GetIndexOfNextPrintableItem( iPhotoIndex ); iPhotoIndex++; } INT iRes = IDCONTINUE; for (INT i = 0; (!IsWizardShuttingDown()) && (iRes == IDCONTINUE) && (i < iPhotosPerTemplate); i++) { if (SUCCEEDED(pTemplate->GetNominalRectForPhoto(i, &rcNominal))) { if ((-1 == rcNominal.left) && (-1 == rcNominal.right) && (-1 == rcNominal.top) && (-1 == rcNominal.bottom)) { WIA_TRACE((TEXT("RenderPrintedPage: rcNominal is -1,-1,-1,-1 -- scaling to full page"))); rcNominal.left = 0; rcNominal.top = 0; rcNominal.right = ro.Dim.NominalDevicePrintArea.cx; rcNominal.bottom = ro.Dim.NominalDevicePrintArea.cy; WIA_TRACE((TEXT("RenderPrintedPage: rcNominal(%d) is ( %d, %d, %d, %d )"),i,rcNominal.left, rcNominal.top, rcNominal.right, rcNominal.bottom )); } else { WIA_TRACE((TEXT("RenderPrintedPage: rcNominal(%d) is ( %d, %d, %d, %d )"),i,rcNominal.left, rcNominal.top, rcNominal.right, rcNominal.bottom )); rcNominal.left += xOffset; rcNominal.right += xOffset; rcNominal.top += yOffset; rcNominal.bottom += yOffset; } // // Get the photo object // if (!bRepeat) { iPhotoIndex = GetIndexOfNextPrintableItem( iPhotoIndex ); } if ((!IsWizardShuttingDown()) && (iPhotoIndex != -1)) { pPhoto = GetListItem( iPhotoIndex, TRUE ); if (pPhoto) { // // Set up destination rectangle to draw into // Gdiplus::Rect dest( rcNominal.left, rcNominal.top, rcNominal.right - rcNominal.left, rcNominal.bottom - rcNominal.top ); WIA_TRACE((TEXT("RenderPrintedPage: rcPhotoDest(%d) is (%d x %d) a (%d, %d)"),i, dest.Width, dest.Height, dest.X, dest.Y )); // // supply the graphic objects to use... // ro.g = &g; ro.pDest = &dest; do { // // This variable will be set to TRUE in status.cpp if the user cancels the // print job. // extern BOOL g_bCancelPrintJob; // // Draw the image! // hr = pPhoto->Render( &ro ); // // Check to see if we've been cancelled. // If we have, we are going to break out // before displaying any errors. // if (g_bCancelPrintJob) { iRes = IDCANCEL; break; } if (FAILED(hr)) { iRes = ShowError( NULL, hr, 0, TRUE, pPhoto->GetPIDL() ); if (iRes == IDCONTINUE) { hr = S_FALSE; } } else { iRes = IDCONTINUE; } } while ( iRes == IDTRYAGAIN ); // // Print the filename if warranted // if (bPrintFilename) { _RenderFilenameOfPhoto( &g, &rcNominal, pPhoto ); } // // Update the percentage complete if needed // if (pPercent) { *pPercent += (float)(fProgressStep / (float)iPhotosPerTemplate); if (hwndProgress) { INT iPercent = (INT)(*pPercent); WIA_TRACE((TEXT("RenderPrinterPage: iPercent = %d"),iPercent)); PostMessage( hwndProgress, PBM_SETPOS, (WPARAM)iPercent, 0 ); } } } else { hr = E_OUTOFMEMORY; break; } if (!bRepeat) { iPhotoIndex++; } } } } } } } } else { WIA_ERROR((TEXT("RenderPrintedPage: couldn't create graphics, hr = 0x%x"),hr)); } if (hPrinter) { ClosePrinter( hPrinter ); } WIA_RETURN_HR(hr); } /***************************************************************************** CWizardInfo::RenderPreview Given a template index and an HWND, sizes the HWND to be aspect correct for the chosen printer/paper, and then returns an HBITMAP of a preview for this template that will fit in the window. *****************************************************************************/ HBITMAP CWizardInfoBlob::RenderPreview( INT iTemplateIndex, HWND hwndScreen ) { WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::RenderPreview( iTemplateIndex = %d )"),iTemplateIndex)); HBITMAP hbmp = NULL; RENDER_OPTIONS ro = {0}; if ((iTemplateIndex < 0) || (iTemplateIndex > _templates.Count())) { return NULL; } // // Get the correct template... // CTemplateInfo * pTemplate = NULL; HRESULT hr = _templates.GetTemplate( iTemplateIndex, &pTemplate ); if (FAILED(hr) || (!pTemplate)) { return NULL; } // // Tell the render engine we're rendering to the screen // ro.Flags |= RF_SET_QUALITY_FOR_SCREEN; // // Before we do anything, check to see if we're in Portrait or Landscape // and rotate the template accordingly... // PDEVMODE pDevMode = GetDevModeToUse(); if (pDevMode) { if (pDevMode->dmFields & DM_ORIENTATION) { if (pDevMode->dmOrientation == DMORIENT_PORTRAIT) { pTemplate->RotateForPortrait(); } else if (pDevMode->dmOrientation == DMORIENT_LANDSCAPE) { pTemplate->RotateForLandscape(); ro.Flags |= RF_ROTATE_270; } } } // // Do we have any photos to print for this page? // INT iPhotosPerTemplate = pTemplate->PhotosPerPage(); INT iCountOfPhotos = CountOfSelectedPhotos(TRUE); // // Compute the dimensions of the drawable area... // _SetupDimensionsForScreen( pTemplate, hwndScreen, &ro.Dim ); // // Does this template want the filenames printed out under each photo? // BOOL bPrintFilename = FALSE; pTemplate->GetPrintFilename( &bPrintFilename ); // // Get the control flags from the template... // ro.Flags |= RF_SCALE_TO_FIT; BOOL bCanRotate = TRUE; pTemplate->GetCanRotate( &bCanRotate ); if (bCanRotate) { ro.Flags |= RF_ROTATE_AS_NEEDED; } BOOL bCanCrop = TRUE; pTemplate->GetCanCrop( &bCanCrop ); if (bCanCrop) { ro.Flags |= RF_CROP_TO_FIT; } // // Is this a template that repeats photos? // BOOL bRepeat = FALSE; pTemplate->GetRepeatPhotos( &bRepeat ); // // Does the template use thumbnail data for printing? Match that for display // BOOL bUseThumbnails = TRUE; pTemplate->GetUseThumbnailsToPrint( &bUseThumbnails ); if (bUseThumbnails) { ro.Flags |= RF_USE_THUMBNAIL_DATA; } else { ro.Flags |= RF_USE_FULL_IMAGE_DATA; } // // Compute offset to use... // INT xOffset = ro.Dim.NominalPageOffset.cx + ro.Dim.NominalPhysicalOffset.cx; INT yOffset = ro.Dim.NominalPageOffset.cy + ro.Dim.NominalPhysicalOffset.cy; WIA_TRACE((TEXT("RenderPreview: Offset is (%d, %d)"),xOffset,yOffset)); // // Get clip rectangle... // Gdiplus::Rect clip( ro.Dim.rcNominalPageClip.left, ro.Dim.rcNominalPageClip.top, ro.Dim.rcNominalPageClip.right - ro.Dim.rcNominalPageClip.left, ro.Dim.rcNominalPageClip.bottom - ro.Dim.rcNominalPageClip.top ); // // Get size of preview window // RECT rcWnd = {0}; GetClientRect( hwndScreen, &ro.Dim.rcDevice ); Gdiplus::Rect rectWindow( 0, 0, ro.Dim.rcDevice.right - ro.Dim.rcDevice.left, ro.Dim.rcDevice.bottom - ro.Dim.rcDevice.top ); ro.Dim.bDeviceIsScreen = TRUE; // // Need to create a new preview bitmap for this template... // BITMAPINFO BitmapInfo; ZeroMemory( &BitmapInfo, sizeof(BITMAPINFO) ); BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); BitmapInfo.bmiHeader.biWidth = rectWindow.Width; BitmapInfo.bmiHeader.biHeight = rectWindow.Height; BitmapInfo.bmiHeader.biPlanes = 1; BitmapInfo.bmiHeader.biBitCount = 24; BitmapInfo.bmiHeader.biCompression = BI_RGB; // // Create the DIB section // PBYTE pBitmapData = NULL; HDC hdc = CreateCompatibleDC( NULL ); hbmp = CreateDIBSection( hdc, &BitmapInfo, DIB_RGB_COLORS, (LPVOID*)&pBitmapData, NULL, 0 ); if (hdc && hbmp) { // // Select the DIB section into the DC // SelectObject( hdc, hbmp ); // // Create Graphics object around memory DC // Gdiplus::Graphics g( hdc ); if (Gdiplus::Ok == g.GetLastStatus()) { // // First, draw bounding rectangle // g.SetPageUnit( Gdiplus::UnitPixel ); g.SetPageScale( 1.0 ); Gdiplus::Color white(255,255,255,255); Gdiplus::SolidBrush WhiteBrush( white ); // // Clear out the contents // g.FillRectangle( &WhiteBrush, rectWindow ); // // Frame the outside // Gdiplus::Color OutsideColor(255,64,64,64); Gdiplus::Pen OutsidePen( OutsideColor ); rectWindow.Width--; rectWindow.Height--; g.DrawRectangle( &OutsidePen, rectWindow ); // // Set up transform so that we can draw in nominal // template coordinates from here on out... // g.ScaleTransform( (Gdiplus::REAL)((DOUBLE)rectWindow.Width / (DOUBLE)ro.Dim.NominalPhysicalSize.cx), (Gdiplus::REAL)((DOUBLE)rectWindow.Height / (DOUBLE)ro.Dim.NominalPhysicalSize.cy) ); // // Set clip rectangle... // //WIA_TRACE((TEXT("RenderPreview: setting clip rect to (%d, %d) with size (%d, %d)"),clip.X,clip.Y,clip.Width,clip.Height)); //g.SetClip( clip, Gdiplus::CombineModeReplace ); // // Now loop through each image in the template, and draw it... // RECT rcNominal; INT iPhotoIndex = 0; CListItem * pPhoto = NULL; if (bRepeat) { iPhotoIndex = GetIndexOfNextPrintableItem( 0 ); } INT iRes = IDCONTINUE; hr = S_OK; for (INT i = 0; (iRes == IDCONTINUE) && (!IsWizardShuttingDown()) && (i < iPhotosPerTemplate); i++) { if (SUCCEEDED(pTemplate->GetNominalRectForPhoto(i, &rcNominal))) { if ((-1 == rcNominal.left) && (-1 == rcNominal.right) && (-1 == rcNominal.top) && (-1 == rcNominal.bottom)) { WIA_TRACE((TEXT("RenderPreview: rcNominal is -1,-1,-1,-1 -- scaling to full page"))); rcNominal = ro.Dim.rcNominalPageClip; WIA_TRACE((TEXT("RenderPreview: rcNominal(%d) is ( %d, %d, %d, %d )"),i,rcNominal.left, rcNominal.top, rcNominal.right, rcNominal.bottom )); } else { WIA_TRACE((TEXT("RenderPreview: rcNominal(%d) is ( %d, %d, %d, %d )"),i,rcNominal.left, rcNominal.top, rcNominal.right, rcNominal.bottom )); rcNominal.left += xOffset; rcNominal.right += xOffset; rcNominal.top += yOffset; rcNominal.bottom += yOffset; } // // Get the photo object // if (!bRepeat) { iPhotoIndex = GetIndexOfNextPrintableItem( iPhotoIndex ); } if ((!IsWizardShuttingDown()) && (iPhotoIndex != -1)) { pPhoto = GetListItem( iPhotoIndex, TRUE ); if (pPhoto) { // // Set up the destination rectangle to draw into // Gdiplus::Rect dest( rcNominal.left, rcNominal.top, rcNominal.right - rcNominal.left, rcNominal.bottom - rcNominal.top ); WIA_TRACE((TEXT("RenderPreview: rcPhoto(%d) is (%d x %d) at (%d, %d)"),i, dest.Width, dest.Height, dest.X, dest.Y )); // // Supply the GDI/GDI+ objects to use... // ro.g = &g; ro.pDest = &dest; // // Save the flags before trying to do throttling... // ULONG uFlagsSave = ro.Flags; // // throttle back to thumbnails if we're on a low-end system // and it's a large file... // if (_bMinimumMemorySystem) { // // We're on a low memory system...is this a // large file? We say anything over 1MB // is large. // if (pPhoto->GetFileSize() > (LONGLONG)LARGE_IMAGE_SIZE) { WIA_TRACE((TEXT("RenderPreview: throttling back to thumbnail data because not enough memory!"))); // // Clear out old render quality flags // ro.Flags &= (~RF_QUALITY_FLAGS_MASK); ro.Flags |= RF_USE_THUMBNAIL_DATA; } } // // Is this a really large file? We say anything // greater than 5MB is really large // if (pPhoto->GetFileSize() > (LONGLONG)REALLY_LARGE_IMAGE_SIZE) { // // Unless we have a really large memory // system, then throttle back on this file // and only show the thumbnail // if (!_bLargeMemorySystem) { WIA_TRACE((TEXT("RenderPreview: throttling back to thumbnail data because of really large file!"))); // // Clear out old render quality flags // ro.Flags &= (~RF_QUALITY_FLAGS_MASK); ro.Flags |= RF_USE_THUMBNAIL_DATA; } } // // Now that we have everything set up, try to draw the image... // do { // // Draw the image! // hr = pPhoto->Render( &ro ); if (FAILED(hr)) { iRes = ShowError( NULL, hr, 0, TRUE, pPhoto->GetPIDL() ); hr = S_FALSE; } else { iRes = IDCONTINUE; } } while ( iRes == IDTRYAGAIN ); // // Restore flags... // ro.Flags = uFlagsSave; // // Print the filename if warranted // if (bPrintFilename) { _RenderFilenameOfPhoto( &g, &rcNominal, pPhoto ); } } else { hr = E_FAIL; break; } if (!bRepeat) { iPhotoIndex++; } } } } // // Last -- draw a dashed rectangle that represents // the printable area on the bitmap if the template // won't fit. // if ((ro.Dim.NominalPageOffset.cx < 0) || (ro.Dim.NominalPageOffset.cy < 0)) { //Gdiplus::Pen DashedPen( black, (Gdiplus::REAL)1.0 ); //DashedPen.SetDashStyle( Gdiplus::DashStyleDash ); Gdiplus::Color InsideColor(255,180,180,180); Gdiplus::Pen InsidePen( InsideColor ); g.DrawRectangle( &InsidePen, clip ); } } else { WIA_ERROR((TEXT("RenderPreview: couldn't get a Graphics from the bmp, Status = %d"),g.GetLastStatus())); } } else { if (hbmp) { DeleteObject(hbmp); hbmp = NULL; } WIA_ERROR((TEXT("RenderPreview: couldn't create DIB section"))); } if (hdc) { DeleteDC( hdc ); } return hbmp; }