root/third_party/wtl/include/atlprint.h

/* [<][>][^][v][top][bottom][index][help] */
// Windows Template Library - WTL version 8.0
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// This file is a part of the Windows Template Library.
// The use and distribution terms for this software are covered by the
// Microsoft Permissive License (Ms-PL) which can be found in the file
// Ms-PL.txt at the root of this distribution.

#ifndef __ATLPRINT_H__
#define __ATLPRINT_H__

#pragma once

#ifndef __cplusplus
        #error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifdef _WIN32_WCE
        #error atlprint.h is not supported on Windows CE
#endif

#ifndef __ATLAPP_H__
        #error atlprint.h requires atlapp.h to be included first
#endif

#ifndef __ATLWIN_H__
        #error atlprint.h requires atlwin.h to be included first
#endif


///////////////////////////////////////////////////////////////////////////////
// Classes in this file:
//
// CPrinterInfo<t_nInfo>
// CPrinterT<t_bManaged>
// CDevModeT<t_bManaged>
// CPrinterDC
// CPrintJobInfo
// CPrintJob
// CPrintPreview
// CPrintPreviewWindowImpl<T, TBase, TWinTraits>
// CPrintPreviewWindow
// CZoomPrintPreviewWindowImpl<T, TBase, TWinTraits>
// CZoomPrintPreviewWindow

namespace WTL
{

///////////////////////////////////////////////////////////////////////////////
// CPrinterInfo - This class wraps all of the PRINTER_INFO_* structures
//                and provided by ::GetPrinter.

template <unsigned int t_nInfo>
class _printer_info
{
public:
        typedef void infotype;
};

template <> class _printer_info<1> { public: typedef PRINTER_INFO_1 infotype; };
template <> class _printer_info<2> { public: typedef PRINTER_INFO_2 infotype; };
template <> class _printer_info<3> { public: typedef PRINTER_INFO_3 infotype; };
template <> class _printer_info<4> { public: typedef PRINTER_INFO_4 infotype; };
template <> class _printer_info<5> { public: typedef PRINTER_INFO_5 infotype; };
template <> class _printer_info<6> { public: typedef PRINTER_INFO_6 infotype; };
template <> class _printer_info<7> { public: typedef PRINTER_INFO_7 infotype; };
// these are not in the old (vc6.0) headers
#ifdef _ATL_USE_NEW_PRINTER_INFO
template <> class _printer_info<8> { public: typedef PRINTER_INFO_8 infotype; };
template <> class _printer_info<9> { public: typedef PRINTER_INFO_9 infotype; };
#endif // _ATL_USE_NEW_PRINTER_INFO


template <unsigned int t_nInfo>
class CPrinterInfo
{
public:
// Data members
        typename _printer_info<t_nInfo>::infotype* m_pi;

// Constructor/destructor
        CPrinterInfo() : m_pi(NULL)
        { }

        CPrinterInfo(HANDLE hPrinter) : m_pi(NULL)
        {
                GetPrinterInfo(hPrinter);
        }

        ~CPrinterInfo()
        {
                Cleanup();
        }

// Operations
        bool GetPrinterInfo(HANDLE hPrinter)
        {
                Cleanup();
                return GetPrinterInfoHelper(hPrinter, (BYTE**)&m_pi, t_nInfo);
        }

// Implementation
        void Cleanup()
        {
                delete [] (BYTE*)m_pi;
                m_pi = NULL;
        }

        static bool GetPrinterInfoHelper(HANDLE hPrinter, BYTE** pi, int nIndex)
        {
                ATLASSERT(pi != NULL);
                DWORD dw = 0;
                BYTE* pb = NULL;
                ::GetPrinter(hPrinter, nIndex, NULL, 0, &dw);
                if (dw > 0)
                {
                        ATLTRY(pb = new BYTE[dw]);
                        if (pb != NULL)
                        {
                                memset(pb, 0, dw);
                                DWORD dwNew;
                                if (!::GetPrinter(hPrinter, nIndex, pb, dw, &dwNew))
                                {
                                        delete [] pb;
                                        pb = NULL;
                                }
                        }
                }
                *pi = pb;
                return (pb != NULL);
        }
};


///////////////////////////////////////////////////////////////////////////////
// CPrinter - Wrapper class for a HANDLE to a printer

template <bool t_bManaged>
class CPrinterT
{
public:
// Data members
        HANDLE m_hPrinter;

// Constructor/destructor
        CPrinterT(HANDLE hPrinter = NULL) : m_hPrinter(hPrinter)
        { }

        ~CPrinterT()
        {
                ClosePrinter();
        }

// Operations
        CPrinterT& operator =(HANDLE hPrinter)
        {
                if (hPrinter != m_hPrinter)
                {
                        ClosePrinter();
                        m_hPrinter = hPrinter;
                }
                return *this;
        }

        bool IsNull() const { return (m_hPrinter == NULL); }

        bool OpenPrinter(HANDLE hDevNames, const DEVMODE* pDevMode = NULL)
        {
                bool b = false;
                DEVNAMES* pdn = (DEVNAMES*)::GlobalLock(hDevNames);
                if (pdn != NULL)
                {
                        LPTSTR lpszPrinterName = (LPTSTR)pdn + pdn->wDeviceOffset;
                        b = OpenPrinter(lpszPrinterName, pDevMode);
                        ::GlobalUnlock(hDevNames);
                }
                return b;
        }

        bool OpenPrinter(LPCTSTR lpszPrinterName, const DEVMODE* pDevMode = NULL)
        {
                ClosePrinter();
                PRINTER_DEFAULTS pdefs = { NULL, (DEVMODE*)pDevMode, PRINTER_ACCESS_USE };
                ::OpenPrinter((LPTSTR) lpszPrinterName, &m_hPrinter, (pDevMode == NULL) ? NULL : &pdefs);

                return (m_hPrinter != NULL);
        }

        bool OpenPrinter(LPCTSTR lpszPrinterName, PRINTER_DEFAULTS* pprintdefs)
        {
                ClosePrinter();
                ::OpenPrinter((LPTSTR) lpszPrinterName, &m_hPrinter, pprintdefs);
                return (m_hPrinter != NULL);
        }

        bool OpenDefaultPrinter(const DEVMODE* pDevMode = NULL)
        {
                ClosePrinter();
                const int cchBuff = 512;
                TCHAR buffer[cchBuff];
                buffer[0] = 0;
                ::GetProfileString(_T("windows"), _T("device"), _T(",,,"), buffer, cchBuff);
                int nLen = lstrlen(buffer);
                if (nLen != 0)
                {
                        LPTSTR lpsz = buffer;
                        while (*lpsz)
                        {
                                if (*lpsz == _T(','))
                                {
                                        *lpsz = 0;
                                        break;
                                }
                                lpsz = CharNext(lpsz);
                        }
                        PRINTER_DEFAULTS pdefs = { NULL, (DEVMODE*)pDevMode, PRINTER_ACCESS_USE };
                        ::OpenPrinter(buffer, &m_hPrinter, (pDevMode == NULL) ? NULL : &pdefs);
                }
                return m_hPrinter != NULL;
        }

        void ClosePrinter()
        {
                if (m_hPrinter != NULL)
                {
                        if (t_bManaged)
                                ::ClosePrinter(m_hPrinter);
                        m_hPrinter = NULL;
                }
        }

        bool PrinterProperties(HWND hWnd = NULL)
        {
                if (hWnd == NULL)
                        hWnd = ::GetActiveWindow();
                return !!::PrinterProperties(hWnd, m_hPrinter);
        }

        HANDLE CopyToHDEVNAMES() const
        {
                HANDLE h = NULL;
                CPrinterInfo<5> pinfon5;
                CPrinterInfo<2> pinfon2;
                LPTSTR lpszPrinterName = NULL;
                // Some printers fail for PRINTER_INFO_5 in some situations
                if (pinfon5.GetPrinterInfo(m_hPrinter))
                        lpszPrinterName = pinfon5.m_pi->pPrinterName;
                else if (pinfon2.GetPrinterInfo(m_hPrinter))
                        lpszPrinterName = pinfon2.m_pi->pPrinterName;
                if (lpszPrinterName != NULL)
                {
                        int nLen = sizeof(DEVNAMES) + (lstrlen(lpszPrinterName) + 1) * sizeof(TCHAR);
                        h = ::GlobalAlloc(GMEM_MOVEABLE, nLen);
                        BYTE* pv = (BYTE*)::GlobalLock(h);
                        DEVNAMES* pdev = (DEVNAMES*)pv;
                        if (pv != NULL)
                        {
                                memset(pv, 0, nLen);
                                pdev->wDeviceOffset = sizeof(DEVNAMES) / sizeof(TCHAR);
                                pv = pv + sizeof(DEVNAMES); // now points to end
                                SecureHelper::strcpy_x((LPTSTR)pv, lstrlen(lpszPrinterName) + 1, lpszPrinterName);
                                ::GlobalUnlock(h);
                        }
                }
                return h;
        }

        HDC CreatePrinterDC(const DEVMODE* pdm = NULL) const
        {
                CPrinterInfo<5> pinfo5;
                CPrinterInfo<2> pinfo2;
                HDC hDC = NULL;
                LPTSTR lpszPrinterName = NULL;
                // Some printers fail for PRINTER_INFO_5 in some situations
                if (pinfo5.GetPrinterInfo(m_hPrinter))
                        lpszPrinterName = pinfo5.m_pi->pPrinterName;
                else if (pinfo2.GetPrinterInfo(m_hPrinter))
                        lpszPrinterName = pinfo2.m_pi->pPrinterName;
                if (lpszPrinterName != NULL)
                        hDC = ::CreateDC(NULL, lpszPrinterName, NULL, pdm);
                return hDC;
        }

        HDC CreatePrinterIC(const DEVMODE* pdm = NULL) const
        {
                CPrinterInfo<5> pinfo5;
                CPrinterInfo<2> pinfo2;
                HDC hDC = NULL;
                LPTSTR lpszPrinterName = NULL;
                // Some printers fail for PRINTER_INFO_5 in some situations
                if (pinfo5.GetPrinterInfo(m_hPrinter))
                        lpszPrinterName = pinfo5.m_pi->pPrinterName;
                else if (pinfo2.GetPrinterInfo(m_hPrinter))
                        lpszPrinterName = pinfo2.m_pi->pPrinterName;
                if (lpszPrinterName != NULL)
                        hDC = ::CreateIC(NULL, lpszPrinterName, NULL, pdm);
                return hDC;
        }

        void Attach(HANDLE hPrinter)
        {
                ClosePrinter();
                m_hPrinter = hPrinter;
        }

        HANDLE Detach()
        {
                HANDLE hPrinter = m_hPrinter;
                m_hPrinter = NULL;
                return hPrinter;
        }

        operator HANDLE() const { return m_hPrinter; }
};

typedef CPrinterT<false>   CPrinterHandle;
typedef CPrinterT<true>    CPrinter;


///////////////////////////////////////////////////////////////////////////////
// CDevMode - Wrapper class for DEVMODE

template <bool t_bManaged>
class CDevModeT
{
public:
// Data members
        HANDLE m_hDevMode;
        DEVMODE* m_pDevMode;

// Constructor/destructor
        CDevModeT(HANDLE hDevMode = NULL) : m_hDevMode(hDevMode)
        {
                m_pDevMode = (m_hDevMode != NULL) ? (DEVMODE*)::GlobalLock(m_hDevMode) : NULL;
        }

        ~CDevModeT()
        {
                Cleanup();
        }

// Operations
        CDevModeT<t_bManaged>& operator =(HANDLE hDevMode)
        {
                Attach(hDevMode);
                return *this;
        }

        void Attach(HANDLE hDevModeNew)
        {
                Cleanup();
                m_hDevMode = hDevModeNew;
                m_pDevMode = (m_hDevMode != NULL) ? (DEVMODE*)::GlobalLock(m_hDevMode) : NULL;
        }

        HANDLE Detach()
        {
                if (m_hDevMode != NULL)
                        ::GlobalUnlock(m_hDevMode);
                HANDLE hDevMode = m_hDevMode;
                m_hDevMode = NULL;
                return hDevMode;
        }

        bool IsNull() const { return (m_hDevMode == NULL); }

        bool CopyFromPrinter(HANDLE hPrinter)
        {
                CPrinterInfo<2> pinfo;
                bool b = pinfo.GetPrinterInfo(hPrinter);
                if (b)
                 b = CopyFromDEVMODE(pinfo.m_pi->pDevMode);
                return b;
        }

        bool CopyFromDEVMODE(const DEVMODE* pdm)
        {
                if (pdm == NULL)
                        return false;
                int nSize = pdm->dmSize + pdm->dmDriverExtra;
                HANDLE h = ::GlobalAlloc(GMEM_MOVEABLE, nSize);
                if (h != NULL)
                {
                        void* p = ::GlobalLock(h);
                        SecureHelper::memcpy_x(p, nSize, pdm, nSize);
                        ::GlobalUnlock(h);
                }
                Attach(h);
                return (h != NULL);
        }

        bool CopyFromHDEVMODE(HANDLE hdm)
        {
                bool b = false;
                if (hdm != NULL)
                {
                        DEVMODE* pdm = (DEVMODE*)::GlobalLock(hdm);
                        b = CopyFromDEVMODE(pdm);
                        ::GlobalUnlock(hdm);
                }
                return b;
        }

        HANDLE CopyToHDEVMODE()
        {
                if ((m_hDevMode == NULL) || (m_pDevMode == NULL))
                        return NULL;
                int nSize = m_pDevMode->dmSize + m_pDevMode->dmDriverExtra;
                HANDLE h = ::GlobalAlloc(GMEM_MOVEABLE, nSize);
                if (h != NULL)
                {
                        void* p = ::GlobalLock(h);
                        SecureHelper::memcpy_x(p, nSize, m_pDevMode, nSize);
                        ::GlobalUnlock(h);
                }
                return h;
        }

        // If this devmode was for another printer, this will create a new devmode
        // based on the existing devmode, but retargeted at the new printer
        bool UpdateForNewPrinter(HANDLE hPrinter)
        {
                bool bRet = false;
                LONG nLen = ::DocumentProperties(NULL, hPrinter, NULL, NULL, NULL, 0);
                CTempBuffer<DEVMODE, _WTL_STACK_ALLOC_THRESHOLD> buff;
                DEVMODE* pdm = buff.AllocateBytes(nLen);
                if(pdm != NULL)
                {
                        memset(pdm, 0, nLen);
                        LONG l = ::DocumentProperties(NULL, hPrinter, NULL, pdm, m_pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);
                        if (l == IDOK)
                                bRet = CopyFromDEVMODE(pdm);
                }

                return bRet;
        }

        bool DocumentProperties(HANDLE hPrinter, HWND hWnd = NULL)
        {
                CPrinterInfo<1> pi;
                pi.GetPrinterInfo(hPrinter);
                if (hWnd == NULL)
                        hWnd = ::GetActiveWindow();

                bool bRet = false;
                LONG nLen = ::DocumentProperties(hWnd, hPrinter, pi.m_pi->pName, NULL, NULL, 0);
                CTempBuffer<DEVMODE, _WTL_STACK_ALLOC_THRESHOLD> buff;
                DEVMODE* pdm = buff.AllocateBytes(nLen);
                if(pdm != NULL)
                {
                        memset(pdm, 0, nLen);
                        LONG l = ::DocumentProperties(hWnd, hPrinter, pi.m_pi->pName, pdm, m_pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER | DM_PROMPT);
                        if (l == IDOK)
                                bRet = CopyFromDEVMODE(pdm);
                }

                return bRet;
        }

        operator HANDLE() const { return m_hDevMode; }

        operator DEVMODE*() const { return m_pDevMode; }

// Implementation
        void Cleanup()
        {
                if (m_hDevMode != NULL)
                {
                        ::GlobalUnlock(m_hDevMode);
                        if(t_bManaged)
                                ::GlobalFree(m_hDevMode);
                        m_hDevMode = NULL;
                }
        }
};

typedef CDevModeT<false>   CDevModeHandle;
typedef CDevModeT<true>    CDevMode;


///////////////////////////////////////////////////////////////////////////////
// CPrinterDC

class CPrinterDC : public CDC
{
public:
// Constructors/destructor
        CPrinterDC()
        {
                CPrinter printer;
                printer.OpenDefaultPrinter();
                Attach(printer.CreatePrinterDC());
                ATLASSERT(m_hDC != NULL);
        }

        CPrinterDC(HANDLE hPrinter, const DEVMODE* pdm = NULL)
        {
                CPrinterHandle p;
                p.Attach(hPrinter);
                Attach(p.CreatePrinterDC(pdm));
                ATLASSERT(m_hDC != NULL);
        }

        ~CPrinterDC()
        {
                DeleteDC();
        }
};


///////////////////////////////////////////////////////////////////////////////
// CPrintJob - Wraps a set of tasks for a specific printer (StartDoc/EndDoc)
//             Handles aborting, background printing

// Defines callbacks used by CPrintJob (not a COM interface)
class ATL_NO_VTABLE IPrintJobInfo
{
public:
        virtual void BeginPrintJob(HDC hDC) = 0;                // allocate handles needed, etc.
        virtual void EndPrintJob(HDC hDC, bool bAborted) = 0;   // free handles, etc.
        virtual void PrePrintPage(UINT nPage, HDC hDC) = 0;
        virtual bool PrintPage(UINT nPage, HDC hDC) = 0;
        virtual void PostPrintPage(UINT nPage, HDC hDC) = 0;
        // If you want per page devmodes, return the DEVMODE* to use for nPage.
        // You can optimize by only returning a new DEVMODE* when it is different
        // from the one for nLastPage, otherwise return NULL.
        // When nLastPage==0, the current DEVMODE* will be the default passed to
        // StartPrintJob.
        // Note: During print preview, nLastPage will always be "0".
        virtual DEVMODE* GetNewDevModeForPage(UINT nLastPage, UINT nPage) = 0;
        virtual bool IsValidPage(UINT nPage) = 0;
};

// Provides a default implementatin for IPrintJobInfo
// Typically, MI'd into a document or view class
class ATL_NO_VTABLE CPrintJobInfo : public IPrintJobInfo
{
public:
        virtual void BeginPrintJob(HDC /*hDC*/)   // allocate handles needed, etc
        {
        }

        virtual void EndPrintJob(HDC /*hDC*/, bool /*bAborted*/)   // free handles, etc
        {
        }

        virtual void PrePrintPage(UINT /*nPage*/, HDC hDC)
        {
                m_nPJState = ::SaveDC(hDC);
        }

        virtual bool PrintPage(UINT /*nPage*/, HDC /*hDC*/) = 0;

        virtual void PostPrintPage(UINT /*nPage*/, HDC hDC)
        {
                RestoreDC(hDC, m_nPJState);
        }

        virtual DEVMODE* GetNewDevModeForPage(UINT /*nLastPage*/, UINT /*nPage*/)
        {
                return NULL;
        }

        virtual bool IsValidPage(UINT /*nPage*/)
        {
                return true;
        }

// Implementation - data
        int m_nPJState;
};


class CPrintJob
{
public:
// Data members
        CPrinterHandle m_printer;
        IPrintJobInfo* m_pInfo;
        DEVMODE* m_pDefDevMode;
        DOCINFO m_docinfo;
        int m_nJobID;
        bool m_bCancel;
        bool m_bComplete;
        unsigned long m_nStartPage;
        unsigned long m_nEndPage;

// Constructor/destructor
        CPrintJob() : m_nJobID(0), m_bCancel(false), m_bComplete(true)
        { }

        ~CPrintJob()
        {
                ATLASSERT(IsJobComplete()); // premature destruction?
        }

// Operations
        bool IsJobComplete() const
        {
                return m_bComplete;
        }

        bool StartPrintJob(bool bBackground, HANDLE hPrinter, DEVMODE* pDefaultDevMode,
                        IPrintJobInfo* pInfo, LPCTSTR lpszDocName, 
                        unsigned long nStartPage, unsigned long nEndPage,
                        bool bPrintToFile = false, LPCTSTR lpstrOutputFile = NULL)
        {
                ATLASSERT(m_bComplete); // previous job not done yet?
                if (pInfo == NULL)
                        return false;

                memset(&m_docinfo, 0, sizeof(m_docinfo));
                m_docinfo.cbSize = sizeof(m_docinfo);
                m_docinfo.lpszDocName = lpszDocName;
                m_pInfo = pInfo;
                m_nStartPage = nStartPage;
                m_nEndPage = nEndPage;
                m_printer.Attach(hPrinter);
                m_pDefDevMode = pDefaultDevMode;
                m_bComplete = false;

                if(bPrintToFile)
                        m_docinfo.lpszOutput = (lpstrOutputFile != NULL) ? lpstrOutputFile : _T("FILE:");

                if (!bBackground)
                {
                        m_bComplete = true;
                        return StartHelper();
                }

                // Create a thread and return
                DWORD dwThreadID = 0;
#if !defined(_ATL_MIN_CRT) && defined(_MT)
                HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, (UINT (WINAPI*)(void*))StartProc, this, 0, (UINT*)&dwThreadID);
#else
                HANDLE hThread = ::CreateThread(NULL, 0, StartProc, (void*)this, 0, &dwThreadID);
#endif
                if (hThread == NULL)
                        return false;

                ::CloseHandle(hThread);

                return true;
        }

// Implementation
        static DWORD WINAPI StartProc(void* p)
        {
                CPrintJob* pThis = (CPrintJob*)p;
                pThis->StartHelper();
                pThis->m_bComplete = true;
                return 0;
        }

        bool StartHelper()
        {
                CDC dcPrinter;
                dcPrinter.Attach(m_printer.CreatePrinterDC(m_pDefDevMode));
                if (dcPrinter.IsNull())
                        return false;
                        
                m_nJobID = ::StartDoc(dcPrinter, &m_docinfo);
                if (m_nJobID <= 0)
                        return false;

                m_pInfo->BeginPrintJob(dcPrinter);

                // print all the pages now
                unsigned long nLastPage = 0;
                for (unsigned long nPage = m_nStartPage; nPage <= m_nEndPage; nPage++)
                {
                        if (!m_pInfo->IsValidPage(nPage))
                                break;
                        DEVMODE* pdm = m_pInfo->GetNewDevModeForPage(nLastPage, nPage);
                        if (pdm != NULL)
                                dcPrinter.ResetDC(pdm);
                        dcPrinter.StartPage();
                        m_pInfo->PrePrintPage(nPage, dcPrinter);
                        if (!m_pInfo->PrintPage(nPage, dcPrinter))
                                m_bCancel = true;
                        m_pInfo->PostPrintPage(nPage, dcPrinter);
                        dcPrinter.EndPage();
                        if (m_bCancel)
                                break;
                        nLastPage = nPage;
                }

                m_pInfo->EndPrintJob(dcPrinter, m_bCancel);
                if (m_bCancel)
                        ::AbortDoc(dcPrinter);
                else
                        ::EndDoc(dcPrinter);
                m_nJobID = 0;
                return true;
        }

        // Cancels a print job. Can be called asynchronously.
        void CancelPrintJob()
        {
                m_bCancel = true;
        }
};


///////////////////////////////////////////////////////////////////////////////
// CPrintPreview - Adds print preview support to an existing window

class CPrintPreview
{
public:
// Data members
        IPrintJobInfo* m_pInfo;
        CPrinterHandle m_printer;
        CEnhMetaFile m_meta;
        DEVMODE* m_pDefDevMode;
        DEVMODE* m_pCurDevMode;
        SIZE m_sizeCurPhysOffset;

// Constructor
        CPrintPreview() : m_pInfo(NULL), m_pDefDevMode(NULL), m_pCurDevMode(NULL)
        {
                m_sizeCurPhysOffset.cx = 0;
                m_sizeCurPhysOffset.cy = 0;
        }

// Operations
        void SetPrintPreviewInfo(HANDLE hPrinter, DEVMODE* pDefaultDevMode, IPrintJobInfo* pji)
        {
                m_printer.Attach(hPrinter);
                m_pDefDevMode = pDefaultDevMode;
                m_pInfo = pji;
                m_nCurPage = 0;
                m_pCurDevMode = NULL;
        }

        void SetEnhMetaFile(HENHMETAFILE hEMF)
        {
                m_meta = hEMF;
        }

        void SetPage(int nPage)
        {
                if (!m_pInfo->IsValidPage(nPage))
                        return;
                m_nCurPage = nPage;
                m_pCurDevMode = m_pInfo->GetNewDevModeForPage(0, nPage);
                if (m_pCurDevMode == NULL)
                        m_pCurDevMode = m_pDefDevMode;
                CDC dcPrinter = m_printer.CreatePrinterDC(m_pCurDevMode);

                int iWidth = dcPrinter.GetDeviceCaps(PHYSICALWIDTH); 
                int iHeight = dcPrinter.GetDeviceCaps(PHYSICALHEIGHT); 
                int nLogx = dcPrinter.GetDeviceCaps(LOGPIXELSX);
                int nLogy = dcPrinter.GetDeviceCaps(LOGPIXELSY);

                RECT rcMM = { 0, 0, ::MulDiv(iWidth, 2540, nLogx), ::MulDiv(iHeight, 2540, nLogy) };

                m_sizeCurPhysOffset.cx = dcPrinter.GetDeviceCaps(PHYSICALOFFSETX);
                m_sizeCurPhysOffset.cy = dcPrinter.GetDeviceCaps(PHYSICALOFFSETY);
                
                CEnhMetaFileDC dcMeta(dcPrinter, &rcMM);
                m_pInfo->PrePrintPage(nPage, dcMeta);
                m_pInfo->PrintPage(nPage, dcMeta);
                m_pInfo->PostPrintPage(nPage, dcMeta);
                m_meta.Attach(dcMeta.Close());
        }

        void GetPageRect(RECT& rc, LPRECT prc)
        {
                int x1 = rc.right-rc.left;
                int y1 = rc.bottom - rc.top;
                if ((x1 < 0) || (y1 < 0))
                        return;

                CEnhMetaFileInfo emfinfo(m_meta);
                ENHMETAHEADER* pmh = emfinfo.GetEnhMetaFileHeader();

                // Compute whether we are OK vertically or horizontally
                int x2 = pmh->szlDevice.cx;
                int y2 = pmh->szlDevice.cy;
                int y1p = MulDiv(x1, y2, x2);
                int x1p = MulDiv(y1, x2, y2);
                ATLASSERT((x1p <= x1) || (y1p <= y1));
                if (x1p <= x1)
                {
                        prc->left = rc.left + (x1 - x1p) / 2;
                        prc->right = prc->left + x1p;
                        prc->top = rc.top;
                        prc->bottom = rc.bottom;
                }
                else
                {
                        prc->left = rc.left;
                        prc->right = rc.right;
                        prc->top = rc.top + (y1 - y1p) / 2;
                        prc->bottom = prc->top + y1p;
                }
        }

// Painting helpers
        void DoPaint(CDCHandle dc)
        {
                // this one is not used
        }

        void DoPaint(CDCHandle dc, RECT& rc)
        {
                CEnhMetaFileInfo emfinfo(m_meta);
                ENHMETAHEADER* pmh = emfinfo.GetEnhMetaFileHeader();
                int nOffsetX = MulDiv(m_sizeCurPhysOffset.cx, rc.right-rc.left, pmh->szlDevice.cx);
                int nOffsetY = MulDiv(m_sizeCurPhysOffset.cy, rc.bottom-rc.top, pmh->szlDevice.cy);

                dc.OffsetWindowOrg(-nOffsetX, -nOffsetY);
                dc.PlayMetaFile(m_meta, &rc);
        }

// Implementation - data
        int m_nCurPage;
};


///////////////////////////////////////////////////////////////////////////////
// CPrintPreviewWindow - Implements a print preview window

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CPrintPreviewWindowImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>, public CPrintPreview
{
public:
        DECLARE_WND_CLASS_EX(NULL, CS_VREDRAW | CS_HREDRAW, -1)

        enum { m_cxOffset = 10, m_cyOffset = 10 };

// Constructor
        CPrintPreviewWindowImpl() : m_nMaxPage(0), m_nMinPage(0)
        { }

// Operations
        void SetPrintPreviewInfo(HANDLE hPrinter, DEVMODE* pDefaultDevMode, 
                IPrintJobInfo* pji, int nMinPage, int nMaxPage)
        {
                CPrintPreview::SetPrintPreviewInfo(hPrinter, pDefaultDevMode, pji);
                m_nMinPage = nMinPage;
                m_nMaxPage = nMaxPage;
        }

        bool NextPage()
        {
                if (m_nCurPage == m_nMaxPage)
                        return false;
                SetPage(m_nCurPage + 1);
                Invalidate();
                return true;
        }

        bool PrevPage()
        {
                if (m_nCurPage == m_nMinPage)
                        return false;
                if (m_nCurPage == 0)
                        return false;
                SetPage(m_nCurPage - 1);
                Invalidate();
                return true;
        }

// Message map and handlers
        BEGIN_MSG_MAP(CPrintPreviewWindowImpl)
                MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
                MESSAGE_HANDLER(WM_PAINT, OnPaint)
                MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
        END_MSG_MAP()

        LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                return 1;   // no need for the background
        }

        LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                RECT rc = { 0 };

                if(wParam != NULL)
                {
                        pT->DoPrePaint((HDC)wParam, rc);
                        pT->DoPaint((HDC)wParam, rc);
                }
                else
                {
                        CPaintDC dc(m_hWnd);
                        pT->DoPrePaint(dc.m_hDC, rc);
                        pT->DoPaint(dc.m_hDC, rc);
                }

                return 0;
        }

// Painting helper
        void DoPrePaint(CDCHandle dc, RECT& rc)
        {
                RECT rcClient = { 0 };
                GetClientRect(&rcClient);
                RECT rcArea = rcClient;
                T* pT = static_cast<T*>(this);
                pT;   // avoid level 4 warning
                ::InflateRect(&rcArea, -pT->m_cxOffset, -pT->m_cyOffset);
                if (rcArea.left > rcArea.right)
                        rcArea.right = rcArea.left;
                if (rcArea.top > rcArea.bottom)
                        rcArea.bottom = rcArea.top;
                GetPageRect(rcArea, &rc);
                CRgn rgn1, rgn2;
                rgn1.CreateRectRgnIndirect(&rc);
                rgn2.CreateRectRgnIndirect(&rcClient);
                rgn2.CombineRgn(rgn1, RGN_DIFF);
                dc.SelectClipRgn(rgn2);
                dc.FillRect(&rcClient, COLOR_BTNSHADOW);
                dc.SelectClipRgn(NULL);
                dc.FillRect(&rc, (HBRUSH)::GetStockObject(WHITE_BRUSH));
        }

// Implementation - data
        int m_nMinPage;
        int m_nMaxPage;
};


class CPrintPreviewWindow : public CPrintPreviewWindowImpl<CPrintPreviewWindow>
{
public:
        DECLARE_WND_CLASS_EX(_T("WTL_PrintPreview"), CS_VREDRAW | CS_HREDRAW, -1)
};


///////////////////////////////////////////////////////////////////////////////
// CZoomPrintPreviewWindowImpl - Implements print preview window with zooming

#ifdef __ATLSCRL_H__

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE CZoomPrintPreviewWindowImpl : public CPrintPreviewWindowImpl< T, TBase, TWinTraits >, public CZoomScrollImpl< T >
{
public:
        bool m_bSized;

        CZoomPrintPreviewWindowImpl()  
        {
                SetScrollExtendedStyle(SCRL_DISABLENOSCROLL);
                InitZoom();
        }

        // should be called to reset data members before recreating window 
        void InitZoom()
        {
                m_bSized = false;       
                m_nZoomMode = ZOOMMODE_OFF;
                m_fZoomScaleMin = 1.0;
                m_fZoomScale = 1.0;
        }

        BEGIN_MSG_MAP(CZoomPrintPreviewWindowImpl)
                MESSAGE_HANDLER(WM_SETCURSOR, CZoomScrollImpl< T >::OnSetCursor)
                MESSAGE_HANDLER(WM_VSCROLL, CScrollImpl< T >::OnVScroll)
                MESSAGE_HANDLER(WM_HSCROLL, CScrollImpl< T >::OnHScroll)
                MESSAGE_HANDLER(WM_MOUSEWHEEL, CScrollImpl< T >::OnMouseWheel)
#if !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(m_uMsgMouseWheel, CScrollImpl< T >::OnMouseWheel)
#endif // !((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400))
                MESSAGE_HANDLER(WM_SETTINGCHANGE, CScrollImpl< T >::OnSettingChange)
                MESSAGE_HANDLER(WM_LBUTTONDOWN, CZoomScrollImpl< T >::OnLButtonDown)
                MESSAGE_HANDLER(WM_MOUSEMOVE, CZoomScrollImpl< T >::OnMouseMove)
                MESSAGE_HANDLER(WM_LBUTTONUP, CZoomScrollImpl< T >::OnLButtonUp)
                MESSAGE_HANDLER(WM_CAPTURECHANGED, CZoomScrollImpl< T >::OnCaptureChanged)
                MESSAGE_HANDLER(WM_SIZE, OnSize)
                MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
                MESSAGE_HANDLER(WM_PAINT, OnPaint)
                MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
        ALT_MSG_MAP(1)
                COMMAND_ID_HANDLER(ID_SCROLL_UP, CScrollImpl< T >::OnScrollUp)
                COMMAND_ID_HANDLER(ID_SCROLL_DOWN, CScrollImpl< T >::OnScrollDown)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_UP, CScrollImpl< T >::OnScrollPageUp)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_DOWN, CScrollImpl< T >::OnScrollPageDown)
                COMMAND_ID_HANDLER(ID_SCROLL_TOP, CScrollImpl< T >::OnScrollTop)
                COMMAND_ID_HANDLER(ID_SCROLL_BOTTOM, CScrollImpl< T >::OnScrollBottom)
                COMMAND_ID_HANDLER(ID_SCROLL_LEFT, CScrollImpl< T >::OnScrollLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_RIGHT, CScrollImpl< T >::OnScrollRight)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_LEFT, CScrollImpl< T >::OnScrollPageLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_PAGE_RIGHT, CScrollImpl< T >::OnScrollPageRight)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_LEFT, CScrollImpl< T >::OnScrollAllLeft)
                COMMAND_ID_HANDLER(ID_SCROLL_ALL_RIGHT, CScrollImpl< T >::OnScrollAllRight)
        END_MSG_MAP()
        
        LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
                SIZE sizeClient = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
                POINT ptOffset = m_ptOffset;
                SIZE sizeAll = m_sizeAll;
                SetScrollSize(sizeClient);
                if(sizeAll.cx > 0)
                        ptOffset.x = ::MulDiv(ptOffset.x, m_sizeAll.cx, sizeAll.cx);
                if(sizeAll.cy > 0)
                        ptOffset.y = ::MulDiv(ptOffset.y, m_sizeAll.cy, sizeAll.cy);
                SetScrollOffset(ptOffset);
                CScrollImpl< T >::OnSize(uMsg, wParam, lParam, bHandled);
                if(!m_bSized)
                {
                        m_bSized = true;
                        T* pT = static_cast<T*>(this);
                        pT->ShowScrollBar(SB_HORZ, TRUE);
                        pT->ShowScrollBar(SB_VERT, TRUE);
                }
                return 0;
        }

        LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                return 1;
        }

        LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                RECT rc = { 0 };

                if(wParam != NULL)
                {
                        CDCHandle dc = (HDC)wParam;
                        int nMapModeSav = dc.GetMapMode();
                        dc.SetMapMode(MM_ANISOTROPIC);
                        SIZE szWindowExt = { 0, 0 };
                        dc.SetWindowExt(m_sizeLogAll, &szWindowExt);
                        SIZE szViewportExt = { 0, 0 };
                        dc.SetViewportExt(m_sizeAll, &szViewportExt);
                        POINT ptViewportOrg = { 0, 0 };
                        dc.SetViewportOrg(-m_ptOffset.x, -m_ptOffset.y, &ptViewportOrg);

                        pT->DoPrePaint(dc, rc);
                        pT->DoPaint(dc, rc);

                        dc.SetMapMode(nMapModeSav);
                        dc.SetWindowExt(szWindowExt);
                        dc.SetViewportExt(szViewportExt);
                        dc.SetViewportOrg(ptViewportOrg);
                }
                else
                {
                        CPaintDC dc(pT->m_hWnd);
                        pT->PrepareDC(dc.m_hDC);
                        pT->DoPrePaint(dc.m_hDC, rc);
                        pT->DoPaint(dc.m_hDC, rc);
                }

                return 0;
        }

        // Painting helpers
        void DoPaint(CDCHandle dc)
        {
                // this one is not used
        }

        void DoPrePaint(CDCHandle dc, RECT& rc)
        {
                RECT rcClient;
                GetClientRect(&rcClient);
                RECT rcArea = rcClient;
                T* pT = static_cast<T*>(this);
                pT;   // avoid level 4 warning
                ::InflateRect(&rcArea, -pT->m_cxOffset, -pT->m_cyOffset);
                if (rcArea.left > rcArea.right)
                        rcArea.right = rcArea.left;
                if (rcArea.top > rcArea.bottom)
                        rcArea.bottom = rcArea.top;
                GetPageRect(rcArea, &rc);
                HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW));
                dc.PatBlt(rcClient.left, rcClient.top, rc.left - rcClient.left, rcClient.bottom - rcClient.top, PATCOPY);
                dc.PatBlt(rc.left, rcClient.top, rc.right - rc.left, rc.top - rcClient.top, PATCOPY);
                dc.PatBlt(rc.right, rcClient.top, rcClient.right - rc.right, rcClient.bottom - rcClient.top, PATCOPY);
                dc.PatBlt(rc.left, rc.bottom, rc.right - rc.left, rcClient.bottom - rc.bottom, PATCOPY);
                dc.SelectBrush((HBRUSH)::GetStockObject(WHITE_BRUSH));
                dc.PatBlt(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
                dc.SelectBrush(::GetSysColorBrush(COLOR_3DDKSHADOW));
                dc.PatBlt(rc.right, rc.top + 4, 4, rc.bottom - rc.top, PATCOPY);
                dc.PatBlt(rc.left + 4, rc.bottom, rc.right - rc.left, 4, PATCOPY);
                dc.SelectBrush(hbrOld);
        }

        void DoPaint(CDCHandle dc, RECT& rc)
        {
                CEnhMetaFileInfo emfinfo(m_meta);
                ENHMETAHEADER* pmh = emfinfo.GetEnhMetaFileHeader();
                int nOffsetX = MulDiv(m_sizeCurPhysOffset.cx, rc.right-rc.left, pmh->szlDevice.cx);
                int nOffsetY = MulDiv(m_sizeCurPhysOffset.cy, rc.bottom-rc.top, pmh->szlDevice.cy);

                dc.OffsetWindowOrg(-nOffsetX, -nOffsetY);
                dc.PlayMetaFile(m_meta, &rc);
        }
};

class CZoomPrintPreviewWindow : public CZoomPrintPreviewWindowImpl<CZoomPrintPreviewWindow>
{
public:
        DECLARE_WND_CLASS_EX(_T("WTL_ZoomPrintPreview"), CS_VREDRAW | CS_HREDRAW, -1)
};

#endif // __ATLSCRL_H__

}; // namespace WTL

#endif // __ATLPRINT_H__

/* [<][>][^][v][top][bottom][index][help] */