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