X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/3b111dbe5329143c501d4ff8853691a832b7d2ab..52b5ab7edc7296c18f895225efda65db0de62711:/contrib/src/canvas/canvas.cpp?ds=inline diff --git a/contrib/src/canvas/canvas.cpp b/contrib/src/canvas/canvas.cpp index 3556034ad6..7a63dc0352 100644 --- a/contrib/src/canvas/canvas.cpp +++ b/contrib/src/canvas/canvas.cpp @@ -18,6 +18,8 @@ #endif #include "wx/canvas/canvas.h" +#include "wx/canvas/polygon.h" +#include "wx/canvas/liner.h" #ifdef __WXGTK__ #include @@ -25,335 +27,2915 @@ #include "wx/gtk/win_gtk.h" #endif -// WDR: class implementations +#ifndef wxUSE_FREETYPE + #define wxUSE_FREETYPE 0 +#endif + +#if wxUSE_FREETYPE + #include +#endif + +//#define CANVASDEBUG + +//---------------------------------------------------------------------------- +// globals +//---------------------------------------------------------------------------- + +const double pi = 3.1415926535; + +#if wxUSE_FREETYPE +FT_Library g_freetypeLibrary; +#endif //---------------------------------------------------------------------------- // wxCanvasObject //---------------------------------------------------------------------------- -wxCanvasObject::wxCanvasObject( int x, int y, int width, int height ) +wxCanvasObject::wxCanvasObject() { - m_owner = NULL; - m_area.x = x; - m_area.y = y; - m_area.width = width; - m_area.height = height; + // the default event handler is just this object + m_eventHandler=this; + m_admin = NULL; m_isControl = FALSE; m_isVector = FALSE; m_isImage = FALSE; + m_visible = TRUE; + m_dragmode = wxDRAG_ONTOP; +// handy when debugging +// m_dragmode = wxDRAG_RECTANGLE; + m_dragable = TRUE; } -void wxCanvasObject::Move( int x, int y ) +bool wxCanvasObject::ProcessCanvasObjectEvent(wxEvent& event) { - 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 - // to break up into four - m_owner->Update( old_x, old_y, m_area.width, m_area.height ); - m_owner->Update( x, y, m_area.width, m_area.height ); - } + return m_eventHandler->ProcessEvent(event); } -void wxCanvasObject::WriteSVG( wxTextOutputStream &stream ) +void wxCanvasObject::PushEventHandler(wxEvtHandler *handler) { + handler->SetNextHandler(GetEventHandler()); + m_eventHandler=handler; } -void wxCanvasObject::Render( int clip_x, int clip_y, int clip_width, int clip_height ) +wxEvtHandler *wxCanvasObject::PopEventHandler(bool deleteHandler) { -} - -//---------------------------------------------------------------------------- -// wxCanvasImage -//---------------------------------------------------------------------------- + wxEvtHandler *handlerA = m_eventHandler; + if ( handlerA ) + { + wxEvtHandler *handlerB = handlerA->GetNextHandler(); + handlerA->SetNextHandler((wxEvtHandler *)NULL); + m_eventHandler=handlerB; + if ( deleteHandler ) + { + delete handlerA; + handlerA = (wxEvtHandler *)NULL; + } + } -wxCanvasImage::wxCanvasImage( const wxImage &image, int x, int y ) - : wxCanvasObject( x, y, image.GetWidth(), image.GetHeight() ) -{ - m_image = image; - m_isImage = TRUE; + return handlerA; } -void wxCanvasImage::Render( int clip_x, int clip_y, int clip_width, int clip_height ) +void wxCanvasObject::AppendEventHandler(wxEvtHandler *handler) { - m_owner->GetBuffer()->Paste( m_image, m_area.x, m_area.y ); + GetEventHandler()->SetNextHandler(handler); } -void wxCanvasImage::WriteSVG( wxTextOutputStream &stream ) +wxEvtHandler *wxCanvasObject::RemoveLastEventHandler(bool deleteHandler) { - // no idea -} + //always the first in the row + wxEvtHandler *handlerA = m_eventHandler; + wxEvtHandler *handlerB=handlerA; + //goto the end + while ( handlerA->GetNextHandler() ) + { + handlerB = handlerA; + handlerA = handlerA->GetNextHandler(); + } -//---------------------------------------------------------------------------- -// wxCanvasCtrl -//---------------------------------------------------------------------------- + handlerB->SetNextHandler((wxEvtHandler *)NULL); + if ( deleteHandler ) + { + delete handlerA; + } -wxCanvasControl::wxCanvasControl( wxWindow *control ) - : wxCanvasObject( -1, -1, -1, -1 ) -{ - m_control = control; - UpdateSize(); + return GetEventHandler(); } -wxCanvasControl::~wxCanvasControl() +wxRect wxCanvasObject::GetAbsoluteArea(const wxTransformMatrix& cworld) { - m_control->Destroy(); -} + wxBoundingBox tmp=m_bbox; + tmp.MapBbox(cworld); -void wxCanvasControl::Move( int x, int y ) -{ - m_control->Move( x, y ); + int x1 = m_admin->LogicalToDeviceX( tmp.GetMinX() ); + int y1 = m_admin->LogicalToDeviceY( tmp.GetMinY() ); + int x2 = m_admin->LogicalToDeviceX( tmp.GetMaxX() ); + int y2 = m_admin->LogicalToDeviceY( tmp.GetMaxY() ); + + if (x1 > x2) + { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + if (y1 > y2) + { + int tmp = y1; + y1 = y2; + y2 = tmp; + } + + wxRect tmparea; + tmparea.x = x1; + tmparea.y = y1; + tmparea.width = x2-x1; // FIXME +1 ? + tmparea.height = y2-y1; // FIXME +1 ? + + return tmparea; } -void wxCanvasControl::UpdateSize() +void wxCanvasObject::MoveAbsolute( double x, double y ) { - m_control->GetSize( &m_area.width, &m_area.height ); - m_control->GetPosition( &m_area.x, &m_area.y ); -} + //save old position of boundingbox + double oldx = GetXMin(); + double oldy = GetYMin(); + double w = m_bbox.GetWidth(); + double h = m_bbox.GetHeight(); -//---------------------------------------------------------------------------- -// wxCanvas -//---------------------------------------------------------------------------- + SetPosXY(x,y); -IMPLEMENT_CLASS(wxCanvas,wxScrolledWindow) + double newx=GetXMin(); + double newy=GetYMin(); -BEGIN_EVENT_TABLE(wxCanvas,wxScrolledWindow) - EVT_CHAR( wxCanvas::OnChar ) - EVT_PAINT( wxCanvas::OnPaint ) - EVT_SIZE( wxCanvas::OnSize ) - EVT_IDLE( wxCanvas::OnIdle ) - EVT_MOUSE_EVENTS( wxCanvas::OnMouse ) - EVT_SET_FOCUS( wxCanvas::OnSetFocus ) - EVT_KILL_FOCUS( wxCanvas::OnKillFocus ) -END_EVENT_TABLE() + double leftu,rightu,bottomu,topu ; + leftu = wxMin (oldx, newx ) ; + rightu = wxMax (oldx + w, newx + w) ; + topu = wxMin (oldy, newy) ; + bottomu = wxMax (oldy + h, newy + h) ; -wxCanvas::wxCanvas( wxWindow *parent, wxWindowID id, - const wxPoint &position, const wxSize& size, long style ) : - wxScrolledWindow( parent, id, position, size, style ) -{ - m_needUpdate = FALSE; - m_objects.DeleteContents( TRUE ); + if ( rightu - leftu < 2*w && bottomu - topu < 2*h ) + { + m_admin->Update( this,leftu, topu, rightu - leftu, bottomu - topu); + } + else + { + m_admin->Update( this, oldx, oldy, w, h ); + m_admin->Update( this, newx, newy, w, h ); + } } -wxCanvas::~wxCanvas() +void wxCanvasObject::MoveRelative( double x, double y ) { - wxNode *node = m_updateRects.First(); - while (node) + //save old position of boundingbox + double oldx = GetXMin(); + double oldy = GetYMin(); + double w = m_bbox.GetWidth(); + double h = m_bbox.GetHeight(); + + TransLate(x,y); + + double newx=GetXMin(); + double newy=GetYMin(); + + double leftu,rightu,bottomu,topu ; + leftu = wxMin (oldx, newx ) ; + rightu = wxMax (oldx + w, newx + w) ; + topu = wxMin (oldy, newy) ; + bottomu = wxMax (oldy + h, newy + h) ; + + if ( rightu - leftu < 2*w && bottomu - topu < 2*h ) { - wxRect *rect = (wxRect*) node->Data(); - delete rect; - m_updateRects.DeleteNode( node ); - node = m_updateRects.First(); + m_admin->Update( this,leftu, topu, rightu - leftu, bottomu - topu); + } + else + { + m_admin->Update( this, oldx, oldy, w, h ); + m_admin->Update( this, newx, newy, w, h ); } } -void wxCanvas::SetArea( int width, int height ) -{ - m_buffer = wxImage( width, height ); - SetScrollbars( 10, 10, width/10, height/10 ); -} -void wxCanvas::Update( int x, int y, int width, int height ) +void wxCanvasObject::DragStart() { - m_needUpdate = TRUE; - - m_updateRects.Append( - (wxObject*) new wxRect( x,y,width,height ) ); - - // speed up with direct access - int xx,yy,ww,hh; - for (yy = y; yy < y+height; yy++) - for (xx = x; xx < x+width; xx++) - m_buffer.SetRGB( xx, yy, 0, 0, 0 ); + if (m_dragmode == wxDRAG_RECTANGLE) + { + this->SetVisible(FALSE); + wxTransformMatrix help; + double x = GetXMin(); + double y = GetYMin(); + double w = m_bbox.GetWidth(); + double h = m_bbox.GetHeight(); + m_admin->Update( this, x, y, w, h ); + m_admin->UpdateNow(); - wxNode *node = m_objects.First(); - while (node) + wxRect recold=GetAbsoluteArea(help); + wxClientDC dc(m_admin->GetActive()); + dc.SetPen(*wxBLACK_PEN); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.SetLogicalFunction(wxINVERT); + dc.DrawRectangle(recold); + dc.SetBrush(wxNullBrush); + dc.SetPen(wxNullPen); + } + else if (m_dragmode != wxDRAG_REDRAW) { - wxCanvasObject *obj = (wxCanvasObject*) node->Data(); - xx = obj->GetX(); - yy = obj->GetY(); - ww = obj->GetWidth(); - hh = obj->GetHeight(); - - if (!obj->IsControl()) - { - obj->Render( x, y, width, height ); - } - - node = node->Next(); + this->SetVisible(FALSE); + wxTransformMatrix help; + double x = GetXMin(); + double y = GetYMin(); + double w = m_bbox.GetWidth(); + double h = m_bbox.GetHeight(); + + wxRect recnew=GetAbsoluteArea(help); + + //redraw in buffer what should be there without this object + m_admin->Update( this, x, y, w, h ); + m_admin->GetActive()->Freeze(); + + //save the drawing (without the object itself to a bitmap) + m_atnewpos = wxBitmap(recnew.width,recnew.height); + wxMemoryDC dcm; + dcm.SelectObject(*m_admin->GetActive()->GetBuffer()); + wxMemoryDC tmp; + tmp.SelectObject(m_atnewpos); + tmp.Blit(0,0,recnew.width,recnew.height,&dcm,recnew.x,recnew.y,wxCOPY,FALSE); + tmp.SelectObject(wxNullBitmap); + dcm.SelectObject(wxNullBitmap); } } -void wxCanvas::BlitBuffer( wxDC &dc ) + +void wxCanvasObject::DragRelative( double x, double y) { - wxNode *node = m_updateRects.First(); - while (node) + if (m_dragmode == wxDRAG_RECTANGLE) { - wxRect *rect = (wxRect*) node->Data(); - wxImage sub_image( m_buffer.GetSubImage( *rect ) ); + wxTransformMatrix help; - // DirectDraw here, please + wxRect recold=GetAbsoluteArea(help); -#ifdef __WXGTK__ - int bpp = wxDisplayDepth(); - if (bpp > 8) - { - // the init code is doubled in wxImage - static bool s_hasInitialized = FALSE; + TransLate(x,y); - if (!s_hasInitialized) - { - 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_image.GetWidth(), sub_image.GetHeight(), - GDK_RGB_DITHER_NONE, - sub_image.GetData(), - sub_image.GetWidth()*3 ); + wxRect recnew=GetAbsoluteArea(help); + + wxClientDC dc(m_admin->GetActive()); + dc.SetPen(*wxBLACK_PEN); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.SetLogicalFunction(wxINVERT); + dc.DrawRectangle(recold); + dc.DrawRectangle(recnew); + dc.SetBrush(wxNullBrush); + dc.SetPen(wxNullPen); + } + else if (m_dragmode != wxDRAG_REDRAW) + { + wxClientDC dc(m_admin->GetActive()); + wxMemoryDC tmp; + + wxTransformMatrix help; + wxRect recold=GetAbsoluteArea(help); + + //restore what was there (without the object itself) + wxMemoryDC dcm; + dcm.SelectObject(*m_admin->GetActive()->GetBuffer()); + tmp.SelectObject(m_atnewpos); + dcm.Blit(recold.x,recold.y,recold.width,recold.height,&tmp,0,0,wxCOPY,FALSE); + + TransLate(x,y); + + wxRect recnew=GetAbsoluteArea(help); + + //save the contents of the buffer at the new position + tmp.Blit(0,0,recnew.width,recnew.height,&dcm,recnew.x,recnew.y,wxCOPY,FALSE); + tmp.SelectObject(wxNullBitmap); + + //m_atnewpos = m_admin->GetActive()->GetBuffer()->GetSubBitmap( recnew ); + + this->SetVisible(TRUE); + //redraw object into the buffer + m_admin->GetActive()->SetDC(&dcm); + Render(&help,recnew.x,recnew.y,recnew.width,recnew.height); + + //draw the union or seperate to the canvas + double leftu,rightu,bottomu,topu ; + leftu = wxMin (recold.x, recnew.x ) ; + rightu = wxMax (recold.x + recold.width, recnew.x + recnew.width ) ; + topu = wxMin (recold.y, recnew.y) ; + bottomu = wxMax (recold.y + recold.height, recnew.y + recnew.height) ; + + if ( rightu - leftu < 2*recold.width && bottomu - topu < 2*recold.height) + { + dc.Blit(leftu,topu,rightu - leftu,bottomu - topu,&dcm,leftu,topu,wxCOPY,FALSE); } else { - wxBitmap bitmap( sub_image.ConvertToBitmap() ); - dc.DrawBitmap( bitmap, rect->x, rect->y ); + //do them seperate + //first redraw what should be at the old position in the canvas + dc.Blit(recold.x,recold.y,recold.width,recold.height,&dcm,recold.x,recold.y,wxCOPY,FALSE); + //blit the new position of the object to the canvas + dc.Blit(recnew.x,recnew.y,recnew.width,recnew.height,&dcm,recnew.x,recnew.y,wxCOPY,FALSE); } -#endif + dcm.SelectObject(wxNullBitmap); + this->SetVisible(FALSE); + } + else + MoveRelative(x,y); +} -#ifndef __WXGTK__ - wxBitmap bitmap( sub_image.ConvertToBitmap() ); - dc.DrawBitmap( bitmap, rect->x, rect->y ); -#endif - - delete rect; - m_updateRects.DeleteNode( node ); - node = m_updateRects.First(); + +void wxCanvasObject::DragEnd() +{ + m_atnewpos = wxBitmap(0,0); + m_admin->GetActive()->Thaw(); + this->SetVisible(TRUE); + double x = GetXMin(); + double y = GetYMin(); + double w = m_bbox.GetWidth(); + double h = m_bbox.GetHeight(); + m_admin->Update( this, x, y, w, h ); + m_admin->UpdateNow(); +} + +wxCanvasObject* wxCanvasObject::IsHitWorld( double x, double y, double margin ) +{ + if ((x >= m_bbox.GetMinX()-margin) && + (x <= m_bbox.GetMaxX()+margin) && + (y >= m_bbox.GetMinY()-margin) && + (y <= m_bbox.GetMaxY()+margin)) + { + return this; } - m_needUpdate = FALSE; + return (wxCanvasObject*) NULL; } -void wxCanvas::UpdateNow() +wxCanvasObject* wxCanvasObject::Contains( wxCanvasObject* obj ) { - if (!m_needUpdate) return; - - wxClientDC dc( this ); - PrepareDC( dc ); - - BlitBuffer( dc ); + if (obj == this) + return this; + + return (wxCanvasObject*) NULL; } -void wxCanvas::Prepend( wxCanvasObject* obj ) +void wxCanvasObject::CaptureMouse() { - m_objects.Insert( obj ); - - obj->SetOwner( this ); - - if (!obj->IsControl()) - Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() ); + m_admin->GetActive()->SetCaptureMouse( this ); } -void wxCanvas::Append( wxCanvasObject* obj ) +void wxCanvasObject::ReleaseMouse() { - m_objects.Append( obj ); - - obj->SetOwner( this ); - - if (!obj->IsControl()) - Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() ); + m_admin->GetActive()->SetCaptureMouse( NULL ); } -void wxCanvas::Insert( size_t before, wxCanvasObject* obj ) +bool wxCanvasObject::IsCapturedMouse() { - m_objects.Insert( before, obj ); - - obj->SetOwner( this ); - - if (!obj->IsControl()) - Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() ); + return (m_admin->GetActive()->GetCaptured() == this); } -void wxCanvas::Remove( wxCanvasObject* obj ) + +void wxCanvasObject::Render(wxTransformMatrix* cworld, int clip_x, int clip_y, int clip_width, int clip_height ) { - int x = obj->GetX(); - int y = obj->GetY(); - int w = obj->GetWidth(); - int h = obj->GetHeight(); - bool ic = obj->IsControl(); - - m_objects.DeleteObject( obj ); - - if (!ic) - Update( x, y, w, h ); } -void wxCanvas::OnPaint(wxPaintEvent &event) +void wxCanvasObject::CalcBoundingBox() { - wxPaintDC dc(this); - PrepareDC( dc ); - - m_needUpdate = TRUE; +} - wxRegionIterator it( GetUpdateRegion() ); - while (it) +void wxCanvasObject::WriteSVG( wxTextOutputStream &stream ) +{ +} + +//---------------------------------------------------------------------------- +// wxCanvasObjectGroup +//---------------------------------------------------------------------------- + +wxCanvasObjectGroup::wxCanvasObjectGroup(double x, double y) +{ + lworld.Translate(x,y); + //no objects make the bounding box the x,y and take care of it later + m_bbox.Expand(x,y); +} + +wxCanvasObjectGroup::~wxCanvasObjectGroup() +{ +} + +void wxCanvasObjectGroup::PushEventHandler(wxEvtHandler *handler) +{ + wxCanvasObject::PushEventHandler(handler); + wxNode *node = m_objects.First(); + while (node) { - 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; - - m_updateRects.Append( (wxObject*) new wxRect( x, y, w, h ) ); - - it++; + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + obj->PushEventHandler(handler); + + node = node->Next(); } - - BlitBuffer( dc ); } -void wxCanvas::OnMouse(wxMouseEvent &event) +wxEvtHandler *wxCanvasObjectGroup::PopEventHandler(bool deleteHandler) { - // Propagate to objects here + wxNode *node = m_objects.First(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + obj->PopEventHandler(deleteHandler); + + node = node->Next(); + } + return wxCanvasObject::PopEventHandler(deleteHandler); } -void wxCanvas::OnSize(wxSizeEvent &event) +void wxCanvasObjectGroup::AppendEventHandler(wxEvtHandler *handler) { - event.Skip(); + wxCanvasObject::AppendEventHandler(handler); + wxNode *node = m_objects.First(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + obj->AppendEventHandler(handler); + + node = node->Next(); + } } -void wxCanvas::OnIdle(wxIdleEvent &event) +wxEvtHandler *wxCanvasObjectGroup::RemoveLastEventHandler(bool deleteHandler) { - UpdateNow(); - event.Skip(); + wxNode *node = m_objects.First(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + obj->RemoveLastEventHandler(deleteHandler); + + node = node->Next(); + } + return wxCanvasObject::RemoveLastEventHandler(deleteHandler); } -void wxCanvas::OnSetFocus(wxFocusEvent &event) +void wxCanvasObjectGroup::TransLate( double x, double y ) { + lworld.Translate(x,y); + CalcBoundingBox(); } -void wxCanvas::OnKillFocus(wxFocusEvent &event) +void wxCanvasObjectGroup::SetAdmin(wxCanvasAdmin* admin) +{ + m_admin=admin; + wxNode *node = m_objects.First(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + obj->SetAdmin(admin); + + node = node->Next(); + } +} + +void wxCanvasObjectGroup::DeleteContents( bool flag) { + m_objects.DeleteContents( flag ); + m_bbox.SetValid(FALSE); + CalcBoundingBox(); } -void wxCanvas::OnChar(wxKeyEvent &event) +void wxCanvasObjectGroup::Prepend( wxCanvasObject* obj ) { - event.Skip(); + m_objects.Insert( obj ); + if (m_objects.First()) + { + m_bbox.Expand(obj->GetBbox()); + } + else + { + m_bbox.SetValid(FALSE); + CalcBoundingBox(); + } } +void wxCanvasObjectGroup::Append( wxCanvasObject* obj ) +{ + m_objects.Append( obj ); + if (m_objects.First()) + { + m_bbox.Expand(obj->GetBbox()); + } + else + { + m_bbox.SetValid(FALSE); + CalcBoundingBox(); + } +} + +void wxCanvasObjectGroup::Insert( size_t before, wxCanvasObject* obj ) +{ + m_objects.Insert( before, obj ); + m_bbox.SetValid(FALSE); + if (m_objects.First()) + { + m_bbox.Expand(obj->GetBbox()); + } + else + { + m_bbox.SetValid(FALSE); + CalcBoundingBox(); + } +} + +void wxCanvasObjectGroup::Remove( wxCanvasObject* obj ) +{ + m_objects.DeleteObject( obj ); + m_bbox.SetValid(FALSE); + CalcBoundingBox(); +} + +void wxCanvasObjectGroup::CalcBoundingBox() +{ + m_bbox.SetValid(FALSE); + wxNode *node = m_objects.First(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + + obj->CalcBoundingBox(); + wxBoundingBox tmp; + tmp=obj->GetBbox(); + tmp.MapBbox(lworld); + + m_bbox.Expand( tmp ); + node = node->Next(); + } +} + +void wxCanvasObjectGroup::Render(wxTransformMatrix* cworld, int x, int y, int width, int height ) +{ + if (!m_visible) return; + + wxTransformMatrix backup = *cworld; + *cworld *= lworld; + + wxNode *node = m_objects.First(); + + if (!node) return; + + +#ifdef CANVASDEBUG + wxRect absarea=GetAbsoluteArea(*cworld); + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetPen(*wxBLACK_PEN); + dc->SetBrush(*wxTRANSPARENT_BRUSH); + dc->DrawRectangle( absarea.x , absarea.y , absarea.width , absarea.height ); + dc->SetBrush(wxNullBrush); + dc->SetPen(wxNullPen); +#endif + //TODO the next will only work if all boundingboxes stay up to date (problem when mowing currently + /* + if (! ( wxMax(absarea.x, x) < wxMin(absarea.x + absarea.width , x + width ) && + wxMax(absarea.y, y) < wxMin(absarea.y + absarea.height , y + height ) + ) + ) + return; + */ + + // cycle through all objects + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + if (!obj->IsControl() && obj->GetVisible()) + { + + //get area at the absolute position + wxRect absareaobject = obj->GetAbsoluteArea(*cworld); + + // 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 = absareaobject.x; + int clip_width = absareaobject.width; + 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 = absareaobject.y; + int clip_height = absareaobject.height; + 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(cworld, clip_x, clip_y, clip_width, clip_height ); + } + } + } + } + } + + node = node->Next(); + } + *cworld = backup; +} + +void wxCanvasObjectGroup::WriteSVG( wxTextOutputStream &stream ) +{ +} + +wxCanvasObject* wxCanvasObjectGroup::IsHitWorld( double x, double y, double margin ) +{ + //KKKfirst check if within bbox + //will only work if they are always uptodate + //if (!m_bbox.PointInBox(x,y,margin)) + // return (wxCanvasObject*) NULL; + + wxTransformMatrix inverse = lworld; + double xh,yh; + inverse.Invert(); + inverse.TransformPoint(x,y,xh,yh); + + wxCanvasObject *obj=0; + wxNode *node = m_objects.Last(); + while (node) + { + obj=(wxCanvasObject*) node->Data(); + + if (!obj->IsControl() ) + { + if (obj->IsHitWorld(xh,yh,margin)) + { + return obj; + } + } + node = node->Previous(); + } + + return (wxCanvasObject*) NULL; +} + +wxCanvasObject* wxCanvasObjectGroup::Contains( wxCanvasObject* obj ) +{ + wxCanvasObject* cobj; + wxNode *node = m_objects.First(); + while (node) + { + cobj=(wxCanvasObject*) node->Data(); + + if (cobj->Contains(obj)) + { + return obj; + } + node = node->Next(); + } + + return (wxCanvasObject*) NULL; +} + +int wxCanvasObjectGroup::IndexOf( wxCanvasObject* obj ) +{ + return m_objects.IndexOf( obj ); +} + +//---------------------------------------------------------------------------- +// wxCanvasObjectRef +//---------------------------------------------------------------------------- + +wxCanvasObjectRef::wxCanvasObjectRef(double x, double y, wxCanvasObject* obj) + : wxCanvasObject() +{ + lworld.Translate(x,y); + m_obj = obj; + + m_bbox.SetValid(FALSE); + wxBoundingBox tmp; + tmp=obj->GetBbox(); + tmp.MapBbox(lworld); + m_bbox.Expand( tmp ); +} + +void wxCanvasObjectRef::PushEventHandler(wxEvtHandler *handler) +{ + wxCanvasObject::PushEventHandler(handler); + m_obj->PushEventHandler(handler); +} + +wxEvtHandler *wxCanvasObjectRef::PopEventHandler(bool deleteHandler) +{ + m_obj->PopEventHandler(deleteHandler); + return wxCanvasObject::PopEventHandler(deleteHandler); +} + +void wxCanvasObjectRef::AppendEventHandler(wxEvtHandler *handler) +{ + wxCanvasObject::AppendEventHandler(handler); + m_obj->AppendEventHandler(handler); +} + +wxEvtHandler *wxCanvasObjectRef::RemoveLastEventHandler(bool deleteHandler) +{ + m_obj->RemoveLastEventHandler(deleteHandler); + return wxCanvasObject::RemoveLastEventHandler(deleteHandler); +} + +void wxCanvasObjectRef::TransLate( double x, double y ) +{ + lworld.Translate(x,y); + CalcBoundingBox(); +} + +wxCanvasObject* wxCanvasObjectRef::Contains( wxCanvasObject* obj ) +{ + if (obj == this || m_obj->Contains(obj)) + return this; + + return (wxCanvasObject*) NULL; +} + + +void wxCanvasObjectRef::SetRotation(double rotation) +{ + lworld.SetRotation(rotation); + CalcBoundingBox(); +} + +void wxCanvasObjectRef::SetScale(double scalex,double scaley) +{ + lworld.Scale(scalex,scaley,lworld.GetValue(2,0),lworld.GetValue(2,1)); + CalcBoundingBox(); +} + +void wxCanvasObjectRef::SetAdmin(wxCanvasAdmin* admin) +{ + m_admin = admin; + m_obj->SetAdmin(admin); +} + +void wxCanvasObjectRef::CalcBoundingBox() +{ + m_bbox.SetValid(FALSE); + m_obj->CalcBoundingBox(); + + wxBoundingBox tmp; + tmp=m_obj->GetBbox(); + tmp.MapBbox(lworld); + m_bbox.Expand( tmp ); +} + +void wxCanvasObjectRef::Render(wxTransformMatrix* cworld, int x, int y, int width, int height ) +{ + if (!m_visible) return; + + //get the absolute area (without the local matrix included) + //the boundingbox is relative to the parent. + wxRect absarea=GetAbsoluteArea(*cworld); + + wxTransformMatrix backup = *cworld; + *cworld *= lworld; + +#ifdef CANVASDEBUG + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetPen(*wxBLACK_PEN); + dc->SetBrush(*wxTRANSPARENT_BRUSH); + dc->DrawRectangle( absarea.x , absarea.y , absarea.width , absarea.height ); + dc->SetBrush(wxNullBrush); + dc->SetPen(wxNullPen); +#endif + + int clip_x = absarea.x; + int clip_width = absarea.width; + 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 = absarea.y; + int clip_height = absarea.height; + 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_obj->Render(cworld, clip_x, clip_y, clip_width, clip_height ); + } + } + } + + *cworld = backup; +} + +void wxCanvasObjectRef::WriteSVG( wxTextOutputStream &stream ) +{ +} + +wxCanvasObject* wxCanvasObjectRef::IsHitWorld( double x, double y, double margin ) +{ + //KKKfirst check if within bbox + //will only work if they are always uptodate + //if (!m_bbox.PointInBox(x,y,margin)) + // return (wxCanvasObject*) NULL; + + wxTransformMatrix inverse = lworld; + double xh,yh; + inverse.Invert(); + inverse.TransformPoint(x,y,xh,yh); + + if (m_obj->IsHitWorld(xh,yh,margin)) + return this; + + return (wxCanvasObject*) NULL; +} + + + +//---------------------------------------------------------------------------- +// wxCanvasRect +//---------------------------------------------------------------------------- + +wxCanvasRect::wxCanvasRect( double x, double y, double w, double h , double radius ) + : wxCanvasObject() +{ + m_x = x; + m_y = y; + m_width = w; + m_height = h; + m_radius = radius; + + m_brush = *wxBLACK_BRUSH; + m_pen = *wxTRANSPARENT_PEN; + CalcBoundingBox(); +} + +void wxCanvasRect::TransLate( double x, double y ) +{ + m_x += x; + m_y += y; + CalcBoundingBox(); +} + +void wxCanvasRect::CalcBoundingBox() +{ + m_bbox.SetMin( m_x , m_y); + m_bbox.SetMax( m_x + m_width ,m_y + m_height ); + + //include the pen width also +//KKK m_bbox.EnLarge(m_pen.GetWidth()+m_radius); + m_bbox.EnLarge(m_pen.GetWidth()/2); +} + +void wxCanvasRect::Render(wxTransformMatrix* cworld, int clip_x, int clip_y, int clip_width, int clip_height ) +{ + if (!m_visible) return; + + if (cworld->GetRotation()) + { + wxPoint *cpoints = new wxPoint[4]; + double x; + double y; + cworld->TransformPoint( m_x, m_y, x, y ); + cpoints[0].x = m_admin->LogicalToDeviceX(x); + cpoints[0].y = m_admin->LogicalToDeviceY(y); + cworld->TransformPoint( m_x , m_y + m_height, x, y ); + cpoints[1].x = m_admin->LogicalToDeviceX(x); + cpoints[1].y = m_admin->LogicalToDeviceY(y); + cworld->TransformPoint( m_x + m_width, m_y + m_height, x, y ); + cpoints[2].x = m_admin->LogicalToDeviceX(x); + cpoints[2].y = m_admin->LogicalToDeviceY(y); + cworld->TransformPoint( m_x + m_width, m_y , x, y ); + cpoints[3].x = m_admin->LogicalToDeviceX(x); + cpoints[3].y = m_admin->LogicalToDeviceY(y); + + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetClippingRegion(clip_x, clip_y, clip_width, clip_height); + dc->SetBrush(m_brush); + int pw=m_pen.GetWidth(); + m_pen.SetWidth(m_admin->LogicalToDeviceXRel(pw)); + dc->SetPen(m_pen); + dc->DrawPolygon(4, cpoints, 0,0,wxWINDING_RULE); + delete [] cpoints; + dc->SetBrush(wxNullBrush); + dc->SetPen(wxNullPen); + dc->DestroyClippingRegion(); + m_pen.SetWidth(pw); + } + else + { + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetClippingRegion(clip_x, clip_y, clip_width, clip_height); + dc->SetBrush(m_brush); + int pw=m_pen.GetWidth(); + m_pen.SetWidth(m_admin->LogicalToDeviceXRel(pw)); + dc->SetPen(m_pen); + //yes the whole not only the clipping region, because we have a pen also + int x = m_admin->LogicalToDeviceX(cworld->GetValue(2,0) + m_x ); + int y = m_admin->LogicalToDeviceY(cworld->GetValue(2,1) + m_y ); + int w = m_admin->LogicalToDeviceXRel( m_width ); + int h = m_admin->LogicalToDeviceYRel( m_height ); + int r = m_admin->LogicalToDeviceYRel( m_radius ); + if (w > 0 && w < 1) w=1; + if (w < 0 && w > -1) w=-1; + if (h > 0 && h < 1) h=1; + if (h < 0 && h > -1) h=-1; + if (m_radius) + dc->DrawRoundedRectangle( x,y,w,h,r); + else + dc->DrawRectangle( x,y,w,h); + dc->SetBrush(wxNullBrush); + dc->SetPen(wxNullPen); + dc->DestroyClippingRegion(); + m_pen.SetWidth(pw); + } +} + +void wxCanvasRect::WriteSVG( wxTextOutputStream &stream ) +{ +} + +//---------------------------------------------------------------------------- +// wxCanvasCircle +//---------------------------------------------------------------------------- + +wxCanvasCircle::wxCanvasCircle( double x, double y, double radius ) + : wxCanvasObject() +{ + m_x = x; + m_y = y; + m_radius = radius; + + m_brush = *wxBLACK_BRUSH; + m_pen = *wxTRANSPARENT_PEN; + CalcBoundingBox(); +} + +void wxCanvasCircle::TransLate( double x, double y ) +{ + m_x += x; + m_y += y; + CalcBoundingBox(); +} + +void wxCanvasCircle::CalcBoundingBox() +{ + m_bbox.SetMin( m_x-m_radius , m_y-m_radius ); + m_bbox.SetMax( m_x+m_radius , m_y+m_radius ); + + //include the pen width also + m_bbox.EnLarge(m_pen.GetWidth()/2); +} + +void wxCanvasCircle::Render(wxTransformMatrix* cworld, int clip_x, int clip_y, int clip_width, int clip_height ) +{ + if (!m_visible) return; + + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetClippingRegion(clip_x, clip_y, clip_width, clip_height); + dc->SetBrush(m_brush); + int pw=m_pen.GetWidth(); + m_pen.SetWidth(m_admin->LogicalToDeviceXRel(pw)); + dc->SetPen(m_pen); + //yes the whole not only the clipping region, because we have a pen also + //and rotation on a circle is not important so only a shift with cworld + int x = m_admin->LogicalToDeviceX(cworld->GetValue(2,0) + m_x ); + int y = m_admin->LogicalToDeviceY(cworld->GetValue(2,1) + m_y ); + int radius = m_admin->LogicalToDeviceXRel( m_radius ); + if (radius < 1) radius=1; + dc->DrawCircle( x,y,radius); + dc->SetBrush(wxNullBrush); + dc->SetPen(wxNullPen); + dc->DestroyClippingRegion(); + m_pen.SetWidth(pw); +} + +void wxCanvasCircle::WriteSVG( wxTextOutputStream &stream ) +{ +} + +wxCanvasObject* wxCanvasCircle::IsHitWorld( double x, double y, double margin ) +{ + if ((x >= m_bbox.GetMinX()-margin) && + (x <= m_bbox.GetMaxX()+margin) && + (y >= m_bbox.GetMinY()-margin) && + (y <= m_bbox.GetMaxY()+margin) + ) + { + if (m_radius+m_pen.GetWidth()/2+margin > sqrt(pow(m_x-x,2)+pow(m_y-y,2))) + return this; + else + return (wxCanvasObject*) NULL; + } + return (wxCanvasObject*) NULL; +} + +//---------------------------------------------------------------------------- +// wxCanvasEllipse +//---------------------------------------------------------------------------- + +wxCanvasEllipse::wxCanvasEllipse( double x, double y, double width, double height ) + : wxCanvasObject() +{ + m_x = x; + m_y = y; + m_width = width; + m_height = height; + + m_brush = *wxBLACK_BRUSH; + m_pen = *wxTRANSPARENT_PEN; + CalcBoundingBox(); +} + +void wxCanvasEllipse::TransLate( double x, double y ) +{ + m_x += x; + m_y += y; + CalcBoundingBox(); +} + +void wxCanvasEllipse::CalcBoundingBox() +{ + m_bbox.SetMin( m_x, m_y ); + m_bbox.SetMax( m_x+m_width , m_y+m_height ); + + //include the pen width also + m_bbox.EnLarge(m_pen.GetWidth()/2); +} + +void wxCanvasEllipse::Render(wxTransformMatrix* cworld, int clip_x, int clip_y, int clip_width, int clip_height ) +{ + if (!m_visible) return; + + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetClippingRegion(clip_x, clip_y, clip_width, clip_height); + dc->SetBrush(m_brush); + int pw=m_pen.GetWidth(); + m_pen.SetWidth(m_admin->LogicalToDeviceXRel(pw)); + dc->SetPen(m_pen); + int x = m_admin->LogicalToDeviceX(cworld->GetValue(2,0) + m_x ); + int y = m_admin->LogicalToDeviceY(cworld->GetValue(2,1) + m_y ); + int w = m_admin->LogicalToDeviceXRel( m_width ); + int h = m_admin->LogicalToDeviceYRel( m_height ); + if (w > 0 && w < 1) w=1; + if (w < 0 && w > -1) w=-1; + if (h > 0 && h < 1) h=1; + if (h < 0 && h > -1) h=-1; + dc->DrawEllipse( x,y,w,h); + dc->SetBrush(wxNullBrush); + dc->SetPen(wxNullPen); + dc->DestroyClippingRegion(); + m_pen.SetWidth(pw); +} + +void wxCanvasEllipse::WriteSVG( wxTextOutputStream &stream ) +{ +} + +wxCanvasObject* wxCanvasEllipse::IsHitWorld( double x, double y, double margin ) +{ + if ((x >= m_bbox.GetMinX()-margin) && + (x <= m_bbox.GetMaxX()+margin) && + (y >= m_bbox.GetMinY()-margin) && + (y <= m_bbox.GetMaxY()+margin) + ) + { + double a=(m_width+m_pen.GetWidth())/2+margin ; + double b=(m_height+m_pen.GetWidth())/2+margin; + double c=pow((m_x+m_width/2-x)/a,2)+pow((m_y+m_height/2-y)/b,2); + if ( 1 > c) + return this; + else + return (wxCanvasObject*) NULL; + } + return (wxCanvasObject*) NULL; +} + +//---------------------------------------------------------------------------- +// wxCanvasEllipticArc +//---------------------------------------------------------------------------- + +wxCanvasEllipticArc::wxCanvasEllipticArc( double x, double y, double width, double height, double start, double end ) + : wxCanvasObject() +{ + m_x = x; + m_y = y; + m_width = width; + m_height = height; + m_start = start; + m_end = end; + + m_brush = *wxBLACK_BRUSH; + m_pen = *wxTRANSPARENT_PEN; + CalcBoundingBox(); +} + +void wxCanvasEllipticArc::TransLate( double x, double y ) +{ + m_x += x; + m_y += y; + CalcBoundingBox(); +} + +void wxCanvasEllipticArc::CalcBoundingBox() +{ + m_bbox.SetMin( m_x, m_y ); + m_bbox.SetMax( m_x+m_width , m_y+m_height ); + + //include the pen width also + m_bbox.EnLarge(m_pen.GetWidth()/2); +} + +void wxCanvasEllipticArc::Render(wxTransformMatrix* cworld, int clip_x, int clip_y, int clip_width, int clip_height ) +{ + if (!m_visible) return; + + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetClippingRegion(clip_x, clip_y, clip_width, clip_height); + dc->SetBrush(m_brush); + int pw=m_pen.GetWidth(); + m_pen.SetWidth(m_admin->LogicalToDeviceXRel(pw)); + dc->SetPen(m_pen); + int x = m_admin->LogicalToDeviceX(cworld->GetValue(2,0) + m_x ); + int y = m_admin->LogicalToDeviceY(cworld->GetValue(2,1) + m_y ); + int w = m_admin->LogicalToDeviceXRel( m_width ); + int h = m_admin->LogicalToDeviceYRel( m_height ); + if (w > 0 && w < 1) w=1; + if (w < 0 && w > -1) w=-1; + if (h > 0 && h < 1) h=1; + if (h < 0 && h > -1) h=-1; + if (m_admin->GetActive()->GetYaxis()) + dc->DrawEllipticArc( x,y,w,h,-m_end,-m_start); + else + dc->DrawEllipticArc( x,y,w,h,m_start,m_end); + dc->SetBrush(wxNullBrush); + dc->SetPen(wxNullPen); + dc->DestroyClippingRegion(); + m_pen.SetWidth(pw); +} + +void wxCanvasEllipticArc::WriteSVG( wxTextOutputStream &stream ) +{ +} + +wxCanvasObject* wxCanvasEllipticArc::IsHitWorld( double x, double y, double margin ) +{ + if ((x >= m_bbox.GetMinX()-margin) && + (x <= m_bbox.GetMaxX()+margin) && + (y >= m_bbox.GetMinY()-margin) && + (y <= m_bbox.GetMaxY()+margin) + ) + { + double a=(m_width+m_pen.GetWidth())/2+margin ; + double b=(m_height+m_pen.GetWidth())/2+margin; + double c=pow((m_x+m_width/2-x)/a,2)+pow((m_y+m_height/2-y)/b,2); + if ( 1 > c) + return this; + else + return (wxCanvasObject*) NULL; + } + return (wxCanvasObject*) NULL; +} + +//---------------------------------------------------------------------------- +// wxCanvasLine +//---------------------------------------------------------------------------- + +wxCanvasLine::wxCanvasLine( double x1, double y1, double x2, double y2 ) + : wxCanvasObject() +{ + m_x1 = x1; + m_y1 = y1; + m_x2 = x2; + m_y2 = y2; + + m_pen = *wxBLACK_PEN; + CalcBoundingBox(); +} + +void wxCanvasLine::TransLate( double x, double y ) +{ + m_x1 += x; + m_y1 += y; + m_x2 += x; + m_y2 += y; + CalcBoundingBox(); +} + +void wxCanvasLine::CalcBoundingBox() +{ + m_bbox.SetMin( m_x1 , m_y1); + m_bbox.SetMax( m_x2 , m_y2); + + //include the pen width also + m_bbox.EnLarge(m_pen.GetWidth()/2); +} + +void wxCanvasLine::Render(wxTransformMatrix* cworld, int clip_x, int clip_y, int clip_width, int clip_height ) +{ + if (!m_visible) return; + + double x1,y1,x2,y2; + cworld->TransformPoint( m_x1, m_y1, x1, y1 ); + cworld->TransformPoint( m_x2, m_y2, x2, y2 ); + x1 = m_admin->LogicalToDeviceX( x1 ); + y1 = m_admin->LogicalToDeviceY( y1 ); + x2 = m_admin->LogicalToDeviceX( x2 ); + y2 = m_admin->LogicalToDeviceY( y2 ); + + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetClippingRegion( clip_x, clip_y, clip_width, clip_height ); + int pw=m_pen.GetWidth(); + m_pen.SetWidth(m_admin->LogicalToDeviceXRel(pw)); + dc->SetPen( m_pen ); + dc->DrawLine( x1, y1, x2, y2 ); + + dc->DestroyClippingRegion(); + m_pen.SetWidth(pw); +} + +void wxCanvasLine::WriteSVG( wxTextOutputStream &stream ) +{ + // no idea +} + +wxCanvasObject* wxCanvasLine::IsHitWorld( double x, double y, double margin ) +{ + if ((x >= m_bbox.GetMinX()-margin) && + (x <= m_bbox.GetMaxX()+margin) && + (y >= m_bbox.GetMinY()-margin) && + (y <= m_bbox.GetMaxY()+margin) + ) + { + wxLine line1(m_x1,m_y1,m_x2,m_y2); + wxPoint2DDouble P=wxPoint2DDouble(x,y); + double distance; + if (line1.PointInLine(P,distance,m_pen.GetWidth()/2+margin) == R_IN_AREA) + return this; + else + return (wxCanvasObject*) NULL; + } + return (wxCanvasObject*) NULL; +} + +//---------------------------------------------------------------------------- +// wxCanvasImage +//---------------------------------------------------------------------------- + +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_orgw = m_image.GetWidth(); + m_orgh = m_image.GetHeight(); + + m_isImage = TRUE; + CalcBoundingBox(); +} + +void wxCanvasImage::SetPosXY( double x, double y) +{ + m_x = x; + m_y = y; + CalcBoundingBox(); +} + +void wxCanvasImage::TransLate( double x, double y ) +{ + m_x += x; + m_y += y; + CalcBoundingBox(); +} + +void wxCanvasImage::CalcBoundingBox() +{ + m_bbox.SetMin( m_x, m_y ); + m_bbox.SetMax( m_x + m_width, m_y + m_height ); +} + +void wxCanvasImage::Render(wxTransformMatrix* cworld, int clip_x, int clip_y, int clip_width, int clip_height ) +{ + if (!m_visible) return; + + wxRect tmparea; + + tmparea.x = m_admin->LogicalToDeviceXRel( m_bbox.GetMinX()); + tmparea.y = m_admin->LogicalToDeviceYRel( m_bbox.GetMinY()); + tmparea.width = m_admin->LogicalToDeviceXRel( m_bbox.GetWidth() ); + tmparea.height = m_admin->LogicalToDeviceYRel( m_bbox.GetHeight() ); + + double x; + double y; + cworld->TransformPoint( m_x, m_y, x, y ); + x = m_admin->LogicalToDeviceX(x); + y = m_admin->LogicalToDeviceY(y); + + + // What is this??? + if ( m_orgw*5 < m_admin->LogicalToDeviceXRel( m_bbox.GetWidth() ) || + m_orgw/5 > m_admin->LogicalToDeviceXRel( m_bbox.GetWidth() ) || + m_orgh*5 < m_admin->LogicalToDeviceYRel( m_bbox.GetHeight() ) || + m_orgh/5 > m_admin->LogicalToDeviceYRel( m_bbox.GetHeight() ) + ) + { + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->SetClippingRegion(clip_x, clip_y, clip_width, clip_height); + dc->SetBrush(*wxTRANSPARENT_BRUSH); + dc->SetPen(*wxBLACK_PEN); + //yes the whole not only the clipping region, because we have a pen also + int x = m_admin->LogicalToDeviceX(cworld->GetValue(2,0) + m_x ); + int y = m_admin->LogicalToDeviceY(cworld->GetValue(2,1) + m_y ); + int w = m_admin->LogicalToDeviceXRel( m_width ); + int h = m_admin->LogicalToDeviceYRel( m_height ); + if (w < 1) w=1; + if (h < 1) h=1; + dc->DrawRectangle( x,y,w,h); + dc->SetBrush(wxNullBrush); + dc->SetPen(wxNullPen); + dc->DestroyClippingRegion(); + return; + } + + wxImage tmp; + bool is_cashed = FALSE; + + if (m_cImage.Ok() && (m_cW == m_bbox.GetWidth()) && (m_cH == m_bbox.GetHeight())) + { + // use cached image + tmp = m_cImage; + is_cashed = TRUE; + } + else + { + if ((m_admin->LogicalToDeviceXRel( m_bbox.GetWidth() ) == m_image.GetWidth()) && + (m_admin->LogicalToDeviceYRel( m_bbox.GetHeight() ) == m_image.GetHeight())) + { + tmp = m_image; + } + else + { + tmp = m_image.Scale( m_admin->LogicalToDeviceXRel( m_bbox.GetWidth()), + m_admin->LogicalToDeviceYRel( m_bbox.GetHeight()) ); + } + + // create cached image + m_cImage = tmp; + m_cW = tmp.GetWidth(); + m_cH = tmp.GetHeight(); + } + +// wxPoint centr(m_admin->LogicalToDeviceX(m_x),m_admin->LogicalToDeviceY(m_y)); + wxPoint centr(0,0); + + wxBitmap bmp; + + if (m_cBitmap.Ok() && is_cashed && (m_cR == cworld->GetRotation())) + { + bmp = m_cBitmap; + } + else + { + if (cworld->GetRotation()) + tmp = tmp.Rotate(-cworld->GetRotation()/180.0 * pi, centr, TRUE, NULL ); + + bmp = tmp.ConvertToBitmap(); + + // create cached bitmap + m_cBitmap = bmp; + m_cR = cworld->GetRotation(); + } + + wxDC *dc = m_admin->GetActive()->GetDC(); + + wxPoint centr2; + if (cworld->GetRotation()> 0) + { + centr2.x= (int) (x+m_height*sin(-cworld->GetRotation()/180.0 * pi)); + centr2.y= (int) y; + } + else + { + centr2.x= (int) x; + centr2.y= (int) (y-m_width*sin(-cworld->GetRotation()/180.0 * pi)); + } + + if (cworld->GetRotation() != 0) + { + //TODO clipping not right + dc->DrawBitmap(bmp,centr2,TRUE ); +// dc->DrawPoint(centr2); +// dc->DrawPoint(x,y); + } + else + { + dc->SetClippingRegion( clip_x, clip_y, clip_width, clip_height ); + dc->DrawBitmap( bmp, x, y, TRUE ); + dc->DestroyClippingRegion(); + } +} + +void wxCanvasImage::WriteSVG( wxTextOutputStream &stream ) +{ + // no idea +} + +//---------------------------------------------------------------------------- +// wxCanvasCtrl +//---------------------------------------------------------------------------- + +wxCanvasControl::wxCanvasControl( wxWindow *control ) + : wxCanvasObject() +{ + m_isControl = TRUE; + m_control = control; + CalcBoundingBox(); +} + +double wxCanvasControl::GetPosX() +{ + int x,y ; + m_control->GetPosition( &x, &y ); + return m_admin->DeviceToLogicalX(x); +} + +double wxCanvasControl::GetPosY() +{ + int x,y ; + m_control->GetPosition( &x, &y ); + return m_admin->DeviceToLogicalY(y); +} + +void wxCanvasControl::SetPosXY( double x, double y) +{ + int xd = m_admin->LogicalToDeviceX(x); + int yd = m_admin->LogicalToDeviceY(y); + m_control->Move(xd,yd); +} + + +void wxCanvasControl::TransLate( double x, double y ) +{ + int xdo,ydo; + m_control->GetPosition( &xdo, &ydo ); + int xd = m_admin->LogicalToDeviceX(x)-xdo; + int yd = m_admin->LogicalToDeviceY(y)-ydo; + m_control->Move(xd,yd); + CalcBoundingBox(); +} + +wxCanvasControl::~wxCanvasControl() +{ + m_control->Destroy(); +} + +void wxCanvasControl::CalcBoundingBox() +{ + wxRect tmparea; + + m_control->GetSize( &tmparea.width, &tmparea.height ); + m_control->GetPosition( &tmparea.x, &tmparea.y ); + + m_bbox.SetMin( tmparea.x , tmparea.y); + m_bbox.SetMax( tmparea.x + tmparea.width , tmparea.y + tmparea.height); + +} + +void wxCanvasControl::MoveRelative( double x, double y ) +{ + m_control->Move( m_admin->LogicalToDeviceX(x), m_admin->LogicalToDeviceX(y) ); +} + +//---------------------------------------------------------------------------- +// wxCanvasText +//---------------------------------------------------------------------------- + +class wxFaceData +{ +public: +#if wxUSE_FREETYPE + FT_Face m_face; +#else + void *m_dummy; +#endif +}; + +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; + + 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, + &(data->m_face) ); + + error = FT_Set_Char_Size( data->m_face, + 0, + m_size*64, + 96, // screen dpi + 96 ); +#endif + CalcBoundingBox(); +} + +wxCanvasText::~wxCanvasText() +{ +#if wxUSE_FREETYPE + wxFaceData *data = (wxFaceData*) m_faceData; + delete data; +#endif + + if (m_alpha) delete [] m_alpha; +} + +void wxCanvasText::SetRGB( unsigned char red, unsigned char green, unsigned char blue ) +{ + m_red = red; + m_green = green; + m_blue = blue; +} + +void wxCanvasText::SetFlag( int flag ) +{ + m_flag = flag; +} + +void wxCanvasText::Render(wxTransformMatrix* cworld, int clip_x, int clip_y, int clip_width, int clip_height ) +{ + if (!m_visible) return; + + wxRect tmparea; + tmparea.x = m_admin->LogicalToDeviceX( m_bbox.GetMinX()); + tmparea.y = m_admin->LogicalToDeviceY( m_bbox.GetMinY()); + tmparea.width = m_admin->LogicalToDeviceXRel( m_bbox.GetWidth() ); + tmparea.height = m_admin->LogicalToDeviceYRel( m_bbox.GetHeight() ); + + m_alpha = new unsigned char[tmparea.width*tmparea.height]; + memset( m_alpha, 0, tmparea.width*tmparea.height ); + + if (!m_alpha) return; + +#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[(unsigned int)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++) + for (int x = 0; x < bitmap->width; x++) + { + 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 * tmparea.width + xx ] = alpha; + } + + pen_x += slot->advance.x >> 6; + pen_y += slot->advance.y >> 6; + } +#endif + + wxBitmap *bitmap = m_admin->GetActive()->GetBuffer(); + wxRect sub_rect( clip_x, clip_y, clip_width, clip_height ); + wxBitmap sub_bitmap( bitmap->GetSubBitmap( sub_rect ) ); + + wxImage image( sub_bitmap ); + + // local coordinates + int start_x = clip_x - tmparea.x; + int end_x = clip_width + start_x; + int start_y = clip_y - tmparea.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*tmparea.width + x]; + if (alpha) + { + int image_x = x - start_x; + int image_y = y - start_y; + if (alpha == 255) + { + image.SetRGB( image_x, image_y, m_red, m_green, m_blue ); + continue; + } + 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 ); + int blue2 = image.GetBlue( image_x, image_y ); + red2 = (red2 * alpha) / 255; + green2 = (green2 * alpha) / 255; + blue2 = (blue2 * alpha) / 255; + + image.SetRGB( image_x, image_y, red1+red2, green1+green2, blue1+blue2 ); + } + } + + sub_bitmap = image.ConvertToBitmap(); + + wxDC *dc = m_admin->GetActive()->GetDC(); + dc->DrawBitmap( sub_bitmap, clip_x, clip_y ); +} + +void wxCanvasText::WriteSVG( wxTextOutputStream &stream ) +{ +} + +void wxCanvasText::TransLate( double x, double y ) +{ + m_x += x; + m_y += y; + CalcBoundingBox(); +} + +void wxCanvasText::CalcBoundingBox() +{ + if (m_alpha) delete [] m_alpha; + + m_bbox.SetMin( m_x , m_y); + m_bbox.SetMax( m_x + 100 , m_y + m_size + (m_size/2)); + + +} + +//---------------------------------------------------------------------------- +// wxCanvas +//---------------------------------------------------------------------------- + +IMPLEMENT_CLASS(wxCanvas,wxScrolledWindow) + +BEGIN_EVENT_TABLE(wxCanvas,wxScrolledWindow) + EVT_PAINT( wxCanvas::OnPaint ) + EVT_IDLE( wxCanvas::OnIdle ) + EVT_SIZE( wxCanvas::OnSize ) + 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( wxCanvasAdmin* admin, wxWindow *parent, wxWindowID id, + const wxPoint &position, const wxSize& size, long style ) : + wxScrolledWindow( parent, id, position, size, style ) +{ + // These are unused in wxVectorCanvas + m_bufferX = 0; + m_bufferY = 0; + + m_admin = admin; + m_admin->Append( this ); + + m_needUpdate = FALSE; + m_background = *wxWHITE; + m_lastMouse = (wxCanvasObject*)NULL; + m_captureMouse = (wxCanvasObject*)NULL; + m_frozen = FALSE; + m_oldDeviceX = 0; + m_oldDeviceY = 0; + m_root = (wxCanvasObjectGroup*)NULL; +} + +wxCanvas::~wxCanvas() +{ + wxNode *node = m_updateRects.First(); + while (node) + { + wxRect *rect = (wxRect*) node->Data(); + delete rect; + m_updateRects.DeleteNode( node ); + node = m_updateRects.First(); + } +} + +double wxCanvas::GetMinX() const +{ + return 0.0; +} + +double wxCanvas::GetMinY() const +{ + return 0.0; +} + +double wxCanvas::GetMaxX() const +{ + int width; + GetVirtualSize( &width, NULL ); + return width; +} + +double wxCanvas::GetMaxY() const +{ + int height; + GetVirtualSize( NULL, &height ); + return height; +} + +void wxCanvas::SetColour( const wxColour& background ) +{ + m_background = background; + SetBackgroundColour( m_background ); + + if (m_frozen) return; + + wxMemoryDC dc; + dc.SelectObject( m_buffer ); + dc.SetPen( *wxTRANSPARENT_PEN ); + wxBrush brush( m_background, wxSOLID ); + dc.SetBrush( brush ); + dc.DrawRectangle( 0, 0, m_buffer.GetWidth(), m_buffer.GetHeight() ); + dc.SelectObject( wxNullBitmap ); +} + +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 ) +{ + CalcScrolledPosition( 0, 0, &m_oldDeviceX, &m_oldDeviceY ); + + m_admin->SetActive(this); + + if (!m_root) return; + + if (m_frozen) return; + + // clip to buffer + if (x < m_bufferX) + { + width -= m_bufferX-x; + x = m_bufferX; + } + if (width <= 0) return; + + if (y < m_bufferY) + { + height -= m_bufferY-y; + y = m_bufferY; + } + if (height <= 0) return; + + if (x+width > m_bufferX+m_buffer.GetWidth()) + { + width = m_bufferX+m_buffer.GetWidth() - x; + } + if (width <= 0) return; + + if (y+height > m_bufferY+m_buffer.GetHeight()) + { + 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 + if (blit) + { + m_updateRects.Append( + (wxObject*) new wxRect( x,y,width,height ) ); + } + + wxTransformMatrix cworld; + + wxMemoryDC dc; + dc.SelectObject( m_buffer ); + + dc.SetPen( *wxTRANSPARENT_PEN ); + wxBrush brush( m_background, wxSOLID ); + dc.SetBrush( brush ); + dc.SetLogicalFunction(wxCOPY); + +#if 0 + if (width != m_buffer.GetWidth() && height != m_buffer.GetHeight()) + { + dc.SetClippingRegion(x,y,width,height); + dc.DrawRectangle(x-2,y-2,width+4,height+4); + dc.DestroyClippingRegion(); + } + else + { + dc.Clear(); + dc.DrawRectangle(0,0,m_buffer.GetWidth(),m_buffer.GetHeight()); + } +#else + // No idea, what the code up there does. + dc.DrawRectangle( x-m_bufferX, y-m_bufferY, width, height ); +#endif + + dc.SetBrush(wxNullBrush); + dc.SetPen(wxNullPen); + + dc.SetDeviceOrigin( m_oldDeviceX, m_oldDeviceY ); + m_renderDC = &dc; + + m_root->Render( &cworld, x, y, width, height ); + + m_renderDC = NULL; + dc.SelectObject( wxNullBitmap ); +} + +void wxCanvas::BlitBuffer( wxDC &dc ) +{ + wxNode *node = m_updateRects.First(); + while (node) + { + wxRect *rect = (wxRect*) node->Data(); + + wxMemoryDC mdc; + mdc.SelectObject( m_buffer ); + dc.Blit( rect->x, + rect->y, + rect->width, + rect->height, + &mdc, + rect->x - m_bufferX, + rect->y - m_bufferY ); + mdc.SelectObject( wxNullBitmap ); + + 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 ); +} + +void wxCanvas::OnSize(wxSizeEvent &event) +{ + int w,h; + GetClientSize( &w, &h ); + m_buffer = wxBitmap( 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(); +} + +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() ); + while (it) + { + int x = it.GetX(); + int y = it.GetY(); + + 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)) + { + x += m_bufferX; + y += m_bufferY; + 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 as well as + // the previous device origin values. + wxClientDC dc( this ); + dc.SetDeviceOrigin( m_oldDeviceX, m_oldDeviceY ); + BlitBuffer( dc ); + + // 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 ); + + // Update everything. + Update( m_bufferX, m_bufferY, m_buffer.GetWidth(), m_buffer.GetHeight(), FALSE ); + + // Scroll, actually. + wxWindow::ScrollWindow( dx, dy, rect ); +} + +void wxCanvas::OnMouse(wxMouseEvent &event) +{ + m_admin->SetActive(this); + if (!m_root) + { + event.Skip(); + return; + } + + int x = event.GetX(); + int y = event.GetY(); + + //to world coordinates to do hit test in world coordinates + double xw = DeviceToLogicalX( x ); + double yw = DeviceToLogicalY( y ); + + //make a select margin of 2 pixels, so also zero line thickness will be hit + double margin = DeviceToLogicalXRel( 2 ); + + 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; + child_event.m_y = y; + 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->ProcessCanvasObjectEvent( child_event ); + return; + } + else + { + wxCanvasObject *obj = m_root->IsHitWorld(xw,yw,margin); + + if (obj && !obj->IsControl()) + { + wxMouseEvent child_event( wxEVT_MOTION ); + child_event.SetEventObject( obj ); + child_event.m_x = x; + child_event.m_y = y; + 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; + child_event.m_y = y; + m_lastMouse->ProcessCanvasObjectEvent( child_event ); + + m_lastMouse = obj; + child_event.SetEventType( wxEVT_ENTER_WINDOW ); + child_event.SetEventObject( m_lastMouse ); + child_event.m_x = x; + child_event.m_y = y; + m_lastMouse->ProcessCanvasObjectEvent( child_event ); + + child_event.SetEventType( wxEVT_MOTION ); + child_event.SetEventObject( obj ); + } + + obj->ProcessCanvasObjectEvent( child_event ); + return; + } + } + if (m_lastMouse) + { + wxMouseEvent child_event( wxEVT_LEAVE_WINDOW ); + child_event.SetEventObject( m_lastMouse ); + child_event.m_x = x; + child_event.m_y = y; + 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->ProcessCanvasObjectEvent( child_event ); + + m_lastMouse = (wxCanvasObject*) NULL; + return; + } + } + else + { + if (m_captureMouse) //no matter what go to this one + { + wxMouseEvent child_event( event.GetEventType() ); + child_event.SetEventObject(m_captureMouse); + child_event.m_x = x; + child_event.m_y = y; + 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->ProcessCanvasObjectEvent( child_event ); + } + else + { + wxCanvasObject *obj = m_root->IsHitWorld(xw,yw,margin); + + if (obj && !obj->IsControl()) + { + wxMouseEvent child_event( event.GetEventType() ); + child_event.SetEventObject( obj ); + child_event.m_x = x; + child_event.m_y = y; + 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; + + obj->ProcessCanvasObjectEvent( child_event ); + return; + } + } + } + + event.Skip(); +} + +void wxCanvas::OnIdle(wxIdleEvent &event) +{ + m_admin->SetActive(this); + UpdateNow(); + event.Skip(); +} + +void wxCanvas::OnSetFocus(wxFocusEvent &event) +{ + m_admin->SetActive(this); +} + +void wxCanvas::OnKillFocus(wxFocusEvent &event) +{ +} + + +void wxCanvas::OnEraseBackground(wxEraseEvent &event) +{ +} + +// coordinates conversions +// ----------------------- +double wxCanvas::DeviceToLogicalX(int x) const +{ + return (double)(x); +} + +double wxCanvas::DeviceToLogicalY(int y) const +{ + return (double)(y); +} + +double wxCanvas::DeviceToLogicalXRel(int x) const +{ + return (double)x; +} + +double wxCanvas::DeviceToLogicalYRel(int y) const +{ + return (double)y; +} + +int wxCanvas::LogicalToDeviceX(double x) const +{ + return (int)(x + 0.5); +} + +int wxCanvas::LogicalToDeviceY(double y) const +{ + return (int)(y + 0.5); +} + +int wxCanvas::LogicalToDeviceXRel(double x) const +{ + return (int)(x + 0.5); +} + +int wxCanvas::LogicalToDeviceYRel(double y) const +{ + return (int)(y + 0.5); +} + +//---------------------------------------------------------------------------- +// wxVectorCanvas +//---------------------------------------------------------------------------- + +IMPLEMENT_CLASS(wxVectorCanvas,wxCanvas) + +BEGIN_EVENT_TABLE(wxVectorCanvas,wxCanvas) + EVT_SCROLLWIN( wxVectorCanvas::OnScroll ) + EVT_CHAR( wxVectorCanvas::OnChar ) + EVT_SIZE( wxVectorCanvas::OnSize ) +END_EVENT_TABLE() + +wxVectorCanvas::wxVectorCanvas( wxCanvasAdmin* admin, wxWindow *parent, wxWindowID id, + const wxPoint &position, const wxSize& size, long style ) : + wxCanvas( admin, parent, id, position, size, style ) +{ + m_scrolled = FALSE; + m_yaxis = FALSE; +} + +double wxVectorCanvas::GetMinX() const +{ + return m_virt_minX; +} + +double wxVectorCanvas::GetMinY() const +{ + return m_virt_minY; +} + +double wxVectorCanvas::GetMaxX() const +{ + return m_virt_maxX; +} + +double wxVectorCanvas::GetMaxY() const +{ + return m_virt_maxY; +} + +void wxVectorCanvas::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 as well as + // the previous device origin values. + wxClientDC dc( this ); + dc.SetDeviceOrigin( m_oldDeviceX, m_oldDeviceY ); + BlitBuffer( dc ); + + if (dy != 0) + { + double dyv=DeviceToLogicalYRel(dy); + m_virt_minY=m_virt_minY-dyv; + m_virt_maxY=m_virt_maxY-dyv; + } + if (dx != 0) + { + double dxv=DeviceToLogicalXRel(dx); + m_virt_minX=m_virt_minX-dxv; + m_virt_maxX=m_virt_maxX-dxv; + } + + m_admin->SetActive(this); + SetMappingScroll(m_virt_minX,m_virt_minY,m_virt_maxX,m_virt_maxY,FALSE); + + + if (dy != 0) + { + if (dy > 0 && dy < m_buffer.GetHeight()) + { + wxRect rect( 0, 0, m_buffer.GetWidth(), m_buffer.GetHeight()-dy); + wxBitmap sub_bitmap( m_buffer.GetSubBitmap( rect ) ); + wxMemoryDC dcm; + dcm.SelectObject( m_buffer ); + dcm.DrawBitmap( sub_bitmap, 0, dy, TRUE ); + dcm.SelectObject( wxNullBitmap ); + + Update( 0, 0, m_buffer.GetWidth(), dy, TRUE ); + } + else if (dy < 0 && dy > -m_buffer.GetHeight()) + { + wxRect rect( 0, -dy, m_buffer.GetWidth(), m_buffer.GetHeight()+dy); + wxBitmap sub_bitmap( m_buffer.GetSubBitmap( rect ) ); + wxMemoryDC dcm; + dcm.SelectObject( m_buffer ); + dcm.DrawBitmap( sub_bitmap, 0, 0, TRUE ); + dcm.SelectObject( wxNullBitmap ); + + Update( 0, m_buffer.GetHeight()+dy, m_buffer.GetWidth(), m_buffer.GetHeight(), TRUE ); + } + else + Update( 0, 0, m_buffer.GetWidth(), m_buffer.GetHeight(), TRUE ); + } + + if (dx != 0) + { + if (dx > 0 && dx < m_buffer.GetWidth()) + { + wxRect rect( 0, 0, m_buffer.GetWidth()-dx, m_buffer.GetHeight()); + wxBitmap sub_bitmap( m_buffer.GetSubBitmap( rect ) ); + wxMemoryDC dcm; + dcm.SelectObject( m_buffer ); + dcm.DrawBitmap( sub_bitmap, dx, 0, TRUE ); + dcm.SelectObject( wxNullBitmap ); + + Update( 0, 0, dx, m_buffer.GetHeight(), TRUE ); + } + else if (dx < 0 && dx > -m_buffer.GetWidth()) + { + wxRect rect( -dx, 0, m_buffer.GetWidth()+dx, m_buffer.GetHeight()); + wxBitmap sub_bitmap( m_buffer.GetSubBitmap( rect ) ); + wxMemoryDC dcm; + dcm.SelectObject( m_buffer ); + dcm.DrawBitmap( sub_bitmap, 0, 0, TRUE ); + dcm.SelectObject( wxNullBitmap ); + + Update( m_buffer.GetWidth()+dx, 0, m_buffer.GetWidth(), m_buffer.GetHeight(), TRUE ); + } + else + Update( 0,0, m_buffer.GetWidth(), m_buffer.GetHeight(), TRUE ); + } + + wxWindow::ScrollWindow( dx, dy, rect ); + + //must be done now because quick repeated scrolling will prevent wxPaint + //from doing it properly + UpdateNow(); +} + +void wxVectorCanvas::OnSize(wxSizeEvent &event) +{ + int w,h; + + GetClientSize( &w, &h ); + + wxMemoryDC dc; + m_buffer = wxBitmap( w, h ); + dc.SelectObject( m_buffer ); + dc.SetPen( *wxTRANSPARENT_PEN ); + wxBrush brush( m_background , wxSOLID ); + dc.SetBrush( brush ); + dc.DrawRectangle( 0, 0, m_buffer.GetWidth(), m_buffer.GetHeight() ); + dc.SelectObject( wxNullBitmap ); + + 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; + + m_admin->SetActive(this); + SetMappingScroll(m_virt_minX,m_virt_minY,m_virt_maxX,m_virt_maxY,FALSE); + + Update( 0,0, m_buffer.GetWidth(), m_buffer.GetHeight(), FALSE ); + +// event.Skip(); +} + +// maps the virtual window (Real drawing to the window coordinates +// also used for zooming +void wxVectorCanvas::SetMappingScroll( double vx1, double vy1, double vx2, double vy2, bool border) +{ + int dwxi,dwyi; + GetClientSize(&dwxi,&dwyi); + + if (vx2==vx1) vx2=vx1+100000; + if (vy2==vy1) vy2=vy1+100000; + m_virt_minX=vx1; + m_virt_minY=vy1; + m_virt_maxX=vx2; + m_virt_maxY=vy2; + + double dwx=dwxi; + double dwy=dwyi; + if (dwx==0) dwx=1; + if (dwy==0) dwy=1; + + double dvx = m_virt_maxX - m_virt_minX; + double dvy = m_virt_maxY - m_virt_minY; + + // calculate the scaling factor for the virtual window + double temp_x=0; + double temp_y=0; + if ((dvy / dvx) < (dwy / dwx)) + { + dvy = dvx * (dwy / dwx); + // calculate the change in the coordinates + temp_y = (dvy - (m_virt_maxY - m_virt_minY) )/ 2.0; + } + else + { + dvx = dvy * (dwx / dwy); + // calculate the change in the coordinates + temp_x = (dvx - (m_virt_maxX - m_virt_minX) )/ 2.0; + } + + // add or substract the change from the original coordinates + m_virt_minX=m_virt_minX-temp_x; + m_virt_minY=m_virt_minY-temp_y; + + m_virt_maxX=m_virt_maxX+temp_x; + m_virt_maxY=m_virt_maxY+temp_y; + + // initialize the mapping_matrix used for mapping the + // virtual windows to the drawing window + + // make mappingmatrix + m_mapping_matrix.Identity(); + if (!border) + { + // translate the drawing to 0,0 + if (m_yaxis) + m_mapping_matrix.Translate(-m_virt_minX,-m_virt_maxY); + else + m_mapping_matrix.Translate(-m_virt_minX,-m_virt_minY); + } + else + { + // make a small white border around the drawing + m_virt_minX=m_virt_minX- 0.05 * dvx; + m_virt_minY=m_virt_minY- 0.05 * dvy; + + m_virt_maxX=m_virt_maxX+ 0.05 * dvx; + m_virt_maxY=m_virt_maxY+ 0.05 * dvy; + + // translate the drawing to 0,0 + if (m_yaxis) + m_mapping_matrix.Translate(-m_virt_minX,-m_virt_maxY); + else + m_mapping_matrix.Translate(-m_virt_minX,-m_virt_minY); + } + + double scalefactor_x = dwx; + scalefactor_x /= (m_virt_maxX - m_virt_minX); + + double scalefactor_y = dwy; + scalefactor_y /= (m_virt_maxY - m_virt_minY); + + // scale the drawing so it fit's in the window + m_mapping_matrix.Scale(scalefactor_x, scalefactor_y, 0, 0); + + // because of coordinate change mirror over X + // 0,0 in graphic computerscreens: upperleft corner + // 0,0 in cartesian: lowerleft corner + if (m_yaxis) + { + m_mapping_matrix.Mirror(); + } + // make inverse of mapping matrix + // this is to set coordinates in the statusbar + // and the calculate screencoordinates to world coordinates used + // in zooming + m_inverse_mapping=m_mapping_matrix; + m_inverse_mapping.Invert(); + + if (m_scrolled) + SetScroll(m_virtm_minX,m_virtm_minY,m_virtm_maxX,m_virtm_maxY); + + int dx2,dy2; + GetClientSize(&dx2,&dy2); + if ( dwxi != dx2 || dwyi != dy2) //scrollbar is/became empty + SetScroll(m_virtm_minX,m_virtm_minY,m_virtm_maxX,m_virtm_maxY); +} + + +void wxVectorCanvas::SetScroll(double vx1,double vy1,double vx2,double vy2) +{ + m_virtm_minX=vx1; + m_virtm_minY=vy1; + m_virtm_maxX=vx2; + m_virtm_maxY=vy2; + + double dvx = m_virt_maxX - m_virt_minX; + double dvy = m_virt_maxY - m_virt_minY; + double dmvx = m_virtm_maxX - m_virtm_minX; + double dmvy = m_virtm_maxY - m_virtm_minY; + + SetScrollbar(wxHORIZONTAL,(m_virt_minX-m_virtm_minX)/dmvx *1000,dvx/dmvx *1000,1000,FALSE); + if (m_yaxis) + { + SetScrollbar(wxVERTICAL,(m_virtm_maxY-m_virt_maxY)/dmvy *1000,dvy/dmvy *1000,1000,FALSE); + } + else + { + SetScrollbar(wxVERTICAL,(m_virt_minY-m_virtm_minY)/dmvy *1000,dvy/dmvy *1000,1000,FALSE); + } + + m_scrolled=TRUE; +} + +// coordinates conversions +// ----------------------- +double wxVectorCanvas::DeviceToLogicalX(int x) const +{ + return m_inverse_mapping.GetValue(0,0) * x + m_inverse_mapping.GetValue(2,0); +} + +double wxVectorCanvas::DeviceToLogicalY(int y) const +{ + return m_inverse_mapping.GetValue(1,1) * y + m_inverse_mapping.GetValue(2,1); +} + +double wxVectorCanvas::DeviceToLogicalXRel(int x) const +{ + return x*m_inverse_mapping.GetValue(0,0); +} + +double wxVectorCanvas::DeviceToLogicalYRel(int y) const +{ + return y*m_inverse_mapping.GetValue(1,1); +} + +int wxVectorCanvas::LogicalToDeviceX(double x) const +{ + return (int) (m_mapping_matrix.GetValue(0,0) * x + m_mapping_matrix.GetValue(2,0) + 0.5); +} + +int wxVectorCanvas::LogicalToDeviceY(double y) const +{ + return (int) (m_mapping_matrix.GetValue(1,1) * y + m_mapping_matrix.GetValue(2,1) + 0.5); +} + +int wxVectorCanvas::LogicalToDeviceXRel(double x) const +{ + return (int) (x*m_mapping_matrix.GetValue(0,0) + 0.5); +} + +int wxVectorCanvas::LogicalToDeviceYRel(double y) const +{ + return (int) (y*m_mapping_matrix.GetValue(1,1) + 0.5); +} + + +// return the inverse mapping matrix for zooming or coordinates +wxTransformMatrix wxVectorCanvas::GetInverseMappingMatrix() +{ + return m_inverse_mapping; +} + +wxTransformMatrix wxVectorCanvas::GetMappingMatrix() +{ + return m_mapping_matrix; +} + + +// ---------------------------------------------------------------------------- +// scrolling behaviour +// ---------------------------------------------------------------------------- + +void wxVectorCanvas::OnScroll(wxScrollWinEvent& event) +{ + if (event.GetEventType()==wxEVT_SCROLLWIN_THUMBRELEASE) + { + if (event.GetOrientation()==wxHORIZONTAL) + { + double x=m_virtm_minX+event.GetPosition()/1000.0*(m_virtm_maxX-m_virtm_minX); + x=LogicalToDeviceXRel(x-m_virt_minX); + ScrollWindow(-x, 0, (const wxRect *) NULL); + } + else + { + double y=m_virtm_minY+event.GetPosition()/1000.0*(m_virtm_maxY-m_virtm_minY); + y=LogicalToDeviceYRel(y-m_virt_minY); + ScrollWindow(0, -y, (const wxRect *) NULL); + } + } + else if (event.GetEventType()==wxEVT_SCROLLWIN_PAGEUP) + { + if (event.GetOrientation()==wxHORIZONTAL) + { + double x=GetBufferWidth(); + ScrollWindow(x, 0, (const wxRect *) NULL); + } + else + { + double y=GetBufferHeight(); + ScrollWindow(0, y, (const wxRect *) NULL); + } + } + else if (event.GetEventType()==wxEVT_SCROLLWIN_PAGEDOWN) + { + if (event.GetOrientation()==wxHORIZONTAL) + { + double x=-GetBufferWidth(); + ScrollWindow(x, 0, (const wxRect *) NULL); + } + else + { + double y=-GetBufferHeight(); + ScrollWindow(0, y, (const wxRect *) NULL); + } + } + else if (event.GetEventType()==wxEVT_SCROLLWIN_LINEUP) + { + if (event.GetOrientation()==wxHORIZONTAL) + { + int x=GetBufferWidth()/10; + ScrollWindow(x, 0, (const wxRect *) NULL); + } + else + { + int y=GetBufferHeight()/10; + ScrollWindow(0, y, (const wxRect *) NULL); + } + } + else if (event.GetEventType()==wxEVT_SCROLLWIN_LINEDOWN) + { + if (event.GetOrientation()==wxHORIZONTAL) + { + int x=-GetBufferWidth()/10; + ScrollWindow(x, 0, (const wxRect *) NULL); + } + else + { + int y=-GetBufferHeight()/10; + ScrollWindow(0, y, (const wxRect *) NULL); + } + } + +} + +void wxVectorCanvas::OnChar(wxKeyEvent& event) +{ + switch ( event.KeyCode() ) + { + case WXK_PAGEUP: + case WXK_PRIOR: + { + double y=GetBufferHeight(); + ScrollWindow(0, y, (const wxRect *) NULL); + } + break; + case WXK_PAGEDOWN: + case WXK_NEXT: + { + double y=-GetBufferHeight(); + ScrollWindow(0, y, (const wxRect *) NULL); + } + break; + case WXK_HOME: + { + double y=m_virtm_minY; + y=LogicalToDeviceYRel(y-m_virt_minY); + ScrollWindow(0, -y, (const wxRect *) NULL); + } + break; + case WXK_END: + { + double y=m_virtm_minY+(m_virtm_maxY-m_virtm_minY); + y=LogicalToDeviceYRel(y-m_virt_minY); + ScrollWindow(0, -y, (const wxRect *) NULL); + } + break; + case WXK_UP: + { + int y; + if (!event.ControlDown()) + y=GetBufferHeight()/10; + else + y=GetBufferHeight(); + ScrollWindow(0, y, (const wxRect *) NULL); + } + break; + + case WXK_DOWN: + { + int y; + if (!event.ControlDown()) + y=-GetBufferHeight()/10; + else + y=-GetBufferHeight(); + ScrollWindow(0, y, (const wxRect *) NULL); + } + break; + + case WXK_LEFT: + { + int x; + if (!event.ControlDown()) + x=GetBufferWidth()/10; + else + x=GetBufferWidth(); + ScrollWindow(x, 0, (const wxRect *) NULL); + } + break; + case WXK_RIGHT: + { + int x; + if (!event.ControlDown()) + x=-GetBufferWidth()/10; + else + x=-GetBufferWidth(); + ScrollWindow(x, 0, (const wxRect *) NULL); + } + break; + default: + // not for us + event.Skip(); + } +} + + +//---------------------------------------------------------------------------- +// wxCanvasAdmin +//---------------------------------------------------------------------------- + +wxCanvasAdmin::wxCanvasAdmin() +{ + +} + +wxCanvasAdmin::~wxCanvasAdmin() +{ +} + + +void wxCanvasAdmin::Append( wxCanvas* canvas ) +{ + m_canvaslist.Append( canvas ); +} + +void wxCanvasAdmin::Remove( wxCanvas* canvas ) +{ + m_canvaslist.DeleteObject( canvas ); +} + +void wxCanvasAdmin::Update(wxCanvasObject* obj, double x, double y, double width, double height) +{ + wxNode *node = m_canvaslist.First(); + while (node) + { + + wxCanvas *canvas = (wxCanvas*) node->Data(); + + if (m_active == canvas) + { + int xi = canvas->LogicalToDeviceX( x); + int yi = canvas->LogicalToDeviceY( y); + int wi = canvas->LogicalToDeviceXRel( width ); + int hi = canvas->LogicalToDeviceYRel( height); + //update a little more then is strictly needed, + //to get rid of the 1 bit bugs + if (canvas->GetYaxis()) + canvas->Update( xi-2, yi+hi-2, wi+4, -hi+4); + else + canvas->Update( xi-2, yi-2, wi+4, hi+4); + } + else + { wxCanvasObject* topobj=canvas->GetRoot()->Contains(obj); + if (topobj) + { + wxCanvas* tcanvas = m_active; + SetActive(canvas); + + /* + //KKK TODO somehow the next does not work for update i do not know why + canvas->GetRoot()->CalcBoundingBox(); + int xi = topobj->GetX(); + int yi = topobj->GetY(); + int wi = topobj->GetWidth(); + int hi = topobj->GetHeight(); + */ + canvas->Update( 0,0, canvas->GetBufferWidth(),canvas->GetBufferHeight()); + SetActive(tcanvas); + } + } + + node = node->Next(); + } +} + +void wxCanvasAdmin::UpdateNow() +{ + wxNode *node = m_canvaslist.First(); + while (node) + { + wxCanvas *canvas = (wxCanvas*) node->Data(); + + canvas->UpdateNow(); + node = node->Next(); + } +} + +// coordinates conversions +// ----------------------- +double wxCanvasAdmin::DeviceToLogicalX(int x) const +{ + return m_active->DeviceToLogicalX(x); +} + +double wxCanvasAdmin::DeviceToLogicalY(int y) const +{ + return m_active->DeviceToLogicalY(y); +} + +double wxCanvasAdmin::DeviceToLogicalXRel(int x) const +{ + return m_active->DeviceToLogicalXRel(x); +} + +double wxCanvasAdmin::DeviceToLogicalYRel(int y) const +{ + return m_active->DeviceToLogicalYRel(y); +} + +int wxCanvasAdmin::LogicalToDeviceX(double x) const +{ + return m_active->LogicalToDeviceX(x); +} + +int wxCanvasAdmin::LogicalToDeviceY(double y) const +{ + return m_active->LogicalToDeviceY(y); +} + +int wxCanvasAdmin::LogicalToDeviceXRel(double x) const +{ + return m_active->LogicalToDeviceXRel(x); +} + +int wxCanvasAdmin::LogicalToDeviceYRel(double y) const +{ + return m_active->LogicalToDeviceYRel(y); +} + +void wxCanvasAdmin::SetActive(wxCanvas* activate) +{ + wxNode *node = m_canvaslist.First(); + while (node) + { + wxCanvas *canvas = (wxCanvas*) node->Data(); + + if (activate == canvas) + { + m_active=canvas; + break; + } + node = node->Next(); + } +} +//-------------------------------------------------------------------- +// wxCanvasModule +//-------------------------------------------------------------------- + +class wxCanvasModule : public wxModule +{ +public: + virtual bool OnInit(); + virtual void OnExit(); + +private: + DECLARE_DYNAMIC_CLASS(wxCanvasModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxCanvasModule, wxModule) + +bool wxCanvasModule::OnInit() +{ +#if wxUSE_FREETYPE + int error = FT_Init_FreeType( &g_freetypeLibrary ); + if (error) return FALSE; +#endif + + return TRUE; +} + +void wxCanvasModule::OnExit() +{ +#if wxUSE_FREETYPE + FT_Done_FreeType( g_freetypeLibrary ); +#endif +} + + +