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