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