wxMessageBox off the main thread lost result code.
[wxWidgets.git] / src / gtk / region.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/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 #include <gdk/gdk.h>
24
25 // ----------------------------------------------------------------------------
26 // wxRegionRefData: private class containing the information about the region
27 // ----------------------------------------------------------------------------
28
29 class wxRegionRefData : public wxGDIRefData
30 {
31 public:
32 wxRegionRefData()
33 {
34 m_region = NULL;
35 }
36
37 wxRegionRefData(const wxRegionRefData& refData)
38 : wxGDIRefData()
39 {
40 #ifdef __WXGTK3__
41 m_region = cairo_region_copy(refData.m_region);
42 #else
43 m_region = gdk_region_copy(refData.m_region);
44 #endif
45 }
46
47 virtual ~wxRegionRefData()
48 {
49 if (m_region)
50 {
51 #ifdef __WXGTK3__
52 cairo_region_destroy(m_region);
53 #else
54 gdk_region_destroy( m_region );
55 #endif
56 }
57 }
58
59 #ifdef __WXGTK3__
60 cairo_region_t* m_region;
61 #else
62 GdkRegion *m_region;
63 #endif
64 };
65
66 // ----------------------------------------------------------------------------
67 // macros
68 // ----------------------------------------------------------------------------
69
70 #define M_REGIONDATA static_cast<wxRegionRefData*>(m_refData)
71 #define M_REGIONDATA_OF(r) static_cast<wxRegionRefData*>(r.m_refData)
72
73 IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
74 IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator,wxObject)
75
76 // ----------------------------------------------------------------------------
77 // wxRegion construction
78 // ----------------------------------------------------------------------------
79
80 void wxRegion::InitRect(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
81 {
82 GdkRectangle rect;
83 rect.x = x;
84 rect.y = y;
85 rect.width = w;
86 rect.height = h;
87
88 m_refData = new wxRegionRefData();
89
90 #ifdef __WXGTK3__
91 M_REGIONDATA->m_region = cairo_region_create_rectangle(&rect);
92 #else
93 M_REGIONDATA->m_region = gdk_region_rectangle( &rect );
94 #endif
95 }
96
97 #ifndef __WXGTK3__
98 wxRegion::wxRegion(const GdkRegion* region)
99 {
100 m_refData = new wxRegionRefData();
101 M_REGIONDATA->m_region = gdk_region_copy(const_cast<GdkRegion*>(region));
102 }
103 #endif
104
105 wxRegion::wxRegion( size_t n, const wxPoint *points,
106 wxPolygonFillMode fillStyle )
107 {
108 #ifdef __WXGTK3__
109 // Make a cairo path from the points, draw it onto an image surface, use
110 // gdk_cairo_region_create_from_surface() to get a cairo region
111
112 // need at least 3 points to make a useful polygon
113 if (n < 3)
114 return;
115 // get bounding rect
116 int min_x = points[0].x;
117 int max_x = min_x;
118 int min_y = points[0].y;
119 int max_y = min_y;
120 size_t i;
121 for (i = 1; i < n; i++)
122 {
123 const int x = points[i].x;
124 if (min_x > x)
125 min_x = x;
126 else if (max_x < x)
127 max_x = x;
128 const int y = points[i].y;
129 if (min_y > y)
130 min_y = y;
131 else if (max_y < y)
132 max_y = y;
133 }
134 const int w = max_x - min_x + 1;
135 const int h = max_y - min_y + 1;
136 // make surface just big enough to contain polygon (A1 is native format
137 // for gdk_cairo_region_create_from_surface)
138 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_A1, w, h);
139 memset(cairo_image_surface_get_data(surface), 0, cairo_image_surface_get_stride(surface) * h);
140 cairo_surface_mark_dirty(surface);
141 cairo_surface_set_device_offset(surface, -min_x, -min_y);
142 cairo_t* cr = cairo_create(surface);
143 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
144 if (fillStyle == wxODDEVEN_RULE)
145 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
146 // make path
147 cairo_move_to(cr, points[0].x, points[0].y);
148 for (i = 1; i < n; i++)
149 cairo_line_to(cr, points[i].x, points[i].y);
150 cairo_close_path(cr);
151 cairo_fill(cr);
152 cairo_destroy(cr);
153 cairo_surface_flush(surface);
154 m_refData = new wxRegionRefData;
155 M_REGIONDATA->m_region = gdk_cairo_region_create_from_surface(surface);
156 cairo_surface_destroy(surface);
157 #else
158 GdkPoint *gdkpoints = new GdkPoint[n];
159 for ( size_t i = 0 ; i < n ; i++ )
160 {
161 gdkpoints[i].x = points[i].x;
162 gdkpoints[i].y = points[i].y;
163 }
164
165 m_refData = new wxRegionRefData();
166
167 GdkRegion* reg = gdk_region_polygon
168 (
169 gdkpoints,
170 n,
171 fillStyle == wxWINDING_RULE ? GDK_WINDING_RULE
172 : GDK_EVEN_ODD_RULE
173 );
174
175 M_REGIONDATA->m_region = reg;
176
177 delete [] gdkpoints;
178 #endif
179 }
180
181 wxRegion::~wxRegion()
182 {
183 // m_refData unrefed in ~wxObject
184 }
185
186 wxGDIRefData *wxRegion::CreateGDIRefData() const
187 {
188 // should never be called
189 wxFAIL;
190 return NULL;
191 }
192
193 wxGDIRefData *wxRegion::CloneGDIRefData(const wxGDIRefData *data) const
194 {
195 return new wxRegionRefData(*static_cast<const wxRegionRefData*>(data));
196 }
197
198 // ----------------------------------------------------------------------------
199 // wxRegion comparison
200 // ----------------------------------------------------------------------------
201
202 bool wxRegion::DoIsEqual(const wxRegion& region) const
203 {
204 #ifdef __WXGTK3__
205 return cairo_region_equal(
206 M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
207 #else
208 return gdk_region_equal(M_REGIONDATA->m_region,
209 M_REGIONDATA_OF(region)->m_region) != 0;
210 #endif
211 }
212
213 // ----------------------------------------------------------------------------
214 // wxRegion operations
215 // ----------------------------------------------------------------------------
216
217 void wxRegion::Clear()
218 {
219 UnRef();
220 }
221
222 bool wxRegion::DoUnionWithRect(const wxRect& r)
223 {
224 // workaround for a strange GTK/X11 bug: taking union with an empty
225 // rectangle results in an empty region which is definitely not what we
226 // want
227 if ( r.IsEmpty() )
228 return true;
229
230 if ( !m_refData )
231 {
232 InitRect(r.x, r.y, r.width, r.height);
233 }
234 else
235 {
236 AllocExclusive();
237
238 GdkRectangle rect;
239 rect.x = r.x;
240 rect.y = r.y;
241 rect.width = r.width;
242 rect.height = r.height;
243
244 #ifdef __WXGTK3__
245 cairo_region_union_rectangle(M_REGIONDATA->m_region, &rect);
246 #else
247 gdk_region_union_with_rect( M_REGIONDATA->m_region, &rect );
248 #endif
249 }
250
251 return true;
252 }
253
254 bool wxRegion::DoUnionWithRegion( const wxRegion& region )
255 {
256 if (region.m_refData == NULL)
257 { }
258 else if (m_refData == NULL)
259 {
260 m_refData = new wxRegionRefData(*M_REGIONDATA_OF(region));
261 }
262 else
263 {
264 AllocExclusive();
265 #ifdef __WXGTK3__
266 cairo_region_union(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
267 #else
268 gdk_region_union( M_REGIONDATA->m_region, region.GetRegion() );
269 #endif
270 }
271
272 return true;
273 }
274
275 bool wxRegion::DoIntersect( const wxRegion& region )
276 {
277 if (region.m_refData == NULL || m_refData == NULL)
278 return false;
279
280 AllocExclusive();
281
282 #ifdef __WXGTK3__
283 cairo_region_intersect(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
284 #else
285 gdk_region_intersect( M_REGIONDATA->m_region, region.GetRegion() );
286 #endif
287
288 return true;
289 }
290
291 bool wxRegion::DoSubtract( const wxRegion& region )
292 {
293 if (region.m_refData == NULL || m_refData == NULL)
294 return false;
295
296 AllocExclusive();
297
298 #ifdef __WXGTK3__
299 cairo_region_subtract(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
300 #else
301 gdk_region_subtract( M_REGIONDATA->m_region, region.GetRegion() );
302 #endif
303
304 return true;
305 }
306
307 bool wxRegion::DoXor( const wxRegion& region )
308 {
309 if (region.m_refData == NULL)
310 { }
311 else if (m_refData == NULL)
312 {
313 // XOR-ing with an invalid region is the same as XOR-ing with an empty
314 // one, i.e. it is simply a copy.
315 m_refData = new wxRegionRefData(*M_REGIONDATA_OF(region));
316 }
317 else
318 {
319 AllocExclusive();
320
321 #ifdef __WXGTK3__
322 cairo_region_xor(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
323 #else
324 gdk_region_xor( M_REGIONDATA->m_region, region.GetRegion() );
325 #endif
326 }
327
328 return true;
329 }
330
331 bool wxRegion::DoOffset( wxCoord x, wxCoord y )
332 {
333 wxCHECK_MSG( m_refData, false, wxS("invalid region") );
334
335 AllocExclusive();
336
337 #ifdef __WXGTK3__
338 cairo_region_translate(M_REGIONDATA->m_region, x, y);
339 #else
340 gdk_region_offset( M_REGIONDATA->m_region, x, y );
341 #endif
342
343 return true;
344 }
345
346 // ----------------------------------------------------------------------------
347 // wxRegion tests
348 // ----------------------------------------------------------------------------
349
350 bool wxRegion::DoGetBox( wxCoord &x, wxCoord &y, wxCoord &w, wxCoord &h ) const
351 {
352 if ( m_refData )
353 {
354 GdkRectangle rect;
355 #ifdef __WXGTK3__
356 cairo_region_get_extents(M_REGIONDATA->m_region, &rect);
357 #else
358 gdk_region_get_clipbox( M_REGIONDATA->m_region, &rect );
359 #endif
360 x = rect.x;
361 y = rect.y;
362 w = rect.width;
363 h = rect.height;
364
365 return true;
366 }
367 else
368 {
369 x = 0;
370 y = 0;
371 w = -1;
372 h = -1;
373
374 return false;
375 }
376 }
377
378 bool wxRegion::IsEmpty() const
379 {
380 #ifdef __WXGTK3__
381 return m_refData == NULL || cairo_region_is_empty(M_REGIONDATA->m_region);
382 #else
383 return m_refData == NULL || gdk_region_empty(M_REGIONDATA->m_region);
384 #endif
385 }
386
387 wxRegionContain wxRegion::DoContainsPoint( wxCoord x, wxCoord y ) const
388 {
389 #ifdef __WXGTK3__
390 if (m_refData == NULL || !cairo_region_contains_point(M_REGIONDATA->m_region, x, y))
391 #else
392 if (m_refData == NULL || !gdk_region_point_in(M_REGIONDATA->m_region, x, y))
393 #endif
394 return wxOutRegion;
395
396 return wxInRegion;
397 }
398
399 wxRegionContain wxRegion::DoContainsRect(const wxRect& r) const
400 {
401 if (!m_refData)
402 return wxOutRegion;
403
404 GdkRectangle rect;
405 rect.x = r.x;
406 rect.y = r.y;
407 rect.width = r.width;
408 rect.height = r.height;
409 #ifdef __WXGTK3__
410 switch (cairo_region_contains_rectangle(M_REGIONDATA->m_region, &rect))
411 {
412 case CAIRO_REGION_OVERLAP_IN: return wxInRegion;
413 case CAIRO_REGION_OVERLAP_PART: return wxPartRegion;
414 default: break;
415 }
416 #else
417 GdkOverlapType res = gdk_region_rect_in( M_REGIONDATA->m_region, &rect );
418 switch (res)
419 {
420 case GDK_OVERLAP_RECTANGLE_IN: return wxInRegion;
421 case GDK_OVERLAP_RECTANGLE_OUT: return wxOutRegion;
422 case GDK_OVERLAP_RECTANGLE_PART: return wxPartRegion;
423 }
424 #endif
425 return wxOutRegion;
426 }
427
428 #ifdef __WXGTK3__
429 cairo_region_t* wxRegion::GetRegion() const
430 #else
431 GdkRegion *wxRegion::GetRegion() const
432 #endif
433 {
434 if (!m_refData)
435 return NULL;
436
437 return M_REGIONDATA->m_region;
438 }
439
440 // ----------------------------------------------------------------------------
441 // wxRegionIterator
442 // ----------------------------------------------------------------------------
443
444 wxRegionIterator::wxRegionIterator()
445 {
446 Init();
447 Reset();
448 }
449
450 wxRegionIterator::wxRegionIterator( const wxRegion& region )
451 {
452 Init();
453 Reset(region);
454 }
455
456 void wxRegionIterator::Init()
457 {
458 m_rects = NULL;
459 m_numRects = 0;
460 }
461
462 wxRegionIterator::~wxRegionIterator()
463 {
464 wxDELETEA(m_rects);
465 }
466
467 void wxRegionIterator::CreateRects( const wxRegion& region )
468 {
469 wxDELETEA(m_rects);
470 m_numRects = 0;
471
472 #ifdef __WXGTK3__
473 cairo_region_t* cairoRegion = region.GetRegion();
474 if (cairoRegion == NULL)
475 return;
476 m_numRects = cairo_region_num_rectangles(cairoRegion);
477
478 if (m_numRects)
479 {
480 m_rects = new wxRect[m_numRects];
481 for (int i = 0; i < m_numRects; i++)
482 {
483 GdkRectangle gr;
484 cairo_region_get_rectangle(cairoRegion, i, &gr);
485 wxRect &wr = m_rects[i];
486 wr.x = gr.x;
487 wr.y = gr.y;
488 wr.width = gr.width;
489 wr.height = gr.height;
490 }
491 }
492 #else
493 GdkRegion *gdkregion = region.GetRegion();
494 if (!gdkregion)
495 return;
496
497 GdkRectangle* gdkrects;
498 gdk_region_get_rectangles(gdkregion, &gdkrects, &m_numRects);
499
500 if (m_numRects)
501 {
502 m_rects = new wxRect[m_numRects];
503 for (int i = 0; i < m_numRects; ++i)
504 {
505 GdkRectangle &gr = gdkrects[i];
506 wxRect &wr = m_rects[i];
507 wr.x = gr.x;
508 wr.y = gr.y;
509 wr.width = gr.width;
510 wr.height = gr.height;
511 }
512 }
513 g_free( gdkrects );
514 #endif
515 }
516
517 void wxRegionIterator::Reset( const wxRegion& region )
518 {
519 m_region = region;
520 CreateRects(region);
521 Reset();
522 }
523
524 bool wxRegionIterator::HaveRects() const
525 {
526 return m_current < m_numRects;
527 }
528
529 wxRegionIterator& wxRegionIterator::operator ++ ()
530 {
531 if (HaveRects())
532 ++m_current;
533
534 return *this;
535 }
536
537 wxRegionIterator wxRegionIterator::operator ++ (int)
538 {
539 wxRegionIterator tmp = *this;
540
541 if (HaveRects())
542 ++m_current;
543
544 return tmp;
545 }
546
547 wxCoord wxRegionIterator::GetX() const
548 {
549 wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
550
551 return m_rects[m_current].x;
552 }
553
554 wxCoord wxRegionIterator::GetY() const
555 {
556 wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
557
558 return m_rects[m_current].y;
559 }
560
561 wxCoord wxRegionIterator::GetW() const
562 {
563 wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
564
565 return m_rects[m_current].width;
566 }
567
568 wxCoord wxRegionIterator::GetH() const
569 {
570 wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
571
572 return m_rects[m_current].height;
573 }
574
575 wxRect wxRegionIterator::GetRect() const
576 {
577 wxRect r;
578 if( HaveRects() )
579 r = m_rects[m_current];
580
581 return r;
582 }
583
584 wxRegionIterator& wxRegionIterator::operator=(const wxRegionIterator& ri)
585 {
586 if (this != &ri)
587 {
588 wxDELETEA(m_rects);
589
590 m_current = ri.m_current;
591 m_numRects = ri.m_numRects;
592 if ( m_numRects )
593 {
594 m_rects = new wxRect[m_numRects];
595 memcpy(m_rects, ri.m_rects, m_numRects * sizeof m_rects[0]);
596 }
597 }
598 return *this;
599 }