/////////////////////////////////////////////////////////////////////////////
-// File: region.cpp
+// File: src/x11/region.cpp
// Purpose: Region class
-// Author: Markus Holzem/Julian Smart
+// Author: Julian Smart, Robert Roebling
// Created: Fri Oct 24 10:46:34 MET 1997
-// RCS-ID: $Id$
-// Copyright: (c) 1997 Markus Holzem/Julian Smart
+// RCS-ID: $Id$
+// Copyright: (c) 1997 Julian Smart, Robert Roebling
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
-#ifdef __GNUG__
-#pragma implementation "region.h"
-#endif
+// for compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
#include "wx/region.h"
-#include "wx/gdicmn.h"
+
+#ifndef WX_PRECOMP
+ #include "wx/log.h"
+ #include "wx/gdicmn.h"
+#endif
#ifdef __VMS__
#pragma message disable nosimpint
#endif
-#include <Xm/Xm.h>
+#include "wx/x11/private.h"
+#include "X11/Xutil.h"
#ifdef __VMS__
#pragma message enable nosimpint
#endif
-// #include "wx/motif/private.h"
-
- IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
- IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator, wxObject)
// ----------------------------------------------------------------------------
-// list types
+// wxRegionRefData: private class containing the information about the region
// ----------------------------------------------------------------------------
-#include "wx/listimpl.cpp"
-
-WX_DEFINE_LIST(wxRectList);
-
-//-----------------------------------------------------------------------------
-// wxRegionRefData implementation
-//-----------------------------------------------------------------------------
-
-class WXDLLEXPORT wxRegionRefData : public wxGDIRefData {
+class wxRegionRefData : public wxGDIRefData
+{
public:
wxRegionRefData()
{
- m_region = XCreateRegion();
- m_usingRects = FALSE;
- m_rects = (wxRect*) NULL;
- m_rectCount = 0;
+ m_region = NULL;
}
- wxRegionRefData(const wxRegionRefData& data)
+ wxRegionRefData(const wxRegionRefData& refData)
{
m_region = XCreateRegion();
- m_rects = (wxRect*) NULL;
- m_rectCount = 0;
- XUnionRegion(m_region, data.m_region, m_region);
-
- SetRects(data.m_rectCount, data.m_rects);
+ XUnionRegion( refData.m_region, m_region, m_region );
}
- ~wxRegionRefData()
+ virtual ~wxRegionRefData()
{
- XDestroyRegion(m_region);
- DeleteRects();
+ if (m_region)
+ XDestroyRegion( m_region );
}
- wxRect* GetRects() { return m_rects; };
- void SetRects(const wxRectList& rectList);
- void SetRects(int count, const wxRect* rects);
- bool UsingRects() const { return m_usingRects; }
- int GetRectCount() const { return m_rectCount; }
+ Region m_region;
+};
- void DeleteRects();
+// ----------------------------------------------------------------------------
+// macros
+// ----------------------------------------------------------------------------
- Region m_region;
- wxRect* m_rects;
- int m_rectCount;
- bool m_usingRects; // TRUE if we're using the above.
-};
+#define M_REGIONDATA ((wxRegionRefData *)m_refData)
+#define M_REGIONDATA_OF(rgn) ((wxRegionRefData *)(rgn.m_refData))
+
+IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject)
+IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator,wxObject)
+
+// ----------------------------------------------------------------------------
+// wxRegion construction
+// ----------------------------------------------------------------------------
+
+#define M_REGIONDATA ((wxRegionRefData *)m_refData)
-void wxRegionRefData::SetRects(const wxRectList& rectList)
+void wxRegion::InitRect(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
{
- DeleteRects();
- m_usingRects = (rectList.Number() > 0);
- if (m_usingRects)
- {
- m_rectCount = rectList.Number();
- m_rects = new wxRect[m_rectCount];
- }
+ XRectangle rect;
+ rect.x = (short)x;
+ rect.y = (short)y;
+ rect.width = (unsigned short)w;
+ rect.height = (unsigned short)h;
- wxRectList::Node* node = rectList.GetFirst();
- int i = 0;
- while (node) {
- wxRect* rect = node->GetData();
- m_rects[i] = * rect;
- node = node->GetNext();
- i ++;
- }
+ m_refData = new wxRegionRefData();
+
+ M_REGIONDATA->m_region = XCreateRegion();
+ XUnionRectWithRegion( &rect, M_REGIONDATA->m_region, M_REGIONDATA->m_region );
}
-void wxRegionRefData::SetRects(int count, const wxRect* rects)
+wxRegion::wxRegion( size_t WXUNUSED(n), const wxPoint *WXUNUSED(points), wxPolygonFillMode WXUNUSED(fillStyle) )
{
- DeleteRects();
- m_usingRects = (count > 0);
- if (m_usingRects)
+#if 0
+ XPoint *xpoints = new XPoint[n];
+ for ( size_t i = 0 ; i < n ; i++ )
{
- m_rectCount = count;
- m_rects = new wxRect[m_rectCount];
- int i;
- for (i = 0; i < m_rectCount; i++)
- m_rects[i] = rects[i];
+ xpoints[i].x = points[i].x;
+ xpoints[i].y = points[i].y;
}
-}
-void wxRegionRefData::DeleteRects()
-{
- if (m_rects)
- {
- delete[] m_rects;
- m_rects = (wxRect*) NULL;
- }
- m_rectCount = 0;
- m_usingRects = FALSE;
- }
+ m_refData = new wxRegionRefData();
-#define M_REGION (((wxRegionRefData*)m_refData)->m_region)
+ Region* reg = gdk_region_polygon
+ (
+ gdkpoints,
+ n,
+ fillStyle == wxWINDING_RULE ? GDK_WINDING_RULE
+ : GDK_EVEN_ODD_RULE
+ );
-//-----------------------------------------------------------------------------
-// wxRegion
-//-----------------------------------------------------------------------------
+ M_REGIONDATA->m_region = reg;
-/*!
- * Create an empty region.
- */
-wxRegion::wxRegion()
-{
+ delete [] xpoints;
+#endif
}
-wxRegion::wxRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
+wxRegion::~wxRegion()
{
- m_refData = new wxRegionRefData;
-
- XRectangle rect;
- rect.x = x;
- rect.y = y;
- rect.width = w;
- rect.height = h;
- XUnionRectWithRegion(&rect, M_REGION, M_REGION);
+ // m_refData unrefed in ~wxObject
}
-wxRegion::wxRegion(const wxPoint& topLeft, const wxPoint& bottomRight)
+wxGDIRefData *wxRegion::CreateGDIRefData() const
{
- m_refData = new wxRegionRefData;
-
- XRectangle rect;
- rect.x = topLeft.x;
- rect.y = topLeft.y;
- rect.width = bottomRight.x - topLeft.x;
- rect.height = bottomRight.y - topLeft.y;
- XUnionRectWithRegion(&rect, M_REGION, M_REGION);
+ return new wxRegionRefData;
}
-wxRegion::wxRegion(const wxRect& rect)
+wxGDIRefData *wxRegion::CloneGDIRefData(const wxGDIRefData *data) const
{
- m_refData = new wxRegionRefData;
-
- XRectangle rect1;
- rect1.x = rect.x;
- rect1.y = rect.y;
- rect1.width = rect.width;
- rect1.height = rect.height;
- XUnionRectWithRegion(&rect1, M_REGION, M_REGION);
+ return new wxRegionRefData(*(wxRegionRefData *)data);
}
-/*!
- * Destroy the region.
- */
-wxRegion::~wxRegion()
-{
- // m_refData unrefed in ~wxObject
-}
+// ----------------------------------------------------------------------------
+// wxRegion comparison
+// ----------------------------------------------------------------------------
-// Get the internal region handle
-WXRegion wxRegion::GetXRegion() const
+bool wxRegion::DoIsEqual(const wxRegion& region) const
{
- wxASSERT( m_refData !=NULL );
-
- return (WXRegion) ((wxRegionRefData*)m_refData)->m_region;
+ return XEqualRegion( M_REGIONDATA->m_region,
+ M_REGIONDATA_OF(region)->m_region ) == True;
}
-//-----------------------------------------------------------------------------
-//# Modify region
-//-----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// wxRegion operations
+// ----------------------------------------------------------------------------
-//! Clear current region
void wxRegion::Clear()
{
UnRef();
}
-//! Combine rectangle (x, y, w, h) with this.
-bool wxRegion::Combine(wxCoord x, wxCoord y, wxCoord width, wxCoord height, wxRegionOp op)
+bool wxRegion::DoUnionWithRect(const wxRect& r)
{
- // Don't change shared data
- if (!m_refData) {
+ // work around for XUnionRectWithRegion() bug: taking a union with an empty
+ // rect results in an empty region (at least XFree 3.3.6 and 4.0 have this
+ // problem)
+ if ( r.IsEmpty() )
+ return true;
+
+ XRectangle rect;
+ rect.x = (short)r.x;
+ rect.y = (short)r.y;
+ rect.width = (unsigned short)r.width;
+ rect.height = (unsigned short)r.height;
+
+ if (!m_refData)
+ {
m_refData = new wxRegionRefData();
- } else if (m_refData->GetRefCount() > 1) {
- wxRegionRefData* ref = (wxRegionRefData*)m_refData;
- UnRef();
- m_refData = new wxRegionRefData(*ref);
+ M_REGIONDATA->m_region = XCreateRegion();
+ }
+ else
+ {
+ AllocExclusive();
}
- // If ref count is 1, that means it's 'ours' anyway so no action.
- Region rectRegion = XCreateRegion();
+ XUnionRectWithRegion( &rect, M_REGIONDATA->m_region, M_REGIONDATA->m_region );
- XRectangle rect;
- rect.x = x;
- rect.y = y;
- rect.width = width;
- rect.height = height;
- XUnionRectWithRegion(&rect, rectRegion, rectRegion);
+ return true;
+}
+
+bool wxRegion::DoUnionWithRegion( const wxRegion& region )
+{
+ wxCHECK_MSG( region.Ok(), false, wxT("invalid region") );
- switch (op)
+ if (!m_refData)
+ {
+ m_refData = new wxRegionRefData();
+ M_REGIONDATA->m_region = XCreateRegion();
+ }
+ else
{
- case wxRGN_AND:
- XIntersectRegion(M_REGION, rectRegion, M_REGION);
- break ;
- case wxRGN_OR:
- XUnionRegion(M_REGION, rectRegion, M_REGION);
- break ;
- case wxRGN_XOR:
- // TODO
- break ;
- case wxRGN_DIFF:
- // TODO
- break ;
- case wxRGN_COPY: // Don't have to do this one
- default:
- // TODO
- break ;
+ AllocExclusive();
}
- return FALSE;
+ XUnionRegion( M_REGIONDATA->m_region,
+ M_REGIONDATA_OF(region)->m_region,
+ M_REGIONDATA->m_region );
+
+ return true;
}
-//! Union /e region with this.
-bool wxRegion::Combine(const wxRegion& region, wxRegionOp op)
+bool wxRegion::DoIntersect( const wxRegion& region )
{
- if (region.Empty())
- return FALSE;
+ wxCHECK_MSG( region.Ok(), false, wxT("invalid region") );
- // Don't change shared data
- if (!m_refData) {
+ if (!m_refData)
+ {
m_refData = new wxRegionRefData();
- } else if (m_refData->GetRefCount() > 1) {
- wxRegionRefData* ref = (wxRegionRefData*)m_refData;
- UnRef();
- m_refData = new wxRegionRefData(*ref);
+ M_REGIONDATA->m_region = XCreateRegion();
+
+ // leave here
+ return true;
+ }
+ else
+ {
+ AllocExclusive();
}
- switch (op)
+ XIntersectRegion( M_REGIONDATA->m_region,
+ M_REGIONDATA_OF(region)->m_region,
+ M_REGIONDATA->m_region );
+
+ return true;
+}
+
+bool wxRegion::DoSubtract( const wxRegion& region )
+{
+ wxCHECK_MSG( region.Ok(), false, wxT("invalid region") );
+
+ if (!m_refData)
+ {
+ m_refData = new wxRegionRefData();
+ M_REGIONDATA->m_region = XCreateRegion();
+ }
+ else
{
- case wxRGN_AND:
- XIntersectRegion(M_REGION, ((wxRegionRefData*)region.m_refData)->m_region,
- M_REGION);
- break ;
- case wxRGN_OR:
- XUnionRegion(M_REGION, ((wxRegionRefData*)region.m_refData)->m_region,
- M_REGION);
- break ;
- case wxRGN_XOR:
- // TODO
- break ;
- case wxRGN_DIFF:
- // TODO
- break ;
- case wxRGN_COPY: // Don't have to do this one
- default:
- // TODO
- break ;
+ AllocExclusive();
}
- return FALSE;
+ XSubtractRegion( M_REGIONDATA->m_region,
+ M_REGIONDATA_OF(region)->m_region,
+ M_REGIONDATA->m_region );
+
+ return true;
}
-bool wxRegion::Combine(const wxRect& rect, wxRegionOp op)
+bool wxRegion::DoXor( const wxRegion& region )
{
- return Combine(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight(), op);
+ wxCHECK_MSG( region.Ok(), false, wxT("invalid region") );
+
+ if (!m_refData)
+ {
+ m_refData = new wxRegionRefData();
+ M_REGIONDATA->m_region = XCreateRegion();
+ }
+ else
+ {
+ AllocExclusive();
+ }
+
+ XXorRegion( M_REGIONDATA->m_region,
+ M_REGIONDATA_OF(region)->m_region,
+ M_REGIONDATA->m_region );
+
+ return true;
}
-//-----------------------------------------------------------------------------
-//# Information on region
-//-----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// wxRegion tests
+// ----------------------------------------------------------------------------
-// Outer bounds of region
-void wxRegion::GetBox(wxCoord& x, wxCoord& y, wxCoord&w, wxCoord &h) const
+bool wxRegion::DoGetBox( wxCoord &x, wxCoord &y, wxCoord &w, wxCoord &h ) const
{
- if (m_refData) {
+ if (m_refData)
+ {
XRectangle rect;
- XClipBox(M_REGION, &rect);
+ XClipBox( M_REGIONDATA->m_region, &rect );
x = rect.x;
y = rect.y;
w = rect.width;
h = rect.height;
- } else {
- x = y = w = h = 0;
+
+ return true;
}
-}
+ else
+ {
+ x = 0;
+ y = 0;
+ w = -1;
+ h = -1;
-wxRect wxRegion::GetBox() const
-{
- wxCoord x, y, w, h;
- GetBox(x, y, w, h);
- return wxRect(x, y, w, h);
+ return false;
+ }
}
-// Is region empty?
-bool wxRegion::Empty() const
+bool wxRegion::DoOffset( wxCoord x, wxCoord y )
{
- return m_refData ? XEmptyRegion(M_REGION) : TRUE;
-}
+ if (!m_refData)
+ return false;
+
+ AllocExclusive();
-//-----------------------------------------------------------------------------
-//# Tests
-//-----------------------------------------------------------------------------
+ XOffsetRegion( M_REGIONDATA->m_region, x, y );
-// Does the region contain the point (x,y)?
-wxRegionContain wxRegion::Contains(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y)) const
+ return true;
+}
+
+bool wxRegion::IsEmpty() const
{
if (!m_refData)
- return wxOutRegion;
+ return true;
- // TODO. Return wxInRegion if within region.
- if (0)
- return wxInRegion;
- return wxOutRegion;
+ return XEmptyRegion( M_REGIONDATA->m_region ) == True;
}
-// Does the region contain the point pt?
-wxRegionContain wxRegion::Contains(const wxPoint& pt) const
+wxRegionContain wxRegion::DoContainsPoint( wxCoord x, wxCoord y ) const
{
if (!m_refData)
return wxOutRegion;
- return XPointInRegion(M_REGION, pt.x, pt.y) ? wxInRegion : wxOutRegion;
+ if (XPointInRegion( M_REGIONDATA->m_region, x, y ))
+ return wxInRegion;
+ else
+ return wxOutRegion;
}
-// Does the region contain the rectangle (x, y, w, h)?
-wxRegionContain wxRegion::Contains(wxCoord x, wxCoord y, wxCoord w, wxCoord h) const
+wxRegionContain wxRegion::DoContainsRect(const wxRect& r) const
{
if (!m_refData)
return wxOutRegion;
- switch (XRectInRegion(M_REGION, x, y, w, h)) {
- case RectangleIn: return wxInRegion;
+ int res = XRectInRegion(M_REGIONDATA->m_region, r.x, r.y, r.width, r.height);
+ switch (res)
+ {
+ case RectangleIn: return wxInRegion;
+ case RectangleOut: return wxOutRegion;
case RectanglePart: return wxPartRegion;
}
return wxOutRegion;
}
-// Does the region contain the rectangle rect
-wxRegionContain wxRegion::Contains(const wxRect& rect) const
+WXRegion *wxRegion::GetX11Region() const
{
if (!m_refData)
- return wxOutRegion;
+ return NULL;
- wxCoord x, y, w, h;
- x = rect.x;
- y = rect.y;
- w = rect.GetWidth();
- h = rect.GetHeight();
- return Contains(x, y, w, h);
+ return (WXRegion*) M_REGIONDATA->m_region;
}
-bool wxRegion::UsingRects() const
-{
- return ((wxRegionRefData*)m_refData)->UsingRects();
-}
+// ----------------------------------------------------------------------------
+// wxRegionIterator
+// ----------------------------------------------------------------------------
-/*
-wxRectList& wxRegion::GetRectList()
-{
- return ((wxRegionRefData*)m_refData)->GetRectList();
-}
-*/
+// the following structures must match the private structures
+// in X11 region code ( xc/lib/X11/region.h )
-wxRect* wxRegion::GetRects()
-{
- return ((wxRegionRefData*)m_refData)->GetRects();
-}
+// this makes the Region type transparent
+// and we have access to the region rectangles
-int wxRegion::GetRectCount() const
-{
- return ((wxRegionRefData*)m_refData)->GetRectCount();
-}
+struct _XBox {
+ short x1, x2, y1, y2;
+};
-void wxRegion::SetRects(const wxRectList& rectList)
-{
- ((wxRegionRefData*)m_refData)->SetRects(rectList);
-}
+struct _XRegion {
+ long size , numRects;
+ _XBox *rects, extents;
+};
-void wxRegion::SetRects(int count, const wxRect* rects)
+class wxRIRefData: public wxGDIRefData
{
- ((wxRegionRefData*)m_refData)->SetRects(count, rects);
-}
+public:
+
+ wxRIRefData() : m_rects(0), m_numRects(0){}
+ virtual ~wxRIRefData();
-///////////////////////////////////////////////////////////////////////////////
-// //
-// wxRegionIterator //
-// //
-///////////////////////////////////////////////////////////////////////////////
+ wxRect *m_rects;
+ size_t m_numRects;
-/*!
- * Initialize empty iterator
- */
-wxRegionIterator::wxRegionIterator() : m_current(0), m_numRects(0), m_rects(NULL)
+ void CreateRects( const wxRegion& r );
+};
+
+wxRIRefData::~wxRIRefData()
{
+ delete [] m_rects;
}
-wxRegionIterator::~wxRegionIterator()
+void wxRIRefData::CreateRects( const wxRegion& region )
{
if (m_rects)
- delete[] m_rects;
+ delete [] m_rects;
+
+ m_rects = 0;
+ m_numRects = 0;
+
+ if (region.IsEmpty()) return;
+
+ Region r = (Region) region.GetX11Region();
+ if (r)
+ {
+#if wxUSE_NANOX
+ GR_RECT rect;
+ GrGetRegionBox(r, & rect);
+ m_numRects = 1;
+ m_rects = new wxRect[1];
+ m_rects[0].x = rect.x;
+ m_rects[0].y = rect.y;
+ m_rects[0].width = rect.width;
+ m_rects[0].height = rect.height;
+#else
+ m_numRects = r->numRects;
+ if (m_numRects)
+ {
+ m_rects = new wxRect[m_numRects];
+ for (size_t i=0; i < m_numRects; ++i)
+ {
+ _XBox &xr = r->rects[i];
+ wxRect &wr = m_rects[i];
+ wr.x = xr.x1;
+ wr.y = xr.y1;
+ wr.width = xr.x2-xr.x1;
+ wr.height = xr.y2-xr.y1;
+ }
+ }
+#endif
+ }
}
-/*!
- * Initialize iterator for region
- */
-wxRegionIterator::wxRegionIterator(const wxRegion& region)
+wxRegionIterator::wxRegionIterator()
{
- m_rects = NULL;
+ m_refData = new wxRIRefData();
+ Reset();
+}
+wxRegionIterator::wxRegionIterator( const wxRegion& region )
+{
+ m_refData = new wxRIRefData();
Reset(region);
}
-/*!
- * Reset iterator for a new /e region.
- */
-void wxRegionIterator::Reset(const wxRegion& region)
+void wxRegionIterator::Reset( const wxRegion& region )
{
- m_current = 0;
m_region = region;
+ ((wxRIRefData*)m_refData)->CreateRects(region);
+ Reset();
+}
- if (m_rects)
- delete[] m_rects;
-
- m_rects = NULL;
-
- if (m_region.Empty())
- m_numRects = 0;
- else
- {
- // Create m_rects and fill with rectangles for this region.
- // Since we can't find the rectangles in a region, we cheat
- // by retrieving the rectangles explicitly set in wxPaintDC::wxPaintDC
- // (dcclient.cpp).
- if (m_region.UsingRects())
- {
- wxRect* rects = m_region.GetRects();
- int count = m_region.GetRectCount();
- m_numRects = count;
- m_rects = new wxRect[m_numRects];
+bool wxRegionIterator::HaveRects() const
+{
+ return m_current < ((wxRIRefData*)m_refData)->m_numRects;
+}
- for (size_t i = 0; i < m_numRects; i++)
- m_rects[i] = rects[i];
-
- /*
- int i = 0;
- wxRectList::Node* node = rectList.GetFirst();
- while (node) {
- wxRect* rect = node->GetData();
- m_rects[i] = * rect;
- node = node->GetNext();
- i ++;
- }
- */
- }
- else
- {
- // For now, fudge by getting the whole bounding box.
- m_rects = new wxRect[1];
- m_numRects = 1;
- m_rects[0] = m_region.GetBox();
- }
- }
+wxRegionIterator::operator bool () const
+{
+ return HaveRects();
}
-/*!
- * Increment iterator. The rectangle returned is the one after the
- * incrementation.
- */
void wxRegionIterator::operator ++ ()
{
- if (m_current < m_numRects)
- ++m_current;
+ if (HaveRects()) ++m_current;
}
-/*!
- * Increment iterator. The rectangle returned is the one before the
- * incrementation.
- */
void wxRegionIterator::operator ++ (int)
{
- if (m_current < m_numRects)
- ++m_current;
+ if (HaveRects()) ++m_current;
}
wxCoord wxRegionIterator::GetX() const
{
- if (m_current < m_numRects)
- return m_rects[m_current].x;
- return 0;
+ if( !HaveRects() ) return 0;
+ return ((wxRIRefData*)m_refData)->m_rects[m_current].x;
}
wxCoord wxRegionIterator::GetY() const
{
- if (m_current < m_numRects)
- return m_rects[m_current].y;
- return 0;
+ if( !HaveRects() ) return 0;
+ return ((wxRIRefData*)m_refData)->m_rects[m_current].y;
}
wxCoord wxRegionIterator::GetW() const
{
- if (m_current < m_numRects)
- return m_rects[m_current].width ;
- return 0;
+ if( !HaveRects() ) return -1;
+ return ((wxRIRefData*)m_refData)->m_rects[m_current].width;
}
wxCoord wxRegionIterator::GetH() const
{
- if (m_current < m_numRects)
- return m_rects[m_current].height;
- return 0;
+ if( !HaveRects() ) return -1;
+ return ((wxRIRefData*)m_refData)->m_rects[m_current].height;
}
+wxRect wxRegionIterator::GetRect() const
+{
+ wxRect r;
+ if( HaveRects() )
+ r = ((wxRIRefData*)m_refData)->m_rects[m_current];
+
+ return r;
+}