1. added wxRegionBase class and derive all wxRegions from it 2. also added wxRegionWithCombine for the ports providing Combine() method (MSW, OS2 and Mac) to reduce code duplication further 3. made sure region comparison works the same way in all ports (except those not implementing it) and added IsEqual() method git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41429 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
469 lines
12 KiB
C++
469 lines
12 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/region.cpp
|
|
// Purpose: wxRegion implementation using Win32 API
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: Fri Oct 24 10:46:34 MET 1997
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 1997-2002 wxWidgets team
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#include "wx/region.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/gdicmn.h"
|
|
#endif
|
|
|
|
#include "wx/msw/private.h"
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
|
|
IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator, wxObject)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxRegionRefData implementation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class WXDLLEXPORT wxRegionRefData : public wxGDIRefData
|
|
{
|
|
public:
|
|
wxRegionRefData()
|
|
{
|
|
m_region = 0;
|
|
}
|
|
|
|
wxRegionRefData(const wxRegionRefData& data) : wxGDIRefData()
|
|
{
|
|
#if defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
|
|
DWORD noBytes = ::GetRegionData(data.m_region, 0, NULL);
|
|
RGNDATA *rgnData = (RGNDATA*) new char[noBytes];
|
|
::GetRegionData(data.m_region, noBytes, rgnData);
|
|
m_region = ::ExtCreateRegion(NULL, noBytes, rgnData);
|
|
delete[] (char*) rgnData;
|
|
#else
|
|
RECT rect;
|
|
::GetRgnBox(data.m_region, &rect);
|
|
m_region = ::CreateRectRgnIndirect(&rect);
|
|
#endif
|
|
}
|
|
|
|
virtual ~wxRegionRefData()
|
|
{
|
|
::DeleteObject(m_region);
|
|
m_region = 0;
|
|
}
|
|
|
|
HRGN m_region;
|
|
|
|
private:
|
|
// Cannot use
|
|
// DECLARE_NO_COPY_CLASS(wxRegionRefData)
|
|
// because copy constructor is explicitly declared above;
|
|
// but no copy assignment operator is defined, so declare
|
|
// it private to prevent the compiler from defining it:
|
|
wxRegionRefData& operator=(const wxRegionRefData&);
|
|
};
|
|
|
|
#define M_REGION (((wxRegionRefData*)m_refData)->m_region)
|
|
#define M_REGION_OF(rgn) (((wxRegionRefData*)(rgn.m_refData))->m_region)
|
|
|
|
// ============================================================================
|
|
// wxRegion implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// ctors and dtor
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxRegion::wxRegion()
|
|
{
|
|
m_refData = (wxRegionRefData *)NULL;
|
|
}
|
|
|
|
wxRegion::wxRegion(WXHRGN hRegion)
|
|
{
|
|
m_refData = new wxRegionRefData;
|
|
M_REGION = (HRGN) hRegion;
|
|
}
|
|
|
|
wxRegion::wxRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
|
|
{
|
|
m_refData = new wxRegionRefData;
|
|
M_REGION = ::CreateRectRgn(x, y, x + w, y + h);
|
|
}
|
|
|
|
wxRegion::wxRegion(const wxPoint& topLeft, const wxPoint& bottomRight)
|
|
{
|
|
m_refData = new wxRegionRefData;
|
|
M_REGION = ::CreateRectRgn(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
|
|
}
|
|
|
|
wxRegion::wxRegion(const wxRect& rect)
|
|
{
|
|
m_refData = new wxRegionRefData;
|
|
M_REGION = ::CreateRectRgn(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
|
|
}
|
|
|
|
wxRegion::wxRegion(size_t n, const wxPoint *points, int fillStyle)
|
|
{
|
|
#if defined(__WXMICROWIN__) || defined(__WXWINCE__)
|
|
wxUnusedVar(n);
|
|
wxUnusedVar(points);
|
|
wxUnusedVar(fillStyle);
|
|
m_refData = NULL;
|
|
M_REGION = NULL;
|
|
#else
|
|
m_refData = new wxRegionRefData;
|
|
M_REGION = ::CreatePolygonRgn
|
|
(
|
|
(POINT*)points,
|
|
n,
|
|
fillStyle == wxODDEVEN_RULE ? ALTERNATE : WINDING
|
|
);
|
|
#endif
|
|
}
|
|
|
|
wxRegion::~wxRegion()
|
|
{
|
|
// m_refData unrefed in ~wxObject
|
|
}
|
|
|
|
wxObjectRefData *wxRegion::CreateRefData() const
|
|
{
|
|
return new wxRegionRefData;
|
|
}
|
|
|
|
wxObjectRefData *wxRegion::CloneRefData(const wxObjectRefData *data) const
|
|
{
|
|
return new wxRegionRefData(*(wxRegionRefData *)data);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxRegion operations
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Clear current region
|
|
void wxRegion::Clear()
|
|
{
|
|
UnRef();
|
|
}
|
|
|
|
bool wxRegion::DoOffset(wxCoord x, wxCoord y)
|
|
{
|
|
wxCHECK_MSG( M_REGION, false, _T("invalid wxRegion") );
|
|
|
|
if ( !x && !y )
|
|
{
|
|
// nothing to do
|
|
return true;
|
|
}
|
|
|
|
AllocExclusive();
|
|
|
|
if ( ::OffsetRgn(GetHrgn(), x, y) == ERROR )
|
|
{
|
|
wxLogLastError(_T("OffsetRgn"));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// combine another region with this one
|
|
bool wxRegion::DoCombine(const wxRegion& rgn, wxRegionOp op)
|
|
{
|
|
// we can't use the API functions if we don't have a valid region handle
|
|
if ( !m_refData )
|
|
{
|
|
// combining with an empty/invalid region works differently depending
|
|
// on the operation
|
|
switch ( op )
|
|
{
|
|
case wxRGN_COPY:
|
|
case wxRGN_OR:
|
|
case wxRGN_XOR:
|
|
*this = rgn;
|
|
break;
|
|
|
|
default:
|
|
wxFAIL_MSG( _T("unknown region operation") );
|
|
// fall through
|
|
|
|
case wxRGN_AND:
|
|
case wxRGN_DIFF:
|
|
// leave empty/invalid
|
|
return false;
|
|
}
|
|
}
|
|
else // we have a valid region
|
|
{
|
|
AllocExclusive();
|
|
|
|
int mode;
|
|
switch ( op )
|
|
{
|
|
case wxRGN_AND:
|
|
mode = RGN_AND;
|
|
break;
|
|
|
|
case wxRGN_OR:
|
|
mode = RGN_OR;
|
|
break;
|
|
|
|
case wxRGN_XOR:
|
|
mode = RGN_XOR;
|
|
break;
|
|
|
|
case wxRGN_DIFF:
|
|
mode = RGN_DIFF;
|
|
break;
|
|
|
|
default:
|
|
wxFAIL_MSG( _T("unknown region operation") );
|
|
// fall through
|
|
|
|
case wxRGN_COPY:
|
|
mode = RGN_COPY;
|
|
break;
|
|
}
|
|
|
|
if ( ::CombineRgn(M_REGION, M_REGION, M_REGION_OF(rgn), mode) == ERROR )
|
|
{
|
|
wxLogLastError(_T("CombineRgn"));
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxRegion bounding box
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Outer bounds of region
|
|
bool wxRegion::DoGetBox(wxCoord& x, wxCoord& y, wxCoord&w, wxCoord &h) const
|
|
{
|
|
if (m_refData)
|
|
{
|
|
RECT rect;
|
|
::GetRgnBox(M_REGION, & rect);
|
|
x = rect.left;
|
|
y = rect.top;
|
|
w = rect.right - rect.left;
|
|
h = rect.bottom - rect.top;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
x = y = w = h = 0;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Is region empty?
|
|
bool wxRegion::IsEmpty() const
|
|
{
|
|
wxCoord x, y, w, h;
|
|
GetBox(x, y, w, h);
|
|
|
|
return (w == 0) && (h == 0);
|
|
}
|
|
|
|
bool wxRegion::DoIsEqual(const wxRegion& region) const
|
|
{
|
|
return ::EqualRgn(M_REGION, M_REGION_OF(region)) != 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxRegion hit testing
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Does the region contain the point (x,y)?
|
|
wxRegionContain wxRegion::DoContainsPoint(wxCoord x, wxCoord y) const
|
|
{
|
|
if (!m_refData)
|
|
return wxOutRegion;
|
|
|
|
return ::PtInRegion(M_REGION, (int) x, (int) y) ? wxInRegion : wxOutRegion;
|
|
}
|
|
|
|
// Does the region contain the rectangle (x, y, w, h)?
|
|
wxRegionContain wxRegion::DoContainsRect(const wxRect& rect) const
|
|
{
|
|
if (!m_refData)
|
|
return wxOutRegion;
|
|
|
|
RECT rc;
|
|
wxCopyRectToRECT(rect, rc);
|
|
|
|
return ::RectInRegion(M_REGION, &rc) ? wxInRegion : wxOutRegion;
|
|
}
|
|
|
|
// Get internal region handle
|
|
WXHRGN wxRegion::GetHRGN() const
|
|
{
|
|
return (WXHRGN)(m_refData ? M_REGION : 0);
|
|
}
|
|
|
|
// ============================================================================
|
|
// wxRegionIterator implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxRegionIterator ctors/dtor
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxRegionIterator::Init()
|
|
{
|
|
m_current =
|
|
m_numRects = 0;
|
|
|
|
m_rects = NULL;
|
|
}
|
|
|
|
wxRegionIterator::~wxRegionIterator()
|
|
{
|
|
delete [] m_rects;
|
|
}
|
|
|
|
// Initialize iterator for region
|
|
wxRegionIterator::wxRegionIterator(const wxRegion& region)
|
|
{
|
|
m_rects = NULL;
|
|
|
|
Reset(region);
|
|
}
|
|
|
|
wxRegionIterator& wxRegionIterator::operator=(const wxRegionIterator& ri)
|
|
{
|
|
delete [] m_rects;
|
|
|
|
m_current = ri.m_current;
|
|
m_numRects = ri.m_numRects;
|
|
if ( m_numRects )
|
|
{
|
|
m_rects = new wxRect[m_numRects];
|
|
for ( long n = 0; n < m_numRects; n++ )
|
|
m_rects[n] = ri.m_rects[n];
|
|
}
|
|
else
|
|
{
|
|
m_rects = NULL;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxRegionIterator operations
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Reset iterator for a new region.
|
|
void wxRegionIterator::Reset(const wxRegion& region)
|
|
{
|
|
m_current = 0;
|
|
m_region = region;
|
|
|
|
if (m_rects)
|
|
{
|
|
delete[] m_rects;
|
|
|
|
m_rects = NULL;
|
|
}
|
|
|
|
if (m_region.Empty())
|
|
m_numRects = 0;
|
|
else
|
|
{
|
|
DWORD noBytes = ::GetRegionData(((wxRegionRefData*)region.m_refData)->m_region, 0, NULL);
|
|
RGNDATA *rgnData = (RGNDATA*) new char[noBytes];
|
|
::GetRegionData(((wxRegionRefData*)region.m_refData)->m_region, noBytes, rgnData);
|
|
|
|
RGNDATAHEADER* header = (RGNDATAHEADER*) rgnData;
|
|
|
|
m_rects = new wxRect[header->nCount];
|
|
|
|
RECT* rect = (RECT*) ((char*)rgnData + sizeof(RGNDATAHEADER));
|
|
size_t i;
|
|
for (i = 0; i < header->nCount; i++)
|
|
{
|
|
m_rects[i] = wxRect(rect->left, rect->top,
|
|
rect->right - rect->left, rect->bottom - rect->top);
|
|
rect ++; // Advances pointer by sizeof(RECT)
|
|
}
|
|
|
|
m_numRects = header->nCount;
|
|
|
|
delete[] (char*) rgnData;
|
|
}
|
|
}
|
|
|
|
wxRegionIterator& wxRegionIterator::operator++()
|
|
{
|
|
if (m_current < m_numRects)
|
|
++m_current;
|
|
|
|
return *this;
|
|
}
|
|
|
|
wxRegionIterator wxRegionIterator::operator ++ (int)
|
|
{
|
|
wxRegionIterator tmp = *this;
|
|
if (m_current < m_numRects)
|
|
++m_current;
|
|
|
|
return tmp;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxRegionIterator accessors
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxCoord wxRegionIterator::GetX() const
|
|
{
|
|
wxCHECK_MSG( m_current < m_numRects, 0, _T("invalid wxRegionIterator") );
|
|
|
|
return m_rects[m_current].x;
|
|
}
|
|
|
|
wxCoord wxRegionIterator::GetY() const
|
|
{
|
|
wxCHECK_MSG( m_current < m_numRects, 0, _T("invalid wxRegionIterator") );
|
|
|
|
return m_rects[m_current].y;
|
|
}
|
|
|
|
wxCoord wxRegionIterator::GetW() const
|
|
{
|
|
wxCHECK_MSG( m_current < m_numRects, 0, _T("invalid wxRegionIterator") );
|
|
|
|
return m_rects[m_current].width;
|
|
}
|
|
|
|
wxCoord wxRegionIterator::GetH() const
|
|
{
|
|
wxCHECK_MSG( m_current < m_numRects, 0, _T("invalid wxRegionIterator") );
|
|
|
|
return m_rects[m_current].height;
|
|
}
|