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