Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

799 lines
18 KiB

/*****************************************************************************
*
* PRINT.C
*
* Copyright (C) Microsoft Corporation 1990.
* All Rights reserved.
*
******************************************************************************
*
* Module Intent
*
* Code to print help topics.
*
*****************************************************************************/
#include "help.h"
#pragma hdrstop
#include <string.h>
#include "inc\printset.h"
#define MAX_PRINTING 100
#define fPrintInsert 0
#define fPrintRegular 1
typedef BOOL (APIENTRY* DLGPRINTPROC)(LPPRINTDLG);
typedef DWORD (APIENTRY* COMMDLGEXTENDEDERROR)(VOID);
BOOL (APIENTRY* pPrintDlg)(LPPRINTDLG);
DWORD (APIENTRY* pCommDlgExtendedError)(void);
// Prototypes
INT EXPORT AbortPrint(HDC, INT);
BOOL EXPORT AbortPrintDlg(HWND, UINT, WPARAM, LPARAM);
static int STDCALL DyPrintLayoutHde(HDE, BOOL, RECT, int, int *);
void STDCALL ReportComDlgError(DWORD Error);
// Global Variables:
BOOL fQuitHelp; // Set to TRUE if we should quit help
static HDC hdcPrint;
static BOOL fStartPage;
#ifndef NO_PRAGMAS
#pragma data_seg(".text", "CODE")
#endif
const char txtPrintDlg[] = "PrintDlgA";
const char txtCommDlgError[] = "CommDlgExtendedError";
#ifndef NO_PRAGMAS
#pragma data_seg()
#endif
PRINTDLG* ppd;
extern int cxAspect, cyAspect, cBitCount, cPlanes; // used in bitmap.c
HDC STDCALL HdcGetPrinter(void)
{
#ifdef _DEBUG
LPSTR qchPrinter, qchDriver, qchPort;
#endif
HDC hdc;
PDEVMODE lpDevMode;
LPDEVNAMES lpDevNames;
if (!InitPrintDialogStruct(ahwnd[iCurWindow].hwndParent))
return NULL;
// Let the user know where we are printing to, and give them the option
// of changing things like DPI if the driver supports it.
ASSERT(pPrintDlg);
#ifdef _DEBUG
ppd->Flags = PD_NOPAGENUMS | PD_NOSELECTION |
PD_USEDEVMODECOPIES;
#else
ppd->Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_DISABLEPRINTTOFILE |
PD_USEDEVMODECOPIES;
#endif
for (;;) {
DWORD result = pPrintDlg(ppd);
if (result == 0 && !PrintDlgFailed()) // did the user cancel?
return NULL;
else
break;
}
/*
* Commdlg is around and we did get DevNames info. Use it in creating
* our print DC.
*/
if (ppd->hDevNames) {
if (ppd->hDevMode)
lpDevMode = (DEVMODE*) GlobalLock(ppd->hDevMode);
lpDevNames = (LPDEVNAMES) GlobalLock(ppd->hDevNames);
}
else {
Error(wERRS_NOPRINTSETUP, wERRA_RETURN);
return NULL;
}
#ifdef _DEBUG
qchDriver = (LPSTR) lpDevNames + lpDevNames->wDriverOffset;
qchPrinter = (LPSTR) lpDevNames + lpDevNames->wDeviceOffset;
qchPort = (LPSTR) lpDevNames + lpDevNames->wOutputOffset;
#endif
hdc = CreateDC((LPSTR) lpDevNames + lpDevNames->wDriverOffset,
(LPSTR) lpDevNames + lpDevNames->wDeviceOffset,
(LPSTR) lpDevNames + lpDevNames->wOutputOffset, lpDevMode);
GlobalUnlock(ppd->hDevNames);
GlobalUnlock(ppd->hDevMode);
if (!hdc)
Error(wERRS_OOM, wERRA_RETURN);
return hdc;
}
/*
* AbortPrintDlg() printing dialog proc (has a cancle button on it)
*
* Procedure:
*
* globals used:
* fAbortPrint -- sets this when the user hits cancel.
* hdlgPrint -- set to NULL when dialog has been ended.
*/
BOOL EXPORT AbortPrintDlg(HWND hwndDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
switch (wMsg) {
case WM_COMMAND:
EnableWindows();
DestroyWindow(hwndDlg);
break;
case WM_DESTROY:
hdlgPrint = NULL;
fAbortPrint = TRUE;
break;
case WM_INITDIALOG:
ChangeDlgFont(hwndDlg);
break;
default:
return FALSE;
}
return TRUE;
}
/*
* AbortProc() printing abort procedure
*
* globals used:
* fAbortPrint -- indicates the user hit CANCEL on the print dialog
* hdlgPrint -- handle of the print dialog
*
*/
int CALLBACK AbortPrint(HDC hdc, int wCode)
{
MSG msg;
while (!fAbortPrint && PeekMessage(&msg, (HWND) 0, 0, 0, PM_REMOVE)) {
if (!hdlgPrint || !IsDialogMessage(hdlgPrint, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return !fAbortPrint;
}
/***************
**
** void STDCALL PrintHde( hde )
**
** purpose
** Main entry point into printing. This will cause the current
** topic, passed in the hde, to be printed.
**
** arguments
** HDE hde -- handle to the current topic to be printed.
**
** return value
** none
**
** notes
** This function is not re-entrant.
**
***************/
BOOL STDCALL PrintHde(HDE hdeTopic)
{
HDE hde;
QDE qde;
char rgchDialogText[MAX_PRINTING];
RECT rct;
PT pt;
int spErr; // Spooler error
int dyTopOfPage;
TLP tlp;
DOCINFO di;
if (fQuitHelp)
return FALSE;
WaitCursor();
if (hwndNote)
SetWindowPos(hwndNote, HWND_NOTOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
/***********************************************************************
*
* CAUTION: While the hourglass is up, we must not call any
* code that can yield control, or wCursor may become invalid.
* This includes Error() and PeekMessage().
*
************************************************************************/
if (fMultiPrinting) {
if ((hde = HdeCreate(NULL, hdeTopic, dePrint)) == NULL) {
spErr = 1; // The appropriate error message has already
goto PrintError; // been displayed.
}
}
else {
// Set things up for an error condition:
ASSERT(hdlgPrint == NULL);
hde = NULL;
if ((hdcPrint = HdcGetPrinter()) == NULL ||
(hde = HdeCreate(NULL, hdeTopic, dePrint)) == NULL) {
spErr = 1; // The appropriate error message has already
goto PrintError; // been displayed.
}
}
qde = QdeFromGh(hde);
qde->hdc = hdcPrint;
cxAspect = qde->wXAspectMul = GetDeviceCaps(hdcPrint, LOGPIXELSX);
cyAspect = qde->wYAspectMul = GetDeviceCaps(hdcPrint, LOGPIXELSY);
cBitCount = GetDeviceCaps(hdcPrint, BITSPIXEL);
cPlanes = GetDeviceCaps(hdcPrint, PLANES);
// Review: We copy the hwnd from hdeTopic because our current hde does
// not have one. Way down in the layout code, if the topic has an
// annotation, the hwnd will be used, (PtAnnoLim()) and therefore must
// be present. Is there a better way for PtAnnoLim to do it's job? Is this
// the right HWND to use? if not, what?
qde->hwnd = QdeFromGh(hdeTopic)->hwnd;
/*
* Review -- If hTitle is nil, then it could be because key elements
* in the DE have not yet been initized. Therefore, we will make sure
* these elements get initialized here. This may be considered a hack.
*/
if (qde->top.hTitle == NULL) {
rct.top = rct.left = 0;
rct.right = rct.bottom = 100;
SetSizeHdeQrct(hde, &rct, TRUE);
}
if (!fMultiPrinting) {
// Set up dialog for abort function
hdlgPrint = CreateDialog(hInsNow, MAKEINTRESOURCE(ABORTPRINTDLG),
ahwnd[iCurWindow].hwndParent, AbortPrintDlg);
if (hdlgPrint == NULL) {
spErr = SP_OUTOFMEMORY;
goto PrintError;
}
fAbortPrint = FALSE;
}
DisableWindows();
// Set text fields in dialog box.
{
char szTitle[MAX_TOPIC_TITLE];
/*
* The "default" dialog box text is really the template for the
* final text.
*/
GetCurrentTitleQde(qde, szTitle, sizeof(szTitle));
ASSERT(!IsEmptyString(szTitle));
wsprintf(rgchDialogText,
GetStringResource(sidPrintText), szTitle);
ASSERT(lstrlen(rgchDialogText) < sizeof(rgchDialogText));
SetDlgItemText(hdlgPrint, ABORTPRINTTEXT, rgchDialogText);
}
ShowWindow(hdlgPrint, SW_SHOW);
UpdateWindow(hdlgPrint);
/*
* Set up printing rectangle, using one inch margins (assuming we're in
* MM_TEXT mode, where 1 logical unit = 1 pixel.)
*/
Escape(qde->hdc, GETPHYSPAGESIZE, 0, NULL, (LPSTR) &pt);
ASSERT(GetMapMode(qde->hdc) == MM_TEXT);
rct.left = GetDeviceCaps(qde->hdc, LOGPIXELSX);
rct.right = pt.x - rct.left;
ASSERT(rct.right > rct.left);
rct.top = dyTopOfPage = GetDeviceCaps(qde->hdc, LOGPIXELSY);
rct.bottom = pt.y - rct.top;
ASSERT(rct.bottom > rct.top);
qde->rct = rct;
RemoveWaitCursor();
/*
* Print document. For the STARTDOC call, we need to create a unique
* string.
*/
SetAbortProc(qde->hdc, AbortPrint);
ZeroMemory(&di, sizeof(DOCINFO));
di.cbSize = sizeof(DOCINFO);
di.lpszDocName = rgchDialogText;
spErr = StartDoc(qde->hdc, &di);
fStartPage = FALSE;
// Non-scrolling region support
if (FTopicHasNSR(hde) && spErr > 0 && !fAbortPrint) {
HSGC hsgc;
/*
* HACK ALERT! Currently we have no way, given a DE, to tell which
* layout to use, other than the DE type or the value of
* qde->top.vaCurr. Because we do a jump here, we rely on the latter.
*/
GetTLPNSRStartHde(hde, &tlp);
JumpTLP(hde, tlp);
rct.top += DyPrintLayoutHde(hde, fPrintInsert, rct, dyTopOfPage, &spErr);
#if 0
/* Draw a line across bottom of NSR */
hsgc = (HSGC) HsgcFromQde(qde);
FSetPen(hsgc, 1, coBLACK, coBLACK, wOPAQUE, roCOPY, wPenSolid);
GotoXY(hsgc, rct.left, rct.top);
DrawTo(hsgc, rct.right, rct.top);
FreeHsgc(hsgc);
#else
Unreferenced(hsgc);
#endif /* 0 */
++rct.top;
SetSizeHdeQrct(hde, &rct, FALSE);
}
if (spErr > 0 && !fAbortPrint) {
if (FTopicHasSR(hde)) {
GetTLPTopicStartHde(hde, &tlp);
JumpTLP(hde, tlp);
// NOTE: DyPrintLayoutHde may modify the DE rect.
DyPrintLayoutHde(hde, fPrintRegular, rct, dyTopOfPage, &spErr);
}
else {
/*
* Special Case: There is no scrolling region, so force out
* whatever we inserted for the non-scrolling region above.
*/
ASSERT(FTopicHasNSR(hde));
spErr = EndPage(qde->hdc);
}
}
if (spErr > 0 && !fAbortPrint)
EndDoc(qde->hdc);
PrintError:
if (!fMultiPrinting) {
if (hdcPrint != NULL) {
DeleteDC(hdcPrint);
hdcPrint = NULL;
}
if (hde != NULL)
DestroyHde(hde);
if (hdlgPrint != NULL)
SendMessage(hdlgPrint, WM_COMMAND, IDCANCEL, 0L);
if (hwndNote)
SetWindowPos(hwndNote, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
ASSERT(hdeTopic);
if (hdeTopic) {
cxAspect = GetDeviceCaps(QdeFromGh(hdeTopic)->hdc, LOGPIXELSX);
cyAspect = GetDeviceCaps(QdeFromGh(hdeTopic)->hdc, LOGPIXELSY);
cBitCount = GetDeviceCaps(QdeFromGh(hdeTopic)->hdc, BITSPIXEL);
cPlanes = GetDeviceCaps(QdeFromGh(hdeTopic)->hdc, PLANES);
}
// This was possibly set, if print was called by macro
ClearMacroFlag();
if (fQuitHelp)
DestroyHelp();
else {
if (fMultiPrinting && (spErr < 0 || fAbortPrint)) {
fMultiPrinting = FALSE;
EnableWindows();
if (spErr != SP_ERROR && spErr != SP_OUTOFMEMORY)
return FALSE;
}
switch (spErr) {
case SP_ERROR:
if (!fAbortPrint)
Error(wERRS_PRINT_ERROR, wERRA_RETURN);
return FALSE;
case SP_OUTOFMEMORY:
Error(wERRS_OOM, wERRA_RETURN);
return FALSE;
default:
if (fMultiPrinting)
EnableWindows();
return TRUE;
}
}
}
static int STDCALL DyPrintLayoutHde(HDE hde, BOOL fMode, RECT rct,
int dyTopOfPage, int* qspErr)
{
QDE qde;
POINT pt;
POINT dpt;
int spErr;
int dyExtra = 0;
BOOL fHaveFullPage;
ASSERT(hde);
qde = QdeFromGh(hde);
ASSERT(qspErr);
spErr = *qspErr;
while (spErr > 0 && !fAbortPrint) {
if (!fStartPage) {
spErr = StartPage(qde->hdc);
if (spErr <= 0)
break;
fStartPage = TRUE;
}
if (fMode == fPrintInsert) {
pt.x = 0;
/*
* NOTE: For NSRs longer than one page, this may cause the
* last frame on a page to be duplicated on the next page. We
* don't expect or care about NSRs longer than one page.
*/
pt.y = min(qde->rct.bottom - qde->rct.top,
DyGetLayoutHeightHde(hde));
}
else {
pt.x = 0;
pt.y = DyCleanLayoutHeight(qde);
// Adjust pt.y if it is too small or negative
if (pt.y < (qde->rct.bottom - qde->rct.top) / 2)
pt.y = qde->rct.bottom - qde->rct.top;
}
dyExtra = pt.y;
// Draw appropriate rectangle
rct.bottom = rct.top + pt.y - 1;
/*
* fHaveFullPage handles the case where our insertion has
* completely filled one page. In this case, we need to force a new
* page.
*/
fHaveFullPage = (rct.bottom + 1 == qde->rct.bottom);
/* NOTE:
* Previously, we have been clipping our output to rct.
* Unfortunately, clipping rectangles are scaled by the printer's
* scaling factor (even though nothing else is), and trying to get
* that scaling factor was causing problems with the rest of the
* print job. Since DyCleanLayoutHeight should take care of most
* clipping problems, I have decided to blow off clipping.
*/
RefreshHde(hde, &rct);
if (fMode == fPrintRegular || fHaveFullPage) {
spErr = EndPage(qde->hdc);
fStartPage = FALSE;
if (spErr <= 0)
break;
}
/*
* If our initial rectangle began smaller than a page height,
* (because of a previous insertion), then reset to full page height
* to prepare for next page.
*/
if (rct.top != dyTopOfPage) {
rct.top = dyTopOfPage;
SetSizeHdeQrct( hde, &rct, TRUE);
}
// Change pt.y to a negative value to scroll down.
pt.y = -pt.y;
dpt = DptScrollLayout(qde, pt);
if (dpt.y != pt.y)
break; // Reached end of topic
}
*qspErr = spErr;
return dyExtra;
}
BOOL STDCALL InitMPrint(void)
{
if (hwndNote || fHelp == POPUP_HELP)
return FALSE; // no multiple printing from a popup
if ((hdcPrint = HdcGetPrinter()) == NULL)
return FALSE;
// Set up dialog for abort function
fAbortPrint = FALSE;
fMultiPrinting = TRUE;
hdlgPrint = CreateDialog(hInsNow, MAKEINTRESOURCE(ABORTPRINTDLG),
ahwnd[MAIN_HWND].hwndParent, AbortPrintDlg);
// REVIEW: does this do any good?
SetWindowPos(hdlgPrint, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
if (!hdlgPrint) {
EndMPrint();
return FALSE;
}
return TRUE;
}
void STDCALL EndMPrint(void)
{
if (hdlgPrint)
SendMessage(hdlgPrint, WM_COMMAND, IDCANCEL, 0L);
if (hdcPrint) {
DeleteDC(hdcPrint);
hdcPrint = NULL;
}
fMultiPrinting = FALSE;
}
BOOL STDCALL MPrintHash(PCSTR pszFile, HASH hash)
{
if (!fMultiPrinting || fAbortPrint || fQuitHelp)
return FALSE;
FJumpHash(pszFile, hash);
FlushMessageQueue(0);
return PrintHde(HdeGetEnv());
}
BOOL STDCALL MPrintId(PCSTR pszFile, PCSTR pszCtx)
{
if (!fMultiPrinting || fAbortPrint || fQuitHelp)
return FALSE;
if (IsEmptyString(pszCtx)) // empty means pszFile is the topic id
FJumpId(txtZeroLength, pszFile);
else
FJumpId(pszFile, pszCtx);
FlushMessageQueue(0);
return PrintHde(HdeGetEnv());
}
#ifdef _DEBUG
/***************************************************************************
FUNCTION: ReportComDlgError
PURPOSE: For debug version, specify what the error constant is. For
non-debug version, report an internal error.
RETURNS:
COMMENTS:
MODIFICATION DATES:
16-Sep-1991 [ralphw]
***************************************************************************/
#include <cderr.h>
static PSTR apszCDErr[] = {
"CDERR_GENERALCODES",
"CDERR_STRUCTSIZE",
"CDERR_INITIALIZATION",
"CDERR_NOTEMPLATE",
"CDERR_NOHINSTANCE",
"CDERR_LOADSTRFAILURE",
"CDERR_FINDRESFAILURE",
"CDERR_LOADRESFAILURE",
"CDERR_LOCKRESFAILURE",
"CDERR_MEMALLOCFAILURE",
"CDERR_MEMLOCKFAILURE",
"CDERR_NOHOOK"
};
static PSTR apszFNErr[] = {
"FNERR_FILENAMECODES",
"FNERR_SUBCLASSFAILURE",
"FNERR_INVALIDFILENAME",
"FNERR_BUFFERTOOSMALL"
};
/***************************************************************************
FUNCTION: ReportComDlgError
PURPOSE: Display the constant name of the common dialog box error
RETURNS:
COMMENTS:
MODIFICATION DATES:
16-Sep-1991 [ralphw]
***************************************************************************/
void STDCALL ReportComDlgError(DWORD err)
{
char szMsg[100];
strcpy(szMsg, "Common Dialog Box error: ");
if (err >= CDERR_GENERALCODES && err <= PDERR_PRINTERCODES)
strcat(szMsg, apszCDErr[err]);
else if (err >= FNERR_FILENAMECODES && err <= FRERR_FINDREPLACECODES)
strcat(szMsg, apszFNErr[err - FNERR_FILENAMECODES]);
else
wsprintf(szMsg + strlen(szMsg), "%lu", err);
AuthorMsg(szMsg, NULL);
return;
}
#endif
BOOL STDCALL InitPrintDialogStruct(HWND hwnd)
{
HLIBMOD hmodule;
if (!ppd) {
ppd = (PRINTDLG*) lcCalloc(sizeof(PRINTDLG));
if (!ppd) {
Error(wERRS_OOM, wERRA_RETURN);
return FALSE;
}
ppd->lStructSize = sizeof(PRINTDLG);
ppd->hInstance = hInsNow;
}
ppd->hwndOwner = hwnd;
/*
* Check if we have picked up a DEVNAMES structure previously from
* commdlg during print setup (or here).
*/
if (!ppd->hDevMode) {
if (!pPrintDlg) {
if ((hmodule = HFindDLL(txtCommDlg, FALSE))) {
pPrintDlg = (DLGPRINTPROC) GetProcAddress(hmodule,
txtPrintDlg);
pCommDlgExtendedError =
(COMMDLGEXTENDEDERROR) GetProcAddress(hmodule,
txtCommDlgError);
}
else {
Error(wERRS_MISSINGCOMMDLG, wERRA_RETURN);
return NULL;
}
if (!pPrintDlg || !pCommDlgExtendedError) {
Error(wERRS_CORRUPTCOMMDLG, wERRA_RETURN);
return NULL;
}
}
ppd->Flags = PD_RETURNDEFAULT | PD_PRINTSETUP;
pPrintDlg(ppd);
if (!ppd->hDevNames) {
Error(wERRS_NOPRINTSETUP, wERRA_RETURN);
return FALSE;
}
}
return TRUE;
}
/***************************************************************************
FUNCTION: PrintDlgFailed
PURPOSE: Determins cause of PrintDlg returning 0
PARAMETERS:
void
RETURNS: TRUE to call the dialog again
COMMENTS:
MODIFICATION DATES:
29-Sep-1994 [ralphw]
***************************************************************************/
BOOL STDCALL PrintDlgFailed(void)
{
DWORD errno = pCommDlgExtendedError();
if (errno) {
/*
* There are quite a number of extended errors from commdlg
* and it's not clear what all will come back from Printer Setup.
* Many of them have to do with commdlg encountering resource
* limits. We punt on these and report a Print Setup error msg.
* These two have to do with someone changing the printer
* settings on us while we're running. The error handling here
* was drawn from the win 3.1 notepad.exe sources.
*/
if (errno == PDERR_PRINTERNOTFOUND || errno == PDERR_DNDMMISMATCH) {
if (ppd->hDevMode) {
GlobalFree(ppd->hDevMode);
ppd->hDevMode = NULL;
}
if (ppd->hDevNames) {
GlobalFree(ppd->hDevNames);
ppd->hDevNames = NULL;
}
return TRUE;
}
Error(wERRS_NOPRINTSETUP, wERRA_RETURN);
}
return FALSE;
}