]> git.saurik.com Git - wxWidgets.git/blobdiff - contrib/src/canvas/canvas.cpp
added groups of canvasobject and references to groups
[wxWidgets.git] / contrib / src / canvas / canvas.cpp
index 109ec8f2da3f1f4f768d973e3d94c39ee1a6f243..af813f01731326c81566eb149244779e93a82564 100644 (file)
     #include "wx/gtk/win_gtk.h"
 #endif
 
-#define USE_FREETYPE 1
+#ifndef wxUSE_FREETYPE
+    #define wxUSE_FREETYPE 1
+#endif
 
-#if USE_FREETYPE
-#include <freetype/freetype.h>
+#if wxUSE_FREETYPE
+    #include <freetype/freetype.h>
 #endif
 
 //----------------------------------------------------------------------------
 // globals
 //----------------------------------------------------------------------------
 
-#if USE_FREETYPE
+#if wxUSE_FREETYPE
 FT_Library g_freetypeLibrary;
 #endif
 
@@ -43,26 +45,42 @@ FT_Library g_freetypeLibrary;
 // wxCanvasObject
 //----------------------------------------------------------------------------
 
-wxCanvasObject::wxCanvasObject( int x, int y, int width, int height )
+wxCanvasObject::wxCanvasObject()
 {
     m_owner = NULL;
+    m_area.x = -1;
+    m_area.y = -1;
+    m_area.width = -1;
+    m_area.height = -1;
+    m_isControl = FALSE;
+    m_isVector = FALSE;
+    m_isImage = FALSE;
+}
+
+void wxCanvasObject::SetArea( int x, int y, int width, int height )
+{
     m_area.x = x;
     m_area.y = y;
     m_area.width = width;
     m_area.height = height;
-    m_isControl = FALSE;
-    m_isVector = FALSE;
-    m_isImage = FALSE;
+}
+
+void wxCanvasObject::SetArea( wxRect rect )
+{
+    m_area.x = rect.x;
+    m_area.y = rect.y;
+    m_area.width = rect.width;
+    m_area.height = rect.height;
 }
 
 void wxCanvasObject::Move( int x, int y )
 {
     int old_x = m_area.x;
     int old_y = m_area.y;
-    
+
     m_area.x = x;
     m_area.y = y;
-    
+
     if (!m_isControl)
     {
         // TODO: sometimes faster to merge into 1 Update or
@@ -72,32 +90,399 @@ void wxCanvasObject::Move( int x, int y )
     }
 }
 
+bool wxCanvasObject::IsHit( int x, int y, int margin )
+{
+    return ((x >= m_area.x-margin) &&
+            (x <= m_area.x+m_area.width+margin) &&
+            (y >= m_area.y-margin) &&
+            (y <= m_area.y+m_area.height+margin));
+}
+
+void wxCanvasObject::CaptureMouse()
+{
+    m_owner->SetCaptureMouse( this );
+}
+
+void wxCanvasObject::ReleaseMouse()
+{
+    m_owner->SetCaptureMouse( NULL );
+}
+
+bool wxCanvasObject::IsCapturedMouse()
+{
+    return m_owner->m_captureMouse==this;
+}
+
+
+void wxCanvasObject::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height )
+{
+}
+
+void wxCanvasObject::Recreate()
+{
+}
+
 void wxCanvasObject::WriteSVG( wxTextOutputStream &stream )
 {
 }
 
-void wxCanvasObject::Render( int clip_x, int clip_y, int clip_width, int clip_height )
+//----------------------------------------------------------------------------
+// wxCanvasObjectGroup
+//----------------------------------------------------------------------------
+
+wxCanvasObjectGroup::wxCanvasObjectGroup()
+{
+    m_validbounds=false;
+}
+
+void wxCanvasObjectGroup::SetOwner(wxCanvas* canvas)
+{
+    m_owner=canvas;
+    wxNode *node = m_objects.First();
+    while (node)
+    {
+        wxCanvasObject *obj = (wxCanvasObject*) node->Data();
+
+        obj->SetOwner(canvas);
+
+        node = node->Next();
+    }
+}
+
+void wxCanvasObjectGroup::ExtendArea(int x, int y)
+{
+   if (m_validbounds)
+   {
+     if ( x < m_minx ) m_minx = x;
+     if ( y < m_miny ) m_miny = y;
+     if ( x > m_maxx ) m_maxx = x;
+     if ( y > m_maxy ) m_maxy = y;
+   }
+   else
+   {
+      m_validbounds = true;
+
+      m_minx = x;
+      m_miny = y;
+      m_maxx = x;
+      m_maxy = y;
+   }
+
+}
+
+
+void wxCanvasObjectGroup::DeleteContents( bool flag)
+{
+    m_objects.DeleteContents( flag );
+    m_validbounds=false;
+}
+
+
+void wxCanvasObjectGroup::Prepend( wxCanvasObject* obj )
+{
+    m_objects.Insert( obj );
+    m_validbounds=false;
+}
+
+void wxCanvasObjectGroup::Append( wxCanvasObject* obj )
+{
+    m_objects.Append( obj );
+    m_validbounds=false;
+}
+
+void wxCanvasObjectGroup::Insert( size_t before, wxCanvasObject* obj )
+{
+    m_objects.Insert( before, obj );
+    m_validbounds=false;
+}
+
+void wxCanvasObjectGroup::Remove( wxCanvasObject* obj )
+{
+    m_objects.DeleteObject( obj );
+    m_validbounds=false;
+}
+
+void wxCanvasObjectGroup::Recreate()
+{
+    m_validbounds=false;
+    wxNode *node = m_objects.First();
+    while (node)
+    {
+        wxCanvasObject *obj = (wxCanvasObject*) node->Data();
+
+        obj->Recreate();
+        ExtendArea(obj->GetX(),obj->GetY());
+        ExtendArea(obj->GetX()+obj->GetWidth(),obj->GetY()+obj->GetHeight());
+
+        node = node->Next();
+    }
+}
+
+void wxCanvasObjectGroup::Render(int xabs, int yabs, int x, int y, int width, int height )
+{
+    wxImage *image = m_owner->GetBuffer();
+    // cycle through all objects
+    wxNode *node = m_objects.First();
+    while (node)
+    {
+        wxCanvasObject *obj = (wxCanvasObject*) node->Data();
+
+        if (!obj->IsControl())
+        {
+            // If we have 10.000 objects, we will go through
+            // this 10.000 times for each update, so we have
+            // to optimise carefully.
+            int clip_x = xabs + obj->GetX();
+            int clip_width = obj->GetWidth();
+            if (clip_x < x)
+            {
+                clip_width -= x-clip_x;
+                clip_x = x;
+            }
+            if (clip_width > 0)
+            {
+                if (clip_x + clip_width > x + width)
+                    clip_width = x+width-clip_x;
+
+                if (clip_width > 0)
+                {
+                    int clip_y = yabs + obj->GetY();
+                    int clip_height = obj->GetHeight();
+                    if (clip_y < y)
+                    {
+                        clip_height -= y-clip_y;
+                        clip_y = y;
+                    }
+                    if (clip_height > 0)
+                    {
+                        if (clip_y + clip_height > y + height)
+                            clip_height = y+height-clip_y;
+
+                        if (clip_height > 0)
+                            obj->Render(xabs,yabs, clip_x, clip_y, clip_width, clip_height );
+                    }
+                }
+            }
+        }
+
+        node = node->Next();
+    }
+}
+
+void wxCanvasObjectGroup::WriteSVG( wxTextOutputStream &stream )
+{
+}
+
+bool wxCanvasObjectGroup::IsHit( int x, int y, int margin )
+{
+    wxNode *node = m_objects.Last();
+    while (node)
+    {
+        wxCanvasObject *obj = (wxCanvasObject*) node->Data();
+
+        if (!obj->IsControl())
+        {
+            if (obj->IsHit(x,y,margin))
+            {
+                return true;
+            }
+        }
+        node = node->Previous();
+    }
+    return false;
+}
+
+wxCanvasObject* wxCanvasObjectGroup::IsHitObject( int x, int y, int margin )
+{
+    wxCanvasObject *obj=0;
+    wxNode *node = m_objects.Last();
+    while (node)
+    {
+        obj=(wxCanvasObject*) node->Data();
+
+        if (!obj->IsControl())
+        {
+            if (obj->IsHit(x,y,margin))
+            {
+                return obj;
+            }
+        }
+        node = node->Previous();
+    }
+    return 0;
+}
+
+//----------------------------------------------------------------------------
+// wxCanvasObjectGroupRef
+//----------------------------------------------------------------------------
+
+wxCanvasObjectGroupRef::wxCanvasObjectGroupRef(double x, double y, wxCanvasObjectGroup* group)
+   : wxCanvasObject()
+{
+    m_x = x;
+    m_y = y;
+    m_validbounds=false;
+    m_group=group;
+}
+
+void wxCanvasObjectGroupRef::SetOwner(wxCanvas* canvas)
+{
+    m_owner=canvas;
+    m_group->SetOwner(canvas);
+}
+
+void wxCanvasObjectGroupRef::ExtendArea(int x, int y)
+{
+   if (m_validbounds)
+   {
+     if ( x < m_minx ) m_minx = x;
+     if ( y < m_miny ) m_miny = y;
+     if ( x > m_maxx ) m_maxx = x;
+     if ( y > m_maxy ) m_maxy = y;
+   }
+   else
+   {
+      m_validbounds = true;
+
+      m_minx = x;
+      m_miny = y;
+      m_maxx = x;
+      m_maxy = y;
+   }
+
+}
+
+void wxCanvasObjectGroupRef::Recreate()
+{
+    m_validbounds=false;
+    m_group->Recreate();
+    ExtendArea(m_group->GetXMin(),m_group->GetYMin());
+    ExtendArea(m_group->GetXMax(),m_group->GetYMax());
+
+    //set the area in pixels relative to the parent
+    SetArea( m_owner->GetDeviceX( m_x + m_minx ),
+             m_owner->GetDeviceY( m_y + m_miny ),
+             m_owner->GetDeviceWidth( m_maxx-m_minx ),
+             m_owner->GetDeviceHeight( m_maxy-m_miny ) );
+}
+
+void wxCanvasObjectGroupRef::Render(int xabs, int yabs, int x, int y, int width, int height )
+{
+    wxImage *image = m_owner->GetBuffer();
+    xabs+=m_owner->GetDeviceX(GetPosX());
+    yabs+=m_owner->GetDeviceY(GetPosY());
+
+    int clip_x = xabs + m_group->GetXMin();
+    int clip_width = m_group->GetXMax()-m_group->GetXMin();
+    if (clip_x < x)
+    {
+        clip_width -= x-clip_x;
+        clip_x = x;
+    }
+    if (clip_width > 0)
+    {
+        if (clip_x + clip_width > x + width)
+            clip_width = x+width-clip_x;
+
+        if (clip_width > 0)
+        {
+            int clip_y = yabs + m_group->GetYMin();
+            int clip_height = m_group->GetYMax()-m_group->GetYMin();
+            if (clip_y < y)
+            {
+                clip_height -= y-clip_y;
+                clip_y = y;
+            }
+            if (clip_height > 0)
+            {
+                if (clip_y + clip_height > y + height)
+                    clip_height = y+height-clip_y;
+
+                if (clip_height > 0)
+                    m_group->Render(xabs,yabs, clip_x, clip_y, clip_width, clip_height );
+            }
+        }
+    }
+}
+
+void wxCanvasObjectGroupRef::WriteSVG( wxTextOutputStream &stream )
+{
+}
+
+bool wxCanvasObjectGroupRef::IsHit( int x, int y, int margin )
 {
+    return m_group->IsHit(x-GetPosX(),y-GetPosY(),margin);
 }
 
+wxCanvasObject* wxCanvasObjectGroupRef::IsHitObject( int x, int y, int margin )
+{
+    return m_group->IsHitObject(x-GetPosX(),y-GetPosY(),margin);
+}
+
+void wxCanvasObjectGroupRef::Move( int x, int y )
+{
+    int old_x = m_x;
+    int old_y = m_y;
+
+    m_x = x;
+    m_y = y;
+
+    if (!m_isControl)
+    {
+        // TODO: sometimes faster to merge into 1 Update or
+        // to break up into four
+        m_owner->Update(m_area.x, m_area.y, m_area.width, m_area.height );
+        //calculate the new area in pixels relative to the parent
+        SetArea( m_owner->GetDeviceX(  m_x + m_minx ),
+                 m_owner->GetDeviceY(  m_y + m_miny ),
+                 m_owner->GetDeviceWidth( m_maxx-m_minx ),
+                 m_owner->GetDeviceHeight( m_maxy-m_miny ) );
+        m_owner->Update( m_area.x, m_area.y, m_area.width, m_area.height );
+    }
+}
+
+
 //----------------------------------------------------------------------------
 // wxCanvasRect
 //----------------------------------------------------------------------------
 
-wxCanvasRect::wxCanvasRect( int x, int y, int w, int h, unsigned char red, unsigned char green, unsigned char blue )
-   : wxCanvasObject( x, y, w, h )
+wxCanvasRect::wxCanvasRect( double x, double y, double w, double h,
+                            unsigned char red, unsigned char green, unsigned char blue )
+   : wxCanvasObject()
 {
+    m_x = x;
+    m_y = y;
+    m_width = w;
+    m_height = h;
+
     m_red = red;
     m_green = green;
     m_blue = blue;
 }
 
-void wxCanvasRect::Render( int clip_x, int clip_y, int clip_width, int clip_height )
+void wxCanvasRect::Recreate()
+{
+    SetArea( m_owner->GetDeviceX( m_x ),
+             m_owner->GetDeviceY( m_y ),
+             m_owner->GetDeviceWidth( m_width ),
+             m_owner->GetDeviceHeight( m_height ) );
+}
+
+void wxCanvasRect::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height )
 {
     wxImage *image = m_owner->GetBuffer();
+    int buffer_x = m_owner->GetBufferX();
+    int buffer_y = m_owner->GetBufferY();
+
+    int start_y = clip_y - buffer_y;
+    int end_y = clip_y+clip_height - buffer_y;
+
+    int start_x = clip_x - buffer_x;
+    int end_x = clip_x+clip_width - buffer_x;
+
     // speed up later
-    for (int y = clip_y; y < clip_y+clip_height; y++)
-        for (int x = clip_x; x < clip_x+clip_width; x++)
+    for (int y = start_y; y < end_y; y++)
+        for (int x = start_x; x < end_x; x++)
             image->SetRGB( x, y, m_red, m_green, m_blue );
 }
 
@@ -105,35 +490,174 @@ void wxCanvasRect::WriteSVG( wxTextOutputStream &stream )
 {
 }
 
+//----------------------------------------------------------------------------
+// wxCanvasLine
+//----------------------------------------------------------------------------
+
+wxCanvasLine::wxCanvasLine( double x1, double y1, double x2, double y2,
+                            unsigned char red, unsigned char green, unsigned char blue )
+   : wxCanvasObject()
+{
+    m_x1 = x1;
+    m_y1 = y1;
+    m_x2 = x2;
+    m_y2 = y2;
+
+    m_red = red;
+    m_green = green;
+    m_blue = blue;
+}
+
+void wxCanvasLine::Recreate()
+{
+    int x1 = m_owner->GetDeviceX( m_x1 );
+    int y1 = m_owner->GetDeviceY( m_y1 );
+    int x2 = m_owner->GetDeviceX( m_x2 );
+    int y2 = m_owner->GetDeviceY( m_y2 );
+    if (x1 > x2)
+    {
+        int tmp = x1;
+        x1 = x2;
+        x2 = tmp;
+    }
+    if (y1 > y2)
+    {
+        int tmp = y1;
+        y1 = y2;
+        y2 = tmp;
+    }
+    SetArea( x1, y1, x2-x1+1, y2-y1+1 );
+}
+
+void wxCanvasLine::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height )
+{
+    wxImage *image = m_owner->GetBuffer();
+    int buffer_x = m_owner->GetBufferX();
+    int buffer_y = m_owner->GetBufferY();
+
+    if ((m_area.width == 0) && (m_area.height == 0))
+    {
+        image->SetRGB( m_area.x-buffer_x, m_area.y-buffer_y, m_red, m_green, m_blue );
+    }
+    else
+    {
+        int x1 = xabs + m_owner->GetDeviceX( m_x1 );
+        int y1 = yabs + m_owner->GetDeviceY( m_y1 );
+        int x2 = xabs + m_owner->GetDeviceX( m_x2 );
+        int y2 = yabs + m_owner->GetDeviceY( m_y2 );
+
+        wxInt32 d, ii, jj, di, ai, si, dj, aj, sj;
+        di = x1 - x2;
+        ai = abs(di) << 1;
+        si = (di < 0)? -1 : 1;
+        dj = y1 - y2;
+        aj = abs(dj) << 1;
+        sj = (dj < 0)? -1 : 1;
+
+        ii = x2;
+        jj = y2;
+
+        if (ai > aj)
+        {
+            // iterate over i
+            d = aj - (ai >> 1);
+
+            while (ii != x1)
+            {
+                if ((ii >= clip_x) && (ii < clip_x+clip_width) &&
+                    (jj >= clip_y) && (jj < clip_y+clip_height))
+                {
+                    image->SetRGB( ii-buffer_x, jj-buffer_y, m_red, m_blue, m_green );
+                }
+                if (d >= 0)
+                {
+                    jj += sj;
+                    d  -= ai;
+                }
+                ii += si;
+                d  += aj;
+            }
+        }
+        else
+        {
+            // iterate over j
+            d = ai - (aj >> 1);
+
+            while (jj != y1)
+            {
+                if ((ii >= clip_x) && (ii < clip_x+clip_width) &&
+                    (jj >= clip_y) && (jj < clip_y+clip_height))
+                {
+                    image->SetRGB( ii-buffer_x, jj-buffer_y, m_red, m_blue, m_green );
+                }
+                if (d >= 0)
+                {
+                    ii += si;
+                    d  -= aj;
+                }
+                jj += sj;
+                d  += ai;
+            }
+        }
+    }
+}
+
+void wxCanvasLine::WriteSVG( wxTextOutputStream &stream )
+{
+    // no idea
+}
+
 //----------------------------------------------------------------------------
 // wxCanvasImage
 //----------------------------------------------------------------------------
 
-wxCanvasImage::wxCanvasImage( const wxImage &image, int x, int y )
-   : wxCanvasObject( x, y, image.GetWidth(), image.GetHeight() )
+wxCanvasImage::wxCanvasImage( const wxImage &image, double x, double y, double w, double h )
+   : wxCanvasObject()
 {
+    m_x = x;
+    m_y = y;
+    m_width = w;
+    m_height = h;
+
     m_image = image;
     m_isImage = TRUE;
 }
 
-void wxCanvasImage::Render( int clip_x, int clip_y, int clip_width, int clip_height )
+void wxCanvasImage::Recreate()
 {
-    if ((clip_x == m_area.x) && 
-        (clip_y == m_area.y) && 
+    SetArea( m_owner->GetDeviceX( m_x ),
+             m_owner->GetDeviceY( m_y ),
+             m_owner->GetDeviceWidth( m_width ),
+             m_owner->GetDeviceHeight( m_height ) );
+
+    if ((m_area.width == m_image.GetWidth()) &&
+        (m_area.width == m_image.GetWidth()))
+        m_tmp = m_image;
+    else
+        m_tmp = m_image.Scale( m_area.width, m_area.height );
+}
+
+void wxCanvasImage::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height )
+{
+    int buffer_x = m_owner->GetBufferX();
+    int buffer_y = m_owner->GetBufferY();
+
+    if ((clip_x == xabs + m_area.x) &&
+        (clip_y == yabs + m_area.y) &&
         (clip_width == m_area.width) &&
         (clip_height == m_area.height))
     {
-        m_owner->GetBuffer()->Paste( m_image, clip_x, clip_y );
+        m_owner->GetBuffer()->Paste( m_tmp, clip_x-buffer_x, clip_y-buffer_y );
     }
     else
     {
         // local coordinates
-        int start_x = clip_x - m_area.x;
-        int start_y = clip_y - m_area.y;
-    
+        int start_x = clip_x - (xabs + m_area.x);
+        int start_y = clip_y - (yabs + m_area.y);
+
         wxRect rect( start_x, start_y, clip_width, clip_height );
-        wxImage sub_image( m_image.GetSubImage( rect ) );
-        m_owner->GetBuffer()->Paste( sub_image, clip_x, clip_y );
+        wxImage sub_image( m_tmp.GetSubImage( rect ) );
+        m_owner->GetBuffer()->Paste( sub_image, clip_x-buffer_x, clip_y-buffer_y );
     }
 }
 
@@ -147,11 +671,10 @@ void wxCanvasImage::WriteSVG( wxTextOutputStream &stream )
 //----------------------------------------------------------------------------
 
 wxCanvasControl::wxCanvasControl( wxWindow *control )
-   : wxCanvasObject( -1, -1, -1, -1 )
+   : wxCanvasObject()
 {
     m_isControl = TRUE;
     m_control = control;
-    UpdateSize();
 }
 
 wxCanvasControl::~wxCanvasControl()
@@ -159,15 +682,15 @@ wxCanvasControl::~wxCanvasControl()
     m_control->Destroy();
 }
 
-void wxCanvasControl::Move( int x, int y )
+void wxCanvasControl::Recreate()
 {
-    m_control->Move( x, y );
+    m_control->GetSize( &m_area.width, &m_area.height );
+    m_control->GetPosition( &m_area.x, &m_area.y );
 }
 
-void wxCanvasControl::UpdateSize()
+void wxCanvasControl::Move( int x, int y )
 {
-    m_control->GetSize( &m_area.width, &m_area.height );
-    m_control->GetPosition( &m_area.x, &m_area.y );
+    m_control->Move( x, y );
 }
 
 //----------------------------------------------------------------------------
@@ -177,34 +700,33 @@ void wxCanvasControl::UpdateSize()
 class wxFaceData
 {
 public:
-#if USE_FREETYPE
+#if wxUSE_FREETYPE
      FT_Face   m_face;
 #else
      void     *m_dummy;
-#endif    
+#endif
 };
 
-wxCanvasText::wxCanvasText( const wxString &text, int x, int y, const wxString &fontFile, int size )
-   : wxCanvasObject( x, y, -1, -1 )
+wxCanvasText::wxCanvasText( const wxString &text, double x, double y, const wxString &fontFile, int size )
+   : wxCanvasObject()
 {
     m_text = text;
     m_fontFileName = fontFile;
     m_size = size;
-    
+
     m_red = 0;
     m_green = 0;
     m_blue = 0;
-    
-    // test
-    m_area.width = 100;
-    m_area.height = m_size;
-    m_alpha = new unsigned char[100*m_size];
-    memset( m_alpha, 0, m_area.width*m_area.height );
-    
-#if USE_FREETYPE    
+
+    m_alpha = NULL;
+
+    m_x = x;
+    m_y = y;
+
+#if wxUSE_FREETYPE
     wxFaceData *data = new wxFaceData;
     m_faceData = data;
-    
+
     int error = FT_New_Face( g_freetypeLibrary,
                              m_fontFileName,
                              0,
@@ -215,13 +737,12 @@ wxCanvasText::wxCanvasText( const wxString &text, int x, int y, const wxString &
                               m_size*64,
                               96,    // screen dpi
                               96 );
-    CreateBuffer();
 #endif
 }
 
 wxCanvasText::~wxCanvasText()
 {
-#if USE_FREETYPE    
+#if wxUSE_FREETYPE
     wxFaceData *data = (wxFaceData*) m_faceData;
     delete data;
 #endif
@@ -241,26 +762,28 @@ void wxCanvasText::SetFlag( int flag )
     m_flag = flag;
 }
 
-void wxCanvasText::Render( int clip_x, int clip_y, int clip_width, int clip_height )
+void wxCanvasText::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height )
 {
     if (!m_alpha) return;
-    
+
     wxImage *image = m_owner->GetBuffer();
+    int buffer_x = m_owner->GetBufferX();
+    int buffer_y = m_owner->GetBufferY();
 
     // local coordinates
     int start_x = clip_x - m_area.x;
     int end_x = clip_width + start_x;
     int start_y = clip_y - m_area.y;
     int end_y = clip_height + start_y;
-    
+
     for (int y = start_y; y < end_y; y++)
         for (int x = start_x; x < end_x; x++)
         {
             int alpha = m_alpha[y*m_area.width + x];
             if (alpha)
             {
-                int image_x = m_area.x+x;
-                int image_y = m_area.y+y;
+                int image_x = m_area.x+x - buffer_x;
+                int image_y = m_area.y+y - buffer_y;
                 if (alpha == 255)
                 {
                     image->SetRGB( image_x, image_y, m_red, m_green, m_blue );
@@ -269,7 +792,7 @@ void wxCanvasText::Render( int clip_x, int clip_y, int clip_width, int clip_heig
                 int red1 = (m_red * alpha) / 255;
                 int green1 = (m_green * alpha) / 255;
                 int blue1 = (m_blue * alpha) / 255;
-                
+
                 alpha = 255-alpha;
                 int red2 = image->GetRed( image_x, image_y );
                 int green2 = image->GetGreen( image_x, image_y );
@@ -277,7 +800,7 @@ void wxCanvasText::Render( int clip_x, int clip_y, int clip_width, int clip_heig
                 red2 = (red2 * alpha) / 255;
                 green2 = (green2 * alpha) / 255;
                 blue2 = (blue2 * alpha) / 255;
-                
+
                 image->SetRGB( image_x, image_y, red1+red2, green1+green2, blue1+blue2 );
             }
         }
@@ -287,24 +810,34 @@ void wxCanvasText::WriteSVG( wxTextOutputStream &stream )
 {
 }
 
-void wxCanvasText::CreateBuffer()
+void wxCanvasText::Recreate()
 {
-#if USE_FREETYPE    
+    if (m_alpha) delete [] m_alpha;
+
+    m_area.x = m_owner->GetDeviceX( m_x );
+    m_area.y = m_owner->GetDeviceY( m_y );
+
+    m_area.width = 100;  // TODO, calculate length
+    m_area.height = m_size;
+    m_alpha = new unsigned char[100*m_size];
+    memset( m_alpha, 0, m_area.width*m_area.height );
+
+#if wxUSE_FREETYPE
     FT_Face face = ((wxFaceData*)m_faceData)->m_face;
     FT_GlyphSlot slot = face->glyph;
     int pen_x = 0;
     int pen_y = m_size;
-    
+
     for (int n = 0; n < (int)m_text.Len(); n++)
     {
         FT_UInt index = FT_Get_Char_Index( face, m_text[n] );
-        
+
         int error = FT_Load_Glyph( face, index, FT_LOAD_DEFAULT );
         if (error) continue;
-        
+
         error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
         if (error) continue;
-        
+
         FT_Bitmap *bitmap = &slot->bitmap;
         unsigned char* buffer = bitmap->buffer;
         for (int y = 0; y < bitmap->rows; y++)
@@ -312,15 +845,15 @@ void wxCanvasText::CreateBuffer()
             {
                 unsigned char alpha = buffer[ y*bitmap->pitch + x ];
                 if (alpha == 0) continue;
-                
+
                 int xx = pen_x + slot->bitmap_left + x;
                 int yy = pen_y - slot->bitmap_top + y;
                 m_alpha[ yy * m_area.width + xx ] = alpha;
             }
-        
+
         pen_x += slot->advance.x >> 6;
         pen_y += slot->advance.y >> 6;
-    }    
+    }
 #endif
 }
 
@@ -338,17 +871,28 @@ BEGIN_EVENT_TABLE(wxCanvas,wxScrolledWindow)
     EVT_MOUSE_EVENTS( wxCanvas::OnMouse )
     EVT_SET_FOCUS( wxCanvas::OnSetFocus )
     EVT_KILL_FOCUS( wxCanvas::OnKillFocus )
+    EVT_ERASE_BACKGROUND( wxCanvas::OnEraseBackground )
 END_EVENT_TABLE()
 
 wxCanvas::wxCanvas( wxWindow *parent, wxWindowID id,
     const wxPoint &position, const wxSize& size, long style ) :
     wxScrolledWindow( parent, id, position, size, style )
 {
+    m_bufferX = 0;
+    m_bufferY = 0;
     m_needUpdate = FALSE;
-    m_objects.DeleteContents( TRUE );
     m_red = 0;
     m_green = 0;
     m_blue = 0;
+    m_lastMouse = (wxCanvasObject*)NULL;
+    m_captureMouse = (wxCanvasObject*)NULL;
+    m_frozen = TRUE;
+    m_requestNewBuffer = TRUE;
+
+    //root group always at 0,0
+    m_root = new wxCanvasObjectGroup();
+    m_root->DeleteContents( TRUE );
+    m_root->SetOwner(this);
 }
 
 wxCanvas::~wxCanvas()
@@ -365,7 +909,6 @@ wxCanvas::~wxCanvas()
 
 void wxCanvas::SetArea( int width, int height )
 {
-    m_buffer = wxImage( width, height );
     SetScrollbars( 10, 10, width/10, height/10 );
 }
 
@@ -374,9 +917,13 @@ void wxCanvas::SetColour( unsigned char red, unsigned char green, unsigned char
     m_red = red;
     m_green = green;
     m_blue = blue;
-    
+
+    SetBackgroundColour( wxColour( red, green, blue ) );
+
+    if (m_frozen) return;
+
     unsigned char *data = m_buffer.GetData();
-    
+
     for (int y = 0; y < m_buffer.GetHeight(); y++)
         for (int x = 0; x < m_buffer.GetWidth(); x++)
         {
@@ -389,93 +936,94 @@ void wxCanvas::SetColour( unsigned char red, unsigned char green, unsigned char
         }
 }
 
-void wxCanvas::Update( int x, int y, int width, int height )
+void wxCanvas::SetCaptureMouse( wxCanvasObject *obj )
+{
+    if (obj)
+    {
+        wxWindow::CaptureMouse();
+        m_captureMouse = obj;
+    }
+    else
+    {
+        wxWindow::ReleaseMouse();
+        m_captureMouse = NULL;
+    }
+}
+
+void wxCanvas::Freeze()
+{
+    m_frozen = TRUE;
+}
+
+void wxCanvas::Thaw()
+{
+    wxNode *node = m_updateRects.First();
+    while (node)
+    {
+        wxRect *rect = (wxRect*) node->Data();
+        delete rect;
+        m_updateRects.DeleteNode( node );
+        node = m_updateRects.First();
+    }
+
+    m_frozen = FALSE;
+
+    if (m_buffer.Ok())
+        Update( m_bufferX, m_bufferY, m_buffer.GetWidth(), m_buffer.GetHeight() );
+}
+
+void wxCanvas::Update( int x, int y, int width, int height, bool blit )
 {
+    if (m_frozen) return;
+
     // clip to buffer
-    if (x < 0)
+    if (x < m_bufferX)
     {
-        width -= x;
-        x = 0;
+        width -= m_bufferX-x;
+        x = m_bufferX;
     }
     if (width < 0) return;
-    
-    if (y < 0)
+
+    if (y < m_bufferY)
     {
-        height -= y;
-        y = 0;
+        height -= m_bufferY-y;
+        y = m_bufferY;
     }
     if (height < 0) return;
-    
-    if (x+width > m_buffer.GetWidth())
+
+    if (x+width > m_bufferX+m_buffer.GetWidth())
     {
-        width = m_buffer.GetWidth() - x;
+        width = m_bufferX+m_buffer.GetWidth() - x;
     }
     if (width < 0) return;
-    
-    if (y+height > m_buffer.GetHeight())
+
+    if (y+height > m_bufferY+m_buffer.GetHeight())
     {
-        height = m_buffer.GetHeight() - y;
+        height = m_bufferY+m_buffer.GetHeight() - y;
     }
     if (height < 0) return;
-    
+
     // update is within the buffer
     m_needUpdate = TRUE;
-    
+
     // has to be blitted to screen later
-    m_updateRects.Append(
-        (wxObject*) new wxRect( x,y,width,height ) );
-    
+    if (blit)
+    {
+        m_updateRects.Append(
+            (wxObject*) new wxRect( x,y,width,height ) );
+    }
+
     // speed up with direct access, maybe add wxImage::Clear(x,y,w,h,r,g,b)
-    for (int yy = y; yy < y+height; yy++)
-        for (int xx = x; xx < x+width; xx++)
+    int start_y = y - m_bufferY;
+    int end_y = y+height - m_bufferY;
+    int start_x = x - m_bufferX;
+    int end_x = x+width - m_bufferX;
+    for (int yy = start_y; yy < end_y; yy++)
+        for (int xx = start_x; xx < end_x; xx++)
             m_buffer.SetRGB( xx, yy, m_red, m_green, m_blue );
 
-    // cycle through all objects
-    wxNode *node = m_objects.First();
-    while (node)
-    {
-        wxCanvasObject *obj = (wxCanvasObject*) node->Data();
-        
-        if (!obj->IsControl())
-        {
-            // If we have 10.000 objects, we will go through
-            // this 10.000 times for each update, so we have
-            // to optimise carefully.
-            int clip_x = obj->GetX();
-            int clip_width = obj->GetWidth();
-            if (clip_x < x)
-            {
-                clip_width -= x-clip_x;
-                clip_x = x;
-            }
-            if (clip_width > 0)
-            {
-                if (clip_x + clip_width > x + width)
-                    clip_width = x+width-clip_x;
-                    
-                if (clip_width > 0)
-                {
-                    int clip_y = obj->GetY();
-                    int clip_height = obj->GetHeight();
-                    if (clip_y < y)
-                    {
-                        clip_height -= y-clip_y;
-                        clip_y = y;
-                    }
-                    if (clip_height > 0)
-                    {
-                        if (clip_y + clip_height > y + height)
-                            clip_height = y+height-clip_y;
-                
-                        if (clip_height > 0)
-                            obj->Render( clip_x, clip_y, clip_width, clip_height );
-                    }
-                }
-            }
-        }
-            
-        node = node->Next();
-    }
+    m_root->Render(0,0, x, y, width, height );
+
 }
 
 void wxCanvas::BlitBuffer( wxDC &dc )
@@ -484,12 +1032,15 @@ void wxCanvas::BlitBuffer( wxDC &dc )
     while (node)
     {
         wxRect *rect = (wxRect*) node->Data();
-        wxImage sub_image( m_buffer.GetSubImage( *rect ) );
 
-        // DirectDraw here, please
+        wxRect sub_rect( *rect );
+        sub_rect.x -= m_bufferX;
+        sub_rect.y -= m_bufferY;
+
+        wxImage sub_image( m_buffer.GetSubImage( sub_rect ) );
 
 #ifdef __WXGTK__
-        int bpp = wxDisplayDepth(); 
+        int bpp = wxDisplayDepth();
         if (bpp > 8)
         {
             // the init code is doubled in wxImage
@@ -500,14 +1051,10 @@ void wxCanvas::BlitBuffer( wxDC &dc )
                 gdk_rgb_init();
                 s_hasInitialized = TRUE;
             }
-            
-            int x = rect->x;
-            int y = rect->y;
-            CalcScrolledPosition( x, y, &x, &y );
 
             gdk_draw_rgb_image( GTK_PIZZA(m_wxwindow)->bin_window,
                             m_wxwindow->style->black_gc,
-                            x, y,
+                            sub_rect.x, sub_rect.y,
                             sub_image.GetWidth(), sub_image.GetHeight(),
                             GDK_RGB_DITHER_NONE,
                             sub_image.GetData(),
@@ -524,51 +1071,81 @@ void wxCanvas::BlitBuffer( wxDC &dc )
         wxBitmap bitmap( sub_image.ConvertToBitmap() );
         dc.DrawBitmap( bitmap, rect->x, rect->y );
 #endif
-        
+
         delete rect;
         m_updateRects.DeleteNode( node );
         node = m_updateRects.First();
     }
-    
+
     m_needUpdate = FALSE;
 }
 
 void wxCanvas::UpdateNow()
 {
+    if (m_frozen) return;
+
     if (!m_needUpdate) return;
-    
+
     wxClientDC dc( this );
     PrepareDC( dc );
-    
+
     BlitBuffer( dc );
 }
 
+int wxCanvas::GetDeviceX( double x )
+{
+    return (int) x;
+}
+
+int wxCanvas::GetDeviceY( double y )
+{
+    return (int) y;
+}
+
+int wxCanvas::GetDeviceWidth( double width )
+{
+    return (int) width;
+}
+
+int wxCanvas::GetDeviceHeight( double height )
+{
+    return (int) height;
+}
+
+void wxCanvas::Recreate()
+{
+    m_root->Recreate();
+}
+
 void wxCanvas::Prepend( wxCanvasObject* obj )
 {
-    m_objects.Insert( obj );
-    
-    obj->SetOwner( this );
-    
+    m_root->Prepend( obj );
+    obj->SetOwner(this);
+
+    m_root->Recreate();
+
     if (!obj->IsControl())
         Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() );
 }
 
 void wxCanvas::Append( wxCanvasObject* obj )
 {
-    m_objects.Append( obj );
-    
-    obj->SetOwner( this );
-    
+    m_root->Append( obj );
+    obj->SetOwner(this);
+
+    m_root->Recreate();
+
     if (!obj->IsControl())
         Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() );
 }
 
 void wxCanvas::Insert( size_t before, wxCanvasObject* obj )
 {
-    m_objects.Insert( before, obj );
-    
-    obj->SetOwner( this );
-    
+    m_root->Insert( before, obj );
+    obj->SetOwner(this);
+
+    m_root->Recreate();
+
     if (!obj->IsControl())
         Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() );
 }
@@ -580,9 +1157,9 @@ void wxCanvas::Remove( wxCanvasObject* obj )
     int w = obj->GetWidth();
     int h = obj->GetHeight();
     bool ic = obj->IsControl();
-    
-    m_objects.DeleteObject( obj );
-    
+
+    m_root->Remove( obj );
+
     if (!ic)
         Update( x, y, w, h );
 }
@@ -591,7 +1168,11 @@ void wxCanvas::OnPaint(wxPaintEvent &event)
 {
     wxPaintDC dc(this);
     PrepareDC( dc );
-    
+
+    if (!m_buffer.Ok()) return;
+
+    if (m_frozen) return;
+
     m_needUpdate = TRUE;
 
     wxRegionIterator it( GetUpdateRegion() );
@@ -599,32 +1180,211 @@ void wxCanvas::OnPaint(wxPaintEvent &event)
     {
         int x = it.GetX();
         int y = it.GetY();
-        CalcUnscrolledPosition( x, y, &x, &y );
-        
+
         int w = it.GetWidth();
         int h = it.GetHeight();
-        
+
         if (x+w > m_buffer.GetWidth())
             w = m_buffer.GetWidth() - x;
         if (y+h > m_buffer.GetHeight())
             h = m_buffer.GetHeight() - y;
-        
+
         if ((w > 0) && (h > 0))
+        {
+            CalcUnscrolledPosition( x, y, &x, &y );
             m_updateRects.Append( (wxObject*) new wxRect( x, y, w, h ) );
-        
+        }
+
         it++;
     }
-    
+
     BlitBuffer( dc );
 }
 
+void wxCanvas::ScrollWindow( int dx, int dy, const wxRect* rect )
+{
+    // If any updates are pending, do them now since they will
+    // expect the previous m_bufferX and m_bufferY values.
+    UpdateNow();
+
+    // The buffer always starts at the top left corner of the
+    // client area. Indeed, it is the client area.
+    CalcUnscrolledPosition( 0, 0, &m_bufferX, &m_bufferY );
+
+    unsigned char* data = m_buffer.GetData();
+
+    if (dy != 0)
+    {
+        if (dy > 0)
+        {
+            unsigned char *source = data;
+            unsigned char *dest = data + (dy * m_buffer.GetWidth() * 3);
+            size_t count = (size_t) (m_buffer.GetWidth() * 3 * (m_buffer.GetHeight()-dy));
+            memmove( dest, source, count );
+
+            // We update the new buffer area, but there is no need to
+            // blit (last param FALSE) since the ensuing paint event will
+            // do that anyway.
+            Update( m_bufferX, m_bufferY, m_buffer.GetWidth(), dy, FALSE );
+        }
+        else
+        {
+            unsigned char *dest = data;
+            unsigned char *source = data + (-dy * m_buffer.GetWidth() * 3);
+            size_t count = (size_t) (m_buffer.GetWidth() * 3 * (m_buffer.GetHeight()+dy));
+            memmove( dest, source, count );
+
+            // We update the new buffer area, but there is no need to
+            // blit (last param FALSE) since the ensuing paint event will
+            // do that anyway.
+            Update( m_bufferX, m_bufferY+m_buffer.GetHeight()+dy, m_buffer.GetWidth(), -dy, FALSE );
+        }
+    }
+
+    if (dx != 0)
+    {
+        if (dx > 0)
+        {
+            unsigned char *source = data;
+            for (int y = 0; y < m_buffer.GetHeight(); y++)
+            {
+                unsigned char *dest = source + dx*3;
+                memmove( dest, source, (m_buffer.GetWidth()-dx) * 3 );
+                source += m_buffer.GetWidth()*3;
+            }
+
+            // We update the new buffer area, but there is no need to
+            // blit (last param FALSE) since the ensuing paint event will
+            // do that anyway.
+            Update( m_bufferX, m_bufferY, dx, m_buffer.GetHeight(), FALSE );
+        }
+        else
+        {
+            unsigned char *dest = data;
+            for (int y = 0; y < m_buffer.GetHeight(); y++)
+            {
+                unsigned char *source = dest - dx*3;
+                memmove( dest, source, (m_buffer.GetWidth()+dx) * 3 );
+                dest += m_buffer.GetWidth()*3;
+            }
+
+            // We update the new buffer area, but there is no need to
+            // blit (last param FALSE) since the ensuing paint event will
+            // do that anyway.
+            Update( m_bufferX+m_buffer.GetWidth()+dx, m_bufferY, -dx, m_buffer.GetHeight(), FALSE );
+        }
+    }
+
+    wxWindow::ScrollWindow( dx, dy, rect );
+}
+
 void wxCanvas::OnMouse(wxMouseEvent &event)
 {
-    // Propagate to objects here
+    int x = event.GetX();
+    int y = event.GetY();
+    CalcUnscrolledPosition( x, y, &x, &y );
+
+    if (event.GetEventType() == wxEVT_MOTION)
+    {
+        if (m_captureMouse) //no matter what go to this one
+        {
+            wxMouseEvent child_event( wxEVT_MOTION );
+            child_event.SetEventObject(m_captureMouse);
+            child_event.m_x = x - m_captureMouse->GetX();
+            child_event.m_y = y - m_captureMouse->GetY();
+            child_event.m_leftDown = event.m_leftDown;
+            child_event.m_rightDown = event.m_rightDown;
+            child_event.m_middleDown = event.m_middleDown;
+            child_event.m_controlDown = event.m_controlDown;
+            child_event.m_shiftDown = event.m_shiftDown;
+            child_event.m_altDown = event.m_altDown;
+            child_event.m_metaDown = event.m_metaDown;
+            m_captureMouse->ProcessEvent( child_event );
+        }
+        else
+        {
+            wxCanvasObject *obj = m_root->IsHitObject(x,y,0);
+
+            if (obj && !obj->IsControl())
+            {
+                wxMouseEvent child_event( wxEVT_MOTION );
+                child_event.SetEventObject( obj );
+                child_event.m_x = x - obj->GetX();
+                child_event.m_y = y - obj->GetY();
+                child_event.m_leftDown = event.m_leftDown;
+                child_event.m_rightDown = event.m_rightDown;
+                child_event.m_middleDown = event.m_middleDown;
+                child_event.m_controlDown = event.m_controlDown;
+                child_event.m_shiftDown = event.m_shiftDown;
+                child_event.m_altDown = event.m_altDown;
+                child_event.m_metaDown = event.m_metaDown;
+
+                if ((obj != m_lastMouse) && (m_lastMouse != NULL))
+                {
+                    child_event.SetEventType( wxEVT_LEAVE_WINDOW );
+                    child_event.SetEventObject( m_lastMouse );
+                    child_event.m_x = x - m_lastMouse->GetX();
+                    child_event.m_y = y - m_lastMouse->GetY();
+                    m_lastMouse->ProcessEvent( child_event );
+
+                    m_lastMouse = obj;
+                    child_event.SetEventType( wxEVT_ENTER_WINDOW );
+                    child_event.SetEventObject( m_lastMouse );
+                    child_event.m_x = x - m_lastMouse->GetX();
+                    child_event.m_y = y - m_lastMouse->GetY();
+                    m_lastMouse->ProcessEvent( child_event );
+
+                    child_event.SetEventType( wxEVT_MOTION );
+                    child_event.SetEventObject( obj );
+                }
+                obj->ProcessEvent( child_event );
+                return;
+            }
+        }
+        if (m_lastMouse)
+        {
+            wxMouseEvent child_event( wxEVT_LEAVE_WINDOW );
+            child_event.SetEventObject( m_lastMouse );
+            child_event.m_x = x - m_lastMouse->GetX();
+            child_event.m_y = y - m_lastMouse->GetY();
+            child_event.m_leftDown = event.m_leftDown;
+            child_event.m_rightDown = event.m_rightDown;
+            child_event.m_middleDown = event.m_middleDown;
+            child_event.m_controlDown = event.m_controlDown;
+            child_event.m_shiftDown = event.m_shiftDown;
+            child_event.m_altDown = event.m_altDown;
+            child_event.m_metaDown = event.m_metaDown;
+            m_lastMouse->ProcessEvent( child_event );
+
+            m_lastMouse = (wxCanvasObject*) NULL;
+            return;
+        }
+    }
+
+    event.Skip();
 }
 
 void wxCanvas::OnSize(wxSizeEvent &event)
 {
+    int w,h;
+    GetClientSize( &w, &h );
+    m_buffer = wxImage( w, h );
+
+    CalcUnscrolledPosition( 0, 0, &m_bufferX, &m_bufferY );
+
+    wxNode *node = m_updateRects.First();
+    while (node)
+    {
+        wxRect *rect = (wxRect*) node->Data();
+        delete rect;
+        m_updateRects.DeleteNode( node );
+        node = m_updateRects.First();
+    }
+
+    m_frozen = FALSE;
+
+    Update( m_bufferX, m_bufferY, m_buffer.GetWidth(), m_buffer.GetHeight(), FALSE );
+
     event.Skip();
 }
 
@@ -647,6 +1407,10 @@ void wxCanvas::OnChar(wxKeyEvent &event)
     event.Skip();
 }
 
+void wxCanvas::OnEraseBackground(wxEraseEvent &event)
+{
+}
+
 //--------------------------------------------------------------------
 // wxCanvasModule
 //--------------------------------------------------------------------
@@ -665,7 +1429,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxCanvasModule, wxModule)
 
 bool wxCanvasModule::OnInit()
 {
-#if USE_FREETYPE
+#if wxUSE_FREETYPE
     int error = FT_Init_FreeType( &g_freetypeLibrary );
     if (error) return FALSE;
 #endif
@@ -675,7 +1439,7 @@ bool wxCanvasModule::OnInit()
 
 void wxCanvasModule::OnExit()
 {
-#if USE_FREETYPE
+#if wxUSE_FREETYPE
     FT_Done_FreeType( g_freetypeLibrary );
 #endif
 }