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