2 * Wordpad implementation - Printing and print preview functions
4 * Copyright 2007-2008 by Alexander N. Sørnes <alex@thehandofagony.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 typedef struct _previewinfo
31 int saved_pages_shown;
32 int *pageEnds, pageCapacity;
44 } previewinfo, *ppreviewinfo;
46 static HGLOBAL devMode;
47 static HGLOBAL devNames;
50 static previewinfo preview;
52 extern const WCHAR wszPreviewWndClass[];
54 static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',0};
55 static const WCHAR var_previewpages[] = {'P','r','e','v','i','e','w','P','a','g','e','s',0};
57 static LPWSTR get_print_file_filter(HWND hMainWnd)
59 static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1];
60 const WCHAR files_prn[] = {'*','.','P','R','N',0};
61 const WCHAR files_all[] = {'*','.','*','\0'};
63 HINSTANCE hInstance = GetModuleHandleW(0);
66 LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
68 lstrcpyW(p, files_prn);
70 LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
72 lstrcpyW(p, files_all);
76 return wszPrintFilter;
79 void registry_set_pagemargins(HKEY hKey)
81 RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT));
84 void registry_read_pagemargins(HKEY hKey)
86 DWORD size = sizeof(RECT);
88 if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins,
89 &size) != ERROR_SUCCESS || size != sizeof(RECT))
92 margins.bottom = 1417;
98 void registry_set_previewpages(HKEY hKey)
100 RegSetValueExW(hKey, var_previewpages, 0, REG_DWORD,
101 (LPBYTE)&preview.pages_shown, sizeof(DWORD));
104 void registry_read_previewpages(HKEY hKey)
106 DWORD size = sizeof(DWORD);
108 RegQueryValueExW(hKey, var_previewpages, 0, NULL,
109 (LPBYTE)&preview.pages_shown, &size) != ERROR_SUCCESS ||
110 size != sizeof(DWORD))
112 preview.pages_shown = 1;
114 if (preview.pages_shown < 1) preview.pages_shown = 1;
115 else if (preview.pages_shown > 2) preview.pages_shown = 2;
120 static void AddTextButton(HWND hRebarWnd, UINT string, UINT command, UINT id)
123 HINSTANCE hInstance = GetModuleHandleW(0);
124 WCHAR text[MAX_STRING_LEN];
127 LoadStringW(hInstance, string, text, MAX_STRING_LEN);
128 hButton = CreateWindowW(WC_BUTTONW, text,
129 WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
130 hRebarWnd, ULongToHandle(command), hInstance, NULL);
132 rb.cbSize = REBARBANDINFOW_V6_SIZE;
133 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
134 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
135 rb.hwndChild = hButton;
136 rb.cyChild = rb.cyMinChild = 22;
137 rb.cx = rb.cxMinChild = 90;
141 SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb);
144 static HDC make_dc(void)
146 if(devNames && devMode)
148 LPDEVNAMES dn = GlobalLock(devNames);
149 LPDEVMODEW dm = GlobalLock(devMode);
152 ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
153 (LPWSTR)dn + dn->wDeviceOffset, 0, dm);
165 static LONG twips_to_centmm(int twips)
167 return MulDiv(twips, CENTMM_PER_INCH, TWIPS_PER_INCH);
170 static LONG centmm_to_twips(int mm)
172 return MulDiv(mm, TWIPS_PER_INCH, CENTMM_PER_INCH);
175 static LONG twips_to_pixels(int twips, int dpi)
177 return MulDiv(twips, dpi, TWIPS_PER_INCH);
180 static LONG devunits_to_twips(int units, int dpi)
182 return MulDiv(units, TWIPS_PER_INCH, dpi);
186 static RECT get_print_rect(HDC hdc)
193 int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
194 int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
195 width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX);
196 height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY);
199 width = centmm_to_twips(18500);
200 height = centmm_to_twips(27000);
203 rc.left = margins.left;
204 rc.right = width - margins.right;
205 rc.top = margins.top;
206 rc.bottom = height - margins.bottom;
211 void target_device(HWND hMainWnd, DWORD wordWrap)
213 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
215 if(wordWrap == ID_WORDWRAP_MARGIN)
220 RECT rc = get_print_rect(hdc);
222 width = rc.right - rc.left;
225 HDC hMaindc = GetDC(hMainWnd);
226 hdc = CreateCompatibleDC(hMaindc);
227 ReleaseDC(hMainWnd, hMaindc);
229 result = SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width);
233 /* otherwise EM_SETTARGETDEVICE failed, so fall back on wrapping
234 * to window using the NULL DC. */
237 if (wordWrap != ID_WORDWRAP_NONE) {
238 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 0);
240 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 1);
245 static LPWSTR dialog_print_to_file(HWND hMainWnd)
248 static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0};
249 static const WCHAR defExt[] = {'P','R','N',0};
250 static LPWSTR file_filter;
253 file_filter = get_print_file_filter(hMainWnd);
255 ZeroMemory(&ofn, sizeof(ofn));
257 ofn.lStructSize = sizeof(ofn);
258 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
259 ofn.hwndOwner = hMainWnd;
260 ofn.lpstrFilter = file_filter;
261 ofn.lpstrFile = file;
262 ofn.nMaxFile = MAX_PATH;
263 ofn.lpstrDefExt = defExt;
265 if(GetSaveFileNameW(&ofn))
271 static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
277 for(i = 1; i < page; i++)
279 int bottom = fr->rc.bottom;
280 fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, (LPARAM)fr);
281 fr->rc.bottom = bottom;
285 static HWND get_ruler_wnd(HWND hMainWnd)
287 return GetDlgItem(GetDlgItem(hMainWnd, IDC_REBAR), IDC_RULER);
290 void redraw_ruler(HWND hRulerWnd)
294 GetClientRect(hRulerWnd, &rc);
295 InvalidateRect(hRulerWnd, &rc, TRUE);
298 static void update_ruler(HWND hRulerWnd)
300 SendMessageW(hRulerWnd, WM_USER, 0, 0);
301 redraw_ruler(hRulerWnd);
304 static void add_ruler_units(HDC hdcRuler, RECT* drawRect, BOOL NewMetrics, LONG EditLeftmost)
310 static HBITMAP hBitmap;
311 int i, x, y, RulerTextEnd;
315 WCHAR FontName[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0};
320 DeleteObject(hBitmap);
323 hdc = CreateCompatibleDC(0);
325 CmPixels = twips_to_pixels(centmm_to_twips(1000), GetDeviceCaps(hdc, LOGPIXELSX));
326 QuarterCmPixels = (int)((float)CmPixels / 4.0);
328 hBitmap = CreateCompatibleBitmap(hdc, drawRect->right, drawRect->bottom);
329 SelectObject(hdc, hBitmap);
330 FillRect(hdc, drawRect, GetStockObject(WHITE_BRUSH));
332 hFont = CreateFontW(10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FontName);
334 SelectObject(hdc, hFont);
335 SetBkMode(hdc, TRANSPARENT);
336 SetTextAlign(hdc, TA_CENTER);
337 y = (int)(((float)drawRect->bottom - (float)drawRect->top) / 2.0) + 1;
338 RulerTextEnd = drawRect->right - EditLeftmost + 1;
339 for(i = 1, x = EditLeftmost; x < (drawRect->right - EditLeftmost + 1); i ++)
342 WCHAR format[] = {'%','d',0};
345 x2 += QuarterCmPixels;
346 if(x2 > RulerTextEnd)
349 MoveToEx(hdc, x2, y, NULL);
350 LineTo(hdc, x2, y+2);
352 x2 += QuarterCmPixels;
353 if(x2 > RulerTextEnd)
356 MoveToEx(hdc, x2, y - 3, NULL);
357 LineTo(hdc, x2, y + 3);
359 x2 += QuarterCmPixels;
360 if(x2 > RulerTextEnd)
363 MoveToEx(hdc, x2, y, NULL);
364 LineTo(hdc, x2, y+2);
370 wsprintfW(str, format, i);
371 TextOutW(hdc, x, 5, str, lstrlenW(str));
376 BitBlt(hdcRuler, 0, 0, drawRect->right, drawRect->bottom, hdc, 0, 0, SRCAND);
379 static void paint_ruler(HWND hWnd, LONG EditLeftmost, BOOL NewMetrics)
382 HDC hdc = BeginPaint(hWnd, &ps);
383 HDC hdcPrint = make_dc();
384 RECT printRect = get_print_rect(hdcPrint);
386 HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
388 GetClientRect(hWnd, &drawRect);
389 FillRect(hdc, &drawRect, hBrush);
392 drawRect.bottom -= 3;
393 drawRect.left = EditLeftmost;
394 drawRect.right = twips_to_pixels(printRect.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
395 FillRect(hdc, &drawRect, GetStockObject(WHITE_BRUSH));
399 DrawEdge(hdc, &drawRect, EDGE_SUNKEN, BF_RECT);
401 drawRect.left = drawRect.right - 1;
402 drawRect.right = twips_to_pixels(printRect.right + margins.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
403 DrawEdge(hdc, &drawRect, EDGE_ETCHED, BF_RECT);
407 add_ruler_units(hdc, &drawRect, NewMetrics, EditLeftmost);
409 SelectObject(hdc, GetStockObject(BLACK_BRUSH));
410 DeleteObject(hBrush);
415 LRESULT CALLBACK ruler_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
417 static WNDPROC pPrevRulerProc;
418 static LONG EditLeftmost;
419 static BOOL NewMetrics;
426 EditLeftmost = ((POINTL*)wParam)->x;
427 pPrevRulerProc = (WNDPROC)lParam;
433 paint_ruler(hWnd, EditLeftmost, NewMetrics);
437 return CallWindowProcW(pPrevRulerProc, hWnd, msg, wParam, lParam);
443 static void print(LPPRINTDLGW pd, LPWSTR wszFileName)
447 HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR);
448 int printedPages = 0;
451 fr.hdcTarget = pd->hDC;
453 fr.rc = get_print_rect(fr.hdc);
455 fr.rcPage.right = fr.rc.right + margins.right;
457 fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
459 ZeroMemory(&di, sizeof(di));
460 di.cbSize = sizeof(di);
461 di.lpszDocName = wszFileName;
463 if(pd->Flags & PD_PRINTTOFILE)
465 di.lpszOutput = dialog_print_to_file(pd->hwndOwner);
470 if(pd->Flags & PD_SELECTION)
472 SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);
476 gt.flags = GTL_DEFAULT;
479 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)>, 0);
481 if(pd->Flags & PD_PAGENUMS)
482 char_from_pagenum(hEditorWnd, &fr, pd->nToPage);
485 StartDocW(fr.hdc, &di);
488 if(StartPage(fr.hdc) <= 0)
491 fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
493 if(EndPage(fr.hdc) <= 0)
497 if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage)))
500 while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
503 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
506 void dialog_printsetup(HWND hMainWnd)
510 ZeroMemory(&ps, sizeof(ps));
511 ps.lStructSize = sizeof(ps);
512 ps.hwndOwner = hMainWnd;
513 ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
514 ps.rtMargin.left = twips_to_centmm(margins.left);
515 ps.rtMargin.right = twips_to_centmm(margins.right);
516 ps.rtMargin.top = twips_to_centmm(margins.top);
517 ps.rtMargin.bottom = twips_to_centmm(margins.bottom);
518 ps.hDevMode = devMode;
519 ps.hDevNames = devNames;
521 if(PageSetupDlgW(&ps))
523 margins.left = centmm_to_twips(ps.rtMargin.left);
524 margins.right = centmm_to_twips(ps.rtMargin.right);
525 margins.top = centmm_to_twips(ps.rtMargin.top);
526 margins.bottom = centmm_to_twips(ps.rtMargin.bottom);
527 devMode = ps.hDevMode;
528 devNames = ps.hDevNames;
529 update_ruler(get_ruler_wnd(hMainWnd));
533 void get_default_printer_opts(void)
536 ZeroMemory(&pd, sizeof(pd));
538 ZeroMemory(&pd, sizeof(pd));
539 pd.lStructSize = sizeof(pd);
540 pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
541 pd.hDevMode = devMode;
545 devMode = pd.hDevMode;
546 devNames = pd.hDevNames;
549 void print_quick(HWND hMainWnd, LPWSTR wszFileName)
553 ZeroMemory(&pd, sizeof(pd));
554 pd.hwndOwner = hMainWnd;
557 print(&pd, wszFileName);
561 void dialog_print(HWND hMainWnd, LPWSTR wszFileName)
564 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
568 ZeroMemory(&pd, sizeof(pd));
569 pd.lStructSize = sizeof(pd);
570 pd.hwndOwner = hMainWnd;
571 pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
574 pd.hDevMode = devMode;
575 pd.hDevNames = devNames;
577 SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to);
579 pd.Flags |= PD_NOSELECTION;
583 devMode = pd.hDevMode;
584 devNames = pd.hDevNames;
585 print(&pd, wszFileName);
586 update_ruler(get_ruler_wnd(hMainWnd));
590 static void preview_bar_show(HWND hMainWnd, BOOL show)
592 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
599 UINT num_pages_string = preview.pages_shown > 1 ? STRING_PREVIEW_ONEPAGE :
600 STRING_PREVIEW_TWOPAGES;
602 AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1);
603 AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2);
604 AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3);
605 AddTextButton(hReBar, num_pages_string, ID_PREVIEW_NUMPAGES, BANDID_PREVIEW_BTN4);
606 AddTextButton(hReBar, STRING_PREVIEW_ZOOMIN, ID_PREVIEW_ZOOMIN, BANDID_PREVIEW_BTN5);
607 AddTextButton(hReBar, STRING_PREVIEW_ZOOMOUT, ID_PREVIEW_ZOOMOUT, BANDID_PREVIEW_BTN6);
608 AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN7);
610 hStatic = CreateWindowW(WC_STATICW, NULL,
611 WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
612 hReBar, NULL, NULL, NULL);
614 rb.cbSize = REBARBANDINFOW_V6_SIZE;
615 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
616 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
617 rb.hwndChild = hStatic;
618 rb.cyChild = rb.cyMinChild = 22;
619 rb.cx = rb.cxMinChild = 90;
621 rb.wID = BANDID_PREVIEW_BUFFER;
623 SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb);
626 for(i = 0; i <= PREVIEW_BUTTONS; i++)
627 SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
631 static const int min_spacing = 10;
633 static void update_preview_scrollbars(HWND hwndPreview, RECT *window)
636 sbi.cbSize = sizeof(sbi);
637 sbi.fMask = SIF_PAGE|SIF_RANGE;
639 if (preview.zoomlevel == 0)
641 /* Hide scrollbars when zoomed out. */
643 sbi.nPage = window->right;
644 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
645 sbi.nPage = window->bottom;
646 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
648 sbi.nMax = preview.bmScaledSize.cx * preview.pages_shown +
649 min_spacing * (preview.pages_shown + 1);
650 sbi.nPage = window->right;
651 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
652 /* Change in the horizontal scrollbar visibility affects the
653 * client rect, so update the client rect. */
654 GetClientRect(hwndPreview, window);
655 sbi.nMax = preview.bmScaledSize.cy + min_spacing * 2;
656 sbi.nPage = window->bottom;
657 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
661 static void update_preview_sizes(HWND hwndPreview, BOOL zoomLevelUpdated)
665 GetClientRect(hwndPreview, &window);
667 /* The zoom ratio isn't updated for partial zoom because of resizing the window. */
668 if (zoomLevelUpdated || preview.zoomlevel != 1)
670 float ratio, ratioHeight, ratioWidth;
671 if (preview.zoomlevel == 2)
675 ratioHeight = (window.bottom - min_spacing * 2) / (float)preview.bmSize.cy;
677 ratioWidth = (float)(window.right -
678 min_spacing * (preview.pages_shown + 1)) /
679 (preview.pages_shown * preview.bmSize.cx);
681 if(ratioWidth > ratioHeight)
686 if (preview.zoomlevel == 1)
687 ratio += (1.0 - ratio) / 2;
689 preview.zoomratio = ratio;
692 preview.bmScaledSize.cx = preview.bmSize.cx * preview.zoomratio;
693 preview.bmScaledSize.cy = preview.bmSize.cy * preview.zoomratio;
695 preview.spacing.cy = max(min_spacing, (window.bottom - preview.bmScaledSize.cy) / 2);
697 preview.spacing.cx = (window.right -
698 preview.bmScaledSize.cx * preview.pages_shown) /
699 (preview.pages_shown + 1);
700 if (preview.spacing.cx < min_spacing)
701 preview.spacing.cx = min_spacing;
703 update_preview_scrollbars(hwndPreview, &window);
706 static void draw_margin_lines(HDC hdc, int x, int y, float ratio)
710 RECT page_margin = preview.rcPage;
712 dpi.cx = GetDeviceCaps(hdc, LOGPIXELSX);
713 dpi.cy = GetDeviceCaps(hdc, LOGPIXELSY);
715 page_margin.left = preview.rcPage.left + margins.left;
716 page_margin.top = preview.rcPage.top + margins.top;
717 page_margin.bottom = preview.rcPage.bottom - margins.bottom;
718 page_margin.right = preview.rcPage.right - margins.right;
720 page_margin.left = (int)((float)twips_to_pixels(page_margin.left, dpi.cx) * ratio);
721 page_margin.top = (int)((float)twips_to_pixels(page_margin.top, dpi.cy) * ratio);
722 page_margin.bottom = (int)((float)twips_to_pixels(page_margin.bottom, dpi.cy) * ratio);
723 page_margin.right = (int)((float)twips_to_pixels(page_margin.right, dpi.cx) * ratio);
725 page_margin.left += x;
726 page_margin.top += y;
727 page_margin.bottom += y;
728 page_margin.right += x;
730 hPen = CreatePen(PS_DOT, 1, RGB(0,0,0));
731 oldPen = SelectObject(hdc, hPen);
733 MoveToEx(hdc, x, page_margin.top, NULL);
734 LineTo(hdc, x + preview.bmScaledSize.cx, page_margin.top);
735 MoveToEx(hdc, x, page_margin.bottom, NULL);
736 LineTo(hdc, x + preview.bmScaledSize.cx, page_margin.bottom);
738 MoveToEx(hdc, page_margin.left, y, NULL);
739 LineTo(hdc, page_margin.left, y + preview.bmScaledSize.cy);
740 MoveToEx(hdc, page_margin.right, y, NULL);
741 LineTo(hdc, page_margin.right, y + preview.bmScaledSize.cy);
743 SelectObject(hdc, oldPen);
747 static BOOL is_last_preview_page(int page)
749 return preview.pageEnds[page - 1] >= preview.textlength;
752 void init_preview(HWND hMainWnd, LPWSTR wszFileName)
754 HINSTANCE hInstance = GetModuleHandleW(0);
758 preview.wszFileName = wszFileName;
759 preview.zoomratio = 0;
760 preview.zoomlevel = 0;
761 preview_bar_show(hMainWnd, TRUE);
763 CreateWindowExW(0, wszPreviewWndClass, NULL,
764 WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL,
765 0, 0, 200, 10, hMainWnd, (HMENU)IDC_PREVIEW, hInstance, NULL);
768 void close_preview(HWND hMainWnd)
770 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
771 preview.window.right = 0;
772 preview.window.bottom = 0;
774 HeapFree(GetProcessHeap(), 0, preview.pageEnds);
775 preview.pageEnds = NULL;
776 preview.pageCapacity = 0;
777 if (preview.zoomlevel > 0)
778 preview.pages_shown = preview.saved_pages_shown;
780 HBITMAP oldbm = GetCurrentObject(preview.hdc, OBJ_BITMAP);
781 DeleteDC(preview.hdc);
786 HBITMAP oldbm = GetCurrentObject(preview.hdc2, OBJ_BITMAP);
787 DeleteDC(preview.hdc2);
792 preview_bar_show(hMainWnd, FALSE);
793 DestroyWindow(hwndPreview);
796 BOOL preview_isactive(void)
798 return preview.page != 0;
801 static void draw_preview(HWND hEditorWnd, FORMATRANGE* lpFr, RECT* paper, int page)
805 if (!preview.pageEnds)
807 preview.pageCapacity = 32;
808 preview.pageEnds = HeapAlloc(GetProcessHeap(), 0,
809 sizeof(int) * preview.pageCapacity);
810 if (!preview.pageEnds) return;
811 } else if (page >= preview.pageCapacity) {
813 new_buffer = HeapReAlloc(GetProcessHeap(), 0, preview.pageEnds,
814 sizeof(int) * preview.pageCapacity * 2);
815 if (!new_buffer) return;
816 preview.pageCapacity *= 2;
817 preview.pageEnds = new_buffer;
820 FillRect(lpFr->hdc, paper, GetStockObject(WHITE_BRUSH));
821 if (page > 1 && is_last_preview_page(page - 1)) return;
822 lpFr->chrg.cpMin = page <= 1 ? 0 : preview.pageEnds[page-2];
823 bottom = lpFr->rc.bottom;
824 preview.pageEnds[page-1] = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)lpFr);
826 /* EM_FORMATRANGE sets fr.rc.bottom to indicate the area printed in,
827 * but we want to keep the original for drawing margins */
828 lpFr->rc.bottom = bottom;
829 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
832 static void update_preview_buttons(HWND hMainWnd)
834 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
835 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
836 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE),
837 !is_last_preview_page(preview.page) &&
838 !is_last_preview_page(preview.page + preview.pages_shown - 1));
839 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES),
840 preview.pages_shown > 1 ||
841 (!is_last_preview_page(1) && preview.zoomlevel == 0));
842 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMIN), preview.zoomlevel < 2);
843 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMOUT), preview.zoomlevel > 0);
846 static LRESULT print_preview(HWND hwndPreview)
850 HRGN back_rgn, excl_rgn;
851 RECT window, background;
855 hdc = BeginPaint(hwndPreview, &ps);
856 GetClientRect(hwndPreview, &window);
857 back_rgn = CreateRectRgnIndirect(&window);
859 x = preview.spacing.cx - GetScrollPos(hwndPreview, SB_HORZ);
860 y = preview.spacing.cy - GetScrollPos(hwndPreview, SB_VERT);
862 /* draw page outlines */
863 hPen = CreatePen(PS_SOLID|PS_INSIDEFRAME, 2, RGB(0,0,0));
864 oldPen = SelectObject(hdc, hPen);
865 background.left = x - 2;
866 background.right = x + preview.bmScaledSize.cx + 2;
867 background.top = y - 2;
868 background.bottom = y + preview.bmScaledSize.cy + 2;
869 Rectangle(hdc, background.left, background.top,
870 background.right, background.bottom);
871 excl_rgn = CreateRectRgnIndirect(&background);
872 CombineRgn(back_rgn, back_rgn, excl_rgn, RGN_DIFF);
873 if(preview.pages_shown > 1)
875 background.left += preview.bmScaledSize.cx + preview.spacing.cx;
876 background.right += preview.bmScaledSize.cx + preview.spacing.cx;
877 Rectangle(hdc, background.left, background.top,
878 background.right, background.bottom);
879 SetRectRgn(excl_rgn, background.left, background.top,
880 background.right, background.bottom);
881 CombineRgn(back_rgn, back_rgn, excl_rgn, RGN_DIFF);
883 SelectObject(hdc, oldPen);
885 FillRgn(hdc, back_rgn, GetStockObject(GRAY_BRUSH));
886 DeleteObject(excl_rgn);
887 DeleteObject(back_rgn);
889 StretchBlt(hdc, x, y, preview.bmScaledSize.cx, preview.bmScaledSize.cy,
890 preview.hdc, 0, 0, preview.bmSize.cx, preview.bmSize.cy, SRCCOPY);
892 draw_margin_lines(hdc, x, y, preview.zoomratio);
894 if(preview.pages_shown > 1)
896 if (!is_last_preview_page(preview.page)) {
897 x += preview.spacing.cx + preview.bmScaledSize.cx;
898 StretchBlt(hdc, x, y,
899 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
901 preview.bmSize.cx, preview.bmSize.cy, SRCCOPY);
903 draw_margin_lines(hdc, x, y, preview.zoomratio);
905 background.left += 2;
906 background.right -= 2;
908 background.bottom -= 2;
909 FillRect(hdc, &background, GetStockObject(WHITE_BRUSH));
913 preview.window = window;
915 EndPaint(hwndPreview, &ps);
920 static void update_preview_statusbar(HWND hMainWnd)
922 HWND hStatusbar = GetDlgItem(hMainWnd, IDC_STATUSBAR);
923 HINSTANCE hInst = GetModuleHandleW(0);
925 WCHAR wstr[MAX_STRING_LEN];
928 if (preview.pages_shown < 2 || is_last_preview_page(preview.page))
930 static const WCHAR fmt[] = {' ','%','d','\0'};
931 p += LoadStringW(hInst, STRING_PREVIEW_PAGE, wstr, MAX_STRING_LEN);
932 wsprintfW(p, fmt, preview.page);
934 static const WCHAR fmt[] = {' ','%','d','-','%','d','\0'};
935 p += LoadStringW(hInst, STRING_PREVIEW_PAGES, wstr, MAX_STRING_LEN);
936 wsprintfW(p, fmt, preview.page, preview.page + 1);
938 SetWindowTextW(hStatusbar, wstr);
941 /* Update for page changes. */
942 static void update_preview(HWND hMainWnd)
945 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
946 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
947 HBITMAP hBitmapCapture;
949 HDC hdc = GetDC(hwndPreview);
951 fr.hdcTarget = make_dc();
952 fr.rc = fr.rcPage = preview.rcPage;
953 fr.rc.left += margins.left;
954 fr.rc.top += margins.top;
955 fr.rc.bottom -= margins.bottom;
956 fr.rc.right -= margins.right;
959 fr.chrg.cpMax = preview.textlength;
962 paper.right = preview.bmSize.cx;
964 paper.bottom = preview.bmSize.cy;
967 preview.hdc = CreateCompatibleDC(hdc);
968 hBitmapCapture = CreateCompatibleBitmap(hdc, preview.bmSize.cx, preview.bmSize.cy);
969 SelectObject(preview.hdc, hBitmapCapture);
972 fr.hdc = preview.hdc;
973 draw_preview(hEditorWnd, &fr, &paper, preview.page);
975 if(preview.pages_shown > 1)
979 preview.hdc2 = CreateCompatibleDC(hdc);
980 hBitmapCapture = CreateCompatibleBitmap(hdc,
983 SelectObject(preview.hdc2, hBitmapCapture);
986 fr.hdc = preview.hdc2;
987 draw_preview(hEditorWnd, &fr, &fr.rcPage, preview.page + 1);
989 DeleteDC(fr.hdcTarget);
990 ReleaseDC(hwndPreview, hdc);
992 InvalidateRect(hwndPreview, NULL, FALSE);
993 update_preview_buttons(hMainWnd);
994 update_preview_statusbar(hMainWnd);
997 static void toggle_num_pages(HWND hMainWnd)
999 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
1000 WCHAR name[MAX_STRING_LEN];
1001 HINSTANCE hInst = GetModuleHandleW(0);
1004 preview.pages_shown = preview.pages_shown > 1 ? 1 : 2;
1006 nPreviewPages = preview.zoomlevel > 0 ? preview.saved_pages_shown :
1007 preview.pages_shown;
1009 LoadStringW(hInst, nPreviewPages > 1 ? STRING_PREVIEW_ONEPAGE :
1010 STRING_PREVIEW_TWOPAGES,
1011 name, MAX_STRING_LEN);
1013 SetWindowTextW(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), name);
1014 update_preview_sizes(GetDlgItem(hMainWnd, IDC_PREVIEW), TRUE);
1015 update_preview(hMainWnd);
1018 /* Returns the page shown that the point is in (1 or 2) or 0 if the point
1019 * isn't inside either page */
1020 static int preview_page_hittest(POINT pt)
1023 rc.left = preview.spacing.cx;
1024 rc.right = rc.left + preview.bmScaledSize.cx;
1025 rc.top = preview.spacing.cy;
1026 rc.bottom = rc.top + preview.bmScaledSize.cy;
1027 if (PtInRect(&rc, pt))
1030 if (preview.pages_shown <= 1)
1033 rc.left += preview.bmScaledSize.cx + preview.spacing.cx;
1034 rc.right += preview.bmScaledSize.cx + preview.spacing.cx;
1035 if (PtInRect(&rc, pt))
1036 return is_last_preview_page(preview.page) ? 1 : 2;
1041 LRESULT CALLBACK preview_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1047 HWND hMainWnd = GetParent(hWnd);
1048 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
1050 GETTEXTLENGTHEX gt = {GTL_DEFAULT, 1200};
1051 HDC hdc = GetDC(hWnd);
1052 HDC hdcTarget = make_dc();
1054 fr.rc = preview.rcPage = get_print_rect(hdcTarget);
1055 preview.rcPage.bottom += margins.bottom;
1056 preview.rcPage.right += margins.right;
1057 preview.rcPage.top = preview.rcPage.left = 0;
1058 fr.rcPage = preview.rcPage;
1060 preview.bmSize.cx = twips_to_pixels(preview.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
1061 preview.bmSize.cy = twips_to_pixels(preview.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
1063 preview.textlength = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)>, 0);
1065 fr.hdc = CreateCompatibleDC(hdc);
1066 fr.hdcTarget = hdcTarget;
1068 fr.chrg.cpMax = preview.textlength;
1070 DeleteDC(hdcTarget);
1071 ReleaseDC(hWnd, hdc);
1073 update_preview_sizes(hWnd, TRUE);
1074 update_preview(hMainWnd);
1079 return print_preview(hWnd);
1083 update_preview_sizes(hWnd, FALSE);
1084 InvalidateRect(hWnd, NULL, FALSE);
1093 int nBar = (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ;
1096 GetClientRect(hWnd, &rc);
1097 si.cbSize = sizeof(si);
1099 GetScrollInfo(hWnd, nBar, &si);
1101 switch(LOWORD(wParam))
1103 case SB_TOP: /* == SB_LEFT */
1106 case SB_BOTTOM: /* == SB_RIGHT */
1109 case SB_LINEUP: /* == SB_LINELEFT */
1110 si.nPos -= si.nPage / 10;
1112 case SB_LINEDOWN: /* == SB_LINERIGHT */
1113 si.nPos += si.nPage / 10;
1115 case SB_PAGEUP: /* == SB_PAGELEFT */
1116 si.nPos -= si.nPage;
1118 case SB_PAGEDOWN: /* SB_PAGERIGHT */
1119 si.nPos += si.nPage;
1122 si.nPos = si.nTrackPos;
1126 SetScrollInfo(hWnd, nBar, &si, TRUE);
1127 GetScrollInfo(hWnd, nBar, &si);
1128 if (si.nPos != origPos)
1130 int amount = origPos - si.nPos;
1131 if (msg == WM_VSCROLL)
1132 ScrollWindow(hWnd, 0, amount, NULL, NULL);
1134 ScrollWindow(hWnd, amount, 0, NULL, NULL);
1143 int bHittest = FALSE;
1144 DWORD messagePos = GetMessagePos();
1145 pt.x = (short)LOWORD(messagePos);
1146 pt.y = (short)HIWORD(messagePos);
1147 ScreenToClient(hWnd, &pt);
1149 GetClientRect(hWnd, &rc);
1150 if (PtInRect(&rc, pt))
1152 pt.x += GetScrollPos(hWnd, SB_HORZ);
1153 pt.y += GetScrollPos(hWnd, SB_VERT);
1154 bHittest = preview_page_hittest(pt);
1158 SetCursor(LoadCursorW(GetModuleHandleW(0),
1159 MAKEINTRESOURCEW(IDC_ZOOM)));
1161 SetCursor(LoadCursorW(NULL, (WCHAR*)IDC_ARROW));
1166 case WM_LBUTTONDOWN:
1170 pt.x = (short)LOWORD(lParam) + GetScrollPos(hWnd, SB_HORZ);
1171 pt.y = (short)HIWORD(lParam) + GetScrollPos(hWnd, SB_VERT);
1172 if ((page = preview_page_hittest(pt)) > 0)
1174 HWND hMainWnd = GetParent(hWnd);
1176 /* Convert point from client coordinate to unzoomed page
1178 pt.x -= preview.spacing.cx;
1180 pt.x -= preview.bmScaledSize.cx + preview.spacing.cx;
1181 pt.y -= preview.spacing.cy;
1182 pt.x /= preview.zoomratio;
1183 pt.y /= preview.zoomratio;
1185 if (preview.zoomlevel == 0)
1186 preview.saved_pages_shown = preview.pages_shown;
1187 preview.zoomlevel = (preview.zoomlevel + 1) % 3;
1188 preview.zoomratio = 0;
1189 if (preview.zoomlevel == 0 && preview.saved_pages_shown > 1)
1191 toggle_num_pages(hMainWnd);
1192 } else if (preview.pages_shown > 1) {
1193 if (page >= 2) preview.page++;
1194 toggle_num_pages(hMainWnd);
1196 update_preview_sizes(hWnd, TRUE);
1197 InvalidateRect(hWnd, NULL, FALSE);
1198 update_preview_buttons(hMainWnd);
1201 if (preview.zoomlevel > 0) {
1203 /* Convert the coordinate back to client coordinate. */
1204 pt.x *= preview.zoomratio;
1205 pt.y *= preview.zoomratio;
1206 pt.x += preview.spacing.cx;
1207 pt.y += preview.spacing.cy;
1208 /* Scroll to center view at that point on the page */
1209 si.cbSize = sizeof(si);
1210 si.fMask = SIF_PAGE;
1211 GetScrollInfo(hWnd, SB_HORZ, &si);
1212 pt.x -= si.nPage / 2;
1213 SetScrollPos(hWnd, SB_HORZ, pt.x, TRUE);
1214 GetScrollInfo(hWnd, SB_VERT, &si);
1215 pt.y -= si.nPage / 2;
1216 SetScrollPos(hWnd, SB_VERT, pt.y, TRUE);
1222 return DefWindowProcW(hWnd, msg, wParam, lParam);
1228 LRESULT preview_command(HWND hWnd, WPARAM wParam)
1230 switch(LOWORD(wParam))
1233 PostMessageW(hWnd, WM_CLOSE, 0, 0);
1236 case ID_PREVIEW_NEXTPAGE:
1237 case ID_PREVIEW_PREVPAGE:
1239 if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
1244 update_preview(hWnd);
1248 case ID_PREVIEW_NUMPAGES:
1249 toggle_num_pages(hWnd);
1252 case ID_PREVIEW_ZOOMIN:
1253 if (preview.zoomlevel < 2)
1255 if (preview.zoomlevel == 0)
1256 preview.saved_pages_shown = preview.pages_shown;
1257 preview.zoomlevel++;
1258 preview.zoomratio = 0;
1259 if (preview.pages_shown > 1)
1261 /* Forced switch to one page when zooming in. */
1262 toggle_num_pages(hWnd);
1264 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1265 update_preview_sizes(hwndPreview, TRUE);
1266 InvalidateRect(hwndPreview, NULL, FALSE);
1267 update_preview_buttons(hWnd);
1272 case ID_PREVIEW_ZOOMOUT:
1273 if (preview.zoomlevel > 0)
1275 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1276 preview.zoomlevel--;
1277 preview.zoomratio = 0;
1278 if (preview.zoomlevel == 0 && preview.saved_pages_shown > 1) {
1279 toggle_num_pages(hWnd);
1281 update_preview_sizes(hwndPreview, TRUE);
1282 InvalidateRect(hwndPreview, NULL, FALSE);
1283 update_preview_buttons(hWnd);
1289 dialog_print(hWnd, preview.wszFileName);
1290 SendMessageW(hWnd, WM_CLOSE, 0, 0);