fix Union(empty rect) bug
[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/log.h"
24
25 #include "wx/region.h"
26
27 #include "wx/gtk/private.h"
28
29 #ifndef __WXGTK20__
30
31 // ----------------------------------------------------------------------------
32 // wxGdkRegion: creates a new region in ctor and destroys in dtor
33 // ----------------------------------------------------------------------------
34
35 class wxGdkRegion
36 {
37 public:
38 wxGdkRegion() { m_region = gdk_region_new(); }
39 ~wxGdkRegion() { gdk_region_destroy(m_region); }
40
41 operator GdkRegion *() const { return m_region; }
42
43 private:
44 GdkRegion *m_region;
45 };
46
47 #endif // __WXGTK20__
48
49 // ----------------------------------------------------------------------------
50 // wxRegionRefData: private class containing the information about the region
51 // ----------------------------------------------------------------------------
52
53 class wxRegionRefData : public wxObjectRefData
54 {
55 public:
56 wxRegionRefData()
57 {
58 m_region = NULL;
59 }
60
61 wxRegionRefData(const wxRegionRefData& refData)
62 : wxObjectRefData()
63 {
64 #ifdef __WXGTK20__
65 m_region = gdk_region_copy(refData.m_region);
66 #else
67 m_region = gdk_regions_union(wxGdkRegion(), refData.m_region);
68 #endif
69 }
70
71 ~wxRegionRefData()
72 {
73 if (m_region)
74 gdk_region_destroy( m_region );
75 }
76
77 GdkRegion *m_region;
78 };
79
80 // ----------------------------------------------------------------------------
81 // macros
82 // ----------------------------------------------------------------------------
83
84 #define M_REGIONDATA ((wxRegionRefData *)m_refData)
85 #define M_REGIONDATA_OF(rgn) ((wxRegionRefData *)(rgn.m_refData))
86
87 IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
88 IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator,wxObject)
89
90 // ----------------------------------------------------------------------------
91 // wxRegion construction
92 // ----------------------------------------------------------------------------
93
94 #define M_REGIONDATA ((wxRegionRefData *)m_refData)
95
96 void wxRegion::InitRect(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
97 {
98 GdkRectangle rect;
99 rect.x = x;
100 rect.y = y;
101 rect.width = w;
102 rect.height = h;
103
104 m_refData = new wxRegionRefData();
105
106 #ifdef __WXGTK20__
107 M_REGIONDATA->m_region = gdk_region_rectangle( &rect );
108 #else
109 M_REGIONDATA->m_region = gdk_region_union_with_rect( wxGdkRegion(), &rect );
110 #endif
111 }
112
113 wxRegion::wxRegion( GdkRegion *region )
114 {
115 m_refData = new wxRegionRefData();
116 #ifdef __WXGTK20__
117 M_REGIONDATA->m_region = gdk_region_copy( region );
118 #else
119 M_REGIONDATA->m_region = gdk_regions_union(wxGdkRegion(), region);
120 #endif
121 }
122
123 wxRegion::wxRegion( size_t n, const wxPoint *points, int fillStyle )
124 {
125 GdkPoint *gdkpoints = new GdkPoint[n];
126 for ( size_t i = 0 ; i < n ; i++ )
127 {
128 gdkpoints[i].x = points[i].x;
129 gdkpoints[i].y = points[i].y;
130 }
131
132 m_refData = new wxRegionRefData();
133
134 GdkRegion* reg = gdk_region_polygon
135 (
136 gdkpoints,
137 n,
138 fillStyle == wxWINDING_RULE ? GDK_WINDING_RULE
139 : GDK_EVEN_ODD_RULE
140 );
141
142 M_REGIONDATA->m_region = reg;
143
144 delete [] gdkpoints;
145 }
146
147 wxRegion::~wxRegion()
148 {
149 // m_refData unrefed in ~wxObject
150 }
151
152 wxObjectRefData *wxRegion::CreateRefData() const
153 {
154 return new wxRegionRefData;
155 }
156
157 wxObjectRefData *wxRegion::CloneRefData(const wxObjectRefData *data) const
158 {
159 return new wxRegionRefData(*(wxRegionRefData *)data);
160 }
161
162 // ----------------------------------------------------------------------------
163 // wxRegion comparison
164 // ----------------------------------------------------------------------------
165
166 bool wxRegion::operator==( const wxRegion& region )
167 {
168 if (m_refData == region.m_refData) return TRUE;
169
170 if (!m_refData || !region.m_refData) return FALSE;
171
172 // compare the regions themselves, not the pointers to ref data!
173 return gdk_region_equal(M_REGIONDATA->m_region,
174 M_REGIONDATA_OF(region)->m_region);
175 }
176
177 // ----------------------------------------------------------------------------
178 // wxRegion operations
179 // ----------------------------------------------------------------------------
180
181 void wxRegion::Clear()
182 {
183 UnRef();
184 }
185
186 bool wxRegion::Union( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
187 {
188 // workaround for a strange GTK/X11 bug: taking union with an empty
189 // rectangle results in an empty region which is definitely not what we
190 // want
191 if ( !width || !height )
192 return TRUE;
193
194 GdkRectangle rect;
195 rect.x = x;
196 rect.y = y;
197 rect.width = width;
198 rect.height = height;
199
200 if (!m_refData)
201 {
202 m_refData = new wxRegionRefData();
203 #ifdef __WXGTK20__
204 M_REGIONDATA->m_region = gdk_region_rectangle( &rect );
205 #else
206 M_REGIONDATA->m_region = gdk_region_union_with_rect(wxGdkRegion(), &rect);
207 #endif
208 }
209 else
210 {
211 AllocExclusive();
212
213 #ifdef __WXGTK20__
214 gdk_region_union_with_rect( M_REGIONDATA->m_region, &rect );
215 #else
216 GdkRegion *reg = gdk_region_union_with_rect( M_REGIONDATA->m_region, &rect );
217 gdk_region_destroy( M_REGIONDATA->m_region );
218 M_REGIONDATA->m_region = reg;
219 #endif
220 }
221
222 return TRUE;
223 }
224
225 bool wxRegion::Union( const wxRect& rect )
226 {
227 return Union( rect.x, rect.y, rect.width, rect.height );
228 }
229
230 bool wxRegion::Union( const wxRegion& region )
231 {
232 if (region.IsNull())
233 return FALSE;
234
235 if (!m_refData)
236 {
237 m_refData = new wxRegionRefData();
238 M_REGIONDATA->m_region = gdk_region_new();
239 }
240 else
241 {
242 AllocExclusive();
243 }
244
245 #ifdef __WXGTK20__
246 gdk_region_union( M_REGIONDATA->m_region, region.GetRegion() );
247 #else
248 GdkRegion *reg = gdk_regions_union( M_REGIONDATA->m_region, region.GetRegion() );
249 gdk_region_destroy( M_REGIONDATA->m_region );
250 M_REGIONDATA->m_region = reg;
251 #endif
252
253 return TRUE;
254 }
255
256 bool wxRegion::Intersect( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
257 {
258 wxRegion reg( x, y, width, height );
259
260 return Intersect( reg );
261 }
262
263 bool wxRegion::Intersect( const wxRect& rect )
264 {
265 wxRegion reg( rect );
266
267 return Intersect( reg );
268 }
269
270 bool wxRegion::Intersect( const wxRegion& region )
271 {
272 if (region.IsNull())
273 return FALSE;
274
275 if (!m_refData)
276 {
277 m_refData = new wxRegionRefData();
278 M_REGIONDATA->m_region = gdk_region_new();
279
280 // leave here
281 return TRUE;
282 }
283 else
284 {
285 AllocExclusive();
286 }
287
288 #ifdef __WXGTK20__
289 gdk_region_intersect( M_REGIONDATA->m_region, region.GetRegion() );
290 #else
291 GdkRegion *reg = gdk_regions_intersect( M_REGIONDATA->m_region, region.GetRegion() );
292 gdk_region_destroy( M_REGIONDATA->m_region );
293 M_REGIONDATA->m_region = reg;
294 #endif
295
296 return TRUE;
297 }
298
299 bool wxRegion::Subtract( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
300 {
301 wxRegion reg( x, y, width, height );
302 return Subtract( reg );
303 }
304
305 bool wxRegion::Subtract( const wxRect& rect )
306 {
307 wxRegion reg( rect );
308 return Subtract( reg );
309 }
310
311 bool wxRegion::Subtract( const wxRegion& region )
312 {
313 if (region.IsNull())
314 return FALSE;
315
316 if (!m_refData)
317 {
318 m_refData = new wxRegionRefData();
319 M_REGIONDATA->m_region = gdk_region_new();
320 }
321 else
322 {
323 AllocExclusive();
324 }
325
326 #ifdef __WXGTK20__
327 gdk_region_subtract( M_REGIONDATA->m_region, region.GetRegion() );
328 #else
329 GdkRegion *reg = gdk_regions_subtract( M_REGIONDATA->m_region, region.GetRegion() );
330 gdk_region_destroy( M_REGIONDATA->m_region );
331 M_REGIONDATA->m_region = reg;
332 #endif
333
334 return TRUE;
335 }
336
337 bool wxRegion::Xor( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
338 {
339 wxRegion reg( x, y, width, height );
340 return Xor( reg );
341 }
342
343 bool wxRegion::Xor( const wxRect& rect )
344 {
345 wxRegion reg( rect );
346 return Xor( reg );
347 }
348
349 bool wxRegion::Xor( const wxRegion& region )
350 {
351 if (region.IsNull())
352 return FALSE;
353
354 if (!m_refData)
355 {
356 m_refData = new wxRegionRefData();
357 M_REGIONDATA->m_region = gdk_region_new();
358 }
359 else
360 {
361 AllocExclusive();
362 }
363
364 #ifdef __WXGTK20__
365 gdk_region_xor( M_REGIONDATA->m_region, region.GetRegion() );
366 #else
367 GdkRegion *reg = gdk_regions_xor( M_REGIONDATA->m_region, region.GetRegion() );
368 gdk_region_destroy( M_REGIONDATA->m_region );
369 M_REGIONDATA->m_region = reg;
370 #endif
371
372 return TRUE;
373 }
374
375 // ----------------------------------------------------------------------------
376 // wxRegion tests
377 // ----------------------------------------------------------------------------
378
379 void wxRegion::GetBox( wxCoord &x, wxCoord &y, wxCoord &w, wxCoord &h ) const
380 {
381 if ( m_refData )
382 {
383 GdkRectangle rect;
384 gdk_region_get_clipbox( M_REGIONDATA->m_region, &rect );
385 x = rect.x;
386 y = rect.y;
387 w = rect.width;
388 h = rect.height;
389 }
390 else
391 {
392 x = 0;
393 y = 0;
394 w = -1;
395 h = -1;
396 }
397 }
398
399 wxRect wxRegion::GetBox() const
400 {
401 wxCoord x, y, w, h;
402 GetBox( x, y, w, h );
403 return wxRect( x, y, w, h );
404 }
405
406 bool wxRegion::Offset( wxCoord x, wxCoord y )
407 {
408 if (!m_refData)
409 return FALSE;
410
411 AllocExclusive();
412
413 gdk_region_offset( M_REGIONDATA->m_region, x, y );
414
415 return TRUE;
416 }
417
418 bool wxRegion::Empty() const
419 {
420 if (!m_refData)
421 return TRUE;
422
423 return gdk_region_empty( M_REGIONDATA->m_region );
424 }
425
426 wxRegionContain wxRegion::Contains( wxCoord x, wxCoord y ) const
427 {
428 if (!m_refData)
429 return wxOutRegion;
430
431 if (gdk_region_point_in( M_REGIONDATA->m_region, x, y ))
432 return wxInRegion;
433 else
434 return wxOutRegion;
435 }
436
437 wxRegionContain wxRegion::Contains( wxCoord x, wxCoord y, wxCoord w, wxCoord h ) const
438 {
439 if (!m_refData)
440 return wxOutRegion;
441
442 GdkRectangle rect;
443 rect.x = x;
444 rect.y = y;
445 rect.width = w;
446 rect.height = h;
447 GdkOverlapType res = gdk_region_rect_in( M_REGIONDATA->m_region, &rect );
448 switch (res)
449 {
450 case GDK_OVERLAP_RECTANGLE_IN: return wxInRegion;
451 case GDK_OVERLAP_RECTANGLE_OUT: return wxOutRegion;
452 case GDK_OVERLAP_RECTANGLE_PART: return wxPartRegion;
453 }
454 return wxOutRegion;
455 }
456
457 wxRegionContain wxRegion::Contains(const wxPoint& pt) const
458 {
459 return Contains( pt.x, pt.y );
460 }
461
462 wxRegionContain wxRegion::Contains(const wxRect& rect) const
463 {
464 return Contains( rect.x, rect.y, rect.width, rect.height );
465 }
466
467 GdkRegion *wxRegion::GetRegion() const
468 {
469 if (!m_refData)
470 return (GdkRegion*) NULL;
471
472 return M_REGIONDATA->m_region;
473 }
474
475 // ----------------------------------------------------------------------------
476 // wxRegionIterator
477 // ----------------------------------------------------------------------------
478
479 #ifndef __WXGTK20__
480
481 // the following structures must match the private structures
482 // in X11 region code ( xc/lib/X11/region.h )
483
484 // this makes the Region type transparent
485 // and we have access to the region rectangles
486
487 #include <gdk/gdkprivate.h>
488
489 struct _XBox {
490 short x1, x2, y1, y2;
491 };
492
493 struct _XRegion {
494 long size , numRects;
495 _XBox *rects, extents;
496 };
497
498 #endif
499
500 class wxRIRefData: public wxObjectRefData
501 {
502 public:
503 wxRIRefData() { Init(); }
504 virtual ~wxRIRefData();
505
506 void CreateRects( const wxRegion& r );
507
508 void Init() { m_rects = NULL; m_numRects = 0; }
509
510 wxRect *m_rects;
511 size_t m_numRects;
512 };
513
514 wxRIRefData::~wxRIRefData()
515 {
516 delete [] m_rects;
517 }
518
519 void wxRIRefData::CreateRects( const wxRegion& region )
520 {
521 delete [] m_rects;
522
523 Init();
524
525 GdkRegion *gdkregion = region.GetRegion();
526 if (!gdkregion)
527 return;
528
529 #ifdef __WXGTK20__
530 GdkRectangle *gdkrects = NULL;
531 gint numRects = 0;
532 gdk_region_get_rectangles( gdkregion, &gdkrects, &numRects );
533
534 m_numRects = numRects;
535 if (numRects)
536 {
537 m_rects = new wxRect[m_numRects];
538 for (size_t i=0; i < m_numRects; ++i)
539 {
540 GdkRectangle &gr = gdkrects[i];
541 wxRect &wr = m_rects[i];
542 wr.x = gr.x;
543 wr.y = gr.y;
544 wr.width = gr.width;
545 wr.height = gr.height;
546 }
547 }
548 g_free( gdkrects );
549 #else // GTK+ 1.x
550 Region r = ((GdkRegionPrivate *)gdkregion)->xregion;
551 if (r)
552 {
553 m_numRects = r->numRects;
554 if (m_numRects)
555 {
556 m_rects = new wxRect[m_numRects];
557 for (size_t i=0; i < m_numRects; ++i)
558 {
559 _XBox &xr = r->rects[i];
560 wxRect &wr = m_rects[i];
561 wr.x = xr.x1;
562 wr.y = xr.y1;
563 wr.width = xr.x2-xr.x1;
564 wr.height = xr.y2-xr.y1;
565 }
566 }
567 }
568 #endif // GTK+ 2.0/1.x
569 }
570
571 wxRegionIterator::wxRegionIterator()
572 {
573 m_refData = new wxRIRefData();
574 Reset();
575 }
576
577 wxRegionIterator::wxRegionIterator( const wxRegion& region )
578 {
579 m_refData = new wxRIRefData();
580 Reset(region);
581 }
582
583 void wxRegionIterator::Reset( const wxRegion& region )
584 {
585 m_region = region;
586 ((wxRIRefData*)m_refData)->CreateRects(region);
587 Reset();
588 }
589
590 bool wxRegionIterator::HaveRects() const
591 {
592 return m_current < ((wxRIRefData*)m_refData)->m_numRects;
593 }
594
595 wxRegionIterator::operator bool () const
596 {
597 return HaveRects();
598 }
599
600 void wxRegionIterator::operator ++ ()
601 {
602 if (HaveRects()) ++m_current;
603 }
604
605 void wxRegionIterator::operator ++ (int)
606 {
607 if (HaveRects()) ++m_current;
608 }
609
610 wxCoord wxRegionIterator::GetX() const
611 {
612 if( !HaveRects() ) return 0;
613 return ((wxRIRefData*)m_refData)->m_rects[m_current].x;
614 }
615
616 wxCoord wxRegionIterator::GetY() const
617 {
618 if( !HaveRects() ) return 0;
619 return ((wxRIRefData*)m_refData)->m_rects[m_current].y;
620 }
621
622 wxCoord wxRegionIterator::GetW() const
623 {
624 if( !HaveRects() ) return -1;
625 return ((wxRIRefData*)m_refData)->m_rects[m_current].width;
626 }
627
628 wxCoord wxRegionIterator::GetH() const
629 {
630 if( !HaveRects() ) return -1;
631 return ((wxRIRefData*)m_refData)->m_rects[m_current].height;
632 }
633
634 wxRect wxRegionIterator::GetRect() const
635 {
636 wxRect r;
637 if( HaveRects() )
638 r = ((wxRIRefData*)m_refData)->m_rects[m_current];
639
640 return r;
641 }
642