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