blob: 5888999e93769fb34187a6688f39886d6d8fc07e [file] [log] [blame]
/*
* Wordpad implementation - Printing and print preview functions
*
* Copyright 2007 by Alexander N. Sørnes <alex@thehandofagony.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <windows.h>
#include <richedit.h>
#include <commctrl.h>
#include "wordpad.h"
typedef struct _previewinfo
{
int page;
int pages;
HDC hdc;
HDC hdcSized;
RECT window;
LPWSTR wszFileName;
} previewinfo, *ppreviewinfo;
static HGLOBAL devMode;
static HGLOBAL devNames;
static RECT margins;
static previewinfo preview;
static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',0};
static LPWSTR get_print_file_filter(HWND hMainWnd)
{
static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1];
const WCHAR files_prn[] = {'*','.','P','R','N',0};
const WCHAR files_all[] = {'*','.','*','\0'};
LPWSTR p;
HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hMainWnd, GWLP_HINSTANCE);
p = wszPrintFilter;
LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
p += lstrlenW(p) + 1;
lstrcpyW(p, files_prn);
p += lstrlenW(p) + 1;
LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
p += lstrlenW(p) + 1;
lstrcpyW(p, files_all);
p += lstrlenW(p) + 1;
*p = 0;
return wszPrintFilter;
}
void registry_set_pagemargins(HKEY hKey)
{
RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT));
}
void registry_read_pagemargins(HKEY hKey)
{
DWORD size = sizeof(RECT);
if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins,
&size) != ERROR_SUCCESS || size != sizeof(RECT))
{
margins.top = 1417;
margins.bottom = 1417;
margins.left = 1757;
margins.right = 1757;
}
}
static void AddTextButton(HWND hRebarWnd, int string, int command, int id)
{
REBARBANDINFOW rb;
HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hRebarWnd, GWLP_HINSTANCE);
WCHAR text[MAX_STRING_LEN];
HWND hButton;
LoadStringW(hInstance, string, text, MAX_STRING_LEN);
hButton = CreateWindowW(WC_BUTTONW, text,
WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
hRebarWnd, (HMENU)command, hInstance, NULL);
rb.cbSize = sizeof(rb);
rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
rb.hwndChild = hButton;
rb.cyChild = rb.cyMinChild = 22;
rb.cx = rb.cxMinChild = 90;
rb.cxIdeal = 100;
rb.wID = id;
SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb);
}
static HDC make_dc(void)
{
if(devNames && devMode)
{
LPDEVNAMES dn = GlobalLock(devNames);
LPDEVMODEW dm = GlobalLock(devMode);
HDC ret;
ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
(LPWSTR)dn + dn->wDeviceOffset, 0, dm);
GlobalUnlock(dn);
GlobalUnlock(dm);
return ret;
} else
{
return 0;
}
}
static LONG twips_to_centmm(int twips)
{
return MulDiv(twips, 1000, 567);
}
LONG centmm_to_twips(int mm)
{
return MulDiv(mm, 567, 1000);
}
static LONG twips_to_pixels(int twips, int dpi)
{
float ret = ((float)twips / ((float)567 * 2.54)) * (float)dpi;
return (LONG)ret;
}
static LONG devunits_to_twips(int units, int dpi)
{
float ret = ((float)units / (float)dpi) * (float)567 * 2.54;
return (LONG)ret;
}
static RECT get_print_rect(HDC hdc)
{
RECT rc;
int width, height;
if(hdc)
{
int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX);
height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY);
} else
{
width = centmm_to_twips(18500);
height = centmm_to_twips(27000);
}
rc.left = margins.left;
rc.right = width - margins.right;
rc.top = margins.top;
rc.bottom = height - margins.bottom;
return rc;
}
void target_device(HWND hMainWnd, DWORD wordWrap)
{
HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
HDC hdc = make_dc();
int width = 0;
if(wordWrap == ID_WORDWRAP_MARGIN)
{
RECT rc = get_print_rect(hdc);
width = rc.right;
}
if(!hdc)
{
HDC hMaindc = GetDC(hMainWnd);
hdc = CreateCompatibleDC(hMaindc);
ReleaseDC(hMainWnd, hMaindc);
}
SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width);
DeleteDC(hdc);
}
static LPWSTR dialog_print_to_file(HWND hMainWnd)
{
OPENFILENAMEW ofn;
static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0};
static const WCHAR defExt[] = {'P','R','N',0};
static LPWSTR file_filter;
if(!file_filter)
file_filter = get_print_file_filter(hMainWnd);
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
ofn.hwndOwner = hMainWnd;
ofn.lpstrFilter = file_filter;
ofn.lpstrFile = (LPWSTR)file;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrDefExt = (LPWSTR)defExt;
if(GetSaveFileNameW(&ofn))
return (LPWSTR)file;
else
return FALSE;
}
static int get_num_pages(HWND hEditorWnd, FORMATRANGE fr)
{
int page = 0;
do
{
page++;
fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE,
(LPARAM)&fr);
}
while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
return page;
}
static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
{
int i;
for(i = 1; i <= page; i++)
{
if(i == page)
break;
fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)fr);
}
}
static void print(LPPRINTDLGW pd, LPWSTR wszFileName)
{
FORMATRANGE fr;
DOCINFOW di;
HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR);
int printedPages = 0;
fr.hdc = pd->hDC;
fr.hdcTarget = pd->hDC;
fr.rc = get_print_rect(fr.hdc);
fr.rcPage.left = 0;
fr.rcPage.right = fr.rc.right + margins.right;
fr.rcPage.top = 0;
fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
ZeroMemory(&di, sizeof(di));
di.cbSize = sizeof(di);
di.lpszDocName = wszFileName;
if(pd->Flags & PD_PRINTTOFILE)
{
di.lpszOutput = dialog_print_to_file(pd->hwndOwner);
if(!di.lpszOutput)
return;
}
if(pd->Flags & PD_SELECTION)
{
SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);
} else
{
GETTEXTLENGTHEX gt;
gt.flags = GTL_DEFAULT;
gt.codepage = 1200;
fr.chrg.cpMin = 0;
fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
if(pd->Flags & PD_PAGENUMS)
char_from_pagenum(hEditorWnd, &fr, pd->nToPage);
}
StartDocW(fr.hdc, &di);
do
{
if(StartPage(fr.hdc) <= 0)
break;
fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
if(EndPage(fr.hdc) <= 0)
break;
printedPages++;
if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage)))
break;
}
while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
EndDoc(fr.hdc);
SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
}
void dialog_printsetup(HWND hMainWnd)
{
PAGESETUPDLGW ps;
ZeroMemory(&ps, sizeof(ps));
ps.lStructSize = sizeof(ps);
ps.hwndOwner = hMainWnd;
ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
ps.rtMargin.left = twips_to_centmm(margins.left);
ps.rtMargin.right = twips_to_centmm(margins.right);
ps.rtMargin.top = twips_to_centmm(margins.top);
ps.rtMargin.bottom = twips_to_centmm(margins.bottom);
ps.hDevMode = devMode;
ps.hDevNames = devNames;
if(PageSetupDlgW(&ps))
{
margins.left = centmm_to_twips(ps.rtMargin.left);
margins.right = centmm_to_twips(ps.rtMargin.right);
margins.top = centmm_to_twips(ps.rtMargin.top);
margins.bottom = centmm_to_twips(ps.rtMargin.bottom);
devMode = ps.hDevMode;
devNames = ps.hDevNames;
}
}
void get_default_printer_opts(void)
{
PRINTDLGW pd;
ZeroMemory(&pd, sizeof(pd));
ZeroMemory(&pd, sizeof(pd));
pd.lStructSize = sizeof(pd);
pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
pd.hDevMode = devMode;
PrintDlgW(&pd);
devMode = pd.hDevMode;
devNames = pd.hDevNames;
}
void print_quick(LPWSTR wszFileName)
{
PRINTDLGW pd;
ZeroMemory(&pd, sizeof(pd));
pd.hDC = make_dc();
print(&pd, wszFileName);
}
void dialog_print(HWND hMainWnd, LPWSTR wszFileName)
{
PRINTDLGW pd;
HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
int from = 0;
int to = 0;
ZeroMemory(&pd, sizeof(pd));
pd.lStructSize = sizeof(pd);
pd.hwndOwner = hMainWnd;
pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
pd.nMinPage = 1;
pd.nMaxPage = -1;
pd.hDevMode = devMode;
pd.hDevNames = devNames;
SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to);
if(from == to)
pd.Flags |= PD_NOSELECTION;
if(PrintDlgW(&pd))
{
devMode = pd.hDevMode;
devNames = pd.hDevNames;
print(&pd, wszFileName);
}
}
static void preview_bar_show(HWND hMainWnd, BOOL show)
{
HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
int i;
if(show)
{
REBARBANDINFOW rb;
HWND hStatic;
AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1);
AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2);
AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3);
AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN4);
hStatic = CreateWindowW(WC_STATICW, NULL,
WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
hReBar, NULL, NULL, NULL);
rb.cbSize = sizeof(rb);
rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
rb.hwndChild = hStatic;
rb.cyChild = rb.cyMinChild = 22;
rb.cx = rb.cxMinChild = 90;
rb.cxIdeal = 100;
rb.wID = BANDID_PREVIEW_BUFFER;
SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb);
} else
{
for(i = 0; i <= PREVIEW_BUTTONS; i++)
SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
}
}
void init_preview(HWND hMainWnd, LPWSTR wszFileName)
{
preview.page = 1;
preview.hdc = 0;
preview.wszFileName = wszFileName;
preview_bar_show(hMainWnd, TRUE);
}
void close_preview(HWND hMainWnd)
{
preview.window.right = 0;
preview.window.bottom = 0;
preview.page = 0;
preview.pages = 0;
preview_bar_show(hMainWnd, FALSE);
}
BOOL preview_isactive(void)
{
return preview.page != 0;
}
LRESULT print_preview(HWND hMainWnd)
{
FORMATRANGE fr;
GETTEXTLENGTHEX gt;
HDC hdc;
RECT window, background;
HBITMAP hBitmapCapture, hBitmapScaled;
int bmWidth, bmHeight, bmNewWidth, bmNewHeight;
float ratioWidth, ratioHeight, ratio;
int xOffset, yOffset;
int barheight;
HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
PAINTSTRUCT ps;
hdc = BeginPaint(hMainWnd, &ps);
GetClientRect(hMainWnd, &window);
fr.hdcTarget = make_dc();
fr.rc = get_print_rect(fr.hdcTarget);
fr.rcPage.left = 0;
fr.rcPage.top = 0;
fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
fr.rcPage.right = fr.rc.right + margins.right;
bmWidth = twips_to_pixels(fr.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
bmHeight = twips_to_pixels(fr.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
hBitmapCapture = CreateCompatibleBitmap(hdc, bmWidth, bmHeight);
if(!preview.hdc)
{
RECT paper;
HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
preview.hdc = CreateCompatibleDC(hdc);
fr.hdc = preview.hdc;
gt.flags = GTL_DEFAULT;
gt.codepage = 1200;
fr.chrg.cpMin = 0;
fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
paper.left = 0;
paper.right = bmWidth;
paper.top = 0;
paper.bottom = bmHeight;
if(!preview.pages)
preview.pages = get_num_pages(hEditorWnd, fr);
SelectObject(preview.hdc, hBitmapCapture);
char_from_pagenum(hEditorWnd, &fr, preview.page);
FillRect(preview.hdc, &paper, GetStockObject(WHITE_BRUSH));
SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE), preview.page < preview.pages);
}
barheight = SendMessageW(hReBar, RB_GETBARHEIGHT, 0, 0);
ratioWidth = ((float)window.right - 20.0) / (float)bmHeight;
ratioHeight = ((float)window.bottom - 20.0 - (float)barheight) / (float)bmHeight;
if(ratioWidth > ratioHeight)
ratio = ratioHeight;
else
ratio = ratioWidth;
bmNewWidth = (int)((float)bmWidth * ratio);
bmNewHeight = (int)((float)bmHeight * ratio);
hBitmapScaled = CreateCompatibleBitmap(hdc, bmNewWidth, bmNewHeight);
xOffset = ((window.right - bmNewWidth) / 2);
yOffset = ((window.bottom - bmNewHeight + barheight) / 2);
if(window.right != preview.window.right || window.bottom != preview.window.bottom)
{
DeleteDC(preview.hdcSized),
preview.hdcSized = CreateCompatibleDC(hdc);
SelectObject(preview.hdcSized, hBitmapScaled);
StretchBlt(preview.hdcSized, 0, 0, bmNewWidth, bmNewHeight, preview.hdc, 0, 0, bmWidth, bmHeight, SRCCOPY);
}
window.top = barheight;
FillRect(hdc, &window, GetStockObject(GRAY_BRUSH));
SelectObject(hdc, hBitmapScaled);
background.left = xOffset - 2;
background.right = xOffset + bmNewWidth + 2;
background.top = yOffset - 2;
background.bottom = yOffset + bmNewHeight + 2;
FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
BitBlt(hdc, xOffset, yOffset, bmNewWidth, bmNewHeight, preview.hdcSized, 0, 0, SRCCOPY);
DeleteDC(fr.hdcTarget);
preview.window = window;
EndPaint(hMainWnd, &ps);
return 0;
}
LRESULT preview_command(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
switch(LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessageW(hWnd, WM_CLOSE, 0, 0);
break;
case ID_PREVIEW_NEXTPAGE:
case ID_PREVIEW_PREVPAGE:
{
HWND hReBar = GetDlgItem(hWnd, IDC_REBAR);
RECT rc;
if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
preview.page++;
else
preview.page--;
preview.hdc = 0;
preview.window.right = 0;
GetClientRect(hWnd, &rc);
rc.top += SendMessageW(hReBar, RB_GETBARHEIGHT, 0, 0);
InvalidateRect(hWnd, &rc, TRUE);
}
break;
case ID_PRINT:
dialog_print(hWnd, preview.wszFileName);
SendMessageW(hWnd, WM_CLOSE, 0, 0);
break;
}
return 0;
}