windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upg/ui/prntsave.c
2020-09-26 16:20:57 +08:00

1355 lines
30 KiB
C

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
prntsave.c
Abstract:
Routines to print or save the incompatibility reports. Functions
PrintReport and SaveReport are called when the user clicks
Save As... or Print... buttons in the UI. We then present
a common dialog box to the user and perform the operation.
Author:
Jim Schmidt (jimschm) 13-Mar-1997
Revision History:
--*/
#include "pch.h"
#include "uip.h"
#include <commdlg.h>
#include <winspool.h>
#define DBG_PRINTSAVE "Print/Save"
GROWBUFFER g_PunctTable = GROWBUF_INIT;
VOID
BuildPunctTable (
VOID
)
{
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
WORD cp;
TCHAR cpString[16];
PTSTR p;
PTSTR q;
MULTISZ_ENUM e;
DWORD d;
GetGlobalCodePage (&cp, NULL);
wsprintf (cpString, TEXT("%u"), (UINT) cp);
g_PunctTable.End = 0;
if (InfFindFirstLine (g_Win95UpgInf, TEXT("Wrap Exceptions"), cpString, &is)) {
p = InfGetMultiSzField (&is, 1);
if (EnumFirstMultiSz (&e, p)) {
do {
p = (PTSTR) e.CurrentString;
while (*p) {
q = p;
if (StringIPrefix (p, TEXT("0x"))) {
d = _tcstoul (p + 2, &p, 16);
} else {
d = _tcstoul (p, &p, 10);
}
if (q == p) {
break;
}
GrowBufAppendDword (&g_PunctTable, d);
if (*p) {
p = (PTSTR) SkipSpace (p);
}
if (*p == TEXT(',')) {
p++;
}
}
} while (EnumNextMultiSz (&e));
}
}
GrowBufAppendDword (&g_PunctTable, 0);
InfCleanUpInfStruct (&is);
}
VOID
FreePunctTable (
VOID
)
{
FreeGrowBuffer (&g_PunctTable);
}
BOOL
IsPunct (
MBCHAR Char
)
{
PDWORD p;
if (_ismbcpunct (Char)) {
return TRUE;
}
p = (PDWORD) g_PunctTable.Buf;
if (p) {
while (*p) {
if (*p == Char) {
return TRUE;
}
p++;
}
}
return FALSE;
}
BOOL
pGetSaveAsName (
IN HWND ParentWnd,
IN OUT PTSTR Buffer
)
/*++
Routine Description:
Calls common dialog box to obtain the file name in which to save
the compatibility report text file.
Arguments:
ParentWnd - A handle to the parent of the save as dialog
Buffer - A caller-supplied buffer. Supplies a file name to
initialize the common dialog box with, and receives
the file name the user chose to use.
Return Value:
TRUE if the user clicked OK, or FALSE if the user canceled or an
error occurred.
--*/
{
TCHAR CwdSave[MAX_TCHAR_PATH];
OPENFILENAME ofn;
BOOL SaveFlag;
TCHAR Filter[512];
PCTSTR FilterResStr;
PTSTR p;
TCHAR desktopFolder[MAX_TCHAR_PATH];
LPITEMIDLIST pIDL;
BOOL b = FALSE;
#define MAX_EXT 3
PCTSTR ext[MAX_EXT];
INT i = 0;
FilterResStr = GetStringResource (MSG_FILE_NAME_FILTER);
if (FilterResStr) {
StringCopy (Filter, FilterResStr);
FreeStringResource (FilterResStr);
} else {
MYASSERT (FALSE);
Filter[0] = 0;
}
for (p = Filter ; *p ; p = _tcsinc (p)) {
if (*p == TEXT('|')) {
*p = 0;
}
}
for (p = Filter; *p; p = GetEndOfString (p) + 1) {
if (i & 1) {
//
// skip the * in "*.ext"
// if extension is "*.*" reduce that to an empty string (no extension)
//
MYASSERT (i / 2 < MAX_EXT);
ext[i / 2] = _tcsinc (p);
if (StringMatch (ext[i / 2], TEXT(".*"))) {
ext[i / 2] = S_EMPTY;
}
}
i++;
}
if (SHGetSpecialFolderLocation (ParentWnd, CSIDL_DESKTOP, &pIDL) == S_OK) {
LPMALLOC pMalloc;
b = SHGetPathFromIDList (pIDL, desktopFolder);
if (SHGetMalloc (&pMalloc) == S_OK) {
IMalloc_Free (pMalloc, pIDL);
IMalloc_Release (pMalloc);
}
}
if (!b) {
desktopFolder[0] = 0;
}
// Initialize OPENFILENAME
ZeroMemory (&ofn, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = ParentWnd;
ofn.lpstrFile = Buffer;
ofn.lpstrFilter = Filter;
ofn.nMaxFile = MAX_TCHAR_PATH;
// Force to begin at Desktop
// ofn.lpstrInitialDir = TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}");
ofn.lpstrInitialDir = desktopFolder;
ofn.Flags = OFN_NOCHANGEDIR | // leave the CWD unchanged
OFN_EXPLORER |
OFN_OVERWRITEPROMPT |
OFN_HIDEREADONLY;
// Let user select disk or directory
GetCurrentDirectory (sizeof (CwdSave), CwdSave);
SaveFlag = GetSaveFileName (&ofn);
SetCurrentDirectory (CwdSave);
//
// if no extension provided, append the default one
// this was selected by the user and returned in ofn.nFilterIndex
//
#define DEFAULT_EXTENSION TEXT(".htm")
p = (PTSTR)GetFileNameFromPath (Buffer);
if (!p) {
p = Buffer;
}
if (!_tcschr (p, TEXT('.'))) {
if (SizeOfString (Buffer) + sizeof (DEFAULT_EXTENSION) <= MAX_TCHAR_PATH * sizeof (TCHAR)) {
if (ofn.nFilterIndex >= MAX_EXT + 1) {
ofn.nFilterIndex = 1;
}
StringCat (p, ext[ofn.nFilterIndex - 1]);
}
}
return SaveFlag;
}
UINT
pCreateWordWrappedString (
OUT PTSTR Buffer,
IN PCTSTR Str,
IN UINT FirstLineSize,
IN UINT RestLineSize
)
/*++
Routine Description:
Converts a string to a series of lines, where no line is bigger
than LineSize. If a buffer is not supplied, this function estimates
the number of bytes needed.
If the code page is a far-east code page, then lines are broken at any
multi-byte character, as well as at the spaces.
Arguments:
Buffer - If non-NULL, supplies address of buffer big enough to hold
enlarged, wrapped string. If NULL, parameter is ignored.
Str - Supplies the string that needs to be wrapped.
FirstLineSize - Specifies the max size the first line can be.
RestLineSize - Specifies the max size the remaining lines can be.
Return Value:
The size of the string copied to Buffer including the terminating NULL,
or the size of the needed buffer if Buffer is NULL.
--*/
{
PCTSTR p, Start;
UINT Col;
PCTSTR LastSpace;
CHARTYPE c;
UINT Size;
BOOL PrevCharMb;
UINT LineSize;
LineSize = FirstLineSize;
p = Str;
if (!p)
return 0;
Size = SizeOfString(Str);
if (Buffer) {
*Buffer = 0;
}
while (*p) {
// Beginning of line
Col = 0;
LastSpace = NULL;
Start = p;
PrevCharMb = FALSE;
do {
// Is this a hard-coded line break?
c = _tcsnextc (p);
if (c == TEXT('\r') || c == TEXT('\n')) {
LastSpace = p;
p = _tcsinc (p);
if (c == TEXT('\r') && _tcsnextc (p) == TEXT('\n')) {
p = _tcsinc (p);
} else {
Size += sizeof (TCHAR);
}
c = TEXT('\n');
break;
}
else if (_istspace (c)) {
LastSpace = p;
}
else if (IsLeadByte (*p)) {
// MB chars are usually two cols wide
Col++;
if (PrevCharMb) {
//
// If this char is not punctuation, then we can
// break here
//
if (!IsPunct (c)) {
LastSpace = p;
}
}
PrevCharMb = TRUE;
}
else {
if (PrevCharMb) {
LastSpace = p;
}
PrevCharMb = FALSE;
}
// Continue until line gets too long
Col++;
p = _tcsinc (p);
} while (*p && Col < LineSize);
// If no more text, or line that has no space needs to be broken
if (!(*p) || (c != TEXT('\n') && !LastSpace)) {
LastSpace = p;
}
if (Buffer) {
StringCopyAB (Buffer, Start, LastSpace);
Buffer = GetEndOfString (Buffer);
}
if (*p && c != TEXT('\n')) {
p = LastSpace;
Size += sizeof (TCHAR) * 2;
}
// remove space at start of wrapped line
while (_tcsnextc (p) == TEXT(' ')) {
Size -= sizeof (TCHAR);
p = _tcsinc (p);
}
if (Buffer && *p) {
Buffer = _tcsappend (Buffer, TEXT("\r\n"));
}
LineSize = RestLineSize;
}
return Size;
}
PCTSTR
CreateIndentedString (
IN PCTSTR UnwrappedStr,
IN UINT Indent,
IN INT HangingIndent,
IN UINT LineLen
)
/*++
Routine Description:
Takes an unwrapped string, word-wraps it (via pCreateWordWrappedString),
inserts spaces before each line, optionally skipping the first line.
If the code page is a far-east code page, then lines are broken at any
multi-byte character, as well as at the spaces.
Arguments:
UnwrappedStr - A pointer to the string that is to be word-wrapped and
adjusted with space inserts.
Indent - The number of spaces to insert before each line.
HangingIndent - The adjustment made to the indent after the first line
LineLen - The maximum line size. Spaces must always be smaller
than LineLen (and should be considerably smaller).
FirstLine - If TRUE, the first line is indented. If FALSE, the
first line is skipped ("hanging indent").
Return Value:
A pointer to the indented string, or NULL if MemAlloc failed. The
caller must free the string with MemFree.
--*/
{
UINT Size;
UINT Count;
PCTSTR p, q;
PTSTR d;
PTSTR Dest;
PTSTR Str;
UINT FirstLineLen;
UINT RestLineLen;
UINT FirstLineIndent;
UINT RestLineIndent;
UINT RealIndent;
if (!UnwrappedStr) {
return NULL;
}
MYASSERT ((INT)Indent + HangingIndent >= 0);
FirstLineIndent = Indent;
RestLineIndent = Indent + HangingIndent;
MYASSERT (LineLen > FirstLineIndent);
MYASSERT (LineLen > RestLineIndent);
FirstLineLen = LineLen - FirstLineIndent;
RestLineLen = LineLen - RestLineIndent;
//
// Estimate line size, then do the wrapping
//
Str = (PTSTR) MemAlloc (
g_hHeap,
0,
pCreateWordWrappedString (
NULL,
UnwrappedStr,
FirstLineLen,
RestLineLen
)
);
if (!Str) {
return NULL;
}
pCreateWordWrappedString (
Str,
UnwrappedStr,
FirstLineLen,
RestLineLen
);
if (!FirstLineIndent && !RestLineIndent) {
return Str;
}
//
// Count number of lines
//
for (Count = 1, p = Str ; *p ; p = _tcsinc (p)) {
if (*p == TEXT('\n')) {
Count++;
}
}
//
// Allocate a new buffer that is big enough for all the indented text
//
Size = max (FirstLineIndent, RestLineIndent) * Count + SizeOfString (Str);
Dest = MemAlloc (g_hHeap, 0, Size);
if (Dest) {
*Dest = 0;
//
// Indent each line
//
p = Str;
d = Dest;
RealIndent = FirstLineIndent;
while (*p) {
for (Count = 0 ; Count < RealIndent ; Count++) {
*d++ = TEXT(' ');
}
q = _tcschr (p, TEXT('\n'));
if (!q) {
q = GetEndOfString (p);
} else {
q = _tcsinc (q);
}
StringCopyAB (d, p, q);
d = GetEndOfString (d);
p = q;
RealIndent = RestLineIndent;
}
}
MemFree (g_hHeap, 0, Str);
return Dest;
}
BOOL
pSaveReportToDisk (
IN HWND Parent,
IN PCTSTR FileSpec,
IN BOOL Html,
IN DWORD MinLevel
)
{
HANDLE File;
BOOL b;
PCTSTR Msg;
//
// Create the report file
//
File = CreateFile (
FileSpec,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (File == INVALID_HANDLE_VALUE) {
LOG ((LOG_ERROR, "Error while saving report to %s", FileSpec));
return FALSE;
}
//
// Save the report text to disk
//
b = FALSE;
Msg = CreateReportText (Html, 70, MinLevel, FALSE);
if (Msg) {
b = WriteFileString (File, Msg);
}
FreeReportText();
//
// Close the file and alert the user to save errors!
//
CloseHandle (File);
return b;
}
BOOL
SaveReport (
IN HWND Parent, OPTIONAL
IN PCTSTR Path OPTIONAL
)
/*++
Routine Description:
Obtains a file name from the user via common Save As dialog,
creates the file and writes an incompatibility list to disk.
Arguments:
Parent - The parent of the common dialog box. Optional If this is NULL,
no UI is displayed.
Path - The path to save to. If this is NULL, parent must be specified.
Return Value:
TRUE if the report was saved, or FALSE if the user canceled the
operation or the save failed. The user is alerted when the
save fails.
--*/
{
TCHAR Buffer[MAX_TCHAR_PATH + 4];
PCTSTR Str;
BOOL b;
DWORD attributes;
PTSTR p;
BOOL saved = FALSE;
BOOL bSaveBothFormats = FALSE;
MYASSERT(Parent != NULL || Path != NULL);
//
// Obtain a path, or use caller-supplied path
//
if (Path) {
attributes = GetFileAttributes(Path);
}
if (!Path || attributes != 0xFFFFFFFF && (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
if (Path) {
StringCopy(Buffer,Path);
AppendPathWack(Buffer);
bSaveBothFormats = TRUE;
} else {
Buffer[0] = 0;
}
Str = GetStringResource (MSG_DEFAULT_REPORT_FILE);
MYASSERT (Str);
if (!Str) {
return FALSE;
}
StringCat (Buffer, Str);
FreeStringResource (Str);
} else {
StringCopy(Buffer,Path);
}
if (Parent) {
if (!pGetSaveAsName (Parent, Buffer)) {
return FALSE;
}
}
p = _tcsrchr (Buffer, TEXT('.'));
if (!p || _tcschr (p, TEXT('\\'))) {
p = GetEndOfString (Buffer);
}
//
// Save as text, if extension is .txt
//
if (StringIMatch (p, TEXT(".txt"))) {
b = pSaveReportToDisk (Parent, Buffer, FALSE, REPORTLEVEL_VERBOSE);
if (Parent) {
saved = b;
}
} else {
b = TRUE;
}
//
// Save as HTML unless user chose to save as .txt
//
if (b && !saved) {
if (!Parent && p) {
StringCopy (p, TEXT(".htm"));
}
b = pSaveReportToDisk (Parent, Buffer, TRUE, REPORTLEVEL_VERBOSE);
}
if (bSaveBothFormats) {
StringCopy (p, TEXT(".txt"));
b = pSaveReportToDisk (Parent, Buffer, FALSE, REPORTLEVEL_VERBOSE);
}
if (!b) {
if (Parent) {
ResourceMessageBox (Parent, MSG_CANT_SAVE, MB_OK, NULL);
}
}
return TRUE;
}
VOID
pFreePrintMem (
IN OUT PRINTDLG *ppd
)
/*++
Routine Description:
Frees all memory associated with PRINTDLG structure.
Arguments:
ppd - Pointer to PRINTDLG structure.
Return Value:
none
--*/
{
if (ppd->hDevMode) {
GlobalFree (ppd->hDevMode);
ppd->hDevMode = NULL;
}
if(ppd->hDevNames) {
GlobalFree (ppd->hDevNames);
ppd->hDevNames = NULL;
}
}
VOID
pInitPrintDlgStruct (
OUT PRINTDLG *ppd,
IN HWND Parent,
IN DWORD Flags
)
/*++
Routine Description:
Initializes PRINTDLG structure, setting the owner window and
print dialog flags.
Arguments:
ppd - Pointer to PRINTDLG structure to be initialized
Parent - Handle to parent window for the dialog
Flags - PrintDlg flags (PD_*)
Return Value:
none (structure is initialized)
--*/
{
ZeroMemory (ppd, sizeof (PRINTDLG));
ppd->lStructSize = sizeof (PRINTDLG);
ppd->hwndOwner = Parent;
ppd->Flags = Flags;
ppd->hInstance = g_hInst;
}
HDC
pGetPrintDC (
IN HWND Parent
)
/*++
Routine Description:
Displays common dialog box to the user, and if the user chooses
a printer, returns a device context handle.
Arguments:
Parent - The parent of the common dialog to be displayed
Return Value:
A handle to a device context of the chosen printer, or NULL if the
user canceled printing.
--*/
{
PRINTDLG pd;
pInitPrintDlgStruct (
&pd,
Parent,
PD_ALLPAGES|PD_NOPAGENUMS|PD_NOSELECTION|PD_RETURNDC
);
if (PrintDlg (&pd)) {
pFreePrintMem (&pd);
return pd.hDC;
}
return NULL;
}
typedef struct {
HDC hdc;
INT Page;
INT Line;
RECT HeaderRect; // in logical units
RECT PrintableRect; // in logical units
TEXTMETRIC tm;
INT LineHeight;
INT TotalLines; // printable height/line height
INT TotalCols; // printable width/char width
HFONT FontHandle;
BOOL PageActive;
} PRINT_POSITION, *PPRINT_POSITION;
PCTSTR
pDrawLineText (
IN OUT PPRINT_POSITION PrintPos,
IN PCTSTR Text,
IN DWORD Flags,
IN BOOL Header
)
/*++
Routine Description:
Draws a single line of text on a printer device context and
returns a pointer to the next line or nul terminator.
Arguments:
PrintPos - A pointer to the current PRINT_POSITION structure
that gives page position settings.
Text - A pointer to the text string containing the line.
Flags - Additional DrawText flags (DT_LEFT, DT_CENTER,
DT_RIGHT and/or DT_RTLREADING)
Header - TRUE if text should be written to header, or
FALSE if it should be written to the current line
Return Value:
A pointer to the next line within the string, a pointer to the
nul terminator, or NULL if an error occurred.
--*/
{
RECT rect;
PCTSTR p;
CHARTYPE ch;
if (Header) {
CopyMemory (&rect, &PrintPos->HeaderRect, sizeof (RECT));
} else {
CopyMemory (&rect, &PrintPos->PrintableRect, sizeof (RECT));
rect.top += PrintPos->LineHeight * PrintPos->Line;
}
Flags = Flags & (DT_CENTER|DT_LEFT|DT_RIGHT|DT_RTLREADING);
Flags |= DT_TOP|DT_EDITCONTROL|DT_NOPREFIX|DT_EXTERNALLEADING;
for (ch = 0, p = Text ; *p ; p = _tcsinc (p)) {
ch = _tcsnextc (p);
if (ch == TEXT('\n') || ch == TEXT('\r')) {
break;
}
}
if (p != Text) {
if (!DrawText (PrintPos->hdc, Text, p - Text, &rect, Flags)) {
LOG ((LOG_ERROR, "Failure while sending text to printer."));
return NULL;
}
}
// Skip past line break
if (ch == TEXT('\r')) {
p = _tcsinc (p);
ch = _tcsnextc (p);
}
if (ch == TEXT('\n')) {
p = _tcsinc (p);
}
return p;
}
BOOL
pPrintString (
IN OUT PPRINT_POSITION PrintPos,
IN PCTSTR MultiLineString
)
/*++
Routine Description:
Dumps a multi-line string to the printer. If necessary, the
string may be printed on a new page. This function tries to
eliminate widows and orphans by printing the entire string
on the same page if possible.
Arguments:
PrintPos - The current position information, describing
the printer device context, page number, line
number and metrics.
MultiLineString - A pointer to the string to print.
Return Value:
TRUE if printing was successful, or FALSE if an error occurrred.
--*/
{
INT LineCount;
PCTSTR p;
PCTSTR Str;
PCTSTR Args[1];
TCHAR Buffer[32];
CHARTYPE ch;
HDC hdc;
hdc = PrintPos->hdc;
//
// Count lines in MultiLineString
//
ch = TEXT('\n');
for (LineCount = 0, p = MultiLineString ; *p ; p = _tcsinc (p)) {
ch = _tcsnextc (p);
if (ch == TEXT('\n')) {
LineCount++;
}
}
if (ch != TEXT('\n')) {
LineCount++;
}
//
// Widow/orphan suppression: If all lines do not fit on
// the page, and we are more than half way down the page,
// roll to the next page.
//
if (PrintPos->Line + LineCount > PrintPos->TotalLines) {
if (PrintPos->Line > PrintPos->TotalLines / 2) {
// Move to next page
EndPage (hdc);
PrintPos->PageActive = FALSE;
PrintPos->Page++;
PrintPos->Line = 0;
}
}
//
// Send each line in MultiLineString
//
while (*MultiLineString) {
//
// Draw header if necessary
//
if (!PrintPos->Line) {
StartPage (hdc);
PrintPos->PageActive = TRUE;
SetMapMode (hdc, MM_TWIPS);
SelectObject (hdc, PrintPos->FontHandle);
SetBkMode (hdc, TRANSPARENT);
//Rectangle (hdc, PrintPos->HeaderRect.left, PrintPos->HeaderRect.top, PrintPos->HeaderRect.right, PrintPos->HeaderRect.bottom);
//Rectangle (hdc, PrintPos->PrintableRect.left, PrintPos->PrintableRect.top, PrintPos->PrintableRect.right, PrintPos->PrintableRect.bottom);
wsprintf (Buffer, TEXT("%u"), PrintPos->Page);
Args[0] = Buffer;
// Left side
Str = ParseMessageID (
MSG_REPORT_HEADER_LEFT,
Args
);
if (Str && *Str) {
p = pDrawLineText (PrintPos, Str, DT_LEFT, TRUE);
if (!p) {
return FALSE;
}
}
// Center
Str = ParseMessageID (
MSG_REPORT_HEADER_CENTER,
Args
);
if (Str && *Str) {
p = pDrawLineText (PrintPos, Str, DT_CENTER, TRUE);
if (!p) {
return FALSE;
}
}
// Right side
Str = ParseMessageID (
MSG_REPORT_HEADER_RIGHT,
Args
);
if (Str && *Str) {
p = pDrawLineText (PrintPos, Str, DT_RIGHT, TRUE);
if (!p) {
return FALSE;
}
}
}
//
// Draw line
//
MultiLineString = pDrawLineText (
PrintPos,
MultiLineString,
DT_LEFT,
FALSE
);
if (!MultiLineString) {
return FALSE;
}
PrintPos->Line++;
if (PrintPos->Line >= PrintPos->TotalLines) {
EndPage (hdc);
PrintPos->PageActive = FALSE;
PrintPos->Page++;
PrintPos->Line = 0;
}
}
return TRUE;
}
VOID
pCalculatePageMetrics (
IN OUT PPRINT_POSITION PrintPos
)
/*++
Routine Description:
Calculates all the page metrics (margins, header position,
line count, col count, etc). Positions are in TWIPS and
counts are in characters or lines.
Arguments:
PrintPos - Pointer to PRINT_POSITION structure which gives
the printer device context. Structure receives
metrics.
Return Value:
none
--*/
{
INT WidthPixels, HeightPixels;
INT DpiX, DpiY;
INT UnprintableLeftPixels, UnprintableTopPixels;
POINT TempPoint;
HDC hdc;
hdc = PrintPos->hdc;
//
// Make no assumptions about hdc
//
SetMapMode (hdc, MM_TWIPS);
SelectObject (hdc, PrintPos->FontHandle);
GetTextMetrics (hdc, &PrintPos->tm);
//
// Get device dimensions
//
DpiX = GetDeviceCaps (hdc, LOGPIXELSX);
DpiY = GetDeviceCaps (hdc, LOGPIXELSY);
UnprintableLeftPixels = GetDeviceCaps (hdc, PHYSICALOFFSETX);
UnprintableTopPixels = GetDeviceCaps (hdc, PHYSICALOFFSETY);
WidthPixels = GetDeviceCaps (hdc, PHYSICALWIDTH);
HeightPixels = GetDeviceCaps (hdc, PHYSICALHEIGHT);
// Calulate 3/4 inch left/right margins
PrintPos->HeaderRect.left = (DpiX * 3 / 4) - UnprintableLeftPixels;
PrintPos->HeaderRect.right = WidthPixels - (DpiX * 3 / 4) - UnprintableLeftPixels;
// Calculate 1/2 inch top margin for header
PrintPos->HeaderRect.top = (DpiY / 2) - UnprintableTopPixels;
PrintPos->HeaderRect.bottom = DpiY - UnprintableTopPixels;
// Convert pixels (device units) into logical units
DPtoLP (hdc, (LPPOINT) (&PrintPos->HeaderRect), 2);
// Copy header's left & right margins to printable rect
// Copy header's bottom margin to printable rect's top margin
PrintPos->PrintableRect.left = PrintPos->HeaderRect.left;
PrintPos->PrintableRect.right = PrintPos->HeaderRect.right;
PrintPos->PrintableRect.top = PrintPos->HeaderRect.bottom;
// Calculate printable rect's bottom margin (3/4 inch)
TempPoint.x = 0;
TempPoint.y = HeightPixels - (DpiY * 3 / 4) - UnprintableTopPixels;
DPtoLP (hdc, &TempPoint, 1);
PrintPos->PrintableRect.bottom = TempPoint.y;
PrintPos->LineHeight = -(PrintPos->tm.tmHeight + PrintPos->tm.tmInternalLeading + PrintPos->tm.tmExternalLeading);
MYASSERT (PrintPos->LineHeight);
PrintPos->TotalLines = (PrintPos->PrintableRect.bottom - PrintPos->PrintableRect.top) / PrintPos->LineHeight;
PrintPos->TotalCols = (PrintPos->PrintableRect.right - PrintPos->PrintableRect.left) / PrintPos->tm.tmAveCharWidth;
}
BOOL
PrintReport (
IN HWND Parent,
IN DWORD Level
)
/*++
Routine Description:
Obtains a printer from the user via common Print dialog,
starts the print job and sends an incompatibility list to
one or more pages.
Arguments:
Parent - A handle to the parent of the print dialog
Return Value:
TRUE if printing completed, or FALSE if it was canceled or an
error occurred.
--*/
{
HDC hdc;
PRINT_POSITION pp;
LOGFONT Font;
BOOL b;
DOCINFO di;
INT JobId;
PCTSTR Msg;
HANDLE DefaultUiFont;
hdc = pGetPrintDC (Parent);
if (!hdc) {
return FALSE; // user canceled print dialog
}
if (!BeginMessageProcessing()) {
// unexpected out-of-memory
DeleteDC (hdc);
return FALSE;
}
//
// Initialize PRINT_POSITION
//
b = TRUE;
TurnOnWaitCursor();
ZeroMemory (&pp, sizeof (pp));
pp.hdc = hdc;
pp.Page = 1;
//
// Start doc
//
ZeroMemory (&di, sizeof (di));
di.cbSize = sizeof (di);
di.lpszDocName = GetStringResource (MSG_REPORT_DOC_NAME);
MYASSERT (di.lpszDocName);
if (di.lpszDocName) {
JobId = StartDoc (hdc, &di);
if (!JobId) {
LOG ((LOG_ERROR, "Cannot start print job."));
ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL);
b = FALSE;
}
} else {
//
// not enough memory
//
JobId = 0;
b = FALSE;
}
if (b) {
//
// Create font
//
ZeroMemory (&Font, sizeof (Font));
DefaultUiFont = (HFONT) GetStockObject (DEFAULT_GUI_FONT);
if (DefaultUiFont) {
GetObject (DefaultUiFont, sizeof (Font), &Font);
Font.lfHeight = 12 * 20; // height in TWIPS (1/20 of a point)
Font.lfWeight = FW_NORMAL;
Font.lfOutPrecision = OUT_TT_PRECIS;
Font.lfPitchAndFamily = FIXED_PITCH|FF_MODERN;
pp.FontHandle = CreateFontIndirect (&Font);
if (!pp.FontHandle) {
LOG ((LOG_ERROR, "Cannot create font for print operation."));
//
// deferred this call to the end
//
//ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL);
b = FALSE;
}
} else {
b = FALSE;
}
}
if (b) {
//
// Create page metrics
//
pCalculatePageMetrics (&pp);
DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: LineHeight=%i", pp.LineHeight));
DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: TotalLines=%i", pp.TotalLines));
DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: TotalCols=%i", pp.TotalCols));
DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: Header rect: (%i, %i)-(%i, %i)", pp.HeaderRect.left, pp.HeaderRect.top, pp.HeaderRect.right, pp.HeaderRect.bottom));
DEBUGMSG ((DBG_PRINTSAVE, "PrintReport: Printable rect: (%i, %i)-(%i, %i)", pp.PrintableRect.left, pp.PrintableRect.top, pp.PrintableRect.right, pp.PrintableRect.bottom));
//
// Print the report
//
Msg = CreateReportText (FALSE, pp.TotalCols, Level, FALSE);
if (Msg) {
b = pPrintString (&pp, Msg);
}
FreeReportText();
}
if (JobId) {
if (b) {
if (pp.PageActive) {
EndPage (hdc);
}
EndDoc (hdc);
} else {
AbortDoc (hdc);
}
}
DeleteDC (hdc);
if (pp.FontHandle) {
DeleteObject (pp.FontHandle);
}
TurnOffWaitCursor();
if (!b) {
ResourceMessageBox (Parent, MSG_CANT_PRINT, MB_OK, NULL);
}
EndMessageProcessing();
return b;
}