1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/gtk/region.cpp
3// Purpose:
4// Author:      Robert Roebling
5// Modified:    VZ at 05.10.00: use AllocExclusive(), comparison fixed
6// Id:          $Id: region.cpp 42903 2006-11-01 12:56:38Z RR $
7// Copyright:   (c) 1998 Robert Roebling
8// Licence:     wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19// For compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#include "wx/region.h"
23
24#ifndef WX_PRECOMP
25    #include "wx/log.h"
26#endif
27
28#include "wx/gtk/private.h"
29
30
31// ----------------------------------------------------------------------------
32// wxRegionRefData: private class containing the information about the region
33// ----------------------------------------------------------------------------
34
35class wxRegionRefData : public wxObjectRefData
36{
37public:
38    wxRegionRefData()
39    {
40        m_region = NULL;
41    }
42
43    wxRegionRefData(const wxRegionRefData& refData)
44        : wxObjectRefData()
45    {
46        m_region = gdk_region_copy(refData.m_region);
47    }
48
49    virtual ~wxRegionRefData()
50    {
51        if (m_region)
52            gdk_region_destroy( m_region );
53    }
54
55    GdkRegion  *m_region;
56};
57
58// ----------------------------------------------------------------------------
59// macros
60// ----------------------------------------------------------------------------
61
62#define M_REGIONDATA ((wxRegionRefData *)m_refData)
63#define M_REGIONDATA_OF(rgn) ((wxRegionRefData *)(rgn.m_refData))
64
65IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
66IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator,wxObject)
67
68// ----------------------------------------------------------------------------
69// wxRegion construction
70// ----------------------------------------------------------------------------
71
72#define M_REGIONDATA ((wxRegionRefData *)m_refData)
73
74void wxRegion::InitRect(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
75{
76    GdkRectangle rect;
77    rect.x = x;
78    rect.y = y;
79    rect.width = w;
80    rect.height = h;
81
82    m_refData = new wxRegionRefData();
83
84    M_REGIONDATA->m_region = gdk_region_rectangle( &rect );
85}
86
87wxRegion::wxRegion( GdkRegion *region )
88{
89    m_refData = new wxRegionRefData();
90    M_REGIONDATA->m_region = gdk_region_copy( region );
91}
92
93wxRegion::wxRegion( size_t n, const wxPoint *points, int fillStyle )
94{
95    GdkPoint *gdkpoints = new GdkPoint[n];
96    for ( size_t i = 0 ; i < n ; i++ )
97    {
98        gdkpoints[i].x = points[i].x;
99        gdkpoints[i].y = points[i].y;
100    }
101
102    m_refData = new wxRegionRefData();
103
104    GdkRegion* reg = gdk_region_polygon
105                     (
106                        gdkpoints,
107                        n,
108                        fillStyle == wxWINDING_RULE ? GDK_WINDING_RULE
109                                                    : GDK_EVEN_ODD_RULE
110                     );
111
112    M_REGIONDATA->m_region = reg;
113
114    delete [] gdkpoints;
115}
116
117wxRegion::~wxRegion()
118{
119    // m_refData unrefed in ~wxObject
120}
121
122wxObjectRefData *wxRegion::CreateRefData() const
123{
124    return new wxRegionRefData;
125}
126
127wxObjectRefData *wxRegion::CloneRefData(const wxObjectRefData *data) const
128{
129    return new wxRegionRefData(*(wxRegionRefData *)data);
130}
131
132// ----------------------------------------------------------------------------
133// wxRegion comparison
134// ----------------------------------------------------------------------------
135
136bool wxRegion::DoIsEqual(const wxRegion& region) const
137{
138    return gdk_region_equal(M_REGIONDATA->m_region,
139                            M_REGIONDATA_OF(region)->m_region);
140}
141
142// ----------------------------------------------------------------------------
143// wxRegion operations
144// ----------------------------------------------------------------------------
145
146void wxRegion::Clear()
147{
148    UnRef();
149}
150
151bool wxRegion::DoUnionWithRect(const wxRect& r)
152{
153    // workaround for a strange GTK/X11 bug: taking union with an empty
154    // rectangle results in an empty region which is definitely not what we
155    // want
156    if ( r.IsEmpty() )
157        return true;
158
159    if ( !m_refData )
160    {
161        InitRect(r.x, r.y, r.width, r.height);
162    }
163    else
164    {
165        AllocExclusive();
166
167        GdkRectangle rect;
168        rect.x = r.x;
169        rect.y = r.y;
170        rect.width = r.width;
171        rect.height = r.height;
172
173        gdk_region_union_with_rect( M_REGIONDATA->m_region, &rect );
174    }
175
176    return true;
177}
178
179bool wxRegion::DoUnionWithRegion( const wxRegion& region )
180{
181    wxCHECK_MSG( region.Ok(), false, _T("invalid region") );
182
183    if (!m_refData)
184    {
185        m_refData = new wxRegionRefData();
186        M_REGIONDATA->m_region = gdk_region_new();
187    }
188    else
189    {
190        AllocExclusive();
191    }
192
193    gdk_region_union( M_REGIONDATA->m_region, region.GetRegion() );
194
195    return true;
196}
197
198bool wxRegion::DoIntersect( const wxRegion& region )
199{
200    wxCHECK_MSG( region.Ok(), false, _T("invalid region") );
201
202    if (!m_refData)
203    {
204        // intersecting with invalid region doesn't make sense
205        return false;
206    }
207
208    AllocExclusive();
209
210    gdk_region_intersect( M_REGIONDATA->m_region, region.GetRegion() );
211
212    return true;
213}
214
215bool wxRegion::DoSubtract( const wxRegion& region )
216{
217    wxCHECK_MSG( region.Ok(), false, _T("invalid region") );
218
219    if (!m_refData)
220    {
221        // subtracting from an invalid region doesn't make sense
222        return false;
223    }
224
225    AllocExclusive();
226
227    gdk_region_subtract( M_REGIONDATA->m_region, region.GetRegion() );
228
229    return true;
230}
231
232bool wxRegion::DoXor( const wxRegion& region )
233{
234    wxCHECK_MSG( region.Ok(), false, _T("invalid region") );
235
236    if (!m_refData)
237    {
238        return false;
239    }
240
241    AllocExclusive();
242
243    gdk_region_xor( M_REGIONDATA->m_region, region.GetRegion() );
244
245    return true;
246}
247
248bool wxRegion::DoOffset( wxCoord x, wxCoord y )
249{
250    if (!m_refData)
251        return false;
252
253    AllocExclusive();
254
255    gdk_region_offset( M_REGIONDATA->m_region, x, y );
256
257    return true;
258}
259
260// ----------------------------------------------------------------------------
261// wxRegion tests
262// ----------------------------------------------------------------------------
263
264bool wxRegion::DoGetBox( wxCoord &x, wxCoord &y, wxCoord &w, wxCoord &h ) const
265{
266    if ( m_refData )
267    {
268        GdkRectangle rect;
269        gdk_region_get_clipbox( M_REGIONDATA->m_region, &rect );
270        x = rect.x;
271        y = rect.y;
272        w = rect.width;
273        h = rect.height;
274
275        return true;
276    }
277    else
278    {
279        x = 0;
280        y = 0;
281        w = -1;
282        h = -1;
283
284        return false;
285    }
286}
287
288bool wxRegion::IsEmpty() const
289{
290    if (!m_refData)
291        return true;
292
293    return gdk_region_empty( M_REGIONDATA->m_region );
294}
295
296wxRegionContain wxRegion::DoContainsPoint( wxCoord x, wxCoord y ) const
297{
298    if (!m_refData)
299        return wxOutRegion;
300
301    if (gdk_region_point_in( M_REGIONDATA->m_region, x, y ))
302        return wxInRegion;
303    else
304        return wxOutRegion;
305}
306
307wxRegionContain wxRegion::DoContainsRect(const wxRect& r) const
308{
309    if (!m_refData)
310        return wxOutRegion;
311
312    GdkRectangle rect;
313    rect.x = r.x;
314    rect.y = r.y;
315    rect.width = r.width;
316    rect.height = r.height;
317    GdkOverlapType res = gdk_region_rect_in( M_REGIONDATA->m_region, &rect );
318    switch (res)
319    {
320        case GDK_OVERLAP_RECTANGLE_IN:   return wxInRegion;
321        case GDK_OVERLAP_RECTANGLE_OUT:  return wxOutRegion;
322        case GDK_OVERLAP_RECTANGLE_PART: return wxPartRegion;
323    }
324    return wxOutRegion;
325}
326
327GdkRegion *wxRegion::GetRegion() const
328{
329    if (!m_refData)
330        return (GdkRegion*) NULL;
331
332    return M_REGIONDATA->m_region;
333}
334
335// ----------------------------------------------------------------------------
336// wxRegionIterator
337// ----------------------------------------------------------------------------
338
339wxRegionIterator::wxRegionIterator()
340{
341    Init();
342    Reset();
343}
344
345wxRegionIterator::wxRegionIterator( const wxRegion& region )
346{
347    Init();
348    Reset(region);
349}
350
351void wxRegionIterator::Init()
352{
353    m_rects = NULL;
354    m_numRects = 0;
355}
356
357wxRegionIterator::~wxRegionIterator()
358{
359    wxDELETEA(m_rects);
360}
361
362void wxRegionIterator::CreateRects( const wxRegion& region )
363{
364    wxDELETEA(m_rects);
365    m_numRects = 0;
366
367    GdkRegion *gdkregion = region.GetRegion();
368    if (!gdkregion)
369        return;
370
371    GdkRectangle *gdkrects = NULL;
372    gint numRects = 0;
373    gdk_region_get_rectangles( gdkregion, &gdkrects, &numRects );
374
375    m_numRects = numRects;
376    if (numRects)
377    {
378        m_rects = new wxRect[m_numRects];
379        for (size_t i=0; i < m_numRects; ++i)
380        {
381            GdkRectangle &gr = gdkrects[i];
382            wxRect &wr = m_rects[i];
383            wr.x = gr.x;
384            wr.y = gr.y;
385            wr.width = gr.width;
386            wr.height = gr.height;
387        }
388    }
389    g_free( gdkrects );
390}
391
392void wxRegionIterator::Reset( const wxRegion& region )
393{
394    m_region = region;
395    CreateRects(region);
396    Reset();
397}
398
399bool wxRegionIterator::HaveRects() const
400{
401    return m_current < m_numRects;
402}
403
404wxRegionIterator& wxRegionIterator::operator ++ ()
405{
406    if (HaveRects())
407        ++m_current;
408
409    return *this;
410}
411
412wxRegionIterator wxRegionIterator::operator ++ (int)
413{
414    wxRegionIterator tmp = *this;
415
416    if (HaveRects())
417        ++m_current;
418
419    return tmp;
420}
421
422wxCoord wxRegionIterator::GetX() const
423{
424    wxCHECK_MSG( HaveRects(), 0, _T("invalid wxRegionIterator") );
425
426    return m_rects[m_current].x;
427}
428
429wxCoord wxRegionIterator::GetY() const
430{
431    wxCHECK_MSG( HaveRects(), 0, _T("invalid wxRegionIterator") );
432
433    return m_rects[m_current].y;
434}
435
436wxCoord wxRegionIterator::GetW() const
437{
438    wxCHECK_MSG( HaveRects(), 0, _T("invalid wxRegionIterator") );
439
440    return m_rects[m_current].width;
441}
442
443wxCoord wxRegionIterator::GetH() const
444{
445    wxCHECK_MSG( HaveRects(), 0, _T("invalid wxRegionIterator") );
446
447    return m_rects[m_current].height;
448}
449
450wxRect wxRegionIterator::GetRect() const
451{
452    wxRect r;
453    if( HaveRects() )
454        r = m_rects[m_current];
455
456    return r;
457}
458
459wxRegionIterator& wxRegionIterator::operator=(const wxRegionIterator& ri)
460{
461    wxDELETEA(m_rects);
462
463    m_current = ri.m_current;
464    m_numRects = ri.m_numRects;
465    if ( m_numRects )
466    {
467        m_rects = new wxRect[m_numRects];
468        for ( unsigned int n = 0; n < m_numRects; n++ )
469            m_rects[n] = ri.m_rects[n];
470    }
471    else
472    {
473        m_rects = NULL;
474    }
475
476    return *this;
477}
478