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