| /* |
| * 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)>, 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)>, 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; |
| } |