]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
don't log erroneous error messages when writing an empty string to a wxTE_RICH2 control
[wxWidgets.git] / src / msw / dc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dc.cpp
3 // Purpose: wxDC class
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "dc.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/window.h"
33 #include "wx/dc.h"
34 #include "wx/utils.h"
35 #include "wx/dialog.h"
36 #include "wx/app.h"
37 #include "wx/bitmap.h"
38 #include "wx/dcmemory.h"
39 #include "wx/log.h"
40 #include "wx/icon.h"
41 #endif
42
43 #include "wx/sysopt.h"
44 #include "wx/dcprint.h"
45 #include "wx/module.h"
46
47 #include <string.h>
48 #include <math.h>
49
50 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
51
52 #if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__)
53 #include <commdlg.h>
54 #endif
55
56 #ifndef __WIN32__
57 #include <print.h>
58 #endif
59
60 /* Quaternary raster codes */
61 #ifndef MAKEROP4
62 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
63 #endif
64
65 IMPLEMENT_ABSTRACT_CLASS(wxDC, wxDCBase)
66
67 // ---------------------------------------------------------------------------
68 // constants
69 // ---------------------------------------------------------------------------
70
71 static const int VIEWPORT_EXTENT = 1000;
72
73 static const int MM_POINTS = 9;
74 static const int MM_METRIC = 10;
75
76 // usually this is defined in math.h
77 #ifndef M_PI
78 static const double M_PI = 3.14159265358979323846;
79 #endif // M_PI
80
81 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
82 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
83 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
84
85 // ----------------------------------------------------------------------------
86 // macros for logical <-> device coords conversion
87 // ----------------------------------------------------------------------------
88
89 /*
90 We currently let Windows do all the translations itself so these macros are
91 not really needed (any more) but keep them to enhance readability of the
92 code by allowing to see where are the logical and where are the device
93 coordinates used.
94 */
95
96 // logical to device
97 #define XLOG2DEV(x) (x)
98 #define YLOG2DEV(y) (y)
99
100 // device to logical
101 #define XDEV2LOG(x) (x)
102 #define YDEV2LOG(y) (y)
103
104 // ---------------------------------------------------------------------------
105 // private functions
106 // ---------------------------------------------------------------------------
107
108 // convert degrees to radians
109 static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
110
111 // ----------------------------------------------------------------------------
112 // private classes
113 // ----------------------------------------------------------------------------
114
115 // instead of duplicating the same code which sets and then restores text
116 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
117 // encapsulate this in a small helper class
118
119 // wxColourChanger: changes the text colours in the ctor if required and
120 // restores them in the dtor
121 class wxColourChanger
122 {
123 public:
124 wxColourChanger(wxDC& dc);
125 ~wxColourChanger();
126
127 private:
128 wxDC& m_dc;
129
130 COLORREF m_colFgOld, m_colBgOld;
131
132 bool m_changed;
133 };
134
135 // this class saves the old stretch blit mode during its life time
136 class StretchBltModeChanger
137 {
138 public:
139 StretchBltModeChanger(HDC hdc, int mode)
140 : m_hdc(hdc)
141 {
142 m_modeOld = ::SetStretchBltMode(m_hdc, mode);
143 if ( !m_modeOld )
144 wxLogLastError(_T("SetStretchBltMode"));
145 }
146
147 ~StretchBltModeChanger()
148 {
149 if ( !::SetStretchBltMode(m_hdc, m_modeOld) )
150 wxLogLastError(_T("SetStretchBltMode"));
151 }
152
153 private:
154 const HDC m_hdc;
155
156 int m_modeOld;
157 };
158
159 // ===========================================================================
160 // implementation
161 // ===========================================================================
162
163 // ----------------------------------------------------------------------------
164 // wxColourChanger
165 // ----------------------------------------------------------------------------
166
167 wxColourChanger::wxColourChanger(wxDC& dc) : m_dc(dc)
168 {
169 const wxBrush& brush = dc.GetBrush();
170 if ( brush.Ok() && brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE )
171 {
172 HDC hdc = GetHdcOf(dc);
173 m_colFgOld = ::GetTextColor(hdc);
174 m_colBgOld = ::GetBkColor(hdc);
175
176 // note that Windows convention is opposite to wxWindows one, this is
177 // why text colour becomes the background one and vice versa
178 const wxColour& colFg = dc.GetTextForeground();
179 if ( colFg.Ok() )
180 {
181 ::SetBkColor(hdc, colFg.GetPixel());
182 }
183
184 const wxColour& colBg = dc.GetTextBackground();
185 if ( colBg.Ok() )
186 {
187 ::SetTextColor(hdc, colBg.GetPixel());
188 }
189
190 SetBkMode(hdc,
191 dc.GetBackgroundMode() == wxTRANSPARENT ? TRANSPARENT
192 : OPAQUE);
193
194 // flag which telsl us to undo changes in the dtor
195 m_changed = TRUE;
196 }
197 else
198 {
199 // nothing done, nothing to undo
200 m_changed = FALSE;
201 }
202 }
203
204 wxColourChanger::~wxColourChanger()
205 {
206 if ( m_changed )
207 {
208 // restore the colours we changed
209 HDC hdc = GetHdcOf(m_dc);
210
211 ::SetBkMode(hdc, TRANSPARENT);
212 ::SetTextColor(hdc, m_colFgOld);
213 ::SetBkColor(hdc, m_colBgOld);
214 }
215 }
216
217 // ---------------------------------------------------------------------------
218 // wxDC
219 // ---------------------------------------------------------------------------
220
221 // Default constructor
222 wxDC::wxDC()
223 {
224 m_canvas = NULL;
225
226 m_oldBitmap = 0;
227 m_oldPen = 0;
228 m_oldBrush = 0;
229 m_oldFont = 0;
230 #if wxUSE_PALETTE
231 m_oldPalette = 0;
232 #endif // wxUSE_PALETTE
233
234 m_bOwnsDC = FALSE;
235 m_hDC = 0;
236 }
237
238 wxDC::~wxDC()
239 {
240 if ( m_hDC != 0 )
241 {
242 SelectOldObjects(m_hDC);
243
244 // if we own the HDC, we delete it, otherwise we just release it
245
246 if ( m_bOwnsDC )
247 {
248 ::DeleteDC(GetHdc());
249 }
250 else // we don't own our HDC
251 {
252 if (m_canvas)
253 {
254 ::ReleaseDC(GetHwndOf(m_canvas), GetHdc());
255 }
256 else
257 {
258 // Must have been a wxScreenDC
259 ::ReleaseDC((HWND) NULL, GetHdc());
260 }
261 }
262 }
263 }
264
265 // This will select current objects out of the DC,
266 // which is what you have to do before deleting the
267 // DC.
268 void wxDC::SelectOldObjects(WXHDC dc)
269 {
270 if (dc)
271 {
272 if (m_oldBitmap)
273 {
274 ::SelectObject((HDC) dc, (HBITMAP) m_oldBitmap);
275 if (m_selectedBitmap.Ok())
276 {
277 m_selectedBitmap.SetSelectedInto(NULL);
278 }
279 }
280 m_oldBitmap = 0;
281 if (m_oldPen)
282 {
283 ::SelectObject((HDC) dc, (HPEN) m_oldPen);
284 }
285 m_oldPen = 0;
286 if (m_oldBrush)
287 {
288 ::SelectObject((HDC) dc, (HBRUSH) m_oldBrush);
289 }
290 m_oldBrush = 0;
291 if (m_oldFont)
292 {
293 ::SelectObject((HDC) dc, (HFONT) m_oldFont);
294 }
295 m_oldFont = 0;
296
297 #if wxUSE_PALETTE
298 if (m_oldPalette)
299 {
300 ::SelectPalette((HDC) dc, (HPALETTE) m_oldPalette, FALSE);
301 }
302 m_oldPalette = 0;
303 #endif // wxUSE_PALETTE
304 }
305
306 m_brush = wxNullBrush;
307 m_pen = wxNullPen;
308 #if wxUSE_PALETTE
309 m_palette = wxNullPalette;
310 #endif // wxUSE_PALETTE
311 m_font = wxNullFont;
312 m_backgroundBrush = wxNullBrush;
313 m_selectedBitmap = wxNullBitmap;
314 }
315
316 // ---------------------------------------------------------------------------
317 // clipping
318 // ---------------------------------------------------------------------------
319
320 void wxDC::UpdateClipBox()
321 {
322 #ifdef __WXMICROWIN__
323 if (!GetHDC()) return;
324 #endif
325
326 RECT rect;
327 ::GetClipBox(GetHdc(), &rect);
328
329 m_clipX1 = (wxCoord) XDEV2LOG(rect.left);
330 m_clipY1 = (wxCoord) YDEV2LOG(rect.top);
331 m_clipX2 = (wxCoord) XDEV2LOG(rect.right);
332 m_clipY2 = (wxCoord) YDEV2LOG(rect.bottom);
333 }
334
335 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
336 void wxDC::SetClippingHrgn(WXHRGN hrgn)
337 {
338 wxCHECK_RET( hrgn, wxT("invalid clipping region") );
339
340 #ifdef __WXMICROWIN__
341 if (!GetHdc()) return;
342 #endif // __WXMICROWIN__
343
344 // note that we combine the new clipping region with the existing one: this
345 // is compatible with what the other ports do and is the documented
346 // behaviour now (starting with 2.3.3)
347 #ifdef __WIN16__
348 RECT rectClip;
349 if ( !::GetClipBox(GetHdc(), &rectClip) )
350 return;
351
352 HRGN hrgnDest = ::CreateRectRgn(0, 0, 0, 0);
353 HRGN hrgnClipOld = ::CreateRectRgn(rectClip.left, rectClip.top,
354 rectClip.right, rectClip.bottom);
355
356 if ( ::CombineRgn(hrgnDest, hrgnClipOld, (HRGN)hrgn, RGN_AND) != ERROR )
357 {
358 ::SelectClipRgn(GetHdc(), hrgnDest);
359 }
360
361 ::DeleteObject(hrgnClipOld);
362 ::DeleteObject(hrgnDest);
363 #else // Win32
364 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)hrgn, RGN_AND) == ERROR )
365 {
366 wxLogLastError(_T("ExtSelectClipRgn"));
367
368 return;
369 }
370 #endif // Win16/32
371
372 m_clipping = TRUE;
373
374 UpdateClipBox();
375 }
376
377 void wxDC::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
378 {
379 // the region coords are always the device ones, so do the translation
380 // manually
381 //
382 // FIXME: possible +/-1 error here, to check!
383 HRGN hrgn = ::CreateRectRgn(LogicalToDeviceX(x),
384 LogicalToDeviceY(y),
385 LogicalToDeviceX(x + w),
386 LogicalToDeviceY(y + h));
387 if ( !hrgn )
388 {
389 wxLogLastError(_T("CreateRectRgn"));
390 }
391 else
392 {
393 SetClippingHrgn((WXHRGN)hrgn);
394
395 ::DeleteObject(hrgn);
396 }
397 }
398
399 void wxDC::DoSetClippingRegionAsRegion(const wxRegion& region)
400 {
401 SetClippingHrgn(region.GetHRGN());
402 }
403
404 void wxDC::DestroyClippingRegion()
405 {
406 #ifdef __WXMICROWIN__
407 if (!GetHDC()) return;
408 #endif
409
410 if (m_clipping && m_hDC)
411 {
412 // TODO: this should restore the previous clipping region,
413 // so that OnPaint processing works correctly, and the update
414 // clipping region doesn't get destroyed after the first
415 // DestroyClippingRegion.
416 HRGN rgn = CreateRectRgn(0, 0, 32000, 32000);
417 ::SelectClipRgn(GetHdc(), rgn);
418 ::DeleteObject(rgn);
419 }
420
421 m_clipping = FALSE;
422 }
423
424 // ---------------------------------------------------------------------------
425 // query capabilities
426 // ---------------------------------------------------------------------------
427
428 bool wxDC::CanDrawBitmap() const
429 {
430 return TRUE;
431 }
432
433 bool wxDC::CanGetTextExtent() const
434 {
435 #ifdef __WXMICROWIN__
436 // TODO Extend MicroWindows' GetDeviceCaps function
437 return TRUE;
438 #else
439 // What sort of display is it?
440 int technology = ::GetDeviceCaps(GetHdc(), TECHNOLOGY);
441
442 return (technology == DT_RASDISPLAY) || (technology == DT_RASPRINTER);
443 #endif
444 }
445
446 int wxDC::GetDepth() const
447 {
448 #ifdef __WXMICROWIN__
449 if (!GetHDC()) return 16;
450 #endif
451
452 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
453 }
454
455 // ---------------------------------------------------------------------------
456 // drawing
457 // ---------------------------------------------------------------------------
458
459 void wxDC::Clear()
460 {
461 #ifdef __WXMICROWIN__
462 if (!GetHDC()) return;
463 #endif
464
465 RECT rect;
466 if ( m_canvas )
467 {
468 GetClientRect((HWND) m_canvas->GetHWND(), &rect);
469 }
470 else
471 {
472 // No, I think we should simply ignore this if printing on e.g.
473 // a printer DC.
474 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
475 if (!m_selectedBitmap.Ok())
476 return;
477
478 rect.left = 0; rect.top = 0;
479 rect.right = m_selectedBitmap.GetWidth();
480 rect.bottom = m_selectedBitmap.GetHeight();
481 }
482
483 (void) ::SetMapMode(GetHdc(), MM_TEXT);
484
485 DWORD colour = ::GetBkColor(GetHdc());
486 HBRUSH brush = ::CreateSolidBrush(colour);
487 ::FillRect(GetHdc(), &rect, brush);
488 ::DeleteObject(brush);
489
490 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
491 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
492
493 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
494 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
495 ::SetWindowExtEx(GetHdc(), width, height, NULL);
496 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
497 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
498 }
499
500 bool wxDC::DoFloodFill(wxCoord x, wxCoord y, const wxColour& col, int style)
501 {
502 #ifdef __WXMICROWIN__
503 if (!GetHDC()) return FALSE;
504 #endif
505
506 bool success = (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
507 col.GetPixel(),
508 style == wxFLOOD_SURFACE ? FLOODFILLSURFACE
509 : FLOODFILLBORDER) ) ;
510 if (!success)
511 {
512 // quoting from the MSDN docs:
513 //
514 // Following are some of the reasons this function might fail:
515 //
516 // * The filling could not be completed.
517 // * The specified point has the boundary color specified by the
518 // crColor parameter (if FLOODFILLBORDER was requested).
519 // * The specified point does not have the color specified by
520 // crColor (if FLOODFILLSURFACE was requested)
521 // * The point is outside the clipping region that is, it is not
522 // visible on the device.
523 //
524 wxLogLastError(wxT("ExtFloodFill"));
525 }
526
527 CalcBoundingBox(x, y);
528
529 return success;
530 }
531
532 bool wxDC::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
533 {
534 #ifdef __WXMICROWIN__
535 if (!GetHDC()) return FALSE;
536 #endif
537
538 wxCHECK_MSG( col, FALSE, _T("NULL colour parameter in wxDC::GetPixel") );
539
540 // get the color of the pixel
541 COLORREF pixelcolor = ::GetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y));
542
543 wxRGBToColour(*col, pixelcolor);
544
545 return TRUE;
546 }
547
548 void wxDC::DoCrossHair(wxCoord x, wxCoord y)
549 {
550 #ifdef __WXMICROWIN__
551 if (!GetHDC()) return;
552 #endif
553
554 wxCoord x1 = x-VIEWPORT_EXTENT;
555 wxCoord y1 = y-VIEWPORT_EXTENT;
556 wxCoord x2 = x+VIEWPORT_EXTENT;
557 wxCoord y2 = y+VIEWPORT_EXTENT;
558
559 (void)MoveToEx(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y), NULL);
560 (void)LineTo(GetHdc(), XLOG2DEV(x2), YLOG2DEV(y));
561
562 (void)MoveToEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y1), NULL);
563 (void)LineTo(GetHdc(), XLOG2DEV(x), YLOG2DEV(y2));
564
565 CalcBoundingBox(x1, y1);
566 CalcBoundingBox(x2, y2);
567 }
568
569 void wxDC::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
570 {
571 #ifdef __WXMICROWIN__
572 if (!GetHDC()) return;
573 #endif
574
575 (void)MoveToEx(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y1), NULL);
576 (void)LineTo(GetHdc(), XLOG2DEV(x2), YLOG2DEV(y2));
577
578 CalcBoundingBox(x1, y1);
579 CalcBoundingBox(x2, y2);
580 }
581
582 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
583 // and ending at (x2, y2)
584 void wxDC::DoDrawArc(wxCoord x1, wxCoord y1,
585 wxCoord x2, wxCoord y2,
586 wxCoord xc, wxCoord yc)
587 {
588 #ifdef __WXMICROWIN__
589 if (!GetHDC()) return;
590 #endif
591
592 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
593
594 double dx = xc - x1;
595 double dy = yc - y1;
596 double radius = (double)sqrt(dx*dx+dy*dy);
597 wxCoord r = (wxCoord)radius;
598
599 // treat the special case of full circle separately
600 if ( x1 == x2 && y1 == y2 )
601 {
602 DrawEllipse(xc - r, yc - r, 2*r, 2*r);
603 return;
604 }
605
606 wxCoord xx1 = XLOG2DEV(x1);
607 wxCoord yy1 = YLOG2DEV(y1);
608 wxCoord xx2 = XLOG2DEV(x2);
609 wxCoord yy2 = YLOG2DEV(y2);
610 wxCoord xxc = XLOG2DEV(xc);
611 wxCoord yyc = YLOG2DEV(yc);
612 wxCoord ray = (wxCoord) sqrt(double((xxc-xx1)*(xxc-xx1)+(yyc-yy1)*(yyc-yy1)));
613
614 wxCoord xxx1 = (wxCoord) (xxc-ray);
615 wxCoord yyy1 = (wxCoord) (yyc-ray);
616 wxCoord xxx2 = (wxCoord) (xxc+ray);
617 wxCoord yyy2 = (wxCoord) (yyc+ray);
618
619 if ( m_brush.Ok() && m_brush.GetStyle() != wxTRANSPARENT )
620 {
621 // Have to add 1 to bottom-right corner of rectangle
622 // to make semi-circles look right (crooked line otherwise).
623 // Unfortunately this is not a reliable method, depends
624 // on the size of shape.
625 // TODO: figure out why this happens!
626 Pie(GetHdc(),xxx1,yyy1,xxx2+1,yyy2+1, xx1,yy1,xx2,yy2);
627 }
628 else
629 {
630 Arc(GetHdc(),xxx1,yyy1,xxx2,yyy2, xx1,yy1,xx2,yy2);
631 }
632
633 CalcBoundingBox(xc - r, yc - r);
634 CalcBoundingBox(xc + r, yc + r);
635 }
636
637 void wxDC::DoDrawCheckMark(wxCoord x1, wxCoord y1,
638 wxCoord width, wxCoord height)
639 {
640 #ifdef __WXMICROWIN__
641 if (!GetHDC()) return;
642 #endif
643
644 wxCoord x2 = x1 + width,
645 y2 = y1 + height;
646
647 #if defined(__WIN32__) && !defined(__SC__) && !defined(__WXMICROWIN__)
648 RECT rect;
649 rect.left = x1;
650 rect.top = y1;
651 rect.right = x2;
652 rect.bottom = y2;
653
654 DrawFrameControl(GetHdc(), &rect, DFC_MENU, DFCS_MENUCHECK);
655 #else // Win16
656 // In WIN16, draw a cross
657 HPEN blackPen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
658 HPEN whiteBrush = (HPEN)::GetStockObject(WHITE_BRUSH);
659 HPEN hPenOld = (HPEN)::SelectObject(GetHdc(), blackPen);
660 HPEN hBrushOld = (HPEN)::SelectObject(GetHdc(), whiteBrush);
661 ::SetROP2(GetHdc(), R2_COPYPEN);
662 Rectangle(GetHdc(), x1, y1, x2, y2);
663 MoveToEx(GetHdc(), x1, y1, NULL);
664 LineTo(GetHdc(), x2, y2);
665 MoveToEx(GetHdc(), x2, y1, NULL);
666 LineTo(GetHdc(), x1, y2);
667 ::SelectObject(GetHdc(), hPenOld);
668 ::SelectObject(GetHdc(), hBrushOld);
669 ::DeleteObject(blackPen);
670 #endif // Win32/16
671
672 CalcBoundingBox(x1, y1);
673 CalcBoundingBox(x2, y2);
674 }
675
676 void wxDC::DoDrawPoint(wxCoord x, wxCoord y)
677 {
678 #ifdef __WXMICROWIN__
679 if (!GetHDC()) return;
680 #endif
681
682 COLORREF color = 0x00ffffff;
683 if (m_pen.Ok())
684 {
685 color = m_pen.GetColour().GetPixel();
686 }
687
688 SetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), color);
689
690 CalcBoundingBox(x, y);
691 }
692
693 void wxDC::DoDrawPolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset,int fillStyle)
694 {
695 #ifdef __WXMICROWIN__
696 if (!GetHDC()) return;
697 #endif
698
699 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
700
701 // Do things less efficiently if we have offsets
702 if (xoffset != 0 || yoffset != 0)
703 {
704 POINT *cpoints = new POINT[n];
705 int i;
706 for (i = 0; i < n; i++)
707 {
708 cpoints[i].x = (int)(points[i].x + xoffset);
709 cpoints[i].y = (int)(points[i].y + yoffset);
710
711 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
712 }
713 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
714 (void)Polygon(GetHdc(), cpoints, n);
715 SetPolyFillMode(GetHdc(),prev);
716 delete[] cpoints;
717 }
718 else
719 {
720 int i;
721 for (i = 0; i < n; i++)
722 CalcBoundingBox(points[i].x, points[i].y);
723
724 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
725 (void)Polygon(GetHdc(), (POINT*) points, n);
726 SetPolyFillMode(GetHdc(),prev);
727 }
728 }
729
730 void wxDC::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
731 {
732 #ifdef __WXMICROWIN__
733 if (!GetHDC()) return;
734 #endif
735
736 // Do things less efficiently if we have offsets
737 if (xoffset != 0 || yoffset != 0)
738 {
739 POINT *cpoints = new POINT[n];
740 int i;
741 for (i = 0; i < n; i++)
742 {
743 cpoints[i].x = (int)(points[i].x + xoffset);
744 cpoints[i].y = (int)(points[i].y + yoffset);
745
746 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
747 }
748 (void)Polyline(GetHdc(), cpoints, n);
749 delete[] cpoints;
750 }
751 else
752 {
753 int i;
754 for (i = 0; i < n; i++)
755 CalcBoundingBox(points[i].x, points[i].y);
756
757 (void)Polyline(GetHdc(), (POINT*) points, n);
758 }
759 }
760
761 void wxDC::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
762 {
763 #ifdef __WXMICROWIN__
764 if (!GetHDC()) return;
765 #endif
766
767 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
768
769 wxCoord x2 = x + width;
770 wxCoord y2 = y + height;
771
772 if ((m_logicalFunction == wxCOPY) && (m_pen.GetStyle() == wxTRANSPARENT))
773 {
774 RECT rect;
775 rect.left = XLOG2DEV(x);
776 rect.top = YLOG2DEV(y);
777 rect.right = XLOG2DEV(x2);
778 rect.bottom = YLOG2DEV(y2);
779 (void)FillRect(GetHdc(), &rect, (HBRUSH)m_brush.GetResourceHandle() );
780 }
781 else
782 {
783 // Windows draws the filled rectangles without outline (i.e. drawn with a
784 // transparent pen) one pixel smaller in both directions and we want them
785 // to have the same size regardless of which pen is used - adjust
786
787 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
788 if ( m_pen.GetStyle() == wxTRANSPARENT )
789 {
790 x2++;
791 y2++;
792 }
793
794 (void)Rectangle(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
795 }
796
797
798 CalcBoundingBox(x, y);
799 CalcBoundingBox(x2, y2);
800 }
801
802 void wxDC::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
803 {
804 #ifdef __WXMICROWIN__
805 if (!GetHDC()) return;
806 #endif
807
808 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
809
810 // Now, a negative radius value is interpreted to mean
811 // 'the proportion of the smallest X or Y dimension'
812
813 if (radius < 0.0)
814 {
815 double smallest = 0.0;
816 if (width < height)
817 smallest = width;
818 else
819 smallest = height;
820 radius = (- radius * smallest);
821 }
822
823 wxCoord x2 = (x+width);
824 wxCoord y2 = (y+height);
825
826 // Windows draws the filled rectangles without outline (i.e. drawn with a
827 // transparent pen) one pixel smaller in both directions and we want them
828 // to have the same size regardless of which pen is used - adjust
829 if ( m_pen.GetStyle() == wxTRANSPARENT )
830 {
831 x2++;
832 y2++;
833 }
834
835 (void)RoundRect(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2),
836 YLOG2DEV(y2), (int) (2*XLOG2DEV(radius)), (int)( 2*YLOG2DEV(radius)));
837
838 CalcBoundingBox(x, y);
839 CalcBoundingBox(x2, y2);
840 }
841
842 void wxDC::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
843 {
844 #ifdef __WXMICROWIN__
845 if (!GetHDC()) return;
846 #endif
847
848 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
849
850 wxCoord x2 = (x+width);
851 wxCoord y2 = (y+height);
852
853 (void)Ellipse(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
854
855 CalcBoundingBox(x, y);
856 CalcBoundingBox(x2, y2);
857 }
858
859 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
860 void wxDC::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
861 {
862 #ifdef __WXMICROWIN__
863 if (!GetHDC()) return;
864 #endif
865
866 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
867
868 wxCoord x2 = x + w;
869 wxCoord y2 = y + h;
870
871 int rx1 = XLOG2DEV(x+w/2);
872 int ry1 = YLOG2DEV(y+h/2);
873 int rx2 = rx1;
874 int ry2 = ry1;
875
876 sa = DegToRad(sa);
877 ea = DegToRad(ea);
878
879 rx1 += (int)(100.0 * abs(w) * cos(sa));
880 ry1 -= (int)(100.0 * abs(h) * m_signY * sin(sa));
881 rx2 += (int)(100.0 * abs(w) * cos(ea));
882 ry2 -= (int)(100.0 * abs(h) * m_signY * sin(ea));
883
884 // draw pie with NULL_PEN first and then outline otherwise a line is
885 // drawn from the start and end points to the centre
886 HPEN hpenOld = (HPEN) ::SelectObject(GetHdc(), (HPEN) ::GetStockObject(NULL_PEN));
887 if (m_signY > 0)
888 {
889 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2)+1, YLOG2DEV(y2)+1,
890 rx1, ry1, rx2, ry2);
891 }
892 else
893 {
894 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y)-1, XLOG2DEV(x2)+1, YLOG2DEV(y2),
895 rx1, ry1-1, rx2, ry2-1);
896 }
897
898 ::SelectObject(GetHdc(), hpenOld);
899
900 (void)Arc(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2),
901 rx1, ry1, rx2, ry2);
902
903 CalcBoundingBox(x, y);
904 CalcBoundingBox(x2, y2);
905 }
906
907 void wxDC::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y)
908 {
909 #ifdef __WXMICROWIN__
910 if (!GetHDC()) return;
911 #endif
912
913 wxCHECK_RET( icon.Ok(), wxT("invalid icon in DrawIcon") );
914
915 #ifdef __WIN32__
916 ::DrawIconEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon), icon.GetWidth(), icon.GetHeight(), 0, NULL, DI_NORMAL);
917 #else
918 ::DrawIcon(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon));
919 #endif
920
921 CalcBoundingBox(x, y);
922 CalcBoundingBox(x + icon.GetWidth(), y + icon.GetHeight());
923 }
924
925 void wxDC::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask )
926 {
927 #ifdef __WXMICROWIN__
928 if (!GetHDC()) return;
929 #endif
930
931 wxCHECK_RET( bmp.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
932
933 int width = bmp.GetWidth(),
934 height = bmp.GetHeight();
935
936 HBITMAP hbmpMask = 0;
937
938 #if wxUSE_PALETTE
939 HPALETTE oldPal = 0;
940 #endif // wxUSE_PALETTE
941
942 if ( useMask )
943 {
944 wxMask *mask = bmp.GetMask();
945 if ( mask )
946 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
947
948 if ( !hbmpMask )
949 {
950 // don't give assert here because this would break existing
951 // programs - just silently ignore useMask parameter
952 useMask = FALSE;
953 }
954 }
955 if ( useMask )
956 {
957 #ifdef __WIN32__
958 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
959 // points
960 // On some systems, MaskBlt succeeds yet is much much slower
961 // than the wxWindows fall-back implementation. So we need
962 // to be able to switch this on and off at runtime.
963 bool ok = FALSE;
964 #if wxUSE_SYSTEM_OPTIONS
965 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
966 #endif
967 {
968 HDC cdc = GetHdc();
969 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
970 HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp));
971 #if wxUSE_PALETTE
972 wxPalette *pal = bmp.GetPalette();
973 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
974 {
975 oldPal = ::SelectPalette(hdcMem, GetHpaletteOf(*pal), FALSE);
976 ::RealizePalette(hdcMem);
977 }
978 #endif // wxUSE_PALETTE
979
980 ok = ::MaskBlt(cdc, x, y, width, height,
981 hdcMem, 0, 0,
982 hbmpMask, 0, 0,
983 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
984
985 #if wxUSE_PALETTE
986 if (oldPal)
987 ::SelectPalette(hdcMem, oldPal, FALSE);
988 #endif // wxUSE_PALETTE
989
990 ::SelectObject(hdcMem, hOldBitmap);
991 ::DeleteDC(hdcMem);
992 }
993
994 if ( !ok )
995 #endif // Win32
996 {
997 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
998 // level
999 wxMemoryDC memDC;
1000 memDC.SelectObject(bmp);
1001
1002 Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
1003
1004 memDC.SelectObject(wxNullBitmap);
1005 }
1006 }
1007 else // no mask, just use BitBlt()
1008 {
1009 HDC cdc = GetHdc();
1010 HDC memdc = ::CreateCompatibleDC( cdc );
1011 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1012
1013 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1014
1015 COLORREF old_textground = ::GetTextColor(GetHdc());
1016 COLORREF old_background = ::GetBkColor(GetHdc());
1017 if (m_textForegroundColour.Ok())
1018 {
1019 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1020 }
1021 if (m_textBackgroundColour.Ok())
1022 {
1023 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1024 }
1025
1026 #if wxUSE_PALETTE
1027 wxPalette *pal = bmp.GetPalette();
1028 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1029 {
1030 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
1031 ::RealizePalette(memdc);
1032 }
1033 #endif // wxUSE_PALETTE
1034
1035 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
1036 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
1037
1038 #if wxUSE_PALETTE
1039 if (oldPal)
1040 ::SelectPalette(memdc, oldPal, FALSE);
1041 #endif // wxUSE_PALETTE
1042
1043 ::SelectObject( memdc, hOldBitmap );
1044 ::DeleteDC( memdc );
1045
1046 ::SetTextColor(GetHdc(), old_textground);
1047 ::SetBkColor(GetHdc(), old_background);
1048 }
1049 }
1050
1051 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
1052 {
1053 #ifdef __WXMICROWIN__
1054 if (!GetHDC()) return;
1055 #endif
1056
1057 DrawAnyText(text, x, y);
1058
1059 // update the bounding box
1060 CalcBoundingBox(x, y);
1061
1062 wxCoord w, h;
1063 GetTextExtent(text, &w, &h);
1064 CalcBoundingBox(x + w, y + h);
1065 }
1066
1067 void wxDC::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
1068 {
1069 #ifdef __WXMICROWIN__
1070 if (!GetHDC()) return;
1071 #endif
1072
1073 // prepare for drawing the text
1074 if ( m_textForegroundColour.Ok() )
1075 SetTextColor(GetHdc(), m_textForegroundColour.GetPixel());
1076
1077 DWORD old_background = 0;
1078 if ( m_textBackgroundColour.Ok() )
1079 {
1080 old_background = SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1081 }
1082
1083 SetBkMode(GetHdc(), m_backgroundMode == wxTRANSPARENT ? TRANSPARENT
1084 : OPAQUE);
1085
1086 if ( ::TextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
1087 text.c_str(), text.length()) == 0 )
1088 {
1089 wxLogLastError(wxT("TextOut"));
1090 }
1091
1092 // restore the old parameters (text foreground colour may be left because
1093 // it never is set to anything else, but background should remain
1094 // transparent even if we just drew an opaque string)
1095 if ( m_textBackgroundColour.Ok() )
1096 (void)SetBkColor(GetHdc(), old_background);
1097
1098 SetBkMode(GetHdc(), TRANSPARENT);
1099 }
1100
1101 void wxDC::DoDrawRotatedText(const wxString& text,
1102 wxCoord x, wxCoord y,
1103 double angle)
1104 {
1105 #ifdef __WXMICROWIN__
1106 if (!GetHDC()) return;
1107 #endif
1108
1109 // we test that we have some font because otherwise we should still use the
1110 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1111 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1112 // font for drawing rotated fonts unfortunately)
1113 if ( (angle == 0.0) && m_font.Ok() )
1114 {
1115 DoDrawText(text, x, y);
1116 }
1117 #ifndef __WXMICROWIN__
1118 else
1119 {
1120 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1121 // because it's not TrueType and so can't have non zero
1122 // orientation/escapement under Win9x
1123 wxFont font = m_font.Ok() ? m_font : *wxSWISS_FONT;
1124 HFONT hfont = (HFONT)font.GetResourceHandle();
1125 LOGFONT lf;
1126 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1127 {
1128 wxLogLastError(wxT("GetObject(hfont)"));
1129 }
1130
1131 // GDI wants the angle in tenth of degree
1132 long angle10 = (long)(angle * 10);
1133 lf.lfEscapement = angle10;
1134 lf. lfOrientation = angle10;
1135
1136 hfont = ::CreateFontIndirect(&lf);
1137 if ( !hfont )
1138 {
1139 wxLogLastError(wxT("CreateFont"));
1140 }
1141 else
1142 {
1143 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
1144
1145 DrawAnyText(text, x, y);
1146
1147 (void)::SelectObject(GetHdc(), hfontOld);
1148 (void)::DeleteObject(hfont);
1149 }
1150
1151 // call the bounding box by adding all four vertices of the rectangle
1152 // containing the text to it (simpler and probably not slower than
1153 // determining which of them is really topmost/leftmost/...)
1154 wxCoord w, h;
1155 GetTextExtent(text, &w, &h);
1156
1157 double rad = DegToRad(angle);
1158
1159 // "upper left" and "upper right"
1160 CalcBoundingBox(x, y);
1161 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(h*sin(rad)));
1162
1163 // "bottom left" and "bottom right"
1164 x += (wxCoord)(h*sin(rad));
1165 y += (wxCoord)(h*cos(rad));
1166 CalcBoundingBox(x, y);
1167 CalcBoundingBox(x + wxCoord(h*sin(rad)), y + wxCoord(h*cos(rad)));
1168 }
1169 #endif
1170 }
1171
1172 // ---------------------------------------------------------------------------
1173 // set GDI objects
1174 // ---------------------------------------------------------------------------
1175
1176 #if wxUSE_PALETTE
1177
1178 void wxDC::DoSelectPalette(bool realize)
1179 {
1180 #ifdef __WXMICROWIN__
1181 if (!GetHDC()) return;
1182 #endif
1183
1184 // Set the old object temporarily, in case the assignment deletes an object
1185 // that's not yet selected out.
1186 if (m_oldPalette)
1187 {
1188 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
1189 m_oldPalette = 0;
1190 }
1191
1192 if ( m_palette.Ok() )
1193 {
1194 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1195 GetHpaletteOf(m_palette),
1196 FALSE);
1197 if (!m_oldPalette)
1198 m_oldPalette = (WXHPALETTE) oldPal;
1199
1200 if (realize)
1201 ::RealizePalette(GetHdc());
1202 }
1203 }
1204
1205 void wxDC::SetPalette(const wxPalette& palette)
1206 {
1207 if ( palette.Ok() )
1208 {
1209 m_palette = palette;
1210 DoSelectPalette(TRUE);
1211 }
1212 }
1213
1214 void wxDC::InitializePalette()
1215 {
1216 if ( wxDisplayDepth() <= 8 )
1217 {
1218 // look for any window or parent that has a custom palette. If any has
1219 // one then we need to use it in drawing operations
1220 wxWindow *win = m_canvas->GetAncestorWithCustomPalette();
1221
1222 m_hasCustomPalette = win && win->HasCustomPalette();
1223 if ( m_hasCustomPalette )
1224 {
1225 m_palette = win->GetPalette();
1226
1227 // turn on MSW translation for this palette
1228 DoSelectPalette();
1229 }
1230 }
1231 }
1232
1233 #endif // wxUSE_PALETTE
1234
1235 void wxDC::SetFont(const wxFont& the_font)
1236 {
1237 #ifdef __WXMICROWIN__
1238 if (!GetHDC()) return;
1239 #endif
1240
1241 // Set the old object temporarily, in case the assignment deletes an object
1242 // that's not yet selected out.
1243 if (m_oldFont)
1244 {
1245 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
1246 m_oldFont = 0;
1247 }
1248
1249 m_font = the_font;
1250
1251 if (!the_font.Ok())
1252 {
1253 if (m_oldFont)
1254 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
1255 m_oldFont = 0;
1256 }
1257
1258 if (m_font.Ok() && m_font.GetResourceHandle())
1259 {
1260 HFONT f = (HFONT) ::SelectObject(GetHdc(), (HFONT) m_font.GetResourceHandle());
1261 if (f == (HFONT) NULL)
1262 {
1263 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1264 }
1265 if (!m_oldFont)
1266 m_oldFont = (WXHFONT) f;
1267 }
1268 }
1269
1270 void wxDC::SetPen(const wxPen& pen)
1271 {
1272 #ifdef __WXMICROWIN__
1273 if (!GetHDC()) return;
1274 #endif
1275
1276 // Set the old object temporarily, in case the assignment deletes an object
1277 // that's not yet selected out.
1278 if (m_oldPen)
1279 {
1280 ::SelectObject(GetHdc(), (HPEN) m_oldPen);
1281 m_oldPen = 0;
1282 }
1283
1284 m_pen = pen;
1285
1286 if (!m_pen.Ok())
1287 {
1288 if (m_oldPen)
1289 ::SelectObject(GetHdc(), (HPEN) m_oldPen);
1290 m_oldPen = 0;
1291 }
1292
1293 if (m_pen.Ok())
1294 {
1295 if (m_pen.GetResourceHandle())
1296 {
1297 HPEN p = (HPEN) ::SelectObject(GetHdc(), (HPEN)m_pen.GetResourceHandle());
1298 if (!m_oldPen)
1299 m_oldPen = (WXHPEN) p;
1300 }
1301 }
1302 }
1303
1304 void wxDC::SetBrush(const wxBrush& brush)
1305 {
1306 #ifdef __WXMICROWIN__
1307 if (!GetHDC()) return;
1308 #endif
1309
1310 // Set the old object temporarily, in case the assignment deletes an object
1311 // that's not yet selected out.
1312 if (m_oldBrush)
1313 {
1314 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1315 m_oldBrush = 0;
1316 }
1317
1318 m_brush = brush;
1319
1320 if (!m_brush.Ok())
1321 {
1322 if (m_oldBrush)
1323 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1324 m_oldBrush = 0;
1325 }
1326
1327 if (m_brush.Ok())
1328 {
1329 // to make sure the brush is alligned with the logical coordinates
1330 wxBitmap *stipple = m_brush.GetStipple();
1331 if ( stipple && stipple->Ok() )
1332 {
1333 #ifdef __WIN32__
1334 ::SetBrushOrgEx(GetHdc(),
1335 m_deviceOriginX % stipple->GetWidth(),
1336 m_deviceOriginY % stipple->GetHeight(),
1337 NULL); // don't need previous brush origin
1338 #else
1339 ::SetBrushOrg(GetHdc(),
1340 m_deviceOriginX % stipple->GetWidth(),
1341 m_deviceOriginY % stipple->GetHeight());
1342 #endif
1343 }
1344
1345 if ( m_brush.GetResourceHandle() )
1346 {
1347 HBRUSH b = 0;
1348 b = (HBRUSH) ::SelectObject(GetHdc(), (HBRUSH)m_brush.GetResourceHandle());
1349 if (!m_oldBrush)
1350 m_oldBrush = (WXHBRUSH) b;
1351 }
1352 }
1353 }
1354
1355 void wxDC::SetBackground(const wxBrush& brush)
1356 {
1357 #ifdef __WXMICROWIN__
1358 if (!GetHDC()) return;
1359 #endif
1360
1361 m_backgroundBrush = brush;
1362
1363 if (!m_backgroundBrush.Ok())
1364 return;
1365
1366 if (m_canvas)
1367 {
1368 bool customColours = TRUE;
1369 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1370 // change background colours from the control-panel specified colours.
1371 if (m_canvas->IsKindOf(CLASSINFO(wxWindow)) && ((m_canvas->GetWindowStyleFlag() & wxUSER_COLOURS) != wxUSER_COLOURS))
1372 customColours = FALSE;
1373
1374 if (customColours)
1375 {
1376 if (m_backgroundBrush.GetStyle()==wxTRANSPARENT)
1377 {
1378 m_canvas->SetTransparent(TRUE);
1379 }
1380 else
1381 {
1382 // New behaviour, 10/2/99: setting the background brush of a DC
1383 // doesn't affect the window background colour. However,
1384 // I'm leaving in the transparency setting because it's needed by
1385 // various controls (e.g. wxStaticText) to determine whether to draw
1386 // transparently or not. TODO: maybe this should be a new function
1387 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1388 // parent?
1389 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1390 m_canvas->SetTransparent(FALSE);
1391 }
1392 }
1393 }
1394 COLORREF new_color = m_backgroundBrush.GetColour().GetPixel();
1395 {
1396 (void)SetBkColor(GetHdc(), new_color);
1397 }
1398 }
1399
1400 void wxDC::SetBackgroundMode(int mode)
1401 {
1402 #ifdef __WXMICROWIN__
1403 if (!GetHDC()) return;
1404 #endif
1405
1406 m_backgroundMode = mode;
1407
1408 // SetBackgroundColour now only refers to text background
1409 // and m_backgroundMode is used there
1410 }
1411
1412 void wxDC::SetLogicalFunction(int function)
1413 {
1414 #ifdef __WXMICROWIN__
1415 if (!GetHDC()) return;
1416 #endif
1417
1418 m_logicalFunction = function;
1419
1420 SetRop(m_hDC);
1421 }
1422
1423 void wxDC::SetRop(WXHDC dc)
1424 {
1425 if ( !dc || m_logicalFunction < 0 )
1426 return;
1427
1428 int rop;
1429
1430 switch (m_logicalFunction)
1431 {
1432 case wxCLEAR: rop = R2_BLACK; break;
1433 case wxXOR: rop = R2_XORPEN; break;
1434 case wxINVERT: rop = R2_NOT; break;
1435 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1436 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1437 case wxCOPY: rop = R2_COPYPEN; break;
1438 case wxAND: rop = R2_MASKPEN; break;
1439 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1440 case wxNO_OP: rop = R2_NOP; break;
1441 case wxNOR: rop = R2_NOTMERGEPEN; break;
1442 case wxEQUIV: rop = R2_NOTXORPEN; break;
1443 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1444 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1445 case wxNAND: rop = R2_NOTMASKPEN; break;
1446 case wxOR: rop = R2_MERGEPEN; break;
1447 case wxSET: rop = R2_WHITE; break;
1448
1449 default:
1450 wxFAIL_MSG( wxT("unsupported logical function") );
1451 return;
1452 }
1453
1454 SetROP2(GetHdc(), rop);
1455 }
1456
1457 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1458 {
1459 // We might be previewing, so return TRUE to let it continue.
1460 return TRUE;
1461 }
1462
1463 void wxDC::EndDoc()
1464 {
1465 }
1466
1467 void wxDC::StartPage()
1468 {
1469 }
1470
1471 void wxDC::EndPage()
1472 {
1473 }
1474
1475 // ---------------------------------------------------------------------------
1476 // text metrics
1477 // ---------------------------------------------------------------------------
1478
1479 wxCoord wxDC::GetCharHeight() const
1480 {
1481 #ifdef __WXMICROWIN__
1482 if (!GetHDC()) return 0;
1483 #endif
1484
1485 TEXTMETRIC lpTextMetric;
1486
1487 GetTextMetrics(GetHdc(), &lpTextMetric);
1488
1489 return lpTextMetric.tmHeight;
1490 }
1491
1492 wxCoord wxDC::GetCharWidth() const
1493 {
1494 #ifdef __WXMICROWIN__
1495 if (!GetHDC()) return 0;
1496 #endif
1497
1498 TEXTMETRIC lpTextMetric;
1499
1500 GetTextMetrics(GetHdc(), &lpTextMetric);
1501
1502 return lpTextMetric.tmAveCharWidth;
1503 }
1504
1505 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1506 wxCoord *descent, wxCoord *externalLeading,
1507 wxFont *font) const
1508 {
1509 #ifdef __WXMICROWIN__
1510 if (!GetHDC())
1511 {
1512 if (x) *x = 0;
1513 if (y) *y = 0;
1514 if (descent) *descent = 0;
1515 if (externalLeading) *externalLeading = 0;
1516 return;
1517 }
1518 #endif // __WXMICROWIN__
1519
1520 HFONT hfontOld;
1521 if ( font )
1522 {
1523 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1524
1525 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1526 }
1527 else // don't change the font
1528 {
1529 hfontOld = 0;
1530 }
1531
1532 SIZE sizeRect;
1533 TEXTMETRIC tm;
1534
1535 GetTextExtentPoint(GetHdc(), string, string.length(), &sizeRect);
1536 GetTextMetrics(GetHdc(), &tm);
1537
1538 if (x)
1539 *x = sizeRect.cx;
1540 if (y)
1541 *y = sizeRect.cy;
1542 if (descent)
1543 *descent = tm.tmDescent;
1544 if (externalLeading)
1545 *externalLeading = tm.tmExternalLeading;
1546
1547 if ( hfontOld )
1548 {
1549 ::SelectObject(GetHdc(), hfontOld);
1550 }
1551 }
1552
1553 void wxDC::SetMapMode(int mode)
1554 {
1555 #ifdef __WXMICROWIN__
1556 if (!GetHDC()) return;
1557 #endif
1558
1559 m_mappingMode = mode;
1560
1561 if ( mode == wxMM_TEXT )
1562 {
1563 m_logicalScaleX =
1564 m_logicalScaleY = 1.0;
1565 }
1566 else // need to do some calculations
1567 {
1568 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1569 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1570 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1571 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1572
1573 if ( (mm_width == 0) || (mm_height == 0) )
1574 {
1575 // we can't calculate mm2pixels[XY] then!
1576 return;
1577 }
1578
1579 double mm2pixelsX = pixel_width / mm_width,
1580 mm2pixelsY = pixel_height / mm_height;
1581
1582 switch (mode)
1583 {
1584 case wxMM_TWIPS:
1585 m_logicalScaleX = twips2mm * mm2pixelsX;
1586 m_logicalScaleY = twips2mm * mm2pixelsY;
1587 break;
1588
1589 case wxMM_POINTS:
1590 m_logicalScaleX = pt2mm * mm2pixelsX;
1591 m_logicalScaleY = pt2mm * mm2pixelsY;
1592 break;
1593
1594 case wxMM_METRIC:
1595 m_logicalScaleX = mm2pixelsX;
1596 m_logicalScaleY = mm2pixelsY;
1597 break;
1598
1599 case wxMM_LOMETRIC:
1600 m_logicalScaleX = mm2pixelsX / 10.0;
1601 m_logicalScaleY = mm2pixelsY / 10.0;
1602 break;
1603
1604 default:
1605 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1606 }
1607 }
1608
1609 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1610 // cases we could do with MM_TEXT and in the remaining 0.9% with
1611 // MM_ISOTROPIC (TODO!)
1612 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1613
1614 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1615 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1616
1617 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1618 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1619
1620 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1621 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1622 }
1623
1624 void wxDC::SetUserScale(double x, double y)
1625 {
1626 #ifdef __WXMICROWIN__
1627 if (!GetHDC()) return;
1628 #endif
1629
1630 if ( x == m_userScaleX && y == m_userScaleY )
1631 return;
1632
1633 m_userScaleX = x;
1634 m_userScaleY = y;
1635
1636 SetMapMode(m_mappingMode);
1637 }
1638
1639 void wxDC::SetAxisOrientation(bool xLeftRight, bool yBottomUp)
1640 {
1641 #ifdef __WXMICROWIN__
1642 if (!GetHDC()) return;
1643 #endif
1644
1645 int signX = xLeftRight ? 1 : -1,
1646 signY = yBottomUp ? -1 : 1;
1647
1648 if ( signX != m_signX || signY != m_signY )
1649 {
1650 m_signX = signX;
1651 m_signY = signY;
1652
1653 SetMapMode(m_mappingMode);
1654 }
1655 }
1656
1657 void wxDC::SetSystemScale(double x, double y)
1658 {
1659 #ifdef __WXMICROWIN__
1660 if (!GetHDC()) return;
1661 #endif
1662
1663 if ( x == m_scaleX && y == m_scaleY )
1664 return;
1665
1666 m_scaleX = x;
1667 m_scaleY = y;
1668
1669 SetMapMode(m_mappingMode);
1670 }
1671
1672 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1673 {
1674 #ifdef __WXMICROWIN__
1675 if (!GetHDC()) return;
1676 #endif
1677
1678 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1679 return;
1680
1681 m_logicalOriginX = x;
1682 m_logicalOriginY = y;
1683
1684 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1685 }
1686
1687 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1688 {
1689 #ifdef __WXMICROWIN__
1690 if (!GetHDC()) return;
1691 #endif
1692
1693 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1694 return;
1695
1696 m_deviceOriginX = x;
1697 m_deviceOriginY = y;
1698
1699 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1700 }
1701
1702 // ---------------------------------------------------------------------------
1703 // coordinates transformations
1704 // ---------------------------------------------------------------------------
1705
1706 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1707 {
1708 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1709 }
1710
1711 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1712 {
1713 // axis orientation is not taken into account for conversion of a distance
1714 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1715 }
1716
1717 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1718 {
1719 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1720 }
1721
1722 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1723 {
1724 // axis orientation is not taken into account for conversion of a distance
1725 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1726 }
1727
1728 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1729 {
1730 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1731 }
1732
1733 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1734 {
1735 // axis orientation is not taken into account for conversion of a distance
1736 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1737 }
1738
1739 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1740 {
1741 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1742 }
1743
1744 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1745 {
1746 // axis orientation is not taken into account for conversion of a distance
1747 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1748 }
1749
1750 // ---------------------------------------------------------------------------
1751 // bit blit
1752 // ---------------------------------------------------------------------------
1753
1754 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1755 wxCoord width, wxCoord height,
1756 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1757 int rop, bool useMask,
1758 wxCoord xsrcMask, wxCoord ysrcMask)
1759 {
1760 #ifdef __WXMICROWIN__
1761 if (!GetHDC()) return FALSE;
1762 #endif
1763
1764 wxMask *mask = NULL;
1765 if ( useMask )
1766 {
1767 const wxBitmap& bmp = source->m_selectedBitmap;
1768 mask = bmp.GetMask();
1769
1770 if ( !(bmp.Ok() && mask && mask->GetMaskBitmap()) )
1771 {
1772 // don't give assert here because this would break existing
1773 // programs - just silently ignore useMask parameter
1774 useMask = FALSE;
1775 }
1776 }
1777
1778 if (xsrcMask == -1 && ysrcMask == -1)
1779 {
1780 xsrcMask = xsrc; ysrcMask = ysrc;
1781 }
1782
1783 COLORREF old_textground = ::GetTextColor(GetHdc());
1784 COLORREF old_background = ::GetBkColor(GetHdc());
1785 if (m_textForegroundColour.Ok())
1786 {
1787 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1788 }
1789 if (m_textBackgroundColour.Ok())
1790 {
1791 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1792 }
1793
1794 DWORD dwRop = SRCCOPY;
1795 switch (rop)
1796 {
1797 case wxXOR: dwRop = SRCINVERT; break;
1798 case wxINVERT: dwRop = DSTINVERT; break;
1799 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1800 case wxAND_REVERSE: dwRop = SRCERASE; break;
1801 case wxCLEAR: dwRop = BLACKNESS; break;
1802 case wxSET: dwRop = WHITENESS; break;
1803 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1804 case wxAND: dwRop = SRCAND; break;
1805 case wxOR: dwRop = SRCPAINT; break;
1806 case wxEQUIV: dwRop = 0x00990066; break;
1807 case wxNAND: dwRop = 0x007700E6; break;
1808 case wxAND_INVERT: dwRop = 0x00220326; break;
1809 case wxCOPY: dwRop = SRCCOPY; break;
1810 case wxNO_OP: dwRop = DSTCOPY; break;
1811 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1812 case wxNOR: dwRop = NOTSRCCOPY; break;
1813 default:
1814 wxFAIL_MSG( wxT("unsupported logical function") );
1815 return FALSE;
1816 }
1817
1818 bool success = FALSE;
1819
1820 if (useMask)
1821 {
1822 #ifdef __WIN32__
1823 // we want the part of the image corresponding to the mask to be
1824 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1825 // meaning of fg and bg is inverted which corresponds to wxWin notion
1826 // of the mask which is also contrary to the Windows one)
1827
1828 // On some systems, MaskBlt succeeds yet is much much slower
1829 // than the wxWindows fall-back implementation. So we need
1830 // to be able to switch this on and off at runtime.
1831 #if wxUSE_SYSTEM_OPTIONS
1832 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1833 #endif
1834 {
1835 success = ::MaskBlt
1836 (
1837 GetHdc(),
1838 xdest, ydest, width, height,
1839 GetHdcOf(*source),
1840 xsrc, ysrc,
1841 (HBITMAP)mask->GetMaskBitmap(),
1842 xsrcMask, ysrcMask,
1843 MAKEROP4(dwRop, DSTCOPY)
1844 ) != 0;
1845 }
1846
1847 if ( !success )
1848 #endif // Win32
1849 {
1850 // Blit bitmap with mask
1851 HDC dc_mask ;
1852 HDC dc_buffer ;
1853 HBITMAP buffer_bmap ;
1854
1855 #if wxUSE_DC_CACHEING
1856 // create a temp buffer bitmap and DCs to access it and the mask
1857 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
1858 dc_mask = (HDC) dcCacheEntry1->m_dc;
1859
1860 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
1861 dc_buffer = (HDC) dcCacheEntry2->m_dc;
1862
1863 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
1864 width, height);
1865
1866 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
1867 #else // !wxUSE_DC_CACHEING
1868 // create a temp buffer bitmap and DCs to access it and the mask
1869 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
1870 dc_buffer = ::CreateCompatibleDC(GetHdc());
1871 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
1872 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1873 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
1874 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
1875
1876 // copy dest to buffer
1877 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1878 GetHdc(), xdest, ydest, SRCCOPY) )
1879 {
1880 wxLogLastError(wxT("BitBlt"));
1881 }
1882
1883 // copy src to buffer using selected raster op
1884 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1885 GetHdcOf(*source), xsrc, ysrc, dwRop) )
1886 {
1887 wxLogLastError(wxT("BitBlt"));
1888 }
1889
1890 // set masked area in buffer to BLACK (pixel value 0)
1891 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1892 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1893 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1894 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1895 {
1896 wxLogLastError(wxT("BitBlt"));
1897 }
1898
1899 // set unmasked area in dest to BLACK
1900 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1901 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1902 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
1903 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1904 {
1905 wxLogLastError(wxT("BitBlt"));
1906 }
1907 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
1908 ::SetTextColor(GetHdc(), prevCol);
1909
1910 // OR buffer to dest
1911 success = ::BitBlt(GetHdc(), xdest, ydest,
1912 (int)width, (int)height,
1913 dc_buffer, 0, 0, SRCPAINT) != 0;
1914 if ( !success )
1915 {
1916 wxLogLastError(wxT("BitBlt"));
1917 }
1918
1919 // tidy up temporary DCs and bitmap
1920 ::SelectObject(dc_mask, hOldMaskBitmap);
1921 ::SelectObject(dc_buffer, hOldBufferBitmap);
1922
1923 #if !wxUSE_DC_CACHEING
1924 {
1925 ::DeleteDC(dc_mask);
1926 ::DeleteDC(dc_buffer);
1927 ::DeleteObject(buffer_bmap);
1928 }
1929 #endif
1930 }
1931 }
1932 else // no mask, just BitBlt() it
1933 {
1934 // use StretchBlt() if available
1935 if ( ::GetDeviceCaps(GetHdc(), RASTERCAPS) & RC_STRETCHBLT )
1936 {
1937 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
1938
1939 success = ::StretchBlt
1940 (
1941 GetHdc(),
1942 xdest, ydest, width, height,
1943 GetHdcOf(*source),
1944 xsrc, ysrc, width, height,
1945 dwRop
1946 ) != 0;
1947 }
1948 else
1949 {
1950 success = ::BitBlt
1951 (
1952 GetHdc(),
1953 xdest, ydest,
1954 (int)width, (int)height,
1955 GetHdcOf(*source),
1956 xsrc, ysrc,
1957 dwRop
1958 ) != 0;
1959 }
1960
1961 if ( !success )
1962 {
1963 wxLogLastError(wxT("BitBlt/StretchBlt"));
1964 }
1965 }
1966
1967 ::SetTextColor(GetHdc(), old_textground);
1968 ::SetBkColor(GetHdc(), old_background);
1969
1970 return success;
1971 }
1972
1973 void wxDC::DoGetSize(int *w, int *h) const
1974 {
1975 #ifdef __WXMICROWIN__
1976 if (!GetHDC()) return;
1977 #endif
1978
1979 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
1980 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
1981 }
1982
1983 void wxDC::DoGetSizeMM(int *w, int *h) const
1984 {
1985 #ifdef __WXMICROWIN__
1986 if (!GetHDC()) return;
1987 #endif
1988
1989 // if we implement it in terms of DoGetSize() instead of directly using the
1990 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
1991 // will also work for wxWindowDC and wxClientDC even though their size is
1992 // not the same as the total size of the screen
1993 int wPixels, hPixels;
1994 DoGetSize(&wPixels, &hPixels);
1995
1996 if ( w )
1997 {
1998 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
1999
2000 wxCHECK_RET( wTotal, _T("0 width device?") );
2001
2002 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2003 }
2004
2005 if ( h )
2006 {
2007 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2008
2009 wxCHECK_RET( hTotal, _T("0 height device?") );
2010
2011 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2012 }
2013 }
2014
2015 wxSize wxDC::GetPPI() const
2016 {
2017 #ifdef __WXMICROWIN__
2018 if (!GetHDC()) return wxSize();
2019 #endif
2020
2021 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2022 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2023
2024 return wxSize(x, y);
2025 }
2026
2027 // For use by wxWindows only, unless custom units are required.
2028 void wxDC::SetLogicalScale(double x, double y)
2029 {
2030 #ifdef __WXMICROWIN__
2031 if (!GetHDC()) return;
2032 #endif
2033
2034 m_logicalScaleX = x;
2035 m_logicalScaleY = y;
2036 }
2037
2038 #if wxUSE_DC_CACHEING
2039
2040 /*
2041 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2042 * improve it in due course, either using arrays, or simply storing pointers to one
2043 * entry for the bitmap, and two for the DCs. -- JACS
2044 */
2045
2046 wxList wxDC::sm_bitmapCache;
2047 wxList wxDC::sm_dcCache;
2048
2049 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2050 {
2051 m_bitmap = hBitmap;
2052 m_dc = 0;
2053 m_width = w;
2054 m_height = h;
2055 m_depth = depth;
2056 }
2057
2058 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2059 {
2060 m_bitmap = 0;
2061 m_dc = hDC;
2062 m_width = 0;
2063 m_height = 0;
2064 m_depth = depth;
2065 }
2066
2067 wxDCCacheEntry::~wxDCCacheEntry()
2068 {
2069 if (m_bitmap)
2070 ::DeleteObject((HBITMAP) m_bitmap);
2071 if (m_dc)
2072 ::DeleteDC((HDC) m_dc);
2073 }
2074
2075 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2076 {
2077 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2078 wxNode* node = sm_bitmapCache.First();
2079 while (node)
2080 {
2081 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->Data();
2082
2083 if (entry->m_depth == depth)
2084 {
2085 if (entry->m_width < w || entry->m_height < h)
2086 {
2087 ::DeleteObject((HBITMAP) entry->m_bitmap);
2088 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2089 if ( !entry->m_bitmap)
2090 {
2091 wxLogLastError(wxT("CreateCompatibleBitmap"));
2092 }
2093 entry->m_width = w; entry->m_height = h;
2094 return entry;
2095 }
2096 return entry;
2097 }
2098
2099 node = node->Next();
2100 }
2101 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2102 if ( !hBitmap)
2103 {
2104 wxLogLastError(wxT("CreateCompatibleBitmap"));
2105 }
2106 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2107 AddToBitmapCache(entry);
2108 return entry;
2109 }
2110
2111 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2112 {
2113 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2114 wxNode* node = sm_dcCache.First();
2115 while (node)
2116 {
2117 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->Data();
2118
2119 // Don't return the same one as we already have
2120 if (!notThis || (notThis != entry))
2121 {
2122 if (entry->m_depth == depth)
2123 {
2124 return entry;
2125 }
2126 }
2127
2128 node = node->Next();
2129 }
2130 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2131 if ( !hDC)
2132 {
2133 wxLogLastError(wxT("CreateCompatibleDC"));
2134 }
2135 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2136 AddToDCCache(entry);
2137 return entry;
2138 }
2139
2140 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2141 {
2142 sm_bitmapCache.Append(entry);
2143 }
2144
2145 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2146 {
2147 sm_dcCache.Append(entry);
2148 }
2149
2150 void wxDC::ClearCache()
2151 {
2152 sm_dcCache.DeleteContents(TRUE);
2153 sm_dcCache.Clear();
2154 sm_dcCache.DeleteContents(FALSE);
2155 sm_bitmapCache.DeleteContents(TRUE);
2156 sm_bitmapCache.Clear();
2157 sm_bitmapCache.DeleteContents(FALSE);
2158 }
2159
2160 // Clean up cache at app exit
2161 class wxDCModule : public wxModule
2162 {
2163 public:
2164 virtual bool OnInit() { return TRUE; }
2165 virtual void OnExit() { wxDC::ClearCache(); }
2166
2167 private:
2168 DECLARE_DYNAMIC_CLASS(wxDCModule)
2169 };
2170
2171 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2172
2173 #endif
2174 // wxUSE_DC_CACHEING
2175