The real, definitive, Visual C++ support branch. Accept no substitutes
[reactos.git] / base / applications / wordpad / print.c
1 /*
2 * Wordpad implementation - Printing and print preview functions
3 *
4 * Copyright 2007-2008 by Alexander N. Sørnes <alex@thehandofagony.com>
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include <windows.h>
22 #include <richedit.h>
23 #include <commctrl.h>
24
25 #include "wordpad.h"
26
27 typedef struct _previewinfo
28 {
29 int page;
30 int pages;
31 HDC hdc;
32 HDC hdc2;
33 HDC hdcSized;
34 HDC hdcSized2;
35 RECT window;
36 LPWSTR wszFileName;
37 } previewinfo, *ppreviewinfo;
38
39 static HGLOBAL devMode;
40 static HGLOBAL devNames;
41
42 static RECT margins;
43 static previewinfo preview;
44
45 static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',0};
46
47 static LPWSTR get_print_file_filter(HWND hMainWnd)
48 {
49 static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1];
50 const WCHAR files_prn[] = {'*','.','P','R','N',0};
51 const WCHAR files_all[] = {'*','.','*','\0'};
52 LPWSTR p;
53 HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hMainWnd, GWLP_HINSTANCE);
54
55 p = wszPrintFilter;
56 LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
57 p += lstrlenW(p) + 1;
58 lstrcpyW(p, files_prn);
59 p += lstrlenW(p) + 1;
60 LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
61 p += lstrlenW(p) + 1;
62 lstrcpyW(p, files_all);
63 p += lstrlenW(p) + 1;
64 *p = 0;
65
66 return wszPrintFilter;
67 }
68
69 void registry_set_pagemargins(HKEY hKey)
70 {
71 RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT));
72 }
73
74 void registry_read_pagemargins(HKEY hKey)
75 {
76 DWORD size = sizeof(RECT);
77
78 if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins,
79 &size) != ERROR_SUCCESS || size != sizeof(RECT))
80 {
81 margins.top = 1417;
82 margins.bottom = 1417;
83 margins.left = 1757;
84 margins.right = 1757;
85 }
86 }
87
88 static void AddTextButton(HWND hRebarWnd, UINT string, UINT command, UINT id)
89 {
90 REBARBANDINFOW rb;
91 HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hRebarWnd, GWLP_HINSTANCE);
92 WCHAR text[MAX_STRING_LEN];
93 HWND hButton;
94
95 LoadStringW(hInstance, string, text, MAX_STRING_LEN);
96 hButton = CreateWindowW(WC_BUTTONW, text,
97 WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
98 hRebarWnd, (HMENU)ULongToHandle(command), hInstance, NULL);
99
100 rb.cbSize = sizeof(rb);
101 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
102 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
103 rb.hwndChild = hButton;
104 rb.cyChild = rb.cyMinChild = 22;
105 rb.cx = rb.cxMinChild = 90;
106 rb.cxIdeal = 100;
107 rb.wID = id;
108
109 SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb);
110 }
111
112 static HDC make_dc(void)
113 {
114 if(devNames && devMode)
115 {
116 LPDEVNAMES dn = GlobalLock(devNames);
117 LPDEVMODEW dm = GlobalLock(devMode);
118 HDC ret;
119
120 ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
121 (LPWSTR)dn + dn->wDeviceOffset, 0, dm);
122
123 GlobalUnlock(dn);
124 GlobalUnlock(dm);
125
126 return ret;
127 } else
128 {
129 return 0;
130 }
131 }
132
133 static LONG twips_to_centmm(int twips)
134 {
135 return MulDiv(twips, CENTMM_PER_INCH, TWIPS_PER_INCH);
136 }
137
138 static LONG centmm_to_twips(int mm)
139 {
140 return MulDiv(mm, TWIPS_PER_INCH, CENTMM_PER_INCH);
141 }
142
143 static LONG twips_to_pixels(int twips, int dpi)
144 {
145 return MulDiv(twips, dpi, TWIPS_PER_INCH);
146 }
147
148 static LONG devunits_to_twips(int units, int dpi)
149 {
150 return MulDiv(units, TWIPS_PER_INCH, dpi);
151 }
152
153
154 static RECT get_print_rect(HDC hdc)
155 {
156 RECT rc;
157 int width, height;
158
159 if(hdc)
160 {
161 int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
162 int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
163 width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX);
164 height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY);
165 } else
166 {
167 width = centmm_to_twips(18500);
168 height = centmm_to_twips(27000);
169 }
170
171 rc.left = margins.left;
172 rc.right = width - margins.right;
173 rc.top = margins.top;
174 rc.bottom = height - margins.bottom;
175
176 return rc;
177 }
178
179 void target_device(HWND hMainWnd, DWORD wordWrap)
180 {
181 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
182
183 if(wordWrap == ID_WORDWRAP_MARGIN)
184 {
185 int width = 0;
186 LRESULT result;
187 HDC hdc = make_dc();
188 RECT rc = get_print_rect(hdc);
189
190 width = rc.right - rc.left;
191 if(!hdc)
192 {
193 HDC hMaindc = GetDC(hMainWnd);
194 hdc = CreateCompatibleDC(hMaindc);
195 ReleaseDC(hMainWnd, hMaindc);
196 }
197 result = SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width);
198 DeleteDC(hdc);
199 if (result)
200 return;
201 /* otherwise EM_SETTARGETDEVICE failed, so fall back on wrapping
202 * to window using the NULL DC. */
203 }
204
205 if (wordWrap != ID_WORDWRAP_NONE) {
206 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 0);
207 } else {
208 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 1);
209 }
210
211 }
212
213 static LPWSTR dialog_print_to_file(HWND hMainWnd)
214 {
215 OPENFILENAMEW ofn;
216 static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0};
217 static const WCHAR defExt[] = {'P','R','N',0};
218 static LPWSTR file_filter;
219
220 if(!file_filter)
221 file_filter = get_print_file_filter(hMainWnd);
222
223 ZeroMemory(&ofn, sizeof(ofn));
224
225 ofn.lStructSize = sizeof(ofn);
226 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
227 ofn.hwndOwner = hMainWnd;
228 ofn.lpstrFilter = file_filter;
229 ofn.lpstrFile = (LPWSTR)file;
230 ofn.nMaxFile = MAX_PATH;
231 ofn.lpstrDefExt = (LPWSTR)defExt;
232
233 if(GetSaveFileNameW(&ofn))
234 return (LPWSTR)file;
235 else
236 return FALSE;
237 }
238
239 static int get_num_pages(HWND hEditorWnd, FORMATRANGE fr)
240 {
241 int page = 0;
242 fr.chrg.cpMin = 0;
243
244 do
245 {
246 page++;
247 fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE,
248 (LPARAM)&fr);
249 }
250 while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
251
252 return page;
253 }
254
255 static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
256 {
257 int i;
258
259 fr->chrg.cpMin = 0;
260
261 for(i = 1; i < page; i++)
262 {
263 fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)fr);
264 }
265 }
266
267 static HWND get_ruler_wnd(HWND hMainWnd)
268 {
269 return GetDlgItem(GetDlgItem(hMainWnd, IDC_REBAR), IDC_RULER);
270 }
271
272 void redraw_ruler(HWND hRulerWnd)
273 {
274 RECT rc;
275
276 GetClientRect(hRulerWnd, &rc);
277 InvalidateRect(hRulerWnd, &rc, TRUE);
278 }
279
280 static void update_ruler(HWND hRulerWnd)
281 {
282 SendMessageW(hRulerWnd, WM_USER, 0, 0);
283 redraw_ruler(hRulerWnd);
284 }
285
286 static void print(LPPRINTDLGW pd, LPWSTR wszFileName)
287 {
288 FORMATRANGE fr;
289 DOCINFOW di;
290 HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR);
291 int printedPages = 0;
292
293 fr.hdc = pd->hDC;
294 fr.hdcTarget = pd->hDC;
295
296 fr.rc = get_print_rect(fr.hdc);
297 fr.rcPage.left = 0;
298 fr.rcPage.right = fr.rc.right + margins.right;
299 fr.rcPage.top = 0;
300 fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
301
302 ZeroMemory(&di, sizeof(di));
303 di.cbSize = sizeof(di);
304 di.lpszDocName = wszFileName;
305
306 if(pd->Flags & PD_PRINTTOFILE)
307 {
308 di.lpszOutput = dialog_print_to_file(pd->hwndOwner);
309 if(!di.lpszOutput)
310 return;
311 }
312
313 if(pd->Flags & PD_SELECTION)
314 {
315 SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);
316 } else
317 {
318 GETTEXTLENGTHEX gt;
319 gt.flags = GTL_DEFAULT;
320 gt.codepage = 1200;
321 fr.chrg.cpMin = 0;
322 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
323
324 if(pd->Flags & PD_PAGENUMS)
325 char_from_pagenum(hEditorWnd, &fr, pd->nToPage);
326 }
327
328 StartDocW(fr.hdc, &di);
329 do
330 {
331 if(StartPage(fr.hdc) <= 0)
332 break;
333
334 fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
335
336 if(EndPage(fr.hdc) <= 0)
337 break;
338
339 printedPages++;
340 if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage)))
341 break;
342 }
343 while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
344
345 EndDoc(fr.hdc);
346 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
347 }
348
349 void dialog_printsetup(HWND hMainWnd)
350 {
351 PAGESETUPDLGW ps;
352
353 ZeroMemory(&ps, sizeof(ps));
354 ps.lStructSize = sizeof(ps);
355 ps.hwndOwner = hMainWnd;
356 ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
357 ps.rtMargin.left = twips_to_centmm(margins.left);
358 ps.rtMargin.right = twips_to_centmm(margins.right);
359 ps.rtMargin.top = twips_to_centmm(margins.top);
360 ps.rtMargin.bottom = twips_to_centmm(margins.bottom);
361 ps.hDevMode = devMode;
362 ps.hDevNames = devNames;
363
364 if(PageSetupDlgW(&ps))
365 {
366 margins.left = centmm_to_twips(ps.rtMargin.left);
367 margins.right = centmm_to_twips(ps.rtMargin.right);
368 margins.top = centmm_to_twips(ps.rtMargin.top);
369 margins.bottom = centmm_to_twips(ps.rtMargin.bottom);
370 devMode = ps.hDevMode;
371 devNames = ps.hDevNames;
372 update_ruler(get_ruler_wnd(hMainWnd));
373 }
374 }
375
376 void get_default_printer_opts(void)
377 {
378 PRINTDLGW pd;
379 ZeroMemory(&pd, sizeof(pd));
380
381 ZeroMemory(&pd, sizeof(pd));
382 pd.lStructSize = sizeof(pd);
383 pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
384 pd.hDevMode = devMode;
385
386 PrintDlgW(&pd);
387
388 devMode = pd.hDevMode;
389 devNames = pd.hDevNames;
390 }
391
392 void print_quick(LPWSTR wszFileName)
393 {
394 PRINTDLGW pd;
395
396 ZeroMemory(&pd, sizeof(pd));
397 pd.hDC = make_dc();
398
399 print(&pd, wszFileName);
400 }
401
402 void dialog_print(HWND hMainWnd, LPWSTR wszFileName)
403 {
404 PRINTDLGW pd;
405 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
406 int from = 0;
407 int to = 0;
408
409 ZeroMemory(&pd, sizeof(pd));
410 pd.lStructSize = sizeof(pd);
411 pd.hwndOwner = hMainWnd;
412 pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
413 pd.nMinPage = 1;
414 pd.nMaxPage = -1;
415 pd.hDevMode = devMode;
416 pd.hDevNames = devNames;
417
418 SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to);
419 if(from == to)
420 pd.Flags |= PD_NOSELECTION;
421
422 if(PrintDlgW(&pd))
423 {
424 devMode = pd.hDevMode;
425 devNames = pd.hDevNames;
426 print(&pd, wszFileName);
427 update_ruler(get_ruler_wnd(hMainWnd));
428 }
429 }
430
431 static void preview_bar_show(HWND hMainWnd, BOOL show)
432 {
433 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
434 int i;
435
436 if(show)
437 {
438 REBARBANDINFOW rb;
439 HWND hStatic;
440
441 AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1);
442 AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2);
443 AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3);
444 AddTextButton(hReBar, STRING_PREVIEW_TWOPAGES, ID_PREVIEW_NUMPAGES, BANDID_PREVIEW_BTN4);
445 AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN5);
446
447 hStatic = CreateWindowW(WC_STATICW, NULL,
448 WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
449 hReBar, NULL, NULL, NULL);
450
451 rb.cbSize = sizeof(rb);
452 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
453 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
454 rb.hwndChild = hStatic;
455 rb.cyChild = rb.cyMinChild = 22;
456 rb.cx = rb.cxMinChild = 90;
457 rb.cxIdeal = 100;
458 rb.wID = BANDID_PREVIEW_BUFFER;
459
460 SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb);
461 } else
462 {
463 for(i = 0; i <= PREVIEW_BUTTONS; i++)
464 SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
465 }
466 }
467
468 void init_preview(HWND hMainWnd, LPWSTR wszFileName)
469 {
470 preview.page = 1;
471 preview.hdc = 0;
472 preview.hdc2 = 0;
473 preview.wszFileName = wszFileName;
474 preview_bar_show(hMainWnd, TRUE);
475 }
476
477 void close_preview(HWND hMainWnd)
478 {
479 preview.window.right = 0;
480 preview.window.bottom = 0;
481 preview.page = 0;
482 preview.pages = 0;
483
484 preview_bar_show(hMainWnd, FALSE);
485 }
486
487 BOOL preview_isactive(void)
488 {
489 return preview.page != 0;
490 }
491
492 static void add_ruler_units(HDC hdcRuler, RECT* drawRect, BOOL NewMetrics, long EditLeftmost)
493 {
494 static HDC hdc;
495
496 if(NewMetrics)
497 {
498 static HBITMAP hBitmap;
499 int i, x, y, RulerTextEnd;
500 int CmPixels;
501 int QuarterCmPixels;
502 HFONT hFont;
503 WCHAR FontName[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0};
504
505 if(hdc)
506 {
507 DeleteDC(hdc);
508 DeleteObject(hBitmap);
509 }
510
511 hdc = CreateCompatibleDC(0);
512
513 CmPixels = twips_to_pixels(centmm_to_twips(1000), GetDeviceCaps(hdc, LOGPIXELSX));
514 QuarterCmPixels = (int)((float)CmPixels / 4.0);
515
516 hBitmap = CreateCompatibleBitmap(hdc, drawRect->right, drawRect->bottom);
517 SelectObject(hdc, hBitmap);
518 FillRect(hdc, drawRect, GetStockObject(WHITE_BRUSH));
519
520 hFont = CreateFontW(10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FontName);
521
522 SelectObject(hdc, hFont);
523 SetBkMode(hdc, TRANSPARENT);
524 SetTextAlign(hdc, TA_CENTER);
525 y = (int)(((float)drawRect->bottom - (float)drawRect->top) / 2.0) + 1;
526 RulerTextEnd = drawRect->right - EditLeftmost + 1;
527 for(i = 1, x = EditLeftmost; x < (drawRect->right - EditLeftmost + 1); i ++)
528 {
529 WCHAR str[3];
530 WCHAR format[] = {'%','d',0};
531 int x2 = x;
532
533 x2 += QuarterCmPixels;
534 if(x2 > RulerTextEnd)
535 break;
536
537 MoveToEx(hdc, x2, y, NULL);
538 LineTo(hdc, x2, y+2);
539
540 x2 += QuarterCmPixels;
541 if(x2 > RulerTextEnd)
542 break;
543
544 MoveToEx(hdc, x2, y - 3, NULL);
545 LineTo(hdc, x2, y + 3);
546
547 x2 += QuarterCmPixels;
548 if(x2 > RulerTextEnd)
549 break;
550
551 MoveToEx(hdc, x2, y, NULL);
552 LineTo(hdc, x2, y+2);
553
554 x += CmPixels;
555 if(x > RulerTextEnd)
556 break;
557
558 wsprintfW(str, format, i);
559 TextOutW(hdc, x, 5, str, lstrlenW(str));
560 }
561 DeleteObject(hFont);
562 }
563
564 BitBlt(hdcRuler, 0, 0, drawRect->right, drawRect->bottom, hdc, 0, 0, SRCAND);
565 }
566
567 static void paint_ruler(HWND hWnd, long EditLeftmost, BOOL NewMetrics)
568 {
569 PAINTSTRUCT ps;
570 HDC hdc = BeginPaint(hWnd, &ps);
571 HDC hdcPrint = make_dc();
572 RECT printRect = get_print_rect(hdcPrint);
573 RECT drawRect;
574 HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
575
576 GetClientRect(hWnd, &drawRect);
577 FillRect(hdc, &drawRect, hBrush);
578
579 drawRect.top += 3;
580 drawRect.bottom -= 3;
581 drawRect.left = EditLeftmost;
582 drawRect.right = twips_to_pixels(printRect.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
583 FillRect(hdc, &drawRect, GetStockObject(WHITE_BRUSH));
584
585 drawRect.top--;
586 drawRect.bottom++;
587 DrawEdge(hdc, &drawRect, EDGE_SUNKEN, BF_RECT);
588
589 drawRect.left = drawRect.right - 1;
590 drawRect.right = twips_to_pixels(printRect.right + margins.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
591 DrawEdge(hdc, &drawRect, EDGE_ETCHED, BF_RECT);
592
593 drawRect.left = 0;
594 drawRect.top = 0;
595 add_ruler_units(hdc, &drawRect, NewMetrics, EditLeftmost);
596
597 SelectObject(hdc, GetStockObject(BLACK_BRUSH));
598 DeleteObject(hBrush);
599 DeleteDC(hdcPrint);
600 EndPaint(hWnd, &ps);
601 }
602
603 LRESULT CALLBACK ruler_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
604 {
605 static WNDPROC pPrevRulerProc;
606 static long EditLeftmost;
607 static BOOL NewMetrics;
608
609 switch(msg)
610 {
611 case WM_USER:
612 if(wParam)
613 {
614 EditLeftmost = ((POINTL*)wParam)->x;
615 pPrevRulerProc = (WNDPROC)lParam;
616 }
617 NewMetrics = TRUE;
618 break;
619
620 case WM_PAINT:
621 paint_ruler(hWnd, EditLeftmost, NewMetrics);
622 break;
623
624 default:
625 return CallWindowProcW(pPrevRulerProc, hWnd, msg, wParam, lParam);
626 }
627
628 return 0;
629 }
630
631 static void draw_preview_page(HDC hdc, HDC* hdcSized, FORMATRANGE* lpFr, float ratio, int bmNewWidth, int bmNewHeight, int bmWidth, int bmHeight)
632 {
633 HBITMAP hBitmapScaled = CreateCompatibleBitmap(hdc, bmNewWidth, bmNewHeight);
634 HPEN hPen;
635 int TopMargin = (int)((float)twips_to_pixels(lpFr->rc.top, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
636 int BottomMargin = (int)((float)twips_to_pixels(lpFr->rc.bottom, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
637 int LeftMargin = (int)((float)twips_to_pixels(lpFr->rc.left, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
638 int RightMargin = (int)((float)twips_to_pixels(lpFr->rc.right, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
639
640 if(*hdcSized)
641 DeleteDC(*hdcSized);
642 *hdcSized = CreateCompatibleDC(hdc);
643 SelectObject(*hdcSized, hBitmapScaled);
644
645 StretchBlt(*hdcSized, 0, 0, bmNewWidth, bmNewHeight, hdc, 0, 0, bmWidth, bmHeight, SRCCOPY);
646
647 /* Draw margin lines */
648 hPen = CreatePen(PS_DOT, 1, RGB(0,0,0));
649 SelectObject(*hdcSized, hPen);
650
651 MoveToEx(*hdcSized, 0, TopMargin, NULL);
652 LineTo(*hdcSized, bmNewWidth, TopMargin);
653 MoveToEx(*hdcSized, 0, BottomMargin, NULL);
654 LineTo(*hdcSized, bmNewWidth, BottomMargin);
655
656 MoveToEx(*hdcSized, LeftMargin, 0, NULL);
657 LineTo(*hdcSized, LeftMargin, bmNewHeight);
658 MoveToEx(*hdcSized, RightMargin, 0, NULL);
659 LineTo(*hdcSized, RightMargin, bmNewHeight);
660
661 }
662
663 static void draw_preview(HWND hEditorWnd, FORMATRANGE* lpFr, int bmWidth, int bmHeight, RECT* paper, int page)
664 {
665 HBITMAP hBitmapCapture = CreateCompatibleBitmap(lpFr->hdc, bmWidth, bmHeight);
666
667 char_from_pagenum(hEditorWnd, lpFr, page);
668 SelectObject(lpFr->hdc, hBitmapCapture);
669 FillRect(lpFr->hdc, paper, GetStockObject(WHITE_BRUSH));
670 SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)lpFr);
671 /* EM_FORMATRANGE sets fr.rc to indicate the area printed in, but we want to
672 keep the original for drawing margins */
673 lpFr->rc = get_print_rect(lpFr->hdcTarget);
674 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
675 }
676
677 LRESULT print_preview(HWND hMainWnd)
678 {
679 FORMATRANGE fr;
680 GETTEXTLENGTHEX gt;
681 HDC hdc;
682 RECT window, background;
683 int bmWidth, bmHeight, bmNewWidth, bmNewHeight;
684 float ratioWidth, ratioHeight, ratio;
685 int xOffset, yOffset;
686 int barheight;
687 float spacing = 20.0;
688 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
689 PAINTSTRUCT ps;
690
691 hdc = BeginPaint(hMainWnd, &ps);
692 GetClientRect(hMainWnd, &window);
693
694 fr.hdcTarget = make_dc();
695 fr.rc = get_print_rect(fr.hdcTarget);
696 fr.rcPage.left = 0;
697 fr.rcPage.top = 0;
698 fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
699 fr.rcPage.right = fr.rc.right + margins.right;
700
701 bmWidth = twips_to_pixels(fr.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
702 bmHeight = twips_to_pixels(fr.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
703
704 if(!preview.hdc)
705 {
706 RECT paper;
707 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
708
709 preview.hdc = CreateCompatibleDC(hdc);
710
711 if(preview.hdc2)
712 {
713 if(preview.hdc2 != (HDC)-1)
714 DeleteDC(preview.hdc2);
715 preview.hdc2 = CreateCompatibleDC(hdc);
716 }
717
718 fr.hdc = preview.hdc;
719 gt.flags = GTL_DEFAULT;
720 gt.codepage = 1200;
721 fr.chrg.cpMin = 0;
722 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
723
724 paper.left = 0;
725 paper.right = bmWidth;
726 paper.top = 0;
727 paper.bottom = bmHeight;
728
729 if(!preview.pages)
730 preview.pages = get_num_pages(hEditorWnd, fr);
731
732 fr.hdc = preview.hdc;
733 draw_preview(hEditorWnd, &fr, bmWidth, bmHeight, &paper, preview.page);
734
735 if(preview.hdc2)
736 {
737 fr.hdc = preview.hdc2;
738 draw_preview(hEditorWnd, &fr, bmWidth, bmHeight, &paper, preview.page + 1);
739 }
740
741 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
742 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE), preview.hdc2 ?
743 (preview.page + 1) < preview.pages :
744 preview.page < preview.pages);
745 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), preview.pages > 1);
746 }
747
748 barheight = SendMessageW(hReBar, RB_GETBARHEIGHT, 0, 0);
749 ratioHeight = ((float)window.bottom - spacing - (float)barheight) / (float)bmHeight;
750
751 if(preview.hdc2)
752 ratioWidth = ((float)window.right / 2.0 - spacing * 2.0) / (float)bmWidth;
753 else
754 ratioWidth = ((float)window.right - spacing * 3.0) / (float)bmWidth;
755
756 if(ratioWidth > ratioHeight)
757 ratio = ratioHeight;
758 else
759 ratio = ratioWidth;
760
761 bmNewWidth = (int)((float)bmWidth * ratio);
762 bmNewHeight = (int)((float)bmHeight * ratio);
763
764 yOffset = ((window.bottom - bmNewHeight + barheight) / 2);
765
766 if(!preview.hdc2)
767 xOffset = (window.right - bmNewWidth) / 2;
768 else
769 xOffset = (window.right - bmNewWidth * 2) / 2;
770
771 window.top = barheight;
772 FillRect(hdc, &window, GetStockObject(GRAY_BRUSH));
773
774 background.left = xOffset - 2;
775 background.right = xOffset + bmNewWidth + 2;
776 background.top = yOffset - 2;
777 background.bottom = yOffset + bmNewHeight + 2;
778
779 FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
780
781 if(window.right != preview.window.right || window.bottom != preview.window.bottom)
782 {
783 draw_preview_page(preview.hdc, &preview.hdcSized, &fr, ratio, bmNewWidth, bmNewHeight, bmWidth, bmHeight);
784
785 if(preview.hdc2)
786 {
787 background.left += bmNewWidth + spacing;
788 background.right += bmNewWidth + spacing;
789
790 FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
791
792 draw_preview_page(preview.hdc2, &preview.hdcSized2, &fr, ratio, bmNewWidth, bmNewHeight, bmWidth, bmHeight);
793 }
794 }
795
796 BitBlt(hdc, xOffset, yOffset, bmNewWidth, bmNewHeight, preview.hdcSized, 0, 0, SRCCOPY);
797
798 if(preview.hdc2)
799 {
800 BitBlt(hdc, xOffset + bmNewWidth + spacing, yOffset, bmNewWidth, bmNewHeight, preview.hdcSized2, 0, 0, SRCCOPY);
801 }
802
803 DeleteDC(fr.hdcTarget);
804 preview.window = window;
805
806 EndPaint(hMainWnd, &ps);
807
808 return 0;
809 }
810
811 static void update_preview(HWND hWnd)
812 {
813 RECT rc;
814
815 DeleteDC(preview.hdc);
816 preview.hdc = 0;
817
818 preview.window.right = 0;
819
820 GetClientRect(hWnd, &rc);
821 rc.top += SendMessageW(GetDlgItem(hWnd, IDC_REBAR), RB_GETBARHEIGHT, 0, 0);
822 InvalidateRect(hWnd, &rc, TRUE);
823 }
824
825 LRESULT preview_command(HWND hWnd, WPARAM wParam)
826 {
827 switch(LOWORD(wParam))
828 {
829 case ID_FILE_EXIT:
830 PostMessageW(hWnd, WM_CLOSE, 0, 0);
831 break;
832
833 case ID_PREVIEW_NEXTPAGE:
834 case ID_PREVIEW_PREVPAGE:
835 {
836 if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
837 preview.page++;
838 else
839 preview.page--;
840
841 update_preview(hWnd);
842 }
843 break;
844
845 case ID_PREVIEW_NUMPAGES:
846 {
847 HWND hReBar = GetDlgItem(hWnd, IDC_REBAR);
848 WCHAR name[MAX_STRING_LEN];
849 HINSTANCE hInst = (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE);
850
851 if(preview.hdc2)
852 {
853 DeleteDC(preview.hdc2);
854 preview.hdc2 = 0;
855 } else
856 {
857 if(preview.page == preview.pages)
858 preview.page--;
859 preview.hdc2 = (HDC)-1;
860 }
861
862 LoadStringW(hInst, preview.hdc2 ? STRING_PREVIEW_ONEPAGE : STRING_PREVIEW_TWOPAGES,
863 name, MAX_STRING_LEN);
864
865 SetWindowTextW(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), name);
866 update_preview(hWnd);
867 }
868 break;
869
870 case ID_PRINT:
871 dialog_print(hWnd, preview.wszFileName);
872 SendMessageW(hWnd, WM_CLOSE, 0, 0);
873 break;
874 }
875
876 return 0;
877 }