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