oleview: Fixed crash with loading typelib information.
[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;
31     HDC hdc;
32     HDC hdc2;
33     HDC hdcSized;
34     HDC hdcSized2;
35     RECT window;
36     LPWSTR wszFileName;
37 } previewinfo, *ppreviewinfo;
38
39 static HGLOBAL devMode;
40 static HGLOBAL devNames;
41
42 static RECT margins;
43 static previewinfo preview;
44
45 static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',0};
46
47 static LPWSTR get_print_file_filter(HWND hMainWnd)
48 {
49     static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1];
50     const WCHAR files_prn[] = {'*','.','P','R','N',0};
51     const WCHAR files_all[] = {'*','.','*','\0'};
52     LPWSTR p;
53     HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hMainWnd, GWLP_HINSTANCE);
54
55     p = wszPrintFilter;
56     LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
57     p += lstrlenW(p) + 1;
58     lstrcpyW(p, files_prn);
59     p += lstrlenW(p) + 1;
60     LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
61     p += lstrlenW(p) + 1;
62     lstrcpyW(p, files_all);
63     p += lstrlenW(p) + 1;
64     *p = 0;
65
66     return wszPrintFilter;
67 }
68
69 void registry_set_pagemargins(HKEY hKey)
70 {
71     RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT));
72 }
73
74 void registry_read_pagemargins(HKEY hKey)
75 {
76     DWORD size = sizeof(RECT);
77
78     if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins,
79                      &size) != ERROR_SUCCESS || size != sizeof(RECT))
80     {
81         margins.top = 1417;
82         margins.bottom = 1417;
83         margins.left = 1757;
84         margins.right = 1757;
85     }
86 }
87
88 static void AddTextButton(HWND hRebarWnd, UINT string, UINT command, UINT id)
89 {
90     REBARBANDINFOW rb;
91     HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hRebarWnd, GWLP_HINSTANCE);
92     WCHAR text[MAX_STRING_LEN];
93     HWND hButton;
94
95     LoadStringW(hInstance, string, text, MAX_STRING_LEN);
96     hButton = CreateWindowW(WC_BUTTONW, text,
97                             WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
98                             hRebarWnd, (HMENU)ULongToHandle(command), hInstance, NULL);
99
100     rb.cbSize = sizeof(rb);
101     rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
102     rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
103     rb.hwndChild = hButton;
104     rb.cyChild = rb.cyMinChild = 22;
105     rb.cx = rb.cxMinChild = 90;
106     rb.cxIdeal = 100;
107     rb.wID = id;
108
109     SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb);
110 }
111
112 static HDC make_dc(void)
113 {
114     if(devNames && devMode)
115     {
116         LPDEVNAMES dn = GlobalLock(devNames);
117         LPDEVMODEW dm = GlobalLock(devMode);
118         HDC ret;
119
120         ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
121                          (LPWSTR)dn + dn->wDeviceOffset, 0, dm);
122
123         GlobalUnlock(dn);
124         GlobalUnlock(dm);
125
126         return ret;
127     } else
128     {
129         return 0;
130     }
131 }
132
133 static LONG twips_to_centmm(int twips)
134 {
135     return MulDiv(twips, 1000, TWIPS_PER_CM);
136 }
137
138 LONG centmm_to_twips(int mm)
139 {
140     return MulDiv(mm, TWIPS_PER_CM, 1000);
141 }
142
143 static LONG twips_to_pixels(int twips, int dpi)
144 {
145     float ret = ((float)twips / ((float)TWIPS_PER_CM * 2.54)) * (float)dpi;
146     return (LONG)ret;
147 }
148
149 static LONG devunits_to_twips(int units, int dpi)
150 {
151     float ret = ((float)units / (float)dpi) * (float)TWIPS_PER_CM * 2.54;
152     return (LONG)ret;
153 }
154
155
156 static RECT get_print_rect(HDC hdc)
157 {
158     RECT rc;
159     int width, height;
160
161     if(hdc)
162     {
163         int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
164         int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
165         width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX);
166         height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY);
167     } else
168     {
169         width = centmm_to_twips(18500);
170         height = centmm_to_twips(27000);
171     }
172
173     rc.left = margins.left;
174     rc.right = width - margins.right;
175     rc.top = margins.top;
176     rc.bottom = height - margins.bottom;
177
178     return rc;
179 }
180
181 void target_device(HWND hMainWnd, DWORD wordWrap)
182 {
183     HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
184     HDC hdc = make_dc();
185     int width = 0;
186
187     if(wordWrap == ID_WORDWRAP_MARGIN)
188     {
189         RECT rc = get_print_rect(hdc);
190         width = rc.right - rc.left;
191     }
192
193     if(!hdc)
194     {
195         HDC hMaindc = GetDC(hMainWnd);
196         hdc = CreateCompatibleDC(hMaindc);
197         ReleaseDC(hMainWnd, hMaindc);
198     }
199
200     SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width);
201
202     DeleteDC(hdc);
203 }
204
205 static LPWSTR dialog_print_to_file(HWND hMainWnd)
206 {
207     OPENFILENAMEW ofn;
208     static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0};
209     static const WCHAR defExt[] = {'P','R','N',0};
210     static LPWSTR file_filter;
211
212     if(!file_filter)
213         file_filter = get_print_file_filter(hMainWnd);
214
215     ZeroMemory(&ofn, sizeof(ofn));
216
217     ofn.lStructSize = sizeof(ofn);
218     ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
219     ofn.hwndOwner = hMainWnd;
220     ofn.lpstrFilter = file_filter;
221     ofn.lpstrFile = (LPWSTR)file;
222     ofn.nMaxFile = MAX_PATH;
223     ofn.lpstrDefExt = (LPWSTR)defExt;
224
225     if(GetSaveFileNameW(&ofn))
226         return (LPWSTR)file;
227     else
228         return FALSE;
229 }
230
231 static int get_num_pages(HWND hEditorWnd, FORMATRANGE fr)
232 {
233     int page = 0;
234     fr.chrg.cpMin = 0;
235
236     do
237     {
238         page++;
239         fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE,
240                                      (LPARAM)&fr);
241     }
242     while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
243
244     return page;
245 }
246
247 static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
248 {
249     int i;
250
251     fr->chrg.cpMin = 0;
252
253     for(i = 1; i < page; i++)
254     {
255         fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)fr);
256     }
257 }
258
259 static HWND get_ruler_wnd(HWND hMainWnd)
260 {
261     return GetDlgItem(GetDlgItem(hMainWnd, IDC_REBAR), IDC_RULER);
262 }
263
264 void redraw_ruler(HWND hRulerWnd)
265 {
266     RECT rc;
267
268     GetClientRect(hRulerWnd, &rc);
269     InvalidateRect(hRulerWnd, &rc, TRUE);
270 }
271
272 static void update_ruler(HWND hRulerWnd)
273 {
274      SendMessageW(hRulerWnd, WM_USER, 0, 0);
275      redraw_ruler(hRulerWnd);
276 }
277
278 static void print(LPPRINTDLGW pd, LPWSTR wszFileName)
279 {
280     FORMATRANGE fr;
281     DOCINFOW di;
282     HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR);
283     int printedPages = 0;
284
285     fr.hdc = pd->hDC;
286     fr.hdcTarget = pd->hDC;
287
288     fr.rc = get_print_rect(fr.hdc);
289     fr.rcPage.left = 0;
290     fr.rcPage.right = fr.rc.right + margins.right;
291     fr.rcPage.top = 0;
292     fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
293
294     ZeroMemory(&di, sizeof(di));
295     di.cbSize = sizeof(di);
296     di.lpszDocName = wszFileName;
297
298     if(pd->Flags & PD_PRINTTOFILE)
299     {
300         di.lpszOutput = dialog_print_to_file(pd->hwndOwner);
301         if(!di.lpszOutput)
302             return;
303     }
304
305     if(pd->Flags & PD_SELECTION)
306     {
307         SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);
308     } else
309     {
310         GETTEXTLENGTHEX gt;
311         gt.flags = GTL_DEFAULT;
312         gt.codepage = 1200;
313         fr.chrg.cpMin = 0;
314         fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
315
316         if(pd->Flags & PD_PAGENUMS)
317             char_from_pagenum(hEditorWnd, &fr, pd->nToPage);
318     }
319
320     StartDocW(fr.hdc, &di);
321     do
322     {
323         if(StartPage(fr.hdc) <= 0)
324             break;
325
326         fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
327
328         if(EndPage(fr.hdc) <= 0)
329             break;
330
331         printedPages++;
332         if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage)))
333             break;
334     }
335     while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
336
337     EndDoc(fr.hdc);
338     SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
339 }
340
341 void dialog_printsetup(HWND hMainWnd)
342 {
343     PAGESETUPDLGW ps;
344
345     ZeroMemory(&ps, sizeof(ps));
346     ps.lStructSize = sizeof(ps);
347     ps.hwndOwner = hMainWnd;
348     ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
349     ps.rtMargin.left = twips_to_centmm(margins.left);
350     ps.rtMargin.right = twips_to_centmm(margins.right);
351     ps.rtMargin.top = twips_to_centmm(margins.top);
352     ps.rtMargin.bottom = twips_to_centmm(margins.bottom);
353     ps.hDevMode = devMode;
354     ps.hDevNames = devNames;
355
356     if(PageSetupDlgW(&ps))
357     {
358         margins.left = centmm_to_twips(ps.rtMargin.left);
359         margins.right = centmm_to_twips(ps.rtMargin.right);
360         margins.top = centmm_to_twips(ps.rtMargin.top);
361         margins.bottom = centmm_to_twips(ps.rtMargin.bottom);
362         devMode = ps.hDevMode;
363         devNames = ps.hDevNames;
364         update_ruler(get_ruler_wnd(hMainWnd));
365     }
366 }
367
368 void get_default_printer_opts(void)
369 {
370     PRINTDLGW pd;
371     ZeroMemory(&pd, sizeof(pd));
372
373     ZeroMemory(&pd, sizeof(pd));
374     pd.lStructSize = sizeof(pd);
375     pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
376     pd.hDevMode = devMode;
377
378     PrintDlgW(&pd);
379
380     devMode = pd.hDevMode;
381     devNames = pd.hDevNames;
382 }
383
384 void print_quick(LPWSTR wszFileName)
385 {
386     PRINTDLGW pd;
387
388     ZeroMemory(&pd, sizeof(pd));
389     pd.hDC = make_dc();
390
391     print(&pd, wszFileName);
392 }
393
394 void dialog_print(HWND hMainWnd, LPWSTR wszFileName)
395 {
396     PRINTDLGW pd;
397     HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
398     int from = 0;
399     int to = 0;
400
401     ZeroMemory(&pd, sizeof(pd));
402     pd.lStructSize = sizeof(pd);
403     pd.hwndOwner = hMainWnd;
404     pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
405     pd.nMinPage = 1;
406     pd.nMaxPage = -1;
407     pd.hDevMode = devMode;
408     pd.hDevNames = devNames;
409
410     SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to);
411     if(from == to)
412         pd.Flags |= PD_NOSELECTION;
413
414     if(PrintDlgW(&pd))
415     {
416         devMode = pd.hDevMode;
417         devNames = pd.hDevNames;
418         print(&pd, wszFileName);
419         update_ruler(get_ruler_wnd(hMainWnd));
420     }
421 }
422
423 static void preview_bar_show(HWND hMainWnd, BOOL show)
424 {
425     HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
426     int i;
427
428     if(show)
429     {
430         REBARBANDINFOW rb;
431         HWND hStatic;
432
433         AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1);
434         AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2);
435         AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3);
436         AddTextButton(hReBar, STRING_PREVIEW_TWOPAGES, ID_PREVIEW_NUMPAGES, BANDID_PREVIEW_BTN4);
437         AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN5);
438
439         hStatic = CreateWindowW(WC_STATICW, NULL,
440                                 WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
441                                 hReBar, NULL, NULL, NULL);
442
443         rb.cbSize = sizeof(rb);
444         rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
445         rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
446         rb.hwndChild = hStatic;
447         rb.cyChild = rb.cyMinChild = 22;
448         rb.cx = rb.cxMinChild = 90;
449         rb.cxIdeal = 100;
450         rb.wID = BANDID_PREVIEW_BUFFER;
451
452         SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb);
453     } else
454     {
455         for(i = 0; i <= PREVIEW_BUTTONS; i++)
456             SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
457     }
458 }
459
460 void init_preview(HWND hMainWnd, LPWSTR wszFileName)
461 {
462     preview.page = 1;
463     preview.hdc = 0;
464     preview.hdc2 = 0;
465     preview.wszFileName = wszFileName;
466     preview_bar_show(hMainWnd, TRUE);
467 }
468
469 void close_preview(HWND hMainWnd)
470 {
471     preview.window.right = 0;
472     preview.window.bottom = 0;
473     preview.page = 0;
474     preview.pages = 0;
475
476     preview_bar_show(hMainWnd, FALSE);
477 }
478
479 BOOL preview_isactive(void)
480 {
481     return preview.page != 0;
482 }
483
484 static void add_ruler_units(HDC hdcRuler, RECT* drawRect, BOOL NewMetrics, long EditLeftmost)
485 {
486     static HDC hdc;
487
488     if(NewMetrics)
489     {
490         static HBITMAP hBitmap;
491         int i, x, y, RulerTextEnd;
492         int CmPixels;
493         int QuarterCmPixels;
494         HFONT hFont;
495         WCHAR FontName[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0};
496
497         if(hdc)
498         {
499             DeleteDC(hdc);
500             DeleteObject(hBitmap);
501         }
502
503         hdc = CreateCompatibleDC(0);
504
505         CmPixels = twips_to_pixels(TWIPS_PER_CM, GetDeviceCaps(hdc, LOGPIXELSX));
506         QuarterCmPixels = (int)((float)CmPixels / 4.0);
507
508         hBitmap = CreateCompatibleBitmap(hdc, drawRect->right, drawRect->bottom);
509         SelectObject(hdc, hBitmap);
510         FillRect(hdc, drawRect, GetStockObject(WHITE_BRUSH));
511
512         hFont = CreateFontW(10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FontName);
513
514         SelectObject(hdc, hFont);
515         SetBkMode(hdc, TRANSPARENT);
516         SetTextAlign(hdc, TA_CENTER);
517         y = (int)(((float)drawRect->bottom - (float)drawRect->top) / 2.0) + 1;
518         RulerTextEnd = drawRect->right - EditLeftmost + 1;
519         for(i = 1, x = EditLeftmost; x < (drawRect->right - EditLeftmost + 1); i ++)
520         {
521             WCHAR str[3];
522             WCHAR format[] = {'%','d',0};
523             int x2 = x;
524
525             x2 += QuarterCmPixels;
526             if(x2 > RulerTextEnd)
527                 break;
528
529             MoveToEx(hdc, x2, y, NULL);
530             LineTo(hdc, x2, y+2);
531
532             x2 += QuarterCmPixels;
533             if(x2 > RulerTextEnd)
534                 break;
535
536             MoveToEx(hdc, x2, y - 3, NULL);
537             LineTo(hdc, x2, y + 3);
538
539             x2 += QuarterCmPixels;
540             if(x2 > RulerTextEnd)
541                 break;
542
543             MoveToEx(hdc, x2, y, NULL);
544             LineTo(hdc, x2, y+2);
545
546             x += CmPixels;
547             if(x > RulerTextEnd)
548                 break;
549
550             wsprintfW(str, format, i);
551             TextOutW(hdc, x, 5, str, lstrlenW(str));
552         }
553         DeleteObject(hFont);
554     }
555
556     BitBlt(hdcRuler, 0, 0, drawRect->right, drawRect->bottom, hdc, 0, 0, SRCAND);
557 }
558
559 static void paint_ruler(HWND hWnd, long EditLeftmost, BOOL NewMetrics)
560 {
561     PAINTSTRUCT ps;
562     HDC hdc = BeginPaint(hWnd, &ps);
563     HDC hdcPrint = make_dc();
564     RECT printRect = get_print_rect(hdcPrint);
565     RECT drawRect;
566     HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
567
568     GetClientRect(hWnd, &drawRect);
569     FillRect(hdc, &drawRect, hBrush);
570
571     drawRect.top += 3;
572     drawRect.bottom -= 3;
573     drawRect.left = EditLeftmost;
574     drawRect.right = twips_to_pixels(printRect.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
575     FillRect(hdc, &drawRect, GetStockObject(WHITE_BRUSH));
576
577     drawRect.top--;
578     drawRect.bottom++;
579     DrawEdge(hdc, &drawRect, EDGE_SUNKEN, BF_RECT);
580
581     drawRect.left = drawRect.right - 1;
582     drawRect.right = twips_to_pixels(printRect.right + margins.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
583     DrawEdge(hdc, &drawRect, EDGE_ETCHED, BF_RECT);
584
585     drawRect.left = 0;
586     drawRect.top = 0;
587     add_ruler_units(hdc, &drawRect, NewMetrics, EditLeftmost);
588
589     SelectObject(hdc, GetStockObject(BLACK_BRUSH));
590     DeleteObject(hBrush);
591     DeleteDC(hdcPrint);
592     EndPaint(hWnd, &ps);
593 }
594
595 LRESULT CALLBACK ruler_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
596 {
597     static WNDPROC pPrevRulerProc;
598     static long EditLeftmost;
599     static BOOL NewMetrics;
600
601     switch(msg)
602     {
603         case WM_USER:
604             if(wParam)
605             {
606                 EditLeftmost = ((POINTL*)wParam)->x;
607                 pPrevRulerProc = (WNDPROC)lParam;
608             }
609             NewMetrics = TRUE;
610             break;
611
612         case WM_PAINT:
613             paint_ruler(hWnd, EditLeftmost, NewMetrics);
614             break;
615
616         default:
617             return CallWindowProcW(pPrevRulerProc, hWnd, msg, wParam, lParam);
618     }
619
620     return 0;
621 }
622
623 void draw_preview_page(HDC hdc, HDC* hdcSized, FORMATRANGE* lpFr, float ratio, int bmNewWidth, int bmNewHeight, int bmWidth, int bmHeight)
624 {
625     HBITMAP hBitmapScaled = CreateCompatibleBitmap(hdc, bmNewWidth, bmNewHeight);
626     HPEN hPen;
627     int TopMargin = (int)((float)twips_to_pixels(lpFr->rc.top, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
628     int BottomMargin = (int)((float)twips_to_pixels(lpFr->rc.bottom, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
629     int LeftMargin = (int)((float)twips_to_pixels(lpFr->rc.left, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
630     int RightMargin = (int)((float)twips_to_pixels(lpFr->rc.right, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
631
632     if(*hdcSized)
633         DeleteDC(*hdcSized);
634     *hdcSized = CreateCompatibleDC(hdc);
635     SelectObject(*hdcSized, hBitmapScaled);
636
637     StretchBlt(*hdcSized, 0, 0, bmNewWidth, bmNewHeight, hdc, 0, 0, bmWidth, bmHeight, SRCCOPY);
638
639     /* Draw margin lines */
640     hPen = CreatePen(PS_DOT, 1, RGB(0,0,0));
641     SelectObject(*hdcSized, hPen);
642
643     MoveToEx(*hdcSized, 0, TopMargin, NULL);
644     LineTo(*hdcSized, bmNewWidth, TopMargin);
645     MoveToEx(*hdcSized, 0, BottomMargin, NULL);
646     LineTo(*hdcSized, bmNewWidth, BottomMargin);
647
648     MoveToEx(*hdcSized, LeftMargin, 0, NULL);
649     LineTo(*hdcSized, LeftMargin, bmNewHeight);
650     MoveToEx(*hdcSized, RightMargin, 0, NULL);
651     LineTo(*hdcSized, RightMargin, bmNewHeight);
652
653 }
654
655 static void draw_preview(HWND hEditorWnd, FORMATRANGE* lpFr, int bmWidth, int bmHeight, RECT* paper, int page)
656 {
657     HBITMAP hBitmapCapture = CreateCompatibleBitmap(lpFr->hdc, bmWidth, bmHeight);
658
659     char_from_pagenum(hEditorWnd, lpFr, page);
660     SelectObject(lpFr->hdc, hBitmapCapture);
661     FillRect(lpFr->hdc, paper, GetStockObject(WHITE_BRUSH));
662     SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)lpFr);
663     /* EM_FORMATRANGE sets fr.rc to indicate the area printed in, but we want to
664        keep the original for drawing margins */
665     lpFr->rc = get_print_rect(lpFr->hdcTarget);
666     SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
667 }
668
669 LRESULT print_preview(HWND hMainWnd)
670 {
671     FORMATRANGE fr;
672     GETTEXTLENGTHEX gt;
673     HDC hdc;
674     RECT window, background;
675     int bmWidth, bmHeight, bmNewWidth, bmNewHeight;
676     float ratioWidth, ratioHeight, ratio;
677     int xOffset, yOffset;
678     int barheight;
679     float spacing = 20.0;
680     HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
681     PAINTSTRUCT ps;
682
683     hdc = BeginPaint(hMainWnd, &ps);
684     GetClientRect(hMainWnd, &window);
685
686     fr.hdcTarget = make_dc();
687     fr.rc = get_print_rect(fr.hdcTarget);
688     fr.rcPage.left = 0;
689     fr.rcPage.top = 0;
690     fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
691     fr.rcPage.right = fr.rc.right + margins.right;
692
693     bmWidth = twips_to_pixels(fr.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
694     bmHeight = twips_to_pixels(fr.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
695
696     if(!preview.hdc)
697     {
698         RECT paper;
699         HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
700
701         preview.hdc = CreateCompatibleDC(hdc);
702
703         if(preview.hdc2)
704         {
705             if(preview.hdc2 != (HDC)-1)
706                 DeleteDC(preview.hdc2);
707             preview.hdc2 = CreateCompatibleDC(hdc);
708         }
709
710         fr.hdc = preview.hdc;
711         gt.flags = GTL_DEFAULT;
712         gt.codepage = 1200;
713         fr.chrg.cpMin = 0;
714         fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
715
716         paper.left = 0;
717         paper.right = bmWidth;
718         paper.top = 0;
719         paper.bottom = bmHeight;
720
721         if(!preview.pages)
722             preview.pages = get_num_pages(hEditorWnd, fr);
723
724         fr.hdc = preview.hdc;
725         draw_preview(hEditorWnd, &fr, bmWidth, bmHeight, &paper, preview.page);
726
727         if(preview.hdc2)
728         {
729             fr.hdc = preview.hdc2;
730             draw_preview(hEditorWnd, &fr, bmWidth, bmHeight, &paper, preview.page + 1);
731         }
732
733         EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
734         EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE), preview.hdc2 ?
735                                                               (preview.page + 1) < preview.pages :
736                                                               preview.page < preview.pages);
737         EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), preview.pages > 1);
738     }
739
740     barheight = SendMessageW(hReBar, RB_GETBARHEIGHT, 0, 0);
741     ratioHeight = ((float)window.bottom - spacing - (float)barheight) / (float)bmHeight;
742
743     if(preview.hdc2)
744         ratioWidth = ((float)window.right / 2.0 - spacing * 2.0) / (float)bmWidth;
745     else
746         ratioWidth = ((float)window.right - spacing * 3.0) / (float)bmWidth;
747
748     if(ratioWidth > ratioHeight)
749         ratio = ratioHeight;
750     else
751         ratio = ratioWidth;
752
753     bmNewWidth = (int)((float)bmWidth * ratio);
754     bmNewHeight = (int)((float)bmHeight * ratio);
755
756     yOffset = ((window.bottom - bmNewHeight + barheight) / 2);
757
758     if(!preview.hdc2)
759         xOffset = (window.right - bmNewWidth) / 2;
760     else
761         xOffset = (window.right - bmNewWidth * 2) / 2;
762
763     window.top = barheight;
764     FillRect(hdc, &window, GetStockObject(GRAY_BRUSH));
765
766     background.left = xOffset - 2;
767     background.right = xOffset + bmNewWidth + 2;
768     background.top = yOffset - 2;
769     background.bottom = yOffset + bmNewHeight + 2;
770
771     FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
772
773     if(window.right != preview.window.right || window.bottom != preview.window.bottom)
774     {
775         draw_preview_page(preview.hdc, &preview.hdcSized, &fr, ratio, bmNewWidth, bmNewHeight, bmWidth, bmHeight);
776
777         if(preview.hdc2)
778         {
779             background.left += bmNewWidth + spacing;
780             background.right += bmNewWidth + spacing;
781
782             FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
783
784             draw_preview_page(preview.hdc2, &preview.hdcSized2, &fr, ratio, bmNewWidth, bmNewHeight, bmWidth, bmHeight);
785         }
786     }
787
788     BitBlt(hdc, xOffset, yOffset, bmNewWidth, bmNewHeight, preview.hdcSized, 0, 0, SRCCOPY);
789
790     if(preview.hdc2)
791     {
792         BitBlt(hdc, xOffset + bmNewWidth + spacing, yOffset, bmNewWidth, bmNewHeight, preview.hdcSized2, 0, 0, SRCCOPY);
793     }
794
795     DeleteDC(fr.hdcTarget);
796     preview.window = window;
797
798     EndPaint(hMainWnd, &ps);
799
800     return 0;
801 }
802
803 void update_preview(HWND hWnd)
804 {
805     RECT rc;
806
807     DeleteDC(preview.hdc);
808     preview.hdc = 0;
809
810     preview.window.right = 0;
811
812     GetClientRect(hWnd, &rc);
813     rc.top += SendMessageW(GetDlgItem(hWnd, IDC_REBAR), RB_GETBARHEIGHT, 0, 0);
814     InvalidateRect(hWnd, &rc, TRUE);
815 }
816
817 LRESULT preview_command(HWND hWnd, WPARAM wParam, LPARAM lParam)
818 {
819     switch(LOWORD(wParam))
820     {
821         case ID_FILE_EXIT:
822             PostMessageW(hWnd, WM_CLOSE, 0, 0);
823             break;
824
825         case ID_PREVIEW_NEXTPAGE:
826         case ID_PREVIEW_PREVPAGE:
827         {
828             if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
829                 preview.page++;
830             else
831                 preview.page--;
832
833             update_preview(hWnd);
834         }
835         break;
836
837         case ID_PREVIEW_NUMPAGES:
838         {
839             HWND hReBar = GetDlgItem(hWnd, IDC_REBAR);
840             WCHAR name[MAX_STRING_LEN];
841             HINSTANCE hInst = (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE);
842
843             if(preview.hdc2)
844             {
845                 DeleteDC(preview.hdc2);
846                 preview.hdc2 = 0;
847             } else
848             {
849                 if(preview.page == preview.pages)
850                     preview.page--;
851                 preview.hdc2 = (HDC)-1;
852             }
853
854             LoadStringW(hInst, preview.hdc2 ? STRING_PREVIEW_ONEPAGE : STRING_PREVIEW_TWOPAGES,
855                         name, MAX_STRING_LEN);
856
857             SetWindowTextW(GetDlgItem(hReBar, ID_PREVIEW_NUMPAGES), name);
858             update_preview(hWnd);
859         }
860         break;
861
862         case ID_PRINT:
863             dialog_print(hWnd, preview.wszFileName);
864             SendMessageW(hWnd, WM_CLOSE, 0, 0);
865             break;
866     }
867
868     return 0;
869 }