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