- Merge from trunk up to r45543
[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 RECT rcPage;
37 SIZE bmSize;
38 SIZE bmScaledSize;
39 SIZE spacing;
40 float zoomratio;
41 int zoomlevel;
42 LPWSTR wszFileName;
43 } previewinfo, *ppreviewinfo;
44
45 static HGLOBAL devMode;
46 static HGLOBAL devNames;
47
48 static RECT margins;
49 static previewinfo preview;
50
51 extern const WCHAR wszPreviewWndClass[];
52
53 static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',0};
54
55 static LPWSTR get_print_file_filter(HWND hMainWnd)
56 {
57 static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1];
58 const WCHAR files_prn[] = {'*','.','P','R','N',0};
59 const WCHAR files_all[] = {'*','.','*','\0'};
60 LPWSTR p;
61 HINSTANCE hInstance = GetModuleHandleW(0);
62
63 p = wszPrintFilter;
64 LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
65 p += lstrlenW(p) + 1;
66 lstrcpyW(p, files_prn);
67 p += lstrlenW(p) + 1;
68 LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
69 p += lstrlenW(p) + 1;
70 lstrcpyW(p, files_all);
71 p += lstrlenW(p) + 1;
72 *p = 0;
73
74 return wszPrintFilter;
75 }
76
77 void registry_set_pagemargins(HKEY hKey)
78 {
79 RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT));
80 }
81
82 void registry_read_pagemargins(HKEY hKey)
83 {
84 DWORD size = sizeof(RECT);
85
86 if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins,
87 &size) != ERROR_SUCCESS || size != sizeof(RECT))
88 {
89 margins.top = 1417;
90 margins.bottom = 1417;
91 margins.left = 1757;
92 margins.right = 1757;
93 }
94 }
95
96 static void AddTextButton(HWND hRebarWnd, UINT string, UINT command, UINT id)
97 {
98 REBARBANDINFOW rb;
99 HINSTANCE hInstance = GetModuleHandleW(0);
100 WCHAR text[MAX_STRING_LEN];
101 HWND hButton;
102
103 LoadStringW(hInstance, string, text, MAX_STRING_LEN);
104 hButton = CreateWindowW(WC_BUTTONW, text,
105 WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
106 hRebarWnd, ULongToHandle(command), hInstance, NULL);
107
108 rb.cbSize = REBARBANDINFOW_V6_SIZE;
109 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
110 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
111 rb.hwndChild = hButton;
112 rb.cyChild = rb.cyMinChild = 22;
113 rb.cx = rb.cxMinChild = 90;
114 rb.cxIdeal = 100;
115 rb.wID = id;
116
117 SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb);
118 }
119
120 static HDC make_dc(void)
121 {
122 if(devNames && devMode)
123 {
124 LPDEVNAMES dn = GlobalLock(devNames);
125 LPDEVMODEW dm = GlobalLock(devMode);
126 HDC ret;
127
128 ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
129 (LPWSTR)dn + dn->wDeviceOffset, 0, dm);
130
131 GlobalUnlock(dn);
132 GlobalUnlock(dm);
133
134 return ret;
135 } else
136 {
137 return 0;
138 }
139 }
140
141 static LONG twips_to_centmm(int twips)
142 {
143 return MulDiv(twips, CENTMM_PER_INCH, TWIPS_PER_INCH);
144 }
145
146 static LONG centmm_to_twips(int mm)
147 {
148 return MulDiv(mm, TWIPS_PER_INCH, CENTMM_PER_INCH);
149 }
150
151 static LONG twips_to_pixels(int twips, int dpi)
152 {
153 return MulDiv(twips, dpi, TWIPS_PER_INCH);
154 }
155
156 static LONG devunits_to_twips(int units, int dpi)
157 {
158 return MulDiv(units, TWIPS_PER_INCH, dpi);
159 }
160
161
162 static RECT get_print_rect(HDC hdc)
163 {
164 RECT rc;
165 int width, height;
166
167 if(hdc)
168 {
169 int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
170 int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
171 width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX);
172 height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY);
173 } else
174 {
175 width = centmm_to_twips(18500);
176 height = centmm_to_twips(27000);
177 }
178
179 rc.left = margins.left;
180 rc.right = width - margins.right;
181 rc.top = margins.top;
182 rc.bottom = height - margins.bottom;
183
184 return rc;
185 }
186
187 void target_device(HWND hMainWnd, DWORD wordWrap)
188 {
189 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
190
191 if(wordWrap == ID_WORDWRAP_MARGIN)
192 {
193 int width = 0;
194 LRESULT result;
195 HDC hdc = make_dc();
196 RECT rc = get_print_rect(hdc);
197
198 width = rc.right - rc.left;
199 if(!hdc)
200 {
201 HDC hMaindc = GetDC(hMainWnd);
202 hdc = CreateCompatibleDC(hMaindc);
203 ReleaseDC(hMainWnd, hMaindc);
204 }
205 result = SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width);
206 DeleteDC(hdc);
207 if (result)
208 return;
209 /* otherwise EM_SETTARGETDEVICE failed, so fall back on wrapping
210 * to window using the NULL DC. */
211 }
212
213 if (wordWrap != ID_WORDWRAP_NONE) {
214 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 0);
215 } else {
216 SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, 0, 1);
217 }
218
219 }
220
221 static LPWSTR dialog_print_to_file(HWND hMainWnd)
222 {
223 OPENFILENAMEW ofn;
224 static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0};
225 static const WCHAR defExt[] = {'P','R','N',0};
226 static LPWSTR file_filter;
227
228 if(!file_filter)
229 file_filter = get_print_file_filter(hMainWnd);
230
231 ZeroMemory(&ofn, sizeof(ofn));
232
233 ofn.lStructSize = sizeof(ofn);
234 ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
235 ofn.hwndOwner = hMainWnd;
236 ofn.lpstrFilter = file_filter;
237 ofn.lpstrFile = file;
238 ofn.nMaxFile = MAX_PATH;
239 ofn.lpstrDefExt = defExt;
240
241 if(GetSaveFileNameW(&ofn))
242 return file;
243 else
244 return FALSE;
245 }
246
247 static int get_num_pages(HWND hEditorWnd, FORMATRANGE fr)
248 {
249 int page = 0;
250 fr.chrg.cpMin = 0;
251
252 do
253 {
254 int bottom = fr.rc.bottom;
255 page++;
256 fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE,
257 (LPARAM)&fr);
258 fr.rc.bottom = bottom;
259 }
260 while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
261
262 return page;
263 }
264
265 static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
266 {
267 int i;
268
269 fr->chrg.cpMin = 0;
270
271 for(i = 1; i < page; i++)
272 {
273 int bottom = fr->rc.bottom;
274 fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, (LPARAM)fr);
275 fr->rc.bottom = bottom;
276 }
277 }
278
279 static HWND get_ruler_wnd(HWND hMainWnd)
280 {
281 return GetDlgItem(GetDlgItem(hMainWnd, IDC_REBAR), IDC_RULER);
282 }
283
284 void redraw_ruler(HWND hRulerWnd)
285 {
286 RECT rc;
287
288 GetClientRect(hRulerWnd, &rc);
289 InvalidateRect(hRulerWnd, &rc, TRUE);
290 }
291
292 static void update_ruler(HWND hRulerWnd)
293 {
294 SendMessageW(hRulerWnd, WM_USER, 0, 0);
295 redraw_ruler(hRulerWnd);
296 }
297
298 static void print(LPPRINTDLGW pd, LPWSTR wszFileName)
299 {
300 FORMATRANGE fr;
301 DOCINFOW di;
302 HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR);
303 int printedPages = 0;
304
305 fr.hdc = pd->hDC;
306 fr.hdcTarget = pd->hDC;
307
308 fr.rc = get_print_rect(fr.hdc);
309 fr.rcPage.left = 0;
310 fr.rcPage.right = fr.rc.right + margins.right;
311 fr.rcPage.top = 0;
312 fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
313
314 ZeroMemory(&di, sizeof(di));
315 di.cbSize = sizeof(di);
316 di.lpszDocName = wszFileName;
317
318 if(pd->Flags & PD_PRINTTOFILE)
319 {
320 di.lpszOutput = dialog_print_to_file(pd->hwndOwner);
321 if(!di.lpszOutput)
322 return;
323 }
324
325 if(pd->Flags & PD_SELECTION)
326 {
327 SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);
328 } else
329 {
330 GETTEXTLENGTHEX gt;
331 gt.flags = GTL_DEFAULT;
332 gt.codepage = 1200;
333 fr.chrg.cpMin = 0;
334 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
335
336 if(pd->Flags & PD_PAGENUMS)
337 char_from_pagenum(hEditorWnd, &fr, pd->nToPage);
338 }
339
340 StartDocW(fr.hdc, &di);
341 do
342 {
343 int bottom = fr.rc.bottom;
344 if(StartPage(fr.hdc) <= 0)
345 break;
346
347 fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
348
349 if(EndPage(fr.hdc) <= 0)
350 break;
351 bottom = fr.rc.bottom;
352
353 printedPages++;
354 if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage)))
355 break;
356 }
357 while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
358
359 EndDoc(fr.hdc);
360 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
361 }
362
363 void dialog_printsetup(HWND hMainWnd)
364 {
365 PAGESETUPDLGW ps;
366
367 ZeroMemory(&ps, sizeof(ps));
368 ps.lStructSize = sizeof(ps);
369 ps.hwndOwner = hMainWnd;
370 ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
371 ps.rtMargin.left = twips_to_centmm(margins.left);
372 ps.rtMargin.right = twips_to_centmm(margins.right);
373 ps.rtMargin.top = twips_to_centmm(margins.top);
374 ps.rtMargin.bottom = twips_to_centmm(margins.bottom);
375 ps.hDevMode = devMode;
376 ps.hDevNames = devNames;
377
378 if(PageSetupDlgW(&ps))
379 {
380 margins.left = centmm_to_twips(ps.rtMargin.left);
381 margins.right = centmm_to_twips(ps.rtMargin.right);
382 margins.top = centmm_to_twips(ps.rtMargin.top);
383 margins.bottom = centmm_to_twips(ps.rtMargin.bottom);
384 devMode = ps.hDevMode;
385 devNames = ps.hDevNames;
386 update_ruler(get_ruler_wnd(hMainWnd));
387 }
388 }
389
390 void get_default_printer_opts(void)
391 {
392 PRINTDLGW pd;
393 ZeroMemory(&pd, sizeof(pd));
394
395 ZeroMemory(&pd, sizeof(pd));
396 pd.lStructSize = sizeof(pd);
397 pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
398 pd.hDevMode = devMode;
399
400 PrintDlgW(&pd);
401
402 devMode = pd.hDevMode;
403 devNames = pd.hDevNames;
404 }
405
406 void print_quick(LPWSTR wszFileName)
407 {
408 PRINTDLGW pd;
409
410 ZeroMemory(&pd, sizeof(pd));
411 pd.hDC = make_dc();
412
413 print(&pd, wszFileName);
414 }
415
416 void dialog_print(HWND hMainWnd, LPWSTR wszFileName)
417 {
418 PRINTDLGW pd;
419 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
420 int from = 0;
421 int to = 0;
422
423 ZeroMemory(&pd, sizeof(pd));
424 pd.lStructSize = sizeof(pd);
425 pd.hwndOwner = hMainWnd;
426 pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
427 pd.nMinPage = 1;
428 pd.nMaxPage = -1;
429 pd.hDevMode = devMode;
430 pd.hDevNames = devNames;
431
432 SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to);
433 if(from == to)
434 pd.Flags |= PD_NOSELECTION;
435
436 if(PrintDlgW(&pd))
437 {
438 devMode = pd.hDevMode;
439 devNames = pd.hDevNames;
440 print(&pd, wszFileName);
441 update_ruler(get_ruler_wnd(hMainWnd));
442 }
443 }
444
445 static void preview_bar_show(HWND hMainWnd, BOOL show)
446 {
447 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
448 int i;
449
450 if(show)
451 {
452 REBARBANDINFOW rb;
453 HWND hStatic;
454
455 AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1);
456 AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2);
457 AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3);
458 AddTextButton(hReBar, STRING_PREVIEW_TWOPAGES, ID_PREVIEW_NUMPAGES, BANDID_PREVIEW_BTN4);
459 AddTextButton(hReBar, STRING_PREVIEW_ZOOMIN, ID_PREVIEW_ZOOMIN, BANDID_PREVIEW_BTN5);
460 AddTextButton(hReBar, STRING_PREVIEW_ZOOMOUT, ID_PREVIEW_ZOOMOUT, BANDID_PREVIEW_BTN6);
461 AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN7);
462
463 hStatic = CreateWindowW(WC_STATICW, NULL,
464 WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
465 hReBar, NULL, NULL, NULL);
466
467 rb.cbSize = REBARBANDINFOW_V6_SIZE;
468 rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
469 rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
470 rb.hwndChild = hStatic;
471 rb.cyChild = rb.cyMinChild = 22;
472 rb.cx = rb.cxMinChild = 90;
473 rb.cxIdeal = 100;
474 rb.wID = BANDID_PREVIEW_BUFFER;
475
476 SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb);
477 } else
478 {
479 for(i = 0; i <= PREVIEW_BUTTONS; i++)
480 SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
481 }
482 }
483
484 static const int min_spacing = 10;
485
486 static void update_preview_scrollbars(HWND hwndPreview, RECT *window)
487 {
488 SCROLLINFO sbi;
489 sbi.cbSize = sizeof(sbi);
490 sbi.fMask = SIF_PAGE|SIF_RANGE;
491 sbi.nMin = 0;
492 if (preview.zoomlevel == 0)
493 {
494 /* Hide scrollbars when zoomed out. */
495 sbi.nMax = 0;
496 sbi.nPage = window->right;
497 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
498 sbi.nPage = window->bottom;
499 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
500 } else {
501 if (!preview.hdc2)
502 sbi.nMax = preview.bmScaledSize.cx + min_spacing * 2;
503 else
504 sbi.nMax = preview.bmScaledSize.cx * 2 + min_spacing * 3;
505 sbi.nPage = window->right;
506 SetScrollInfo(hwndPreview, SB_HORZ, &sbi, TRUE);
507 /* Change in the horizontal scrollbar visibility affects the
508 * client rect, so update the client rect. */
509 GetClientRect(hwndPreview, window);
510 sbi.nMax = preview.bmScaledSize.cy + min_spacing * 2;
511 sbi.nPage = window->bottom;
512 SetScrollInfo(hwndPreview, SB_VERT, &sbi, TRUE);
513 }
514 }
515
516 static void update_preview_sizes(HWND hwndPreview, BOOL zoomLevelUpdated)
517 {
518 RECT window;
519
520 GetClientRect(hwndPreview, &window);
521
522 /* The zoom ratio isn't updated for partial zoom because of resizing the window. */
523 if (zoomLevelUpdated || preview.zoomlevel != 1)
524 {
525 float ratio, ratioHeight, ratioWidth;
526 if (preview.zoomlevel == 2)
527 {
528 ratio = 1.0;
529 } else {
530 ratioHeight = (window.bottom - min_spacing * 2) / (float)preview.bmSize.cy;
531
532 if(preview.hdc2)
533 ratioWidth = ((window.right - min_spacing * 3) / 2.0) / (float)preview.bmSize.cx;
534 else
535 ratioWidth = (window.right - min_spacing * 2) / (float)preview.bmSize.cx;
536
537 if(ratioWidth > ratioHeight)
538 ratio = ratioHeight;
539 else
540 ratio = ratioWidth;
541
542 if (preview.zoomlevel == 1)
543 ratio += (1.0 - ratio) / 2;
544 }
545 preview.zoomratio = ratio;
546 }
547
548 preview.bmScaledSize.cx = preview.bmSize.cx * preview.zoomratio;
549 preview.bmScaledSize.cy = preview.bmSize.cy * preview.zoomratio;
550
551 preview.spacing.cy = max(min_spacing, (window.bottom - preview.bmScaledSize.cy) / 2);
552
553 if(!preview.hdc2)
554 preview.spacing.cx = (window.right - preview.bmScaledSize.cx) / 2;
555 else
556 preview.spacing.cx = (window.right - preview.bmScaledSize.cx * 2) / 3;
557 if (preview.spacing.cx < min_spacing)
558 preview.spacing.cx = min_spacing;
559
560 update_preview_scrollbars(hwndPreview, &window);
561 }
562
563 /* Update for zoom ratio changes with same page. */
564 static void update_scaled_preview(HWND hMainWnd)
565 {
566 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
567 preview.window.right = 0;
568 InvalidateRect(hwndPreview, NULL, TRUE);
569 }
570
571 LRESULT CALLBACK preview_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
572 {
573 switch(msg)
574 {
575 case WM_CREATE:
576 {
577 HWND hEditorWnd = GetDlgItem(GetParent(hWnd), IDC_EDITOR);
578 FORMATRANGE fr;
579 GETTEXTLENGTHEX gt = {GTL_DEFAULT, 1200};
580 HDC hdc = GetDC(hWnd);
581 HDC hdcTarget = make_dc();
582
583 fr.rc = preview.rcPage = get_print_rect(hdcTarget);
584 preview.rcPage.bottom += margins.bottom;
585 preview.rcPage.right += margins.right;
586 preview.rcPage.top = preview.rcPage.left = 0;
587 fr.rcPage = preview.rcPage;
588
589 preview.bmSize.cx = twips_to_pixels(preview.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
590 preview.bmSize.cy = twips_to_pixels(preview.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
591
592 fr.hdc = CreateCompatibleDC(hdc);
593 fr.hdcTarget = hdcTarget;
594 fr.chrg.cpMin = 0;
595 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
596 preview.pages = get_num_pages(hEditorWnd, fr);
597 DeleteDC(fr.hdc);
598
599 update_preview_sizes(hWnd, TRUE);
600 break;
601 }
602
603 case WM_PAINT:
604 return print_preview(hWnd);
605
606 case WM_SIZE:
607 {
608 update_preview_sizes(hWnd, FALSE);
609 update_scaled_preview(hWnd);
610 break;
611 }
612
613 case WM_VSCROLL:
614 case WM_HSCROLL:
615 {
616 SCROLLINFO si;
617 RECT rc;
618 int nBar = (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ;
619 int origPos;
620
621 GetClientRect(hWnd, &rc);
622 si.cbSize = sizeof(si);
623 si.fMask = SIF_ALL;
624 GetScrollInfo(hWnd, nBar, &si);
625 origPos = si.nPos;
626 switch(LOWORD(wParam))
627 {
628 case SB_TOP: /* == SB_LEFT */
629 si.nPos = si.nMin;
630 break;
631 case SB_BOTTOM: /* == SB_RIGHT */
632 si.nPos = si.nMax;
633 break;
634 case SB_LINEUP: /* == SB_LINELEFT */
635 si.nPos -= si.nPage / 10;
636 break;
637 case SB_LINEDOWN: /* == SB_LINERIGHT */
638 si.nPos += si.nPage / 10;
639 break;
640 case SB_PAGEUP: /* == SB_PAGELEFT */
641 si.nPos -= si.nPage;
642 break;
643 case SB_PAGEDOWN: /* SB_PAGERIGHT */
644 si.nPos += si.nPage;
645 break;
646 case SB_THUMBTRACK:
647 si.nPos = si.nTrackPos;
648 break;
649 }
650 si.fMask = SIF_POS;
651 SetScrollInfo(hWnd, nBar, &si, TRUE);
652 GetScrollInfo(hWnd, nBar, &si);
653 if (si.nPos != origPos)
654 {
655 int amount = origPos - si.nPos;
656 if (msg == WM_VSCROLL)
657 ScrollWindow(hWnd, 0, amount, NULL, NULL);
658 else
659 ScrollWindow(hWnd, amount, 0, NULL, NULL);
660 }
661 return 0;
662 }
663
664 default:
665 return DefWindowProcW(hWnd, msg, wParam, lParam);
666 }
667
668 return 0;
669 }
670
671 void init_preview(HWND hMainWnd, LPWSTR wszFileName)
672 {
673 HWND hwndPreview;
674 HINSTANCE hInstance = GetModuleHandleW(0);
675 preview.page = 1;
676 preview.hdc = 0;
677 preview.hdc2 = 0;
678 preview.wszFileName = wszFileName;
679 preview.zoomratio = 0;
680 preview.zoomlevel = 0;
681 preview_bar_show(hMainWnd, TRUE);
682
683 hwndPreview = CreateWindowExW(0, wszPreviewWndClass, NULL,
684 WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL,
685 0, 0, 200, 10, hMainWnd, (HMENU)IDC_PREVIEW, hInstance, NULL);
686 }
687
688 void close_preview(HWND hMainWnd)
689 {
690 HWND hwndPreview = GetDlgItem(hMainWnd, IDC_PREVIEW);
691 preview.window.right = 0;
692 preview.window.bottom = 0;
693 preview.page = 0;
694 preview.pages = 0;
695
696 preview_bar_show(hMainWnd, FALSE);
697 DestroyWindow(hwndPreview);
698 }
699
700 BOOL preview_isactive(void)
701 {
702 return preview.page != 0;
703 }
704
705 static void add_ruler_units(HDC hdcRuler, RECT* drawRect, BOOL NewMetrics, LONG EditLeftmost)
706 {
707 static HDC hdc;
708
709 if(NewMetrics)
710 {
711 static HBITMAP hBitmap;
712 int i, x, y, RulerTextEnd;
713 int CmPixels;
714 int QuarterCmPixels;
715 HFONT hFont;
716 WCHAR FontName[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0};
717
718 if(hdc)
719 {
720 DeleteDC(hdc);
721 DeleteObject(hBitmap);
722 }
723
724 hdc = CreateCompatibleDC(0);
725
726 CmPixels = twips_to_pixels(centmm_to_twips(1000), GetDeviceCaps(hdc, LOGPIXELSX));
727 QuarterCmPixels = (int)((float)CmPixels / 4.0);
728
729 hBitmap = CreateCompatibleBitmap(hdc, drawRect->right, drawRect->bottom);
730 SelectObject(hdc, hBitmap);
731 FillRect(hdc, drawRect, GetStockObject(WHITE_BRUSH));
732
733 hFont = CreateFontW(10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FontName);
734
735 SelectObject(hdc, hFont);
736 SetBkMode(hdc, TRANSPARENT);
737 SetTextAlign(hdc, TA_CENTER);
738 y = (int)(((float)drawRect->bottom - (float)drawRect->top) / 2.0) + 1;
739 RulerTextEnd = drawRect->right - EditLeftmost + 1;
740 for(i = 1, x = EditLeftmost; x < (drawRect->right - EditLeftmost + 1); i ++)
741 {
742 WCHAR str[3];
743 WCHAR format[] = {'%','d',0};
744 int x2 = x;
745
746 x2 += QuarterCmPixels;
747 if(x2 > RulerTextEnd)
748 break;
749
750 MoveToEx(hdc, x2, y, NULL);
751 LineTo(hdc, x2, y+2);
752
753 x2 += QuarterCmPixels;
754 if(x2 > RulerTextEnd)
755 break;
756
757 MoveToEx(hdc, x2, y - 3, NULL);
758 LineTo(hdc, x2, y + 3);
759
760 x2 += QuarterCmPixels;
761 if(x2 > RulerTextEnd)
762 break;
763
764 MoveToEx(hdc, x2, y, NULL);
765 LineTo(hdc, x2, y+2);
766
767 x += CmPixels;
768 if(x > RulerTextEnd)
769 break;
770
771 wsprintfW(str, format, i);
772 TextOutW(hdc, x, 5, str, lstrlenW(str));
773 }
774 DeleteObject(hFont);
775 }
776
777 BitBlt(hdcRuler, 0, 0, drawRect->right, drawRect->bottom, hdc, 0, 0, SRCAND);
778 }
779
780 static void paint_ruler(HWND hWnd, LONG EditLeftmost, BOOL NewMetrics)
781 {
782 PAINTSTRUCT ps;
783 HDC hdc = BeginPaint(hWnd, &ps);
784 HDC hdcPrint = make_dc();
785 RECT printRect = get_print_rect(hdcPrint);
786 RECT drawRect;
787 HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
788
789 GetClientRect(hWnd, &drawRect);
790 FillRect(hdc, &drawRect, hBrush);
791
792 drawRect.top += 3;
793 drawRect.bottom -= 3;
794 drawRect.left = EditLeftmost;
795 drawRect.right = twips_to_pixels(printRect.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
796 FillRect(hdc, &drawRect, GetStockObject(WHITE_BRUSH));
797
798 drawRect.top--;
799 drawRect.bottom++;
800 DrawEdge(hdc, &drawRect, EDGE_SUNKEN, BF_RECT);
801
802 drawRect.left = drawRect.right - 1;
803 drawRect.right = twips_to_pixels(printRect.right + margins.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
804 DrawEdge(hdc, &drawRect, EDGE_ETCHED, BF_RECT);
805
806 drawRect.left = 0;
807 drawRect.top = 0;
808 add_ruler_units(hdc, &drawRect, NewMetrics, EditLeftmost);
809
810 SelectObject(hdc, GetStockObject(BLACK_BRUSH));
811 DeleteObject(hBrush);
812 DeleteDC(hdcPrint);
813 EndPaint(hWnd, &ps);
814 }
815
816 LRESULT CALLBACK ruler_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
817 {
818 static WNDPROC pPrevRulerProc;
819 static LONG EditLeftmost;
820 static BOOL NewMetrics;
821
822 switch(msg)
823 {
824 case WM_USER:
825 if(wParam)
826 {
827 EditLeftmost = ((POINTL*)wParam)->x;
828 pPrevRulerProc = (WNDPROC)lParam;
829 }
830 NewMetrics = TRUE;
831 break;
832
833 case WM_PAINT:
834 paint_ruler(hWnd, EditLeftmost, NewMetrics);
835 break;
836
837 default:
838 return CallWindowProcW(pPrevRulerProc, hWnd, msg, wParam, lParam);
839 }
840
841 return 0;
842 }
843
844 static void draw_preview_page(HDC hdc, HDC* hdcSized, FORMATRANGE* lpFr, float ratio, int bmNewWidth, int bmNewHeight, int bmWidth, int bmHeight)
845 {
846 HBITMAP hBitmapScaled = CreateCompatibleBitmap(hdc, bmNewWidth, bmNewHeight);
847 HPEN hPen;
848 int TopMargin = (int)((float)twips_to_pixels(lpFr->rc.top, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
849 int BottomMargin = (int)((float)twips_to_pixels(lpFr->rc.bottom, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
850 int LeftMargin = (int)((float)twips_to_pixels(lpFr->rc.left, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
851 int RightMargin = (int)((float)twips_to_pixels(lpFr->rc.right, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
852
853 if(*hdcSized)
854 DeleteDC(*hdcSized);
855 *hdcSized = CreateCompatibleDC(hdc);
856 SelectObject(*hdcSized, hBitmapScaled);
857
858 StretchBlt(*hdcSized, 0, 0, bmNewWidth, bmNewHeight, hdc, 0, 0, bmWidth, bmHeight, SRCCOPY);
859
860 /* Draw margin lines */
861 hPen = CreatePen(PS_DOT, 1, RGB(0,0,0));
862 SelectObject(*hdcSized, hPen);
863
864 MoveToEx(*hdcSized, 0, TopMargin, NULL);
865 LineTo(*hdcSized, bmNewWidth, TopMargin);
866 MoveToEx(*hdcSized, 0, BottomMargin, NULL);
867 LineTo(*hdcSized, bmNewWidth, BottomMargin);
868
869 MoveToEx(*hdcSized, LeftMargin, 0, NULL);
870 LineTo(*hdcSized, LeftMargin, bmNewHeight);
871 MoveToEx(*hdcSized, RightMargin, 0, NULL);
872 LineTo(*hdcSized, RightMargin, bmNewHeight);
873
874 }
875
876 static void draw_preview(HWND hEditorWnd, FORMATRANGE* lpFr, int bmWidth, int bmHeight, RECT* paper, int page)
877 {
878 HBITMAP hBitmapCapture = CreateCompatibleBitmap(lpFr->hdc, bmWidth, bmHeight);
879 int bottom;
880
881 char_from_pagenum(hEditorWnd, lpFr, page);
882 SelectObject(lpFr->hdc, hBitmapCapture);
883 FillRect(lpFr->hdc, paper, GetStockObject(WHITE_BRUSH));
884 bottom = lpFr->rc.bottom;
885 SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)lpFr);
886 /* EM_FORMATRANGE sets fr.rc.bottom to indicate the area printed in,
887 * but we want to keep the original for drawing margins */
888 lpFr->rc.bottom = bottom;
889 SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
890 }
891
892 static void update_preview_buttons(HWND hMainWnd)
893 {
894 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
895 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
896 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE), preview.hdc2 ?
897 (preview.page + 1) < preview.pages :
898 preview.page < preview.pages);
899 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), preview.pages > 1 && preview.zoomlevel == 0);
900 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMIN), preview.zoomlevel < 2);
901 EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_ZOOMOUT), preview.zoomlevel > 0);
902 }
903
904 LRESULT print_preview(HWND hwndPreview)
905 {
906 FORMATRANGE fr;
907 HDC hdc;
908 RECT window, background;
909 PAINTSTRUCT ps;
910 HWND hMainWnd = GetParent(hwndPreview);
911 POINT scrollpos;
912
913 hdc = BeginPaint(hwndPreview, &ps);
914 GetClientRect(hwndPreview, &window);
915
916 fr.hdcTarget = make_dc();
917 fr.rc = fr.rcPage = preview.rcPage;
918 fr.rc.left += margins.left;
919 fr.rc.top += margins.top;
920 fr.rc.bottom -= margins.bottom;
921 fr.rc.right -= margins.right;
922
923 if(!preview.hdc)
924 {
925 GETTEXTLENGTHEX gt;
926 RECT paper;
927 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
928
929 preview.hdc = CreateCompatibleDC(hdc);
930
931 if(preview.hdc2)
932 {
933 if(preview.hdc2 != (HDC)-1)
934 DeleteDC(preview.hdc2);
935 preview.hdc2 = CreateCompatibleDC(hdc);
936 }
937
938 gt.flags = GTL_DEFAULT;
939 gt.codepage = 1200;
940 fr.chrg.cpMin = 0;
941 fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
942
943 paper.left = 0;
944 paper.right = preview.bmSize.cx;
945 paper.top = 0;
946 paper.bottom = preview.bmSize.cy;
947
948 fr.hdc = preview.hdc;
949 draw_preview(hEditorWnd, &fr, preview.bmSize.cx, preview.bmSize.cy, &paper, preview.page);
950
951 if(preview.hdc2)
952 {
953 fr.hdc = preview.hdc2;
954 draw_preview(hEditorWnd, &fr, preview.bmSize.cx, preview.bmSize.cy, &fr.rcPage, preview.page + 1);
955 }
956
957 update_preview_buttons(hMainWnd);
958 }
959
960 FillRect(hdc, &window, GetStockObject(GRAY_BRUSH));
961
962 scrollpos.x = GetScrollPos(hwndPreview, SB_HORZ);
963 scrollpos.y = GetScrollPos(hwndPreview, SB_VERT);
964
965 background.left = preview.spacing.cx - 2 - scrollpos.x;
966 background.right = background.left + preview.bmScaledSize.cx + 4;
967 background.top = preview.spacing.cy - 2 - scrollpos.y;
968 background.bottom = background.top + preview.bmScaledSize.cy + 4;
969
970 FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
971
972 if(preview.hdc2)
973 {
974 background.left += preview.bmScaledSize.cx + preview.spacing.cx;
975 background.right += preview.bmScaledSize.cx + preview.spacing.cx;
976
977 FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
978 }
979
980 if(window.right != preview.window.right || window.bottom != preview.window.bottom)
981 {
982 draw_preview_page(preview.hdc, &preview.hdcSized, &fr, preview.zoomratio,
983 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
984 preview.bmSize.cx, preview.bmSize.cy);
985
986 if(preview.hdc2)
987 {
988 draw_preview_page(preview.hdc2, &preview.hdcSized2, &fr, preview.zoomratio,
989 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
990 preview.bmSize.cx, preview.bmSize.cy);
991 }
992 }
993
994 BitBlt(hdc, preview.spacing.cx - scrollpos.x, preview.spacing.cy - scrollpos.y,
995 preview.bmScaledSize.cx, preview.bmScaledSize.cy,
996 preview.hdcSized, 0, 0, SRCCOPY);
997
998 if(preview.hdc2)
999 {
1000 BitBlt(hdc, preview.spacing.cx * 2 + preview.bmScaledSize.cx - scrollpos.x,
1001 preview.spacing.cy - scrollpos.y, preview.bmScaledSize.cx,
1002 preview.bmScaledSize.cy, preview.hdcSized2, 0, 0, SRCCOPY);
1003 }
1004
1005 DeleteDC(fr.hdcTarget);
1006 preview.window = window;
1007
1008 EndPaint(hwndPreview, &ps);
1009
1010 return 0;
1011 }
1012
1013 /* Update for page changes. */
1014 static void update_preview(HWND hMainWnd)
1015 {
1016 DeleteDC(preview.hdc);
1017 preview.hdc = 0;
1018
1019 update_scaled_preview(hMainWnd);
1020 }
1021
1022 static void toggle_num_pages(HWND hMainWnd)
1023 {
1024 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
1025 WCHAR name[MAX_STRING_LEN];
1026 HINSTANCE hInst = GetModuleHandleW(0);
1027
1028 if(preview.hdc2)
1029 {
1030 DeleteDC(preview.hdc2);
1031 preview.hdc2 = 0;
1032 } else
1033 {
1034 if(preview.page == preview.pages)
1035 preview.page--;
1036 preview.hdc2 = (HDC)-1;
1037 }
1038
1039 LoadStringW(hInst, preview.hdc2 ? STRING_PREVIEW_ONEPAGE : STRING_PREVIEW_TWOPAGES,
1040 name, MAX_STRING_LEN);
1041
1042 SetWindowTextW(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), name);
1043 update_preview_sizes(GetDlgItem(hMainWnd, IDC_PREVIEW), TRUE);
1044 update_preview(hMainWnd);
1045 }
1046
1047 LRESULT preview_command(HWND hWnd, WPARAM wParam)
1048 {
1049 switch(LOWORD(wParam))
1050 {
1051 case ID_FILE_EXIT:
1052 PostMessageW(hWnd, WM_CLOSE, 0, 0);
1053 break;
1054
1055 case ID_PREVIEW_NEXTPAGE:
1056 case ID_PREVIEW_PREVPAGE:
1057 {
1058 if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
1059 preview.page++;
1060 else
1061 preview.page--;
1062
1063 update_preview(hWnd);
1064 }
1065 break;
1066
1067 case ID_PREVIEW_NUMPAGES:
1068 toggle_num_pages(hWnd);
1069 break;
1070
1071 case ID_PREVIEW_ZOOMIN:
1072 if (preview.zoomlevel < 2)
1073 {
1074 preview.zoomlevel++;
1075 preview.zoomratio = 0;
1076 if (preview.hdc2)
1077 {
1078 /* Forced switch to one page when zooming in. */
1079 toggle_num_pages(hWnd);
1080 } else {
1081 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1082 update_preview_sizes(hwndPreview, TRUE);
1083 update_scaled_preview(hWnd);
1084 update_preview_buttons(hWnd);
1085 }
1086 }
1087 break;
1088
1089 case ID_PREVIEW_ZOOMOUT:
1090 if (preview.zoomlevel > 0)
1091 {
1092 HWND hwndPreview = GetDlgItem(hWnd, IDC_PREVIEW);
1093 preview.zoomlevel--;
1094 preview.zoomratio = 0;
1095 update_preview_sizes(hwndPreview, TRUE);
1096 update_scaled_preview(hWnd);
1097 update_preview_buttons(hWnd);
1098 }
1099 break;
1100
1101 case ID_PRINT:
1102 dialog_print(hWnd, preview.wszFileName);
1103 SendMessageW(hWnd, WM_CLOSE, 0, 0);
1104 break;
1105 }
1106
1107 return 0;
1108 }