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