]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/dc.cpp
made GetColourFromGTKWidget() more general, it is now used for all colours
[wxWidgets.git] / src / msw / dc.cpp
... / ...
CommitLineData
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
53 #include <commdlg.h>
54#endif
55
56#ifndef __WIN32__
57 #include <print.h>
58#endif
59
60IMPLEMENT_ABSTRACT_CLASS(wxDC, wxDCBase)
61
62// ---------------------------------------------------------------------------
63// constants
64// ---------------------------------------------------------------------------
65
66static const int VIEWPORT_EXTENT = 1000;
67
68static const int MM_POINTS = 9;
69static 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// private functions
82// ---------------------------------------------------------------------------
83
84// convert degrees to radians
85static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
86
87// ----------------------------------------------------------------------------
88// private classes
89// ----------------------------------------------------------------------------
90
91// instead of duplicating the same code which sets and then restores text
92// colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
93// encapsulate this in a small helper class
94
95// wxColourChanger: changes the text colours in the ctor if required and
96// restores them in the dtor
97class wxColourChanger
98{
99public:
100 wxColourChanger(wxDC& dc);
101 ~wxColourChanger();
102
103private:
104 wxDC& m_dc;
105
106 COLORREF m_colFgOld, m_colBgOld;
107
108 bool m_changed;
109};
110
111// ===========================================================================
112// implementation
113// ===========================================================================
114
115// ----------------------------------------------------------------------------
116// wxColourChanger
117// ----------------------------------------------------------------------------
118
119wxColourChanger::wxColourChanger(wxDC& dc) : m_dc(dc)
120{
121 if ( dc.GetBrush().GetStyle() == wxSTIPPLE_MASK_OPAQUE )
122 {
123 HDC hdc = GetHdcOf(dc);
124 m_colFgOld = ::GetTextColor(hdc);
125 m_colBgOld = ::GetBkColor(hdc);
126
127 // note that Windows convention is opposite to wxWindows one, this is
128 // why text colour becomes the background one and vice versa
129 const wxColour& colFg = dc.GetTextForeground();
130 if ( colFg.Ok() )
131 {
132 ::SetBkColor(hdc, colFg.GetPixel());
133 }
134
135 const wxColour& colBg = dc.GetTextBackground();
136 if ( colBg.Ok() )
137 {
138 ::SetTextColor(hdc, colBg.GetPixel());
139 }
140
141 SetBkMode(hdc,
142 dc.GetBackgroundMode() == wxTRANSPARENT ? TRANSPARENT
143 : OPAQUE);
144
145 // flag which telsl us to undo changes in the dtor
146 m_changed = TRUE;
147 }
148 else
149 {
150 // nothing done, nothing to undo
151 m_changed = FALSE;
152 }
153}
154
155wxColourChanger::~wxColourChanger()
156{
157 if ( m_changed )
158 {
159 // restore the colours we changed
160 HDC hdc = GetHdcOf(m_dc);
161
162 ::SetBkMode(hdc, TRANSPARENT);
163 ::SetTextColor(hdc, m_colFgOld);
164 ::SetBkColor(hdc, m_colBgOld);
165 }
166}
167
168// ---------------------------------------------------------------------------
169// wxDC
170// ---------------------------------------------------------------------------
171
172// Default constructor
173wxDC::wxDC()
174{
175 m_canvas = NULL;
176
177 m_oldBitmap = 0;
178 m_oldPen = 0;
179 m_oldBrush = 0;
180 m_oldFont = 0;
181 m_oldPalette = 0;
182
183 m_bOwnsDC = FALSE;
184 m_hDC = 0;
185
186 m_windowExtX = VIEWPORT_EXTENT;
187 m_windowExtY = VIEWPORT_EXTENT;
188}
189
190
191wxDC::~wxDC()
192{
193 if ( m_hDC != 0 )
194 {
195 SelectOldObjects(m_hDC);
196
197 // if we own the HDC, we delete it, otherwise we just release it
198
199 if ( m_bOwnsDC )
200 {
201 ::DeleteDC(GetHdc());
202 }
203 else // we don't own our HDC
204 {
205 if (m_canvas)
206 {
207 ::ReleaseDC(GetHwndOf(m_canvas), GetHdc());
208 }
209 else
210 {
211 // Must have been a wxScreenDC
212 ::ReleaseDC((HWND) NULL, GetHdc());
213 }
214 }
215 }
216}
217
218// This will select current objects out of the DC,
219// which is what you have to do before deleting the
220// DC.
221void wxDC::SelectOldObjects(WXHDC dc)
222{
223 if (dc)
224 {
225 if (m_oldBitmap)
226 {
227 ::SelectObject((HDC) dc, (HBITMAP) m_oldBitmap);
228 if (m_selectedBitmap.Ok())
229 {
230 m_selectedBitmap.SetSelectedInto(NULL);
231 }
232 }
233 m_oldBitmap = 0;
234 if (m_oldPen)
235 {
236 ::SelectObject((HDC) dc, (HPEN) m_oldPen);
237 }
238 m_oldPen = 0;
239 if (m_oldBrush)
240 {
241 ::SelectObject((HDC) dc, (HBRUSH) m_oldBrush);
242 }
243 m_oldBrush = 0;
244 if (m_oldFont)
245 {
246 ::SelectObject((HDC) dc, (HFONT) m_oldFont);
247 }
248 m_oldFont = 0;
249 if (m_oldPalette)
250 {
251 ::SelectPalette((HDC) dc, (HPALETTE) m_oldPalette, TRUE);
252 }
253 m_oldPalette = 0;
254 }
255
256 m_brush = wxNullBrush;
257 m_pen = wxNullPen;
258 m_palette = wxNullPalette;
259 m_font = wxNullFont;
260 m_backgroundBrush = wxNullBrush;
261 m_selectedBitmap = wxNullBitmap;
262}
263
264// ---------------------------------------------------------------------------
265// clipping
266// ---------------------------------------------------------------------------
267
268void wxDC::UpdateClipBox()
269{
270#ifdef __WXMICROWIN__
271 if (!GetHDC()) return;
272#endif
273
274 RECT rect;
275 GetClipBox(GetHdc(), &rect);
276
277 m_clipX1 = (wxCoord) XDEV2LOG(rect.left);
278 m_clipY1 = (wxCoord) YDEV2LOG(rect.top);
279 m_clipX2 = (wxCoord) XDEV2LOG(rect.right);
280 m_clipY2 = (wxCoord) YDEV2LOG(rect.bottom);
281}
282
283void wxDC::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
284{
285#ifdef __WXMICROWIN__
286 if (!GetHDC()) return;
287#endif
288
289 m_clipping = TRUE;
290
291 // the region coords are always the device ones, so do the translation
292 // manually
293 //
294 // FIXME: possible +/-1 error here, to check!
295 HRGN hrgn = ::CreateRectRgn(LogicalToDeviceX(x),
296 LogicalToDeviceY(y),
297 LogicalToDeviceX(x + w),
298 LogicalToDeviceY(y + h));
299 if ( !hrgn )
300 {
301 wxLogLastError(_T("CreateRectRgn"));
302 }
303 else
304 {
305 if ( ::SelectClipRgn(GetHdc(), hrgn) == ERROR )
306 {
307 wxLogLastError(_T("SelectClipRgn"));
308 }
309 DeleteObject(hrgn);
310
311 UpdateClipBox();
312 }
313}
314
315void wxDC::DoSetClippingRegionAsRegion(const wxRegion& region)
316{
317#ifdef __WXMICROWIN__
318 if (!GetHDC()) return;
319#endif
320
321 wxCHECK_RET( GetHrgnOf(region), wxT("invalid clipping region") );
322
323 m_clipping = TRUE;
324
325#ifdef __WIN16__
326 SelectClipRgn(GetHdc(), GetHrgnOf(region));
327#else // Win32
328 ExtSelectClipRgn(GetHdc(), GetHrgnOf(region), RGN_AND);
329#endif // Win16/32
330
331 UpdateClipBox();
332}
333
334void wxDC::DestroyClippingRegion()
335{
336#ifdef __WXMICROWIN__
337 if (!GetHDC()) return;
338#endif
339
340 if (m_clipping && m_hDC)
341 {
342 // TODO: this should restore the previous clipping region,
343 // so that OnPaint processing works correctly, and the update clipping region
344 // doesn't get destroyed after the first DestroyClippingRegion.
345 HRGN rgn = CreateRectRgn(0, 0, 32000, 32000);
346 SelectClipRgn(GetHdc(), rgn);
347 DeleteObject(rgn);
348 }
349
350 m_clipping = FALSE;
351}
352
353// ---------------------------------------------------------------------------
354// query capabilities
355// ---------------------------------------------------------------------------
356
357bool wxDC::CanDrawBitmap() const
358{
359 return TRUE;
360}
361
362bool wxDC::CanGetTextExtent() const
363{
364#ifdef __WXMICROWIN__
365 // TODO Extend MicroWindows' GetDeviceCaps function
366 return TRUE;
367#else
368 // What sort of display is it?
369 int technology = ::GetDeviceCaps(GetHdc(), TECHNOLOGY);
370
371 return (technology == DT_RASDISPLAY) || (technology == DT_RASPRINTER);
372#endif
373}
374
375int wxDC::GetDepth() const
376{
377#ifdef __WXMICROWIN__
378 if (!GetHDC()) return 16;
379#endif
380
381 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
382}
383
384// ---------------------------------------------------------------------------
385// drawing
386// ---------------------------------------------------------------------------
387
388void wxDC::Clear()
389{
390#ifdef __WXMICROWIN__
391 if (!GetHDC()) return;
392#endif
393
394 RECT rect;
395 if ( m_canvas )
396 {
397 GetClientRect((HWND) m_canvas->GetHWND(), &rect);
398 }
399 else
400 {
401 // No, I think we should simply ignore this if printing on e.g.
402 // a printer DC.
403 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
404 if (!m_selectedBitmap.Ok())
405 return;
406
407 rect.left = 0; rect.top = 0;
408 rect.right = m_selectedBitmap.GetWidth();
409 rect.bottom = m_selectedBitmap.GetHeight();
410 }
411
412 (void) ::SetMapMode(GetHdc(), MM_TEXT);
413
414 DWORD colour = GetBkColor(GetHdc());
415 HBRUSH brush = CreateSolidBrush(colour);
416 FillRect(GetHdc(), &rect, brush);
417 DeleteObject(brush);
418
419 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
420 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
421 ::SetWindowExtEx(GetHdc(), m_windowExtX, m_windowExtY, NULL);
422 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
423 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
424}
425
426void wxDC::DoFloodFill(wxCoord x, wxCoord y, const wxColour& col, int style)
427{
428#ifdef __WXMICROWIN__
429 if (!GetHDC()) return;
430#endif
431
432 if ( !::ExtFloodFill(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
433 col.GetPixel(),
434 style == wxFLOOD_SURFACE ? FLOODFILLSURFACE
435 : FLOODFILLBORDER) )
436 {
437 // quoting from the MSDN docs:
438 //
439 // Following are some of the reasons this function might fail:
440 //
441 // * The filling could not be completed.
442 // * The specified point has the boundary color specified by the
443 // crColor parameter (if FLOODFILLBORDER was requested).
444 // * The specified point does not have the color specified by
445 // crColor (if FLOODFILLSURFACE was requested)
446 // * The point is outside the clipping region that is, it is not
447 // visible on the device.
448 //
449 wxLogLastError(wxT("ExtFloodFill"));
450 }
451
452 CalcBoundingBox(x, y);
453}
454
455bool wxDC::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
456{
457#ifdef __WXMICROWIN__
458 if (!GetHDC()) return FALSE;
459#endif
460
461 wxCHECK_MSG( col, FALSE, _T("NULL colour parameter in wxDC::GetPixel") );
462
463 // get the color of the pixel
464 COLORREF pixelcolor = ::GetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y));
465
466 wxRGBToColour(*col, pixelcolor);
467
468 return TRUE;
469}
470
471void wxDC::DoCrossHair(wxCoord x, wxCoord y)
472{
473#ifdef __WXMICROWIN__
474 if (!GetHDC()) return;
475#endif
476
477 wxCoord x1 = x-VIEWPORT_EXTENT;
478 wxCoord y1 = y-VIEWPORT_EXTENT;
479 wxCoord x2 = x+VIEWPORT_EXTENT;
480 wxCoord y2 = y+VIEWPORT_EXTENT;
481
482 (void)MoveToEx(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y), NULL);
483 (void)LineTo(GetHdc(), XLOG2DEV(x2), YLOG2DEV(y));
484
485 (void)MoveToEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y1), NULL);
486 (void)LineTo(GetHdc(), XLOG2DEV(x), YLOG2DEV(y2));
487
488 CalcBoundingBox(x1, y1);
489 CalcBoundingBox(x2, y2);
490}
491
492void wxDC::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
493{
494#ifdef __WXMICROWIN__
495 if (!GetHDC()) return;
496#endif
497
498 (void)MoveToEx(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y1), NULL);
499 (void)LineTo(GetHdc(), XLOG2DEV(x2), YLOG2DEV(y2));
500
501 // Normalization: Windows doesn't draw the last point of the line.
502 // But apparently neither does GTK+, so we take it out again.
503// (void)LineTo(GetHdc(), XLOG2DEV(x2) + 1, YLOG2DEV(y2));
504
505 CalcBoundingBox(x1, y1);
506 CalcBoundingBox(x2, y2);
507}
508
509// Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
510// and ending at (x2, y2)
511void wxDC::DoDrawArc(wxCoord x1, wxCoord y1,
512 wxCoord x2, wxCoord y2,
513 wxCoord xc, wxCoord yc)
514{
515#ifdef __WXMICROWIN__
516 if (!GetHDC()) return;
517#endif
518
519 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
520
521 double dx = xc - x1;
522 double dy = yc - y1;
523 double radius = (double)sqrt(dx*dx+dy*dy);
524 wxCoord r = (wxCoord)radius;
525
526 // treat the special case of full circle separately
527 if ( x1 == x2 && y1 == y2 )
528 {
529 DrawEllipse(xc - r, yc - r, 2*r, 2*r);
530 return;
531 }
532
533 wxCoord xx1 = XLOG2DEV(x1);
534 wxCoord yy1 = YLOG2DEV(y1);
535 wxCoord xx2 = XLOG2DEV(x2);
536 wxCoord yy2 = YLOG2DEV(y2);
537 wxCoord xxc = XLOG2DEV(xc);
538 wxCoord yyc = YLOG2DEV(yc);
539 wxCoord ray = (wxCoord) sqrt(double((xxc-xx1)*(xxc-xx1)+(yyc-yy1)*(yyc-yy1)));
540
541 wxCoord xxx1 = (wxCoord) (xxc-ray);
542 wxCoord yyy1 = (wxCoord) (yyc-ray);
543 wxCoord xxx2 = (wxCoord) (xxc+ray);
544 wxCoord yyy2 = (wxCoord) (yyc+ray);
545
546 if ( m_brush.Ok() && m_brush.GetStyle() != wxTRANSPARENT )
547 {
548 // Have to add 1 to bottom-right corner of rectangle
549 // to make semi-circles look right (crooked line otherwise).
550 // Unfortunately this is not a reliable method, depends
551 // on the size of shape.
552 // TODO: figure out why this happens!
553 Pie(GetHdc(),xxx1,yyy1,xxx2+1,yyy2+1, xx1,yy1,xx2,yy2);
554 }
555 else
556 {
557 Arc(GetHdc(),xxx1,yyy1,xxx2,yyy2, xx1,yy1,xx2,yy2);
558 }
559
560 CalcBoundingBox(xc - r, yc - r);
561 CalcBoundingBox(xc + r, yc + r);
562}
563
564void wxDC::DoDrawCheckMark(wxCoord x1, wxCoord y1,
565 wxCoord width, wxCoord height)
566{
567#ifdef __WXMICROWIN__
568 if (!GetHDC()) return;
569#endif
570
571 wxCoord x2 = x1 + width,
572 y2 = y1 + height;
573
574#if defined(__WIN32__) && !defined(__SC__) && !defined(__WXMICROWIN__)
575 RECT rect;
576 rect.left = x1;
577 rect.top = y1;
578 rect.right = x2;
579 rect.bottom = y2;
580
581 DrawFrameControl(GetHdc(), &rect, DFC_MENU, DFCS_MENUCHECK);
582#else // Win16
583 // In WIN16, draw a cross
584 HPEN blackPen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
585 HPEN whiteBrush = (HPEN)::GetStockObject(WHITE_BRUSH);
586 HPEN hPenOld = (HPEN)::SelectObject(GetHdc(), blackPen);
587 HPEN hBrushOld = (HPEN)::SelectObject(GetHdc(), whiteBrush);
588 ::SetROP2(GetHdc(), R2_COPYPEN);
589 Rectangle(GetHdc(), x1, y1, x2, y2);
590 MoveToEx(GetHdc(), x1, y1, NULL);
591 LineTo(GetHdc(), x2, y2);
592 MoveToEx(GetHdc(), x2, y1, NULL);
593 LineTo(GetHdc(), x1, y2);
594 ::SelectObject(GetHdc(), hPenOld);
595 ::SelectObject(GetHdc(), hBrushOld);
596 ::DeleteObject(blackPen);
597#endif // Win32/16
598
599 CalcBoundingBox(x1, y1);
600 CalcBoundingBox(x2, y2);
601}
602
603void wxDC::DoDrawPoint(wxCoord x, wxCoord y)
604{
605#ifdef __WXMICROWIN__
606 if (!GetHDC()) return;
607#endif
608
609 COLORREF color = 0x00ffffff;
610 if (m_pen.Ok())
611 {
612 color = m_pen.GetColour().GetPixel();
613 }
614
615 SetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), color);
616
617 CalcBoundingBox(x, y);
618}
619
620void wxDC::DoDrawPolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset,int fillStyle)
621{
622#ifdef __WXMICROWIN__
623 if (!GetHDC()) return;
624#endif
625
626 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
627
628 // Do things less efficiently if we have offsets
629 if (xoffset != 0 || yoffset != 0)
630 {
631 POINT *cpoints = new POINT[n];
632 int i;
633 for (i = 0; i < n; i++)
634 {
635 cpoints[i].x = (int)(points[i].x + xoffset);
636 cpoints[i].y = (int)(points[i].y + yoffset);
637
638 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
639 }
640 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
641 (void)Polygon(GetHdc(), cpoints, n);
642 SetPolyFillMode(GetHdc(),prev);
643 delete[] cpoints;
644 }
645 else
646 {
647 int i;
648 for (i = 0; i < n; i++)
649 CalcBoundingBox(points[i].x, points[i].y);
650
651 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
652 (void)Polygon(GetHdc(), (POINT*) points, n);
653 SetPolyFillMode(GetHdc(),prev);
654 }
655}
656
657void wxDC::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
658{
659#ifdef __WXMICROWIN__
660 if (!GetHDC()) return;
661#endif
662
663 // Do things less efficiently if we have offsets
664 if (xoffset != 0 || yoffset != 0)
665 {
666 POINT *cpoints = new POINT[n];
667 int i;
668 for (i = 0; i < n; i++)
669 {
670 cpoints[i].x = (int)(points[i].x + xoffset);
671 cpoints[i].y = (int)(points[i].y + yoffset);
672
673 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
674 }
675 (void)Polyline(GetHdc(), cpoints, n);
676 delete[] cpoints;
677 }
678 else
679 {
680 int i;
681 for (i = 0; i < n; i++)
682 CalcBoundingBox(points[i].x, points[i].y);
683
684 (void)Polyline(GetHdc(), (POINT*) points, n);
685 }
686}
687
688void wxDC::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
689{
690#ifdef __WXMICROWIN__
691 if (!GetHDC()) return;
692#endif
693
694 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
695
696 wxCoord x2 = x + width;
697 wxCoord y2 = y + height;
698
699 if ((m_logicalFunction == wxCOPY) && (m_pen.GetStyle() == wxTRANSPARENT))
700 {
701 RECT rect;
702 rect.left = XLOG2DEV(x);
703 rect.top = YLOG2DEV(y);
704 rect.right = XLOG2DEV(x2);
705 rect.bottom = YLOG2DEV(y2);
706 (void)FillRect(GetHdc(), &rect, (HBRUSH)m_brush.GetResourceHandle() );
707 }
708 else
709 {
710 // Windows draws the filled rectangles without outline (i.e. drawn with a
711 // transparent pen) one pixel smaller in both directions and we want them
712 // to have the same size regardless of which pen is used - adjust
713
714