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