windows-nt/Source/XPSP1/NT/enduser/troubleshoot/msinfo/print.cpp
2020-09-26 16:20:57 +08:00

480 lines
13 KiB
C++

// Print.cpp - Functions to handle MSInfo printing of the text report.
//
// Copyright (c) 1998-1999 Microsoft Corporation
#include "stdafx.h"
#include "DataSrc.h"
#include "Resource.h"
#include "DataObj.h"
#include "CompData.h"
#include <afxext.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/*
* PreparePrintDialog - Do all initialization required before the print
* dialog is displayed, most notably calculating the page count.
*
* History: a-jsari 3/17/98 Initial version
*/
BOOL CDataSource::RefreshPrintData(CPrintDialog * pdlgPrint, CFolder * pfolSelection)
{
// FIX: Make calculation of line length device independent.
LPTSTR szLine;
int cLength = 84;
int cLineCount = 57;
unsigned short cPage = 0;
CFolder * pPrintRoot = NULL;
if (pdlgPrint->PrintSelection() && pfolSelection)
pPrintRoot = pfolSelection;
else
pPrintRoot = GetRootNode();
if (m_pPrintContent != NULL)
delete m_pPrintContent;
m_pPrintContent = new CMSInfoMemoryFile();
ASSERT(m_pPrintContent != NULL);
if (m_pPrintContent == NULL)
::AfxThrowMemoryException();
if (m_pPrintInfo != NULL) {
// Set this pointer to NULL so it doesn't delete the
// pointer we're storing internally.
m_pPrintInfo->m_pPD = NULL;
// This prevents the delete below from asserting.
m_pPrintInfo->m_strPageDesc = _T("");
delete m_pPrintInfo;
m_pPrintInfo = NULL;
}
m_pPrintInfo = new CPrintInfo;
ASSERT(m_pPrintInfo != NULL);
if (m_pPrintInfo == NULL) {
delete m_pPrintContent;
m_pPrintContent = NULL;
::AfxThrowMemoryException();
}
m_pPrintInfo->m_nCurPage = 0;
m_pPrintInfo->m_pPD = pdlgPrint;
if (FAILED(WriteOutput(m_pPrintContent, pPrintRoot)))
return FALSE;
m_pPrintContent->SeekToBegin();
szLine = new TCHAR[cLength + 1];
m_fEndOfFile = FALSE;
while (!m_fEndOfFile) {
int iLine = cLineCount;
++cPage;
while (iLine--) {
GetLine(szLine, cLength);
if (m_fEndOfFile)
goto WhileEnd;
}
}
WhileEnd:
delete [] szLine;
pdlgPrint->m_pd.nMaxPage = cPage;
if (pdlgPrint->m_pd.nToPage > pdlgPrint->m_pd.nMaxPage)
pdlgPrint->m_pd.nToPage = pdlgPrint->m_pd.nMaxPage;
return TRUE;
}
/*
* PrintReport - Send the report data to the printer.
*
* History: a-jsari 12/9/97 Initial version.
*/
BOOL CDataSource::PrintReport(CPrintDialog *pdlgPrint, CFolder *pfolSelection)
{
BOOL fReturn;
do {
int nResult = BeginPrinting(pdlgPrint, pfolSelection);
if (nResult < 0)
break;
m_pPrintContent->SeekToBegin();
while (m_pPrintInfo->m_bContinuePrinting)
PrintPage(pdlgPrint);
EndPrinting();
fReturn = TRUE;
} while (FALSE);
delete m_pPrintContent;
m_pPrintContent = NULL;
delete m_pPrintInfo;
m_pPrintInfo = NULL;
return fReturn;
}
/*
* BeginPrinting - Do the initialization required to print the file.
*
* History: a-jsari 12/30/97 Thieved from msishell's msiview.cpp,
* modified to allow page ranges.
*/
int CDataSource::BeginPrinting(CPrintDialog *pdlgPrint, CFolder *pfolSelection)
{
ASSERT(m_pPrintContent != NULL);
if (m_pPrintContent == NULL) {
m_fEndOfFile = TRUE;
m_pPrintInfo->m_bContinuePrinting = FALSE;
::AfxThrowMemoryException();
}
if (m_pDC != NULL) {
delete m_pDC;
m_pDC = NULL;
}
m_pDC = new CDC;
ASSERT(m_pDC != NULL);
if (m_pDC == NULL) ::AfxThrowMemoryException();
if (m_pprinterFont != NULL) {
delete m_pprinterFont;
m_pprinterFont = NULL;
}
m_pprinterFont = new CFont;
ASSERT(m_pprinterFont != NULL);
if (m_pprinterFont == NULL) ::AfxThrowMemoryException();
#if 0
m_strHeaderLeft = pScope->MachineName();
#endif
CString strFormat;
COleDateTime datetime;
strFormat.LoadString(IDS_PRINT_HDR_RIGHT_CURRENT);
datetime = COleDateTime::GetCurrentTime();
m_strHeaderRight = datetime.Format(strFormat);
m_strFooterCenter.LoadString(IDS_PRINT_FTR_CTR);
// Reset the end of file member.
m_fEndOfFile = FALSE;
// Create the font for printing. Read font information from string
// resources, to allow the localizers to control what font is
// used for printing. Set the variables for the default font to use.
int nHeight = 10;
int nWeight = FW_NORMAL;
BYTE nCharSet = DEFAULT_CHARSET;
BYTE nPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
CString strFace = "Courier New";
// Load string resources to see if we should use other values
// than the defaults.
CString strHeight, strWeight, strCharSet, strPitchAndFamily, strFaceName;
strHeight.LoadString(IDS_PRINT_FONT_HEIGHT);
strWeight.LoadString(IDS_PRINT_FONT_WEIGHT);
strCharSet.LoadString(IDS_PRINT_FONT_CHARSET);
strPitchAndFamily.LoadString(IDS_PRINT_FONT_PITCHANDFAMILY);
strFaceName.LoadString(IDS_PRINT_FONT_FACENAME);
if (!strHeight.IsEmpty() && ::_ttol(strHeight))
nHeight = ::_ttoi(strHeight);
if (!strWeight.IsEmpty())
nWeight = ::_ttoi(strWeight);
if (!strCharSet.IsEmpty())
nCharSet = (BYTE) ::_ttoi(strCharSet);
if (!strPitchAndFamily.IsEmpty())
nPitchAndFamily = (BYTE) ::_ttoi(strPitchAndFamily);
strFaceName.TrimLeft();
if (!strFaceName.IsEmpty() && strFaceName != CString("facename"))
strFace = strFaceName;
CString strDriver = pdlgPrint->GetDriverName();
CString strDevice = pdlgPrint->GetDeviceName();
CString strPort = pdlgPrint->GetPortName();
LPDEVMODE pdevMode = pdlgPrint->GetDevMode();
VERIFY(m_pDC->CreateDC(strDriver, strDevice, strPort, pdevMode));
// Convert the height from points to a value specific to the printer.
nHeight = -((m_pDC->GetDeviceCaps (LOGPIXELSY) * nHeight) / 72);
// Create the font object.
VERIFY(m_pprinterFont->CreateFont(nHeight, 0, 0, 0, nWeight, 0, 0, 0,
nCharSet, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
DEFAULT_QUALITY, nPitchAndFamily, strFace));
m_pPrintInfo->m_rectDraw.SetRect(0, 0, m_pDC->GetDeviceCaps(HORZRES),
m_pDC->GetDeviceCaps(VERTRES));
m_pDC->DPtoLP(&m_pPrintInfo->m_rectDraw);
m_pPrintInfo->m_bContinuePrinting = TRUE;
CString strDocName;
CString strOutput;
DOCINFO diJob;
diJob.cbSize = sizeof(DOCINFO);
diJob.lpszDocName = strDocName;
diJob.lpszOutput = strOutput;
return m_pDC->StartDoc(&diJob);
}
/*
* OnEndPrinting - Clean up anything we allocated for the print job. Specifically,
* delete the CMemFile holding the content.
*
* History: a-jsari 12/30/97 Thieved from MSIShell's msiview.cpp
*/
void CDataSource::EndPrinting()
{
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
if (m_pPrintContent != NULL)
{
delete m_pPrintContent;
m_pPrintContent = NULL;
}
int nResult = m_pDC->EndDoc();
ASSERT(nResult >= 0);
if (nResult < 0) {
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
CString strError, strTitle;
switch(nResult) {
case SP_OUTOFDISK:
VERIFY(strError.LoadString(IDS_PRINT_NODISK));
break;
case SP_OUTOFMEMORY:
VERIFY(strError.LoadString(IDS_PRINT_NOMEMORY));
break;
case SP_USERABORT:
VERIFY(strError.LoadString(IDS_PRINT_USERABORTED));
break;
case SP_ERROR:
default:
VERIFY(strError.LoadString(IDS_PRINT_GENERIC));
break;
}
strTitle.LoadString(IDS_DESCRIPTION);
::MessageBox( ::AfxGetMainWnd()->GetSafeHwnd(), strError, strTitle, MB_OK);
}
VERIFY(m_pprinterFont->DeleteObject());
VERIFY(m_pDC->DeleteDC());
}
//---------------------------------------------------------------------------
// In this function, we use two rectangles: rectOuter is what we consider
// the printable area of the page, and includes headers and footers.
// rectInner is the rectange we actually print the content in (doesn't
// include headers or footers).
//
// TBD: add header and footer
// TBD: add error checking
// TBD: fix intelegence issues and speed this up
// TBD: insure that 80 columns will always fit
//---------------------------------------------------------------------------
#define TOP_MARGIN 2540/2 // HIMETRIC for 1/2 inch
#define BOTTOM_MARGIN 2540/2 // HIMETRIC for 1/2 inch
#define LEFT_MARGIN 2540/2 // HIMETRIC for 1/2 inch
#define RIGHT_MARGIN 2540/2 // HIMETRIC for 1/2 inch
/*
* GetLine - Copy a line of data from m_pPrintContent into szLineBuffer.
*
* History: a-jsari 3/16/98 Initial version.
*/
void CDataSource::GetLine(LPTSTR szLineBuffer, int cSize)
{
for (int i = 0; i < cSize; i++)
{
try {
m_pPrintContent->ReadTchar(szLineBuffer[i]);
} catch (...)
{
m_pPrintInfo->m_bContinuePrinting = FALSE;
m_fEndOfFile = TRUE;
szLineBuffer[i] = '\0';
break;
}
if (szLineBuffer[i] == '\t')
{
// Convert tabs into spaces for printing.
szLineBuffer[i] = ' ';
}
else if (szLineBuffer[i] == '\r')
{
i--;
}
else if (szLineBuffer[i] == '\n')
{
szLineBuffer[i] = '\0';
break;
}
}
}
/*
* GetTextSize - Get the sizes of the text.
*
* History: a-jsari 3/16/98 Initial version.
*/
void CDataSource::GetTextSize(int &cLineLength, int &cCharHeight, CRect &rectOuter, CRect &rectText)
{
TEXTMETRIC tm;
m_pDC->GetTextMetrics(&tm);
cCharHeight = tm.tmHeight + tm.tmExternalLeading;
// Take the actual print area (in pInfo) and adjust it for the
// header and footer.
rectOuter = m_pPrintInfo->m_rectDraw;
CSize sizeLeftTop(LEFT_MARGIN, TOP_MARGIN);
CSize sizeRightBottom(RIGHT_MARGIN, BOTTOM_MARGIN);
m_pDC->HIMETRICtoDP(&sizeLeftTop);
m_pDC->HIMETRICtoDP(&sizeRightBottom);
rectOuter.DeflateRect(sizeLeftTop.cx, sizeLeftTop.cy, sizeRightBottom.cx, sizeRightBottom.cy);
rectText = rectOuter;
rectText.DeflateRect(0, cCharHeight * 4);
// Get the number of characters which will fit on a line (use the average, because this
// might not be a monospaced font). Note: it's possible that text could be cut off
// if a line consisted of very wide characters. This is unlikely because the font
// used SHOULD be monospace (for alignment of text, etc.).
cLineLength = rectText.Width() / tm.tmAveCharWidth;
}
/*
* PrintPage - Write one page to the printer Device Context.
*
* History: a-jsari 12/30/97 Thieved from msishell's msiview.cpp.
*/
void CDataSource::PrintPage(CPrintDialog *pdlgPrint)
{
int cLineLength;
int cy = 0, cyCharHeight;
LPTSTR szLineBuffer;
CRect rectOuter, rectInner;
ASSERT(m_pPrintContent != NULL);
if (m_pPrintContent == NULL)
{
m_fEndOfFile = TRUE;
m_pPrintInfo->m_bContinuePrinting = FALSE;
return;
}
// Select the font used for printing.
CGdiObject* pOldFont = m_pDC->SelectObject(m_pprinterFont);
// this makes a small font: CGdiObject *pOldFont = pDC->SelectStockObject(ANSI_FIXED_FONT);
// Compute the height of each character (we'll need the text metric).
GetTextSize(cLineLength, cyCharHeight, rectOuter, rectInner);
++m_pPrintInfo->m_nCurPage;
if (!(pdlgPrint->PrintAll() || pdlgPrint->PrintSelection())
&& m_pPrintInfo->m_nCurPage > m_pPrintInfo->GetToPage()) {
m_pPrintInfo->m_bContinuePrinting = FALSE;
m_pDC->SelectObject(pOldFont);
return;
}
// Allocate the buffer of character to hold a single line of the print out.
szLineBuffer = new TCHAR[cLineLength + 1];
szLineBuffer[cLineLength] = (TCHAR)'\0';
if (pdlgPrint->PrintAll() || pdlgPrint->PrintSelection()
|| m_pPrintInfo->m_nCurPage >= m_pPrintInfo->GetFromPage()) {
m_pDC->StartPage();
// Draw the header.
m_pDC->TextOut(rectOuter.left, rectOuter.top, m_strHeaderLeft);
int cxHeaderRight = rectOuter.right - m_pDC->GetTextExtent(m_strHeaderRight).cx;
m_pDC->TextOut(cxHeaderRight, rectOuter.top, m_strHeaderRight);
}
// Process the output a line at a time, until either we have emptied out
// the memfile, or we have gotten to the bottom of this page.
while (!m_fEndOfFile)
{
if (cy + cyCharHeight > rectInner.Height())
break;
GetLine(szLineBuffer, cLineLength);
if (pdlgPrint->PrintAll() || pdlgPrint->PrintSelection()
|| m_pPrintInfo->m_nCurPage >= m_pPrintInfo->GetFromPage())
m_pDC->TextOut(rectInner.left, rectInner.top + cy, szLineBuffer, ::_tcslen(szLineBuffer));
cy += cyCharHeight;
}
if (pdlgPrint->PrintAll() || pdlgPrint->PrintSelection()
|| m_pPrintInfo->m_nCurPage >= m_pPrintInfo->GetFromPage()) {
// Draw the footer.
CString strActualFooter;
strActualFooter.Format(m_strFooterCenter, m_pPrintInfo->m_nCurPage);
int cxFooterCenter = (rectOuter.Width() - m_pDC->GetTextExtent(strActualFooter).cx) / 2;
m_pDC->TextOut(rectOuter.left + cxFooterCenter, rectOuter.bottom - cyCharHeight, strActualFooter);
int nResult = m_pDC->EndPage();
ASSERT(nResult >= 0);
}
#if 0
if (nResult < 0) {
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
CString strError;
switch (nResult) {
case SP_APPABORT:
VERIFY(strError.LoadString(IDS_PRINT_APPABORTED));
break;
case SP_USERABORT:
VERIFY(strError.LoadString(IDS_PRINT_USERABORTED));
break;
case SP_OUTOFDISK:
VERIFY(strError.LoadString(IDS_PRINT_NODISK));
break;
case SP_OUTOFMEMORY:
VERIFY(strError.LoadString(IDS_PRINT_NOMEMORY));
break;
case SP_ERROR:
default:
VERIFY(strError.LoadString(IDS_PRINT_GENERIC));
break;
}
strTitle.LoadString(IDS_DESCRIPTION)
::MessageBox( ::AfxGetMainWnd()->GetSafeHwnd(), strError, strTitle, MB_OK);
}
// Clean up.
#endif
delete [] szLineBuffer;
m_pDC->SelectObject(pOldFont);
}