1/////////////////////////////////////////////////////////////////////////////
2// Name:      src/msw/region.cpp
3// Purpose:   wxRegion implementation using Win32 API
4// Author:    Vadim Zeitlin
5// Modified by:
6// Created:   Fri Oct 24 10:46:34 MET 1997
7// RCS-ID:    $Id: region.cpp 41429 2006-09-25 11:47:23Z VZ $
8// Copyright: (c) 1997-2002 wxWidgets team
9// Licence:   wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24    #pragma hdrstop
25#endif
26
27#include "wx/region.h"
28
29#ifndef WX_PRECOMP
30    #include "wx/gdicmn.h"
31#endif
32
33#include "wx/msw/private.h"
34
35IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
36IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator, wxObject)
37
38// ----------------------------------------------------------------------------
39// wxRegionRefData implementation
40// ----------------------------------------------------------------------------
41
42class WXDLLEXPORT wxRegionRefData : public wxGDIRefData
43{
44public:
45    wxRegionRefData()
46    {
47        m_region = 0;
48    }
49
50    wxRegionRefData(const wxRegionRefData& data) : wxGDIRefData()
51    {
52#if defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
53        DWORD noBytes = ::GetRegionData(data.m_region, 0, NULL);
54        RGNDATA *rgnData = (RGNDATA*) new char[noBytes];
55        ::GetRegionData(data.m_region, noBytes, rgnData);
56        m_region = ::ExtCreateRegion(NULL, noBytes, rgnData);
57        delete[] (char*) rgnData;
58#else
59        RECT rect;
60        ::GetRgnBox(data.m_region, &rect);
61        m_region = ::CreateRectRgnIndirect(&rect);
62#endif
63    }
64
65    virtual ~wxRegionRefData()
66    {
67        ::DeleteObject(m_region);
68        m_region = 0;
69    }
70
71    HRGN m_region;
72
73private:
74// Cannot use
75//  DECLARE_NO_COPY_CLASS(wxRegionRefData)
76// because copy constructor is explicitly declared above;
77// but no copy assignment operator is defined, so declare
78// it private to prevent the compiler from defining it:
79    wxRegionRefData& operator=(const wxRegionRefData&);
80};
81
82#define M_REGION (((wxRegionRefData*)m_refData)->m_region)
83#define M_REGION_OF(rgn) (((wxRegionRefData*)(rgn.m_refData))->m_region)
84
85// ============================================================================
86// wxRegion implementation
87// ============================================================================
88
89// ----------------------------------------------------------------------------
90// ctors and dtor
91// ----------------------------------------------------------------------------
92
93wxRegion::wxRegion()
94{
95    m_refData = (wxRegionRefData *)NULL;
96}
97
98wxRegion::wxRegion(WXHRGN hRegion)
99{
100    m_refData = new wxRegionRefData;
101    M_REGION = (HRGN) hRegion;
102}
103
104wxRegion::wxRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
105{
106    m_refData = new wxRegionRefData;
107    M_REGION = ::CreateRectRgn(x, y, x + w, y + h);
108}
109
110wxRegion::wxRegion(const wxPoint& topLeft, const wxPoint& bottomRight)
111{
112    m_refData = new wxRegionRefData;
113    M_REGION = ::CreateRectRgn(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
114}
115
116wxRegion::wxRegion(const wxRect& rect)
117{
118    m_refData = new wxRegionRefData;
119    M_REGION = ::CreateRectRgn(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
120}
121
122wxRegion::wxRegion(size_t n, const wxPoint *points, int fillStyle)
123{
124#if defined(__WXMICROWIN__) || defined(__WXWINCE__)
125    wxUnusedVar(n);
126    wxUnusedVar(points);
127    wxUnusedVar(fillStyle);
128    m_refData = NULL;
129    M_REGION = NULL;
130#else
131    m_refData = new wxRegionRefData;
132    M_REGION = ::CreatePolygonRgn
133               (
134                    (POINT*)points,
135                    n,
136                    fillStyle == wxODDEVEN_RULE ? ALTERNATE : WINDING
137               );
138#endif
139}
140
141wxRegion::~wxRegion()
142{
143    // m_refData unrefed in ~wxObject
144}
145
146wxObjectRefData *wxRegion::CreateRefData() const
147{
148    return new wxRegionRefData;
149}
150
151wxObjectRefData *wxRegion::CloneRefData(const wxObjectRefData *data) const
152{
153    return new wxRegionRefData(*(wxRegionRefData *)data);
154}
155
156// ----------------------------------------------------------------------------
157// wxRegion operations
158// ----------------------------------------------------------------------------
159
160// Clear current region
161void wxRegion::Clear()
162{
163    UnRef();
164}
165
166bool wxRegion::DoOffset(wxCoord x, wxCoord y)
167{
168    wxCHECK_MSG( M_REGION, false, _T("invalid wxRegion") );
169
170    if ( !x && !y )
171    {
172        // nothing to do
173        return true;
174    }
175
176    AllocExclusive();
177
178    if ( ::OffsetRgn(GetHrgn(), x, y) == ERROR )
179    {
180        wxLogLastError(_T("OffsetRgn"));
181
182        return false;
183    }
184
185    return true;
186}
187
188// combine another region with this one
189bool wxRegion::DoCombine(const wxRegion& rgn, wxRegionOp op)
190{
191    // we can't use the API functions if we don't have a valid region handle
192    if ( !m_refData )
193    {
194        // combining with an empty/invalid region works differently depending
195        // on the operation
196        switch ( op )
197        {
198            case wxRGN_COPY:
199            case wxRGN_OR:
200            case wxRGN_XOR:
201                *this = rgn;
202                break;
203
204            default:
205                wxFAIL_MSG( _T("unknown region operation") );
206                // fall through
207
208            case wxRGN_AND:
209            case wxRGN_DIFF:
210                // leave empty/invalid
211                return false;
212        }
213    }
214    else // we have a valid region
215    {
216        AllocExclusive();
217
218        int mode;
219        switch ( op )
220        {
221            case wxRGN_AND:
222                mode = RGN_AND;
223                break;
224
225            case wxRGN_OR:
226                mode = RGN_OR;
227                break;
228
229            case wxRGN_XOR:
230                mode = RGN_XOR;
231                break;
232
233            case wxRGN_DIFF:
234                mode = RGN_DIFF;
235                break;
236
237            default:
238                wxFAIL_MSG( _T("unknown region operation") );
239                // fall through
240
241            case wxRGN_COPY:
242                mode = RGN_COPY;
243                break;
244        }
245
246        if ( ::CombineRgn(M_REGION, M_REGION, M_REGION_OF(rgn), mode) == ERROR )
247        {
248            wxLogLastError(_T("CombineRgn"));
249
250            return false;
251        }
252    }
253
254    return true;
255}
256
257// ----------------------------------------------------------------------------
258// wxRegion bounding box
259// ----------------------------------------------------------------------------
260
261// Outer bounds of region
262bool wxRegion::DoGetBox(wxCoord& x, wxCoord& y, wxCoord&w, wxCoord &h) const
263{
264    if (m_refData)
265    {
266        RECT rect;
267        ::GetRgnBox(M_REGION, & rect);
268        x = rect.left;
269        y = rect.top;
270        w = rect.right - rect.left;
271        h = rect.bottom - rect.top;
272
273        return true;
274    }
275    else
276    {
277        x = y = w = h = 0;
278
279        return false;
280    }
281}
282
283// Is region empty?
284bool wxRegion::IsEmpty() const
285{
286    wxCoord x, y, w, h;
287    GetBox(x, y, w, h);
288
289    return (w == 0) && (h == 0);
290}
291
292bool wxRegion::DoIsEqual(const wxRegion& region) const
293{
294    return ::EqualRgn(M_REGION, M_REGION_OF(region)) != 0;
295}
296
297// ----------------------------------------------------------------------------
298// wxRegion hit testing
299// ----------------------------------------------------------------------------
300
301// Does the region contain the point (x,y)?
302wxRegionContain wxRegion::DoContainsPoint(wxCoord x, wxCoord y) const
303{
304    if (!m_refData)
305        return wxOutRegion;
306
307    return ::PtInRegion(M_REGION, (int) x, (int) y) ? wxInRegion : wxOutRegion;
308}
309
310// Does the region contain the rectangle (x, y, w, h)?
311wxRegionContain wxRegion::DoContainsRect(const wxRect& rect) const
312{
313    if (!m_refData)
314        return wxOutRegion;
315
316    RECT rc;
317    wxCopyRectToRECT(rect, rc);
318
319    return ::RectInRegion(M_REGION, &rc) ? wxInRegion : wxOutRegion;
320}
321
322// Get internal region handle
323WXHRGN wxRegion::GetHRGN() const
324{
325    return (WXHRGN)(m_refData ? M_REGION : 0);
326}
327
328// ============================================================================
329// wxRegionIterator implementation
330// ============================================================================
331
332// ----------------------------------------------------------------------------
333// wxRegionIterator ctors/dtor
334// ----------------------------------------------------------------------------
335
336void wxRegionIterator::Init()
337{
338    m_current =
339    m_numRects = 0;
340
341    m_rects = NULL;
342}
343
344wxRegionIterator::~wxRegionIterator()
345{
346    delete [] m_rects;
347}
348
349// Initialize iterator for region
350wxRegionIterator::wxRegionIterator(const wxRegion& region)
351{
352    m_rects = NULL;
353
354    Reset(region);
355}
356
357wxRegionIterator& wxRegionIterator::operator=(const wxRegionIterator& ri)
358{
359    delete [] m_rects;
360
361    m_current = ri.m_current;
362    m_numRects = ri.m_numRects;
363    if ( m_numRects )
364    {
365        m_rects = new wxRect[m_numRects];
366        for ( long n = 0; n < m_numRects; n++ )
367            m_rects[n] = ri.m_rects[n];
368    }
369    else
370    {
371        m_rects = NULL;
372    }
373
374    return *this;
375}
376
377// ----------------------------------------------------------------------------
378// wxRegionIterator operations
379// ----------------------------------------------------------------------------
380
381// Reset iterator for a new region.
382void wxRegionIterator::Reset(const wxRegion& region)
383{
384    m_current = 0;
385    m_region = region;
386
387    if (m_rects)
388    {
389        delete[] m_rects;
390
391        m_rects = NULL;
392    }
393
394    if (m_region.Empty())
395        m_numRects = 0;
396    else
397    {
398        DWORD noBytes = ::GetRegionData(((wxRegionRefData*)region.m_refData)->m_region, 0, NULL);
399        RGNDATA *rgnData = (RGNDATA*) new char[noBytes];
400        ::GetRegionData(((wxRegionRefData*)region.m_refData)->m_region, noBytes, rgnData);
401
402        RGNDATAHEADER* header = (RGNDATAHEADER*) rgnData;
403
404        m_rects = new wxRect[header->nCount];
405
406        RECT* rect = (RECT*) ((char*)rgnData + sizeof(RGNDATAHEADER));
407        size_t i;
408        for (i = 0; i < header->nCount; i++)
409        {
410            m_rects[i] = wxRect(rect->left, rect->top,
411                                 rect->right - rect->left, rect->bottom - rect->top);
412            rect ++; // Advances pointer by sizeof(RECT)
413        }
414
415        m_numRects = header->nCount;
416
417        delete[] (char*) rgnData;
418    }
419}
420
421wxRegionIterator& wxRegionIterator::operator++()
422{
423    if (m_current < m_numRects)
424        ++m_current;
425
426    return *this;
427}
428
429wxRegionIterator wxRegionIterator::operator ++ (int)
430{
431    wxRegionIterator tmp = *this;
432    if (m_current < m_numRects)
433        ++m_current;
434
435    return tmp;
436}
437
438// ----------------------------------------------------------------------------
439// wxRegionIterator accessors
440// ----------------------------------------------------------------------------
441
442wxCoord wxRegionIterator::GetX() const
443{
444    wxCHECK_MSG( m_current < m_numRects, 0, _T("invalid wxRegionIterator") );
445
446    return m_rects[m_current].x;
447}
448
449wxCoord wxRegionIterator::GetY() const
450{
451    wxCHECK_MSG( m_current < m_numRects, 0, _T("invalid wxRegionIterator") );
452
453    return m_rects[m_current].y;
454}
455
456wxCoord wxRegionIterator::GetW() const
457{
458    wxCHECK_MSG( m_current < m_numRects, 0, _T("invalid wxRegionIterator") );
459
460    return m_rects[m_current].width;
461}
462
463wxCoord wxRegionIterator::GetH() const
464{
465    wxCHECK_MSG( m_current < m_numRects, 0, _T("invalid wxRegionIterator") );
466
467    return m_rects[m_current].height;
468}
469