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
103 KiB
3419 lines
103 KiB
/*****************************************************************************
|
|
*
|
|
* (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...
|
|
|
|
<Notes>
|
|
|
|
*****************************************************************************/
|
|
|
|
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
|
|
|
|
<Notes>
|
|
|
|
*****************************************************************************/
|
|
|
|
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_PTR 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<HICON>(LoadImage( g_hInst, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR ));
|
|
_hLargeIcon = reinterpret_cast<HICON>(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<IXMLDOMDocument> 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
|
|
|
|
<Notes>
|
|
|
|
*****************************************************************************/
|
|
|
|
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<IShellFolder> 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; i<iCount; i++)
|
|
{
|
|
pListItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i);
|
|
|
|
if (pListItem)
|
|
{
|
|
if (pListItem->IsCopyItem())
|
|
{
|
|
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; i<iCount; i++)
|
|
{
|
|
pListItem = (CListItem *)DPA_FastGetPtr(_hdpaItems,i);
|
|
|
|
if (pListItem)
|
|
{
|
|
//
|
|
// Add this item to the new list...
|
|
//
|
|
|
|
INT iRes = DPA_AppendPtr( hdpaNew, (LPVOID)pListItem );
|
|
if (iRes != -1)
|
|
{
|
|
WIA_TRACE((TEXT("CWizardInfoBlob::AddCopiesOfPhotos -- root item 0x%x added to new list"),pListItem));
|
|
|
|
|
|
//
|
|
// Now, add n-1 copies. We do n-1 because we've
|
|
// already added the root item one.
|
|
//
|
|
|
|
for (UINT uCopy = 1; uCopy < uCopies; uCopy++)
|
|
{
|
|
pListItemCopy = new CListItem( pListItem->GetSubItem(), 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<INamespaceWalk> 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<IShellFolder> 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;i<cidl;i++)
|
|
{
|
|
AddPhoto(aidl[i]);
|
|
}
|
|
_uItemsInInitialSelection = bSelectAll?0:1;
|
|
|
|
//
|
|
// If there was only one item passed to us, then force select
|
|
// all on by default (even if it's a multi-frame image)
|
|
//
|
|
|
|
if (cidl == 1)
|
|
{
|
|
_bForceSelectAll = TRUE;
|
|
}
|
|
|
|
_bAllPicturesAdded = TRUE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
|
|
CWizardInfoBlob::AddPhoto
|
|
|
|
Creates a CListItem for the given fully qualified pidl to the speified photo,
|
|
and then adds it to the HDPA.
|
|
|
|
*****************************************************************************/
|
|
|
|
HRESULT CWizardInfoBlob::AddPhoto( LPITEMIDLIST pidlFull )
|
|
{
|
|
WIA_PUSH_FUNCTION_MASK((TRACE_WIZ_INFO_BLOB, TEXT("CWizardInfoBlob::AddPhoto()") ));
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CAutoCriticalSection lock(_csItems);
|
|
|
|
if (!_hdpaItems)
|
|
{
|
|
_hdpaItems = DPA_Create(DEFAULT_DPA_SIZE);
|
|
}
|
|
|
|
if (_hdpaItems)
|
|
{
|
|
//
|
|
// Next, create CPhotoItem for this pidl.. and store in the DPA...
|
|
//
|
|
|
|
BOOL bItemAddedToDPA = FALSE;
|
|
CPhotoItem * pItem = new CPhotoItem( pidlFull );
|
|
|
|
if (pItem)
|
|
{
|
|
//
|
|
// Got the photo item, now create a list item for each page...
|
|
//
|
|
|
|
LONG lFrames;
|
|
hr = pItem->GetImageFrameCount(&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; i<iCountOfItems; i++)
|
|
{
|
|
pListItem = (CListItem *)DPA_FastGetPtr( _hdpaItems, i );
|
|
if (pListItem == pRootItem)
|
|
{
|
|
iIndexOfRootItem = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now walk the copies, if there are any...
|
|
//
|
|
|
|
if (iIndexOfRootItem != -1)
|
|
{
|
|
INT iIndexOfFirstCopy = iIndexOfRootItem + 1;
|
|
|
|
if (iIndexOfFirstCopy < iCountOfItems)
|
|
{
|
|
for (INT i=iIndexOfFirstCopy; i < iCountOfItems; i++)
|
|
{
|
|
pListItem = (CListItem *)DPA_FastGetPtr( _hdpaItems, i );
|
|
|
|
//
|
|
// If we get back a NULL item, then bail...
|
|
//
|
|
|
|
if (!pListItem)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we get a new root item, then we have traversed all the
|
|
// copies, so bail...
|
|
//
|
|
|
|
if (!pListItem->IsCopyItem())
|
|
{
|
|
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 (pTemplate && (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;
|
|
|
|
}
|