/***************************************************************
 * Name:      DiagramManager.cpp
 * Purpose:   Implements shape manager class
 * Author:    Michal Bližňák (michal.bliznak@tiscali.cz)
 * Created:   2007-07-25
 * Copyright: Michal Bližňák
 * License:   wxWidgets license (www.wxwidgets.org)
 * Notes:
 **************************************************************/

#include "wx_pch.h"

#ifdef _DEBUG_MSVC
#define new DEBUG_NEW
#endif

#include <wx/wfstream.h>
#include <wx/mstream.h>
#include <wx/listimpl.cpp>

#include "wx/wxsf/DiagramManager.h"
#include "wx/wxsf/ShapeCanvas.h"
#include "wx/wxsf/ControlShape.h"
#include "wx/wxsf/LineShape.h"
#include "wx/wxsf/GridShape.h"

using namespace wxSFCommonFcn;

// TODO: implement autolayout
// TODO: implement better line X ellipse check

WX_DEFINE_LIST(IDList);

XS_IMPLEMENT_CLONABLE_CLASS(wxSFDiagramManager, wxXmlSerializer);

wxSFDiagramManager::wxSFDiagramManager()
{
    m_pShapeCanvas = NULL;
    m_lstIDPairs.DeleteContents(true);

    m_sSFVersion =  wxT("1.12.4 beta");

    SetSerializerOwner(wxT("wxShapeFramework"));
    SetSerializerVersion(wxT("1.0"));
    SetSerializerRootName(wxT("chart"));
}

wxSFDiagramManager::wxSFDiagramManager(const wxSFDiagramManager &obj)
: wxXmlSerializer(obj)
{
	m_pShapeCanvas = NULL;
	m_sSFVersion = obj.m_sSFVersion;

    m_lstIDPairs.DeleteContents(true);
}

wxSFDiagramManager::~wxSFDiagramManager()
{
    Clear();
}

//----------------------------------------------------------------------------------//
// Adding/removing shapes functions
//----------------------------------------------------------------------------------//

wxSFShapeBase* wxSFDiagramManager::AddShape(wxClassInfo* shapeInfo, bool saveState, wxSF::ERRCODE *err)
{
    wxPoint shapePos;

    if(m_pShapeCanvas)
    {
        wxRect crect = m_pShapeCanvas->GetClientRect();
        shapePos = wxPoint((crect.GetRight() - crect.GetLeft())/2,
                (crect.GetBottom() - crect.GetTop())/2);
    }

	wxSFShapeBase* pShape = AddShape(shapeInfo, shapePos, saveState, err);

	return pShape;
}

wxSFShapeBase* wxSFDiagramManager::AddShape(wxClassInfo* shapeInfo, const wxPoint& pos, bool saveState, wxSF::ERRCODE *err)
{
	wxASSERT( shapeInfo );
	
    if( shapeInfo && IsShapeAccepted(shapeInfo->GetClassName()) )
    {
        // create shape object from class info
        wxSFShapeBase *pShape = (wxSFShapeBase*)shapeInfo->CreateObject();

        wxSFShapeBase *pParentShape = NULL;
		// update given possition
		wxPoint lpos = pos;
		if( m_pShapeCanvas )
		{
			lpos = m_pShapeCanvas->FitPositionToGrid( m_pShapeCanvas->DP2LP(pos) );
		}
		// line shapes can be assigned to root only
		if( !pShape->IsKindOf(CLASSINFO(wxSFLineShape)) ) pParentShape = GetShapeAtPosition(lpos);
		
        if( pParentShape && pParentShape->IsChildAccepted(shapeInfo->GetClassName()) )
        {
            pShape = AddShape(pShape, (xsSerializable*)pParentShape, pos - Conv2Point( pParentShape->GetAbsolutePosition() ), sfINITIALIZE, saveState, err);
        }
        else
            pShape = AddShape(pShape, GetRootItem(), pos, sfINITIALIZE, saveState, err);


		if( pParentShape )pParentShape->Update();

        return pShape;
    }
    else
	{
		if( err ) *err = wxSF::errNOT_ACCEPTED;
        return NULL;
	}
}

wxSFShapeBase* wxSFDiagramManager::AddShape(wxSFShapeBase* shape, xsSerializable* parent, const wxPoint& pos, bool initialize, bool saveState, wxSF::ERRCODE *err)
{
	if(shape)
	{
		if( shape->IsKindOf(CLASSINFO(wxSFShapeBase)) && IsShapeAccepted(shape->GetClassInfo()->GetClassName()) )
		{
		    if( m_pShapeCanvas )
		    {
                wxPoint newPos = m_pShapeCanvas->FitPositionToGrid(m_pShapeCanvas->DP2LP(pos));
                shape->SetRelativePosition( Conv2RealPoint(newPos) );
		    }
		    else
                shape->SetRelativePosition( Conv2RealPoint(pos) );

            // add parent shape to the data manager (serializer)
            if(parent)
            {
                AddItem(parent, shape);
            }
            else
                AddItem(GetRootItem(), shape);

            // initialize added shape
			if(initialize)
			{
				shape->CreateHandles();
				
				if( m_pShapeCanvas )
				{
                    shape->SetHoverColour(m_pShapeCanvas->GetHoverColour());
				}

                if(HasChildren(shape))
                {
                    wxSFShapeBase* pChild;
                    ShapeList lstChildren;

                    // get shape's children (if exist)
                    shape->GetChildShapes(sfANY, lstChildren, sfRECURSIVE);
                    // initialize shape's children
                    ShapeList::compatibility_iterator node = lstChildren.GetFirst();
                    while(node)
                    {
                        pChild = (wxSFShapeBase*)node->GetData();

                        pChild->CreateHandles();
                        pChild->Update();
						
                        if( m_pShapeCanvas )
                        {
                            pChild->SetHoverColour(m_pShapeCanvas->GetHoverColour());
                        }

                        node = node->GetNext();
                    }
                }
			}

			// reset scale of assigned shape canvas (if exists and it is necessary...)
			if( m_pShapeCanvas && shape->IsKindOf( CLASSINFO(wxSFControlShape) ) )
			{
				m_pShapeCanvas->SetScale( 1 );
			}
			
            if( m_pShapeCanvas )
            {
                if( saveState )
                {
                    m_pShapeCanvas->SaveCanvasState();
                }
            }
			
			if( err ) *err = wxSF::errOK;
		}
		else
		{
			//wxMessageBox(wxString::Format(wxT("Unable to add '%s' class object to the canvas"), shape->GetClassInfo()->GetClassName()), wxT("ShapeFramework"), wxICON_WARNING);

			delete shape;
			shape = NULL;
			
			if( err ) *err = wxSF::errNOT_ACCEPTED;
		}
	}
	else if( err ) *err = wxSF::errINVALID_INPUT;

	return shape;
}

wxSFShapeBase* wxSFDiagramManager::CreateConnection(long srcId, long trgId, bool saveState, wxSF::ERRCODE *err)
{
    return CreateConnection(srcId, trgId, CLASSINFO(wxSFLineShape), saveState, err);
}

wxSFShapeBase* wxSFDiagramManager::CreateConnection(long srcId, long trgId, wxClassInfo *lineInfo, bool saveState, wxSF::ERRCODE *err)
{
    wxSFShapeBase* pShape = AddShape(lineInfo, sfDONT_SAVE_STATE, err);
    if(pShape)
    {
        wxSFLineShape *pLine = (wxSFLineShape*)pShape;
        pLine->SetSrcShapeId(srcId);
        pLine->SetTrgShapeId(trgId);


        if( m_pShapeCanvas )
        {
            if(saveState)m_pShapeCanvas->SaveCanvasState();
            pLine->Refresh();
        }
    }
    return pShape;
}

wxSFShapeBase* wxSFDiagramManager::CreateConnection(long srcId, long trgId, wxSFLineShape *line, bool saveState, wxSF::ERRCODE *err)
{
    wxSFShapeBase* pShape = AddShape(line, NULL, wxDefaultPosition, sfINITIALIZE, sfDONT_SAVE_STATE, err);
    if(pShape)
    {
        wxSFLineShape *pLine = (wxSFLineShape*)pShape;
        pLine->SetSrcShapeId(srcId);
        pLine->SetTrgShapeId(trgId);


        if( m_pShapeCanvas )
        {
            if(saveState)m_pShapeCanvas->SaveCanvasState();
            pLine->Refresh();
        }
    }
    return pShape;
}


void wxSFDiagramManager::RemoveShape(wxSFShapeBase* shape, bool refresh)
{
	if(shape)
	{
	    wxSFShapeBase *pParent = shape->GetParentShape();

        // remove connected lines (to all children)
        ShapeList lstChildren;
        ShapeList lstConnections;
        ShapeList lstRemovedConnections;

        // get all shape's children
        shape->GetChildShapes(sfANY, lstChildren, sfRECURSIVE);
        lstChildren.Append(shape);

        // retrieve all assigned lines
        ShapeList::compatibility_iterator snode = lstChildren.GetFirst();
        while(snode)
        {
            GetAssignedConnections(snode->GetData(), CLASSINFO(wxSFLineShape), wxSFShapeBase::lineBOTH, lstConnections);
            snode = snode->GetNext();
        }

		// remove all assigne lines
		ShapeList::compatibility_iterator node = lstConnections.GetFirst();
		while(node)
		{
		    // one connection may be used by the parent and also by his child
		    if(lstRemovedConnections.IndexOf(node->GetData()) == wxNOT_FOUND)
		    {
		        lstRemovedConnections.Append(node->GetData());
                RemoveShape(node->GetData(), false);
		    }
			node = node->GetNext();
		}

		// remove the shape also from m_lstCurrentShapes list
		if( m_pShapeCanvas ) m_pShapeCanvas->RemoveFromTemporaries( shape );
		
        // remove the shape
		RemoveItem(shape);
		
        if( pParent ) pParent->Update();

		if( refresh && m_pShapeCanvas ) m_pShapeCanvas->Refresh(false);
	}
}

void wxSFDiagramManager::RemoveShapes(const ShapeList& selection)
{
    wxSFShapeBase* pShape;
	ShapeList::compatibility_iterator node = selection.GetFirst();
	while(node)
	{
	    pShape = node->GetData();
	    // it is important to check whether double-linked shapes already exist before
	    // they are deleted
	    if(Contains(pShape))RemoveShape(pShape, false);
		node = node->GetNext();
	}
}

void wxSFDiagramManager::Clear()
{
	RemoveAll();

    if( m_pShapeCanvas )
    {
        m_pShapeCanvas->GetMultiselectionBox().Show(false);
        m_pShapeCanvas->UpdateVirtualSize();
    }
}

//----------------------------------------------------------------------------------//
// Serialization/deserialization functions
//----------------------------------------------------------------------------------//

bool wxSFDiagramManager::SerializeToXml(const wxString& file, bool withroot)
{
    return wxXmlSerializer::SerializeToXml(file, withroot);
}

bool wxSFDiagramManager::SerializeToXml(wxOutputStream& outstream, bool withroot)
{
    return wxXmlSerializer::SerializeToXml(outstream, withroot);
}

bool wxSFDiagramManager::DeserializeFromXml(const wxString& file)
{
	bool fSuccess = false;
	
	wxFileInputStream instream(file);
	if(instream.IsOk())
	{
        if( m_pShapeCanvas) m_pShapeCanvas->ClearCanvasHistory();

		fSuccess = DeserializeFromXml(instream);

        if( m_pShapeCanvas) m_pShapeCanvas->SaveCanvasState();
	}
	else
		wxMessageBox(wxT("Unable to initialize input stream."), wxT("ShapeFramework"), wxOK | wxICON_ERROR);

	return fSuccess;
}

bool wxSFDiagramManager::DeserializeFromXml(wxInputStream& instream)
{
	// load an XML file
	try
	{
		wxXmlDocument xmlDoc;
		xmlDoc.Load(instream);

		wxXmlNode* root = xmlDoc.GetRoot();
		if(root && (root->GetName() == wxT("chart")))
		{
			// read shape objects from XML recursively
			DeserializeObjects(NULL, root);
			return true;
		}
		else
			wxMessageBox(wxT("Unknown file format."), wxT("ShapeFramework"), wxOK | wxICON_WARNING);
	}
	catch (...)
	{
		wxMessageBox(wxT("Unable to load XML file."), wxT("ShapeFramework"), wxOK | wxICON_ERROR);
	}
	
	return false;
}

void wxSFDiagramManager::DeserializeObjects(xsSerializable* parent, wxXmlNode* node)
{
    _DeserializeObjects(parent, node);

    // update IDs in connection lines and grids
    UpdateConnections();
	UpdateGrids();
	
	m_lstIDPairs.Clear();
	
    if( m_pShapeCanvas )
    {
        //m_pShapeCanvas->MoveShapesFromNegatives();
        m_pShapeCanvas->UpdateVirtualSize();
    }
}

void wxSFDiagramManager::_DeserializeObjects(xsSerializable* parent, wxXmlNode* node)
{
	wxSFShapeBase *pShape;
	
	wxXS::IntArray arrNewIDs;
	SerializableList lstForUpdate;

	wxXmlNode* shapeNode = node->GetChildren();
	while(shapeNode)
	{
		if(shapeNode->GetName() == wxT("object"))
		{
#if wxVERSION_NUMBER < 2900
			pShape = AddShape((wxSFShapeBase*)wxCreateDynamicObject(shapeNode->GetPropVal(wxT("type"), wxT(""))), parent, wxPoint(0, 0), true, sfDONT_SAVE_STATE);
#else
			pShape = AddShape((wxSFShapeBase*)wxCreateDynamicObject(shapeNode->GetAttribute(wxT("type"), wxT(""))), parent, wxPoint(0, 0), true, sfDONT_SAVE_STATE);
#endif
			if(pShape)
			{
				// store new assigned ID
				lstForUpdate.Append( pShape );
				pShape->GetChildrenRecursively( NULL, lstForUpdate );
				
				for( SerializableList::iterator it = lstForUpdate.begin(); it != lstForUpdate.end(); ++it )
				{
					arrNewIDs.Add( (*it)->GetId() );
				}
				
				// deserialize stored content
				pShape->DeserializeObject(shapeNode);

				// update handle in line shapes
				if( pShape->IsKindOf( CLASSINFO(wxSFLineShape) ) )
				{
					pShape->CreateHandles();
					m_lstLinesForUpdate.Append(pShape);
				}
				else if( pShape->IsKindOf( CLASSINFO(wxSFGridShape) ) )
				{
					m_lstGridsForUpdate.Append(pShape);
				}

				// store information about IDs' changes and re-assign shapes' IDs
				int newId, i = 0;
				for( SerializableList::iterator it = lstForUpdate.begin(); it != lstForUpdate.end(); ++it )
				{
					newId = arrNewIDs[i++];
					if( newId != (*it)->GetId() )
					{
						m_lstIDPairs.Append( new IDPair((*it)->GetId(), newId) );
						(*it)->SetId( newId );
					}
				}

				// deserialize child objects
				_DeserializeObjects(pShape, shapeNode);
				
				arrNewIDs.Clear();
				lstForUpdate.Clear();
			}
			else
			{
				// there are some unsupported shapes so the diagrams must be cleared because of possible damage
				RemoveAll();
				m_lstLinesForUpdate.Clear();
				m_lstGridsForUpdate.Clear();
				
				wxMessageBox( wxT("Deserialization couldn't be completed because not of all shapes are accepted."), wxT("wxShapeFramework"), wxOK | wxICON_WARNING );
				return;
			}
		}
		else if(shapeNode->GetName() == m_sRootName + wxT("_properties"))
		{
		    m_pRoot->DeserializeObject(shapeNode->GetChildren());
		}
		shapeNode = shapeNode->GetNext();
	}
}

//----------------------------------------------------------------------------------//
// Shape handling functions
//----------------------------------------------------------------------------------//

void wxSFDiagramManager::AcceptShape(const wxString& type)
{
    if(m_arrAcceptedShapes.Index(type) == wxNOT_FOUND)
    {
        m_arrAcceptedShapes.Add(type);
    }
}
bool wxSFDiagramManager::IsShapeAccepted(const wxString& type)
{
    if( m_arrAcceptedShapes.Index(type) != wxNOT_FOUND )return true;
    else if( m_arrAcceptedShapes.Index(wxT("All")) != wxNOT_FOUND )return true;
    else
        return false;
}

void wxSFDiagramManager::GetAssignedConnections(wxSFShapeBase* parent, wxClassInfo* shapeInfo, wxSFShapeBase::CONNECTMODE mode, ShapeList& lines)
{
	wxSFLineShape* pLine;
	
	if( parent->GetId() == -1 ) return;

	SerializableList lstLines;
	// lines are children of root item only so we have not to search recursively...
	GetRootItem()->GetChildren( shapeInfo, lstLines );
	
	if( !lstLines.IsEmpty() )
    {
        SerializableList::compatibility_iterator node = lstLines.GetFirst();
        while(node)
        {
            pLine = (wxSFLineShape*)node->GetData();
            switch(mode)
            {
                case wxSFShapeBase::lineSTARTING:
                    if( pLine->GetSrcShapeId() == parent->GetId() ) lines.Append(pLine);
                    break;

                case wxSFShapeBase::lineENDING:
                    if( pLine->GetTrgShapeId() == parent->GetId() ) lines.Append(pLine);
                    break;

                case wxSFShapeBase::lineBOTH:
                    if( ( pLine->GetSrcShapeId() == parent->GetId() ) ||
					    ( pLine->GetTrgShapeId() == parent->GetId() ) ) lines.Append(pLine);
                    break;
            }
            node = node->GetNext();
        }
    }
}

void wxSFDiagramManager::GetShapes(wxClassInfo* shapeInfo, ShapeList& shapes, xsSerializable::SEARCHMODE mode)
{
    GetItems(shapeInfo, (SerializableList&)shapes, mode);
}

wxSFShapeBase* wxSFDiagramManager::GetShapeAtPosition(const wxPoint& pos, int zorder, SEARCHMODE mode)
{
	int nCounter = 0;
	ShapeList m_lstSortedShapes;
	wxSFShapeBase* pShape;

    // sort shapes list in the way that the line shapes will be at the top of the list
    ShapeList shapes;
    GetShapes(CLASSINFO(wxSFShapeBase), shapes, xsSerializable::searchBFS);

	ShapeList::compatibility_iterator node = shapes.GetFirst();
	while(node)
	{
	    pShape = node->GetData();
	    if(pShape->IsKindOf(CLASSINFO(wxSFLineShape)))
	    {
	        m_lstSortedShapes.Insert(pShape);
	        nCounter++;
	    }
	    else
            m_lstSortedShapes.Insert(nCounter, pShape);

        node = node->GetNext();
	}

    // find the topmost shape according to the given rules
    nCounter = 1;
	node = m_lstSortedShapes.GetFirst();
	while(node)
	{
		pShape = (wxSFShapeBase*)node->GetData();
		if(pShape->IsVisible() && pShape->IsActive() && pShape->Contains(pos))
		{
			switch(mode)
			{
			case searchSELECTED:
				if(pShape->IsSelected())
				{
					if(nCounter == zorder)return pShape;
					else
						nCounter++;
				}
				break;

			case searchUNSELECTED:
				if(!pShape->IsSelected())
				{
					if(nCounter == zorder)return pShape;
					else
						nCounter++;
				}
				break;

			case searchBOTH:
				if(nCounter == zorder)return pShape;
				else
					nCounter++;
				break;
			}
		}
		node = node->GetNext();
	}

	return NULL;
}

void wxSFDiagramManager::GetShapesAtPosition(const wxPoint& pos, ShapeList& shapes)
{
	shapes.Clear();
	wxSFShapeBase *pShape;

    ShapeList lstShapes;
    GetShapes(CLASSINFO(wxSFShapeBase), lstShapes);

	ShapeList::compatibility_iterator node = lstShapes.GetFirst();
	while(node)
	{
		pShape = node->GetData();
		if(pShape->IsVisible() && pShape->IsActive() && pShape->Contains(pos))shapes.Append(pShape);
		node = node->GetNext();
	}
}

void wxSFDiagramManager::GetShapesInside(const wxRect& rct, ShapeList& shapes)
{
	shapes.Clear();
	wxSFShapeBase* pShape;

    ShapeList lstShapes;
    GetShapes(CLASSINFO(wxSFShapeBase), lstShapes);

	ShapeList::compatibility_iterator node = lstShapes.GetFirst();
	while(node)
	{
		pShape = node->GetData();
		if(pShape->IsVisible() && pShape->IsActive() && pShape->Intersects(rct))shapes.Append(pShape);
		node = node->GetNext();
	}
}

wxSFShapeBase* wxSFDiagramManager::FindShape(long id)
{
    if(id == -1)return NULL;
    else
        return (wxSFShapeBase*)GetItem(id);
}

void wxSFDiagramManager::GetNeighbours(wxSFShapeBase* parent, ShapeList& neighbours, wxClassInfo *shapeInfo, wxSFShapeBase::CONNECTMODE condir, bool direct)
{
    if(parent)
    {
        parent->GetNeighbours(neighbours, shapeInfo, condir, direct);
    }
    else
    {
        wxASSERT(GetRootItem());

		wxSFShapeBase* pShape;

		SerializableList::compatibility_iterator node = GetRootItem()->GetFirstChildNode();
		while(node)
		{
			pShape = (wxSFShapeBase*)node->GetData();
            pShape->GetNeighbours(neighbours, shapeInfo, condir, direct);
			node = node->GetNext();
		}
    }
}

bool wxSFDiagramManager::HasChildren(wxSFShapeBase* parent)
{
	if(parent->GetFirstChildNode())return true;
	else
        return false;
}

void wxSFDiagramManager::UpdateConnections()
{
	if( !m_lstLinesForUpdate.IsEmpty() )
	{		
		wxSFLineShape* pLine;
		IDPair* pIDPair;
	
        // now check ids
		long oldSrcId, oldTrgId;
		long newSrcId, newTrgId;
		IDList::compatibility_iterator idnode;
		
		ShapeList::compatibility_iterator node = m_lstLinesForUpdate.GetFirst();
		while(node)
        {
			pLine = (wxSFLineShape*)node->GetData();
			newSrcId = oldSrcId = pLine->GetSrcShapeId();
			newTrgId = oldTrgId = pLine->GetTrgShapeId();
			idnode = m_lstIDPairs.GetFirst();
			while(idnode)
            {
				pIDPair = idnode->GetData();
				/*if(pIDPair->m_nNewID != pIDPair->m_nOldID)
				{*/
				if(oldSrcId == pIDPair->m_nOldID) newSrcId = pIDPair->m_nNewID;
				if(oldTrgId == pIDPair->m_nOldID) newTrgId = pIDPair->m_nNewID;
				/*}*/
				idnode = idnode->GetNext();
			}
			pLine->SetSrcShapeId(newSrcId);
			pLine->SetTrgShapeId(newTrgId);
			
			// check whether line's src and trg shapes really exists
			if(!GetItem(pLine->GetSrcShapeId()) || !GetItem(pLine->GetTrgShapeId()))
            {
                RemoveItem(pLine);
            }
			
			node = node->GetNext();
		}
		
		m_lstLinesForUpdate.Clear();
    }
}

void wxSFDiagramManager::UpdateGrids()
{
	if( !m_lstGridsForUpdate.IsEmpty() )
	{		
        // now check ids
		wxSFGridShape* pGrid;
		IDPair* pIDPair;
		int nIndex;
		IDList::compatibility_iterator idnode;
		
		ShapeList::compatibility_iterator node = m_lstGridsForUpdate.GetFirst();
		while(node)
        {
			pGrid = (wxSFGridShape*)node->GetData();
			nIndex = wxNOT_FOUND;
			
			idnode = m_lstIDPairs.GetFirst();
			while(idnode)
            {
				pIDPair = idnode->GetData();
				
				nIndex = pGrid->m_arrCells.Index( pIDPair->m_nOldID );
				if( nIndex != wxNOT_FOUND )	pGrid->m_arrCells[ nIndex ] = pIDPair->m_nNewID;
				
				idnode = idnode->GetNext();
			}
			
			// check whether grid's children really exists
			for( size_t i = 0; i < pGrid->m_arrCells.GetCount(); )
			{
				if( !GetItem( pGrid->m_arrCells[i] ) ) pGrid->RemoveFromGrid( pGrid->m_arrCells[i] );
				else
					i++;
			}
			
			node = node->GetNext();
		}
		
		m_lstGridsForUpdate.Clear();	
    }
}

void wxSFDiagramManager::UpdateAll()
{
	wxSFShapeBase *pShape;
	
	ShapeList lstShapes;
	GetShapes( CLASSINFO(wxSFShapeBase), lstShapes );
	
	ShapeList::compatibility_iterator node = lstShapes.GetFirst();
	while( node )
	{
		pShape = node->GetData();
		// update only shapes withour children because the Update() function is called recursively on all parents
		if( !HasChildren( pShape ) ) pShape->Update();
		
		node = node->GetNext();
	}
}

void wxSFDiagramManager::MoveShapesFromNegatives()
{
	wxSFShapeBase *pShape;
	wxRealPoint shapePos;
	double minx = 0, miny = 0;

	// find the maximal negative position value
    ShapeList shapes;
    GetShapes(CLASSINFO(wxSFShapeBase), shapes);

	ShapeList::compatibility_iterator node = shapes.GetFirst();
	while(node)
	{
		shapePos = node->GetData()->GetAbsolutePosition();

		if(node == shapes.GetFirst())
		{
			minx = shapePos.x;
			miny = shapePos.y;
		}
		else
		{
            if(shapePos.x < minx)minx = shapePos.x;
            if(shapePos.y < miny)miny = shapePos.y;
		}

		node = node->GetNext();
	}

	// move all parents shape so they (and their children) will be located in the positive values only
	if((minx < 0) || (miny < 0))
	{
		node = shapes.GetFirst();
		while(node)
		{
			pShape = node->GetData();

			if(pShape->GetParentShape() == NULL)
			{
				if(minx < 0)pShape->MoveBy(abs((int)minx), 0);
				if(miny < 0)pShape->MoveBy(0, abs((int)miny));
			}
			node = node->GetNext();
		}
	}
}
