// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #ifdef AFX_PRINT_SEG #pragma code_seg(AFX_PRINT_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Printing Dialog class CPrintingDialog : public CDialog { public: //{{AFX_DATA(CPrintingDialog) enum { IDD = AFX_IDD_PRINTDLG }; //}}AFX_DATA CPrintingDialog::CPrintingDialog(CWnd* pParent) { Create(CPrintingDialog::IDD, pParent); // modeless ! _afxWinState->m_bUserAbort = FALSE; } virtual ~CPrintingDialog() { } virtual BOOL OnInitDialog(); virtual void OnCancel(); }; BOOL CALLBACK _AfxAbortProc(HDC, int) { _AFX_WIN_STATE* pWinState = _afxWinState; MSG msg; while (!pWinState->m_bUserAbort && ::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE)) { if (!AfxGetThread()->PumpMessage()) return FALSE; // terminate if WM_QUIT received } return !pWinState->m_bUserAbort; } BOOL CPrintingDialog::OnInitDialog() { SetWindowText(AfxGetAppName()); CenterWindow(); return CDialog::OnInitDialog(); } void CPrintingDialog::OnCancel() { _afxWinState->m_bUserAbort = TRUE; // flag that user aborted print CDialog::OnCancel(); } ///////////////////////////////////////////////////////////////////////////// // CView printing commands BOOL CView::DoPreparePrinting(CPrintInfo* pInfo) { ASSERT(pInfo != NULL); ASSERT(pInfo->m_pPD != NULL); if (pInfo->m_pPD->m_pd.nMinPage > pInfo->m_pPD->m_pd.nMaxPage) pInfo->m_pPD->m_pd.nMaxPage = pInfo->m_pPD->m_pd.nMinPage; // don't prompt the user if we're doing print preview, printing directly, // or printing via IPrint and have been instructed not to ask CWinApp* pApp = AfxGetApp(); if (pInfo->m_bPreview || pInfo->m_bDirect || (pInfo->m_bDocObject && !(pInfo->m_dwFlags & PRINTFLAG_PROMPTUSER))) { if (pInfo->m_pPD->m_pd.hDC == NULL) { // if no printer set then, get default printer DC and create DC without calling // print dialog. if (!pApp->GetPrinterDeviceDefaults(&pInfo->m_pPD->m_pd)) { // bring up dialog to alert the user they need to install a printer. if (!pInfo->m_bDocObject || (pInfo->m_dwFlags & PRINTFLAG_MAYBOTHERUSER)) if (pApp->DoPrintDialog(pInfo->m_pPD) != IDOK) return FALSE; } if (pInfo->m_pPD->m_pd.hDC == NULL) { // call CreatePrinterDC if DC was not created by above if (pInfo->m_pPD->CreatePrinterDC() == NULL) return FALSE; } } // set up From and To page range from Min and Max pInfo->m_pPD->m_pd.nFromPage = (WORD)pInfo->GetMinPage(); pInfo->m_pPD->m_pd.nToPage = (WORD)pInfo->GetMaxPage(); } else { // otherwise, bring up the print dialog and allow user to change things // preset From-To range same as Min-Max range pInfo->m_pPD->m_pd.nFromPage = (WORD)pInfo->GetMinPage(); pInfo->m_pPD->m_pd.nToPage = (WORD)pInfo->GetMaxPage(); if (pApp->DoPrintDialog(pInfo->m_pPD) != IDOK) return FALSE; // do not print } ASSERT(pInfo->m_pPD != NULL); ASSERT(pInfo->m_pPD->m_pd.hDC != NULL); if (pInfo->m_pPD->m_pd.hDC == NULL) return FALSE; pInfo->m_nNumPreviewPages = pApp->m_nNumPreviewPages; VERIFY(pInfo->m_strPageDesc.LoadString(AFX_IDS_PREVIEWPAGEDESC)); return TRUE; } void CView::OnFilePrint() { // get default print info CPrintInfo printInfo; ASSERT(printInfo.m_pPD != NULL); // must be set if (LOWORD(GetCurrentMessage()->wParam) == ID_FILE_PRINT_DIRECT) { CCommandLineInfo* pCmdInfo = AfxGetApp()->m_pCmdInfo; if (pCmdInfo != NULL) { if (pCmdInfo->m_nShellCommand == CCommandLineInfo::FilePrintTo) { printInfo.m_pPD->m_pd.hDC = ::CreateDC(pCmdInfo->m_strDriverName, pCmdInfo->m_strPrinterName, pCmdInfo->m_strPortName, NULL); if (printInfo.m_pPD->m_pd.hDC == NULL) { AfxMessageBox(AFX_IDP_FAILED_TO_START_PRINT); return; } } } printInfo.m_bDirect = TRUE; } if (OnPreparePrinting(&printInfo)) { // hDC must be set (did you remember to call DoPreparePrinting?) ASSERT(printInfo.m_pPD->m_pd.hDC != NULL); // gather file to print to if print-to-file selected CString strOutput; if (printInfo.m_pPD->m_pd.Flags & PD_PRINTTOFILE && !printInfo.m_bDocObject) { // construct CFileDialog for browsing CString strDef(MAKEINTRESOURCE(AFX_IDS_PRINTDEFAULTEXT)); CString strPrintDef(MAKEINTRESOURCE(AFX_IDS_PRINTDEFAULT)); CString strFilter(MAKEINTRESOURCE(AFX_IDS_PRINTFILTER)); CString strCaption(MAKEINTRESOURCE(AFX_IDS_PRINTCAPTION)); CFileDialog dlg(FALSE, strDef, strPrintDef, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, strFilter); dlg.m_ofn.lpstrTitle = strCaption; if (dlg.DoModal() != IDOK) return; // set output device to resulting path name strOutput = dlg.GetPathName(); } // set up document info and start the document printing process CString strTitle; CDocument* pDoc = GetDocument(); if (pDoc != NULL) strTitle = pDoc->GetTitle(); else GetParentFrame()->GetWindowText(strTitle); if (strTitle.GetLength() > 31) strTitle.ReleaseBuffer(31); DOCINFO docInfo; memset(&docInfo, 0, sizeof(DOCINFO)); docInfo.cbSize = sizeof(DOCINFO); docInfo.lpszDocName = strTitle; CString strPortName; int nFormatID; if (strOutput.IsEmpty()) { docInfo.lpszOutput = NULL; strPortName = printInfo.m_pPD->GetPortName(); nFormatID = AFX_IDS_PRINTONPORT; } else { docInfo.lpszOutput = strOutput; AfxGetFileTitle(strOutput, strPortName.GetBuffer(_MAX_PATH), _MAX_PATH); nFormatID = AFX_IDS_PRINTTOFILE; } // setup the printing DC CDC dcPrint; if (!printInfo.m_bDocObject) { dcPrint.Attach(printInfo.m_pPD->m_pd.hDC); // attach printer dc dcPrint.m_bPrinting = TRUE; } OnBeginPrinting(&dcPrint, &printInfo); if (!printInfo.m_bDocObject) dcPrint.SetAbortProc(_AfxAbortProc); // disable main window while printing & init printing status dialog AfxGetMainWnd()->EnableWindow(FALSE); CPrintingDialog dlgPrintStatus(this); CString strTemp; dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_DOCNAME, strTitle); dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PRINTERNAME, printInfo.m_pPD->GetDeviceName()); AfxFormatString1(strTemp, nFormatID, strPortName); dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PORTNAME, strTemp); dlgPrintStatus.ShowWindow(SW_SHOW); dlgPrintStatus.UpdateWindow(); // start document printing process if (!printInfo.m_bDocObject && dcPrint.StartDoc(&docInfo) == SP_ERROR) { // enable main window before proceeding AfxGetMainWnd()->EnableWindow(TRUE); // cleanup and show error message OnEndPrinting(&dcPrint, &printInfo); dlgPrintStatus.DestroyWindow(); dcPrint.Detach(); // will be cleaned up by CPrintInfo destructor AfxMessageBox(AFX_IDP_FAILED_TO_START_PRINT); return; } // Guarantee values are in the valid range UINT nEndPage = printInfo.GetToPage(); UINT nStartPage = printInfo.GetFromPage(); if (nEndPage < printInfo.GetMinPage()) nEndPage = printInfo.GetMinPage(); if (nEndPage > printInfo.GetMaxPage()) nEndPage = printInfo.GetMaxPage(); if (nStartPage < printInfo.GetMinPage()) nStartPage = printInfo.GetMinPage(); if (nStartPage > printInfo.GetMaxPage()) nStartPage = printInfo.GetMaxPage(); int nStep = (nEndPage >= nStartPage) ? 1 : -1; nEndPage = (nEndPage == 0xffff) ? 0xffff : nEndPage + nStep; VERIFY(strTemp.LoadString(AFX_IDS_PRINTPAGENUM)); // If it's a doc object, we don't loop page-by-page // because doc objects don't support that kind of levity. BOOL bError = FALSE; if (printInfo.m_bDocObject) { OnPrepareDC(&dcPrint, &printInfo); OnPrint(&dcPrint, &printInfo); } else { // begin page printing loop for (printInfo.m_nCurPage = nStartPage; printInfo.m_nCurPage != nEndPage; printInfo.m_nCurPage += nStep) { OnPrepareDC(&dcPrint, &printInfo); // check for end of print if (!printInfo.m_bContinuePrinting) break; // write current page TCHAR szBuf[80]; wsprintf(szBuf, strTemp, printInfo.m_nCurPage); dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PAGENUM, szBuf); // set up drawing rect to entire page (in logical coordinates) printInfo.m_rectDraw.SetRect(0, 0, dcPrint.GetDeviceCaps(HORZRES), dcPrint.GetDeviceCaps(VERTRES)); dcPrint.DPtoLP(&printInfo.m_rectDraw); // attempt to start the current page if (dcPrint.StartPage() < 0) { bError = TRUE; break; } // must call OnPrepareDC on newer versions of Windows because // StartPage now resets the device attributes. if (afxData.bMarked4) OnPrepareDC(&dcPrint, &printInfo); ASSERT(printInfo.m_bContinuePrinting); // page successfully started, so now render the page OnPrint(&dcPrint, &printInfo); if (dcPrint.EndPage() < 0 || !_AfxAbortProc(dcPrint.m_hDC, 0)) { bError = TRUE; break; } } } // cleanup document printing process if (!printInfo.m_bDocObject) { if (!bError) dcPrint.EndDoc(); else dcPrint.AbortDoc(); } AfxGetMainWnd()->EnableWindow(); // enable main window OnEndPrinting(&dcPrint, &printInfo); // clean up after printing dlgPrintStatus.DestroyWindow(); dcPrint.Detach(); // will be cleaned up by CPrintInfo destructor } } ///////////////////////////////////////////////////////////////////////////// // CPrintInfo helper structure CPrintInfo::CPrintInfo() { m_pPD = new CPrintDialog(FALSE, PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOSELECTION); ASSERT(m_pPD->m_pd.hDC == NULL); SetMinPage(1); // one based page numbers SetMaxPage(0xffff); // unknown how many pages m_nCurPage = 1; m_lpUserData = NULL; // Initialize to no user data m_bPreview = FALSE; // initialize to not preview m_bDirect = FALSE; // initialize to not direct m_bDocObject = FALSE; // initialize to not IPrint m_bContinuePrinting = TRUE; // Assume it is OK to print m_dwFlags = 0; m_nOffsetPage = 0; } CPrintInfo::~CPrintInfo() { if (m_pPD != NULL && m_pPD->m_pd.hDC != NULL) { ::DeleteDC(m_pPD->m_pd.hDC); m_pPD->m_pd.hDC = NULL; } delete m_pPD; } /////////////////////////////////////////////////////////////////////////////