mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
[NOTEPAD] Move the printing code to the newly added printing.c
CORE-18837
This commit is contained in:
parent
072503c1fc
commit
3b0791547f
3 changed files with 689 additions and 677 deletions
|
@ -2,6 +2,7 @@
|
|||
list(APPEND SOURCE
|
||||
dialog.c
|
||||
main.c
|
||||
printing.c
|
||||
settings.c
|
||||
text.c
|
||||
notepad.h)
|
||||
|
|
|
@ -55,8 +55,6 @@ static UINT EncToStrId[] = {
|
|||
STRING_UTF8_BOM
|
||||
};
|
||||
|
||||
static UINT_PTR CALLBACK DIALOG_PAGESETUP_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
VOID ShowLastError(VOID)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
|
@ -209,17 +207,6 @@ static int AlertFileNotSaved(LPCTSTR szFileName)
|
|||
MB_ICONQUESTION | MB_YESNOCANCEL);
|
||||
}
|
||||
|
||||
static void AlertPrintError(void)
|
||||
{
|
||||
TCHAR szUntitled[MAX_STRING_LEN];
|
||||
|
||||
LoadString(Globals.hInstance, STRING_UNTITLED, szUntitled, ARRAY_SIZE(szUntitled));
|
||||
|
||||
DIALOG_StringMsgBox(Globals.hMainWnd, STRING_PRINTERROR,
|
||||
Globals.szFileName[0] ? Globals.szFileName : szUntitled,
|
||||
MB_ICONEXCLAMATION | MB_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns:
|
||||
* TRUE - if file exists
|
||||
|
@ -240,76 +227,6 @@ BOOL HasFileExtension(LPCTSTR szFilename)
|
|||
return _tcsrchr(szFilename, _T('.')) != NULL;
|
||||
}
|
||||
|
||||
int GetSelectionTextLength(HWND hWnd)
|
||||
{
|
||||
DWORD dwStart = 0, dwEnd = 0;
|
||||
SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
|
||||
return dwEnd - dwStart;
|
||||
}
|
||||
|
||||
int GetSelectionText(HWND hWnd, LPTSTR lpString, int nMaxCount)
|
||||
{
|
||||
DWORD dwStart = 0, dwEnd = 0;
|
||||
INT cchText = GetWindowTextLength(hWnd);
|
||||
LPTSTR pszText;
|
||||
HLOCAL hLocal;
|
||||
HRESULT hr;
|
||||
|
||||
SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
|
||||
if (!lpString || dwStart == dwEnd || cchText == 0)
|
||||
return 0;
|
||||
|
||||
hLocal = (HLOCAL)SendMessage(hWnd, EM_GETHANDLE, 0, 0);
|
||||
pszText = (LPTSTR)LocalLock(hLocal);
|
||||
if (!pszText)
|
||||
return 0;
|
||||
|
||||
hr = StringCchCopyN(lpString, nMaxCount, pszText + dwStart, dwEnd - dwStart);
|
||||
LocalUnlock(hLocal);
|
||||
|
||||
switch (hr)
|
||||
{
|
||||
case S_OK:
|
||||
return dwEnd - dwStart;
|
||||
|
||||
case STRSAFE_E_INSUFFICIENT_BUFFER:
|
||||
return nMaxCount - 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static RECT
|
||||
GetPrintingRect(IN HDC hdc, IN LPCRECT pMargins)
|
||||
{
|
||||
INT iLogPixelsX = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
INT iLogPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
|
||||
INT iHorzRes = GetDeviceCaps(hdc, HORZRES); /* in pixels */
|
||||
INT iVertRes = GetDeviceCaps(hdc, VERTRES); /* in pixels */
|
||||
RECT rcPrintRect, rcPhysical;
|
||||
|
||||
#define CONVERT_X(x) MulDiv((x), iLogPixelsX, 2540) /* 100th millimeters to pixels */
|
||||
#define CONVERT_Y(y) MulDiv((y), iLogPixelsY, 2540) /* 100th millimeters to pixels */
|
||||
SetRect(&rcPrintRect,
|
||||
CONVERT_X(pMargins->left), CONVERT_Y(pMargins->top),
|
||||
iHorzRes - CONVERT_X(pMargins->right),
|
||||
iVertRes - CONVERT_Y(pMargins->bottom));
|
||||
|
||||
rcPhysical.left = GetDeviceCaps(hdc, PHYSICALOFFSETX);
|
||||
rcPhysical.right = rcPhysical.left + GetDeviceCaps(hdc, PHYSICALWIDTH);
|
||||
rcPhysical.top = GetDeviceCaps(hdc, PHYSICALOFFSETY);
|
||||
rcPhysical.bottom = rcPhysical.top + GetDeviceCaps(hdc, PHYSICALHEIGHT);
|
||||
|
||||
/* Adjust the margin */
|
||||
rcPrintRect.left = max(rcPrintRect.left, rcPhysical.left);
|
||||
rcPrintRect.top = max(rcPrintRect.top, rcPhysical.top);
|
||||
rcPrintRect.right = min(rcPrintRect.right, rcPhysical.right);
|
||||
rcPrintRect.bottom = min(rcPrintRect.bottom, rcPhysical.bottom);
|
||||
|
||||
return rcPrintRect;
|
||||
}
|
||||
|
||||
static BOOL DoSaveFile(VOID)
|
||||
{
|
||||
BOOL bRet = FALSE;
|
||||
|
@ -619,527 +536,6 @@ BOOL DIALOG_FileSaveAs(VOID)
|
|||
}
|
||||
}
|
||||
|
||||
/* Convert the points into pixels */
|
||||
#define X_POINTS_TO_PIXELS(hDC, points) MulDiv((points), GetDeviceCaps((hDC), LOGPIXELSX), 72)
|
||||
#define Y_POINTS_TO_PIXELS(hDC, points) MulDiv((points), GetDeviceCaps((hDC), LOGPIXELSY), 72)
|
||||
|
||||
/*
|
||||
* See also:
|
||||
* https://support.microsoft.com/en-us/windows/changing-header-and-footer-commands-in-notepad-c1b0e27b-497d-c478-c4c1-0da491cac148
|
||||
*/
|
||||
static VOID
|
||||
DrawHeaderOrFooter(HDC hDC, LPRECT pRect, LPCTSTR pszFormat, INT nPageNo, const SYSTEMTIME *pstNow)
|
||||
{
|
||||
TCHAR szText[256], szField[128];
|
||||
const TCHAR *pchFormat;
|
||||
UINT uAlign = DT_CENTER, uFlags = DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX;
|
||||
HGDIOBJ hOldPen, hOldBrush;
|
||||
|
||||
/* Draw a rectangle */
|
||||
hOldPen = SelectObject(hDC, GetStockObject(BLACK_PEN));
|
||||
hOldBrush = SelectObject(hDC, GetStockObject(NULL_BRUSH));
|
||||
Rectangle(hDC, pRect->left, pRect->top, pRect->right, pRect->bottom);
|
||||
SelectObject(hDC, hOldBrush);
|
||||
SelectObject(hDC, hOldPen);
|
||||
|
||||
InflateRect(pRect, -X_POINTS_TO_PIXELS(hDC, 3), 0); /* Shrink 3pt */
|
||||
|
||||
szText[0] = 0;
|
||||
|
||||
for (pchFormat = pszFormat; *pchFormat; ++pchFormat)
|
||||
{
|
||||
if (*pchFormat != _T('&'))
|
||||
{
|
||||
StringCchCatN(szText, ARRAY_SIZE(szText), pchFormat, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
++pchFormat;
|
||||
if (*pchFormat == 0)
|
||||
break;
|
||||
|
||||
switch (_totupper(*pchFormat)) /* Make it uppercase */
|
||||
{
|
||||
case _T('&'): /* Found double ampersand */
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), TEXT("&"));
|
||||
break;
|
||||
|
||||
case _T('L'): /* Left */
|
||||
DrawText(hDC, szText, -1, pRect, uAlign | uFlags);
|
||||
szText[0] = 0;
|
||||
uAlign = DT_LEFT;
|
||||
break;
|
||||
|
||||
case _T('C'): /* Center */
|
||||
DrawText(hDC, szText, -1, pRect, uAlign | uFlags);
|
||||
szText[0] = 0;
|
||||
uAlign = DT_CENTER;
|
||||
break;
|
||||
|
||||
case _T('R'): /* Right */
|
||||
DrawText(hDC, szText, -1, pRect, uAlign | uFlags);
|
||||
szText[0] = 0;
|
||||
uAlign = DT_RIGHT;
|
||||
break;
|
||||
|
||||
case _T('D'): /* Date */
|
||||
GetDateFormat(LOCALE_USER_DEFAULT, 0, pstNow, NULL,
|
||||
szField, (INT)ARRAY_SIZE(szField));
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), szField);
|
||||
break;
|
||||
|
||||
case _T('T'): /* Time */
|
||||
GetTimeFormat(LOCALE_USER_DEFAULT, 0, pstNow, NULL,
|
||||
szField, (INT)ARRAY_SIZE(szField));
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), szField);
|
||||
break;
|
||||
|
||||
case _T('F'): /* Filename */
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), Globals.szFileTitle);
|
||||
break;
|
||||
|
||||
case _T('P'): /* Page number */
|
||||
StringCchPrintf(szField, ARRAY_SIZE(szField), TEXT("%u"), nPageNo);
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), szField);
|
||||
break;
|
||||
|
||||
default: /* Otherwise */
|
||||
szField[0] = _T('&');
|
||||
szField[1] = *pchFormat;
|
||||
szField[2] = 0;
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), szField);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DrawText(hDC, szText, -1, pRect, uAlign | uFlags);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PRINTDLG printer;
|
||||
HWND hwndDlg;
|
||||
INT status;
|
||||
INT currentPage;
|
||||
RECT printRect;
|
||||
SYSTEMTIME stNow;
|
||||
HFONT hHeaderFont;
|
||||
HFONT hBodyFont;
|
||||
LPTSTR pszText;
|
||||
DWORD ich;
|
||||
DWORD cchText;
|
||||
INT cyHeader;
|
||||
INT cySpacing;
|
||||
INT cyFooter;
|
||||
} PRINT_DATA, *PPRINT_DATA;
|
||||
|
||||
static BOOL DoPrintBody(PPRINT_DATA pData, DWORD PageCount, BOOL bSkipPage)
|
||||
{
|
||||
LPPRINTDLG pPrinter = &pData->printer;
|
||||
RECT printRect = pData->printRect;
|
||||
INT xLeft = printRect.left, yTop = printRect.top + pData->cyHeader + pData->cySpacing;
|
||||
INT xStart, tabWidth;
|
||||
DWORD ichStart;
|
||||
SIZE charMetrics;
|
||||
TEXTMETRIC tmText;
|
||||
|
||||
/* Calculate a tab width */
|
||||
#define TAB_STOP 8
|
||||
GetTextMetrics(pPrinter->hDC, &tmText);
|
||||
tabWidth = TAB_STOP * tmText.tmAveCharWidth;
|
||||
|
||||
#define DO_FLUSH() do { \
|
||||
if (ichStart < pData->ich && !bSkipPage) { \
|
||||
TextOut(pPrinter->hDC, xStart, yTop, &pData->pszText[ichStart], pData->ich - ichStart); \
|
||||
} \
|
||||
ichStart = pData->ich; \
|
||||
xStart = xLeft; \
|
||||
if (pData->status == STRING_PRINTCANCELING) return FALSE; \
|
||||
} while (0)
|
||||
|
||||
/* The drawing-body loop */
|
||||
for (ichStart = pData->ich, xStart = xLeft; pData->ich < pData->cchText; )
|
||||
{
|
||||
TCHAR ch = pData->pszText[pData->ich];
|
||||
|
||||
if (ch == _T('\r'))
|
||||
{
|
||||
DO_FLUSH();
|
||||
|
||||
pData->ich++; /* Next char */
|
||||
ichStart = pData->ich;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == _T('\n'))
|
||||
{
|
||||
DO_FLUSH();
|
||||
|
||||
/* Next line */
|
||||
yTop += tmText.tmHeight;
|
||||
xLeft = xStart = printRect.left;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ch == _T('\t'))
|
||||
{
|
||||
INT nStepWidth = tabWidth - ((xLeft - printRect.left) % tabWidth);
|
||||
|
||||
DO_FLUSH();
|
||||
|
||||
/* Go to the next tab stop */
|
||||
xLeft += nStepWidth;
|
||||
xStart = xLeft;
|
||||
}
|
||||
else /* Normal char */
|
||||
{
|
||||
GetTextExtentPoint32(pPrinter->hDC, &ch, 1, &charMetrics);
|
||||
xLeft += charMetrics.cx;
|
||||
}
|
||||
|
||||
/* Insert a line break if the next position reached the right edge */
|
||||
if (xLeft + charMetrics.cx >= printRect.right)
|
||||
{
|
||||
if (ch != _T('\t'))
|
||||
DO_FLUSH();
|
||||
|
||||
/* Next line */
|
||||
yTop += tmText.tmHeight;
|
||||
xLeft = xStart = printRect.left;
|
||||
}
|
||||
}
|
||||
|
||||
pData->ich++; /* Next char */
|
||||
if (ch == _T('\t') || ch == _T('\n'))
|
||||
ichStart = pData->ich;
|
||||
|
||||
if (yTop + tmText.tmHeight >= printRect.bottom - pData->cyFooter)
|
||||
break; /* The next line reached the body bottom */
|
||||
}
|
||||
|
||||
DO_FLUSH();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL DoPrintPage(PPRINT_DATA pData, DWORD PageCount)
|
||||
{
|
||||
LPPRINTDLG pPrinter = &pData->printer;
|
||||
BOOL bSkipPage, ret;
|
||||
HFONT hOldFont;
|
||||
|
||||
/* Should we skip this page? */
|
||||
bSkipPage = !(pPrinter->Flags & PD_SELECTION) &&
|
||||
(pPrinter->Flags & PD_PAGENUMS) &&
|
||||
!(pPrinter->nFromPage <= PageCount && PageCount <= pPrinter->nToPage);
|
||||
|
||||
/* The prologue of a page */
|
||||
if (!bSkipPage)
|
||||
{
|
||||
if (StartPage(pPrinter->hDC) <= 0)
|
||||
{
|
||||
pData->status = STRING_PRINTFAILED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pData->cyHeader > 0)
|
||||
{
|
||||
/* Draw the page header */
|
||||
RECT rc = pData->printRect;
|
||||
rc.bottom = rc.top + pData->cyHeader;
|
||||
|
||||
hOldFont = SelectObject(pPrinter->hDC, pData->hHeaderFont);
|
||||
DrawHeaderOrFooter(pPrinter->hDC, &rc, Globals.szHeader, PageCount, &pData->stNow);
|
||||
SelectObject(pPrinter->hDC, hOldFont); /* De-select the font */
|
||||
}
|
||||
}
|
||||
|
||||
hOldFont = SelectObject(pPrinter->hDC, pData->hBodyFont);
|
||||
ret = DoPrintBody(pData, PageCount, bSkipPage);
|
||||
SelectObject(pPrinter->hDC, hOldFont);
|
||||
if (!ret)
|
||||
return FALSE; /* Canceled */
|
||||
|
||||
/* The epilogue of a page */
|
||||
if (!bSkipPage)
|
||||
{
|
||||
if (pData->cyFooter > 0)
|
||||
{
|
||||
/* Draw the page footer */
|
||||
RECT rc = pData->printRect;
|
||||
rc.top = rc.bottom - pData->cyFooter;
|
||||
|
||||
hOldFont = SelectObject(pPrinter->hDC, pData->hHeaderFont);
|
||||
DrawHeaderOrFooter(pPrinter->hDC, &rc, Globals.szFooter, PageCount, &pData->stNow);
|
||||
SelectObject(pPrinter->hDC, hOldFont);
|
||||
}
|
||||
|
||||
if (EndPage(pPrinter->hDC) <= 0)
|
||||
{
|
||||
pData->status = STRING_PRINTFAILED;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define BODY_FONT_SIZE 10 /* 10pt */
|
||||
#define HEADER_FONT_SIZE 9 /* 9pt */
|
||||
#define SPACING_HEIGHT 4 /* 4pt */
|
||||
#define PRINTING_MESSAGE (WM_USER + 100)
|
||||
|
||||
static BOOL DoCreatePrintFonts(LPPRINTDLG pPrinter, PPRINT_DATA pPrintData)
|
||||
{
|
||||
LOGFONT lfBody, lfHeader;
|
||||
|
||||
/* Create the main text font for printing */
|
||||
lfBody = Globals.lfFont;
|
||||
lfBody.lfHeight = -Y_POINTS_TO_PIXELS(pPrinter->hDC, BODY_FONT_SIZE);
|
||||
pPrintData->hBodyFont = CreateFontIndirect(&lfBody);
|
||||
if (pPrintData->hBodyFont == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* Create the header/footer font */
|
||||
lfHeader = Globals.lfFont;
|
||||
lfHeader.lfHeight = -Y_POINTS_TO_PIXELS(pPrinter->hDC, HEADER_FONT_SIZE);
|
||||
lfHeader.lfWeight = FW_BOLD;
|
||||
pPrintData->hHeaderFont = CreateFontIndirect(&lfHeader);
|
||||
if (pPrintData->hHeaderFont == NULL)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL DoPrintDocument(PPRINT_DATA printData)
|
||||
{
|
||||
DOCINFO docInfo;
|
||||
LPPRINTDLG pPrinter = &printData->printer;
|
||||
DWORD CopyCount, PageCount;
|
||||
TEXTMETRIC tmHeader;
|
||||
BOOL ret = FALSE;
|
||||
HFONT hOldFont;
|
||||
|
||||
GetLocalTime(&printData->stNow);
|
||||
|
||||
printData->printRect = GetPrintingRect(pPrinter->hDC, &Globals.lMargins);
|
||||
|
||||
if (!DoCreatePrintFonts(pPrinter, printData))
|
||||
{
|
||||
printData->status = STRING_PRINTFAILED;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
if (pPrinter->Flags & PD_SELECTION)
|
||||
printData->cchText = GetSelectionTextLength(Globals.hEdit);
|
||||
else
|
||||
printData->cchText = GetWindowTextLength(Globals.hEdit);
|
||||
|
||||
/* Allocate a buffer for the text */
|
||||
printData->pszText = HeapAlloc(GetProcessHeap(), 0, (printData->cchText + 1) * sizeof(TCHAR));
|
||||
if (!printData->pszText)
|
||||
{
|
||||
printData->status = STRING_PRINTFAILED;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
if (pPrinter->Flags & PD_SELECTION)
|
||||
GetSelectionText(Globals.hEdit, printData->pszText, printData->cchText + 1);
|
||||
else
|
||||
GetWindowText(Globals.hEdit, printData->pszText, printData->cchText + 1);
|
||||
|
||||
/* Start a document */
|
||||
ZeroMemory(&docInfo, sizeof(docInfo));
|
||||
docInfo.cbSize = sizeof(DOCINFO);
|
||||
docInfo.lpszDocName = Globals.szFileTitle;
|
||||
if (StartDoc(pPrinter->hDC, &docInfo) <= 0)
|
||||
{
|
||||
printData->status = STRING_PRINTFAILED;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
/* Calculate the header and footer heights */
|
||||
hOldFont = SelectObject(pPrinter->hDC, printData->hHeaderFont);
|
||||
GetTextMetrics(pPrinter->hDC, &tmHeader);
|
||||
printData->cyHeader = printData->cyFooter = 2 * tmHeader.tmHeight;
|
||||
printData->cySpacing = Y_POINTS_TO_PIXELS(pPrinter->hDC, SPACING_HEIGHT);
|
||||
SelectObject(pPrinter->hDC, hOldFont); /* De-select the font */
|
||||
if (!Globals.szHeader[0])
|
||||
printData->cyHeader = printData->cySpacing = 0;
|
||||
if (!Globals.szFooter[0])
|
||||
printData->cyFooter = 0;
|
||||
|
||||
/* The printing-copies loop */
|
||||
for (CopyCount = 1; CopyCount <= pPrinter->nCopies; ++CopyCount)
|
||||
{
|
||||
/* The printing-pages loop */
|
||||
for (PageCount = 1, printData->ich = 0; printData->ich < printData->cchText; ++PageCount)
|
||||
{
|
||||
printData->currentPage = PageCount;
|
||||
PostMessage(printData->hwndDlg, PRINTING_MESSAGE, 0, 0);
|
||||
|
||||
if (!DoPrintPage(printData, PageCount))
|
||||
{
|
||||
AbortDoc(pPrinter->hDC); /* Cancel printing */
|
||||
goto Quit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EndDoc(pPrinter->hDC) <= 0)
|
||||
{
|
||||
printData->status = STRING_PRINTFAILED;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
printData->status = STRING_PRINTCOMPLETE;
|
||||
|
||||
Quit:
|
||||
DeleteObject(printData->hHeaderFont);
|
||||
DeleteObject(printData->hBodyFont);
|
||||
if (printData->pszText)
|
||||
HeapFree(GetProcessHeap(), 0, printData->pszText);
|
||||
if (printData->status == STRING_PRINTCANCELING)
|
||||
printData->status = STRING_PRINTCANCELED;
|
||||
PostMessage(printData->hwndDlg, PRINTING_MESSAGE, 0, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DWORD WINAPI PrintThreadFunc(LPVOID arg)
|
||||
{
|
||||
PPRINT_DATA pData = arg;
|
||||
pData->currentPage = 1;
|
||||
pData->status = STRING_NOWPRINTING;
|
||||
PostMessage(pData->hwndDlg, PRINTING_MESSAGE, 0, 0);
|
||||
return DoPrintDocument(pData);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK
|
||||
DIALOG_Printing_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
TCHAR szText[MAX_STRING_LEN];
|
||||
static TCHAR s_szPage[64];
|
||||
static PPRINT_DATA s_pData = NULL;
|
||||
static HANDLE s_hThread = NULL;
|
||||
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
s_pData = (PPRINT_DATA)lParam;
|
||||
s_pData->hwndDlg = hwnd;
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_FILENAME, Globals.szFileTitle);
|
||||
GetDlgItemText(hwnd, IDC_PRINTING_PAGE, s_szPage, ARRAY_SIZE(s_szPage));
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_PAGE, NULL);
|
||||
|
||||
s_hThread = CreateThread(NULL, 0, PrintThreadFunc, s_pData, 0, NULL);
|
||||
if (!s_hThread)
|
||||
{
|
||||
s_pData->status = STRING_PRINTFAILED;
|
||||
EndDialog(hwnd, IDABORT);
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case PRINTING_MESSAGE:
|
||||
switch (s_pData->status)
|
||||
{
|
||||
case STRING_NOWPRINTING:
|
||||
case STRING_PRINTCANCELING:
|
||||
StringCchPrintf(szText, ARRAY_SIZE(szText), s_szPage, s_pData->currentPage);
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_PAGE, szText);
|
||||
|
||||
LoadString(Globals.hInstance, s_pData->status, szText, ARRAY_SIZE(szText));
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_STATUS, szText);
|
||||
break;
|
||||
|
||||
case STRING_PRINTCOMPLETE:
|
||||
case STRING_PRINTCANCELED:
|
||||
case STRING_PRINTFAILED:
|
||||
LoadString(Globals.hInstance, s_pData->status, szText, ARRAY_SIZE(szText));
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_STATUS, szText);
|
||||
|
||||
if (s_pData->status == STRING_PRINTCOMPLETE)
|
||||
EndDialog(hwnd, IDOK);
|
||||
else if (s_pData->status == STRING_PRINTFAILED)
|
||||
EndDialog(hwnd, IDABORT);
|
||||
else
|
||||
EndDialog(hwnd, IDCANCEL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
if (LOWORD(wParam) == IDCANCEL && s_pData->status == STRING_NOWPRINTING)
|
||||
{
|
||||
EnableWindow(GetDlgItem(hwnd, IDCANCEL), FALSE);
|
||||
s_pData->status = STRING_PRINTCANCELING;
|
||||
PostMessage(hwnd, PRINTING_MESSAGE, 0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
if (s_hThread)
|
||||
CloseHandle(s_hThread);
|
||||
DeleteDC(s_pData->printer.hDC);
|
||||
s_pData = LocalFree(s_pData);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VOID DIALOG_FilePrint(VOID)
|
||||
{
|
||||
BOOL ret;
|
||||
LPPRINTDLG printer;
|
||||
PPRINT_DATA printData = LocalAlloc(LPTR, sizeof(PRINT_DATA));
|
||||
if (!printData)
|
||||
{
|
||||
ShowLastError();
|
||||
return;
|
||||
}
|
||||
|
||||
printer = &printData->printer;
|
||||
printer->lStructSize = sizeof(PRINTDLG);
|
||||
printer->hwndOwner = Globals.hMainWnd;
|
||||
printer->Flags = PD_RETURNDC | PD_SELECTION;
|
||||
|
||||
/* Disable the selection radio button if there is no text selected */
|
||||
if (!GetSelectionTextLength(Globals.hEdit))
|
||||
printer->Flags |= PD_NOSELECTION;
|
||||
|
||||
printer->nFromPage = 1;
|
||||
printer->nToPage = MAXWORD;
|
||||
printer->nMinPage = 1;
|
||||
printer->nMaxPage = MAXWORD;
|
||||
|
||||
printer->hDevMode = Globals.hDevMode;
|
||||
printer->hDevNames = Globals.hDevNames;
|
||||
|
||||
ret = PrintDlg(printer);
|
||||
/* NOTE: Even if PrintDlg returns FALSE, hDevMode and hDevNames may have changed. */
|
||||
Globals.hDevMode = printer->hDevMode;
|
||||
Globals.hDevNames = printer->hDevNames;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
LocalFree(printData);
|
||||
return; /* The user canceled printing */
|
||||
}
|
||||
assert(printer->hDC != NULL);
|
||||
|
||||
/* Ensure that each logical unit maps to one pixel */
|
||||
SetMapMode(printer->hDC, MM_TEXT);
|
||||
|
||||
if (DialogBoxParam(Globals.hInstance,
|
||||
MAKEINTRESOURCE(DIALOG_PRINTING),
|
||||
Globals.hMainWnd,
|
||||
DIALOG_Printing_DialogProc,
|
||||
(LPARAM)printer) == IDABORT)
|
||||
{
|
||||
AlertPrintError();
|
||||
}
|
||||
}
|
||||
|
||||
VOID DIALOG_FileExit(VOID)
|
||||
{
|
||||
PostMessage(Globals.hMainWnd, WM_CLOSE, 0, 0);
|
||||
|
@ -1525,76 +921,3 @@ VOID DIALOG_HelpAboutNotepad(VOID)
|
|||
ShellAbout(Globals.hMainWnd, szNotepad, szNotepadAuthors, notepadIcon);
|
||||
DestroyIcon(notepadIcon);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DIALOG_FilePageSetup
|
||||
*/
|
||||
VOID DIALOG_FilePageSetup(void)
|
||||
{
|
||||
PAGESETUPDLG page;
|
||||
|
||||
ZeroMemory(&page, sizeof(page));
|
||||
page.lStructSize = sizeof(page);
|
||||
page.hwndOwner = Globals.hMainWnd;
|
||||
page.Flags = PSD_ENABLEPAGESETUPTEMPLATE | PSD_ENABLEPAGESETUPHOOK | PSD_MARGINS;
|
||||
page.hInstance = Globals.hInstance;
|
||||
page.rtMargin = Globals.lMargins;
|
||||
page.hDevMode = Globals.hDevMode;
|
||||
page.hDevNames = Globals.hDevNames;
|
||||
page.lpPageSetupTemplateName = MAKEINTRESOURCE(DIALOG_PAGESETUP);
|
||||
page.lpfnPageSetupHook = DIALOG_PAGESETUP_Hook;
|
||||
|
||||
PageSetupDlg(&page);
|
||||
|
||||
/* NOTE: Even if PageSetupDlg returns FALSE, the following members may have changed */
|
||||
Globals.hDevMode = page.hDevMode;
|
||||
Globals.hDevNames = page.hDevNames;
|
||||
Globals.lMargins = page.rtMargin;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* DIALOG_PAGESETUP_Hook
|
||||
*/
|
||||
static UINT_PTR CALLBACK DIALOG_PAGESETUP_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case WM_COMMAND:
|
||||
if (HIWORD(wParam) == BN_CLICKED)
|
||||
{
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDOK:
|
||||
/* save user input and close dialog */
|
||||
GetDlgItemText(hDlg, 0x141, Globals.szHeader, ARRAY_SIZE(Globals.szHeader));
|
||||
GetDlgItemText(hDlg, 0x143, Globals.szFooter, ARRAY_SIZE(Globals.szFooter));
|
||||
return FALSE;
|
||||
|
||||
case IDCANCEL:
|
||||
/* discard user input and close dialog */
|
||||
return FALSE;
|
||||
|
||||
case IDHELP:
|
||||
{
|
||||
/* FIXME: Bring this to work */
|
||||
static const TCHAR sorry[] = _T("Sorry, no help available");
|
||||
static const TCHAR help[] = _T("Help");
|
||||
MessageBox(Globals.hMainWnd, sorry, help, MB_ICONEXCLAMATION);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_INITDIALOG:
|
||||
/* fetch last user input prior to display dialog */
|
||||
SetDlgItemText(hDlg, 0x141, Globals.szHeader);
|
||||
SetDlgItemText(hDlg, 0x143, Globals.szFooter);
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
|
688
base/applications/notepad/printing.c
Normal file
688
base/applications/notepad/printing.c
Normal file
|
@ -0,0 +1,688 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Notepad
|
||||
* LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
|
||||
* PURPOSE: Providing a Windows-compatible simple text editor for ReactOS
|
||||
* COPYRIGHT: Copyright 2020-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
|
||||
*/
|
||||
|
||||
#include "notepad.h"
|
||||
|
||||
#include <strsafe.h>
|
||||
#include <assert.h>
|
||||
|
||||
static VOID AlertPrintError(VOID)
|
||||
{
|
||||
TCHAR szUntitled[MAX_STRING_LEN];
|
||||
|
||||
LoadString(Globals.hInstance, STRING_UNTITLED, szUntitled, ARRAY_SIZE(szUntitled));
|
||||
|
||||
DIALOG_StringMsgBox(Globals.hMainWnd, STRING_PRINTERROR,
|
||||
Globals.szFileName[0] ? Globals.szFileName : szUntitled,
|
||||
MB_ICONEXCLAMATION | MB_OK);
|
||||
}
|
||||
|
||||
static RECT
|
||||
GetPrintingRect(IN HDC hdc, IN LPCRECT pMargins)
|
||||
{
|
||||
INT iLogPixelsX = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
INT iLogPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
|
||||
INT iHorzRes = GetDeviceCaps(hdc, HORZRES); /* in pixels */
|
||||
INT iVertRes = GetDeviceCaps(hdc, VERTRES); /* in pixels */
|
||||
RECT rcPrintRect, rcPhysical;
|
||||
|
||||
#define CONVERT_X(x) MulDiv((x), iLogPixelsX, 2540) /* 100th millimeters to pixels */
|
||||
#define CONVERT_Y(y) MulDiv((y), iLogPixelsY, 2540) /* 100th millimeters to pixels */
|
||||
SetRect(&rcPrintRect,
|
||||
CONVERT_X(pMargins->left), CONVERT_Y(pMargins->top),
|
||||
iHorzRes - CONVERT_X(pMargins->right),
|
||||
iVertRes - CONVERT_Y(pMargins->bottom));
|
||||
|
||||
rcPhysical.left = GetDeviceCaps(hdc, PHYSICALOFFSETX);
|
||||
rcPhysical.right = rcPhysical.left + GetDeviceCaps(hdc, PHYSICALWIDTH);
|
||||
rcPhysical.top = GetDeviceCaps(hdc, PHYSICALOFFSETY);
|
||||
rcPhysical.bottom = rcPhysical.top + GetDeviceCaps(hdc, PHYSICALHEIGHT);
|
||||
|
||||
/* Adjust the margin */
|
||||
rcPrintRect.left = max(rcPrintRect.left, rcPhysical.left);
|
||||
rcPrintRect.top = max(rcPrintRect.top, rcPhysical.top);
|
||||
rcPrintRect.right = min(rcPrintRect.right, rcPhysical.right);
|
||||
rcPrintRect.bottom = min(rcPrintRect.bottom, rcPhysical.bottom);
|
||||
|
||||
return rcPrintRect;
|
||||
}
|
||||
|
||||
static INT GetSelectionTextLength(HWND hWnd)
|
||||
{
|
||||
DWORD dwStart = 0, dwEnd = 0;
|
||||
SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
|
||||
return dwEnd - dwStart;
|
||||
}
|
||||
|
||||
static INT GetSelectionText(HWND hWnd, LPTSTR lpString, INT nMaxCount)
|
||||
{
|
||||
DWORD dwStart = 0, dwEnd = 0;
|
||||
INT cchText = GetWindowTextLength(hWnd);
|
||||
LPTSTR pszText;
|
||||
HLOCAL hLocal;
|
||||
HRESULT hr;
|
||||
|
||||
SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
|
||||
if (!lpString || dwStart == dwEnd || cchText == 0)
|
||||
return 0;
|
||||
|
||||
hLocal = (HLOCAL)SendMessage(hWnd, EM_GETHANDLE, 0, 0);
|
||||
pszText = (LPTSTR)LocalLock(hLocal);
|
||||
if (!pszText)
|
||||
return 0;
|
||||
|
||||
hr = StringCchCopyN(lpString, nMaxCount, pszText + dwStart, dwEnd - dwStart);
|
||||
LocalUnlock(hLocal);
|
||||
|
||||
switch (hr)
|
||||
{
|
||||
case S_OK:
|
||||
return dwEnd - dwStart;
|
||||
|
||||
case STRSAFE_E_INSUFFICIENT_BUFFER:
|
||||
return nMaxCount - 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PRINTDLG printer;
|
||||
HWND hwndDlg;
|
||||
INT status;
|
||||
INT currentPage;
|
||||
RECT printRect;
|
||||
SYSTEMTIME stNow;
|
||||
HFONT hHeaderFont;
|
||||
HFONT hBodyFont;
|
||||
LPTSTR pszText;
|
||||
DWORD ich;
|
||||
DWORD cchText;
|
||||
INT cyHeader;
|
||||
INT cySpacing;
|
||||
INT cyFooter;
|
||||
} PRINT_DATA, *PPRINT_DATA;
|
||||
|
||||
/* Convert the points into pixels */
|
||||
#define X_POINTS_TO_PIXELS(hDC, points) MulDiv((points), GetDeviceCaps((hDC), LOGPIXELSX), 72)
|
||||
#define Y_POINTS_TO_PIXELS(hDC, points) MulDiv((points), GetDeviceCaps((hDC), LOGPIXELSY), 72)
|
||||
|
||||
/*
|
||||
* See also:
|
||||
* https://support.microsoft.com/en-us/windows/changing-header-and-footer-commands-in-notepad-c1b0e27b-497d-c478-c4c1-0da491cac148
|
||||
*/
|
||||
static VOID
|
||||
DrawHeaderOrFooter(HDC hDC, LPRECT pRect, LPCTSTR pszFormat, INT nPageNo, const SYSTEMTIME *pstNow)
|
||||
{
|
||||
TCHAR szText[256], szField[128];
|
||||
const TCHAR *pchFormat;
|
||||
UINT uAlign = DT_CENTER, uFlags = DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX;
|
||||
HGDIOBJ hOldPen, hOldBrush;
|
||||
|
||||
/* Draw a rectangle */
|
||||
hOldPen = SelectObject(hDC, GetStockObject(BLACK_PEN));
|
||||
hOldBrush = SelectObject(hDC, GetStockObject(NULL_BRUSH));
|
||||
Rectangle(hDC, pRect->left, pRect->top, pRect->right, pRect->bottom);
|
||||
SelectObject(hDC, hOldBrush);
|
||||
SelectObject(hDC, hOldPen);
|
||||
|
||||
InflateRect(pRect, -X_POINTS_TO_PIXELS(hDC, 3), 0); /* Shrink 3pt */
|
||||
|
||||
szText[0] = 0;
|
||||
|
||||
for (pchFormat = pszFormat; *pchFormat; ++pchFormat)
|
||||
{
|
||||
if (*pchFormat != _T('&'))
|
||||
{
|
||||
StringCchCatN(szText, ARRAY_SIZE(szText), pchFormat, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
++pchFormat;
|
||||
if (*pchFormat == 0)
|
||||
break;
|
||||
|
||||
switch (_totupper(*pchFormat)) /* Make it uppercase */
|
||||
{
|
||||
case _T('&'): /* Found double ampersand */
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), TEXT("&"));
|
||||
break;
|
||||
|
||||
case _T('L'): /* Left */
|
||||
DrawText(hDC, szText, -1, pRect, uAlign | uFlags);
|
||||
szText[0] = 0;
|
||||
uAlign = DT_LEFT;
|
||||
break;
|
||||
|
||||
case _T('C'): /* Center */
|
||||
DrawText(hDC, szText, -1, pRect, uAlign | uFlags);
|
||||
szText[0] = 0;
|
||||
uAlign = DT_CENTER;
|
||||
break;
|
||||
|
||||
case _T('R'): /* Right */
|
||||
DrawText(hDC, szText, -1, pRect, uAlign | uFlags);
|
||||
szText[0] = 0;
|
||||
uAlign = DT_RIGHT;
|
||||
break;
|
||||
|
||||
case _T('D'): /* Date */
|
||||
GetDateFormat(LOCALE_USER_DEFAULT, 0, pstNow, NULL,
|
||||
szField, (INT)ARRAY_SIZE(szField));
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), szField);
|
||||
break;
|
||||
|
||||
case _T('T'): /* Time */
|
||||
GetTimeFormat(LOCALE_USER_DEFAULT, 0, pstNow, NULL,
|
||||
szField, (INT)ARRAY_SIZE(szField));
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), szField);
|
||||
break;
|
||||
|
||||
case _T('F'): /* Filename */
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), Globals.szFileTitle);
|
||||
break;
|
||||
|
||||
case _T('P'): /* Page number */
|
||||
StringCchPrintf(szField, ARRAY_SIZE(szField), TEXT("%u"), nPageNo);
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), szField);
|
||||
break;
|
||||
|
||||
default: /* Otherwise */
|
||||
szField[0] = _T('&');
|
||||
szField[1] = *pchFormat;
|
||||
szField[2] = 0;
|
||||
StringCchCat(szText, ARRAY_SIZE(szText), szField);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DrawText(hDC, szText, -1, pRect, uAlign | uFlags);
|
||||
}
|
||||
|
||||
static BOOL DoPrintBody(PPRINT_DATA pData, DWORD PageCount, BOOL bSkipPage)
|
||||
{
|
||||
LPPRINTDLG pPrinter = &pData->printer;
|
||||
RECT printRect = pData->printRect;
|
||||
INT xLeft = printRect.left, yTop = printRect.top + pData->cyHeader + pData->cySpacing;
|
||||
INT xStart, tabWidth;
|
||||
DWORD ichStart;
|
||||
SIZE charMetrics;
|
||||
TEXTMETRIC tmText;
|
||||
|
||||
/* Calculate a tab width */
|
||||
#define TAB_STOP 8
|
||||
GetTextMetrics(pPrinter->hDC, &tmText);
|
||||
tabWidth = TAB_STOP * tmText.tmAveCharWidth;
|
||||
|
||||
#define DO_FLUSH() do { \
|
||||
if (ichStart < pData->ich && !bSkipPage) { \
|
||||
TextOut(pPrinter->hDC, xStart, yTop, &pData->pszText[ichStart], pData->ich - ichStart); \
|
||||
} \
|
||||
ichStart = pData->ich; \
|
||||
xStart = xLeft; \
|
||||
if (pData->status == STRING_PRINTCANCELING) return FALSE; \
|
||||
} while (0)
|
||||
|
||||
/* The drawing-body loop */
|
||||
for (ichStart = pData->ich, xStart = xLeft; pData->ich < pData->cchText; )
|
||||
{
|
||||
TCHAR ch = pData->pszText[pData->ich];
|
||||
|
||||
if (ch == _T('\r'))
|
||||
{
|
||||
DO_FLUSH();
|
||||
|
||||
pData->ich++; /* Next char */
|
||||
ichStart = pData->ich;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == _T('\n'))
|
||||
{
|
||||
DO_FLUSH();
|
||||
|
||||
/* Next line */
|
||||
yTop += tmText.tmHeight;
|
||||
xLeft = xStart = printRect.left;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ch == _T('\t'))
|
||||
{
|
||||
INT nStepWidth = tabWidth - ((xLeft - printRect.left) % tabWidth);
|
||||
|
||||
DO_FLUSH();
|
||||
|
||||
/* Go to the next tab stop */
|
||||
xLeft += nStepWidth;
|
||||
xStart = xLeft;
|
||||
}
|
||||
else /* Normal char */
|
||||
{
|
||||
GetTextExtentPoint32(pPrinter->hDC, &ch, 1, &charMetrics);
|
||||
xLeft += charMetrics.cx;
|
||||
}
|
||||
|
||||
/* Insert a line break if the next position reached the right edge */
|
||||
if (xLeft + charMetrics.cx >= printRect.right)
|
||||
{
|
||||
if (ch != _T('\t'))
|
||||
DO_FLUSH();
|
||||
|
||||
/* Next line */
|
||||
yTop += tmText.tmHeight;
|
||||
xLeft = xStart = printRect.left;
|
||||
}
|
||||
}
|
||||
|
||||
pData->ich++; /* Next char */
|
||||
if (ch == _T('\t') || ch == _T('\n'))
|
||||
ichStart = pData->ich;
|
||||
|
||||
if (yTop + tmText.tmHeight >= printRect.bottom - pData->cyFooter)
|
||||
break; /* The next line reached the body bottom */
|
||||
}
|
||||
|
||||
DO_FLUSH();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL DoPrintPage(PPRINT_DATA pData, DWORD PageCount)
|
||||
{
|
||||
LPPRINTDLG pPrinter = &pData->printer;
|
||||
BOOL bSkipPage, ret;
|
||||
HFONT hOldFont;
|
||||
|
||||
/* Should we skip this page? */
|
||||
bSkipPage = !(pPrinter->Flags & PD_SELECTION) &&
|
||||
(pPrinter->Flags & PD_PAGENUMS) &&
|
||||
!(pPrinter->nFromPage <= PageCount && PageCount <= pPrinter->nToPage);
|
||||
|
||||
/* The prologue of a page */
|
||||
if (!bSkipPage)
|
||||
{
|
||||
if (StartPage(pPrinter->hDC) <= 0)
|
||||
{
|
||||
pData->status = STRING_PRINTFAILED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pData->cyHeader > 0)
|
||||
{
|
||||
/* Draw the page header */
|
||||
RECT rc = pData->printRect;
|
||||
rc.bottom = rc.top + pData->cyHeader;
|
||||
|
||||
hOldFont = SelectObject(pPrinter->hDC, pData->hHeaderFont);
|
||||
DrawHeaderOrFooter(pPrinter->hDC, &rc, Globals.szHeader, PageCount, &pData->stNow);
|
||||
SelectObject(pPrinter->hDC, hOldFont); /* De-select the font */
|
||||
}
|
||||
}
|
||||
|
||||
hOldFont = SelectObject(pPrinter->hDC, pData->hBodyFont);
|
||||
ret = DoPrintBody(pData, PageCount, bSkipPage);
|
||||
SelectObject(pPrinter->hDC, hOldFont);
|
||||
if (!ret)
|
||||
return FALSE; /* Canceled */
|
||||
|
||||
/* The epilogue of a page */
|
||||
if (!bSkipPage)
|
||||
{
|
||||
if (pData->cyFooter > 0)
|
||||
{
|
||||
/* Draw the page footer */
|
||||
RECT rc = pData->printRect;
|
||||
rc.top = rc.bottom - pData->cyFooter;
|
||||
|
||||
hOldFont = SelectObject(pPrinter->hDC, pData->hHeaderFont);
|
||||
DrawHeaderOrFooter(pPrinter->hDC, &rc, Globals.szFooter, PageCount, &pData->stNow);
|
||||
SelectObject(pPrinter->hDC, hOldFont);
|
||||
}
|
||||
|
||||
if (EndPage(pPrinter->hDC) <= 0)
|
||||
{
|
||||
pData->status = STRING_PRINTFAILED;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define BODY_FONT_SIZE 10 /* 10pt */
|
||||
#define HEADER_FONT_SIZE 9 /* 9pt */
|
||||
#define SPACING_HEIGHT 4 /* 4pt */
|
||||
#define PRINTING_MESSAGE (WM_USER + 100)
|
||||
|
||||
static BOOL DoCreatePrintFonts(LPPRINTDLG pPrinter, PPRINT_DATA pPrintData)
|
||||
{
|
||||
LOGFONT lfBody, lfHeader;
|
||||
|
||||
/* Create the main text font for printing */
|
||||
lfBody = Globals.lfFont;
|
||||
lfBody.lfHeight = -Y_POINTS_TO_PIXELS(pPrinter->hDC, BODY_FONT_SIZE);
|
||||
pPrintData->hBodyFont = CreateFontIndirect(&lfBody);
|
||||
if (pPrintData->hBodyFont == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* Create the header/footer font */
|
||||
lfHeader = Globals.lfFont;
|
||||
lfHeader.lfHeight = -Y_POINTS_TO_PIXELS(pPrinter->hDC, HEADER_FONT_SIZE);
|
||||
lfHeader.lfWeight = FW_BOLD;
|
||||
pPrintData->hHeaderFont = CreateFontIndirect(&lfHeader);
|
||||
if (pPrintData->hHeaderFont == NULL)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL DoPrintDocument(PPRINT_DATA printData)
|
||||
{
|
||||
DOCINFO docInfo;
|
||||
LPPRINTDLG pPrinter = &printData->printer;
|
||||
DWORD CopyCount, PageCount;
|
||||
TEXTMETRIC tmHeader;
|
||||
BOOL ret = FALSE;
|
||||
HFONT hOldFont;
|
||||
|
||||
GetLocalTime(&printData->stNow);
|
||||
|
||||
printData->printRect = GetPrintingRect(pPrinter->hDC, &Globals.lMargins);
|
||||
|
||||
if (!DoCreatePrintFonts(pPrinter, printData))
|
||||
{
|
||||
printData->status = STRING_PRINTFAILED;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
if (pPrinter->Flags & PD_SELECTION)
|
||||
printData->cchText = GetSelectionTextLength(Globals.hEdit);
|
||||
else
|
||||
printData->cchText = GetWindowTextLength(Globals.hEdit);
|
||||
|
||||
/* Allocate a buffer for the text */
|
||||
printData->pszText = HeapAlloc(GetProcessHeap(), 0, (printData->cchText + 1) * sizeof(TCHAR));
|
||||
if (!printData->pszText)
|
||||
{
|
||||
printData->status = STRING_PRINTFAILED;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
if (pPrinter->Flags & PD_SELECTION)
|
||||
GetSelectionText(Globals.hEdit, printData->pszText, printData->cchText + 1);
|
||||
else
|
||||
GetWindowText(Globals.hEdit, printData->pszText, printData->cchText + 1);
|
||||
|
||||
/* Start a document */
|
||||
ZeroMemory(&docInfo, sizeof(docInfo));
|
||||
docInfo.cbSize = sizeof(DOCINFO);
|
||||
docInfo.lpszDocName = Globals.szFileTitle;
|
||||
if (StartDoc(pPrinter->hDC, &docInfo) <= 0)
|
||||
{
|
||||
printData->status = STRING_PRINTFAILED;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
/* Calculate the header and footer heights */
|
||||
hOldFont = SelectObject(pPrinter->hDC, printData->hHeaderFont);
|
||||
GetTextMetrics(pPrinter->hDC, &tmHeader);
|
||||
printData->cyHeader = printData->cyFooter = 2 * tmHeader.tmHeight;
|
||||
printData->cySpacing = Y_POINTS_TO_PIXELS(pPrinter->hDC, SPACING_HEIGHT);
|
||||
SelectObject(pPrinter->hDC, hOldFont); /* De-select the font */
|
||||
if (!Globals.szHeader[0])
|
||||
printData->cyHeader = printData->cySpacing = 0;
|
||||
if (!Globals.szFooter[0])
|
||||
printData->cyFooter = 0;
|
||||
|
||||
/* The printing-copies loop */
|
||||
for (CopyCount = 1; CopyCount <= pPrinter->nCopies; ++CopyCount)
|
||||
{
|
||||
/* The printing-pages loop */
|
||||
for (PageCount = 1, printData->ich = 0; printData->ich < printData->cchText; ++PageCount)
|
||||
{
|
||||
printData->currentPage = PageCount;
|
||||
PostMessage(printData->hwndDlg, PRINTING_MESSAGE, 0, 0);
|
||||
|
||||
if (!DoPrintPage(printData, PageCount))
|
||||
{
|
||||
AbortDoc(pPrinter->hDC); /* Cancel printing */
|
||||
goto Quit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EndDoc(pPrinter->hDC) <= 0)
|
||||
{
|
||||
printData->status = STRING_PRINTFAILED;
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
printData->status = STRING_PRINTCOMPLETE;
|
||||
|
||||
Quit:
|
||||
DeleteObject(printData->hHeaderFont);
|
||||
DeleteObject(printData->hBodyFont);
|
||||
if (printData->pszText)
|
||||
HeapFree(GetProcessHeap(), 0, printData->pszText);
|
||||
if (printData->status == STRING_PRINTCANCELING)
|
||||
printData->status = STRING_PRINTCANCELED;
|
||||
PostMessage(printData->hwndDlg, PRINTING_MESSAGE, 0, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DWORD WINAPI PrintThreadFunc(LPVOID arg)
|
||||
{
|
||||
PPRINT_DATA pData = arg;
|
||||
pData->currentPage = 1;
|
||||
pData->status = STRING_NOWPRINTING;
|
||||
PostMessage(pData->hwndDlg, PRINTING_MESSAGE, 0, 0);
|
||||
return DoPrintDocument(pData);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK
|
||||
DIALOG_Printing_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
TCHAR szText[MAX_STRING_LEN];
|
||||
static TCHAR s_szPage[64];
|
||||
static PPRINT_DATA s_pData = NULL;
|
||||
static HANDLE s_hThread = NULL;
|
||||
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
s_pData = (PPRINT_DATA)lParam;
|
||||
s_pData->hwndDlg = hwnd;
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_FILENAME, Globals.szFileTitle);
|
||||
GetDlgItemText(hwnd, IDC_PRINTING_PAGE, s_szPage, ARRAY_SIZE(s_szPage));
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_PAGE, NULL);
|
||||
|
||||
s_hThread = CreateThread(NULL, 0, PrintThreadFunc, s_pData, 0, NULL);
|
||||
if (!s_hThread)
|
||||
{
|
||||
s_pData->status = STRING_PRINTFAILED;
|
||||
EndDialog(hwnd, IDABORT);
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case PRINTING_MESSAGE:
|
||||
switch (s_pData->status)
|
||||
{
|
||||
case STRING_NOWPRINTING:
|
||||
case STRING_PRINTCANCELING:
|
||||
StringCchPrintf(szText, ARRAY_SIZE(szText), s_szPage, s_pData->currentPage);
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_PAGE, szText);
|
||||
|
||||
LoadString(Globals.hInstance, s_pData->status, szText, ARRAY_SIZE(szText));
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_STATUS, szText);
|
||||
break;
|
||||
|
||||
case STRING_PRINTCOMPLETE:
|
||||
case STRING_PRINTCANCELED:
|
||||
case STRING_PRINTFAILED:
|
||||
LoadString(Globals.hInstance, s_pData->status, szText, ARRAY_SIZE(szText));
|
||||
SetDlgItemText(hwnd, IDC_PRINTING_STATUS, szText);
|
||||
|
||||
if (s_pData->status == STRING_PRINTCOMPLETE)
|
||||
EndDialog(hwnd, IDOK);
|
||||
else if (s_pData->status == STRING_PRINTFAILED)
|
||||
EndDialog(hwnd, IDABORT);
|
||||
else
|
||||
EndDialog(hwnd, IDCANCEL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
if (LOWORD(wParam) == IDCANCEL && s_pData->status == STRING_NOWPRINTING)
|
||||
{
|
||||
EnableWindow(GetDlgItem(hwnd, IDCANCEL), FALSE);
|
||||
s_pData->status = STRING_PRINTCANCELING;
|
||||
PostMessage(hwnd, PRINTING_MESSAGE, 0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
if (s_hThread)
|
||||
CloseHandle(s_hThread);
|
||||
DeleteDC(s_pData->printer.hDC);
|
||||
s_pData = LocalFree(s_pData);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VOID DIALOG_FilePrint(VOID)
|
||||
{
|
||||
BOOL ret;
|
||||
LPPRINTDLG printer;
|
||||
PPRINT_DATA printData = LocalAlloc(LPTR, sizeof(PRINT_DATA));
|
||||
if (!printData)
|
||||
{
|
||||
ShowLastError();
|
||||
return;
|
||||
}
|
||||
|
||||
printer = &printData->printer;
|
||||
printer->lStructSize = sizeof(PRINTDLG);
|
||||
printer->hwndOwner = Globals.hMainWnd;
|
||||
printer->Flags = PD_RETURNDC | PD_SELECTION;
|
||||
|
||||
/* Disable the selection radio button if there is no text selected */
|
||||
if (!GetSelectionTextLength(Globals.hEdit))
|
||||
printer->Flags |= PD_NOSELECTION;
|
||||
|
||||
printer->nFromPage = 1;
|
||||
printer->nToPage = MAXWORD;
|
||||
printer->nMinPage = 1;
|
||||
printer->nMaxPage = MAXWORD;
|
||||
|
||||
printer->hDevMode = Globals.hDevMode;
|
||||
printer->hDevNames = Globals.hDevNames;
|
||||
|
||||
ret = PrintDlg(printer);
|
||||
/* NOTE: Even if PrintDlg returns FALSE, hDevMode and hDevNames may have changed. */
|
||||
Globals.hDevMode = printer->hDevMode;
|
||||
Globals.hDevNames = printer->hDevNames;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
LocalFree(printData);
|
||||
return; /* The user canceled printing */
|
||||
}
|
||||
assert(printer->hDC != NULL);
|
||||
|
||||
/* Ensure that each logical unit maps to one pixel */
|
||||
SetMapMode(printer->hDC, MM_TEXT);
|
||||
|
||||
if (DialogBoxParam(Globals.hInstance,
|
||||
MAKEINTRESOURCE(DIALOG_PRINTING),
|
||||
Globals.hMainWnd,
|
||||
DIALOG_Printing_DialogProc,
|
||||
(LPARAM)printer) == IDABORT)
|
||||
{
|
||||
AlertPrintError();
|
||||
}
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* DIALOG_PAGESETUP_Hook
|
||||
*/
|
||||
static UINT_PTR CALLBACK DIALOG_PAGESETUP_Hook(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
/* fetch last user input prior to display dialog */
|
||||
SetDlgItemText(hDlg, 0x141, Globals.szHeader);
|
||||
SetDlgItemText(hDlg, 0x143, Globals.szFooter);
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
{
|
||||
if (HIWORD(wParam) == BN_CLICKED)
|
||||
{
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDOK:
|
||||
/* save user input and close dialog */
|
||||
GetDlgItemText(hDlg, 0x141, Globals.szHeader, ARRAY_SIZE(Globals.szHeader));
|
||||
GetDlgItemText(hDlg, 0x143, Globals.szFooter, ARRAY_SIZE(Globals.szFooter));
|
||||
return FALSE;
|
||||
|
||||
case IDCANCEL:
|
||||
/* discard user input and close dialog */
|
||||
return FALSE;
|
||||
|
||||
case IDHELP:
|
||||
{
|
||||
/* FIXME: Bring this to work */
|
||||
static const TCHAR sorry[] = _T("Sorry, no help available");
|
||||
static const TCHAR help[] = _T("Help");
|
||||
MessageBox(Globals.hMainWnd, sorry, help, MB_ICONEXCLAMATION);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DIALOG_FilePageSetup
|
||||
*/
|
||||
VOID DIALOG_FilePageSetup(VOID)
|
||||
{
|
||||
PAGESETUPDLG page;
|
||||
|
||||
ZeroMemory(&page, sizeof(page));
|
||||
page.lStructSize = sizeof(page);
|
||||
page.hwndOwner = Globals.hMainWnd;
|
||||
page.Flags = PSD_ENABLEPAGESETUPTEMPLATE | PSD_ENABLEPAGESETUPHOOK | PSD_MARGINS;
|
||||
page.hInstance = Globals.hInstance;
|
||||
page.rtMargin = Globals.lMargins;
|
||||
page.hDevMode = Globals.hDevMode;
|
||||
page.hDevNames = Globals.hDevNames;
|
||||
page.lpPageSetupTemplateName = MAKEINTRESOURCE(DIALOG_PAGESETUP);
|
||||
page.lpfnPageSetupHook = DIALOG_PAGESETUP_Hook;
|
||||
|
||||
PageSetupDlg(&page);
|
||||
|
||||
/* NOTE: Even if PageSetupDlg returns FALSE, the following members may have changed */
|
||||
Globals.hDevMode = page.hDevMode;
|
||||
Globals.hDevNames = page.hDevNames;
|
||||
Globals.lMargins = page.rtMargin;
|
||||
}
|
Loading…
Reference in a new issue