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