PRINT.CPP

/* 
* PRINT.CPP
* Patron Chapter 1
*
* Implementation of printing functions for both CPatronDoc
* and CPages classes. These functions are here to keep clutter
* down in document.cpp and pages.cpp.
*
* Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/

#include "patron.h"

static HWND g_hDlgPrint=NULL;
static BOOL g_fCancelPrint=FALSE;


/*
* CPatronDoc::Print
*
* Purpose:
* Prints the current document.
*
* Parameters:
* hWndFrame HWND of the frame to use for dialog parents.
*
* Return Value:
* BOOL TRUE if printing happened, FALSE if it didn't
* start or didn't complete.
*/

BOOL CPatronDoc::Print(HWND hWndFrame)
{
PRINTDLG pd;
BOOL fSuccess;

memset(&pd, 0, sizeof(PRINTDLG));
pd.lStructSize=sizeof(PRINTDLG);
pd.hwndOwner =hWndFrame;
pd.nCopies =1;
pd.nFromPage =(USHORT)-1;
pd.nToPage =(USHORT)-1;
pd.nMinPage =1;
pd.nMaxPage =m_pPG->NumPagesGet();

pd.lpfnPrintHook=PrintDlgHook;

//Get the current document printer settings
pd.hDevMode=m_pPG->DevModeGet();

pd.Flags=PD_RETURNDC | PD_ALLPAGES | PD_COLLATE
| PD_HIDEPRINTTOFILE | PD_NOSELECTION | PD_ENABLEPRINTHOOK;

if (!PrintDlg(&pd))
return FALSE;

if (NULL!=pd.hDevMode)
GlobalFree(pd.hDevMode);

if (NULL!=pd.hDevNames)
GlobalFree(pd.hDevNames);

//Go do the actual printing.
fSuccess=m_pPG->Print(pd.hDC, PSZ(IDS_DOCUMENTNAME), pd.Flags
, pd.nFromPage, pd.nToPage, pd.nCopies);

if (!fSuccess)
{
MessageBox(m_hWnd, PSZ(IDS_PRINTERROR)
, PSZ(IDS_DOCUMENTCAPTION), MB_OK);
}

return fSuccess;
}






/*
* CPatronDoc::PrinterSetup
*
* Purpose:
* Selects a new printer and options for this document.
*
* Parameters:
* hWndFrame HWND of the frame to use for dialog parents.
* fDefault BOOL to avoid any dialog and just use the
* default.
*
* Return Value:
* UINT Undefined
*/

UINT CPatronDoc::PrinterSetup(HWND hWndFrame, BOOL fDefault)
{
PRINTDLG pd;

//Attempt to get printer metrics for the default printer.
memset(&pd, 0, sizeof(PRINTDLG));
pd.lStructSize=sizeof(PRINTDLG);

if (fDefault)
pd.Flags=PD_RETURNDEFAULT;
else
{
pd.hwndOwner=hWndFrame;
pd.Flags=PD_PRINTSETUP;

//Get the current document printer settings
pd.hDevMode=m_pPG->DevModeGet();
}

if (!PrintDlg(&pd))
return FALSE;

if (!m_pPG->DevModeSet(pd.hDevMode, pd.hDevNames))
{
GlobalFree(pd.hDevNames);
GlobalFree(pd.hDevMode);
return FALSE;
}

FDirtySet(TRUE);
return 1;
}



/*
* PrintDlgHook
*
* Purpose:
* Callback hook for the Print Dialog so we can hide the Setup
* button. Patron only allows Setup before anything exists on
* the page, and is not written to handle setup at Print time.
*/

UINT CALLBACK PrintDlgHook(HWND hDlg, UINT iMsg, WPARAM wParam
, LPARAM lParam)
{
if (WM_INITDIALOG==iMsg)
{
HWND hWnd;

hWnd=GetDlgItem(hDlg, psh1);
ShowWindow(hWnd, SW_HIDE);
return TRUE;
}

return FALSE;
}








/*
* CPages::DevModeSet
*
* Purpose:
* Provides the Pages with the current printer information.
*
* Parameters:
* hDevMode HGLOBAL to the memory containing the DEVMODE.
* This function assumes responsibility for this
* handle.
* hDevNames HGLOBAL providing the driver name and device
* name from which we can create a DC for
* information.
*
* Return Value:
* BOOL TRUE if we could accept this configuration,
* FALSE otherwise. If we return TRUE we also
* delete the old memory we hold.
*/

BOOL CPages::DevModeSet(HGLOBAL hDevMode, HGLOBAL hDevNames)
{
LPDEVNAMES pdn;
LPTSTR psz;

if (NULL==hDevMode || NULL==hDevNames)
return FALSE;

psz=(LPTSTR)GlobalLock(hDevNames);

if (NULL==psz)
return FALSE;

pdn=(LPDEVNAMES)psz;
lstrcpy(m_szDriver, psz+pdn->wDriverOffset);
lstrcpy(m_szDevice, psz+pdn->wDeviceOffset);
lstrcpy(m_szPort, psz+pdn->wOutputOffset);
GlobalUnlock(hDevNames);
GlobalFree(hDevNames);

//Save this new memory and get rid of the old.
if (NULL!=m_hDevMode)
GlobalFree(m_hDevMode);

m_hDevMode=hDevMode;

return ConfigureForDevice();
}







/*
* CPages::DevModeGet
*
* Purpose:
* Retrieves a copy of the current DEVMODE structure for this
* Pages window. The caller is responsible for this memory.
*
* Parameters:
* None
*
* Return Value:
* HGLOBAL Handle to the memory containing the DEVMODE
* structure.
*/

HGLOBAL CPages::DevModeGet(void)
{
HGLOBAL hMem;
DWORD cb;
DWORD i;
LPBYTE pb1, pb2;

cb=GlobalSize(m_hDevMode);

if (0==cb)
return NULL;

hMem=GlobalAlloc(GHND, cb);

if (NULL==hMem)
return NULL;

pb1=(LPBYTE)GlobalLock(hMem);
pb2=(LPBYTE)GlobalLock(m_hDevMode);

//Copy the existing DEVMODE structure
for (i=0; i < cb; i++)
*pb1++=*pb2++;

GlobalUnlock(m_hDevMode);
GlobalUnlock(hMem);

return hMem;
}




/*
* CPages::ConfigureForDevice
*
* Purpose:
* Recalculates our drawing configuration based on the contents of
* an hDC. If no HDC is given we use the contents of our DevMode
* stream.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if successful, FALSE otherwise.
*/

BOOL CPages::ConfigureForDevice(void)
{
POINT ptOffset, ptPaper;
RECT rc;
HDC hDC;
LPDEVMODE pdm;
CHourglass hg; //Shows wait cursor, automatically destroyed

pdm=(LPDEVMODE)GlobalLock(m_hDevMode);

if (NULL==pdm)
return FALSE;

//Get the DC then configure
hDC=CreateIC(m_szDriver, m_szDevice, m_szPort, pdm);

GlobalUnlock(m_hDevMode);

if (NULL==hDC)
return FALSE;

//Get usable page dimensions: already sensitive to orientation
m_cx=GetDeviceCaps(hDC, HORZSIZE)*10-16; //*10: mm to LOMETRIC
m_cy=GetDeviceCaps(hDC, VERTSIZE)*10-16; //-16: for driver bugs.

//Calculate the printer-limited margins on sides in LOMETRIC.
Escape(hDC, GETPRINTINGOFFSET, NULL, NULL, &ptOffset);
Escape(hDC, GETPHYSPAGESIZE, NULL, NULL, &ptPaper);

SetRect(&rc, ptOffset.x, ptOffset.y, ptPaper.x, ptPaper.y);
SetMapMode(hDC, MM_LOMETRIC);
RectConvertMappings(&rc, hDC, FALSE);

//Left and top margins are the printing offset.
m_xMarginLeft= rc.left+8; //+8 to match -16 above
m_yMarginTop =-rc.top+8; //LOMETRIC makes this negative.

//Right is (paper width)-(usable width)-(left margin)
m_xMarginRight =rc.right-m_cx-m_xMarginLeft;

//Bottom is (paper height)-(usable height)-(top margin)+1
m_yMarginBottom=-rc.bottom-m_cy-m_yMarginTop+1;

UpdateScrollRanges();

DeleteDC(hDC);
return TRUE;
}




/*
* CPages::Print
*
* Purpose:
* Prints a specified range of pages to a given hDC. Repeats for
* a given number of copies.
*
* Parameters:
* hDC HDC to which we print.
* pszDoc LPTSTR providing the document name.
* dwFlags DWORD flags from PrintDlg
* iPageStart UINT starting page index (one based)
* iPageEnd UINT ending page index (one based). Includes
* this page.
* cCopies UINT number of copies to print. If PD_COLLATE
* in dwFlags is set, we print multiple copies of
* each page as we cycle through. Otherwise we
* cycle multiple times.
*
* Return Value:
* None
*/

BOOL CPages::Print(HDC hDC, LPTSTR pszDoc, DWORD dwFlags
, UINT iPageStart, UINT iPageEnd, UINT cCopies)
{
BOOL fError=FALSE;
int iPage, iPageInc;
int iUserPage, cPages;
UINT iRepeat, cRepeat;
UINT iCycle, cCycles;
UINT iPageHold=m_iPageCur;
HWND hWndT, hWndTop=NULL;
DOCINFO di;
PCDocument pDoc;

//Validate hDC and page ranges
if (NULL==hDC)
return FALSE;

if ((PD_PAGENUMS & dwFlags))
{
if (-1==iPageStart)
iPageStart=0;
else
iPageStart--; //Switch to zero offset.

if (-1==iPageEnd)
iPageEnd=m_cPages-1;
else
iPageEnd--; //Switch to zero offset.
}
else //Can't test PD_ALLPAGES with & since it's defined as 0L
{
iPageStart=0;
iPageEnd=m_cPages-1;
}

//Arrange cycles and repeats depending on cCopies and collating
if (PD_COLLATE & dwFlags)
{
cCycles=cCopies;
cRepeat=1;
}
else
{
cCycles=1;
cRepeat=cCopies;
}

//Disable the frame window to prevent reentrancy while printing.
hWndT=GetParent(m_hWnd);
pDoc=(PCDocument)SendMessage(hWndT, DOCM_PDOCUMENT, 0, 0L);

if (NULL!=pDoc)
{
PCFrame pFR;

pFR=pDoc->FrameGet();
hWndTop=pFR->Window();
EnableWindow(hWndTop, FALSE);
}

SetAbortProc(hDC, AbortProc);
g_fCancelPrint=FALSE;

//If these don't work then we'll just live without a dialog.
g_hDlgPrint=CreateDialog(m_hInst, MAKEINTRESOURCE(IDD_PRINTING)
, hWndTop, PrintDlgProc);

//Increment for either direction.
iPageInc=(iPageStart > iPageEnd) ? -1 : 1;

//Initial entries in dialog box.
cPages=1+((int)(iPageEnd-iPageStart)*iPageInc);

SendMessage(g_hDlgPrint, PRINTM_PAGEUPDATE, 1, (LPARAM)cPages);
SendMessage(g_hDlgPrint, PRINTM_COPYUPDATE, 1, (LPARAM)cRepeat);

di.cbSize=sizeof(DOCINFO);
di.lpszDocName=pszDoc;
di.lpszOutput=NULL;

if (StartDoc(hDC, &di) > 0)
{
/*
* Iterate over the pages, repeating each page depending on
* the copies we want and if we have collate enabled.
*/

for (iCycle=1; iCycle <= cCycles; iCycle++)
{
if (PD_COLLATE & dwFlags)
{
SendMessage(g_hDlgPrint, PRINTM_COPYUPDATE, iCycle
, (LPARAM)cCycles);
}

//iPageInc controls direction
for (iPage=iPageStart; ; iPage+=iPageInc)
{
iUserPage=1+((iPage-(int)iPageStart)*iPageInc);

SendMessage(g_hDlgPrint, PRINTM_PAGEUPDATE
, iUserPage, (LPARAM)cPages);

m_iPageCur=iPage; //We restore this later.

for (iRepeat=1; iRepeat <= cRepeat; iRepeat++)
{
if (!(PD_COLLATE & dwFlags))
{
SendMessage(g_hDlgPrint, PRINTM_COPYUPDATE
, iRepeat, (LPARAM)cRepeat);
}

StartPage(hDC);
Draw(hDC, TRUE, TRUE);

if (EndPage(hDC) < 0)
fError=TRUE;

if (fError || g_fCancelPrint)
break;
}

if (fError || g_fCancelPrint)
break;

//If we just printed the last page, time to quit.
if (iPage==(int)iPageEnd)
break;
}

if (fError || g_fCancelPrint)
break;
}

if (!fError)
EndDoc(hDC);
else
AbortDoc(hDC);
}
else
fError=TRUE;

//Set the page back to what it was before all this started.
m_iPageCur=iPageHold;

EnableWindow(hWndTop, TRUE);
SetFocus(hWndTop);
DestroyWindow(g_hDlgPrint);
DeleteDC(hDC);
return !fError;
}






/*
* AbortProc
*
* Purpose:
* Abort procedure for printing the pages.
*
* Parameters:
* hDC HDC on which printing is happening.
* iErr int error code.
*
* Return Value:
* BOOL TRUE to continue the print job, FALSE otherwise.
*/

BOOL APIENTRY AbortProc(HDC hDC, int iErr)
{
MSG msg;

while (!g_fCancelPrint
&& PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (NULL==g_hDlgPrint
|| !IsDialogMessage(g_hDlgPrint, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return !g_fCancelPrint;
}




/*
* PrintDlgProc
*
* Purpose:
* Modeless dialog procedure for the dialog displayed while Patron
* is printing pages.
*/

BOOL APIENTRY PrintDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam
, LPARAM lParam)
{
TCHAR szFormat[40];
TCHAR szOutput[80];

switch (iMsg)
{
case WM_INITDIALOG:
EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_CLOSE
, MF_GRAYED);
return TRUE;

case WM_COMMAND:
//Cancel button was pressed.
g_fCancelPrint=TRUE;
ShowWindow(hDlg, SW_HIDE);
return TRUE;

case PRINTM_PAGEUPDATE:
GetDlgItemText(hDlg, ID_PAGESTRING, szFormat
, sizeof(szFormat));
wsprintf(szOutput, szFormat, wParam, (UINT)lParam);
SetDlgItemText(hDlg, ID_CURRENTPAGE, szOutput);
return TRUE;

case PRINTM_COPYUPDATE:
GetDlgItemText(hDlg, ID_COPYSTRING, szFormat
, sizeof(szFormat));
wsprintf(szOutput, szFormat, wParam, (UINT)lParam);
SetDlgItemText(hDlg, ID_CURRENTCOPY, szOutput);
return TRUE;
}

return FALSE;
}