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