Added wxRegion::Offset(x,y)
[wxWidgets.git] / src / gtk1 / 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::Offset( wxCoord x, wxCoord y )
520 {
521 if (!m_refData)
522 return FALSE;
523
524 gdk_region_offset( M_REGIONDATA->m_region, x, y );
525
526 return TRUE;
527 }
528
529 bool wxRegion::Empty() const
530 {
531 if (!m_refData)
532 return TRUE;
533
534 return gdk_region_empty( M_REGIONDATA->m_region );
535 }
536
537 wxRegionContain wxRegion::Contains( wxCoord x, wxCoord y ) const
538 {
539 if (!m_refData)
540 return wxOutRegion;
541
542 if (gdk_region_point_in( M_REGIONDATA->m_region, x, y ))
543 return wxInRegion;
544 else
545 return wxOutRegion;
546 }
547
548 wxRegionContain wxRegion::Contains( wxCoord x, wxCoord y, wxCoord w, wxCoord h ) const
549 {
550 if (!m_refData)
551 return wxOutRegion;
552
553 GdkRectangle rect;
554 rect.x = x;
555 rect.y = y;
556 rect.width = w;
557 rect.height = h;
558 GdkOverlapType res = gdk_region_rect_in( M_REGIONDATA->m_region, &rect );
559 switch (res)
560 {
561 case GDK_OVERLAP_RECTANGLE_IN: return wxInRegion;
562 case GDK_OVERLAP_RECTANGLE_OUT: return wxOutRegion;
563 case GDK_OVERLAP_RECTANGLE_PART: return wxPartRegion;
564 }
565 return wxOutRegion;
566 }
567
568 wxRegionContain wxRegion::Contains(const wxPoint& pt) const
569 {
570 return Contains( pt.x, pt.y );
571 }
572
573 wxRegionContain wxRegion::Contains(const wxRect& rect) const
574 {
575 return Contains( rect.x, rect.y, rect.width, rect.height );
576 }
577
578 GdkRegion *wxRegion::GetRegion() const
579 {
580 if (!m_refData)
581 return (GdkRegion*) NULL;
582
583 return M_REGIONDATA->m_region;
584 }
585
586 wxList *wxRegion::GetRectList() const
587 {
588 #if OLDCODE
589 if (!m_refData)
590 return (wxList*) NULL;
591
592 return &(M_REGIONDATA->m_rects);
593 #else
594 return (wxList*) NULL;
595 #endif
596 }
597
598 // ----------------------------------------------------------------------------
599 // wxRegionIterator
600 // ----------------------------------------------------------------------------
601
602 #if OLDCODE
603
604 wxRegionIterator::wxRegionIterator()
605 {
606 Reset();
607 }
608
609 wxRegionIterator::wxRegionIterator( const wxRegion& region )
610 {
611 Reset(region);
612 }
613
614 void wxRegionIterator::Reset( const wxRegion& region )
615 {
616 m_region = region;
617 Reset();
618 }
619
620 wxRegionIterator::operator bool () const
621 {
622 return m_region.GetRectList() && m_current < (size_t)m_region.GetRectList()->Number();
623 }
624
625 bool wxRegionIterator::HaveRects() const
626 {
627 return m_region.GetRectList() && m_current < (size_t)m_region.GetRectList()->Number();
628 }
629
630 void wxRegionIterator::operator ++ ()
631 {
632 if (HaveRects()) ++m_current;
633 }
634
635 void wxRegionIterator::operator ++ (int)
636 {
637 if (HaveRects()) ++m_current;
638 }
639
640 wxCoord wxRegionIterator::GetX() const
641 {
642 wxNode *node = m_region.GetRectList()->Nth( m_current );
643 if (!node) return 0;
644 wxRect *r = (wxRect*)node->Data();
645 return r->x;
646 }
647
648 wxCoord wxRegionIterator::GetY() const
649 {
650 wxNode *node = m_region.GetRectList()->Nth( m_current );
651 if (!node) return 0;
652 wxRect *r = (wxRect*)node->Data();
653 return r->y;
654 }
655
656 wxCoord wxRegionIterator::GetW() const
657 {
658 wxNode *node = m_region.GetRectList()->Nth( m_current );
659 if (!node) return 0;
660 wxRect *r = (wxRect*)node->Data();
661 return r->width;
662 }
663
664 wxCoord wxRegionIterator::GetH() const
665 {
666 wxNode *node = m_region.GetRectList()->Nth( m_current );
667 if (!node) return 0;
668 wxRect *r = (wxRect*)node->Data();
669 return r->height;
670 }
671
672 #else
673
674 // the following structures must match the private structures
675 // in X11 region code ( xc/lib/X11/region.h )
676
677 // this makes the Region type transparent
678 // and we have access to the region rectangles
679
680 struct _XBox {
681 short x1, x2, y1, y2;
682 };
683
684 struct _XRegion {
685 long size , numRects;
686 _XBox *rects, extents;
687 };
688
689 class wxRIRefData: public wxObjectRefData
690 {
691 public:
692
693 wxRIRefData() : m_rects(0), m_numRects(0){}
694 ~wxRIRefData();
695
696 wxRect *m_rects;
697 size_t m_numRects;
698
699 void CreateRects( const wxRegion& r );
700 };
701
702 wxRIRefData::~wxRIRefData()
703 {
704 delete m_rects;
705 }
706
707 #include <gdk/gdkprivate.h>
708
709 void wxRIRefData::CreateRects( const wxRegion& region )
710 {
711 if( m_rects )
712 delete m_rects;
713 m_rects = 0;
714 m_numRects= 0;
715 GdkRegion *gdkregion= region.GetRegion();
716 if( gdkregion ){
717 Region r= ((GdkRegionPrivate *)gdkregion)->xregion;
718 if( r ){
719 m_numRects= r->numRects;
720 if( m_numRects )
721 {
722 m_rects= new wxRect[m_numRects];
723 for( size_t i=0; i<m_numRects; ++i )
724 {
725 _XBox &xr= r->rects[i];
726 wxRect&wr= m_rects[i];
727 wr.x = xr.x1;
728 wr.y = xr.y1;
729 wr.width = xr.x2-xr.x1;
730 wr.height= xr.y2-xr.y1;
731 }
732 }
733 }
734 }
735 }
736
737 wxRegionIterator::wxRegionIterator()
738 {
739 m_refData = new wxRIRefData();
740 Reset();
741 }
742
743 wxRegionIterator::wxRegionIterator( const wxRegion& region )
744 {
745 m_refData = new wxRIRefData();
746 Reset(region);
747 }
748
749 void wxRegionIterator::Reset( const wxRegion& region )
750 {
751 m_region = region;
752 ((wxRIRefData*)m_refData)->CreateRects(region);
753 Reset();
754 }
755
756 bool wxRegionIterator::HaveRects() const
757 {
758 return m_current < ((wxRIRefData*)m_refData)->m_numRects;
759 }
760
761 wxRegionIterator::operator bool () const
762 {
763 return HaveRects();
764 }
765
766 void wxRegionIterator::operator ++ ()
767 {
768 if (HaveRects()) ++m_current;
769 }
770
771 void wxRegionIterator::operator ++ (int)
772 {
773 if (HaveRects()) ++m_current;
774 }
775
776 wxCoord wxRegionIterator::GetX() const
777 {
778 if( !HaveRects() ) return 0;
779 return ((wxRIRefData*)m_refData)->m_rects[m_current].x;
780 }
781
782 wxCoord wxRegionIterator::GetY() const
783 {
784 if( !HaveRects() ) return 0;
785 return ((wxRIRefData*)m_refData)->m_rects[m_current].y;
786 }
787
788 wxCoord wxRegionIterator::GetW() const
789 {
790 if( !HaveRects() ) return -1;
791 return ((wxRIRefData*)m_refData)->m_rects[m_current].width;
792 }
793
794 wxCoord wxRegionIterator::GetH() const
795 {
796 if( !HaveRects() ) return -1;
797 return ((wxRIRefData*)m_refData)->m_rects[m_current].height;
798 }
799
800 wxRect wxRegionIterator::GetRect() const
801 {
802 wxRect r;
803 if( HaveRects() )
804 r = ((wxRIRefData*)m_refData)->m_rects[m_current];
805
806 return r;
807 }
808
809 #endif
810