added support for polygons to wxRegion
[wxWidgets.git] / src / gtk / region.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: gtk/region.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Modified: VZ at 05.10.00: use Unshare(), comparison fixed
6 // Id: $Id$
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 #ifdef __GNUG__
16 #pragma implementation "region.h"
17 #endif
18
19 // ----------------------------------------------------------------------------
20 // headers
21 // ----------------------------------------------------------------------------
22
23 #include "wx/region.h"
24
25 #include <gdk/gdk.h>
26 #include <gtk/gtk.h>
27
28 // Unfortunately the new way of implementing the region iterator
29 // doesn't work with GTK+ 2.0 or above (can't access a Region in
30 // GdkPrivateRegion)
31 #ifdef __WXGTK20__
32 #define OLDCODE 1
33 #else
34 #define OLDCODE 0
35 #endif
36
37 #include "wx/log.h"
38
39 // ----------------------------------------------------------------------------
40 // wxRegionRefData: private class containing the information about the region
41 // ----------------------------------------------------------------------------
42
43 class wxRegionRefData : public wxObjectRefData
44 {
45 public:
46 wxRegionRefData();
47 wxRegionRefData(const wxRegionRefData& refData);
48 virtual ~wxRegionRefData();
49
50 GdkRegion *m_region;
51 #if OLDCODE
52 wxList m_rects;
53 #endif
54 };
55
56 // ----------------------------------------------------------------------------
57 // macros
58 // ----------------------------------------------------------------------------
59
60 #define M_REGIONDATA ((wxRegionRefData *)m_refData)
61 #define M_REGIONDATA_OF(rgn) ((wxRegionRefData *)(rgn.m_refData))
62
63 IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject);
64 IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator,wxObject);
65
66 // ============================================================================
67 // implementation
68 // ============================================================================
69
70 // ----------------------------------------------------------------------------
71 // wxRegionRefData
72 // ----------------------------------------------------------------------------
73
74 wxRegionRefData::wxRegionRefData()
75 {
76 m_region = (GdkRegion *) NULL;
77 }
78
79 wxRegionRefData::wxRegionRefData(const wxRegionRefData& refData)
80 {
81 #ifdef __WXGTK20__
82 m_region = gdk_region_copy(refData.m_region);
83 #else
84 m_region = gdk_region_new();
85 GdkRegion *regCopy = gdk_regions_union(m_region, refData.m_region);
86 gdk_region_destroy(m_region);
87 m_region = regCopy;
88 #endif
89
90 #if OLDCODE
91 wxNode *node = refData.m_rects.First();
92 while (node)
93 {
94 wxRect *r = (wxRect*)node->Data();
95 m_rects.Append( (wxObject*) new wxRect(*r) );
96 node = node->Next();
97 }
98 #endif
99 }
100
101 wxRegionRefData::~wxRegionRefData()
102 {
103 if (m_region) gdk_region_destroy( m_region );
104
105 #if OLDCODE
106 wxNode *node = m_rects.First();
107 while (node)
108 {
109 wxRect *r = (wxRect*)node->Data();
110 delete r;
111 node = node->Next();
112 }
113 #endif
114 }
115
116 // ----------------------------------------------------------------------------
117 // wxRegion construction
118 // ----------------------------------------------------------------------------
119
120 #define M_REGIONDATA ((wxRegionRefData *)m_refData)
121
122 wxRegion::wxRegion()
123 {
124 }
125
126 wxRegion::wxRegion( wxCoord x, wxCoord y, wxCoord w, wxCoord h )
127 {
128 m_refData = new wxRegionRefData();
129 GdkRegion *reg = gdk_region_new();
130 GdkRectangle rect;
131 rect.x = x;
132 rect.y = y;
133 rect.width = w;
134 rect.height = h;
135 #ifdef __WXGTK20__
136 gdk_region_union_with_rect( reg, &rect );
137 M_REGIONDATA->m_region = reg;
138 #else
139 M_REGIONDATA->m_region = gdk_region_union_with_rect( reg, &rect );
140 gdk_region_destroy( reg );
141 #endif
142 #if OLDCODE
143 M_REGIONDATA->m_rects.Append( (wxObject*) new wxRect(x,y,w,h) );
144 #endif
145 }
146
147 wxRegion::wxRegion( const wxPoint& topLeft, const wxPoint& bottomRight )
148 {
149 m_refData = new wxRegionRefData();
150 GdkRegion *reg = gdk_region_new();
151 GdkRectangle rect;
152 rect.x = topLeft.x;
153 rect.y = topLeft.y;
154 rect.width = bottomRight.x - rect.x;
155 rect.height = bottomRight.y - rect.y;
156 #ifdef __WXGTK20__
157 gdk_region_union_with_rect( reg, &rect );
158 M_REGIONDATA->m_region = reg;
159 #else
160 M_REGIONDATA->m_region = gdk_region_union_with_rect( reg, &rect );
161 gdk_region_destroy( reg );
162 #endif
163 #if OLDCODE
164 M_REGIONDATA->m_rects.Append( (wxObject*) new wxRect(topLeft,bottomRight) );
165 #endif
166 }
167
168 wxRegion::wxRegion( const wxRect& rect )
169 {
170 m_refData = new wxRegionRefData();
171 GdkRegion *reg = gdk_region_new();
172 GdkRectangle g_rect;
173 g_rect.x = rect.x;
174 g_rect.y = rect.y;
175 g_rect.width = rect.width;
176 g_rect.height = rect.height;
177 #ifdef __WXGTK20__
178 gdk_region_union_with_rect( reg, &g_rect );
179 M_REGIONDATA->m_region = reg;
180 #else
181 M_REGIONDATA->m_region = gdk_region_union_with_rect( reg, &g_rect );
182 gdk_region_destroy( reg );
183 #endif
184 #if OLDCODE
185 M_REGIONDATA->m_rects.Append( (wxObject*) new wxRect(rect.x,rect.y,rect.width,rect.height) );
186 #endif
187 }
188
189 wxRegion::wxRegion( size_t n, const wxPoint *points, int fillStyle )
190 {
191 GdkPoint *gdkpoints = new GdkPoint[n];
192 for ( size_t i = 0 ; i < n ; i++ )
193 {
194 gdkpoints[i].x = points[i].x;
195 gdkpoints[i].y = points[i].y;
196 }
197
198 m_refData = new wxRegionRefData();
199
200 GdkRegion* reg = gdk_region_polygon
201 (
202 gdkpoints,
203 n,
204 fillStyle == wxWINDING_RULE ? GDK_WINDING_RULE
205 : GDK_EVEN_ODD_RULE
206 );
207
208 M_REGIONDATA->m_region = reg;
209
210 delete [] gdkpoints;
211 }
212
213 wxRegion::~wxRegion()
214 {
215 }
216
217 bool wxRegion::operator==( const wxRegion& region )
218 {
219 // VZ: compare the regions themselves, not the pointers to ref data!
220 return gdk_region_equal(M_REGIONDATA->m_region,
221 M_REGIONDATA_OF(region)->m_region);
222 }
223
224 bool wxRegion::operator != ( const wxRegion& region )
225 {
226 return !(*this == region);
227 }
228
229 void wxRegion::Unshare()
230 {
231 if ( !m_refData )
232 {
233 m_refData = new wxRegionRefData;
234 M_REGIONDATA->m_region = gdk_region_new();
235 }
236 else if ( m_refData->GetRefCount() > 1 )
237 {
238 wxRegionRefData *refData = new wxRegionRefData(*M_REGIONDATA);
239 UnRef();
240 m_refData = refData;
241 }
242 //else: we're not shared
243 }
244
245 // ----------------------------------------------------------------------------
246 // wxRegion operations
247 // ----------------------------------------------------------------------------
248
249 void wxRegion::Clear()
250 {
251 UnRef();
252 }
253
254 bool wxRegion::Union( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
255 {
256 GdkRectangle rect;
257 rect.x = x;
258 rect.y = y;
259 rect.width = width;
260 rect.height = height;
261 if (!m_refData)
262 {
263 m_refData = new wxRegionRefData();
264 GdkRegion *reg = gdk_region_new();
265 #ifdef __WXGTK20__
266 gdk_region_union_with_rect( reg, &rect );
267 M_REGIONDATA->m_region = reg;
268 #else
269 M_REGIONDATA->m_region = gdk_region_union_with_rect( reg, &rect );
270 gdk_region_destroy( reg );
271 #endif
272 }
273 else
274 {
275 Unshare();
276
277 #ifdef __WXGTK20__
278 gdk_region_union_with_rect( M_REGIONDATA->m_region, &rect );
279 #else
280 GdkRegion *reg = gdk_region_union_with_rect( M_REGIONDATA->m_region, &rect );
281 gdk_region_destroy( M_REGIONDATA->m_region );
282 M_REGIONDATA->m_region = reg;
283 #endif
284 }
285
286 #if OLDCODE
287 M_REGIONDATA->m_rects.Append( (wxObject*) new wxRect(x,y,width,height) );
288 #endif
289
290 return TRUE;
291 }
292
293 bool wxRegion::Union( const wxRect& rect )
294 {
295 return Union( rect.x, rect.y, rect.width, rect.height );
296 }
297
298 bool wxRegion::Union( const wxRegion& region )
299 {
300 if (region.IsNull())
301 return FALSE;
302
303 Unshare();
304
305 #ifdef __WXGTK20__
306 gdk_region_union( M_REGIONDATA->m_region, region.GetRegion() );
307 #else
308 GdkRegion *reg = gdk_regions_union( M_REGIONDATA->m_region, region.GetRegion() );
309 gdk_region_destroy( M_REGIONDATA->m_region );
310 M_REGIONDATA->m_region = reg;
311 #endif
312
313 #if OLDCODE
314 wxNode *node = region.GetRectList()->First();
315 while (node)
316 {
317 wxRect *r = (wxRect*)node->Data();
318 M_REGIONDATA->m_rects.Append( (wxObject*) new wxRect(r->x,r->y,r->width,r->height) );
319 node = node->Next();
320 }
321 #endif
322
323 return TRUE;
324 }
325
326 bool wxRegion::Intersect( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
327 {
328 wxRegion reg( x, y, width, height );
329
330 return Intersect( reg );
331 }
332
333 bool wxRegion::Intersect( const wxRect& rect )
334 {
335 wxRegion reg( rect );
336 return Intersect( reg );
337 }
338
339 // this helper function just computes the region intersection without updating
340 // the list of rectangles each region maintaints: this allows us to call it
341 // from Intersect() itself without going into infinite recursion as we would
342 // if we called Intersect() itself recursively
343 bool wxRegion::IntersectRegionOnly(const wxRegion& region)
344 {
345 Unshare();
346
347 #ifdef __WXGTK20__
348 gdk_region_intersect( M_REGIONDATA->m_region, region.GetRegion() );
349 #else
350 GdkRegion *reg = gdk_regions_intersect( M_REGIONDATA->m_region, region.GetRegion() );
351 gdk_region_destroy( M_REGIONDATA->m_region );
352 M_REGIONDATA->m_region = reg;
353 #endif
354
355 return TRUE;
356 }
357
358 bool wxRegion::Intersect( const wxRegion& region )
359 {
360 if (region.IsNull())
361 return FALSE;
362
363 if (!m_refData)
364 {
365 m_refData = new wxRegionRefData();
366 M_REGIONDATA->m_region = gdk_region_new();
367 return TRUE;
368 }
369
370 if ( !IntersectRegionOnly(region) )
371 {
372 GetRectList()->Clear();
373
374 return FALSE;
375 }
376
377 // we need to update the rect list as well
378 #if OLDCODE
379 wxList& list = *GetRectList();
380 wxNode *node = list.First();
381 while (node)
382 {
383 wxRect *r = (wxRect*)node->Data();
384
385 wxRegion regCopy = region;
386 if ( regCopy.IntersectRegionOnly(*r) )
387 {
388 // replace the node with the intersection
389 *r = regCopy.GetBox();
390 }
391 else
392 {
393 // TODO remove the rect from the list
394 r->width = 0;
395 r->height = 0;
396 }
397
398 node = node->Next();
399 }
400 #endif
401 return TRUE;
402 }
403
404 bool wxRegion::Subtract( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
405 {
406 wxRegion reg( x, y, width, height );
407 return Subtract( reg );
408 }
409
410 bool wxRegion::Subtract( const wxRect& rect )
411 {
412 wxRegion reg( rect );
413 return Subtract( reg );
414 }
415
416 bool wxRegion::Subtract( const wxRegion& region )
417 {
418 if (region.IsNull())
419 return FALSE;
420
421 if (!m_refData)
422 {
423 m_refData = new wxRegionRefData();
424 M_REGIONDATA->m_region = gdk_region_new();
425 }
426
427 Unshare();
428
429 #ifdef __WXGTK20__
430 gdk_region_subtract( M_REGIONDATA->m_region, region.GetRegion() );
431 #else
432 GdkRegion *reg = gdk_regions_subtract( M_REGIONDATA->m_region, region.GetRegion() );
433 gdk_region_destroy( M_REGIONDATA->m_region );
434 M_REGIONDATA->m_region = reg;
435 #endif
436
437 return TRUE;
438 }
439
440 bool wxRegion::Xor( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
441 {
442 wxRegion reg( x, y, width, height );
443 return Xor( reg );
444 }
445
446 bool wxRegion::Xor( const wxRect& rect )
447 {
448 wxRegion reg( rect );
449 return Xor( reg );
450 }
451
452 bool wxRegion::Xor( const wxRegion& region )
453 {
454 if (region.IsNull())
455 return FALSE;
456
457 if (!m_refData)
458 {
459 m_refData = new wxRegionRefData();
460 M_REGIONDATA->m_region = gdk_region_new();
461 }
462 else
463 {
464 Unshare();
465 }
466
467 #ifdef __WXGTK20__
468 gdk_region_xor( M_REGIONDATA->m_region, region.GetRegion() );
469 #else
470 GdkRegion *reg = gdk_regions_xor( M_REGIONDATA->m_region, region.GetRegion() );
471 gdk_region_destroy( M_REGIONDATA->m_region );
472 M_REGIONDATA->m_region = reg;
473 #endif
474
475 #if OLDCODE
476 wxNode *node = region.GetRectList()->First();
477 while (node)
478 {
479 wxRect *r = (wxRect*)node->Data();
480 M_REGIONDATA->m_rects.Append( (wxObject*) new wxRect(r->x,r->y,r->width,r->height) );
481 node = node->Next();
482 }
483 #endif
484
485 return TRUE;
486 }
487
488 // ----------------------------------------------------------------------------
489 // wxRegion tests
490 // ----------------------------------------------------------------------------
491
492 void wxRegion::GetBox( wxCoord &x, wxCoord &y, wxCoord &w, wxCoord &h ) const
493 {
494 if ( m_refData )
495 {
496 GdkRectangle rect;
497 gdk_region_get_clipbox( M_REGIONDATA->m_region, &rect );
498 x = rect.x;
499 y = rect.y;
500 w = rect.width;
501 h = rect.height;
502 }
503 else
504 {
505 x = 0;
506 y = 0;
507 w = -1;
508 h = -1;
509 }
510 }
511
512 wxRect wxRegion::GetBox() const
513 {
514 wxCoord x, y, w, h;
515 GetBox( x, y, w, h );
516 return wxRect( x, y, w, h );
517 }
518
519 bool wxRegion::Empty() const
520 {
521 if (!m_refData)
522 return TRUE;
523
524 return gdk_region_empty( M_REGIONDATA->m_region );
525 }
526
527 wxRegionContain wxRegion::Contains( wxCoord x, wxCoord y ) const
528 {
529 if (!m_refData)
530 return wxOutRegion;
531
532 if (gdk_region_point_in( M_REGIONDATA->m_region, x, y ))
533 return wxInRegion;
534 else
535 return wxOutRegion;
536 }
537
538 wxRegionContain wxRegion::Contains( wxCoord x, wxCoord y, wxCoord w, wxCoord h ) const
539 {
540 if (!m_refData)
541 return wxOutRegion;
542
543 GdkRectangle rect;
544 rect.x = x;
545 rect.y = y;
546 rect.width = w;
547 rect.height = h;
548 GdkOverlapType res = gdk_region_rect_in( M_REGIONDATA->m_region, &rect );
549 switch (res)
550 {
551 case GDK_OVERLAP_RECTANGLE_IN: return wxInRegion;
552 case GDK_OVERLAP_RECTANGLE_OUT: return wxOutRegion;
553 case GDK_OVERLAP_RECTANGLE_PART: return wxPartRegion;
554 }
555 return wxOutRegion;
556 }
557
558 wxRegionContain wxRegion::Contains(const wxPoint& pt) const
559 {
560 return Contains( pt.x, pt.y );
561 }
562
563 wxRegionContain wxRegion::Contains(const wxRect& rect) const
564 {
565 return Contains( rect.x, rect.y, rect.width, rect.height );
566 }
567
568 GdkRegion *wxRegion::GetRegion() const
569 {
570 if (!m_refData)
571 return (GdkRegion*) NULL;
572
573 return M_REGIONDATA->m_region;
574 }
575
576 wxList *wxRegion::GetRectList() const
577 {
578 #if OLDCODE
579 if (!m_refData)
580 return (wxList*) NULL;
581
582 return &(M_REGIONDATA->m_rects);
583 #else
584 return (wxList*) NULL;
585 #endif
586 }
587
588 // ----------------------------------------------------------------------------
589 // wxRegionIterator
590 // ----------------------------------------------------------------------------
591
592 #if OLDCODE
593
594 wxRegionIterator::wxRegionIterator()
595 {
596 Reset();
597 }
598
599 wxRegionIterator::wxRegionIterator( const wxRegion& region )
600 {
601 Reset(region);
602 }
603
604 void wxRegionIterator::Reset( const wxRegion& region )
605 {
606 m_region = region;
607 Reset();
608 }
609
610 wxRegionIterator::operator bool () const
611 {
612 return m_region.GetRectList() && m_current < (size_t)m_region.GetRectList()->Number();
613 }
614
615 bool wxRegionIterator::HaveRects() const
616 {
617 return m_region.GetRectList() && m_current < (size_t)m_region.GetRectList()->Number();
618 }
619
620 void wxRegionIterator::operator ++ ()
621 {
622 if (HaveRects()) ++m_current;
623 }
624
625 void wxRegionIterator::operator ++ (int)
626 {
627 if (HaveRects()) ++m_current;
628 }
629
630 wxCoord wxRegionIterator::GetX() const
631 {
632 wxNode *node = m_region.GetRectList()->Nth( m_current );
633 if (!node) return 0;
634 wxRect *r = (wxRect*)node->Data();
635 return r->x;
636 }
637
638 wxCoord wxRegionIterator::GetY() const
639 {
640 wxNode *node = m_region.GetRectList()->Nth( m_current );
641 if (!node) return 0;
642 wxRect *r = (wxRect*)node->Data();
643 return r->y;
644 }
645
646 wxCoord wxRegionIterator::GetW() const
647 {
648 wxNode *node = m_region.GetRectList()->Nth( m_current );
649 if (!node) return 0;
650 wxRect *r = (wxRect*)node->Data();
651 return r->width;
652 }
653
654 wxCoord wxRegionIterator::GetH() const
655 {
656 wxNode *node = m_region.GetRectList()->Nth( m_current );
657 if (!node) return 0;
658 wxRect *r = (wxRect*)node->Data();
659 return r->height;
660 }
661
662 #else
663
664 // the following structures must match the private structures
665 // in X11 region code ( xc/lib/X11/region.h )
666
667 // this makes the Region type transparent
668 // and we have access to the region rectangles
669
670 struct _XBox {
671 short x1, x2, y1, y2;
672 };
673
674 struct _XRegion {
675 long size , numRects;
676 _XBox *rects, extents;
677 };
678
679 class wxRIRefData: public wxObjectRefData
680 {
681 public:
682
683 wxRIRefData() : m_rects(0), m_numRects(0){}
684 ~wxRIRefData();
685
686 wxRect *m_rects;
687 size_t m_numRects;
688
689 void CreateRects( const wxRegion& r );
690 };
691
692 wxRIRefData::~wxRIRefData()
693 {
694 delete m_rects;
695 }
696
697 #include <gdk/gdkprivate.h>
698
699 void wxRIRefData::CreateRects( const wxRegion& region )
700 {
701 if( m_rects )
702 delete m_rects;
703 m_rects = 0;
704 m_numRects= 0;
705 GdkRegion *gdkregion= region.GetRegion();
706 if( gdkregion ){
707 Region r= ((GdkRegionPrivate *)gdkregion)->xregion;
708 if( r ){
709 m_numRects= r->numRects;
710 if( m_numRects )
711 {
712 m_rects= new wxRect[m_numRects];
713 for( size_t i=0; i<m_numRects; ++i )
714 {
715 _XBox &xr= r->rects[i];
716 wxRect&wr= m_rects[i];
717 wr.x = xr.x1;
718 wr.y = xr.y1;
719 wr.width = xr.x2-xr.x1;
720 wr.height= xr.y2-xr.y1;
721 }
722 }
723 }
724 }
725 }
726
727 wxRegionIterator::wxRegionIterator()
728 {
729 m_refData = new wxRIRefData();
730 Reset();
731 }
732
733 wxRegionIterator::wxRegionIterator( const wxRegion& region )
734 {
735 m_refData = new wxRIRefData();
736 Reset(region);
737 }
738
739 void wxRegionIterator::Reset( const wxRegion& region )
740 {
741 m_region = region;
742 ((wxRIRefData*)m_refData)->CreateRects(region);
743 Reset();
744 }
745
746 bool wxRegionIterator::HaveRects() const
747 {
748 return m_current < ((wxRIRefData*)m_refData)->m_numRects;
749 }
750
751 wxRegionIterator::operator bool () const
752 {
753 return HaveRects();
754 }
755
756 void wxRegionIterator::operator ++ ()
757 {
758 if (HaveRects()) ++m_current;
759 }
760
761 void wxRegionIterator::operator ++ (int)
762 {
763 if (HaveRects()) ++m_current;
764 }
765
766 wxCoord wxRegionIterator::GetX() const
767 {
768 if( !HaveRects() ) return 0;
769 return ((wxRIRefData*)m_refData)->m_rects[m_current].x;
770 }
771
772 wxCoord wxRegionIterator::GetY() const
773 {
774 if( !HaveRects() ) return 0;
775 return ((wxRIRefData*)m_refData)->m_rects[m_current].y;
776 }
777
778 wxCoord wxRegionIterator::GetW() const
779 {
780 if( !HaveRects() ) return -1;
781 return ((wxRIRefData*)m_refData)->m_rects[m_current].width;
782 }
783
784 wxCoord wxRegionIterator::GetH() const
785 {
786 if( !HaveRects() ) return -1;
787 return ((wxRIRefData*)m_refData)->m_rects[m_current].height;
788 }
789
790 wxRect wxRegionIterator::GetRect() const
791 {
792 wxRect r;
793 if( HaveRects() )
794 r = ((wxRIRefData*)m_refData)->m_rects[m_current];
795
796 return r;
797 }
798
799 #endif
800