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