/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Data-Model Framework (DM)
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: dmcmn.cpp,v 1.10 2001/04/04 12:42:36 aleksandras Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "dmcmn.cpp"
    #pragma interface "dmcmn.h"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include "dmcmn.h"

//#include "wx/debug.h"
#include <stdio.h>    // sprintf

/***** Implementation for class wxDMListener *****/

void wxDMListener::OnNodeRemoved( wxDMNode& node )
{}

void wxDMListener::OnNodeAdded( wxDMNode& node )
{}

void wxDMListener::OnNodeChanged( wxDMNode& node )
{}

void wxDMListener::OnNodeOrderChanged( wxDMNode& node )
{}

void wxDMListener::OnPointerChanged( wxDMNode* pFromPtr, wxDMNode* pToPtr )
{}

void wxDMListener::OnNodeReplaced( wxDMNode& node, wxDMNode& withNode )
{}

void wxDMListener::OnFocusChanged( wxDMNode& node )
{}

void wxDMListener::OnFocusRemoved()
{}

void wxDMListener::OnSelectionSet( DMNodeListT& nodes )
{}

void wxDMListener::OnNodeAddedToSelection( wxDMNode& node )
{}

void wxDMListener::OnNodeDeselected( wxDMNode& node )
{}

void wxDMListener::OnUpdateFinished()
{}

/***** Implementation for class wxDMNode *****/

IMPLEMENT_DYNAMIC_CLASS( wxDMNode, wxObject );

wxDMNode::wxDMNode()

	: mpParent( NULL ),
	  mFlags( 0 )
{}

wxDMNode::~wxDMNode()
{
}

size_t wxDMNode::GetGroupCount()
{
	return 0;
}

wxDMAttrGroupInfo* wxDMNode::GetAttrGroupInfo( size_t index )
{
	return NULL;
}

void* wxDMNode::GetAttrGroupData( size_t index )
{
	return NULL;
}

void* wxDMNode::GetAttrGroupData( wxDMAttrGroupInfo* pForGroup )
{
	return NULL;
}

wxString wxDMNode::GetDisplayName()
{
	return "Default-Display-Name";
}

bool wxDMNode::Contains( wxDMNode* pNode )
{
	if ( this == pNode ) return TRUE;

	if ( !HasChildren() ) return FALSE;

	DMNodeListT& lst = GetChildren();

	for( size_t i = 0; i != lst.Count(); ++i )

		if ( lst[i]->Contains( pNode ) )

			return TRUE;

	return FALSE;
}

bool wxDMNode::IsLinked()
{
	return FALSE;
}

wxDMNode* wxDMNode::Clone( bool uniqueCopy, bool cloneChildren )
{
	wxDMNode* pCopy = new wxDMNode();

	AssignData( pCopy, uniqueCopy, cloneChildren );

	return pCopy;
}

size_t wxDMNode::GetOrderNo()
{
	if ( !GetParent() ) return 0;

	DMNodeListT& lst = GetParent()->GetChildren();

	for( size_t i = 0; i != lst.Count(); ++i )

		if ( lst[i] == this )

			return i;

	return 0;
}

void wxDMNode::SetOrderNo( size_t orderNo )
{
	if ( !mpParent ) return;

	if ( GetOrderNo() == orderNo ) return;

	DMNodeListT& lst = GetParent()->GetChildren();

	lst.Remove( this );

	if ( orderNo >= lst.Count() )

		lst.Add( this );
	else	
		lst.Insert( this, orderNo );

	NOTIFY_ONE_ARG( OnNodeOrderChanged, *this );
}

void wxDMNode::AssignData( wxDMNode* pCopy, bool uniqueCopy, bool cloneChildren )

{ /* nothing by default */ }

void wxDMNode::OnStartEdit()  
{ 
	NotifyWillChange( this ); 
}
void wxDMNode::OnFinishEdit() 
{
	NOTIFY_ONE_ARG( OnNodeChanged, *this );
}

void wxDMNode::AddChild( wxDMNode* pChild, int zOrder )
{
	wxASSERT( 0 ); // DBG::
}

void wxDMNode::Remove()
{
	if ( HasParent() )

		mpParent->RemoveChild( this );
	else
		delete this;
}

void wxDMNode::RemoveChild( wxDMNode* pChild )
{
	wxASSERT( 0 ); // DBG::
}

void wxDMNode::RemoveChild( size_t childIndex )
{
	wxASSERT( 0 ); // DBG::
}

bool wxDMNode::CanHaveChildren()
{
	return TRUE;
}

bool wxDMNode::HasChildren()
{
	return FALSE;
}

size_t wxDMNode::GetChildCount()
{
	return 0;
}

wxDMNode* wxDMNode::GetChild( size_t index )
{
	return NULL;
}

void wxDMNode::Replace( wxDMNode* pWithNode )
{
	// keep original ordering while replacing

	if ( mpParent )
	{
		int orderNo = GetOrderNo();

		mpParent->GetChildren()[ orderNo ] = pWithNode;
	}

	// take over children to the replacing node

	if ( HasChildren() )
	{
		DMNodeListT	children = GetChildren();

		pWithNode->GetChildren() = children;
		
		for( size_t i = 0; i != children.Count(); ++i )

			children[i]->SetParent( pWithNode );

		GetChildren().Clear();
	}

	pWithNode->SetParent( mpParent );

	if ( mpParent )

		mpParent->OnChildChanged( pWithNode );

	NOTIFY_TWO_ARG( OnNodeReplaced, *this, *pWithNode );

	// finally, transfer ownership of command-processor (if any)

	if ( this->HasCommandProcessor() )
	{
		pWithNode->SetCommandProcessor( this->GetCommandProcessor() );

		this->SetCommandProcessor( NULL );
	}

	delete this;
}

DMNodeListT& wxDMNode::GetChildren()
{
	wxASSERT( 0 ); // DBG::

	DMNodeListT* pLst = NULL;

	return *pLst;
}

bool wxDMNode::HasParent()
{
	return mpParent != NULL;
}

void wxDMNode::SetParent( wxDMNode* pParent )
{
	mpParent = pParent;
}

wxDMNode* wxDMNode::GetParent()
{
	return mpParent;
}

void wxDMNode::NotifyWillChange( wxDMNode* pObj )
{
	wxDMCommandProcessor* pProc = this->FindCmdProcessor();

	if ( pProc ) pProc->NotifyWillChange( pObj );
}

DMListenerListT& wxDMNode::GetListeners()
{
	return FindCmdProcessor()->GetListeners();
}

void wxDMNode::NotifyWillRemove( wxDMNode* pObj )
{
	wxDMCommandProcessor* pProc = this->FindCmdProcessor();

	if ( pProc ) pProc->NotifyWillRemove( pObj );
}

void wxDMNode::NotifyAdded( wxDMNode* pObj )
{
	wxDMCommandProcessor* pProc = this->FindCmdProcessor();

	if ( pProc ) pProc->NotifyAdded( pObj );
}

void wxDMNode::OnChildInserted( wxDMNode* pNode )
{
}

void wxDMNode::OnChildChanged( wxDMNode* pNode )
{
}

void wxDMNode::OnChildRemoved( wxDMNode* pNode )
{
}

bool wxDMNode::HasCommandProcessor()
{
	return FALSE;
}

void wxDMNode::SetCommandProcessor( wxDMCommandProcessor* pProcessor )
{
	wxASSERT( 0 ); // DBG::
}

wxDMCommandProcessor* wxDMNode::GetCommandProcessor()
{
	return NULL;
}

wxDMCommandProcessor* wxDMNode::FindCmdProcessor()
{
	wxDMNode* pObj = this;

	while( pObj )
	{
		if ( pObj->GetCommandProcessor() != NULL )

			return pObj->GetCommandProcessor();

		pObj = pObj->GetParent();
	}

	return NULL;
}

void wxDMNode::SetSelected( bool isSelected )
{
	if ( isSelected )  mFlags |= NODE_SELECTED;
	if ( !isSelected ) mFlags &= ~NODE_SELECTED;
}

bool wxDMNode::IsSelected()
{
	return mFlags & NODE_SELECTED;
}

void wxDMNode::SetFocused( bool isFocused )
{
	wxDMNode* pPrevFocus = GetRootNode().GetFocus();

	if ( pPrevFocus == this && isFocused ) 

		return;

	if ( isFocused )  mFlags |= NODE_FOCUSED;
	if ( !isFocused ) mFlags &= ~NODE_FOCUSED;

	if ( isFocused )
	{
		if ( pPrevFocus && pPrevFocus != this ) pPrevFocus->mFlags &= ~NODE_FOCUSED;

		NOTIFY_ONE_ARG( OnFocusChanged, *this );
	}
	else
	if ( !isFocused )
	{
		if ( pPrevFocus ) pPrevFocus->mFlags &= ~NODE_FOCUSED;

		NOTIFY_NO_ARG( OnFocusRemoved );
	}
}

bool wxDMNode::IsFocused()
{
	return ( mFlags & NODE_FOCUSED ) != 0;
}

wxDMNode* wxDMNode::FindFocus()
{
	return FindFocusRecursive( this );
}

wxDMNode& wxDMNode::GetRootNode()
{
	wxDMNode* pNode = this;

	while( pNode->HasParent() )

		pNode = pNode->GetParent();

	return *pNode;
}

void wxDMNode::GetSelection( DMNodeListT& toLst )
{
	if ( IsSelected() )

		toLst.Add( this );

	for( size_t i = 0; i != GetChildCount(); ++i )
	
		GetChild(i)->GetSelection( toLst );
}

void wxDMNode::SetSelection( DMNodeListT& lst )
{
	wxHashTable nodeHash;

	for( size_t i = 0; i != lst.Count(); ++i )

		nodeHash.Put( (long)lst[i], (wxObject*)1 );

	NOTIFY_ONE_ARG( OnSelectionSet, lst )

	this->DoSetSelection( nodeHash );
}

wxDMNode* wxDMNode::GetFocus()
{
	if ( IsFocused() ) return this;

	for( size_t i = 0; i != GetChildCount(); ++i )
	{
		wxDMNode* pChild = GetChild( i );

		wxDMNode* pFound = pChild->GetFocus();

		if ( pFound )

			return pFound;
	}
	
	return NULL;
}

void wxDMNode::DoSetSelection( wxHashTable& nodeHash )
{
	if ( nodeHash.Get( (long)this ) == NULL )

		mFlags &= ~NODE_SELECTED;
	else
		mFlags |= NODE_SELECTED;

	for( size_t i = 0; i != GetChildCount(); ++i )

		GetChild(i)->DoSetSelection( nodeHash );
}

wxDMNode* wxDMNode::FindFocusRecursive( wxDMNode* pNode )
{
	if ( pNode->IsFocused() && pNode->IsSelected() )

		return pNode;

	if ( !pNode->HasChildren() ) return NULL;

	for( size_t i = 0; i != pNode->GetChildCount(); ++i )

		if (  FindFocusRecursive( pNode->GetChild(i) ) )

			return pNode->GetChild(i);

	return NULL;
}

/***** Implementation for class wxDMCompositeNode *****/

IMPLEMENT_DYNAMIC_CLASS( wxDMCompositeNode, wxDMNode );

wxDMCompositeNode::wxDMCompositeNode()

	: wxDMNode(),
	  mpCmdProcessor( NULL )
{}

wxDMCompositeNode::~wxDMCompositeNode()
{
	WX_CLEAR_ARRAY( mChildren );

	if ( mpCmdProcessor ) delete mpCmdProcessor;
}

void wxDMCompositeNode::AssignData( wxDMNode* pCopy, bool uniqueCopy, bool cloneChildren )
{
	wxDMCompositeNode* pNode = (wxDMCompositeNode*)pCopy;

	if ( cloneChildren )

		for( size_t i = 0; i != mChildren.Count(); ++i )
		{
			wxDMNode* pCopy = mChildren[i]->Clone( uniqueCopy, cloneChildren );
			pNode->mChildren.Add( pCopy );

			pCopy->SetParent( pNode );
		}
}

void wxDMCompositeNode::AddChild( wxDMNode* pChild, int zOrder )
{
	if ( zOrder == -1 )

		mChildren.Add( pChild );
	else
		mChildren.Insert( pChild, zOrder );

	pChild->SetParent( this );

	this->OnChildInserted( pChild );

	NOTIFY_ONE_ARG( OnNodeAdded, *pChild );

	//NotifyAdded( pChild );
}

void wxDMCompositeNode::RemoveChild( wxDMNode* pChild )
{
	wxASSERT( pChild->GetParent() == this ); // DBG::

	//NotifyWillRemove( pChild );

	mChildren.Remove( pChild );

	this->OnChildRemoved( pChild );

	NOTIFY_ONE_ARG( OnNodeRemoved, *pChild );

	delete pChild;
}

void wxDMCompositeNode::RemoveChild( size_t childIndex )
{
	//NotifyWillRemove( mChildren[ childIndex ] );

	NOTIFY_ONE_ARG( OnNodeRemoved, *mChildren[ childIndex ] );

	delete mChildren[ childIndex ];

	mChildren.Remove( childIndex );
}

bool wxDMCompositeNode::HasChildren()
{
	return mChildren.Count() != 0;
}

size_t wxDMCompositeNode::GetChildCount()
{
	return mChildren.Count();
}

wxDMNode* wxDMCompositeNode::GetChild( size_t index )
{
	return mChildren[ index ];
}

DMNodeListT& wxDMCompositeNode::GetChildren()
{
	return mChildren;
}

void wxDMCompositeNode::SetCommandProcessor( wxDMCommandProcessor* pProcessor )
{
	if ( pProcessor && mpCmdProcessor ) 
		
		delete mpCmdProcessor;

	mpCmdProcessor = pProcessor;
}

bool wxDMCompositeNode::HasCommandProcessor()
{
	return mpCmdProcessor != NULL;
}

wxDMCommandProcessor* wxDMCompositeNode::GetCommandProcessor()
{
	return mpCmdProcessor;
}

/***** Implementation for class wxDMAttrInfo *****/

IMPLEMENT_DYNAMIC_CLASS( wxDMAttrInfo, wxObject );

wxDMAttrInfo::wxDMAttrInfo()

	: mpUserData( NULL )
{}

wxDMAttrInfo::~wxDMAttrInfo()
{}

wxString wxDMAttrInfo::GetStringValue( void* pGroupData )
{
	wxASSERT(NULL); // DBG::
	return "";
}

void wxDMAttrInfo::SetStringValue( const wxString& value, void* pGroupData )
{
	wxASSERT(NULL); // DBG::
}

bool wxDMAttrInfo::ValidateStringValue( const wxString& value, const wxString* pErrMsg )
{
	return TRUE;
}

void wxDMAttrInfo::SetVisible( bool visible )
{
	mVisible = visible;
}

bool wxDMAttrInfo::IsVisible()
{
	return TRUE;
}

void wxDMAttrInfo::SetName( const wxString& name )
{
	mName = name;
}

wxString wxDMAttrInfo::GetName()
{
	return mName;
}

void wxDMAttrInfo::SetDisplayName( const wxString& name )
{
	mDisplayName = name;
}

wxString wxDMAttrInfo::GetDisplayName()
{
	if ( !mDisplayName.Length() )

		return mName;

	return mDisplayName;
}

void wxDMAttrInfo::SetDefaultValue( const wxString& value )
{
	mDefaultValue = value;
}

const wxString& wxDMAttrInfo::GetDefaultValue()
{
	return mDefaultValue;
}

bool wxDMAttrInfo::NeedsCustomEditor()
{
	return FALSE;
}

wxWindow* wxDMAttrInfo::CreateCustomEditor()
{
	wxASSERT(NULL); // DBG::
	return NULL;
}

bool wxDMAttrInfo::ApplayEditorChanges( wxWindow* pCustEditor, void* pGroupData )
{
	wxASSERT(NULL); // DBG::
	return FALSE;
}

bool wxDMAttrInfo::HasRestrictedValues( void* pGroupData )
{
	return FALSE;
}

bool wxDMAttrInfo::SetRestrictedValues( wxStringList& list )
{
	wxASSERT(NULL); // DBG::
	return FALSE;
}

void wxDMAttrInfo::GetRestrictedValues( wxStringList& toLst, void* pGroupData )
{
	wxASSERT(NULL); // DBG::
}

bool wxDMAttrInfo::HasMultipleValues( void* pGroupData )
{
	return FALSE;
}

void wxDMAttrInfo::SetUserData( void* pUserData )
{
	mpUserData = pUserData;
}

void* wxDMAttrInfo::GetUserData()
{
	return mpUserData;
}

bool wxDMAttrInfo::ShownAsBooleanField()
{
	return FALSE;
}

void wxDMAttrInfo::SetMultipleValues( wxStringList& fromList, void *pGroupData )
{}

void wxDMAttrInfo::GetMultipleValues( wxStringList& toList, void* pGroupData )
{}

void wxDMAttrInfo::InitValue( void* pGroupData )
{
	/* nothing by default */
}

void wxDMAttrInfo::DestroyValue( void* pGroupData )
{
	/* nothing by default */
}

void wxDMAttrInfo::CopyValue( void* pDestData, void* pSrcData )
{
	/* nothing by default */
}

bool wxDMAttrInfo::HasValueSelectionDialog()
{
	return FALSE;
}

bool wxDMAttrInfo::ShowValueSelectonDialog( void* pGroupData )
{
	return FALSE;
}

bool wxDMAttrInfo::IsDefaultValue( void* pGroupData )
{
	return GetStringValue( pGroupData ) == mDefaultValue;
}

/***** Implementation for class wxDMAttrGroupInfo *****/

IMPLEMENT_DYNAMIC_CLASS( wxDMAttrGroupInfo, wxObject );

wxDMAttrGroupInfo::wxDMAttrGroupInfo()

	: mName( "Default-name for attrr-group" )
{}

wxDMAttrGroupInfo::~wxDMAttrGroupInfo()
{
	WX_CLEAR_ARRAY( mAttribs );
}

void wxDMAttrGroupInfo::SetName( const wxString& name )
{
	mName = name;
}

const wxString& wxDMAttrGroupInfo::GetName()
{
	return mName;
}

size_t wxDMAttrGroupInfo::Count()
{
	return mAttribs.Count();
}

wxDMAttrInfo* wxDMAttrGroupInfo::GetAttr( size_t index )
{
	return mAttribs[ index ];
}

wxString wxDMAttrGroupInfo::GetAttrValueByName( const wxString& name, 
												void* pGroupData )
{
	wxDMAttrInfo* pAttr = GetAttrByName( name );

	wxASSERT_MSG( pAttr, "[GetAttrValueByName] Attribute with name " + name + " not found!" ); // DBG::

	return pAttr->GetStringValue( pGroupData );
}

void wxDMAttrGroupInfo::SetAttrValueByName( const wxString& name, 
										    const wxString& value, 
											void* pGroupData )
{
	wxDMAttrInfo* pAttr = GetAttrByName( name );

	wxASSERT_MSG( pAttr, "[GetAttrValueByName] Attribute with name " + name + " not found!" ); // DBG::

	pAttr->SetStringValue( value, pGroupData );
}

void wxDMAttrGroupInfo::Add( wxDMAttrInfo* pInfo )
{
	mAttribs.Add( pInfo );
}

void wxDMAttrGroupInfo::RemoveAll()
{
	WX_CLEAR_ARRAY( mAttribs );
}

void wxDMAttrGroupInfo::InitValues( void* pGroupData )
{
	for( size_t i = 0; i != mAttribs.Count(); ++i )

		mAttribs[i]->InitValue( pGroupData );
}

void wxDMAttrGroupInfo::DestroyValues( void* pGroupData )
{
	for( size_t i = 0; i != mAttribs.Count(); ++i )

		mAttribs[i]->DestroyValue( pGroupData );
}

void wxDMAttrGroupInfo::CopyValues( void* pDestGroupData, void* pSrcGroupData )
{
	for( size_t i = 0; i != mAttribs.Count(); ++i )

		mAttribs[i]->CopyValue( pDestGroupData, pSrcGroupData );
}

size_t wxDMAttrGroupInfo::GetAttrIndex( const wxString& attrName )
{
	for( size_t i = 0; i != mAttribs.Count(); ++i )

		if ( mAttribs[i]->GetName() == attrName )

			return i;

	return (size_t)(-1);
}

wxDMAttrInfo* wxDMAttrGroupInfo::GetAttrByName( const wxString& attrName )
{
	for( size_t i = 0; i != mAttribs.Count(); ++i )
	{
		wxString name = mAttribs[i]->GetName();

		if ( mAttribs[i]->GetName() == attrName )

			return mAttribs[i];
	}

	return NULL;
}

/***** Implementation for class wxDMCommandInfo *****/

wxDMCommandInfo::wxDMCommandInfo( int type, wxDMNode* pRealObj, size_t batchLevel )

	: mType           ( type ),
	  mpObjCopy       ( NULL ),
	  mpRealObj       ( pRealObj ),

	  mRealObjIsDelted( FALSE ),
	  mParentIsDeleted( FALSE ),
	  mFinishesBatch  ( FALSE ),
	  mpLatestState   ( NULL )
{
	if ( pRealObj )
	{
		mpParent = pRealObj->GetParent();

		if ( mpParent )

			mZOrder = pRealObj->GetOrderNo();
		else
			mZOrder = 0;

		mpObjCopy = pRealObj->Clone( FALSE );
	}

	if ( batchLevel == 0 )

		mFinishesBatch = TRUE;
}


wxDMCommandInfo::~wxDMCommandInfo()
{
	if ( mpObjCopy ) delete mpObjCopy;

	if ( mpLatestState ) delete mpLatestState;
}

int wxDMCommandInfo::GetType() 
{ 
	return mType; 
}

bool wxDMCommandInfo::FinishesBatch() 
{ 
	return mFinishesBatch; 
}

wxDMCommandInfo* wxDMCommandInfo::GetLatestState()
{
	return mpLatestState;
}

void wxDMCommandInfo::CaptureLatestState()
{
	if ( mpLatestState ) delete mpLatestState;

	mpLatestState = new wxDMCommandInfo( DM_CMD_CHANGE, mpRealObj, 0 );
}

/***** Implementation for class wxDMCommandProcessor *****/

IMPLEMENT_DYNAMIC_CLASS( wxDMCommandProcessor, wxObject );

size_t wxDMCommandProcessor::mIdGenerator = 1;
wxDMClipboard*  wxDMCommandProcessor::mpClipboard = NULL;

wxDMCommandProcessor::wxDMCommandProcessor()

	: mpRootNode( NULL ),
	  mpChangeSender( NULL ),
	  mBatchLevel( 0 ),

	  mMaxCommands( 1000000 ),
	  mNextCmd   ( 0 ),

	  mQueueLocked( FALSE )
{}

wxDMCommandProcessor::wxDMCommandProcessor(  wxDMNode* pRootNode, size_t maxCommands )

	: mpRootNode( mpRootNode ),
	  mBatchLevel( 0 ),

	  mMaxCommands( maxCommands ),
	  mNextCmd   ( 0 ),

	  mQueueLocked( FALSE )
{}

wxDMCommandProcessor::~wxDMCommandProcessor()
{
	CleanUp();
}

void wxDMCommandProcessor::SetRoot( wxDMNode* pObj )
{
	mpRootNode = pObj;

	if ( mpClipboard ) mpClipboard->SetRootNode( pObj );
}

void wxDMCommandProcessor::ClearCommandBuffer()
{
	CleanUp();
}

void wxDMCommandProcessor::SetChangeSender( wxObject* pObject )
{
	mpChangeSender = pObject;
}

wxObject* wxDMCommandProcessor::GetChangeSender()
{
	return mpChangeSender;
}

void wxDMCommandProcessor::NotifyWillChange( wxDMNode* pObj )
{
	InsertCommand( new wxDMCommandInfo( DM_CMD_CHANGE, pObj, mBatchLevel ) );
}

void wxDMCommandProcessor::NotifyWillRemove( wxDMNode* pObj )
{
	StartBatch();

	// do it for all children first (recursively)

	for( size_t i = 0; i != pObj->GetChildCount(); ++i )

		NotifyWillRemove( pObj->GetChild(i) );

	// then for the parent

	InsertCommand( new wxDMCommandInfo( DM_CMD_REMOVE, pObj, mBatchLevel ) ); 

	EncodeDeletedObject( pObj );

	FinishBatch();
}

void wxDMCommandProcessor::NotifyAdded( wxDMNode* pObj )
{
	StartBatch();

	InsertCommand( new wxDMCommandInfo( DM_CMD_ADD, pObj, mBatchLevel ) );

	// do the same for all children (recursively)

	for( size_t i = 0; i != pObj->GetChildCount(); ++i )

		NotifyAdded( pObj->GetChild(i) );

	FinishBatch();
}

void wxDMCommandProcessor::StartBatch()
{
	++mBatchLevel;
}

void wxDMCommandProcessor::FinishBatch()
{
	--mBatchLevel;

	wxASSERT( mBatchLevel >= 0 );

	if ( mBatchLevel == 0 && mCommands.Count() )

		mCommands[ mCommands.Count()-1 ]->mFinishesBatch = TRUE;
}

bool wxDMCommandProcessor::CanUndo()
{
	return mNextCmd != 0;
}

bool wxDMCommandProcessor::CanRedo()
{
	return mNextCmd != mCommands.Count();
}

bool wxDMCommandProcessor::CanDelete()
{
	// FIXME:: slow!

	DMNodeListT lst;

	mpRootNode->GetSelection( lst );

	return lst.Count() != 0;
}

void wxDMCommandProcessor::Delete()
{
	DMNodeListT lst;
	mpRootNode->GetSelection( lst );

	StartBatch();

	for( size_t i = 0; i != lst.Count(); ++i )

		DoDeleteRecursive( lst[i] );

	FinishBatch();

    NOTIFY_NO_ARG( OnUpdateFinished );
}

void wxDMCommandProcessor::DoDeleteRecursive( wxDMNode* pNode )
{
	if ( pNode == mpRootNode )

		return; // FOR NOW:: do not delete top-level nodes

	DMNodeListT lst = pNode->GetChildren();

	for( size_t i = 0; i != lst.Count(); ++i )

		DoDeleteRecursive( lst[i] );

	NotifyWillRemove( pNode );

	pNode->Remove();
}

void wxDMCommandProcessor::SelectAll()
{
	// TBD::
}

void wxDMCommandProcessor::Undo()
{
	mQueueLocked = TRUE;

	wxASSERT( CanUndo() ); // DBG:: user-code should check it

	for(;;)
	{
		--mNextCmd;

		wxDMCommandInfo& cmd = *mCommands[mNextCmd];

		if ( cmd.GetType() == DM_CMD_CHANGE )
		{
			if ( !cmd.GetLatestState() && !HasModsAhead( mNextCmd ) )

				cmd.CaptureLatestState();

			RestoreObject( cmd );
		}
		else
		if ( cmd.GetType() == DM_CMD_ADD )
		{
			RemoveObject( cmd );
		}
		else
		if ( cmd.GetType() == DM_CMD_REMOVE )
		{
			AddObject( cmd );
		}

		if ( mNextCmd == 0 || mCommands[mNextCmd-1]->FinishesBatch() )

		   break;
	}

	mQueueLocked = FALSE;

	NOTIFY_NO_ARG( OnUpdateFinished );
}

void wxDMCommandProcessor::Redo()
{
	mQueueLocked = TRUE;

	wxASSERT( CanRedo() ); // DBG:: user-code should check it

	for(;;)
	{
		wxDMCommandInfo& cmd = *mCommands[mNextCmd];

		if ( cmd.GetType() == DM_CMD_CHANGE )
		{
			if ( cmd.GetLatestState() )

				RestoreObject( *cmd.GetLatestState() );
			else
			{
				// search for the stored-state before subsequent-modification of the given object

				wxDMNode* pObj = cmd.mpRealObj;

				for( size_t n = mNextCmd + 1; n != mCommands.Count(); ++n )
				
					if ( mCommands[n]->mpRealObj == pObj )
					{
						RestoreObject( *mCommands[n] );
						break;
					}
			}
		}
		else
		if ( cmd.GetType() == DM_CMD_ADD )
		{
			AddObject( cmd );
		}
		else
		if ( cmd.GetType() == DM_CMD_REMOVE )
		{
			RemoveObject( cmd );
		}

		++mNextCmd;

		if ( mNextCmd == mCommands.Count() ||
			 mCommands[mNextCmd-1]->FinishesBatch() )

			 break;
	}

	mQueueLocked = FALSE;

	NOTIFY_NO_ARG( OnUpdateFinished );
}

bool wxDMCommandProcessor::CanPaste()
{
	return mpClipboard && mpClipboard->CanPaste();
}

void wxDMCommandProcessor::Copy()
{
	mpClipboard->Copy();
}

void wxDMCommandProcessor::Paste()
{
	mpClipboard->Paste();

	NOTIFY_NO_ARG( OnUpdateFinished );
}

void wxDMCommandProcessor::AddListener( wxDMListener* pListener )
{
	mListeners.Add( pListener );
}

void wxDMCommandProcessor::RemoveListener( wxDMListener* pListener )
{
	mListeners.Remove( pListener );
}

DMListenerListT& wxDMCommandProcessor::GetListeners()
{
	return mListeners;
}

wxDMClipboard* wxDMCommandProcessor::GetClipboard()
{
	return mpClipboard;
}

void wxDMCommandProcessor::SetClipboard( wxDMClipboard* pBoard )
{
	mpClipboard = pBoard;
}

/*** protected methods ***/

void wxDMCommandProcessor::RelinkReferencesTo( wxDMNode* pNewObj, wxDMCommandInfo& cmd )
{
	wxDMNode* pOldObj   = cmd.mpRealObj;
	bool     isDeleted  = cmd.mRealObjIsDelted;

	for( size_t i = 0; i != mCommands.Count(); ++i )
	{
		wxDMCommandInfo& cur = *mCommands[i];

		if ( cur.mRealObjIsDelted == isDeleted && cur.mpRealObj == pOldObj )
		{
			cur.mRealObjIsDelted = FALSE;
			cur.mpRealObj        = pNewObj;

			if ( cur.mpLatestState )
			{
				cur.mpLatestState->mRealObjIsDelted = FALSE;
				cur.mpLatestState->mpRealObj        = pNewObj;
			}
		}

		if ( cur.mParentIsDeleted == isDeleted && cur.mpParent == pOldObj )
		{
			cur.mParentIsDeleted = FALSE;
			cur.mpParent         = pNewObj;
		}
	}
}

void wxDMCommandProcessor::EncodeDeletedObject( wxDMNode* pDeleted )
{
	size_t newId = mIdGenerator++;

	for( size_t i = 0; i != mCommands.Count(); ++i )
	{
		wxDMCommandInfo& cur = *mCommands[i];

		if ( !cur.mRealObjIsDelted && cur.mpRealObj == pDeleted )
		{
			cur.mRealObjIsDelted = TRUE;
			cur.mpRealObj        = (wxDMNode*) newId;

			if ( cur.mpLatestState )
			{
				cur.mpLatestState->mRealObjIsDelted = TRUE;
				cur.mpLatestState->mpRealObj        = (wxDMNode*) newId;;
			}
		}

		if ( !cur.mParentIsDeleted && cur.mpParent == pDeleted )
		{
			cur.mParentIsDeleted = TRUE;
			cur.mpParent         = (wxDMNode*) newId;
		}
	}
}

void wxDMCommandProcessor::RemoveObject( wxDMCommandInfo& cmd )
{
	wxDMNode* pDeleted = cmd.mpRealObj;

	cmd.mpParent->RemoveChild( cmd.mpRealObj );

	//cmd.mpScene->Remove( cmd.mpRealObj, FALSE );

	// encode refrences to delted object using unique id generated for that purpose

	EncodeDeletedObject( pDeleted );
}

void wxDMCommandProcessor::RestoreObject( wxDMCommandInfo& cmd )
{
	wxDMNode* pNewObj = cmd.mpObjCopy->Clone( FALSE );

	if ( !pNewObj ) return;

	if ( cmd.mpRealObj == mpRootNode )
	{
		mpRootNode = pNewObj;
		if ( mpClipboard ) mpClipboard->SetRootNode( pNewObj );
	}

	cmd.mpRealObj->Replace( pNewObj );

	//cmd.mpParent->RemoveChild( cmd.mpRealObj );
	//cmd.mpParent->AddChild( pNewObj );
	pNewObj->SetOrderNo( cmd.mZOrder );

	//cmd.mpScene->Remove( cmd.mpRealObj, FALSE );

	//cmd.mpScene->Insert( pNewObj, FALSE );

	//cmd.mpScene->SetZOrderForObject( pNewObj, cmd.mZOrder );

	RelinkReferencesTo( pNewObj, cmd );
}

void wxDMCommandProcessor::AddObject( wxDMCommandInfo& cmd )
{
	wxDMNode* pNewObj = cmd.mpObjCopy->Clone( FALSE );

	cmd.mpParent->AddChild( pNewObj );

	pNewObj->SetOrderNo( cmd.mZOrder );

	//cmd.mpScene->Insert( pNewObj, FALSE );

	//cmd.mpScene->SetZOrderForObject( pNewObj, cmd.mZOrder );

	RelinkReferencesTo( pNewObj, cmd );
}

bool wxDMCommandProcessor::HasModsAhead( size_t cmdNo )
{
	wxDMNode* pObj = mCommands[cmdNo]->mpRealObj;

	for( size_t n = cmdNo + 1;  n != mCommands.Count(); ++n )
	
		if ( mCommands[n]->mpRealObj == pObj ) return TRUE;

	return FALSE;
}

void wxDMCommandProcessor::CleanUp()
{
	mBatchLevel = 0;
	mNextCmd = 0;
	WX_CLEAR_ARRAY( mCommands )
}

void wxDMCommandProcessor::SetRootNode( wxDMNode* pNode )
{
	mpRootNode = pNode;

	if ( mpClipboard ) mpClipboard->SetRootNode( pNode );
}

void wxDMCommandProcessor::CheckBuffer()
{
	// TBD:: hack...

	while( mCommands.Count() > mMaxCommands )
	{
		delete mCommands[0];

		mCommands.Remove( (size_t)0 );
	}
}

void wxDMCommandProcessor::InsertCommand( wxDMCommandInfo* pCmd )
{
	wxASSERT( !mQueueLocked );
	// check if object was capable of cloning itself
	// if not, the operation cannot be undone

	if ( pCmd->mpObjCopy == NULL )
	{
		delete pCmd; 
		return;
	}

	while( mNextCmd != mCommands.Count() )
	{
		// discard pending redoable operations upon
		// the first new operation

		delete mCommands[mNextCmd];

		mCommands.Remove( mNextCmd );
	}

	mCommands.Add( pCmd );

	++mNextCmd;

	CheckBuffer();
}

/***** Implementation for class wxDMClipboard *****/

wxDMClipboard::wxDMClipboard()

	: mpRoot( NULL ),
	  mpHandler( NULL )
{}

wxDMClipboard::~wxDMClipboard()
{
	Destroy();
}

bool wxDMClipboard::CanCopy()
{
 	DMNodeListT lst;

	mpRoot->GetSelection( lst );

	return (lst.Count() != 0);
}

bool wxDMClipboard::CanPaste()
{
	return (mContent.Count() != 0);
}

void wxDMClipboard::Copy()
{
	Destroy();

 	DMNodeListT lst;

	mpRoot->GetSelection( lst );

	for( size_t i = 0; i != lst.Count(); ++i )
	{
		wxDMNode* pCopy = lst[i]->Clone( FALSE, TRUE );
		mContent.Add( pCopy );
	}
}

void wxDMClipboard::Paste()
{
	wxDMCommandProcessor& cmdp = *mpRoot->GetCommandProcessor();

	cmdp.StartBatch();

	wxDMNode* pTarget = mpRoot->GetFocus();

	while( pTarget && pTarget->GetParent() )
	{
		if ( !pTarget->CanHaveChildren() )

			pTarget = pTarget->GetParent();
		else
			break;
	}

	if ( !pTarget ) 

		pTarget = mpRoot;

	size_t i = 0;

	DMNodeListT newSel;
	for( ; i != mContent.Count(); ++i )
	{
		newSel.Add( DoPasteRecursive( mContent[i], pTarget, 0 ) );
	}

	mpRoot->SetSelection( newSel );

	DMListenerListT& lst = cmdp.GetListeners();

	cmdp.FinishBatch();

	for( i = 0; i != lst.Count(); ++i )

		lst[i]->OnUpdateFinished();

	if ( newSel.Count() )

		newSel[0]->SetFocused( TRUE );
}


void wxDMClipboard::SetRootNode( wxDMNode* pNode )
{
	mpRoot = pNode;
}

void wxDMClipboard::Destroy()
{
	WX_CLEAR_ARRAY( mContent );
}

wxDMNode* wxDMClipboard::DoPasteRecursive( wxDMNode* pNode, wxDMNode* pTarget, int depth )
{
	wxDMNode* pCopy = pNode->Clone( TRUE, FALSE );

	if ( mpHandler )

		mpHandler->HandleNodePasting( pCopy, pTarget, depth );

	pTarget->AddChild( pCopy );
	pTarget->NotifyAdded( pCopy );

	for( size_t i = 0; i != pNode->GetChildCount(); ++i )

		DoPasteRecursive( pNode->GetChild(i), pCopy, depth + 1 );

	return pCopy;
}

void wxDMClipboard::SetPasteHandler( wxDMPasteHandler* pHandler )
{
	mpHandler = pHandler;
}

wxDMPasteHandler* wxDMClipboard::GetPasteHandler()
{
	return mpHandler;
}
