gdi32: Respect the rop mode in SetPixel.
[wine.git] / programs / wordpad / print.c
blobd34fd66edc28fd759da13132c20af168beceef2f
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>
24 #include <commdlg.h>
26 #include "wordpad.h"
28 typedef struct _previewinfo
30 int page;
31 int pages_shown;
32 int saved_pages_shown;
33 int *pageEnds, pageCapacity;
34 int textlength;
35 HDC hdc;
36 HDC hdc2;
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};
56 static const WCHAR var_previewpages[] = {'P','r','e','v','i','e','w','P','a','g','e','s',0};
58 static LPWSTR get_print_file_filter(HWND hMainWnd)
60 static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1];
61 const WCHAR files_prn[] = {'*','.','P','R','N',0};
62 const WCHAR files_all[] = {'*','.','*','\0'};
63 LPWSTR p;
64 HINSTANCE hInstance = GetModuleHandleW(0);
66 p = wszPrintFilter;
67 LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
68 p += lstrlenW(p) + 1;
69 lstrcpyW(p, files_prn);
70 p += lstrlenW(p) + 1;
71 LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
72 p += lstrlenW(p) + 1;
73 lstrcpyW(p, files_all);
74 p += lstrlenW(p) + 1;
75 *p = 0;
77 return wszPrintFilter;
80 void registry_set_pagemargins(HKEY hKey)
82 RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT));
85 void registry_read_pagemargins(HKEY hKey)
87 DWORD size = sizeof(RECT);
89 if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins,
90 &size) != ERROR_SUCCESS || size != sizeof(RECT))
91 SetRect(&margins, 1757, 1417, 1757, 1417);
94 void registry_set_previewpages(HKEY hKey)
96 RegSetValueExW(hKey, var_previewpages, 0, REG_DWORD,
97 (LPBYTE)&preview.pages_shown, sizeof(DWORD));
100 void registry_read_previewpages(HKEY hKey)
102 DWORD size = sizeof(DWORD);
103 if(!hKey ||
104 RegQueryValueExW(hKey, var_previewpages, 0, NULL,
105 (LPBYTE)&preview.pages_shown, &size) != ERROR_SUCCESS ||
106 size != sizeof(DWORD))
108 preview.pages_shown = 1;
109 } else {
110 if (preview.pages_shown < 1) preview.pages_shown = 1;
111 else if (preview.pages_shown > 2) preview.pages_shown = 2;
116 static void AddTextButton(HWND hRebarWnd, UINT string, UINT command, UINT id)
118 REBARBANDINFOW rb;
119 HINSTANCE hInstance = GetModuleHandleW(0);
120 WCHAR text[MAX_STRING_LEN];
121 HWND hButton;
123 LoadStringW(hInstance, string, text, MAX_STRING_LEN);
124 hButton = CreateWindowW(WC_BUTTONW, text,
125 WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
126 hRebarWnd, ULongToHandle(command), hInstance, NULL);
128 rb.cbSize = REBARBANDINFOW_V6_SIZE;
129 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
130 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
131 rb.hwndChild = hButton;
132 rb.cyChild = rb.cyMinChild = 22;
133 rb.cx = rb.cxMinChild = 90;
134 rb.cxIdeal = 100;
135 rb.wID = id;
137 SendMessageW(hRebarWnd, RB_INSERTBANDW, -1, (LPARAM)&rb);
140 static HDC make_dc(void)
142 if(devNames && devMode)
144 LPDEVNAMES dn = GlobalLock(devNames);
145 LPDEVMODEW dm = GlobalLock(devMode);
146 HDC ret;
148 ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
149 (LPWSTR)dn + dn->wDeviceOffset, 0, dm);
151 GlobalUnlock(dn);
152 GlobalUnlock(dm);
154 return ret;
155 } else
157 return 0;
161 static LONG twips_to_centmm(int twips)
163 return MulDiv(twips, CENTMM_PER_INCH, TWIPS_PER_INCH);
166 static LONG centmm_to_twips(int mm)
168 return MulDiv(mm, TWIPS_PER_INCH, CENTMM_PER_INCH);
171 static LONG twips_to_pixels(int twips, int dpi)
173 return MulDiv(twips, dpi, TWIPS_PER_INCH);
176 static LONG devunits_to_twips(int units, int dpi)
178 return MulDiv(units, TWIPS_PER_INCH, dpi);
182 static RECT get_print_rect(HDC hdc)
184 RECT rc;
185 int width, height;
187 if(hdc)
189 int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
190 int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
191 width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX);
192 height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY);
193 } else
195 width = centmm_to_twips(18500);
196 height = centmm_to_twips(27000);
199 SetRect(&rc, margins.left, margins.top, width - margins.right, height - margins.bottom);
201 return rc;
204 void target_device(HWND hMainWnd, DWORD wordWrap)
206 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
208 if(wordWrap == ID_WORDWRAP_MARGIN)
210 int width = 0;
211 LRESULT result;
212 HDC hdc = make_dc();
213 RECT rc = get_print_rect(hdc);
215 width = rc.right - rc.left;
216 if(!hdc)
218 HDC hMaindc = GetDC(hMainWnd);
219 hdc = CreateCompatibleDC(hMaindc);
220 ReleaseDC(hMainWnd, hMaindc);
222 result = SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width);
223 DeleteDC(hdc);
224 if (result)
225 return;
226 /* otherwise EM_SETTARGETDEVICE failed, so fall back on wrapping
227 * to window using the NULL DC. */
230 if (wordWrap != ID_WORDWRAP_NONE) {
231 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 0);
232 } else {
233 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 1);
238 static LPWSTR dialog_print_to_file(HWND hMainWnd)
240 OPENFILENAMEW ofn;
241 static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0};
242 static const WCHAR defExt[] = {'P','R','N',0};
243 static LPWSTR file_filter;
245 if(!file_filter)
246 file_filter = get_print_file_filter(hMainWnd);
248 ZeroMemory(&ofn, sizeof(ofn));
250 ofn.lStructSize = sizeof(ofn);
251 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
252 ofn.hwndOwner = hMainWnd;
253 ofn.lpstrFilter = file_filter;
254 ofn.lpstrFile = file;
255 ofn.nMaxFile = MAX_PATH;
256 ofn.lpstrDefExt = defExt;
258 if(GetSaveFileNameW(&ofn))
259 return file;
260 else
261 return FALSE;
264 static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
266 int i;
268 fr->chrg.cpMin = 0;
270 for(i = 1; i < page; i++)
272 int bottom = fr->rc.bottom;
273 fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, (LPARAM)fr);
274 fr->rc.bottom = bottom;
278 static HWND get_ruler_wnd(HWND hMainWnd)
280 return GetDlgItem(GetDlgItem(hMainWnd, IDC_REBAR), IDC_RULER);
283 void redraw_ruler(HWND hRulerWnd)
285 RECT rc;
287 GetClientRect(hRulerWnd, &rc);
288 InvalidateRect(hRulerWnd, &rc, TRUE);
291 static void update_ruler(HWND hRulerWnd)
293 SendMessageW(hRulerWnd, WM_USER, 0, 0);
294 redraw_ruler(hRulerWnd);
297 static void add_ruler_units(HDC hdcRuler, RECT* drawRect, BOOL NewMetrics, LONG EditLeftmost)
299 static HDC hdc;
301 if(NewMetrics)
303 static HBITMAP hBitmap;
304 int i, x, y, RulerTextEnd;
305 int CmPixels;
306 int QuarterCmPixels;
307 HFONT hFont;
308 WCHAR FontName[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0};
310 if(hdc)
312 DeleteDC(hdc);
313 DeleteObject(hBitmap);
316 hdc = CreateCompatibleDC(0);
318 CmPixels = twips_to_pixels(centmm_to_twips(1000), GetDeviceCaps(hdc, LOGPIXELSX));
319 QuarterCmPixels = (int)((float)CmPixels / 4.0);
321 hBitmap = CreateCompatibleBitmap(hdc, drawRect->right, drawRect->bottom);
322 SelectObject(hdc, hBitmap);
323 FillRect(hdc, drawRect, GetStockObject(WHITE_BRUSH));
325 hFont = CreateFontW(10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FontName);
327 SelectObject(hdc, hFont);
328 SetBkMode(hdc, TRANSPARENT);
329 SetTextAlign(hdc, TA_CENTER);
330 y = (int)(((float)drawRect->bottom - (float)drawRect->top) / 2.0) + 1;
331 RulerTextEnd = drawRect->right - EditLeftmost + 1;
332 for(i = 1, x = EditLeftmost; x < (drawRect->right - EditLeftmost + 1); i ++)
334 WCHAR str[3];
335 WCHAR format[] = {'%','d',0};
336 int x2 = x;
338 x2 += QuarterCmPixels;
339 if(x2 > RulerTextEnd)
340 break;
342 MoveToEx(hdc, x2, y, NULL);
343 LineTo(hdc, x2, y+2);
345 x2 += QuarterCmPixels;
346 if(x2 > RulerTextEnd)
347 break;
349 MoveToEx(hdc, x2, y - 3, NULL);
350 LineTo(hdc, x2, y + 3);
352 x2 += QuarterCmPixels;
353 if(x2 > RulerTextEnd)
354 break;
356 MoveToEx(hdc, x2, y, NULL);
357 LineTo(hdc, x2, y+2);
359 x += CmPixels;
360 if(x > RulerTextEnd)
361 break;
363 wsprintfW(str, format, i);
364 TextOutW(hdc, x, 5, str, lstrlenW(str));
366 DeleteObject(hFont);
369 BitBlt(hdcRuler, 0, 0, drawRect->right, drawRect->bottom, hdc, 0, 0, SRCAND);
372 static void paint_ruler(HWND hWnd, LONG EditLeftmost, BOOL NewMetrics)
374 PAINTSTRUCT ps;
375 HDC hdc = BeginPaint(hWnd, &ps);
376 HDC hdcPrint = make_dc();
377 RECT printRect = get_print_rect(hdcPrint);
378 RECT drawRect;
379 HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
381 GetClientRect(hWnd, &drawRect);
382 FillRect(hdc, &drawRect, hBrush);
384 InflateRect(&drawRect, 0, -3);
385 drawRect.left = EditLeftmost;
386 drawRect.right = twips_to_pixels(printRect.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
387 FillRect(hdc, &drawRect, GetStockObject(WHITE_BRUSH));
389 drawRect.top--;
390 drawRect.bottom++;
391 DrawEdge(hdc, &drawRect, EDGE_SUNKEN, BF_RECT);
393 drawRect.left = drawRect.right - 1;
394 drawRect.right = twips_to_pixels(printRect.right + margins.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
395 DrawEdge(hdc, &drawRect, EDGE_ETCHED, BF_RECT);
397 drawRect.left = 0;
398 drawRect.top = 0;
399 add_ruler_units(hdc, &drawRect, NewMetrics, EditLeftmost);
401 SelectObject(hdc, GetStockObject(BLACK_BRUSH));
402 DeleteObject(hBrush);
403 DeleteDC(hdcPrint);
404 EndPaint(hWnd, &ps);
407 LRESULT CALLBACK ruler_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
409 static WNDPROC pPrevRulerProc;
410 static LONG EditLeftmost;
411 static BOOL NewMetrics;
413 switch(msg)
415 case WM_USER:
416 if(wParam)
418 EditLeftmost = ((POINTL*)wParam)->x;
419 pPrevRulerProc = (WNDPROC)lParam;
421 NewMetrics = TRUE;
422 break;
424 case WM_PAINT:
425 paint_ruler(hWnd, EditLeftmost, NewMetrics);
426 break;
428 default:
429 return CallWindowProcW(pPrevRulerProc, hWnd, msg, wParam, lParam);
432 return 0;
435 static void print(LPPRINTDLGW pd, LPWSTR wszFileName)
437 FORMATRANGE fr;
438 DOCINFOW di;
439 HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR);
440 int printedPages = 0;
442 fr.hdc = pd->hDC;
443 fr.hdcTarget = pd->hDC;
445 fr.rc = get_print_rect(fr.hdc);
446 SetRect(&fr.rcPage, 0, 0, fr.rc.right + margins.right, fr.rc.bottom + margins.bottom);
448 ZeroMemory(&di, sizeof(di));
449 di.cbSize = sizeof(di);
450 di.lpszDocName = wszFileName;
452 if(pd->Flags & PD_PRINTTOFILE)
454 di.lpszOutput = dialog_print_to_file(pd->hwndOwner);
455 if(!di.lpszOutput)
456 return;
459 if(pd->Flags & PD_SELECTION)
461 SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);
462 } else
464 GETTEXTLENGTHEX gt;
465 gt.flags = GTL_DEFAULT;
466 gt.codepage = 1200;
467 fr.chrg.cpMin = 0;
468 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
470 if(pd->Flags & PD_PAGENUMS)
471 char_from_pagenum(hEditorWnd, &fr, pd->nToPage);
474 StartDocW(fr.hdc, &di);
477 if(StartPage(fr.hdc) <= 0)
478 break;
480 fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
482 if(EndPage(fr.hdc) <= 0)
483 break;
485 printedPages++;
486 if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage)))
487 break;
489 while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
491 EndDoc(fr.hdc);
492 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
495 void dialog_printsetup(HWND hMainWnd)
497 PAGESETUPDLGW ps;
499 ZeroMemory(&ps, sizeof(ps));
500 ps.lStructSize = sizeof(ps);
501 ps.hwndOwner = hMainWnd;
502 ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
503 SetRect(&ps.rtMargin, twips_to_centmm(margins.left), twips_to_centmm(margins.top),
504 twips_to_centmm(margins.right), twips_to_centmm(margins.bottom));
505 ps.hDevMode = devMode;
506 ps.hDevNames = devNames;
508 if(PageSetupDlgW(&ps))
510 SetRect(&margins, centmm_to_twips(ps.rtMargin.left), centmm_to_twips(ps.rtMargin.top),
511 centmm_to_twips(ps.rtMargin.right), centmm_to_twips(ps.rtMargin.bottom));
512 devMode = ps.hDevMode;
513 devNames = ps.hDevNames;
514 update_ruler(get_ruler_wnd(hMainWnd));
518 void get_default_printer_opts(void)
520 PRINTDLGW pd;
521 ZeroMemory(&pd, sizeof(pd));
523 ZeroMemory(&pd, sizeof(pd));
524 pd.lStructSize = sizeof(pd);
525 pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
526 pd.hDevMode = devMode;
528 PrintDlgW(&pd);
530 devMode = pd.hDevMode;
531 devNames = pd.hDevNames;
534 void print_quick(HWND hMainWnd, LPWSTR wszFileName)
536 PRINTDLGW pd;
538 ZeroMemory(&pd, sizeof(pd));
539 pd.hwndOwner = hMainWnd;
540 pd.hDC = make_dc();
542 print(&pd, wszFileName);
543 DeleteDC(pd.hDC);
546 void dialog_print(HWND hMainWnd, LPWSTR wszFileName)
548 PRINTDLGW pd;
549 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
550 int from = 0;
551 int to = 0;
553 ZeroMemory(&pd, sizeof(pd));
554 pd.lStructSize = sizeof(pd);
555 pd.hwndOwner = hMainWnd;
556 pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
557 pd.nMinPage = 1;
558 pd.nMaxPage = -1;
559 pd.hDevMode = devMode;
560 pd.hDevNames = devNames;
562 SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to);
563 if(from == to)
564 pd.Flags |= PD_NOSELECTION;
566 if(PrintDlgW(&pd))
568 devMode = pd.hDevMode;
569 devNames = pd.hDevNames;
570 print(&pd, wszFileName);
571 update_ruler(get_ruler_wnd(hMainWnd));
575 static void preview_bar_show(HWND hMainWnd, BOOL show)
577 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
578 int i;
580 if(show)
582 REBARBANDINFOW rb;
583 HWND hStatic;
584 UINT num_pages_string = preview.pages_shown > 1 ? STRING_PREVIEW_ONEPAGE :
585 STRING_PREVIEW_TWOPAGES;
587 AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1);
588 AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2);
589 AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3);
590 AddTextButton(hReBar, num_pages_string, ID_PREVIEW_NUMPAGES, BANDID_PREVIEW_BTN4);
591 AddTextButton(hReBar, STRING_PREVIEW_ZOOMIN, ID_PREVIEW_ZOOMIN, BANDID_PREVIEW_BTN5);
592 AddTextButton(hReBar, STRING_PREVIEW_ZOOMOUT, ID_PREVIEW_ZOOMOUT, BANDID_PREVIEW_BTN6);
593 AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN7);
595 hStatic = CreateWindowW(WC_STATICW, NULL,
596 WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
597 hReBar, NULL, NULL, NULL);
599 rb.cbSize = REBARBANDINFOW_V6_SIZE;
600 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
601 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
602 rb.hwndChild = hStatic;
603 rb.cyChild = rb.cyMinChild = 22;
604 rb.cx = rb.cxMinChild = 90;
605 rb.cxIdeal = 100;
606 rb.wID = BANDID_PREVIEW_BUFFER;
608 SendMessageW(hReBar, RB_INSERTBANDW, -1, (LPARAM)&rb);
609 } else
611 for(i = 0; i <= PREVIEW_BUTTONS; i++)
612 SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
616 static const int min_spacing = 10;
618 static void update_preview_scrollbars(HWND hwndPreview, RECT *window)
620 SCROLLINFO sbi;
621 sbi.cbSize = sizeof(sbi);
622 sbi.fMask = SIF_PAGE|SIF_RANGE;
623 sbi.nMin = 0;
624 if (preview.zoomlevel == 0)
626 /* Hide scrollbars when zoomed out. */
627 sbi.nMax = 0;
628 sbi.nPage = window->right;
629 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
630 sbi.nPage = window->bottom;
631 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
632 } else {
633 sbi.nMax = preview.bmScaledSize.cx * preview.pages_shown +
634 min_spacing * (preview.pages_shown + 1);
635 sbi.nPage = window->right;
636 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
637 /* Change in the horizontal scrollbar visibility affects the
638 * client rect, so update the client rect. */
639 GetClientRect(hwndPreview, window);
640 sbi.nMax = preview.bmScaledSize.cy + min_spacing * 2;
641 sbi.nPage = window->bottom;
642 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
646 static void update_preview_sizes(HWND hwndPreview, BOOL zoomLevelUpdated)
648 RECT window;
650 GetClientRect(hwndPreview, &window);
652 /* The zoom ratio isn't updated for partial zoom because of resizing the window. */
653 if (zoomLevelUpdated || preview.zoomlevel != 1)
655 float ratio, ratioHeight, ratioWidth;
656 if (preview.zoomlevel == 2)
658 ratio = 1.0;
659 } else {
660 ratioHeight = (window.bottom - min_spacing * 2) / (float)preview.bmSize.cy;
662 ratioWidth = (float)(window.right -
663 min_spacing * (preview.pages_shown + 1)) /
664 (preview.pages_shown * preview.bmSize.cx);
666 if(ratioWidth > ratioHeight)
667 ratio = ratioHeight;
668 else
669 ratio = ratioWidth;
671 if (preview.zoomlevel == 1)
672 ratio += (1.0 - ratio) / 2;
674 preview.zoomratio = ratio;
677 preview.bmScaledSize.cx = preview.bmSize.cx * preview.zoomratio;
678 preview.bmScaledSize.cy = preview.bmSize.cy * preview.zoomratio;
680 preview.spacing.cy = max(min_spacing, (window.bottom - preview.bmScaledSize.cy) / 2);
682 preview.spacing.cx = (window.right -
683 preview.bmScaledSize.cx * preview.pages_shown) /
684 (preview.pages_shown + 1);
685 if (preview.spacing.cx < min_spacing)
686 preview.spacing.cx = min_spacing;
688 update_preview_scrollbars(hwndPreview, &window);
691 static void draw_margin_lines(HDC hdc, int x, int y, float ratio)
693 HPEN hPen, oldPen;
694 SIZE dpi;
695 RECT page_margin = preview.rcPage;
697 dpi.cx = GetDeviceCaps(hdc, LOGPIXELSX);
698 dpi.cy = GetDeviceCaps(hdc, LOGPIXELSY);
700 SetRect(&page_margin, preview.rcPage.left + margins.left, preview.rcPage.top + margins.top,
701 preview.rcPage.right - margins.right, preview.rcPage.bottom - margins.bottom);
703 page_margin.left = (int)((float)twips_to_pixels(page_margin.left, dpi.cx) * ratio);
704 page_margin.top = (int)((float)twips_to_pixels(page_margin.top, dpi.cy) * ratio);
705 page_margin.bottom = (int)((float)twips_to_pixels(page_margin.bottom, dpi.cy) * ratio);
706 page_margin.right = (int)((float)twips_to_pixels(page_margin.right, dpi.cx) * ratio);
708 OffsetRect(&page_margin, x, y);
710 hPen = CreatePen(PS_DOT, 1, RGB(0,0,0));
711 oldPen = SelectObject(hdc, hPen);
713 MoveToEx(hdc, x, page_margin.top, NULL);
714 LineTo(hdc, x + preview.bmScaledSize.cx, page_margin.top);
715 MoveToEx(hdc, x, page_margin.bottom, NULL);
716 LineTo(hdc, x + preview.bmScaledSize.cx, page_margin.bottom);
718 MoveToEx(hdc, page_margin.left, y, NULL);
719 LineTo(hdc, page_margin.left, y + preview.bmScaledSize.cy);
720 MoveToEx(hdc, page_margin.right, y, NULL);
721 LineTo(hdc, page_margin.right, y + preview.bmScaledSize.cy);
723 SelectObject(hdc, oldPen);
724 DeleteObject(hPen);
727 static BOOL is_last_preview_page(int page)
729 return preview.pageEnds[page - 1] >= preview.textlength;
732 void init_preview(HWND hMainWnd, LPWSTR wszFileName)
734 HINSTANCE hInstance = GetModuleHandleW(0);
735 preview.page = 1;
736 preview.hdc = 0;
737 preview.hdc2 = 0;
738 preview.wszFileName = wszFileName;
739 preview.zoomratio = 0;
740 preview.zoomlevel = 0;
741 preview_bar_show(hMainWnd, TRUE);
743 CreateWindowExW(0, wszPreviewWndClass, NULL,
744 WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL,
745 0, 0, 200, 10, hMainWnd, (HMENU)IDC_PREVIEW, hInstance, NULL);
748 void close_preview(HWND hMainWnd)
750 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
751 preview.window.right = 0;
752 preview.window.bottom = 0;
753 preview.page = 0;
754 HeapFree(GetProcessHeap(), 0, preview.pageEnds);
755 preview.pageEnds = NULL;
756 preview.pageCapacity = 0;
757 if (preview.zoomlevel > 0)
758 preview.pages_shown = preview.saved_pages_shown;
759 if(preview.hdc) {
760 HBITMAP oldbm = GetCurrentObject(preview.hdc, OBJ_BITMAP);
761 DeleteDC(preview.hdc);
762 DeleteObject(oldbm);
763 preview.hdc = NULL;
765 if(preview.hdc2) {
766 HBITMAP oldbm = GetCurrentObject(preview.hdc2, OBJ_BITMAP);
767 DeleteDC(preview.hdc2);
768 DeleteObject(oldbm);
769 preview.hdc2 = NULL;
772 preview_bar_show(hMainWnd, FALSE);
773 DestroyWindow(hwndPreview);
776 BOOL preview_isactive(void)
778 return preview.page != 0;
781 static void draw_preview(HWND hEditorWnd, FORMATRANGE* lpFr, RECT* paper, int page)
783 int bottom;
785 if (!preview.pageEnds)
787 preview.pageCapacity = 32;
788 preview.pageEnds = HeapAlloc(GetProcessHeap(), 0,
789 sizeof(int) * preview.pageCapacity);
790 if (!preview.pageEnds) return;
791 } else if (page >= preview.pageCapacity) {
792 int *new_buffer;
793 new_buffer = HeapReAlloc(GetProcessHeap(), 0, preview.pageEnds,
794 sizeof(int) * preview.pageCapacity * 2);
795 if (!new_buffer) return;
796 preview.pageCapacity *= 2;
797 preview.pageEnds = new_buffer;
800 FillRect(lpFr->hdc, paper, GetStockObject(WHITE_BRUSH));
801 if (page > 1 && is_last_preview_page(page - 1)) return;
802 lpFr->chrg.cpMin = page <= 1 ? 0 : preview.pageEnds[page-2];
803 bottom = lpFr->rc.bottom;
804 preview.pageEnds[page-1] = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)lpFr);
806 /* EM_FORMATRANGE sets fr.rc.bottom to indicate the area printed in,
807 * but we want to keep the original for drawing margins */
808 lpFr->rc.bottom = bottom;
809 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
812 static void update_preview_buttons(HWND hMainWnd)
814 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
815 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
816 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE),
817 !is_last_preview_page(preview.page) &&
818 !is_last_preview_page(preview.page + preview.pages_shown - 1));
819 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES),
820 preview.pages_shown > 1 ||
821 (!is_last_preview_page(1) && preview.zoomlevel == 0));
822 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMIN), preview.zoomlevel < 2);
823 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMOUT), preview.zoomlevel > 0);
826 static LRESULT print_preview(HWND hwndPreview)
828 HPEN hPen, oldPen;
829 HDC hdc;
830 HRGN back_rgn, excl_rgn;
831 RECT window, background;
832 PAINTSTRUCT ps;
833 int x, y;
835 hdc = BeginPaint(hwndPreview, &ps);
836 GetClientRect(hwndPreview, &window);
837 back_rgn = CreateRectRgnIndirect(&window);
839 x = preview.spacing.cx - GetScrollPos(hwndPreview, SB_HORZ);
840 y = preview.spacing.cy - GetScrollPos(hwndPreview, SB_VERT);
842 /* draw page outlines */
843 hPen = CreatePen(PS_SOLID|PS_INSIDEFRAME, 2, RGB(0,0,0));
844 oldPen = SelectObject(hdc, hPen);
845 SetRect(&background, x - 2, y - 2, x + preview.bmScaledSize.cx + 2,
846 y + preview.bmScaledSize.cy + 2);
847 Rectangle(hdc, background.left, background.top,
848 background.right, background.bottom);
849 excl_rgn = CreateRectRgnIndirect(&background);
850 CombineRgn(back_rgn, back_rgn, excl_rgn, RGN_DIFF);
851 if(preview.pages_shown > 1)
853 background.left += preview.bmScaledSize.cx + preview.spacing.cx;
854 background.right += preview.bmScaledSize.cx + preview.spacing.cx;
855 Rectangle(hdc, background.left, background.top,
856 background.right, background.bottom);
857 SetRectRgn(excl_rgn, background.left, background.top,
858 background.right, background.bottom);
859 CombineRgn(back_rgn, back_rgn, excl_rgn, RGN_DIFF);
861 SelectObject(hdc, oldPen);
862 DeleteObject(hPen);
863 FillRgn(hdc, back_rgn, GetStockObject(GRAY_BRUSH));
864 DeleteObject(excl_rgn);
865 DeleteObject(back_rgn);
867 StretchBlt(hdc, x, y, preview.bmScaledSize.cx, preview.bmScaledSize.cy,
868 preview.hdc, 0, 0, preview.bmSize.cx, preview.bmSize.cy, SRCCOPY);
870 draw_margin_lines(hdc, x, y, preview.zoomratio);
872 if(preview.pages_shown > 1)
874 if (!is_last_preview_page(preview.page)) {
875 x += preview.spacing.cx + preview.bmScaledSize.cx;
876 StretchBlt(hdc, x, y,
877 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
878 preview.hdc2, 0, 0,
879 preview.bmSize.cx, preview.bmSize.cy, SRCCOPY);
881 draw_margin_lines(hdc, x, y, preview.zoomratio);
882 } else {
883 InflateRect(&background, -2, -2);
884 FillRect(hdc, &background, GetStockObject(WHITE_BRUSH));
888 preview.window = window;
890 EndPaint(hwndPreview, &ps);
892 return 0;
895 static void update_preview_statusbar(HWND hMainWnd)
897 HWND hStatusbar = GetDlgItem(hMainWnd, IDC_STATUSBAR);
898 HINSTANCE hInst = GetModuleHandleW(0);
899 WCHAR *p;
900 WCHAR wstr[MAX_STRING_LEN];
902 p = wstr;
903 if (preview.pages_shown < 2 || is_last_preview_page(preview.page))
905 static const WCHAR fmt[] = {' ','%','d','\0'};
906 p += LoadStringW(hInst, STRING_PREVIEW_PAGE, wstr, MAX_STRING_LEN);
907 wsprintfW(p, fmt, preview.page);
908 } else {
909 static const WCHAR fmt[] = {' ','%','d','-','%','d','\0'};
910 p += LoadStringW(hInst, STRING_PREVIEW_PAGES, wstr, MAX_STRING_LEN);
911 wsprintfW(p, fmt, preview.page, preview.page + 1);
913 SetWindowTextW(hStatusbar, wstr);
916 /* Update for page changes. */
917 static void update_preview(HWND hMainWnd)
919 RECT paper;
920 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
921 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
922 HBITMAP hBitmapCapture;
923 FORMATRANGE fr;
924 HDC hdc = GetDC(hwndPreview);
926 fr.hdcTarget = make_dc();
927 fr.rc = fr.rcPage = preview.rcPage;
928 fr.rc.left += margins.left;
929 fr.rc.top += margins.top;
930 fr.rc.bottom -= margins.bottom;
931 fr.rc.right -= margins.right;
933 fr.chrg.cpMin = 0;
934 fr.chrg.cpMax = preview.textlength;
936 SetRect(&paper, 0, 0, preview.bmSize.cx, preview.bmSize.cy);
938 if (!preview.hdc) {
939 preview.hdc = CreateCompatibleDC(hdc);
940 hBitmapCapture = CreateCompatibleBitmap(hdc, preview.bmSize.cx, preview.bmSize.cy);
941 SelectObject(preview.hdc, hBitmapCapture);
944 fr.hdc = preview.hdc;
945 draw_preview(hEditorWnd, &fr, &paper, preview.page);
947 if(preview.pages_shown > 1)
949 if (!preview.hdc2)
951 preview.hdc2 = CreateCompatibleDC(hdc);
952 hBitmapCapture = CreateCompatibleBitmap(hdc,
953 preview.bmSize.cx,
954 preview.bmSize.cy);
955 SelectObject(preview.hdc2, hBitmapCapture);
958 fr.hdc = preview.hdc2;
959 draw_preview(hEditorWnd, &fr, &fr.rcPage, preview.page + 1);
961 DeleteDC(fr.hdcTarget);
962 ReleaseDC(hwndPreview, hdc);
964 InvalidateRect(hwndPreview, NULL, FALSE);
965 update_preview_buttons(hMainWnd);
966 update_preview_statusbar(hMainWnd);
969 static void toggle_num_pages(HWND hMainWnd)
971 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
972 WCHAR name[MAX_STRING_LEN];
973 HINSTANCE hInst = GetModuleHandleW(0);
974 int nPreviewPages;
976 preview.pages_shown = preview.pages_shown > 1 ? 1 : 2;
978 nPreviewPages = preview.zoomlevel > 0 ? preview.saved_pages_shown :
979 preview.pages_shown;
981 LoadStringW(hInst, nPreviewPages > 1 ? STRING_PREVIEW_ONEPAGE :
982 STRING_PREVIEW_TWOPAGES,
983 name, MAX_STRING_LEN);
985 SetWindowTextW(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), name);
986 update_preview_sizes(GetDlgItem(hMainWnd, IDC_PREVIEW), TRUE);
987 update_preview(hMainWnd);
990 /* Returns the page shown that the point is in (1 or 2) or 0 if the point
991 * isn't inside either page */
992 static int preview_page_hittest(POINT pt)
994 RECT rc;
995 rc.left = preview.spacing.cx;
996 rc.right = rc.left + preview.bmScaledSize.cx;
997 rc.top = preview.spacing.cy;
998 rc.bottom = rc.top + preview.bmScaledSize.cy;
999 if (PtInRect(&rc, pt))
1000 return 1;
1002 if (preview.pages_shown <= 1)
1003 return 0;
1005 rc.left += preview.bmScaledSize.cx + preview.spacing.cx;
1006 rc.right += preview.bmScaledSize.cx + preview.spacing.cx;
1007 if (PtInRect(&rc, pt))
1008 return is_last_preview_page(preview.page) ? 1 : 2;
1010 return 0;
1013 LRESULT CALLBACK preview_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
1015 switch(msg)
1017 case WM_CREATE:
1019 HWND hMainWnd = GetParent(hWnd);
1020 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
1021 FORMATRANGE fr;
1022 GETTEXTLENGTHEX gt = {GTL_DEFAULT, 1200};
1023 HDC hdc = GetDC(hWnd);
1024 HDC hdcTarget = make_dc();
1026 fr.rc = preview.rcPage = get_print_rect(hdcTarget);
1027 preview.rcPage.bottom += margins.bottom;
1028 preview.rcPage.right += margins.right;
1029 preview.rcPage.top = preview.rcPage.left = 0;
1030 fr.rcPage = preview.rcPage;
1032 preview.bmSize.cx = twips_to_pixels(preview.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
1033 preview.bmSize.cy = twips_to_pixels(preview.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
1035 preview.textlength = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
1037 fr.hdc = CreateCompatibleDC(hdc);
1038 fr.hdcTarget = hdcTarget;
1039 fr.chrg.cpMin = 0;
1040 fr.chrg.cpMax = preview.textlength;
1041 DeleteDC(fr.hdc);
1042 DeleteDC(hdcTarget);
1043 ReleaseDC(hWnd, hdc);
1045 update_preview_sizes(hWnd, TRUE);
1046 update_preview(hMainWnd);
1047 break;
1050 case WM_PAINT:
1051 return print_preview(hWnd);
1053 case WM_SIZE:
1055 update_preview_sizes(hWnd, FALSE);
1056 InvalidateRect(hWnd, NULL, FALSE);
1057 break;
1060 case WM_VSCROLL:
1061 case WM_HSCROLL:
1063 SCROLLINFO si;
1064 RECT rc;
1065 int nBar = (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ;
1066 int origPos;
1068 GetClientRect(hWnd, &rc);
1069 si.cbSize = sizeof(si);
1070 si.fMask = SIF_ALL;
1071 GetScrollInfo(hWnd, nBar, &si);
1072 origPos = si.nPos;
1073 switch(LOWORD(wParam))
1075 case SB_TOP: /* == SB_LEFT */
1076 si.nPos = si.nMin;
1077 break;
1078 case SB_BOTTOM: /* == SB_RIGHT */
1079 si.nPos = si.nMax;
1080 break;
1081 case SB_LINEUP: /* == SB_LINELEFT */
1082 si.nPos -= si.nPage / 10;
1083 break;
1084 case SB_LINEDOWN: /* == SB_LINERIGHT */
1085 si.nPos += si.nPage / 10;
1086 break;
1087 case SB_PAGEUP: /* == SB_PAGELEFT */
1088 si.nPos -= si.nPage;
1089 break;
1090 case SB_PAGEDOWN: /* SB_PAGERIGHT */
1091 si.nPos += si.nPage;
1092 break;
1093 case SB_THUMBTRACK:
1094 si.nPos = si.nTrackPos;
1095 break;
1097 si.fMask = SIF_POS;
1098 SetScrollInfo(hWnd, nBar, &si, TRUE);
1099 GetScrollInfo(hWnd, nBar, &si);
1100 if (si.nPos != origPos)
1102 int amount = origPos - si.nPos;
1103 if (msg == WM_VSCROLL)
1104 ScrollWindow(hWnd, 0, amount, NULL, NULL);
1105 else
1106 ScrollWindow(hWnd, amount, 0, NULL, NULL);
1108 return 0;
1111 case WM_SETCURSOR:
1113 POINT pt;
1114 RECT rc;
1115 int bHittest = 0;
1116 DWORD messagePos = GetMessagePos();
1117 pt.x = (short)LOWORD(messagePos);
1118 pt.y = (short)HIWORD(messagePos);
1119 ScreenToClient(hWnd, &pt);
1121 GetClientRect(hWnd, &rc);
1122 if (PtInRect(&rc, pt))
1124 pt.x += GetScrollPos(hWnd, SB_HORZ);
1125 pt.y += GetScrollPos(hWnd, SB_VERT);
1126 bHittest = preview_page_hittest(pt);
1129 if (bHittest)
1130 SetCursor(LoadCursorW(GetModuleHandleW(0),
1131 MAKEINTRESOURCEW(IDC_ZOOM)));
1132 else
1133 SetCursor(LoadCursorW(NULL, (WCHAR*)IDC_ARROW));
1135 return TRUE;
1138 case WM_LBUTTONDOWN:
1140 int page;
1141 POINT pt;
1142 pt.x = (short)LOWORD(lParam) + GetScrollPos(hWnd, SB_HORZ);
1143 pt.y = (short)HIWORD(lParam) + GetScrollPos(hWnd, SB_VERT);
1144 if ((page = preview_page_hittest(pt)) > 0)
1146 HWND hMainWnd = GetParent(hWnd);
1148 /* Convert point from client coordinate to unzoomed page
1149 * coordinate. */
1150 pt.x -= preview.spacing.cx;
1151 if (page > 1)
1152 pt.x -= preview.bmScaledSize.cx + preview.spacing.cx;
1153 pt.y -= preview.spacing.cy;
1154 pt.x /= preview.zoomratio;
1155 pt.y /= preview.zoomratio;
1157 if (preview.zoomlevel == 0)
1158 preview.saved_pages_shown = preview.pages_shown;
1159 preview.zoomlevel = (preview.zoomlevel + 1) % 3;
1160 preview.zoomratio = 0;
1161 if (preview.zoomlevel == 0 && preview.saved_pages_shown > 1)
1163 toggle_num_pages(hMainWnd);
1164 } else if (preview.pages_shown > 1) {
1165 if (page >= 2) preview.page++;
1166 toggle_num_pages(hMainWnd);
1167 } else {
1168 update_preview_sizes(hWnd, TRUE);
1169 InvalidateRect(hWnd, NULL, FALSE);
1170 update_preview_buttons(hMainWnd);
1173 if (preview.zoomlevel > 0) {
1174 SCROLLINFO si;
1175 /* Convert the coordinate back to client coordinate. */
1176 pt.x *= preview.zoomratio;
1177 pt.y *= preview.zoomratio;
1178 pt.x += preview.spacing.cx;
1179 pt.y += preview.spacing.cy;
1180 /* Scroll to center view at that point on the page */
1181 si.cbSize = sizeof(si);
1182 si.fMask = SIF_PAGE;
1183 GetScrollInfo(hWnd, SB_HORZ, &si);
1184 pt.x -= si.nPage / 2;
1185 SetScrollPos(hWnd, SB_HORZ, pt.x, TRUE);
1186 GetScrollInfo(hWnd, SB_VERT, &si);
1187 pt.y -= si.nPage / 2;
1188 SetScrollPos(hWnd, SB_VERT, pt.y, TRUE);
1193 default:
1194 return DefWindowProcW(hWnd, msg, wParam, lParam);
1197 return 0;
1200 LRESULT preview_command(HWND hWnd, WPARAM wParam)
1202 switch(LOWORD(wParam))
1204 case ID_FILE_EXIT:
1205 PostMessageW(hWnd, WM_CLOSE, 0, 0);
1206 break;
1208 case ID_PREVIEW_NEXTPAGE:
1209 case ID_PREVIEW_PREVPAGE:
1211 if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
1212 preview.page++;
1213 else
1214 preview.page--;
1216 update_preview(hWnd);
1218 break;
1220 case ID_PREVIEW_NUMPAGES:
1221 toggle_num_pages(hWnd);
1222 break;
1224 case ID_PREVIEW_ZOOMIN:
1225 if (preview.zoomlevel < 2)
1227 if (preview.zoomlevel == 0)
1228 preview.saved_pages_shown = preview.pages_shown;
1229 preview.zoomlevel++;
1230 preview.zoomratio = 0;
1231 if (preview.pages_shown > 1)
1233 /* Forced switch to one page when zooming in. */
1234 toggle_num_pages(hWnd);
1235 } else {
1236 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1237 update_preview_sizes(hwndPreview, TRUE);
1238 InvalidateRect(hwndPreview, NULL, FALSE);
1239 update_preview_buttons(hWnd);
1242 break;
1244 case ID_PREVIEW_ZOOMOUT:
1245 if (preview.zoomlevel > 0)
1247 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1248 preview.zoomlevel--;
1249 preview.zoomratio = 0;
1250 if (preview.zoomlevel == 0 && preview.saved_pages_shown > 1) {
1251 toggle_num_pages(hWnd);
1252 } else {
1253 update_preview_sizes(hwndPreview, TRUE);
1254 InvalidateRect(hwndPreview, NULL, FALSE);
1255 update_preview_buttons(hWnd);
1258 break;
1260 case ID_PRINT:
1261 dialog_print(hWnd, preview.wszFileName);
1262 SendMessageW(hWnd, WM_CLOSE, 0, 0);
1263 break;
1266 return 0;