urlmon: Don't create stgmed_obj for binding to object.
[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 hdcSized;
33     RECT window;
34     LPWSTR wszFileName;
35 } previewinfo, *ppreviewinfo;
36
37 static HGLOBAL devMode;
38 static HGLOBAL devNames;
39
40 static RECT margins;
41 static previewinfo preview;
42
43 static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',0};
44
45 static LPWSTR get_print_file_filter(HWND hMainWnd)
46 {
47     static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1];
48     const WCHAR files_prn[] = {'*','.','P','R','N',0};
49     const WCHAR files_all[] = {'*','.','*','\0'};
50     LPWSTR p;
51     HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hMainWnd, GWLP_HINSTANCE);
52
53     p = wszPrintFilter;
54     LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN);
55     p += lstrlenW(p) + 1;
56     lstrcpyW(p, files_prn);
57     p += lstrlenW(p) + 1;
58     LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
59     p += lstrlenW(p) + 1;
60     lstrcpyW(p, files_all);
61     p += lstrlenW(p) + 1;
62     *p = 0;
63
64     return wszPrintFilter;
65 }
66
67 void registry_set_pagemargins(HKEY hKey)
68 {
69     RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT));
70 }
71
72 void registry_read_pagemargins(HKEY hKey)
73 {
74     DWORD size = sizeof(RECT);
75
76     if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins,
77                      &size) != ERROR_SUCCESS || size != sizeof(RECT))
78     {
79         margins.top = 1417;
80         margins.bottom = 1417;
81         margins.left = 1757;
82         margins.right = 1757;
83     }
84 }
85
86 static void AddTextButton(HWND hRebarWnd, int string, int command, int id)
87 {
88     REBARBANDINFOW rb;
89     HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hRebarWnd, GWLP_HINSTANCE);
90     WCHAR text[MAX_STRING_LEN];
91     HWND hButton;
92
93     LoadStringW(hInstance, string, text, MAX_STRING_LEN);
94     hButton = CreateWindowW(WC_BUTTONW, text,
95                             WS_VISIBLE | WS_CHILD, 5, 5, 100, 15,
96                             hRebarWnd, (HMENU)command, hInstance, NULL);
97
98     rb.cbSize = sizeof(rb);
99     rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
100     rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
101     rb.hwndChild = hButton;
102     rb.cyChild = rb.cyMinChild = 22;
103     rb.cx = rb.cxMinChild = 90;
104     rb.cxIdeal = 100;
105     rb.wID = id;
106
107     SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb);
108 }
109
110 static HDC make_dc(void)
111 {
112     if(devNames && devMode)
113     {
114         LPDEVNAMES dn = GlobalLock(devNames);
115         LPDEVMODEW dm = GlobalLock(devMode);
116         HDC ret;
117
118         ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset,
119                          (LPWSTR)dn + dn->wDeviceOffset, 0, dm);
120
121         GlobalUnlock(dn);
122         GlobalUnlock(dm);
123
124         return ret;
125     } else
126     {
127         return 0;
128     }
129 }
130
131 static LONG twips_to_centmm(int twips)
132 {
133     return MulDiv(twips, 1000, TWIPS_PER_CM);
134 }
135
136 LONG centmm_to_twips(int mm)
137 {
138     return MulDiv(mm, TWIPS_PER_CM, 1000);
139 }
140
141 static LONG twips_to_pixels(int twips, int dpi)
142 {
143     float ret = ((float)twips / ((float)TWIPS_PER_CM * 2.54)) * (float)dpi;
144     return (LONG)ret;
145 }
146
147 static LONG devunits_to_twips(int units, int dpi)
148 {
149     float ret = ((float)units / (float)dpi) * (float)TWIPS_PER_CM * 2.54;
150     return (LONG)ret;
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
233     do
234     {
235         page++;
236         fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE,
237                                      (LPARAM)&fr);
238     }
239     while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax);
240
241     return page;
242 }
243
244 static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page)
245 {
246     int i;
247
248     for(i = 1; i <= page; i++)
249     {
250         if(i == page)
251             break;
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_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN4);
435
436         hStatic = CreateWindowW(WC_STATICW, NULL,
437                                 WS_VISIBLE | WS_CHILD, 0, 0, 0, 0,
438                                 hReBar, NULL, NULL, NULL);
439
440         rb.cbSize = sizeof(rb);
441         rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID;
442         rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT;
443         rb.hwndChild = hStatic;
444         rb.cyChild = rb.cyMinChild = 22;
445         rb.cx = rb.cxMinChild = 90;
446         rb.cxIdeal = 100;
447         rb.wID = BANDID_PREVIEW_BUFFER;
448
449         SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb);
450     } else
451     {
452         for(i = 0; i <= PREVIEW_BUTTONS; i++)
453             SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0);
454     }
455 }
456
457 void init_preview(HWND hMainWnd, LPWSTR wszFileName)
458 {
459     preview.page = 1;
460     preview.hdc = 0;
461     preview.wszFileName = wszFileName;
462     preview_bar_show(hMainWnd, TRUE);
463 }
464
465 void close_preview(HWND hMainWnd)
466 {
467     preview.window.right = 0;
468     preview.window.bottom = 0;
469     preview.page = 0;
470     preview.pages = 0;
471
472     preview_bar_show(hMainWnd, FALSE);
473 }
474
475 BOOL preview_isactive(void)
476 {
477     return preview.page != 0;
478 }
479
480 static void add_ruler_units(HDC hdcRuler, RECT* drawRect, BOOL NewMetrics, long EditLeftmost)
481 {
482     static HDC hdc;
483
484     if(NewMetrics)
485     {
486         static HBITMAP hBitmap;
487         int i, x, y, RulerTextEnd;
488         int CmPixels;
489         int QuarterCmPixels;
490         HFONT hFont;
491         WCHAR FontName[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0};
492
493         if(hdc)
494         {
495             DeleteDC(hdc);
496             DeleteObject(hBitmap);
497         }
498
499         hdc =  CreateCompatibleDC(hdc);
500
501         CmPixels = twips_to_pixels(TWIPS_PER_CM, GetDeviceCaps(hdc, LOGPIXELSX));
502         QuarterCmPixels = (int)((float)CmPixels / 4.0);
503
504         hBitmap = CreateCompatibleBitmap(hdc, drawRect->right, drawRect->bottom);
505         SelectObject(hdc, hBitmap);
506         FillRect(hdc, drawRect, GetStockObject(WHITE_BRUSH));
507
508         hFont = CreateFontW(10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FontName);
509
510         SelectObject(hdc, hFont);
511         SetBkMode(hdc, TRANSPARENT);
512         SetTextAlign(hdc, TA_CENTER);
513         y = (int)(((float)drawRect->bottom - (float)drawRect->top) / 2.0) + 1;
514         RulerTextEnd = drawRect->right - EditLeftmost + 1;
515         for(i = 1, x = EditLeftmost; x < (drawRect->right - EditLeftmost + 1); i ++)
516         {
517             WCHAR str[3];
518             WCHAR format[] = {'%','d',0};
519             int x2 = x;
520
521             x2 += QuarterCmPixels;
522             if(x2 > RulerTextEnd)
523                 break;
524
525             MoveToEx(hdc, x2, y, NULL);
526             LineTo(hdc, x2, y+2);
527
528             x2 += QuarterCmPixels;
529             if(x2 > RulerTextEnd)
530                 break;
531
532             MoveToEx(hdc, x2, y - 3, NULL);
533             LineTo(hdc, x2, y + 3);
534
535             x2 += QuarterCmPixels;
536             if(x2 > RulerTextEnd)
537                 break;
538
539             MoveToEx(hdc, x2, y, NULL);
540             LineTo(hdc, x2, y+2);
541
542             x += CmPixels;
543             if(x > RulerTextEnd)
544                 break;
545
546             wsprintfW(str, format, i);
547             TextOutW(hdc, x, 5, str, lstrlenW(str));
548         }
549         DeleteObject(hFont);
550     }
551
552     BitBlt(hdcRuler, 0, 0, drawRect->right, drawRect->bottom, hdc, 0, 0, SRCAND);
553 }
554
555 static void paint_ruler(HWND hWnd, long EditLeftmost, BOOL NewMetrics)
556 {
557     PAINTSTRUCT ps;
558     HDC hdc = BeginPaint(hWnd, &ps);
559     HDC hdcPrint = make_dc();
560     RECT printRect = get_print_rect(hdcPrint);
561     RECT drawRect;
562     HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
563
564     GetClientRect(hWnd, &drawRect);
565     FillRect(hdc, &drawRect, hBrush);
566
567     drawRect.top += 3;
568     drawRect.bottom -= 3;
569     drawRect.left = EditLeftmost;
570     drawRect.right = twips_to_pixels(printRect.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
571     FillRect(hdc, &drawRect, GetStockObject(WHITE_BRUSH));
572
573     drawRect.top--;
574     drawRect.bottom++;
575     DrawEdge(hdc, &drawRect, EDGE_SUNKEN, BF_RECT);
576
577     drawRect.left = drawRect.right - 1;
578     drawRect.right = twips_to_pixels(printRect.right + margins.right - margins.left, GetDeviceCaps(hdc, LOGPIXELSX));
579     DrawEdge(hdc, &drawRect, EDGE_ETCHED, BF_RECT);
580
581     drawRect.left = 0;
582     drawRect.top = 0;
583     add_ruler_units(hdc, &drawRect, NewMetrics, EditLeftmost);
584
585     SelectObject(hdc, GetStockObject(BLACK_BRUSH));
586     DeleteObject(hBrush);
587     DeleteDC(hdcPrint);
588     EndPaint(hWnd, &ps);
589 }
590
591 LRESULT CALLBACK ruler_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
592 {
593     static WNDPROC pPrevRulerProc;
594     static long EditLeftmost;
595     static BOOL NewMetrics;
596
597     switch(msg)
598     {
599         case WM_USER:
600             if(wParam)
601             {
602                 EditLeftmost = ((POINTL*)wParam)->x;
603                 pPrevRulerProc = (WNDPROC)lParam;
604             }
605             NewMetrics = TRUE;
606             break;
607
608         case WM_PAINT:
609             paint_ruler(hWnd, EditLeftmost, NewMetrics);
610             break;
611
612         default:
613             return CallWindowProcW(pPrevRulerProc, hWnd, msg, wParam, lParam);
614     }
615
616     return 0;
617 }
618
619 LRESULT print_preview(HWND hMainWnd)
620 {
621     FORMATRANGE fr;
622     GETTEXTLENGTHEX gt;
623     HDC hdc;
624     RECT window, background;
625     HBITMAP hBitmapCapture, hBitmapScaled;
626     int bmWidth, bmHeight, bmNewWidth, bmNewHeight;
627     float ratioWidth, ratioHeight, ratio;
628     int xOffset, yOffset;
629     int barheight;
630     HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
631     PAINTSTRUCT ps;
632
633     hdc = BeginPaint(hMainWnd, &ps);
634     GetClientRect(hMainWnd, &window);
635
636     fr.hdcTarget = make_dc();
637     fr.rc = get_print_rect(fr.hdcTarget);
638     fr.rcPage.left = 0;
639     fr.rcPage.top = 0;
640     fr.rcPage.bottom = fr.rc.bottom + margins.bottom;
641     fr.rcPage.right = fr.rc.right + margins.right;
642
643     bmWidth = twips_to_pixels(fr.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX));
644     bmHeight = twips_to_pixels(fr.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY));
645
646     hBitmapCapture = CreateCompatibleBitmap(hdc, bmWidth, bmHeight);
647
648     if(!preview.hdc)
649     {
650         RECT paper;
651         HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
652
653         preview.hdc = CreateCompatibleDC(hdc);
654         fr.hdc = preview.hdc;
655         gt.flags = GTL_DEFAULT;
656         gt.codepage = 1200;
657         fr.chrg.cpMin = 0;
658         fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gt, 0);
659
660         paper.left = 0;
661         paper.right = bmWidth;
662         paper.top = 0;
663         paper.bottom = bmHeight;
664
665         if(!preview.pages)
666             preview.pages = get_num_pages(hEditorWnd, fr);
667
668         SelectObject(preview.hdc, hBitmapCapture);
669
670         char_from_pagenum(hEditorWnd, &fr, preview.page);
671
672         FillRect(preview.hdc, &paper, GetStockObject(WHITE_BRUSH));
673         SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
674         SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0);
675
676         EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1);
677         EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE), preview.page < preview.pages);
678     }
679
680     barheight = SendMessageW(hReBar, RB_GETBARHEIGHT, 0, 0);
681     ratioWidth = ((float)window.right - 20.0) / (float)bmHeight;
682     ratioHeight = ((float)window.bottom - 20.0 - (float)barheight) / (float)bmHeight;
683
684     if(ratioWidth > ratioHeight)
685         ratio = ratioHeight;
686     else
687         ratio = ratioWidth;
688
689     bmNewWidth = (int)((float)bmWidth * ratio);
690     bmNewHeight = (int)((float)bmHeight * ratio);
691     hBitmapScaled = CreateCompatibleBitmap(hdc, bmNewWidth, bmNewHeight);
692
693     xOffset = ((window.right - bmNewWidth) / 2);
694     yOffset = ((window.bottom - bmNewHeight + barheight) / 2);
695
696     if(window.right != preview.window.right || window.bottom != preview.window.bottom)
697     {
698         HPEN hPen;
699         int TopMargin = (int)((float)twips_to_pixels(fr.rc.top, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
700         int BottomMargin = (int)((float)twips_to_pixels(fr.rc.bottom, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio);
701         int LeftMargin = (int)((float)twips_to_pixels(fr.rc.left, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
702         int RightMargin = (int)((float)twips_to_pixels(fr.rc.right, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio);
703
704         DeleteDC(preview.hdcSized);
705         preview.hdcSized = CreateCompatibleDC(hdc);
706         SelectObject(preview.hdcSized, hBitmapScaled);
707
708         StretchBlt(preview.hdcSized, 0, 0, bmNewWidth, bmNewHeight, preview.hdc, 0, 0, bmWidth, bmHeight, SRCCOPY);
709
710         /* Draw margin lines */
711         hPen = CreatePen(PS_DOT, 1, RGB(0,0,0));
712         SelectObject(preview.hdcSized, hPen);
713
714         MoveToEx(preview.hdcSized, 0, TopMargin, NULL);
715         LineTo(preview.hdcSized, bmNewWidth, TopMargin);
716         MoveToEx(preview.hdcSized, 0, BottomMargin, NULL);
717         LineTo(preview.hdcSized, bmNewWidth, BottomMargin);
718
719         MoveToEx(preview.hdcSized, LeftMargin, 0, NULL);
720         LineTo(preview.hdcSized, LeftMargin, bmNewHeight);
721         MoveToEx(preview.hdcSized, RightMargin, 0, NULL);
722         LineTo(preview.hdcSized, RightMargin, bmNewHeight);
723     }
724
725     window.top = barheight;
726     FillRect(hdc, &window, GetStockObject(GRAY_BRUSH));
727
728     SelectObject(hdc, hBitmapScaled);
729
730     background.left = xOffset - 2;
731     background.right = xOffset + bmNewWidth + 2;
732     background.top = yOffset - 2;
733     background.bottom = yOffset + bmNewHeight + 2;
734
735     FillRect(hdc, &background, GetStockObject(BLACK_BRUSH));
736
737     BitBlt(hdc, xOffset, yOffset, bmNewWidth, bmNewHeight, preview.hdcSized, 0, 0, SRCCOPY);
738
739     DeleteDC(fr.hdcTarget);
740     preview.window = window;
741
742     EndPaint(hMainWnd, &ps);
743
744     return 0;
745 }
746
747 LRESULT preview_command(HWND hWnd, WPARAM wParam, LPARAM lParam)
748 {
749     switch(LOWORD(wParam))
750     {
751         case ID_FILE_EXIT:
752             PostMessageW(hWnd, WM_CLOSE, 0, 0);
753             break;
754
755         case ID_PREVIEW_NEXTPAGE:
756         case ID_PREVIEW_PREVPAGE:
757         {
758             HWND hReBar = GetDlgItem(hWnd, IDC_REBAR);
759             RECT rc;
760
761             if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE)
762                 preview.page++;
763             else
764                 preview.page--;
765
766             preview.hdc = 0;
767             preview.window.right = 0;
768
769             GetClientRect(hWnd, &rc);
770             rc.top += SendMessageW(hReBar, RB_GETBARHEIGHT, 0, 0);
771             InvalidateRect(hWnd, &rc, TRUE);
772         }
773         break;
774
775         case ID_PRINT:
776             dialog_print(hWnd, preview.wszFileName);
777             SendMessageW(hWnd, WM_CLOSE, 0, 0);
778             break;
779     }
780
781     return 0;
782 }