]> git.saurik.com Git - wxWidgets.git/blob - src/msw/printwin.cpp
Set up pages range in the wxMSW print dialog correctly.
[wxWidgets.git] / src / msw / printwin.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/printwin.cpp
3 // Purpose: wxWindowsPrinter framework
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 // Don't use the Windows printer if we're in wxUniv mode and using
28 // the PostScript architecture
29 #if wxUSE_PRINTING_ARCHITECTURE && (!defined(__WXUNIVERSAL__) || !wxUSE_POSTSCRIPT_ARCHITECTURE_IN_MSW)
30
31 #ifndef WX_PRECOMP
32 #include "wx/msw/wrapcdlg.h"
33 #include "wx/window.h"
34 #include "wx/msw/private.h"
35 #include "wx/utils.h"
36 #include "wx/dc.h"
37 #include "wx/app.h"
38 #include "wx/msgdlg.h"
39 #include "wx/intl.h"
40 #include "wx/log.h"
41 #include "wx/dcprint.h"
42 #include "wx/dcmemory.h"
43 #include "wx/image.h"
44 #endif
45
46 #include "wx/msw/dib.h"
47 #include "wx/msw/dcmemory.h"
48 #include "wx/msw/printwin.h"
49 #include "wx/msw/printdlg.h"
50 #include "wx/msw/private.h"
51 #include "wx/msw/dcprint.h"
52 #if wxUSE_ENH_METAFILE
53 #include "wx/msw/enhmeta.h"
54 #endif
55 #include <stdlib.h>
56
57 // ---------------------------------------------------------------------------
58 // private functions
59 // ---------------------------------------------------------------------------
60
61 BOOL CALLBACK wxAbortProc(HDC hdc, int error);
62
63 // ---------------------------------------------------------------------------
64 // wxWin macros
65 // ---------------------------------------------------------------------------
66
67 IMPLEMENT_DYNAMIC_CLASS(wxWindowsPrinter, wxPrinterBase)
68 IMPLEMENT_CLASS(wxWindowsPrintPreview, wxPrintPreviewBase)
69
70 // ===========================================================================
71 // implementation
72 // ===========================================================================
73
74 // ---------------------------------------------------------------------------
75 // Printer
76 // ---------------------------------------------------------------------------
77
78 wxWindowsPrinter::wxWindowsPrinter(wxPrintDialogData *data)
79 : wxPrinterBase(data)
80 {
81 }
82
83 bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt)
84 {
85 sm_abortIt = false;
86 sm_abortWindow = NULL;
87
88 if (!printout)
89 {
90 sm_lastError = wxPRINTER_ERROR;
91 return false;
92 }
93
94 // Get some parameters from the printout, if defined
95 int fromPage, toPage;
96 int minPage, maxPage;
97 printout->GetPageInfo(&minPage, &maxPage, &fromPage, &toPage);
98
99 m_printDialogData.SetFromPage(fromPage);
100 m_printDialogData.SetToPage(toPage);
101 m_printDialogData.SetMinPage(minPage);
102 m_printDialogData.SetMaxPage(maxPage);
103 m_printDialogData.SetAllPages((fromPage == minPage) && (toPage == maxPage));
104
105 // Create a suitable device context
106 wxPrinterDC *dc wxDUMMY_INITIALIZE(NULL);
107 if (prompt)
108 {
109 dc = wxDynamicCast(PrintDialog(parent), wxPrinterDC);
110 if (!dc)
111 return false;
112 }
113 else
114 {
115 dc = new wxPrinterDC(m_printDialogData.GetPrintData());
116 }
117
118 // May have pressed cancel.
119 if (!dc || !dc->IsOk())
120 {
121 if (dc) delete dc;
122 return false;
123 }
124
125 wxPrinterDCImpl *impl = (wxPrinterDCImpl*) dc->GetImpl();
126
127 HDC hdc = ::GetDC(NULL);
128 int logPPIScreenX = ::GetDeviceCaps(hdc, LOGPIXELSX);
129 int logPPIScreenY = ::GetDeviceCaps(hdc, LOGPIXELSY);
130 ::ReleaseDC(NULL, hdc);
131
132 int logPPIPrinterX = ::GetDeviceCaps((HDC) impl->GetHDC(), LOGPIXELSX);
133 int logPPIPrinterY = ::GetDeviceCaps((HDC) impl->GetHDC(), LOGPIXELSY);
134 if (logPPIPrinterX == 0 || logPPIPrinterY == 0)
135 {
136 delete dc;
137 sm_lastError = wxPRINTER_ERROR;
138 return false;
139 }
140
141 printout->SetPPIScreen(logPPIScreenX, logPPIScreenY);
142 printout->SetPPIPrinter(logPPIPrinterX, logPPIPrinterY);
143
144 // Set printout parameters
145 printout->SetDC(dc);
146
147 int w, h;
148 dc->GetSize(&w, &h);
149 printout->SetPageSizePixels((int)w, (int)h);
150 printout->SetPaperRectPixels(dc->GetPaperRect());
151
152 dc->GetSizeMM(&w, &h);
153 printout->SetPageSizeMM((int)w, (int)h);
154
155 // Create an abort window
156 wxBusyCursor busyCursor;
157
158 printout->OnPreparePrinting();
159
160 if (maxPage == 0)
161 {
162 sm_lastError = wxPRINTER_ERROR;
163 return false;
164 }
165
166 // Only set min and max, because from and to have been
167 // set by the user
168 m_printDialogData.SetMinPage(minPage);
169 m_printDialogData.SetMaxPage(maxPage);
170
171 wxWindow *win = CreateAbortWindow(parent, printout);
172 wxYield();
173
174 ::SetAbortProc(GetHdcOf(*impl), wxAbortProc);
175
176 if (!win)
177 {
178 wxLogDebug(wxT("Could not create an abort dialog."));
179 sm_lastError = wxPRINTER_ERROR;
180
181 delete dc;
182 return false;
183 }
184 sm_abortWindow = win;
185 sm_abortWindow->Show();
186 wxSafeYield();
187
188 printout->OnBeginPrinting();
189
190 sm_lastError = wxPRINTER_NO_ERROR;
191
192 int minPageNum = minPage, maxPageNum = maxPage;
193
194 if ( !m_printDialogData.GetAllPages() )
195 {
196 minPageNum = m_printDialogData.GetFromPage();
197 maxPageNum = m_printDialogData.GetToPage();
198 }
199
200 int copyCount;
201 for ( copyCount = 1;
202 copyCount <= m_printDialogData.GetNoCopies();
203 copyCount++ )
204 {
205 if ( !printout->OnBeginDocument(minPageNum, maxPageNum) )
206 {
207 wxLogError(_("Could not start printing."));
208 sm_lastError = wxPRINTER_ERROR;
209 break;
210 }
211 if (sm_abortIt)
212 {
213 sm_lastError = wxPRINTER_CANCELLED;
214 break;
215 }
216
217 int pn;
218
219 for ( pn = minPageNum;
220 pn <= maxPageNum && printout->HasPage(pn);
221 pn++ )
222 {
223 if ( sm_abortIt )
224 {
225 sm_lastError = wxPRINTER_CANCELLED;
226 break;
227 }
228
229 dc->StartPage();
230 bool cont = printout->OnPrintPage(pn);
231 dc->EndPage();
232
233 if ( !cont )
234 {
235 sm_lastError = wxPRINTER_CANCELLED;
236 break;
237 }
238 }
239
240 printout->OnEndDocument();
241 }
242
243 printout->OnEndPrinting();
244
245 if (sm_abortWindow)
246 {
247 sm_abortWindow->Show(false);
248 wxDELETE(sm_abortWindow);
249 }
250
251 delete dc;
252
253 return sm_lastError == wxPRINTER_NO_ERROR;
254 }
255
256 wxDC *wxWindowsPrinter::PrintDialog(wxWindow *parent)
257 {
258 wxDC *dc = NULL;
259
260 wxWindowsPrintDialog dialog(parent, & m_printDialogData);
261 int ret = dialog.ShowModal();
262
263 if (ret == wxID_OK)
264 {
265 dc = dialog.GetPrintDC();
266 m_printDialogData = dialog.GetPrintDialogData();
267 if (dc == NULL)
268 sm_lastError = wxPRINTER_ERROR;
269 else
270 sm_lastError = wxPRINTER_NO_ERROR;
271 }
272 else
273 sm_lastError = wxPRINTER_CANCELLED;
274
275 return dc;
276 }
277
278 bool wxWindowsPrinter::Setup(wxWindow *WXUNUSED(parent))
279 {
280 #if 0
281 // We no longer expose that dialog
282 wxPrintDialog dialog(parent, & m_printDialogData);
283 dialog.GetPrintDialogData().SetSetupDialog(true);
284
285 int ret = dialog.ShowModal();
286
287 if (ret == wxID_OK)
288 {
289 m_printDialogData = dialog.GetPrintDialogData();
290 }
291
292 return (ret == wxID_OK);
293 #else
294 return false;
295 #endif
296 }
297
298 /*
299 * Print preview
300 */
301
302 wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout,
303 wxPrintout *printoutForPrinting,
304 wxPrintDialogData *data)
305 : wxPrintPreviewBase(printout, printoutForPrinting, data)
306 {
307 DetermineScaling();
308 }
309
310 wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout,
311 wxPrintout *printoutForPrinting,
312 wxPrintData *data)
313 : wxPrintPreviewBase(printout, printoutForPrinting, data)
314 {
315 DetermineScaling();
316 }
317
318 wxWindowsPrintPreview::~wxWindowsPrintPreview()
319 {
320 }
321
322 bool wxWindowsPrintPreview::Print(bool interactive)
323 {
324 if (!m_printPrintout)
325 return false;
326 wxWindowsPrinter printer(&m_printDialogData);
327 return printer.Print(m_previewFrame, m_printPrintout, interactive);
328 }
329
330 void wxWindowsPrintPreview::DetermineScaling()
331 {
332 ScreenHDC dc;
333 int logPPIScreenX = ::GetDeviceCaps(dc, LOGPIXELSX);
334 int logPPIScreenY = ::GetDeviceCaps(dc, LOGPIXELSY);
335 m_previewPrintout->SetPPIScreen(logPPIScreenX, logPPIScreenY);
336
337 // Get a device context for the currently selected printer
338 wxPrinterDC printerDC(m_printDialogData.GetPrintData());
339
340 int printerWidthMM;
341 int printerHeightMM;
342 int printerXRes;
343 int printerYRes;
344 int logPPIPrinterX;
345 int logPPIPrinterY;
346
347 wxRect paperRect;
348
349 if ( printerDC.IsOk() )
350 {
351 wxPrinterDCImpl *impl = (wxPrinterDCImpl*) printerDC.GetImpl();
352 HDC dc = GetHdcOf(*impl);
353 printerWidthMM = ::GetDeviceCaps(dc, HORZSIZE);
354 printerHeightMM = ::GetDeviceCaps(dc, VERTSIZE);
355 printerXRes = ::GetDeviceCaps(dc, HORZRES);
356 printerYRes = ::GetDeviceCaps(dc, VERTRES);
357 logPPIPrinterX = ::GetDeviceCaps(dc, LOGPIXELSX);
358 logPPIPrinterY = ::GetDeviceCaps(dc, LOGPIXELSY);
359
360 paperRect = printerDC.GetPaperRect();
361
362 if ( logPPIPrinterX == 0 ||
363 logPPIPrinterY == 0 ||
364 printerWidthMM == 0 ||
365 printerHeightMM == 0 )
366 {
367 m_isOk = false;
368 }
369 }
370 else
371 {
372 // use some defaults
373 printerWidthMM = 150;
374 printerHeightMM = 250;
375 printerXRes = 1500;
376 printerYRes = 2500;
377 logPPIPrinterX = 600;
378 logPPIPrinterY = 600;
379
380 paperRect = wxRect(0, 0, printerXRes, printerYRes);
381 m_isOk = false;
382 }
383 m_pageWidth = printerXRes;
384 m_pageHeight = printerYRes;
385 m_previewPrintout->SetPageSizePixels(printerXRes, printerYRes);
386 m_previewPrintout->SetPageSizeMM(printerWidthMM, printerHeightMM);
387 m_previewPrintout->SetPaperRectPixels(paperRect);
388 m_previewPrintout->SetPPIPrinter(logPPIPrinterX, logPPIPrinterY);
389
390 // At 100%, the page should look about page-size on the screen.
391 m_previewScaleX = float(logPPIScreenX) / logPPIPrinterX;
392 m_previewScaleY = float(logPPIScreenY) / logPPIPrinterY;
393 }
394
395 #if wxUSE_ENH_METAFILE
396 bool wxWindowsPrintPreview::RenderPageIntoBitmap(wxBitmap& bmp, int pageNum)
397 {
398 // The preview, as implemented in wxPrintPreviewBase (and as used prior to
399 // wx3) is inexact: it uses screen DC, which has much lower resolution and
400 // has other properties different from printer DC, so the preview is not
401 // quite right.
402 //
403 // To make matters worse, if the application depends heavily on
404 // GetTextExtent() or does text layout itself, the output in preview and on
405 // paper can be very different. In particular, wxHtmlEasyPrinting is
406 // affected and the preview can be easily off by several pages.
407 //
408 // To fix this, we render the preview into high-resolution enhanced
409 // metafile with properties identical to the printer DC. This guarantees
410 // metrics correctness while still being fast.
411
412
413 // print the preview into a metafile:
414 wxPrinterDC printerDC(m_printDialogData.GetPrintData());
415 wxEnhMetaFileDC metaDC(printerDC,
416 wxEmptyString,
417 printerDC.GetSize().x, printerDC.GetSize().y);
418
419 if ( !RenderPageIntoDC(metaDC, pageNum) )
420 return false;
421
422 wxEnhMetaFile *metafile = metaDC.Close();
423 if ( !metafile )
424 return false;
425
426 // now render the metafile:
427 wxMemoryDC bmpDC;
428 bmpDC.SelectObject(bmp);
429 bmpDC.Clear();
430
431 wxRect outRect(0, 0, bmp.GetWidth(), bmp.GetHeight());
432 metafile->Play(&bmpDC, &outRect);
433
434
435 delete metafile;
436
437 // TODO: we should keep the metafile and reuse it when changing zoom level
438
439 return true;
440 }
441 #endif // wxUSE_ENH_METAFILE
442
443 BOOL CALLBACK wxAbortProc(HDC WXUNUSED(hdc), int WXUNUSED(error))
444 {
445 MSG msg;
446
447 if (!wxPrinterBase::sm_abortWindow) /* If the abort dialog isn't up yet */
448 return(TRUE);
449
450 /* Process messages intended for the abort dialog box */
451
452 while (!wxPrinterBase::sm_abortIt && ::PeekMessage(&msg, 0, 0, 0, TRUE))
453 if (!IsDialogMessage((HWND) wxPrinterBase::sm_abortWindow->GetHWND(), &msg)) {
454 TranslateMessage(&msg);
455 DispatchMessage(&msg);
456 }
457
458 /* bAbort is TRUE (return is FALSE) if the user has aborted */
459
460 return !wxPrinterBase::sm_abortIt;
461 }
462
463 #endif
464 // wxUSE_PRINTING_ARCHITECTURE