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;
46 } previewinfo, *ppreviewinfo;
48 static HGLOBAL devMode;
49 static HGLOBAL devNames;
52 static previewinfo preview;
54 extern const WCHAR wszPreviewWndClass[];
56 static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',0};
57 static const WCHAR var_previewpages[] = {'P','r','e','v','i','e','w','P','a','g','e','s',0};
59 static LPWSTR get_print_file_filter(HWND hMainWnd)
61 static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1];
62 const WCHAR files_prn[] = {'*','.','P','R','N',0};
63 const WCHAR files_all[] = {'*','.','*','\0'};
65 HINSTANCE hInstance = GetModuleHandleW(0);
68 LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
70 lstrcpyW(p, files_prn);
72 LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
74 lstrcpyW(p, files_all);
78 return wszPrintFilter;
81 void registry_set_pagemargins(HKEY hKey)
83 RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT));
86 void registry_read_pagemargins(HKEY hKey)
88 DWORD size = sizeof(RECT);
90 if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins,
91 &size) != ERROR_SUCCESS || size != sizeof(RECT))
94 margins.bottom = 1417;
100 void registry_set_previewpages(HKEY hKey)
102 RegSetValueExW(hKey, var_previewpages, 0, REG_DWORD,
103 (LPBYTE)&preview.pages_shown, sizeof(DWORD));
106 void registry_read_previewpages(HKEY hKey)
108 DWORD size = sizeof(DWORD);
110 RegQueryValueExW(hKey, var_previewpages, 0, NULL,
111 (LPBYTE)&preview.pages_shown, &size) != ERROR_SUCCESS ||
112 size != sizeof(DWORD))
114 preview.pages_shown = 1;
116 if (preview.pages_shown < 1) preview.pages_shown = 1;
117 else if (preview.pages_shown > 2) preview.pages_shown = 2;
122 static void AddTextButton(HWND hRebarWnd, UINT string, UINT command, UINT id)
125 HINSTANCE hInstance = GetModuleHandleW(0);
126 WCHAR text[MAX_STRING_LEN];
129 LoadStringW(hInstance, string, text, MAX_STRING_LEN);
130 hButton = CreateWindowW(WC_BUTTONW, text,
131 WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
132 hRebarWnd, ULongToHandle(command), hInstance, NULL);
134 rb.cbSize = REBARBANDINFOW_V6_SIZE;
135 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
136 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
137 rb.hwndChild = hButton;
138 rb.cyChild = rb.cyMinChild = 22;
139 rb.cx = rb.cxMinChild = 90;
143 SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb);
146 static HDC make_dc(void)
148 if(devNames && devMode)
150 LPDEVNAMES dn = GlobalLock(devNames);
151 LPDEVMODEW dm = GlobalLock(devMode);
154 ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
155 (LPWSTR)dn + dn->wDeviceOffset, 0, dm);
167 static LONG twips_to_centmm(int twips)
169 return MulDiv(twips, CENTMM_PER_INCH, TWIPS_PER_INCH);
172 static LONG centmm_to_twips(int mm)
174 return MulDiv(mm, TWIPS_PER_INCH, CENTMM_PER_INCH);
177 static LONG twips_to_pixels(int twips, int dpi)
179 return MulDiv(twips, dpi, TWIPS_PER_INCH);
182 static LONG devunits_to_twips(int units, int dpi)
184 return MulDiv(units, TWIPS_PER_INCH, dpi);
188 static RECT get_print_rect(HDC hdc)
195 int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
196 int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
197 width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX);
198 height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY);
201 width = centmm_to_twips(18500);
202 height = centmm_to_twips(27000);
205 rc.left = margins.left;
206 rc.right = width - margins.right;
207 rc.top = margins.top;
208 rc.bottom = height - margins.bottom;
213 void target_device(HWND hMainWnd, DWORD wordWrap)
215 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
217 if(wordWrap == ID_WORDWRAP_MARGIN)
222 RECT rc = get_print_rect(hdc);
224 width = rc.right - rc.left;
227 HDC hMaindc = GetDC(hMainWnd);
228 hdc = CreateCompatibleDC(hMaindc);
229 ReleaseDC(hMainWnd, hMaindc);
231 result = SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width);
235 /* otherwise EM_SETTARGETDEVICE failed, so fall back on wrapping
236 * to window using the NULL DC. */
239 if (wordWrap != ID_WORDWRAP_NONE) {
240 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 0);
242 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 1);
247 static LPWSTR dialog_print_to_file(HWND hMainWnd)
250 static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0};
251 static const WCHAR defExt[] = {'P','R','N',0};
252 static LPWSTR file_filter;
255 file_filter = get_print_file_filter(hMainWnd);
257 ZeroMemory(&ofn, sizeof(ofn));
259 ofn.lStructSize = sizeof(ofn);
260 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
261 ofn.hwndOwner = hMainWnd;
262 ofn.lpstrFilter = file_filter;
263 ofn.lpstrFile = file;
264 ofn.nMaxFile = MAX_PATH;
265 ofn.lpstrDefExt = defExt;
267 if(GetSaveFileNameW(&ofn))
273 static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
279 for(i = 1; i < page; i++)
281 int bottom = fr->rc.bottom;
282 fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, (LPARAM)fr);
283 fr->rc.bottom = bottom;
287 static HWND get_ruler_wnd(HWND hMainWnd)
289 return GetDlgItem(GetDlgItem(hMainWnd, IDC_REBAR), IDC_RULER);
292 void redraw_ruler(HWND hRulerWnd)
296 GetClientRect(hRulerWnd, &rc);
297 InvalidateRect(hRulerWnd, &rc, TRUE);
300 static void update_ruler(HWND hRulerWnd)
302 SendMessageW(hRulerWnd, WM_USER, 0, 0);
303 redraw_ruler(hRulerWnd);
306 static void add_ruler_units(HDC hdcRuler, RECT* drawRect, BOOL NewMetrics, LONG EditLeftmost)
312 static HBITMAP hBitmap;
313 int i, x, y, RulerTextEnd;
317 WCHAR FontName[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0};
322 DeleteObject(hBitmap);
325 hdc = CreateCompatibleDC(0);
327 CmPixels = twips_to_pixels(centmm_to_twips(1000), GetDeviceCaps(hdc, LOGPIXELSX));
328 QuarterCmPixels = (int)((float)CmPixels / 4.0);
330 hBitmap = CreateCompatibleBitmap(hdc, drawRect->right, drawRect->bottom);
331 SelectObject(hdc, hBitmap);
332 FillRect(hdc, drawRect, GetStockObject(WHITE_BRUSH));
334 hFont = CreateFontW(10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FontName);
336 SelectObject(hdc, hFont);
337 SetBkMode(hdc, TRANSPARENT);
338 SetTextAlign(hdc, TA_CENTER);
339 y = (int)(((float)drawRect->bottom - (float)drawRect->top) / 2.0) + 1;
340 RulerTextEnd = drawRect->right - EditLeftmost + 1;
341 for(i = 1, x = EditLeftmost; x < (drawRect->right - EditLeftmost + 1); i ++)
344 WCHAR format[] = {'%','d',0};
347 x2 += QuarterCmPixels;
348 if(x2 > RulerTextEnd)
351 MoveToEx(hdc, x2, y, NULL);
352 LineTo(hdc, x2, y+2);
354 x2 += QuarterCmPixels;
355 if(x2 > RulerTextEnd)
358 MoveToEx(hdc, x2, y - 3, NULL);
359 LineTo(hdc, x2, y + 3);
361 x2 += QuarterCmPixels;
362 if(x2 > RulerTextEnd)
365 MoveToEx(hdc, x2, y, NULL);
366 LineTo(hdc, x2, y+2);
372 wsprintfW(str, format, i);
373 TextOutW(hdc, x, 5, str, lstrlenW(str));
378 BitBlt(hdcRuler, 0, 0, drawRect->right, drawRect->bottom, hdc, 0, 0, SRCAND);
381 static void paint_ruler(HWND hWnd, LONG EditLeftmost, BOOL NewMetrics)
384 HDC hdc = BeginPaint(hWnd, &ps);
385 HDC hdcPrint = make_dc();
386 RECT printRect = get_print_rect(hdcPrint);
388 HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
390 GetClientRect(hWnd, &drawRect);
391 FillRect(hdc, &drawRect, hBrush);
394 drawRect.bottom -= 3;
395 drawRect.left = EditLeftmost;
396 drawRect.right = twips_to_pixels(printRect.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
397 FillRect(hdc, &drawRect, GetStockObject(WHITE_BRUSH));
401 DrawEdge(hdc, &drawRect, EDGE_SUNKEN, BF_RECT);
403 drawRect.left = drawRect.right - 1;
404 drawRect.right = twips_to_pixels(printRect.right + margins.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
405 DrawEdge(hdc, &drawRect, EDGE_ETCHED, BF_RECT);
409 add_ruler_units(hdc, &drawRect, NewMetrics, EditLeftmost);
411 SelectObject(hdc, GetStockObject(BLACK_BRUSH));
412 DeleteObject(hBrush);
417 LRESULT CALLBACK ruler_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
419 static WNDPROC pPrevRulerProc;
420 static LONG EditLeftmost;
421 static BOOL NewMetrics;
428 EditLeftmost = ((POINTL*)wParam)->x;
429 pPrevRulerProc = (WNDPROC)lParam;
435 paint_ruler(hWnd, EditLeftmost, NewMetrics);
439 return CallWindowProcW(pPrevRulerProc, hWnd, msg, wParam, lParam);
445 static void print(LPPRINTDLGW pd, LPWSTR wszFileName)
449 HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR);
450 int printedPages = 0;
453 fr.hdcTarget = pd->hDC;
455 fr.rc = get_print_rect(fr.hdc);
457 fr.rcPage.right = fr.rc.right + margins.right;
459 fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
461 ZeroMemory(&di, sizeof(di));
462 di.cbSize = sizeof(di);
463 di.lpszDocName = wszFileName;
465 if(pd->Flags & PD_PRINTTOFILE)
467 di.lpszOutput = dialog_print_to_file(pd->hwndOwner);
472 if(pd->Flags & PD_SELECTION)
474 SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);
478 gt.flags = GTL_DEFAULT;
481 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)>, 0);
483 if(pd->Flags & PD_PAGENUMS)
484 char_from_pagenum(hEditorWnd, &fr, pd->nToPage);
487 StartDocW(fr.hdc, &di);
490 int bottom = fr.rc.bottom;
491 if(StartPage(fr.hdc) <= 0)
494 fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
496 if(EndPage(fr.hdc) <= 0)
498 bottom = fr.rc.bottom;
501 if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage)))
504 while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
507 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
510 void dialog_printsetup(HWND hMainWnd)
514 ZeroMemory(&ps, sizeof(ps));
515 ps.lStructSize = sizeof(ps);
516 ps.hwndOwner = hMainWnd;
517 ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
518 ps.rtMargin.left = twips_to_centmm(margins.left);
519 ps.rtMargin.right = twips_to_centmm(margins.right);
520 ps.rtMargin.top = twips_to_centmm(margins.top);
521 ps.rtMargin.bottom = twips_to_centmm(margins.bottom);
522 ps.hDevMode = devMode;
523 ps.hDevNames = devNames;
525 if(PageSetupDlgW(&ps))
527 margins.left = centmm_to_twips(ps.rtMargin.left);
528 margins.right = centmm_to_twips(ps.rtMargin.right);
529 margins.top = centmm_to_twips(ps.rtMargin.top);
530 margins.bottom = centmm_to_twips(ps.rtMargin.bottom);
531 devMode = ps.hDevMode;
532 devNames = ps.hDevNames;
533 update_ruler(get_ruler_wnd(hMainWnd));
537 void get_default_printer_opts(void)
540 ZeroMemory(&pd, sizeof(pd));
542 ZeroMemory(&pd, sizeof(pd));
543 pd.lStructSize = sizeof(pd);
544 pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
545 pd.hDevMode = devMode;
549 devMode = pd.hDevMode;
550 devNames = pd.hDevNames;
553 void print_quick(HWND hMainWnd, LPWSTR wszFileName)
557 ZeroMemory(&pd, sizeof(pd));
558 pd.hwndOwner = hMainWnd;
561 print(&pd, wszFileName);
565 void dialog_print(HWND hMainWnd, LPWSTR wszFileName)
568 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
572 ZeroMemory(&pd, sizeof(pd));
573 pd.lStructSize = sizeof(pd);
574 pd.hwndOwner = hMainWnd;
575 pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
578 pd.hDevMode = devMode;
579 pd.hDevNames = devNames;
581 SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to);
583 pd.Flags |= PD_NOSELECTION;
587 devMode = pd.hDevMode;
588 devNames = pd.hDevNames;
589 print(&pd, wszFileName);
590 update_ruler(get_ruler_wnd(hMainWnd));
594 static void preview_bar_show(HWND hMainWnd, BOOL show)
596 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
603 UINT num_pages_string = preview.pages_shown > 1 ? STRING_PREVIEW_ONEPAGE :
604 STRING_PREVIEW_TWOPAGES;
606 AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1);
607 AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2);
608 AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3);
609 AddTextButton(hReBar, num_pages_string, ID_PREVIEW_NUMPAGES, BANDID_PREVIEW_BTN4);
610 AddTextButton(hReBar, STRING_PREVIEW_ZOOMIN, ID_PREVIEW_ZOOMIN, BANDID_PREVIEW_BTN5);
611 AddTextButton(hReBar, STRING_PREVIEW_ZOOMOUT, ID_PREVIEW_ZOOMOUT, BANDID_PREVIEW_BTN6);
612 AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN7);
614 hStatic = CreateWindowW(WC_STATICW, NULL,
615 WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
616 hReBar, NULL, NULL, NULL);
618 rb.cbSize = REBARBANDINFOW_V6_SIZE;
619 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
620 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
621 rb.hwndChild = hStatic;
622 rb.cyChild = rb.cyMinChild = 22;
623 rb.cx = rb.cxMinChild = 90;
625 rb.wID = BANDID_PREVIEW_BUFFER;
627 SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb);
630 for(i = 0; i <= PREVIEW_BUTTONS; i++)
631 SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
635 static const int min_spacing = 10;
637 static void update_preview_scrollbars(HWND hwndPreview, RECT *window)
640 sbi.cbSize = sizeof(sbi);
641 sbi.fMask = SIF_PAGE|SIF_RANGE;
643 if (preview.zoomlevel == 0)
645 /* Hide scrollbars when zoomed out. */
647 sbi.nPage = window->right;
648 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
649 sbi.nPage = window->bottom;
650 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
652 sbi.nMax = preview.bmScaledSize.cx * preview.pages_shown +
653 min_spacing * (preview.pages_shown + 1);
654 sbi.nPage = window->right;
655 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
656 /* Change in the horizontal scrollbar visibility affects the
657 * client rect, so update the client rect. */
658 GetClientRect(hwndPreview, window);
659 sbi.nMax = preview.bmScaledSize.cy + min_spacing * 2;
660 sbi.nPage = window->bottom;
661 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
665 static void update_preview_sizes(HWND hwndPreview, BOOL zoomLevelUpdated)
669 GetClientRect(hwndPreview, &window);
671 /* The zoom ratio isn't updated for partial zoom because of resizing the window. */
672 if (zoomLevelUpdated || preview.zoomlevel != 1)
674 float ratio, ratioHeight, ratioWidth;
675 if (preview.zoomlevel == 2)
679 ratioHeight = (window.bottom - min_spacing * 2) / (float)preview.bmSize.cy;
681 ratioWidth = (float)(window.right -
682 min_spacing * (preview.pages_shown + 1)) /
683 (preview.pages_shown * preview.bmSize.cx);
685 if(ratioWidth > ratioHeight)
690 if (preview.zoomlevel == 1)
691 ratio += (1.0 - ratio) / 2;
693 preview.zoomratio = ratio;
696 preview.bmScaledSize.cx = preview.bmSize.cx * preview.zoomratio;
697 preview.bmScaledSize.cy = preview.bmSize.cy * preview.zoomratio;
699 preview.spacing.cy = max(min_spacing, (window.bottom - preview.bmScaledSize.cy) / 2);
701 preview.spacing.cx = (window.right -
702 preview.bmScaledSize.cx * preview.pages_shown) /
703 (preview.pages_shown + 1);
704 if (preview.spacing.cx < min_spacing)
705 preview.spacing.cx = min_spacing;
707 update_preview_scrollbars(hwndPreview, &window);
710 static void draw_preview_page(HDC hdc, HDC* hdcSized, FORMATRANGE* lpFr, float ratio, int bmNewWidth, int bmNewHeight, int bmWidth, int bmHeight, BOOL draw_margins)
712 HBITMAP hBitmapScaled = CreateCompatibleBitmap(hdc, bmNewWidth, bmNewHeight);
715 int TopMargin = (int)((float)twips_to_pixels(lpFr->rc.top, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
716 int BottomMargin = (int)((float)twips_to_pixels(lpFr->rc.bottom, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
717 int LeftMargin = (int)((float)twips_to_pixels(lpFr->rc.left, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
718 int RightMargin = (int)((float)twips_to_pixels(lpFr->rc.right, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
721 oldbm = SelectObject(*hdcSized, hBitmapScaled);
724 *hdcSized = CreateCompatibleDC(hdc);
725 SelectObject(*hdcSized, hBitmapScaled);
728 StretchBlt(*hdcSized, 0, 0, bmNewWidth, bmNewHeight, hdc, 0, 0, bmWidth, bmHeight, SRCCOPY);
730 if (!draw_margins) return;
732 /* Draw margin lines */
733 hPen = CreatePen(PS_DOT, 1, RGB(0,0,0));
734 oldPen = SelectObject(*hdcSized, hPen);
736 MoveToEx(*hdcSized, 0, TopMargin, NULL);
737 LineTo(*hdcSized, bmNewWidth, TopMargin);
738 MoveToEx(*hdcSized, 0, BottomMargin, NULL);
739 LineTo(*hdcSized, bmNewWidth, BottomMargin);
741 MoveToEx(*hdcSized, LeftMargin, 0, NULL);
742 LineTo(*hdcSized, LeftMargin, bmNewHeight);
743 MoveToEx(*hdcSized, RightMargin, 0, NULL);
744 LineTo(*hdcSized, RightMargin, bmNewHeight);
746 SelectObject(*hdcSized, oldPen);
750 static BOOL is_last_preview_page(int page)
752 return preview.pageEnds[page - 1] >= preview.textlength;
755 /* Update for zoom ratio changes with same page. */
756 static void update_scaled_preview(HWND hMainWnd)
761 /* This may occur on WM_CREATE before update_preview is called
762 * because a WM_SIZE message is generated from updating the
764 if (!preview.hdc) return;
766 hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
767 fr.hdcTarget = make_dc();
768 fr.rc = fr.rcPage = preview.rcPage;
769 fr.rc.left += margins.left;
770 fr.rc.top += margins.top;
771 fr.rc.bottom -= margins.bottom;
772 fr.rc.right -= margins.right;
774 draw_preview_page(preview.hdc, &preview.hdcSized, &fr, preview.zoomratio,
775 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
776 preview.bmSize.cx, preview.bmSize.cy, TRUE);
778 if(preview.pages_shown > 1)
780 draw_preview_page(preview.hdc2, &preview.hdcSized2, &fr, preview.zoomratio,
781 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
782 preview.bmSize.cx, preview.bmSize.cy,
783 !is_last_preview_page(preview.page));
786 InvalidateRect(hwndPreview, NULL, TRUE);
787 DeleteDC(fr.hdcTarget);
790 void init_preview(HWND hMainWnd, LPWSTR wszFileName)
793 HINSTANCE hInstance = GetModuleHandleW(0);
797 preview.wszFileName = wszFileName;
798 preview.zoomratio = 0;
799 preview.zoomlevel = 0;
800 preview_bar_show(hMainWnd, TRUE);
802 hwndPreview = CreateWindowExW(0, wszPreviewWndClass, NULL,
803 WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL,
804 0, 0, 200, 10, hMainWnd, (HMENU)IDC_PREVIEW, hInstance, NULL);
807 void close_preview(HWND hMainWnd)
809 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
810 preview.window.right = 0;
811 preview.window.bottom = 0;
813 HeapFree(GetProcessHeap(), 0, preview.pageEnds);
814 preview.pageEnds = NULL;
815 preview.pageCapacity = 0;
816 if (preview.zoomlevel > 0)
817 preview.pages_shown = preview.saved_pages_shown;
819 HBITMAP oldbm = GetCurrentObject(preview.hdc, OBJ_BITMAP);
820 DeleteDC(preview.hdc);
825 HBITMAP oldbm = GetCurrentObject(preview.hdc2, OBJ_BITMAP);
826 DeleteDC(preview.hdc2);
830 if(preview.hdcSized) {
831 HBITMAP oldbm = GetCurrentObject(preview.hdcSized, OBJ_BITMAP);
832 DeleteDC(preview.hdcSized);
834 preview.hdcSized = NULL;
836 if(preview.hdcSized2) {
837 HBITMAP oldbm = GetCurrentObject(preview.hdcSized2, OBJ_BITMAP);
838 DeleteDC(preview.hdcSized2);
840 preview.hdcSized2 = NULL;
843 preview_bar_show(hMainWnd, FALSE);
844 DestroyWindow(hwndPreview);
847 BOOL preview_isactive(void)
849 return preview.page != 0;
852 static void draw_preview(HWND hEditorWnd, FORMATRANGE* lpFr, RECT* paper, int page)
856 if (!preview.pageEnds)
858 preview.pageCapacity = 32;
859 preview.pageEnds = HeapAlloc(GetProcessHeap(), 0,
860 sizeof(int) * preview.pageCapacity);
861 if (!preview.pageEnds) return;
862 } else if (page >= preview.pageCapacity) {
864 new_buffer = HeapReAlloc(GetProcessHeap(), 0, preview.pageEnds,
865 sizeof(int) * preview.pageCapacity * 2);
866 if (!new_buffer) return;
867 preview.pageCapacity *= 2;
868 preview.pageEnds = new_buffer;
871 FillRect(lpFr->hdc, paper, GetStockObject(WHITE_BRUSH));
872 if (page > 1 && is_last_preview_page(page - 1)) return;
873 lpFr->chrg.cpMin = page <= 1 ? 0 : preview.pageEnds[page-2];
874 bottom = lpFr->rc.bottom;
875 preview.pageEnds[page-1] = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)lpFr);
877 /* EM_FORMATRANGE sets fr.rc.bottom to indicate the area printed in,
878 * but we want to keep the original for drawing margins */
879 lpFr->rc.bottom = bottom;
880 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
883 static void update_preview_buttons(HWND hMainWnd)
885 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
886 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
887 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE),
888 !is_last_preview_page(preview.page) &&
889 !is_last_preview_page(preview.page + preview.pages_shown - 1));
890 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES),
891 preview.pages_shown > 1 ||
892 (!is_last_preview_page(1) && preview.zoomlevel == 0));
893 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMIN), preview.zoomlevel < 2);
894 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMOUT), preview.zoomlevel > 0);
897 LRESULT print_preview(HWND hwndPreview)
900 RECT window, background;
904 hdc = BeginPaint(hwndPreview, &ps);
905 GetClientRect(hwndPreview, &window);
907 FillRect(hdc, &window, GetStockObject(GRAY_BRUSH));
909 scrollpos.x = GetScrollPos(hwndPreview, SB_HORZ);
910 scrollpos.y = GetScrollPos(hwndPreview, SB_VERT);
912 background.left = preview.spacing.cx - 2 - scrollpos.x;
913 background.right = background.left + preview.bmScaledSize.cx + 4;
914 background.top = preview.spacing.cy - 2 - scrollpos.y;
915 background.bottom = background.top + preview.bmScaledSize.cy + 4;
917 FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
919 if(preview.pages_shown > 1)
921 background.left += preview.bmScaledSize.cx + preview.spacing.cx;
922 background.right += preview.bmScaledSize.cx + preview.spacing.cx;
924 FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
927 BitBlt(hdc, preview.spacing.cx - scrollpos.x, preview.spacing.cy - scrollpos.y,
928 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
929 preview.hdcSized, 0, 0, SRCCOPY);
931 if(preview.pages_shown > 1)
933 BitBlt(hdc, preview.spacing.cx * 2 + preview.bmScaledSize.cx - scrollpos.x,
934 preview.spacing.cy - scrollpos.y, preview.bmScaledSize.cx,
935 preview.bmScaledSize.cy, preview.hdcSized2, 0, 0, SRCCOPY);
938 preview.window = window;
940 EndPaint(hwndPreview, &ps);
945 static void update_preview_statusbar(HWND hMainWnd)
947 HWND hStatusbar = GetDlgItem(hMainWnd, IDC_STATUSBAR);
948 HINSTANCE hInst = GetModuleHandleW(0);
950 WCHAR wstr[MAX_STRING_LEN];
953 if (preview.pages_shown < 2 || is_last_preview_page(preview.page))
955 static const WCHAR fmt[] = {' ','%','d','\0'};
956 p += LoadStringW(hInst, STRING_PREVIEW_PAGE, wstr, MAX_STRING_LEN);
957 wsprintfW(p, fmt, preview.page);
959 static const WCHAR fmt[] = {' ','%','d','-','%','d','\0'};
960 p += LoadStringW(hInst, STRING_PREVIEW_PAGES, wstr, MAX_STRING_LEN);
961 wsprintfW(p, fmt, preview.page, preview.page + 1);
963 SetWindowTextW(hStatusbar, wstr);
966 /* Update for page changes. */
967 static void update_preview(HWND hMainWnd)
970 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
971 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
972 HBITMAP hBitmapCapture;
974 HDC hdc = GetDC(hwndPreview);
976 fr.hdcTarget = make_dc();
977 fr.rc = fr.rcPage = preview.rcPage;
978 fr.rc.left += margins.left;
979 fr.rc.top += margins.top;
980 fr.rc.bottom -= margins.bottom;
981 fr.rc.right -= margins.right;
984 fr.chrg.cpMax = preview.textlength;
987 paper.right = preview.bmSize.cx;
989 paper.bottom = preview.bmSize.cy;
992 preview.hdc = CreateCompatibleDC(hdc);
993 hBitmapCapture = CreateCompatibleBitmap(hdc, preview.bmSize.cx, preview.bmSize.cy);
994 SelectObject(preview.hdc, hBitmapCapture);
997 fr.hdc = preview.hdc;
998 draw_preview(hEditorWnd, &fr, &paper, preview.page);
1000 if(preview.pages_shown > 1)
1004 preview.hdc2 = CreateCompatibleDC(hdc);
1005 hBitmapCapture = CreateCompatibleBitmap(hdc,
1008 SelectObject(preview.hdc2, hBitmapCapture);
1011 fr.hdc = preview.hdc2;
1012 draw_preview(hEditorWnd, &fr, &fr.rcPage, preview.page + 1);
1014 DeleteDC(fr.hdcTarget);
1015 ReleaseDC(hwndPreview, hdc);
1017 update_scaled_preview(hMainWnd);
1018 update_preview_buttons(hMainWnd);
1019 update_preview_statusbar(hMainWnd);
1022 static void toggle_num_pages(HWND hMainWnd)
1024 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
1025 WCHAR name[MAX_STRING_LEN];
1026 HINSTANCE hInst = GetModuleHandleW(0);
1029 preview.pages_shown = preview.pages_shown > 1 ? 1 : 2;
1031 nPreviewPages = preview.zoomlevel > 0 ? preview.saved_pages_shown :
1032 preview.pages_shown;
1034 LoadStringW(hInst, nPreviewPages > 1 ? STRING_PREVIEW_ONEPAGE :
1035 STRING_PREVIEW_TWOPAGES,
1036 name, MAX_STRING_LEN);
1038 SetWindowTextW(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), name);
1039 update_preview_sizes(GetDlgItem(hMainWnd, IDC_PREVIEW), TRUE);
1040 update_preview(hMainWnd);
1043 /* Returns the page shown that the point is in (1 or 2) or 0 if the point
1044 * isn't inside either page */
1045 int preview_page_hittest(POINT pt)
1048 rc.left = preview.spacing.cx;
1049 rc.right = rc.left + preview.bmScaledSize.cx;
1050 rc.top = preview.spacing.cy;
1051 rc.bottom = rc.top + preview.bmScaledSize.cy;
1052 if (PtInRect(&rc, pt))
1055 if (preview.pages_shown <= 1)
1058 rc.left += preview.bmScaledSize.cx + preview.spacing.cx;
1059 rc.right += preview.bmScaledSize.cx + preview.spacing.cx;
1060 if (PtInRect(&rc, pt))
1061 return is_last_preview_page(preview.page) ? 1 : 2;
1066 LRESULT CALLBACK preview_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1072 HWND hMainWnd = GetParent(hWnd);
1073 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
1075 GETTEXTLENGTHEX gt = {GTL_DEFAULT, 1200};
1076 HDC hdc = GetDC(hWnd);
1077 HDC hdcTarget = make_dc();
1079 fr.rc = preview.rcPage = get_print_rect(hdcTarget);
1080 preview.rcPage.bottom += margins.bottom;
1081 preview.rcPage.right += margins.right;
1082 preview.rcPage.top = preview.rcPage.left = 0;
1083 fr.rcPage = preview.rcPage;
1085 preview.bmSize.cx = twips_to_pixels(preview.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
1086 preview.bmSize.cy = twips_to_pixels(preview.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
1088 preview.textlength = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)>, 0);
1090 fr.hdc = CreateCompatibleDC(hdc);
1091 fr.hdcTarget = hdcTarget;
1093 fr.chrg.cpMax = preview.textlength;
1095 DeleteDC(hdcTarget);
1096 ReleaseDC(hWnd, hdc);
1098 update_preview_sizes(hWnd, TRUE);
1099 update_preview(hMainWnd);
1104 return print_preview(hWnd);
1108 update_preview_sizes(hWnd, FALSE);
1109 update_scaled_preview(hWnd);
1118 int nBar = (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ;
1121 GetClientRect(hWnd, &rc);
1122 si.cbSize = sizeof(si);
1124 GetScrollInfo(hWnd, nBar, &si);
1126 switch(LOWORD(wParam))
1128 case SB_TOP: /* == SB_LEFT */
1131 case SB_BOTTOM: /* == SB_RIGHT */
1134 case SB_LINEUP: /* == SB_LINELEFT */
1135 si.nPos -= si.nPage / 10;
1137 case SB_LINEDOWN: /* == SB_LINERIGHT */
1138 si.nPos += si.nPage / 10;
1140 case SB_PAGEUP: /* == SB_PAGELEFT */
1141 si.nPos -= si.nPage;
1143 case SB_PAGEDOWN: /* SB_PAGERIGHT */
1144 si.nPos += si.nPage;
1147 si.nPos = si.nTrackPos;
1151 SetScrollInfo(hWnd, nBar, &si, TRUE);
1152 GetScrollInfo(hWnd, nBar, &si);
1153 if (si.nPos != origPos)
1155 int amount = origPos - si.nPos;
1156 if (msg == WM_VSCROLL)
1157 ScrollWindow(hWnd, 0, amount, NULL, NULL);
1159 ScrollWindow(hWnd, amount, 0, NULL, NULL);
1168 int bHittest = FALSE;
1169 DWORD messagePos = GetMessagePos();
1170 pt.x = (short)LOWORD(messagePos);
1171 pt.y = (short)HIWORD(messagePos);
1172 ScreenToClient(hWnd, &pt);
1174 GetClientRect(hWnd, &rc);
1175 if (PtInRect(&rc, pt))
1177 pt.x += GetScrollPos(hWnd, SB_HORZ);
1178 pt.y += GetScrollPos(hWnd, SB_VERT);
1179 bHittest = preview_page_hittest(pt);
1183 SetCursor(LoadCursorW(GetModuleHandleW(0),
1184 MAKEINTRESOURCEW(IDC_ZOOM)));
1186 SetCursor(LoadCursorW(NULL, (WCHAR*)IDC_ARROW));
1191 case WM_LBUTTONDOWN:
1195 pt.x = (short)LOWORD(lParam) + GetScrollPos(hWnd, SB_HORZ);
1196 pt.y = (short)HIWORD(lParam) + GetScrollPos(hWnd, SB_VERT);
1197 if ((page = preview_page_hittest(pt)) > 0)
1199 HWND hMainWnd = GetParent(hWnd);
1201 /* Convert point from client coordinate to unzoomed page
1203 pt.x -= preview.spacing.cx;
1205 pt.x -= preview.bmScaledSize.cx + preview.spacing.cx;
1206 pt.y -= preview.spacing.cy;
1207 pt.x /= preview.zoomratio;
1208 pt.y /= preview.zoomratio;
1210 if (preview.zoomlevel == 0)
1211 preview.saved_pages_shown = preview.pages_shown;
1212 preview.zoomlevel = (preview.zoomlevel + 1) % 3;
1213 preview.zoomratio = 0;
1214 if (preview.zoomlevel == 0 && preview.saved_pages_shown > 1)
1216 toggle_num_pages(hMainWnd);
1217 } else if (preview.pages_shown > 1) {
1218 if (page >= 2) preview.page++;
1219 toggle_num_pages(hMainWnd);
1221 update_preview_sizes(hWnd, TRUE);
1222 update_scaled_preview(hMainWnd);
1223 update_preview_buttons(hMainWnd);
1226 if (preview.zoomlevel > 0) {
1228 /* Convert the coordinate back to client coordinate. */
1229 pt.x *= preview.zoomratio;
1230 pt.y *= preview.zoomratio;
1231 pt.x += preview.spacing.cx;
1232 pt.y += preview.spacing.cy;
1233 /* Scroll to center view at that point on the page */
1234 si.cbSize = sizeof(si);
1235 si.fMask = SIF_PAGE;
1236 GetScrollInfo(hWnd, SB_HORZ, &si);
1237 pt.x -= si.nPage / 2;
1238 SetScrollPos(hWnd, SB_HORZ, pt.x, TRUE);
1239 GetScrollInfo(hWnd, SB_VERT, &si);
1240 pt.y -= si.nPage / 2;
1241 SetScrollPos(hWnd, SB_VERT, pt.y, TRUE);
1247 return DefWindowProcW(hWnd, msg, wParam, lParam);
1253 LRESULT preview_command(HWND hWnd, WPARAM wParam)
1255 switch(LOWORD(wParam))
1258 PostMessageW(hWnd, WM_CLOSE, 0, 0);
1261 case ID_PREVIEW_NEXTPAGE:
1262 case ID_PREVIEW_PREVPAGE:
1264 if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
1269 update_preview(hWnd);
1273 case ID_PREVIEW_NUMPAGES:
1274 toggle_num_pages(hWnd);
1277 case ID_PREVIEW_ZOOMIN:
1278 if (preview.zoomlevel < 2)
1280 if (preview.zoomlevel == 0)
1281 preview.saved_pages_shown = preview.pages_shown;
1282 preview.zoomlevel++;
1283 preview.zoomratio = 0;
1284 if (preview.pages_shown > 1)
1286 /* Forced switch to one page when zooming in. */
1287 toggle_num_pages(hWnd);
1289 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1290 update_preview_sizes(hwndPreview, TRUE);
1291 update_scaled_preview(hWnd);
1292 update_preview_buttons(hWnd);
1297 case ID_PREVIEW_ZOOMOUT:
1298 if (preview.zoomlevel > 0)
1300 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1301 preview.zoomlevel--;
1302 preview.zoomratio = 0;
1303 if (preview.zoomlevel == 0 && preview.saved_pages_shown > 1) {
1304 toggle_num_pages(hWnd);
1306 update_preview_sizes(hwndPreview, TRUE);
1307 update_scaled_preview(hWnd);
1308 update_preview_buttons(hWnd);
1314 dialog_print(hWnd, preview.wszFileName);
1315 SendMessageW(hWnd, WM_CLOSE, 0, 0);