]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/region.cpp
making ref counting behavior consistent, activating QD variante for polygon region...
[wxWidgets.git] / src / mac / carbon / region.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // File: src/mac/carbon/region.cpp
3 // Purpose: Region class
4 // Author: Stefan Csomor
5 // Created: Fri Oct 24 10:46:34 MET 1997
6 // RCS-ID: $Id$
7 // Copyright: (c) 1997 Stefan Csomor
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #include "wx/region.h"
14
15 #ifndef WX_PRECOMP
16 #include "wx/gdicmn.h"
17 #endif
18
19 #include "wx/mac/uma.h"
20
21 IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
22 IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator, wxObject)
23
24 //-----------------------------------------------------------------------------
25 // wxRegionRefData implementation
26 //-----------------------------------------------------------------------------
27
28 class WXDLLEXPORT wxRegionRefData : public wxGDIRefData
29 {
30 public:
31 wxRegionRefData()
32 {
33 m_macRgn.reset( HIShapeCreateMutable() );
34 }
35
36 wxRegionRefData(wxCFRef<HIShapeRef> &region)
37 {
38 m_macRgn.reset( HIShapeCreateMutableCopy(region) );
39 }
40
41 wxRegionRefData(long x, long y, long w, long h)
42 {
43 CGRect r = CGRectMake(x,y,w,h);
44 wxCFRef<HIShapeRef> rect(HIShapeCreateWithRect(&r));
45 m_macRgn.reset( HIShapeCreateMutableCopy(rect) );
46 }
47
48 wxRegionRefData(const wxRegionRefData& data)
49 : wxGDIRefData()
50 {
51 m_macRgn.reset( HIShapeCreateMutableCopy(data.m_macRgn) );
52 }
53
54 virtual ~wxRegionRefData()
55 {
56 }
57
58 wxCFRef<HIMutableShapeRef> m_macRgn;
59 };
60
61 #define M_REGION (((wxRegionRefData*)m_refData)->m_macRgn)
62 #define OTHER_M_REGION(a) (((wxRegionRefData*)(a.m_refData))->m_macRgn)
63
64 //-----------------------------------------------------------------------------
65 // wxRegion
66 //-----------------------------------------------------------------------------
67
68 /*!
69 * Create an empty region.
70 */
71 wxRegion::wxRegion()
72 {
73 m_refData = new wxRegionRefData();
74 }
75
76 wxRegion::wxRegion(WXHRGN hRegion )
77 {
78 wxCFRef< HIShapeRef > shape( (HIShapeRef) hRegion );
79 m_refData = new wxRegionRefData(shape);
80 }
81
82 wxRegion::wxRegion(long x, long y, long w, long h)
83 {
84 m_refData = new wxRegionRefData(x , y , w , h );
85 }
86
87 wxRegion::wxRegion(const wxPoint& topLeft, const wxPoint& bottomRight)
88 {
89 m_refData = new wxRegionRefData(topLeft.x , topLeft.y ,
90 topLeft.x - bottomRight.x ,
91 topLeft.y - bottomRight.y);
92 }
93
94 wxRegion::wxRegion(const wxRect& rect)
95 {
96 m_refData = new wxRegionRefData(rect.x , rect.y , rect.width , rect.height);
97 }
98
99 wxRegion::wxRegion(size_t n, const wxPoint *points, int WXUNUSED(fillStyle))
100 {
101 wxUnusedVar(n);
102 wxUnusedVar(points);
103
104 #ifndef __LP64__
105
106 // TODO : any APIs ?
107 // OS X somehow does not collect the region invisibly as before, so sometimes things
108 // get drawn on screen instead of just being combined into a region, therefore we allocate a temp gworld now
109
110 GWorldPtr gWorld = NULL;
111 GWorldPtr oldWorld;
112 GDHandle oldGDHandle;
113 OSStatus err;
114 Rect destRect = { 0, 0, 1, 1 };
115
116 ::GetGWorld( &oldWorld, &oldGDHandle );
117 err = ::NewGWorld( &gWorld, 32, &destRect, NULL, NULL, 0 );
118 if ( err == noErr )
119 {
120 ::SetGWorld( gWorld, GetGDevice() );
121
122 OpenRgn();
123
124 wxCoord x1, x2 , y1 , y2 ;
125 x2 = x1 = points[0].x ;
126 y2 = y1 = points[0].y ;
127
128 ::MoveTo( x1, y1 );
129 for (size_t i = 1; i < n; i++)
130 {
131 x2 = points[i].x ;
132 y2 = points[i].y ;
133 ::LineTo( x2, y2 );
134 }
135
136 // close the polyline if necessary
137 if ( x1 != x2 || y1 != y2 )
138 ::LineTo( x1, y1 ) ;
139
140 RgnHandle tempRgn = NewRgn();
141 CloseRgn( tempRgn ) ;
142
143 ::SetGWorld( oldWorld, oldGDHandle );
144 wxCFRef<HIShapeRef> tempShape( HIShapeCreateWithQDRgn(tempRgn ) );
145 m_refData = new wxRegionRefData(tempShape);
146 DisposeRgn( tempRgn );
147 }
148 else
149 {
150 m_refData = new wxRegionRefData;
151 }
152 #else
153 wxFAIL_MSG( "not implemented" );
154 m_refData = NULL;
155 #endif
156 }
157
158 wxRegion::~wxRegion()
159 {
160 // m_refData unrefed in ~wxObject
161 }
162
163 //-----------------------------------------------------------------------------
164 //# Modify region
165 //-----------------------------------------------------------------------------
166
167 //! Clear current region
168 void wxRegion::Clear()
169 {
170 UnRef();
171 }
172
173 // Move the region
174 bool wxRegion::DoOffset(wxCoord x, wxCoord y)
175 {
176 wxCHECK_MSG( M_REGION, false, _T("invalid wxRegion") );
177
178 if ( !x && !y )
179 // nothing to do
180 return true;
181
182 verify_noerr( HIShapeOffset( M_REGION , x , y ) ) ;
183
184 return true ;
185 }
186
187
188 //! Union /e region with this.
189 bool wxRegion::DoCombine(const wxRegion& region, wxRegionOp op)
190 {
191 wxCHECK_MSG( region.Ok(), false, _T("invalid wxRegion") );
192
193 // Don't change shared data
194 if (!m_refData)
195 {
196 m_refData = new wxRegionRefData();
197 }
198 else if (m_refData->GetRefCount() > 1)
199 {
200 wxRegionRefData* ref = (wxRegionRefData*)m_refData;
201 UnRef();
202 m_refData = new wxRegionRefData(*ref);
203 }
204
205 switch (op)
206 {
207 case wxRGN_AND:
208 verify_noerr( HIShapeIntersect( M_REGION , OTHER_M_REGION(region) , M_REGION ) );
209 break ;
210
211 case wxRGN_OR:
212 verify_noerr( HIShapeUnion( M_REGION , OTHER_M_REGION(region) , M_REGION ) );
213 break ;
214
215 case wxRGN_XOR:
216 {
217 // XOR is defined as the difference between union and intersection
218 wxCFRef< HIShapeRef > unionshape( HIShapeCreateUnion( M_REGION , OTHER_M_REGION(region) ) );
219 wxCFRef< HIShapeRef > intersectionshape( HIShapeCreateIntersection( M_REGION , OTHER_M_REGION(region) ) );
220 verify_noerr( HIShapeDifference( unionshape, intersectionshape, M_REGION ) );
221 }
222 break ;
223
224 case wxRGN_DIFF:
225 verify_noerr( HIShapeDifference( M_REGION , OTHER_M_REGION(region) , M_REGION ) ) ;
226 break ;
227
228 case wxRGN_COPY:
229 default:
230 M_REGION.reset( HIShapeCreateMutableCopy( OTHER_M_REGION(region) ) );
231 break ;
232 }
233
234 return true;
235 }
236
237 //-----------------------------------------------------------------------------
238 //# Information on region
239 //-----------------------------------------------------------------------------
240
241 bool wxRegion::DoIsEqual(const wxRegion& WXUNUSED(region)) const
242 {
243 wxFAIL_MSG( _T("not implemented") );
244
245 return false;
246 }
247
248 // Outer bounds of region
249 bool wxRegion::DoGetBox(wxCoord& x, wxCoord& y, wxCoord& w, wxCoord& h) const
250 {
251 if (m_refData)
252 {
253 CGRect box ;
254 HIShapeGetBounds( M_REGION , &box ) ;
255 x = wx_static_cast(int, box.origin.x);
256 y = wx_static_cast(int, box.origin.y);
257 w = wx_static_cast(int, box.size.width);
258 h = wx_static_cast(int, box.size.height);
259
260 return true;
261 }
262 else
263 {
264 x = y = w = h = 0;
265
266 return false;
267 }
268 }
269
270 // Is region empty?
271 bool wxRegion::IsEmpty() const
272 {
273 if ( m_refData )
274 return HIShapeIsEmpty( M_REGION ) ;
275 else
276 return true ;
277 }
278
279 const WXHRGN wxRegion::GetWXHRGN() const
280 {
281 return M_REGION ;
282 }
283
284 //-----------------------------------------------------------------------------
285 //# Tests
286 //-----------------------------------------------------------------------------
287
288 // Does the region contain the point?
289 wxRegionContain wxRegion::DoContainsPoint(wxCoord x, wxCoord y) const
290 {
291 if (!m_refData)
292 return wxOutRegion;
293
294 CGPoint p = { y , x } ;
295 if (HIShapeContainsPoint( M_REGION , &p ) )
296 return wxInRegion;
297
298 return wxOutRegion;
299 }
300
301 // Does the region contain the rectangle (x, y, w, h)?
302 wxRegionContain wxRegion::DoContainsRect(const wxRect& r) const
303 {
304 if (!m_refData)
305 return wxOutRegion;
306
307 CGRect rect = CGRectMake(r.x,r.y,r.width,r.height);
308 wxCFRef<HIShapeRef> rectshape(HIShapeCreateWithRect(&rect));
309 wxCFRef<HIShapeRef> intersect(HIShapeCreateIntersection(rectshape,M_REGION));
310 CGRect bounds;
311 HIShapeGetBounds(intersect, &bounds);
312
313 if ( HIShapeIsRectangular(intersect) && CGRectEqualToRect(rect,bounds) )
314 return wxInRegion;
315 else if ( HIShapeIsEmpty( intersect ) )
316 return wxOutRegion;
317 else
318 return wxPartRegion;
319 }
320
321 ///////////////////////////////////////////////////////////////////////////////
322 // //
323 // wxRegionIterator //
324 // //
325 ///////////////////////////////////////////////////////////////////////////////
326
327 /*!
328 * Initialize empty iterator
329 */
330 wxRegionIterator::wxRegionIterator()
331 : m_current(0), m_numRects(0), m_rects(NULL)
332 {
333 }
334
335 wxRegionIterator::~wxRegionIterator()
336 {
337 if (m_rects)
338 {
339 delete [] m_rects;
340 m_rects = NULL;
341 }
342 }
343
344 wxRegionIterator::wxRegionIterator(const wxRegionIterator& iterator)
345 : wxObject()
346 , m_current(iterator.m_current)
347 , m_numRects(0)
348 , m_rects(NULL)
349 {
350 SetRects(iterator.m_numRects, iterator.m_rects);
351 }
352
353 wxRegionIterator& wxRegionIterator::operator=(const wxRegionIterator& iterator)
354 {
355 m_current = iterator.m_current;
356 SetRects(iterator.m_numRects, iterator.m_rects);
357
358 return *this;
359 }
360
361 /*!
362 * Set iterator rects for region
363 */
364 void wxRegionIterator::SetRects(long numRects, wxRect *rects)
365 {
366 if (m_rects)
367 {
368 delete [] m_rects;
369 m_rects = NULL;
370 }
371
372 if (rects && (numRects > 0))
373 {
374 int i;
375
376 m_rects = new wxRect[numRects];
377 for (i = 0; i < numRects; i++)
378 m_rects[i] = rects[i];
379 }
380
381 m_numRects = numRects;
382 }
383
384 /*!
385 * Initialize iterator for region
386 */
387 wxRegionIterator::wxRegionIterator(const wxRegion& region)
388 {
389 m_rects = NULL;
390
391 Reset(region);
392 }
393
394 /*!
395 * Reset iterator for a new /e region.
396 */
397
398 #ifndef __LP64__
399 OSStatus wxMacRegionToRectsCounterCallback(
400 UInt16 message, RgnHandle WXUNUSED(region), const Rect *WXUNUSED(rect), void *data )
401 {
402 long *m_numRects = (long*) data ;
403 if ( message == kQDRegionToRectsMsgInit )
404 {
405 (*m_numRects) = 0 ;
406 }
407 else if (message == kQDRegionToRectsMsgParse)
408 {
409 (*m_numRects) += 1 ;
410 }
411
412 return noErr;
413 }
414
415 class RegionToRectsCallbackData
416 {
417 public :
418 wxRect* m_rects ;
419 long m_current ;
420 };
421
422 OSStatus wxMacRegionToRectsSetterCallback(
423 UInt16 message, RgnHandle WXUNUSED(region), const Rect *rect, void *data )
424 {
425 if (message == kQDRegionToRectsMsgParse)
426 {
427 RegionToRectsCallbackData *cb = (RegionToRectsCallbackData*) data ;
428 cb->m_rects[cb->m_current++] = wxRect( rect->left , rect->top , rect->right - rect->left , rect->bottom - rect->top ) ;
429 }
430
431 return noErr;
432 }
433 #endif
434
435 void wxRegionIterator::Reset(const wxRegion& region)
436 {
437 m_current = 0;
438 m_region = region;
439
440 if (m_rects)
441 {
442 delete [] m_rects;
443 m_rects = NULL;
444 }
445
446 if (m_region.IsEmpty())
447 {
448 m_numRects = 0;
449 }
450 else
451 {
452 #ifdef __LP64__
453 // copying this to a path and dissecting the path would be an option
454 m_numRects = 1;
455 m_rects = new wxRect[m_numRects];
456 m_rects[0] = m_region.GetBox();
457
458 #else
459 RegionToRectsUPP proc = (RegionToRectsUPP) wxMacRegionToRectsCounterCallback;
460
461 OSStatus err = noErr;
462 RgnHandle rgn = NewRgn();
463 HIShapeGetAsQDRgn(OTHER_M_REGION(region), rgn);
464
465 err = QDRegionToRects (rgn, kQDParseRegionFromTopLeft, proc, (void*)&m_numRects);
466 if (err == noErr)
467 {
468 proc = (RegionToRectsUPP) wxMacRegionToRectsSetterCallback;
469 m_rects = new wxRect[m_numRects];
470 RegionToRectsCallbackData data ;
471 data.m_rects = m_rects ;
472 data.m_current = 0 ;
473 QDRegionToRects( rgn , kQDParseRegionFromTopLeft, proc, (void*)&data );
474 }
475 else
476 {
477 m_numRects = 0;
478 }
479 DisposeRgn( rgn );
480 #endif
481 }
482 }
483
484 /*!
485 * Increment iterator. The rectangle returned is the one after the
486 * incrementation.
487 */
488 wxRegionIterator& wxRegionIterator::operator ++ ()
489 {
490 if (m_current < m_numRects)
491 ++m_current;
492
493 return *this;
494 }
495
496 /*!
497 * Increment iterator. The rectangle returned is the one before the
498 * incrementation.
499 */
500 wxRegionIterator wxRegionIterator::operator ++ (int)
501 {
502 wxRegionIterator previous(*this);
503
504 if (m_current < m_numRects)
505 ++m_current;
506
507 return previous;
508 }
509
510 long wxRegionIterator::GetX() const
511 {
512 if (m_current < m_numRects)
513 return m_rects[m_current].x;
514
515 return 0;
516 }
517
518 long wxRegionIterator::GetY() const
519 {
520 if (m_current < m_numRects)
521 return m_rects[m_current].y;
522
523 return 0;
524 }
525
526 long wxRegionIterator::GetW() const
527 {
528 if (m_current < m_numRects)
529 return m_rects[m_current].width ;
530
531 return 0;
532 }
533
534 long wxRegionIterator::GetH() const
535 {
536 if (m_current < m_numRects)
537 return m_rects[m_current].height;
538
539 return 0;
540 }