fixed calling Union() or Offset() on a previously Clear()ed region, also removed...
[wxWidgets.git] / src / msw / region.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/region.cpp
3 // Purpose: Region handling for wxWindows/X11
4 // Author: Markus Holzem
5 // Modified by:
6 // Created: Fri Oct 24 10:46:34 MET 1997
7 // RCS-ID: $Id$
8 // Copyright: (c) 1997 Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "region.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #include "wx/msw/region.h"
24 #include "wx/gdicmn.h"
25
26 #include "wx/window.h"
27 #include "wx/msw/private.h"
28
29 IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
30 IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator, wxObject)
31
32 //-----------------------------------------------------------------------------
33 // wxRegionRefData implementation
34 //-----------------------------------------------------------------------------
35
36 class WXDLLEXPORT wxRegionRefData : public wxGDIRefData
37 {
38 public:
39 wxRegionRefData()
40 {
41 m_region = 0;
42 }
43
44 wxRegionRefData(const wxRegionRefData& data)
45 {
46 #if defined(__WIN32__) && !defined(__WXMICROWIN__)
47 DWORD noBytes = ::GetRegionData(data.m_region, 0, NULL);
48 RGNDATA *rgnData = (RGNDATA*) new char[noBytes];
49 ::GetRegionData(data.m_region, noBytes, rgnData);
50 m_region = ::ExtCreateRegion(NULL, noBytes, rgnData);
51 delete[] (char*) rgnData;
52 #else
53 RECT rect;
54 ::GetRgnBox(data.m_region, &rect);
55 m_region = ::CreateRectRgnIndirect(&rect);
56 #endif
57 }
58
59 ~wxRegionRefData()
60 {
61 ::DeleteObject(m_region);
62 m_region = 0;
63 }
64
65 HRGN m_region;
66 };
67
68 #define M_REGION (((wxRegionRefData*)m_refData)->m_region)
69 #define M_REGION_OF(rgn) (((wxRegionRefData*)(rgn.m_refData))->m_region)
70
71 //-----------------------------------------------------------------------------
72 // wxRegion
73 //-----------------------------------------------------------------------------
74
75 /*
76 * Create an empty region.
77 */
78 wxRegion::wxRegion()
79 {
80 m_refData = (wxRegionRefData *)NULL;
81 }
82
83 wxRegion::wxRegion(WXHRGN hRegion)
84 {
85 m_refData = new wxRegionRefData;
86 M_REGION = (HRGN) hRegion;
87 }
88
89 wxRegion::wxRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
90 {
91 m_refData = new wxRegionRefData;
92 M_REGION = ::CreateRectRgn(x, y, x + w, y + h);
93 }
94
95 wxRegion::wxRegion(const wxPoint& topLeft, const wxPoint& bottomRight)
96 {
97 m_refData = new wxRegionRefData;
98 M_REGION = ::CreateRectRgn(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
99 }
100
101 wxRegion::wxRegion(const wxRect& rect)
102 {
103 m_refData = new wxRegionRefData;
104 M_REGION = ::CreateRectRgn(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
105 }
106
107 wxRegion::wxRegion(size_t n, const wxPoint *points, int fillStyle)
108 {
109 #ifdef __WXMICROWIN__
110 m_refData = NULL;
111 M_REGION = NULL;
112 #else
113 m_refData = new wxRegionRefData;
114 M_REGION = ::CreatePolygonRgn
115 (
116 (POINT*)points,
117 n,
118 fillStyle == wxODDEVEN_RULE ? ALTERNATE : WINDING
119 );
120 #endif
121 }
122
123 wxRegion::~wxRegion()
124 {
125 // m_refData unrefed in ~wxObject
126 }
127
128 wxObjectRefData *wxRegion::CreateRefData() const
129 {
130 return new wxRegionRefData;
131 }
132
133 wxObjectRefData *wxRegion::CloneRefData(const wxObjectRefData *data) const
134 {
135 return new wxRegionRefData(*(wxRegionRefData *)data);
136 }
137
138 //-----------------------------------------------------------------------------
139 // Modify region
140 //-----------------------------------------------------------------------------
141
142 // Clear current region
143 void wxRegion::Clear()
144 {
145 UnRef();
146 }
147
148 bool wxRegion::Offset(wxCoord x, wxCoord y)
149 {
150 wxCHECK_MSG( M_REGION, FALSE, _T("invalid wxRegion") );
151
152 if ( !x && !y )
153 {
154 // nothing to do
155 return TRUE;
156 }
157
158 AllocExclusive();
159
160 if ( ::OffsetRgn(GetHrgn(), x, y) == ERROR )
161 {
162 wxLogLastError(_T("OffsetRgn"));
163
164 return FALSE;
165 }
166
167 return TRUE;
168 }
169
170 // combine another region with this one
171 bool wxRegion::Combine(const wxRegion& rgn, wxRegionOp op)
172 {
173 // we can't use the API functions if we don't have a valid region handle
174 if ( !m_refData )
175 {
176 // combining with an empty/invalid region works differently depending
177 // on the operation
178 switch ( op )
179 {
180 case wxRGN_COPY:
181 case wxRGN_OR:
182 case wxRGN_XOR:
183 *this = rgn;
184 break;
185
186 default:
187 wxFAIL_MSG( _T("unknown region operation") );
188 // fall through
189
190 case wxRGN_AND:
191 case wxRGN_DIFF:
192 // leave empty/invalid
193 return FALSE;
194 }
195 }
196 else // we have a valid region
197 {
198 int mode;
199 switch ( op )
200 {
201 case wxRGN_AND:
202 mode = RGN_AND;
203 break;
204
205 case wxRGN_OR:
206 mode = RGN_OR;
207 break;
208
209 case wxRGN_XOR:
210 mode = RGN_XOR;
211 break;
212
213 case wxRGN_DIFF:
214 mode = RGN_DIFF;
215 break;
216
217 default:
218 wxFAIL_MSG( _T("unknown region operation") );
219 // fall through
220
221 case wxRGN_COPY:
222 mode = RGN_COPY;
223 break;
224 }
225
226 if ( ::CombineRgn(M_REGION, M_REGION, M_REGION_OF(rgn), mode) == ERROR )
227 {
228 wxLogLastError(_T("CombineRgn"));
229
230 return FALSE;
231 }
232 }
233
234 return TRUE;
235 }
236
237 // Combine rectangle (x, y, w, h) with this.
238 bool wxRegion::Combine(wxCoord x, wxCoord y,
239 wxCoord width, wxCoord height,
240 wxRegionOp op)
241 {
242 return Combine(wxRegion(x, y, width, height), op);
243 }
244
245 bool wxRegion::Combine(const wxRect& rect, wxRegionOp op)
246 {
247 return Combine(rect.GetLeft(), rect.GetTop(),
248 rect.GetWidth(), rect.GetHeight(), op);
249 }
250
251 //-----------------------------------------------------------------------------
252 // Information on region
253 //-----------------------------------------------------------------------------
254
255 // Outer bounds of region
256 void wxRegion::GetBox(wxCoord& x, wxCoord& y, wxCoord&w, wxCoord &h) const
257 {
258 if (m_refData)
259 {
260 RECT rect;
261 ::GetRgnBox(M_REGION, & rect);
262 x = rect.left;
263 y = rect.top;
264 w = rect.right - rect.left;
265 h = rect.bottom - rect.top;
266 }
267 else
268 {
269 x = y = w = h = 0;
270 }
271 }
272
273 wxRect wxRegion::GetBox() const
274 {
275 wxCoord x, y, w, h;
276 GetBox(x, y, w, h);
277 return wxRect(x, y, w, h);
278 }
279
280 // Is region empty?
281 bool wxRegion::Empty() const
282 {
283 wxCoord x, y, w, h;
284 GetBox(x, y, w, h);
285
286 return (w == 0) && (h == 0);
287 }
288
289 //-----------------------------------------------------------------------------
290 // Tests
291 //-----------------------------------------------------------------------------
292
293 // Does the region contain the point (x,y)?
294 wxRegionContain wxRegion::Contains(wxCoord x, wxCoord y) const
295 {
296 if (!m_refData)
297 return wxOutRegion;
298
299 if (::PtInRegion(M_REGION, (int) x, (int) y))
300 return wxInRegion;
301 else
302 return wxOutRegion;
303 }
304
305 // Does the region contain the point pt?
306 wxRegionContain wxRegion::Contains(const wxPoint& pt) const
307 {
308 if (!m_refData)
309 return wxOutRegion;
310
311 if (::PtInRegion(M_REGION, (int) pt.x, (int) pt.y))
312 return wxInRegion;
313 else
314 return wxOutRegion;
315 }
316
317 // Does the region contain the rectangle (x, y, w, h)?
318 wxRegionContain wxRegion::Contains(wxCoord x, wxCoord y, wxCoord w, wxCoord h) const
319 {
320 if (!m_refData)
321 return wxOutRegion;
322
323 RECT rect;
324 rect.left = x;
325 rect.top = y;
326 rect.right = x + w;
327 rect.bottom = y + h;
328
329 if (::RectInRegion(M_REGION, & rect))
330 return wxInRegion;
331 else
332 return wxOutRegion;
333 }
334
335 // Does the region contain the rectangle rect
336 wxRegionContain wxRegion::Contains(const wxRect& rect) const
337 {
338 if (!m_refData)
339 return wxOutRegion;
340
341 wxCoord x, y, w, h;
342 x = rect.x;
343 y = rect.y;
344 w = rect.GetWidth();
345 h = rect.GetHeight();
346 return Contains(x, y, w, h);
347 }
348
349 // Get internal region handle
350 WXHRGN wxRegion::GetHRGN() const
351 {
352 if (!m_refData)
353 return (WXHRGN) 0;
354 return (WXHRGN) M_REGION;
355 }
356
357 ///////////////////////////////////////////////////////////////////////////////
358 // //
359 // wxRegionIterator //
360 // //
361 ///////////////////////////////////////////////////////////////////////////////
362
363 /*
364 * Initialize empty iterator
365 */
366 wxRegionIterator::wxRegionIterator() : m_current(0), m_numRects(0), m_rects(NULL)
367 {
368 }
369
370 wxRegionIterator::~wxRegionIterator()
371 {
372 if (m_rects)
373 delete[] m_rects;
374 }
375
376 /*
377 * Initialize iterator for region
378 */
379 wxRegionIterator::wxRegionIterator(const wxRegion& region)
380 {
381 m_rects = NULL;
382
383 Reset(region);
384 }
385
386 /*
387 * Reset iterator for a new /e region.
388 */
389 void wxRegionIterator::Reset(const wxRegion& region)
390 {
391 m_current = 0;
392 m_region = region;
393
394 if (m_rects)
395 delete[] m_rects;
396
397 m_rects = NULL;
398
399 if (m_region.Empty())
400 m_numRects = 0;
401 else
402 {
403 #if defined(__WIN32__)
404 DWORD noBytes = ::GetRegionData(((wxRegionRefData*)region.m_refData)->m_region, 0, NULL);
405 RGNDATA *rgnData = (RGNDATA*) new char[noBytes];
406 ::GetRegionData(((wxRegionRefData*)region.m_refData)->m_region, noBytes, rgnData);
407
408 RGNDATAHEADER* header = (RGNDATAHEADER*) rgnData;
409
410 m_rects = new wxRect[header->nCount];
411
412 RECT* rect = (RECT*) ((char*)rgnData + sizeof(RGNDATAHEADER));
413 size_t i;
414 for (i = 0; i < header->nCount; i++)
415 {
416 m_rects[i] = wxRect(rect->left, rect->top,
417 rect->right - rect->left, rect->bottom - rect->top);
418 rect ++; // Advances pointer by sizeof(RECT)
419 }
420
421 m_numRects = header->nCount;
422
423 delete[] (char*) rgnData;
424 #else
425 RECT rect;
426 ::GetRgnBox(((wxRegionRefData*)region.m_refData)->m_region, &rect);
427 m_rects = new wxRect[1];
428 m_rects[0].x = rect.left;
429 m_rects[0].y = rect.top;
430 m_rects[0].width = rect.right - rect.left;
431 m_rects[0].height = rect.bottom - rect.top;
432
433 m_numRects = 1;
434 #endif
435 }
436 }
437
438 /*
439 * Increment iterator. The rectangle returned is the one after the
440 * incrementation.
441 */
442 void wxRegionIterator::operator ++ ()
443 {
444 if (m_current < m_numRects)
445 ++m_current;
446 }
447
448 /*
449 * Increment iterator. The rectangle returned is the one before the
450 * incrementation.
451 */
452 void wxRegionIterator::operator ++ (int)
453 {
454 if (m_current < m_numRects)
455 ++m_current;
456 }
457
458 wxCoord wxRegionIterator::GetX() const
459 {
460 if (m_current < m_numRects)
461 return m_rects[m_current].x;
462 return 0;
463 }
464
465 wxCoord wxRegionIterator::GetY() const
466 {
467 if (m_current < m_numRects)
468 return m_rects[m_current].y;
469 return 0;
470 }
471
472 wxCoord wxRegionIterator::GetW() const
473 {
474 if (m_current < m_numRects)
475 return m_rects[m_current].width;
476 return 0;
477 }
478
479 wxCoord wxRegionIterator::GetH() const
480 {
481 if (m_current < m_numRects)
482 return m_rects[m_current].height;
483 return 0;
484 }
485