]> git.saurik.com Git - wxWidgets.git/blobdiff - src/mac/carbon/graphics.cpp
Added outline style sample and vetoing of style sheet changing when loading;
[wxWidgets.git] / src / mac / carbon / graphics.cpp
index 6845a1fb1701d19c82491e381480dd2f65c21e96..49b58ca394faca31a8ab5ccbc77e5acb42e02bdd 100755 (executable)
@@ -16,6 +16,7 @@
 #include "wx/graphics.h"
 
 #ifndef WX_PRECOMP
+    #include "wx/dcclient.h"
     #include "wx/log.h"
     #include "wx/region.h"
 #endif
 typedef float CGFloat;
 #endif
 
+//-----------------------------------------------------------------------------
+// constants
+//-----------------------------------------------------------------------------
+
+#if !defined( __DARWIN__ ) || defined(__MWERKS__)
+#ifndef M_PI
+const double M_PI = 3.14159265358979;
+#endif
+#endif
+
+static const double RAD2DEG = 180.0 / M_PI;
+
 //
 // Graphics Path
 //
 
 class WXDLLEXPORT wxMacCoreGraphicsPath : public wxGraphicsPath
 {
-    DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsPath)
 public :
     wxMacCoreGraphicsPath();
     ~wxMacCoreGraphicsPath();
@@ -83,11 +95,24 @@ public :
     // draws a an arc to two tangents connecting (current) to (x1,y1) and (x1,y1) to (x2,y2), also a straight line from (current) to (x1,y1)
     virtual void AddArcToPoint( wxDouble x1, wxDouble y1 , wxDouble x2, wxDouble y2, wxDouble r );
     
-    CGPathRef GetPath() const;
+    // returns the native path
+    virtual void * GetNativePath() const { return m_path; }
+    
+    // give the native path returned by GetNativePath() back (there might be some deallocations necessary)
+    virtual void UnGetNativePath(void *p) {}
+
+    DECLARE_DYNAMIC_CLASS(wxMacCoreGraphicsPath)
+    DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsPath)
 private :
     CGMutablePathRef m_path;
 };
 
+//-----------------------------------------------------------------------------
+// wxGraphicsPath implementation
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxMacCoreGraphicsPath, wxGraphicsPath)
+
 wxMacCoreGraphicsPath::wxMacCoreGraphicsPath()
 {
     m_path = CGPathCreateMutable();
@@ -148,11 +173,6 @@ void wxMacCoreGraphicsPath::CloseSubpath()
     CGPathCloseSubpath( m_path );
 }
 
-CGPathRef wxMacCoreGraphicsPath::GetPath() const
-{
-    return m_path;
-}
-
 // gets the last point of the current path, (0,0) if not yet set
 void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble& x, wxDouble&y)
 {
@@ -167,17 +187,18 @@ void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble& x, wxDouble&y)
 
 class WXDLLEXPORT wxMacCoreGraphicsContext : public wxGraphicsContext
 {
-    DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext)
-
 public:
-    wxMacCoreGraphicsContext( CGrafPtr port );
-    
     wxMacCoreGraphicsContext( CGContextRef cgcontext );
     
+    wxMacCoreGraphicsContext( WindowRef window );
+    
+    wxMacCoreGraphicsContext( wxWindow* window );
+    
     wxMacCoreGraphicsContext();
     
     ~wxMacCoreGraphicsContext();
 
+    void Init();
 
     // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
     virtual wxGraphicsPath * CreatePath();
@@ -191,6 +212,14 @@ public:
     // clips drawings to the region
     virtual void Clip( const wxRegion &region );
 
+    // clips drawings to the rect
+    virtual void Clip( wxDouble x, wxDouble y, wxDouble w, wxDouble h );
+    
+    // resets the clipping to original extent
+    virtual void ResetClip();
+
+    virtual void * GetNativeContext();
+    
     //
     // transformation
     //
@@ -227,7 +256,7 @@ public:
     virtual void SetFont( const wxFont &font );
     
     // sets the text color
-    virtual void SetTextColor( const wxColour &col );
+    virtual void SetTextColour( const wxColour &col );
 
     // strokes along a path with the current pen
     virtual void StrokePath( const wxGraphicsPath *path );
@@ -259,32 +288,41 @@ public:
 
     virtual void DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h );
 
-    CGContextRef GetNativeContext();
     void SetNativeContext( CGContextRef cg );
     CGPathDrawingMode GetDrawingMode() const { return m_mode; }
+
+
+    virtual bool ShouldOffset() const
+    {
+        int penwidth = m_pen.GetWidth();
+        if ( penwidth == 0 )
+            penwidth = 1;
+        if ( m_pen.GetStyle() == wxTRANSPARENT )
+            penwidth = 0;
+        return ( penwidth % 2 ) == 1; 
+    }
+        
     
+    DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext)
+    DECLARE_DYNAMIC_CLASS(wxMacCoreGraphicsContext)
+
 private:
+    void EnsureIsValid();
+
     CGContextRef m_cgContext;
-    CGrafPtr m_qdPort;
+    WindowRef m_windowRef;
+    int m_originX;
+    int m_originY;
+    wxMacCFRefHolder<HIShapeRef> m_clipRgn;
+    bool m_releaseContext;
     CGPathDrawingMode m_mode;
     ATSUStyle m_macATSUIStyle;
     wxPen m_pen;
     wxBrush m_brush;
+    wxFont m_font;
     wxColor m_textForegroundColor;
 };
 
-//-----------------------------------------------------------------------------
-// constants
-//-----------------------------------------------------------------------------
-
-#if !defined( __DARWIN__ ) || defined(__MWERKS__)
-#ifndef M_PI
-const double M_PI = 3.14159265358979;
-#endif
-#endif
-
-static const double RAD2DEG = 180.0 / M_PI;
-
 //-----------------------------------------------------------------------------
 // device context implementation
 //
@@ -297,68 +335,159 @@ static const double RAD2DEG = 180.0 / M_PI;
 // state we were called with, the other one after changing to HI Graphics orientation
 // (this one is used for getting back clippings etc)
 
-//-----------------------------------------------------------------------------
-// wxGraphicsPath implementation
-//-----------------------------------------------------------------------------
-
 //-----------------------------------------------------------------------------
 // wxGraphicsContext implementation
 //-----------------------------------------------------------------------------
 
-wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGrafPtr port )
+IMPLEMENT_DYNAMIC_CLASS(wxMacCoreGraphicsContext, wxGraphicsContext)
+
+void wxMacCoreGraphicsContext::Init()
 {
-    m_qdPort = port;
     m_cgContext = NULL;
     m_mode = kCGPathFill;
     m_macATSUIStyle = NULL;
+    m_releaseContext = false;
+    HIRect r = CGRectMake(0,0,0,0);
+    m_clipRgn.Set(HIShapeCreateWithRect(&r));
 }
 
 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext )
 {
-    m_qdPort = NULL;
-    m_cgContext = cgcontext;
-    m_mode = kCGPathFill;
-    m_macATSUIStyle = NULL;
-    CGContextSaveGState( m_cgContext );
-    CGContextSaveGState( m_cgContext );
+       Init();
+       m_cgContext = cgcontext;
+    // FIXME: This check is needed because currently we need to use a DC/GraphicsContext
+    // in order to get font properties, like wxFont::GetPixelSize, but since we don't have 
+    // a native window attached to use, I create a wxGraphicsContext with a NULL CGContextRef
+    // for this one operation.
+    
+    // When wxFont::GetPixelSize on Mac no longer needs a graphics context, this check
+    // can be removed. 
+    if (m_cgContext)
+    {
+        CGContextSaveGState( m_cgContext );
+        CGContextSaveGState( m_cgContext );
+    }
+}
+
+wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( WindowRef window )
+{
+    Init();
+    m_windowRef = window;
+}
+
+wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( wxWindow* window )
+{
+    Init();
+    m_windowRef = (WindowRef) window->MacGetTopLevelWindowRef();
+    m_originX = m_originY = 0;
+    window->MacWindowToRootWindow( &m_originX , &m_originY );
 }
 
 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
 {
-    m_qdPort = NULL;
-    m_cgContext = NULL;
-    m_mode = kCGPathFill;
-    m_macATSUIStyle = NULL;
+    Init();
 }
 
 wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
 {
     if ( m_cgContext )
     {
-        CGContextSynchronize( m_cgContext );
+        // TODO : when is this necessary - should we add a Flush() method ? CGContextSynchronize( m_cgContext );
         CGContextRestoreGState( m_cgContext );
         CGContextRestoreGState( m_cgContext );
     }
 
-    if ( m_qdPort )
-        CGContextRelease( m_cgContext );
+    if ( m_releaseContext )
+        QDEndCGContext( GetWindowPort( m_windowRef ) , &m_cgContext);
+}
+
+void wxMacCoreGraphicsContext::EnsureIsValid()
+{
+    if ( !m_cgContext )
+    {
+        OSStatus status = QDBeginCGContext( GetWindowPort( m_windowRef ) , &m_cgContext );
+        wxASSERT_MSG( status == noErr , wxT("Cannot nest wxDCs on the same window") );
+        Rect bounds;
+        GetWindowBounds( m_windowRef, kWindowContentRgn, &bounds );
+        CGContextSaveGState( m_cgContext );
+        CGContextTranslateCTM( m_cgContext , 0 , bounds.bottom - bounds.top );
+        CGContextScaleCTM( m_cgContext , 1 , -1 );
+        CGContextTranslateCTM( m_cgContext, m_originX, m_originY );
+        CGContextSaveGState( m_cgContext );
+        m_releaseContext = true;
+        if ( !HIShapeIsEmpty(m_clipRgn) )
+        {
+            HIShapeReplacePathInCGContext( m_clipRgn, m_cgContext );
+            CGContextClip( m_cgContext );
+        }
+    }
 }
 
+
 void wxMacCoreGraphicsContext::Clip( const wxRegion &region )
 {
-//    ClipCGContextToRegion ( m_cgContext, &bounds , (RgnHandle) dc->m_macCurrentClipRgn );
+    if( m_cgContext )
+    {
+        HIShapeRef shape = HIShapeCreateWithQDRgn( (RgnHandle) region.GetWXHRGN() );
+        HIShapeReplacePathInCGContext( shape, m_cgContext );
+        CGContextClip( m_cgContext );
+        CFRelease( shape );
+    }
+    else
+    {
+        m_clipRgn.Set(HIShapeCreateWithQDRgn( (RgnHandle) region.GetWXHRGN() ));
+    }
+}
+
+// clips drawings to the rect
+void wxMacCoreGraphicsContext::Clip( wxDouble x, wxDouble y, wxDouble w, wxDouble h )
+{
+    HIRect r = CGRectMake( x , y , w , h );
+    if ( m_cgContext )
+    {
+        CGContextClipToRect( m_cgContext, r );
+    }
+    else
+    {
+        m_clipRgn.Set(HIShapeCreateWithRect(&r));
+    }
+}
+    
+    // resets the clipping to original extent
+void wxMacCoreGraphicsContext::ResetClip()
+{
+    if ( m_cgContext )
+    {
+        CGContextRestoreGState( m_cgContext );
+        CGContextSaveGState( m_cgContext );
+    }
+    else
+    {
+        HIRect r = CGRectMake(0,0,0,0);
+        m_clipRgn.Set(HIShapeCreateWithRect(&r));
+    }
 }
 
-void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath *p )
+void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath *path )
 {
-    const wxMacCoreGraphicsPath* path = dynamic_cast< const wxMacCoreGraphicsPath*>( p );
-    CGContextAddPath( m_cgContext , path->GetPath() );
+    EnsureIsValid();
+       
+    bool offset = ShouldOffset();
+
+    if ( offset )
+        CGContextTranslateCTM( m_cgContext, 0.5, 0.5 );
+
+    CGContextAddPath( m_cgContext , (CGPathRef) path->GetNativePath() );
     CGContextStrokePath( m_cgContext );
+
+    if ( offset )
+        CGContextTranslateCTM( m_cgContext, -0.5, -0.5 );
 }
 
-void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath *p , int fillStyle )
+void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath *path , int fillStyle )
 {
-    const wxMacCoreGraphicsPath* path = dynamic_cast< const wxMacCoreGraphicsPath*>( p );
+    EnsureIsValid();
+    
     CGPathDrawingMode mode = m_mode;
 
     if ( fillStyle == wxODDEVEN_RULE )
@@ -368,16 +497,24 @@ void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath *p , int fillStyle
         else if ( mode == kCGPathFillStroke )
             mode = kCGPathEOFillStroke;
     }
+       
+    bool offset = ShouldOffset();
+
+    if ( offset )
+        CGContextTranslateCTM( m_cgContext, 0.5, 0.5 );
 
-    CGContextAddPath( m_cgContext , path->GetPath() );
+    CGContextAddPath( m_cgContext , (CGPathRef) path->GetNativePath() );
     CGContextDrawPath( m_cgContext , mode );
+
+    if ( offset )
+        CGContextTranslateCTM( m_cgContext, -0.5, -0.5 );
 }
 
-void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath *p , int fillStyle )
+void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath *path , int fillStyle )
 {
-    const wxMacCoreGraphicsPath* path = dynamic_cast< const wxMacCoreGraphicsPath*>( p );
-
-    CGContextAddPath( m_cgContext , path->GetPath() );
+    EnsureIsValid();
+    
+    CGContextAddPath( m_cgContext , (CGPathRef) path->GetNativePath() );
     if ( fillStyle == wxODDEVEN_RULE )
         CGContextEOFillPath( m_cgContext );
     else
@@ -386,19 +523,9 @@ void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath *p , int fillStyle
 
 wxGraphicsPath* wxMacCoreGraphicsContext::CreatePath()
 {
-    // make sure that we now have a real cgref, before doing
-    // anything with paths
-    CGContextRef cg = GetNativeContext();
-    cg = NULL;
-
     return new wxMacCoreGraphicsPath();
 }
 
-CGContextRef wxMacCoreGraphicsContext::GetNativeContext()
-{
-    return m_cgContext;
-}
-
 void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg )
 {
     // we allow either setting or clearing but not replacing
@@ -411,21 +538,29 @@ void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg )
 
 void wxMacCoreGraphicsContext::Translate( wxDouble dx , wxDouble dy ) 
 {
+    EnsureIsValid();
+    
     CGContextTranslateCTM( m_cgContext, dx, dy );
 }
 
 void wxMacCoreGraphicsContext::Scale( wxDouble xScale , wxDouble yScale )
 {
+    EnsureIsValid();
+    
     CGContextScaleCTM( m_cgContext , xScale , yScale );
 }
 
 void wxMacCoreGraphicsContext::Rotate( wxDouble angle )
 {
+    EnsureIsValid();
+    
     CGContextRotateCTM( m_cgContext , angle );
 }
 
 void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h ) 
 {
+    EnsureIsValid();
+    
     CGImageRef image = (CGImageRef)( bmp.CGImageCreate() );
     HIRect r = CGRectMake( x , y , w , h );
     HIViewDrawCGImage( m_cgContext , &r , image );
@@ -434,6 +569,8 @@ void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDo
 
 void wxMacCoreGraphicsContext::DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h ) 
 {
+    EnsureIsValid();
+    
     CGRect r = CGRectMake( 00 , 00 , w , h );
     CGContextSaveGState( m_cgContext );
     CGContextTranslateCTM( m_cgContext, x , y + h );
@@ -445,17 +582,23 @@ void wxMacCoreGraphicsContext::DrawIcon( const wxIcon &icon, wxDouble x, wxDoubl
 
 void wxMacCoreGraphicsContext::PushState()
 {
+    EnsureIsValid();
+    
     CGContextSaveGState( m_cgContext );
 }
 
 void wxMacCoreGraphicsContext::PopState() 
 {
+    EnsureIsValid();
+    
     CGContextRestoreGState( m_cgContext );
 }
 
-void wxMacCoreGraphicsContext::SetTextColor( const wxColour &col ) 
+void wxMacCoreGraphicsContext::SetTextColour( const wxColour &col ) 
 {
     m_textForegroundColor = col;
+    // to recreate the native font after color change
+    SetFont( m_font );
 }
 
 #pragma mark -
@@ -921,6 +1064,8 @@ void wxMacCoreGraphicsContext::DrawText( const wxString &str, wxDouble x, wxDoub
 
 void wxMacCoreGraphicsContext::DrawText( const wxString &str, wxDouble x, wxDouble y, wxDouble angle ) 
 {
+    EnsureIsValid();
+    
     OSStatus status = noErr;
     ATSUTextLayout atsuLayout;
     UniCharCount chars = str.length();
@@ -1171,6 +1316,7 @@ void wxMacCoreGraphicsContext::SetFont( const wxFont &font )
 
     if ( font.Ok() )
     {
+        m_font = font ;
         OSStatus status;
 
         status = ATSUCreateAndCopyStyle( (ATSUStyle) font.MacGetATSUStyle() , (ATSUStyle*) &m_macATSUIStyle );
@@ -1205,9 +1351,29 @@ void wxMacCoreGraphicsContext::SetFont( const wxFont &font )
     }
 }
 
+void * wxMacCoreGraphicsContext::GetNativeContext() 
+{
+    return m_cgContext;
+}
+
 wxGraphicsContext* wxGraphicsContext::Create( const wxWindowDC &dc )
 {
     return new wxMacCoreGraphicsContext((CGContextRef)dc.GetWindow()->MacGetCGContextRef() );
 }
 
+wxGraphicsContext* wxGraphicsContext::Create( wxWindow * window )
+{
+    return new wxMacCoreGraphicsContext( window );
+}
+
+wxGraphicsContext* wxGraphicsContext::CreateFromNative( void * context )
+{
+    return new wxMacCoreGraphicsContext((CGContextRef)context);
+}
+
+wxGraphicsContext* wxGraphicsContext::CreateFromNativeWindow( void * window )
+{
+    return new wxMacCoreGraphicsContext((WindowRef)window);
+}
+
 #endif // wxMAC_USE_CORE_GRAPHICS