d3d8: Display FIXME only once in ValidatePixelShader and ValidateVertexShader.
[wine/multimedia.git] / programs / wordpad / print.c
blobf77ab12aa25eae61ad8ebc1e0270faffff6ca16a
1 /*
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
21 #include <windows.h>
22 #include <richedit.h>
23 #include <commctrl.h>
25 #include "wordpad.h"
27 typedef struct _previewinfo
29 int page;
30 int pages_shown;
31 int *pageEnds, pageCapacity;
32 int textlength;
33 HDC hdc;
34 HDC hdc2;
35 HDC hdcSized;
36 HDC hdcSized2;
37 RECT window;
38 RECT rcPage;
39 SIZE bmSize;
40 SIZE bmScaledSize;
41 SIZE spacing;
42 float zoomratio;
43 int zoomlevel;
44 LPWSTR wszFileName;
45 } previewinfo, *ppreviewinfo;
47 static HGLOBAL devMode;
48 static HGLOBAL devNames;
50 static RECT margins;
51 static previewinfo preview;
53 extern const WCHAR wszPreviewWndClass[];
55 static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',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'};
62 LPWSTR p;
63 HINSTANCE hInstance = GetModuleHandleW(0);
65 p = wszPrintFilter;
66 LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
67 p += lstrlenW(p) + 1;
68 lstrcpyW(p, files_prn);
69 p += lstrlenW(p) + 1;
70 LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
71 p += lstrlenW(p) + 1;
72 lstrcpyW(p, files_all);
73 p += lstrlenW(p) + 1;
74 *p = 0;
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))
91 margins.top = 1417;
92 margins.bottom = 1417;
93 margins.left = 1757;
94 margins.right = 1757;
98 static void AddTextButton(HWND hRebarWnd, UINT string, UINT command, UINT id)
100 REBARBANDINFOW rb;
101 HINSTANCE hInstance = GetModuleHandleW(0);
102 WCHAR text[MAX_STRING_LEN];
103 HWND hButton;
105 LoadStringW(hInstance, string, text, MAX_STRING_LEN);
106 hButton = CreateWindowW(WC_BUTTONW, text,
107 WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
108 hRebarWnd, ULongToHandle(command), hInstance, NULL);
110 rb.cbSize = REBARBANDINFOW_V6_SIZE;
111 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
112 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
113 rb.hwndChild = hButton;
114 rb.cyChild = rb.cyMinChild = 22;
115 rb.cx = rb.cxMinChild = 90;
116 rb.cxIdeal = 100;
117 rb.wID = id;
119 SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb);
122 static HDC make_dc(void)
124 if(devNames && devMode)
126 LPDEVNAMES dn = GlobalLock(devNames);
127 LPDEVMODEW dm = GlobalLock(devMode);
128 HDC ret;
130 ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
131 (LPWSTR)dn + dn->wDeviceOffset, 0, dm);
133 GlobalUnlock(dn);
134 GlobalUnlock(dm);
136 return ret;
137 } else
139 return 0;
143 static LONG twips_to_centmm(int twips)
145 return MulDiv(twips, CENTMM_PER_INCH, TWIPS_PER_INCH);
148 static LONG centmm_to_twips(int mm)
150 return MulDiv(mm, TWIPS_PER_INCH, CENTMM_PER_INCH);
153 static LONG twips_to_pixels(int twips, int dpi)
155 return MulDiv(twips, dpi, TWIPS_PER_INCH);
158 static LONG devunits_to_twips(int units, int dpi)
160 return MulDiv(units, TWIPS_PER_INCH, dpi);
164 static RECT get_print_rect(HDC hdc)
166 RECT rc;
167 int width, height;
169 if(hdc)
171 int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
172 int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
173 width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX);
174 height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY);
175 } else
177 width = centmm_to_twips(18500);
178 height = centmm_to_twips(27000);
181 rc.left = margins.left;
182 rc.right = width - margins.right;
183 rc.top = margins.top;
184 rc.bottom = height - margins.bottom;
186 return rc;
189 void target_device(HWND hMainWnd, DWORD wordWrap)
191 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
193 if(wordWrap == ID_WORDWRAP_MARGIN)
195 int width = 0;
196 LRESULT result;
197 HDC hdc = make_dc();
198 RECT rc = get_print_rect(hdc);
200 width = rc.right - rc.left;
201 if(!hdc)
203 HDC hMaindc = GetDC(hMainWnd);
204 hdc = CreateCompatibleDC(hMaindc);
205 ReleaseDC(hMainWnd, hMaindc);
207 result = SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width);
208 DeleteDC(hdc);
209 if (result)
210 return;
211 /* otherwise EM_SETTARGETDEVICE failed, so fall back on wrapping
212 * to window using the NULL DC. */
215 if (wordWrap != ID_WORDWRAP_NONE) {
216 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 0);
217 } else {
218 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 1);
223 static LPWSTR dialog_print_to_file(HWND hMainWnd)
225 OPENFILENAMEW ofn;
226 static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0};
227 static const WCHAR defExt[] = {'P','R','N',0};
228 static LPWSTR file_filter;
230 if(!file_filter)
231 file_filter = get_print_file_filter(hMainWnd);
233 ZeroMemory(&ofn, sizeof(ofn));
235 ofn.lStructSize = sizeof(ofn);
236 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
237 ofn.hwndOwner = hMainWnd;
238 ofn.lpstrFilter = file_filter;
239 ofn.lpstrFile = file;
240 ofn.nMaxFile = MAX_PATH;
241 ofn.lpstrDefExt = defExt;
243 if(GetSaveFileNameW(&ofn))
244 return file;
245 else
246 return FALSE;
249 static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
251 int i;
253 fr->chrg.cpMin = 0;
255 for(i = 1; i < page; i++)
257 int bottom = fr->rc.bottom;
258 fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, (LPARAM)fr);
259 fr->rc.bottom = bottom;
263 static HWND get_ruler_wnd(HWND hMainWnd)
265 return GetDlgItem(GetDlgItem(hMainWnd, IDC_REBAR), IDC_RULER);
268 void redraw_ruler(HWND hRulerWnd)
270 RECT rc;
272 GetClientRect(hRulerWnd, &rc);
273 InvalidateRect(hRulerWnd, &rc, TRUE);
276 static void update_ruler(HWND hRulerWnd)
278 SendMessageW(hRulerWnd, WM_USER, 0, 0);
279 redraw_ruler(hRulerWnd);
282 static void add_ruler_units(HDC hdcRuler, RECT* drawRect, BOOL NewMetrics, LONG EditLeftmost)
284 static HDC hdc;
286 if(NewMetrics)
288 static HBITMAP hBitmap;
289 int i, x, y, RulerTextEnd;
290 int CmPixels;
291 int QuarterCmPixels;
292 HFONT hFont;
293 WCHAR FontName[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0};
295 if(hdc)
297 DeleteDC(hdc);
298 DeleteObject(hBitmap);
301 hdc = CreateCompatibleDC(0);
303 CmPixels = twips_to_pixels(centmm_to_twips(1000), GetDeviceCaps(hdc, LOGPIXELSX));
304 QuarterCmPixels = (int)((float)CmPixels / 4.0);
306 hBitmap = CreateCompatibleBitmap(hdc, drawRect->right, drawRect->bottom);
307 SelectObject(hdc, hBitmap);
308 FillRect(hdc, drawRect, GetStockObject(WHITE_BRUSH));
310 hFont = CreateFontW(10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FontName);
312 SelectObject(hdc, hFont);
313 SetBkMode(hdc, TRANSPARENT);
314 SetTextAlign(hdc, TA_CENTER);
315 y = (int)(((float)drawRect->bottom - (float)drawRect->top) / 2.0) + 1;
316 RulerTextEnd = drawRect->right - EditLeftmost + 1;
317 for(i = 1, x = EditLeftmost; x < (drawRect->right - EditLeftmost + 1); i ++)
319 WCHAR str[3];
320 WCHAR format[] = {'%','d',0};
321 int x2 = x;
323 x2 += QuarterCmPixels;
324 if(x2 > RulerTextEnd)
325 break;
327 MoveToEx(hdc, x2, y, NULL);
328 LineTo(hdc, x2, y+2);
330 x2 += QuarterCmPixels;
331 if(x2 > RulerTextEnd)
332 break;
334 MoveToEx(hdc, x2, y - 3, NULL);
335 LineTo(hdc, x2, y + 3);
337 x2 += QuarterCmPixels;
338 if(x2 > RulerTextEnd)
339 break;
341 MoveToEx(hdc, x2, y, NULL);
342 LineTo(hdc, x2, y+2);
344 x += CmPixels;
345 if(x > RulerTextEnd)
346 break;
348 wsprintfW(str, format, i);
349 TextOutW(hdc, x, 5, str, lstrlenW(str));
351 DeleteObject(hFont);
354 BitBlt(hdcRuler, 0, 0, drawRect->right, drawRect->bottom, hdc, 0, 0, SRCAND);
357 static void paint_ruler(HWND hWnd, LONG EditLeftmost, BOOL NewMetrics)
359 PAINTSTRUCT ps;
360 HDC hdc = BeginPaint(hWnd, &ps);
361 HDC hdcPrint = make_dc();
362 RECT printRect = get_print_rect(hdcPrint);
363 RECT drawRect;
364 HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
366 GetClientRect(hWnd, &drawRect);
367 FillRect(hdc, &drawRect, hBrush);
369 drawRect.top += 3;
370 drawRect.bottom -= 3;
371 drawRect.left = EditLeftmost;
372 drawRect.right = twips_to_pixels(printRect.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
373 FillRect(hdc, &drawRect, GetStockObject(WHITE_BRUSH));
375 drawRect.top--;
376 drawRect.bottom++;
377 DrawEdge(hdc, &drawRect, EDGE_SUNKEN, BF_RECT);
379 drawRect.left = drawRect.right - 1;
380 drawRect.right = twips_to_pixels(printRect.right + margins.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
381 DrawEdge(hdc, &drawRect, EDGE_ETCHED, BF_RECT);
383 drawRect.left = 0;
384 drawRect.top = 0;
385 add_ruler_units(hdc, &drawRect, NewMetrics, EditLeftmost);
387 SelectObject(hdc, GetStockObject(BLACK_BRUSH));
388 DeleteObject(hBrush);
389 DeleteDC(hdcPrint);
390 EndPaint(hWnd, &ps);
393 LRESULT CALLBACK ruler_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
395 static WNDPROC pPrevRulerProc;
396 static LONG EditLeftmost;
397 static BOOL NewMetrics;
399 switch(msg)
401 case WM_USER:
402 if(wParam)
404 EditLeftmost = ((POINTL*)wParam)->x;
405 pPrevRulerProc = (WNDPROC)lParam;
407 NewMetrics = TRUE;
408 break;
410 case WM_PAINT:
411 paint_ruler(hWnd, EditLeftmost, NewMetrics);
412 break;
414 default:
415 return CallWindowProcW(pPrevRulerProc, hWnd, msg, wParam, lParam);
418 return 0;
421 static void print(LPPRINTDLGW pd, LPWSTR wszFileName)
423 FORMATRANGE fr;
424 DOCINFOW di;
425 HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR);
426 int printedPages = 0;
428 fr.hdc = pd->hDC;
429 fr.hdcTarget = pd->hDC;
431 fr.rc = get_print_rect(fr.hdc);
432 fr.rcPage.left = 0;
433 fr.rcPage.right = fr.rc.right + margins.right;
434 fr.rcPage.top = 0;
435 fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
437 ZeroMemory(&di, sizeof(di));
438 di.cbSize = sizeof(di);
439 di.lpszDocName = wszFileName;
441 if(pd->Flags & PD_PRINTTOFILE)
443 di.lpszOutput = dialog_print_to_file(pd->hwndOwner);
444 if(!di.lpszOutput)
445 return;
448 if(pd->Flags & PD_SELECTION)
450 SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);
451 } else
453 GETTEXTLENGTHEX gt;
454 gt.flags = GTL_DEFAULT;
455 gt.codepage = 1200;
456 fr.chrg.cpMin = 0;
457 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
459 if(pd->Flags & PD_PAGENUMS)
460 char_from_pagenum(hEditorWnd, &fr, pd->nToPage);
463 StartDocW(fr.hdc, &di);
466 int bottom = fr.rc.bottom;
467 if(StartPage(fr.hdc) <= 0)
468 break;
470 fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
472 if(EndPage(fr.hdc) <= 0)
473 break;
474 bottom = fr.rc.bottom;
476 printedPages++;
477 if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage)))
478 break;
480 while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
482 EndDoc(fr.hdc);
483 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
486 void dialog_printsetup(HWND hMainWnd)
488 PAGESETUPDLGW ps;
490 ZeroMemory(&ps, sizeof(ps));
491 ps.lStructSize = sizeof(ps);
492 ps.hwndOwner = hMainWnd;
493 ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
494 ps.rtMargin.left = twips_to_centmm(margins.left);
495 ps.rtMargin.right = twips_to_centmm(margins.right);
496 ps.rtMargin.top = twips_to_centmm(margins.top);
497 ps.rtMargin.bottom = twips_to_centmm(margins.bottom);
498 ps.hDevMode = devMode;
499 ps.hDevNames = devNames;
501 if(PageSetupDlgW(&ps))
503 margins.left = centmm_to_twips(ps.rtMargin.left);
504 margins.right = centmm_to_twips(ps.rtMargin.right);
505 margins.top = centmm_to_twips(ps.rtMargin.top);
506 margins.bottom = centmm_to_twips(ps.rtMargin.bottom);
507 devMode = ps.hDevMode;
508 devNames = ps.hDevNames;
509 update_ruler(get_ruler_wnd(hMainWnd));
513 void get_default_printer_opts(void)
515 PRINTDLGW pd;
516 ZeroMemory(&pd, sizeof(pd));
518 ZeroMemory(&pd, sizeof(pd));
519 pd.lStructSize = sizeof(pd);
520 pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
521 pd.hDevMode = devMode;
523 PrintDlgW(&pd);
525 devMode = pd.hDevMode;
526 devNames = pd.hDevNames;
529 void print_quick(HWND hMainWnd, LPWSTR wszFileName)
531 PRINTDLGW pd;
533 ZeroMemory(&pd, sizeof(pd));
534 pd.hwndOwner = hMainWnd;
535 pd.hDC = make_dc();
537 print(&pd, wszFileName);
538 DeleteDC(pd.hDC);
541 void dialog_print(HWND hMainWnd, LPWSTR wszFileName)
543 PRINTDLGW pd;
544 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
545 int from = 0;
546 int to = 0;
548 ZeroMemory(&pd, sizeof(pd));
549 pd.lStructSize = sizeof(pd);
550 pd.hwndOwner = hMainWnd;
551 pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
552 pd.nMinPage = 1;
553 pd.nMaxPage = -1;
554 pd.hDevMode = devMode;
555 pd.hDevNames = devNames;
557 SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to);
558 if(from == to)
559 pd.Flags |= PD_NOSELECTION;
561 if(PrintDlgW(&pd))
563 devMode = pd.hDevMode;
564 devNames = pd.hDevNames;
565 print(&pd, wszFileName);
566 update_ruler(get_ruler_wnd(hMainWnd));
570 static void preview_bar_show(HWND hMainWnd, BOOL show)
572 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
573 int i;
575 if(show)
577 REBARBANDINFOW rb;
578 HWND hStatic;
580 AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1);
581 AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2);
582 AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3);
583 AddTextButton(hReBar, STRING_PREVIEW_TWOPAGES, ID_PREVIEW_NUMPAGES, BANDID_PREVIEW_BTN4);
584 AddTextButton(hReBar, STRING_PREVIEW_ZOOMIN, ID_PREVIEW_ZOOMIN, BANDID_PREVIEW_BTN5);
585 AddTextButton(hReBar, STRING_PREVIEW_ZOOMOUT, ID_PREVIEW_ZOOMOUT, BANDID_PREVIEW_BTN6);
586 AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN7);
588 hStatic = CreateWindowW(WC_STATICW, NULL,
589 WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
590 hReBar, NULL, NULL, NULL);
592 rb.cbSize = REBARBANDINFOW_V6_SIZE;
593 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
594 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
595 rb.hwndChild = hStatic;
596 rb.cyChild = rb.cyMinChild = 22;
597 rb.cx = rb.cxMinChild = 90;
598 rb.cxIdeal = 100;
599 rb.wID = BANDID_PREVIEW_BUFFER;
601 SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb);
602 } else
604 for(i = 0; i <= PREVIEW_BUTTONS; i++)
605 SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
609 static const int min_spacing = 10;
611 static void update_preview_scrollbars(HWND hwndPreview, RECT *window)
613 SCROLLINFO sbi;
614 sbi.cbSize = sizeof(sbi);
615 sbi.fMask = SIF_PAGE|SIF_RANGE;
616 sbi.nMin = 0;
617 if (preview.zoomlevel == 0)
619 /* Hide scrollbars when zoomed out. */
620 sbi.nMax = 0;
621 sbi.nPage = window->right;
622 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
623 sbi.nPage = window->bottom;
624 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
625 } else {
626 sbi.nMax = preview.bmScaledSize.cx * preview.pages_shown +
627 min_spacing * (preview.pages_shown + 1);
628 sbi.nPage = window->right;
629 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
630 /* Change in the horizontal scrollbar visibility affects the
631 * client rect, so update the client rect. */
632 GetClientRect(hwndPreview, window);
633 sbi.nMax = preview.bmScaledSize.cy + min_spacing * 2;
634 sbi.nPage = window->bottom;
635 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
639 static void update_preview_sizes(HWND hwndPreview, BOOL zoomLevelUpdated)
641 RECT window;
643 GetClientRect(hwndPreview, &window);
645 /* The zoom ratio isn't updated for partial zoom because of resizing the window. */
646 if (zoomLevelUpdated || preview.zoomlevel != 1)
648 float ratio, ratioHeight, ratioWidth;
649 if (preview.zoomlevel == 2)
651 ratio = 1.0;
652 } else {
653 ratioHeight = (window.bottom - min_spacing * 2) / (float)preview.bmSize.cy;
655 ratioWidth = (float)(window.right -
656 min_spacing * (preview.pages_shown + 1)) /
657 (preview.pages_shown * preview.bmSize.cx);
659 if(ratioWidth > ratioHeight)
660 ratio = ratioHeight;
661 else
662 ratio = ratioWidth;
664 if (preview.zoomlevel == 1)
665 ratio += (1.0 - ratio) / 2;
667 preview.zoomratio = ratio;
670 preview.bmScaledSize.cx = preview.bmSize.cx * preview.zoomratio;
671 preview.bmScaledSize.cy = preview.bmSize.cy * preview.zoomratio;
673 preview.spacing.cy = max(min_spacing, (window.bottom - preview.bmScaledSize.cy) / 2);
675 preview.spacing.cx = (window.right -
676 preview.bmScaledSize.cx * preview.pages_shown) /
677 (preview.pages_shown + 1);
678 if (preview.spacing.cx < min_spacing)
679 preview.spacing.cx = min_spacing;
681 update_preview_scrollbars(hwndPreview, &window);
684 static void draw_preview_page(HDC hdc, HDC* hdcSized, FORMATRANGE* lpFr, float ratio, int bmNewWidth, int bmNewHeight, int bmWidth, int bmHeight)
686 HBITMAP hBitmapScaled = CreateCompatibleBitmap(hdc, bmNewWidth, bmNewHeight);
687 HBITMAP oldbm;
688 HPEN hPen, oldPen;
689 int TopMargin = (int)((float)twips_to_pixels(lpFr->rc.top, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
690 int BottomMargin = (int)((float)twips_to_pixels(lpFr->rc.bottom, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
691 int LeftMargin = (int)((float)twips_to_pixels(lpFr->rc.left, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
692 int RightMargin = (int)((float)twips_to_pixels(lpFr->rc.right, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
694 if(*hdcSized) {
695 oldbm = SelectObject(*hdcSized, hBitmapScaled);
696 DeleteObject(oldbm);
697 } else {
698 *hdcSized = CreateCompatibleDC(hdc);
699 SelectObject(*hdcSized, hBitmapScaled);
702 StretchBlt(*hdcSized, 0, 0, bmNewWidth, bmNewHeight, hdc, 0, 0, bmWidth, bmHeight, SRCCOPY);
704 /* Draw margin lines */
705 hPen = CreatePen(PS_DOT, 1, RGB(0,0,0));
706 oldPen = SelectObject(*hdcSized, hPen);
708 MoveToEx(*hdcSized, 0, TopMargin, NULL);
709 LineTo(*hdcSized, bmNewWidth, TopMargin);
710 MoveToEx(*hdcSized, 0, BottomMargin, NULL);
711 LineTo(*hdcSized, bmNewWidth, BottomMargin);
713 MoveToEx(*hdcSized, LeftMargin, 0, NULL);
714 LineTo(*hdcSized, LeftMargin, bmNewHeight);
715 MoveToEx(*hdcSized, RightMargin, 0, NULL);
716 LineTo(*hdcSized, RightMargin, bmNewHeight);
718 SelectObject(*hdcSized, oldPen);
719 DeleteObject(hPen);
722 /* Update for zoom ratio changes with same page. */
723 static void update_scaled_preview(HWND hMainWnd)
725 FORMATRANGE fr;
726 HWND hwndPreview;
728 /* This may occur on WM_CREATE before update_preview is called
729 * because a WM_SIZE message is generated from updating the
730 * scrollbars. */
731 if (!preview.hdc) return;
733 hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
734 fr.hdcTarget = make_dc();
735 fr.rc = fr.rcPage = preview.rcPage;
736 fr.rc.left += margins.left;
737 fr.rc.top += margins.top;
738 fr.rc.bottom -= margins.bottom;
739 fr.rc.right -= margins.right;
741 draw_preview_page(preview.hdc, &preview.hdcSized, &fr, preview.zoomratio,
742 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
743 preview.bmSize.cx, preview.bmSize.cy);
745 if(preview.pages_shown > 1)
747 draw_preview_page(preview.hdc2, &preview.hdcSized2, &fr, preview.zoomratio,
748 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
749 preview.bmSize.cx, preview.bmSize.cy);
752 InvalidateRect(hwndPreview, NULL, TRUE);
753 DeleteDC(fr.hdcTarget);
756 void init_preview(HWND hMainWnd, LPWSTR wszFileName)
758 HWND hwndPreview;
759 HINSTANCE hInstance = GetModuleHandleW(0);
760 preview.page = 1;
761 preview.hdc = 0;
762 preview.hdc2 = 0;
763 preview.wszFileName = wszFileName;
764 preview.zoomratio = 0;
765 preview.zoomlevel = 0;
766 preview_bar_show(hMainWnd, TRUE);
767 if (preview.pages_shown < 1) preview.pages_shown = 1;
769 hwndPreview = CreateWindowExW(0, wszPreviewWndClass, NULL,
770 WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL,
771 0, 0, 200, 10, hMainWnd, (HMENU)IDC_PREVIEW, hInstance, NULL);
774 void close_preview(HWND hMainWnd)
776 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
777 preview.window.right = 0;
778 preview.window.bottom = 0;
779 preview.page = 0;
780 HeapFree(GetProcessHeap(), 0, preview.pageEnds);
781 preview.pageEnds = NULL;
782 preview.pageCapacity = 0;
783 if(preview.hdc) {
784 HBITMAP oldbm = GetCurrentObject(preview.hdc, OBJ_BITMAP);
785 DeleteDC(preview.hdc);
786 DeleteObject(oldbm);
787 preview.hdc = NULL;
789 if(preview.hdc2) {
790 HBITMAP oldbm = GetCurrentObject(preview.hdc2, OBJ_BITMAP);
791 DeleteDC(preview.hdc2);
792 DeleteObject(oldbm);
793 preview.hdc2 = NULL;
795 if(preview.hdcSized) {
796 HBITMAP oldbm = GetCurrentObject(preview.hdcSized, OBJ_BITMAP);
797 DeleteDC(preview.hdcSized);
798 DeleteObject(oldbm);
799 preview.hdcSized = NULL;
801 if(preview.hdcSized2) {
802 HBITMAP oldbm = GetCurrentObject(preview.hdcSized2, OBJ_BITMAP);
803 DeleteDC(preview.hdcSized2);
804 DeleteObject(oldbm);
805 preview.hdcSized2 = NULL;
808 preview_bar_show(hMainWnd, FALSE);
809 DestroyWindow(hwndPreview);
812 BOOL preview_isactive(void)
814 return preview.page != 0;
817 static void draw_preview(HWND hEditorWnd, FORMATRANGE* lpFr, RECT* paper, int page)
819 int bottom;
821 if (!preview.pageEnds)
823 preview.pageCapacity = 32;
824 preview.pageEnds = HeapAlloc(GetProcessHeap(), 0,
825 sizeof(int) * preview.pageCapacity);
826 if (!preview.pageEnds) return;
827 } else if (page >= preview.pageCapacity) {
828 int *new_buffer;
829 new_buffer = HeapReAlloc(GetProcessHeap(), 0, preview.pageEnds,
830 sizeof(int) * preview.pageCapacity * 2);
831 if (!new_buffer) return;
832 preview.pageCapacity *= 2;
833 preview.pageEnds = new_buffer;
836 lpFr->chrg.cpMin = page <= 1 ? 0 : preview.pageEnds[page-2];
837 FillRect(lpFr->hdc, paper, GetStockObject(WHITE_BRUSH));
838 bottom = lpFr->rc.bottom;
839 preview.pageEnds[page-1] = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)lpFr);
841 /* EM_FORMATRANGE sets fr.rc.bottom to indicate the area printed in,
842 * but we want to keep the original for drawing margins */
843 lpFr->rc.bottom = bottom;
844 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
847 static BOOL is_last_preview_page(int page)
849 return preview.pageEnds[page - 1] >= preview.textlength;
852 static void update_preview_buttons(HWND hMainWnd)
854 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
855 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
856 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE),
857 !is_last_preview_page(preview.page + preview.pages_shown - 1));
858 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES),
859 !is_last_preview_page(1) && preview.zoomlevel == 0);
860 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMIN), preview.zoomlevel < 2);
861 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMOUT), preview.zoomlevel > 0);
864 LRESULT print_preview(HWND hwndPreview)
866 HDC hdc;
867 RECT window, background;
868 PAINTSTRUCT ps;
869 POINT scrollpos;
871 hdc = BeginPaint(hwndPreview, &ps);
872 GetClientRect(hwndPreview, &window);
874 FillRect(hdc, &window, GetStockObject(GRAY_BRUSH));
876 scrollpos.x = GetScrollPos(hwndPreview, SB_HORZ);
877 scrollpos.y = GetScrollPos(hwndPreview, SB_VERT);
879 background.left = preview.spacing.cx - 2 - scrollpos.x;
880 background.right = background.left + preview.bmScaledSize.cx + 4;
881 background.top = preview.spacing.cy - 2 - scrollpos.y;
882 background.bottom = background.top + preview.bmScaledSize.cy + 4;
884 FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
886 if(preview.pages_shown > 1)
888 background.left += preview.bmScaledSize.cx + preview.spacing.cx;
889 background.right += preview.bmScaledSize.cx + preview.spacing.cx;
891 FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
894 BitBlt(hdc, preview.spacing.cx - scrollpos.x, preview.spacing.cy - scrollpos.y,
895 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
896 preview.hdcSized, 0, 0, SRCCOPY);
898 if(preview.pages_shown > 1)
900 BitBlt(hdc, preview.spacing.cx * 2 + preview.bmScaledSize.cx - scrollpos.x,
901 preview.spacing.cy - scrollpos.y, preview.bmScaledSize.cx,
902 preview.bmScaledSize.cy, preview.hdcSized2, 0, 0, SRCCOPY);
905 preview.window = window;
907 EndPaint(hwndPreview, &ps);
909 return 0;
912 /* Update for page changes. */
913 static void update_preview(HWND hMainWnd)
915 RECT paper;
916 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
917 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
918 HBITMAP hBitmapCapture;
919 FORMATRANGE fr;
920 HDC hdc = GetDC(hwndPreview);
922 fr.hdcTarget = make_dc();
923 fr.rc = fr.rcPage = preview.rcPage;
924 fr.rc.left += margins.left;
925 fr.rc.top += margins.top;
926 fr.rc.bottom -= margins.bottom;
927 fr.rc.right -= margins.right;
929 fr.chrg.cpMin = 0;
930 fr.chrg.cpMax = preview.textlength;
932 paper.left = 0;
933 paper.right = preview.bmSize.cx;
934 paper.top = 0;
935 paper.bottom = preview.bmSize.cy;
937 if (!preview.hdc) {
938 preview.hdc = CreateCompatibleDC(hdc);
939 hBitmapCapture = CreateCompatibleBitmap(hdc, preview.bmSize.cx, preview.bmSize.cy);
940 SelectObject(preview.hdc, hBitmapCapture);
943 fr.hdc = preview.hdc;
944 draw_preview(hEditorWnd, &fr, &paper, preview.page);
946 if(preview.pages_shown > 1)
948 if (!preview.hdc2)
950 preview.hdc2 = CreateCompatibleDC(hdc);
951 hBitmapCapture = CreateCompatibleBitmap(hdc,
952 preview.bmSize.cx,
953 preview.bmSize.cy);
954 SelectObject(preview.hdc2, hBitmapCapture);
957 fr.hdc = preview.hdc2;
958 draw_preview(hEditorWnd, &fr, &fr.rcPage, preview.page + 1);
960 DeleteDC(fr.hdcTarget);
961 ReleaseDC(hwndPreview, hdc);
963 update_scaled_preview(hMainWnd);
964 update_preview_buttons(hMainWnd);
967 static void toggle_num_pages(HWND hMainWnd)
969 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
970 WCHAR name[MAX_STRING_LEN];
971 HINSTANCE hInst = GetModuleHandleW(0);
973 if(preview.pages_shown > 1)
975 preview.pages_shown = 1;
976 } else {
977 if(is_last_preview_page(preview.page))
978 preview.page--;
979 preview.pages_shown = 2;
982 LoadStringW(hInst, preview.pages_shown > 1 ? STRING_PREVIEW_ONEPAGE :
983 STRING_PREVIEW_TWOPAGES,
984 name, MAX_STRING_LEN);
986 SetWindowTextW(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), name);
987 update_preview_sizes(GetDlgItem(hMainWnd, IDC_PREVIEW), TRUE);
988 update_preview(hMainWnd);
991 LRESULT CALLBACK preview_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
993 switch(msg)
995 case WM_CREATE:
997 HWND hMainWnd = GetParent(hWnd);
998 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
999 FORMATRANGE fr;
1000 GETTEXTLENGTHEX gt = {GTL_DEFAULT, 1200};
1001 HDC hdc = GetDC(hWnd);
1002 HDC hdcTarget = make_dc();
1004 fr.rc = preview.rcPage = get_print_rect(hdcTarget);
1005 preview.rcPage.bottom += margins.bottom;
1006 preview.rcPage.right += margins.right;
1007 preview.rcPage.top = preview.rcPage.left = 0;
1008 fr.rcPage = preview.rcPage;
1010 preview.bmSize.cx = twips_to_pixels(preview.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
1011 preview.bmSize.cy = twips_to_pixels(preview.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
1013 preview.textlength = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
1015 fr.hdc = CreateCompatibleDC(hdc);
1016 fr.hdcTarget = hdcTarget;
1017 fr.chrg.cpMin = 0;
1018 fr.chrg.cpMax = preview.textlength;
1019 DeleteDC(fr.hdc);
1020 DeleteDC(hdcTarget);
1021 ReleaseDC(hWnd, hdc);
1023 update_preview_sizes(hWnd, TRUE);
1024 update_preview(hMainWnd);
1025 break;
1028 case WM_PAINT:
1029 return print_preview(hWnd);
1031 case WM_SIZE:
1033 update_preview_sizes(hWnd, FALSE);
1034 update_scaled_preview(hWnd);
1035 break;
1038 case WM_VSCROLL:
1039 case WM_HSCROLL:
1041 SCROLLINFO si;
1042 RECT rc;
1043 int nBar = (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ;
1044 int origPos;
1046 GetClientRect(hWnd, &rc);
1047 si.cbSize = sizeof(si);
1048 si.fMask = SIF_ALL;
1049 GetScrollInfo(hWnd, nBar, &si);
1050 origPos = si.nPos;
1051 switch(LOWORD(wParam))
1053 case SB_TOP: /* == SB_LEFT */
1054 si.nPos = si.nMin;
1055 break;
1056 case SB_BOTTOM: /* == SB_RIGHT */
1057 si.nPos = si.nMax;
1058 break;
1059 case SB_LINEUP: /* == SB_LINELEFT */
1060 si.nPos -= si.nPage / 10;
1061 break;
1062 case SB_LINEDOWN: /* == SB_LINERIGHT */
1063 si.nPos += si.nPage / 10;
1064 break;
1065 case SB_PAGEUP: /* == SB_PAGELEFT */
1066 si.nPos -= si.nPage;
1067 break;
1068 case SB_PAGEDOWN: /* SB_PAGERIGHT */
1069 si.nPos += si.nPage;
1070 break;
1071 case SB_THUMBTRACK:
1072 si.nPos = si.nTrackPos;
1073 break;
1075 si.fMask = SIF_POS;
1076 SetScrollInfo(hWnd, nBar, &si, TRUE);
1077 GetScrollInfo(hWnd, nBar, &si);
1078 if (si.nPos != origPos)
1080 int amount = origPos - si.nPos;
1081 if (msg == WM_VSCROLL)
1082 ScrollWindow(hWnd, 0, amount, NULL, NULL);
1083 else
1084 ScrollWindow(hWnd, amount, 0, NULL, NULL);
1086 return 0;
1089 default:
1090 return DefWindowProcW(hWnd, msg, wParam, lParam);
1093 return 0;
1096 LRESULT preview_command(HWND hWnd, WPARAM wParam)
1098 switch(LOWORD(wParam))
1100 case ID_FILE_EXIT:
1101 PostMessageW(hWnd, WM_CLOSE, 0, 0);
1102 break;
1104 case ID_PREVIEW_NEXTPAGE:
1105 case ID_PREVIEW_PREVPAGE:
1107 if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
1108 preview.page++;
1109 else
1110 preview.page--;
1112 update_preview(hWnd);
1114 break;
1116 case ID_PREVIEW_NUMPAGES:
1117 toggle_num_pages(hWnd);
1118 break;
1120 case ID_PREVIEW_ZOOMIN:
1121 if (preview.zoomlevel < 2)
1123 preview.zoomlevel++;
1124 preview.zoomratio = 0;
1125 if (preview.pages_shown > 1)
1127 /* Forced switch to one page when zooming in. */
1128 toggle_num_pages(hWnd);
1129 } else {
1130 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1131 update_preview_sizes(hwndPreview, TRUE);
1132 update_scaled_preview(hWnd);
1133 update_preview_buttons(hWnd);
1136 break;
1138 case ID_PREVIEW_ZOOMOUT:
1139 if (preview.zoomlevel > 0)
1141 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1142 preview.zoomlevel--;
1143 preview.zoomratio = 0;
1144 update_preview_sizes(hwndPreview, TRUE);
1145 update_scaled_preview(hWnd);
1146 update_preview_buttons(hWnd);
1148 break;
1150 case ID_PRINT:
1151 dialog_print(hWnd, preview.wszFileName);
1152 SendMessageW(hWnd, WM_CLOSE, 0, 0);
1153 break;
1156 return 0;