/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: widgeteditor.cpp,v 1.41 2001/04/23 19:08:05 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas (alex@soften.ktu.lt)
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "minimal.cpp"
    #pragma interface "minimal.cpp"
#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 "widgeteditor.h"

#include "wx/choicdlg.h"
#include "wx/txtstrm.h"
#include "wx/wfstream.h"
#include "wx/log.h"

#include "wx/pmf/pm_undef.h"

#include "build.h"

#define USE_RESOURCE_PREVIEW 1

#if USE_RESOURCE_PREVIEW

	#define USE_WSPP 1

	#include "wx/xml/xmlres.h"

	#if USE_WSPP

		#include "wx/wspp/init.h"

	#endif

#endif

// X-Framework stuff
#include "XObject.h"
#include "XCellBackground.h"
#include "XSelectionEditor.h"
#include "XRectEditor.h"

#include "propedit.h"
#include "dmbrowser.h"
#include "gedthandler.h"
#include "myutils.h"

#include "labeleditdlg.h"

// sorry, 4 .cpp's and .h's of AWT-layouts contrib. needed to compile this .cpp!

#include "wx/awtlay/gridbaglayout.h"
#include "wx/awtlay/flowlayout.h"
#include "wx/awtlay/boxlayout.h"
#include "wx/awtlay/gridlayout.h"

/***** Implementation for class WidgetDocument *****/

IMPLEMENT_DYNAMIC_CLASS( WidgetDocument, wxPMDocument )

bool WidgetDocument::mUBufferLocked = FALSE;

WidgetDocument::WidgetDocument()

	: mpDMCmdProcessor( NULL ),
	  mpRootNode( NULL ),
	  mpRecord( NULL ),
	  mpRenderer( NULL ),
	  mInsertionOfs( 15,15 ),
	  mIdsHash( wxKEY_STRING )
{
#if USE_WSPP
	InitWSPP();
#endif

#if defined(__WINDOWS__)
	mUIType = "WINDOWS";
#elif defined(__WXGTK__)
	mUIType = "GTK";
#elif defined(__WXPM__)
	mUIType = "PM";
#elif defined(__WXMOTIF__)
	mUIType = "MOTIF";
#elif defined(__WXQT__)
	mUIType == "QT";
#elif defined(__WXMAC__)
	mUIType == "MAC";
#else
	mUIType == "UNKNOWN";
#endif

	mpRenderer = new wxUILookRenderer();

	wxString sd = wxPMBootSvc().GetStartupDir();

    wxString fname = sd + wxString(wxFILE_SEP_PATH) + "config_stdwlooks.wlk";

	if ( !mpRenderer->ParseScriptFromFile( fname ) )
	
		wxMessageBox( "Error while loading widget-looks from \"" + fname + "\"!" );
}

WidgetDocument::~WidgetDocument()
{
	if ( mpRootNode )

		delete mpRootNode;
}

bool WidgetDocument::CanHaveFile()
{
	return FALSE;
}

bool WidgetDocument::SavePMDocument()
{
	ResDocument& doc = *((ResDocument*)GetParentDocument());

	StoreNodesRecursively( mpRootNode, *mpRecord, doc.GetTemplateManager() );

	if ( IsModified() )

		// parent's data becomes dirty after child's modifications were saved
		GetParentDocument()->Modify( TRUE );

	return TRUE;
	// TBD::
}

bool WidgetDocument::SaveResToTempFile( const wxString& fname )
{
	ResDocument& doc = *((ResDocument*)GetParentDocument());

	StoreNodesRecursively( mpRootNode, *mpRecord, doc.GetTemplateManager() );

	wxString txt = "<?xml version=\"1.0\" generator=\"wxWorkshop\"?>\n<resource>\n";

	txt += mpRecord->GetXMLTextContent();

	txt += "\n</resource>";

	wxFileOutputStream fout( fname );
	wxTextOutputStream tout( fout );
	tout.SetMode( wxEOL_NATIVE );

	tout << txt;

	return TRUE;
}

void WidgetDocument::SetRootNode( wxDMNode* pRoot )
{
	mpRootNode = (WidgetNode*)pRoot;
}

wxDMNode* WidgetDocument::GetRootNode()
{
	return mpRootNode;
}

WidgetResTemplate* WidgetDocument::GetTemplateForNode( wxDMNode& node )
{
	WidgetNode& wnode = *(WidgetNode*)&node;

	return &wnode.GetTemplate();
}

ResTemplateManager& WidgetDocument::GetTemplateManager()
{
	ResDocument& doc = *((ResDocument*)GetParentDocument());

	return doc.GetTemplateManager();
}

bool WidgetDocument::LoadPMDocument()
{
	ResDocument& doc = *((ResDocument*)GetParentDocument());

	mpRecord = doc.GetCurrentRecord();

	if ( mpRootNode )
        delete mpRootNode;

	bool ok = LoadNodesRecursively( *mpRecord, 
								 NULL, NULL, doc.GetTemplateManager() );

	UpdateAllViews();

	return ok;
}

bool WidgetDocument::IsModified() const
{
	return mpRootNode->FindCmdProcessor()->CanUndo();
}

void WidgetDocument::Modify(bool modify)
{
	if ( !modify && !mUBufferLocked )
	
		mpRootNode->FindCmdProcessor()->ClearCommandBuffer();
}

WidgetNode* WidgetDocument::InsertWidgetByCode( int code, 
											    WidgetNode* pTarget, 
												const XPoint& pos,
												int zOrder )
{
	WidgetResTemplate* pTempl = GetTemplateManager().GetTemplates()[ code ];

	wxClassInfo* pClass = wxClassInfo::FindClass( 
								(char*)(const char*)pTempl->mNodeClass );

	WidgetNode* pNode = NULL;

	if ( pClass ) 
		pNode = (WidgetNode*)(pClass->CreateObject());

	if ( !( pClass && pNode ) )
	{
		wxMessageBox( "Cannot instantiate widget-node object of class" +
					  pTempl->mNodeClass );

		return FALSE;
	}

	pNode->mpTempl = pTempl;
	pNode->mpView = (WidgetView*)GetFirstView();

	pNode->mAttribGroups[ WidgetNode::WIDGET_PROPERTIES_GROUP ].
		SetGroupInfo( pTempl->mpWidgetAttrGroup );

	GetTemplateManager().
		InitGroupData( pTempl->mPropSet, 
					   pNode->mAttribGroups[ WidgetNode::WIDGET_PROPERTIES_GROUP ] );

	// assign extra-properties of parent to child upon insertion

	if ( pTarget->mpTempl->mpExtraAttrGroup )
	{
		pNode->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ].
			SetGroupInfo( pTarget->mpTempl->mpExtraAttrGroup );

		GetTemplateManager().
			InitGroupData( pTarget->mpTempl->mExtraPropSet, 
						   pNode->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ] );
	}

	pNode->mBounds.x = pos.x;
	pNode->mBounds.y = pos.y;

	// now obtain the natural (preferred) dimension of the inserted widget

	wxUILookInfo* pInfo = FindLookForNode( *pNode );

	if ( pInfo )
	{
		// HACK::
		wxClientDC dc( wxPMBootSvc().GetMainWindow() );

		wxString text; int x = 0, y = 0, w = 0, h = 0;

		mSolver.Init( pNode->GetAttrGroupInfo( WidgetNode::WIDGET_PROPERTIES_GROUP ),
					  pNode->GetAttrGroupData( (unsigned int)WidgetNode::WIDGET_PROPERTIES_GROUP ),
					  mUIType, &pNode->GetTemplate() );

		pNode->SetParent( pTarget );

		mpRenderer->CalcItemParams( *pInfo, wxSize(10,10), dc, text, x,y, w,h,
									pNode->GetFont(), pNode->GetBgColor(), pNode->GetFgColor(),
									&mSolver );

		pNode->mBounds.width  = w;
		pNode->mBounds.height = h;
	}
	else
	{
		//FIXME:: ??
		pNode->mBounds.width  = 30;
		pNode->mBounds.height = 30;
	}

	pNode->StoreBoundsInfo();

	wxString newId = GenerateUniqueId( *pNode );

	pNode->SetNodeAttrValue( "name", newId );

	pTarget->AddChild( pNode, zOrder );

	wxDC& dc = pNode->mpView->GetScene().GetCanvas();

	pNode->NotifyAdded( pNode );

	/*
	if ( pTarget->IsKindOf( CLASSINFO( SizerNodeBase ) ) )
	{
		((SizerNodeBase*)pTarget)->AddExtraPropertiesToChild( pNode );

		((WidgetNode*)&pNode->mpView->GetRootNode())->DoLayoutChildren( dc );
	} */

	return pNode;
}

bool WidgetDocument::FindInsertionPoint( XPoint& pos, WidgetNode*& pTarget )
{
	wxASSERT( mpRootNode ); // DBG::

	DMNodeListT lst;
	mpRootNode->GetSelection( lst );

	pos.x = 10000; pos.y = 10000; pTarget = NULL;

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		WidgetNode* pNode = (WidgetNode*)lst[i];

		XRect& bounds = ((WidgetNode*)lst[i])->GetBounds();

		pos.x = wxMin( pos.x, bounds.x );
		pos.y = wxMin( pos.y, bounds.y );
	}

	if ( lst.Count() > 1 )

		pTarget = (WidgetNode*)lst[0]->GetParent();
	else
	{
		if ( lst.Count() == 0 )
		{
			pos.x = mpRootNode->GetBounds().x;
			pos.y = mpRootNode->GetBounds().y;

			pTarget = mpRootNode;
		}
		else
		{
			WidgetNode* pNode = (WidgetNode*)lst[0];

			if ( pNode->mpTempl->mCanHaveChildren )
			{
				pos.x = pNode->GetBounds().x;
				pos.y = pNode->GetBounds().y;

				pTarget = pNode;
			}
			else
				pTarget = (WidgetNode*)pNode->GetParent();
		}
	}

	pos.x += mInsertionOfs.x;
	pos.y += mInsertionOfs.y;

	return TRUE;
}

wxUILookInfo* WidgetDocument::FindLookForNode( WidgetNode& node )
{
	mSolver.Init( node.GetAttrGroupInfo( WidgetNode::WIDGET_PROPERTIES_GROUP ),
				  node.GetAttrGroupData( (unsigned int)WidgetNode::WIDGET_PROPERTIES_GROUP ),
				  mUIType, &node.GetTemplate() );


	UILookInfoListT& lst = mpRenderer->GetInfoItems();
	EqSolverVar result;

	// first, try quick way by testing against "ClassHint" field of w-look

	const wxString& widgetClass = node.GetWidgetClass();

	size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		if ( widgetClass == lst[i]->mClassHint )
		{
			wxUILookInfo& info = *lst[i];

			info.mpStyleFilterExprNode->Evaluate( result, mSolver );

			if ( result.GetBool() == TRUE )

				return &info;
		}
	}

	// otherwise do full-scan of look-infos without class-hints,
	// executing everyone's filter-expression

	for( i = 0; i != lst.Count(); ++i )
	{
		wxUILookInfo& info = *lst[i];

		if ( info.mClassHint.Length() == 0 )
		{
			info.mpStyleFilterExprNode->Evaluate( result, mSolver );

			if ( result.GetBool() == TRUE )

				return &info;
		}
	}

	return NULL;
}

wxUILookRenderer& WidgetDocument::GetRenderer()
{
	return *mpRenderer;
}

const wxString& WidgetDocument::GetUIType()
{
	return mUIType;
}

void WidgetDocument::SetUIType( const wxString& uiType )
{
	mUIType = uiType;
}

void WidgetDocument::LockUndoBuffer( bool lock )
{
	mUBufferLocked = lock;
}

void WidgetDocument::MarkUsedId( const wxString& id )
{
	mIdsHash.Put( id, (wxObject*)this );
}

void WidgetDocument::MarkUnusedId( const wxString& id )
{
	mIdsHash.Delete( id );
}

wxString WidgetDocument::GenerateUniqueId( WidgetNode& forNode )
{
    int i;
	for( i = 1;; ++i )
	{
		wxString id = forNode.mpTempl->mTag + IntToStr( i );

		if ( mIdsHash.Get( id ) == NULL )

			return id;
	}
}

WidgetEqSolver& WidgetDocument::GetEqSolver()
{
	return mSolver;
}

/*** protected methods ***/

static void extract_int_pair( const wxString& s, int& i1, int& i2 )
{
	size_t i = 0; 
	for( ; i != s.Len(); ++i )

		if ( s[i] == ',' ) break;

	i1 = StrToInt( s.Mid( 0, i ) );
	i2 = StrToInt( s.Mid( i + 1, s.Len() - i - 1 ) );
}

void WidgetDocument::DoExtractBounds( Record& rec, wxRect& bounds )
{
	Record* pPos = (Record*)rec.FindField( "pos" );
	Record* pSize = (Record*)rec.FindField( "size" );

	int x = 0, y = 0, w = 0, h = 0;

	if ( pPos )
	
		extract_int_pair( pPos->GetStringValue(), x, y );

	if ( pSize )
	
		extract_int_pair( pSize->GetStringValue(), w, h );

	bounds.x = x; bounds.y = y;
	bounds.width = w;
	bounds.height = h;
}

bool WidgetDocument::LoadNodesRecursively( Record& rec, Record* pExtraPropsRec,
										   WidgetNode* pParent, 
										   ResTemplateManager& mgr,
										   bool isLinked,
										   wxRect* pBounds )
{
	if ( rec.GetName() == "object_ref" )
	{
		ResDocument& doc = *((ResDocument*)GetParentDocument());

		Record* pFound = doc.FindTopLevelItem( rec[ "name" ] );

		wxASSERT_MSG( pFound, "cannot resolve object_ref to " + rec[ "name" ] );

		wxRect bounds;

		DoExtractBounds( rec, bounds ); // extract <size> and <pos> parameters
										// which override the original ones
										// in the referenced object

		return LoadNodesRecursively( *pFound, pExtraPropsRec, pParent, mgr, 
									 TRUE, &bounds );
	}

	ResTemplateManager& templMgr = GetTemplateManager();

	WidgetResTemplate* pTempl = mgr.FindWidgetTemplateForClass( rec[ "class" ] );

	// widget-preview class is provided explicitely in the script,
	// here we try to find and instantiate it

	wxClassInfo* pClass = wxClassInfo::FindClass( 
								(char*)(const char*)pTempl->mNodeClass );

	WidgetNode* pNode = NULL;

	if ( pClass ) pNode = (WidgetNode*)(pClass->CreateObject());

	if ( !( pClass && pNode ) )
	{
		wxMessageBox( "Cannot instantiation widget-node object of class" +
					  pTempl->mNodeClass + " for tag " + rec.GetName() );

		return FALSE;
	}

	pNode->SetIsLinked( isLinked );

	pNode->mpTempl = pTempl;
	pNode->mpView = (WidgetView*)GetFirstView();

	pNode->mAttribGroups[ WidgetNode::WIDGET_PROPERTIES_GROUP ].
		SetGroupInfo( pTempl->mpWidgetAttrGroup );

	mgr.LoadGroupData( pTempl->mPropSet, rec,
					   pNode->mAttribGroups[ WidgetNode::WIDGET_PROPERTIES_GROUP ] );

	// layout-props record always comes from node's parent
	if ( pExtraPropsRec && pParent->mpTempl->mpExtraAttrGroup )
	{
		pNode->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ].
			SetGroupInfo( pParent->mpTempl->mpExtraAttrGroup );

		mgr.LoadGroupData( pParent->mpTempl->mExtraPropSet, *pExtraPropsRec,
						   pNode->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ] );
	}

	if ( !pParent )
	{
		// FIXME:: TBD:: commentds...
		pNode->SetNodeAttrValue( "x", "0" );
		pNode->SetNodeAttrValue( "y", "0" );
	}

	// retriev x,y,width,height, i.e. the "dynamic-properties" and 
	// store them to run-time mBounds structure for efficient access 
	// while layouting

	pNode->ExtractBoundsInfo();

	if ( pNode->mBounds.width == 0 )
	{
		int o=0;
		++o;
	}

	if ( pParent )

		pParent->AddChild( pNode );
	else
	{
		SetRootNode( pNode );

		// root node alaws contains command-processor

		mpDMCmdProcessor = new wxDMCommandProcessor();

		wxDMClipboard* pClip = &((ResDocument*)GetParentDocument())->GetClipboard();

		if ( !pClip->GetPasteHandler() )

			pClip->SetPasteHandler( new wxMyDMPasteHandler() );

		mpDMCmdProcessor->SetClipboard( pClip );

		pNode->SetCommandProcessor( mpDMCmdProcessor );

		mpDMCmdProcessor->SetRoot( pNode );
	}

	MarkUsedId( pNode->GetNodeAttrValue( "name" ) );

	if ( isLinked && pBounds )
	{
		// override original bounds of linked-object with the
		// ones which given within the linking-age block

		pNode->SetNodeAttrValue( "x",      IntToStr( pBounds->x ) );
		pNode->SetNodeAttrValue( "y",      IntToStr( pBounds->y ) );
		pNode->SetNodeAttrValue( "width",  IntToStr( pBounds->width  ) );
		pNode->SetNodeAttrValue( "height", IntToStr( pBounds->height ) );

		pNode->ExtractBoundsInfo();
	}

	wxString extraPropsTag;

	// scan child-fields for composite records, which
	// should normally describe item's children

    size_t i;
	for( i = 0; i != rec.GetFieldCount(); ++i )
	{
		wxString wClass;
		Record* pChildRec = (Record*)&rec.GetFieldByIndex(i);

		if ( rec.GetFieldByIndex(i).GetType() == FT_RECORD &&
			 rec.GetRecordByIndex(i).FindField( "class" ) )

			wClass = rec.GetRecordByIndex(i)[ "class" ];
			 
		// check if item is "wrapped up" into node which additionally
		// contains extra-properties that were assigned by item's parent

		if ( wClass.Length() && wClass == pNode->mpTempl->mExtraPropsTagName )
		{
			Record& extraPropsRec = rec.GetRecordByIndex(i);

			// now see if there are other nested widgets
			// enclosed within extra-props record (eg. within "sizer-item")

            size_t k;
			for( k = 0; k != extraPropsRec.GetFieldCount(); ++k )
			{
				Record& childRec = extraPropsRec.GetRecordByIndex(k);

				if ( extraPropsRec.GetFieldByIndex(k).GetType() == FT_RECORD )
				{
					if ( childRec.FindField( "class" ) &&
					     templMgr.IsKnownClass( childRec[ "class" ] ) )
					{
						if ( !LoadNodesRecursively( childRec, 
													&extraPropsRec, pNode, mgr ) )

							return FALSE;
					}
					else
					if ( childRec.GetName() == "object_ref" )
					{
						// FIXME:: eliminate this cut&paste!

						ResDocument& doc = *((ResDocument*)GetParentDocument());

						Record* pFound = doc.FindTopLevelItem( childRec[ "name" ] );

						wxASSERT_MSG( pFound, "cannot resolve object_ref to " + childRec[ "name" ] );

						wxRect bounds;

						DoExtractBounds( childRec, bounds ); // extract <size> and <pos> parameters
														// which override the original ones
														// in the referenced object

						if ( !LoadNodesRecursively( *pFound, &extraPropsRec, pNode, mgr, 
													 TRUE, &bounds ) )

								return FALSE;
					}
				}
			}
		}
		else
		if ( templMgr.IsKnownClass( wClass ) || 
			 pChildRec->GetName() == "object_ref"
		   )
		{
			if ( !LoadNodesRecursively( rec.GetRecordByIndex(i), 
										NULL, // no extra-properties
										pNode, mgr ) 
			   )
				return FALSE;
		}
	}

	return TRUE;
}

bool WidgetDocument::StoreNodesRecursively( WidgetNode* pNode, Record& rec,
											ResTemplateManager& mgr )
{
	Record* pRec = NULL;

	// if there any extra-properties (assigned by parent, eg. 
	// sizing-parameters to each child of BoxSizer) attached to 
	// the item, then it's parent will be record containg these properties
	// as fields, and the actual item-record as it's child

	if ( pNode->HasExtraProperties() && !pNode->IsLinked() )
	{
		// create parent container-node which will enclose
		// the child togeather with extra-properties

		WidgetResTemplate& parentTempl = *((WidgetNode*)pNode->GetParent())->mpTempl;

		rec.SetName( "object" );
		rec.AddStringField( "class", parentTempl.mExtraPropsTagName );

		mgr.StoreGroupData( parentTempl.mExtraPropSet, rec, 
							pNode->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ] );

		pRec = new Record();

		rec.AddField( pRec ); 
	}
	else
		pRec = &rec; // otherwise store child's properties to the given node

	WidgetResTemplate& templ = *pNode->mpTempl;

	pRec->RemoveAllFields();
	pRec->SetName( "object" );
	pRec->AddStringField( "class", templ.mWidgetClass );

	int prevX = pNode->mBounds.x;
	int prevY = pNode->mBounds.y;

	if ( pNode->GetParent() == NULL || pNode->IsLinked() )
	{
		// FIXME:: TBD:: comments...

		pNode->mBounds.x = 0;
		pNode->mBounds.y = 0;
	}

	// store values of mBounds to  x,y,width,height "dynamic" properties 
	pNode->StoreBoundsInfo();

	mgr.StoreGroupData( templ.mPropSet, *pRec, 
						pNode->mAttribGroups[ WidgetNode::WIDGET_PROPERTIES_GROUP ] );

    size_t i;
	for( i = 0; i != pNode->GetChildCount(); ++i )
	{
		Record* pChildRec = new Record();
		pRec->AddField( pChildRec );

		WidgetNode* pChildNode = (WidgetNode*)pNode->GetChild(i);

		if ( pChildNode->IsLinked() )
		{
			ResDocument& doc = *((ResDocument*)GetParentDocument());

			wxString name = pChildNode->GetNodeAttrValue( "name" );

			Record* pFound = doc.FindTopLevelItem( name );

			if ( !pFound )
			{
				wxMessageBox( "WARNING: object_ref node with name = " + name + " was not found!\n Skipped saving it.." );

				continue;
			}

			if ( pChildNode->HasExtraProperties() )
			{
				// FIXME:: eliminate this cut&paste

				// create parent container-node which will enclose
				// the child together with extra-properties

				WidgetResTemplate& parentTempl = *((WidgetNode*)pChildNode->GetParent())->mpTempl;

				(*pChildRec).SetName( "object" );
				(*pChildRec).AddStringField( "class", parentTempl.mExtraPropsTagName );

				mgr.StoreGroupData( parentTempl.mExtraPropSet, *pChildRec, 
									pChildNode->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ] );

				
				Record* pInnerRec = new Record();

				(*pChildRec).AddField( pInnerRec ); 

				pChildRec = pInnerRec;
			}

			pChildRec->SetName( "object_ref" );
			pChildRec->AddStringField( "name", name );

			// store overriden bounds info for the linked object

			XRect& bounds = pChildNode->GetBounds();

			pChildRec->AddField( new Record( "pos",
								 IntToStr( bounds.x ) + "," + 
								 IntToStr( bounds.y ) )
							   );
								 
			pChildRec->AddField( new Record( "size",
								 IntToStr( bounds.width ) + "," + 
								 IntToStr( bounds.height ) )
							   );

			pFound->RemoveAllFields();

			doc.Modify( TRUE );

			StoreNodesRecursively( pChildNode, *pFound, mgr );
		}
		else
			StoreNodesRecursively( pChildNode, *pChildRec, mgr );
	}

	if ( pNode->GetParent() == NULL || pNode->IsLinked() )
	{
		// FIXME:: TBD:: comments...

		pNode->mBounds.x = prevX;
		pNode->mBounds.y = prevY;
	
		pNode->StoreBoundsInfo();
	}

	return TRUE;
}

/***** Implementation for class WidgetView *****/

static wxCommandInfo _gCanvasCommands[] = {

	wxCommandInfo( WidgetView::ID_SET_UI_TYPE, "Set UI-&Type...", "", "Ctrl+T" ), 
	wxCommandInfo( WidgetView::ID_SET_UNITS, "Set &Units...", "", "" ), 
	wxCommandInfo( WidgetView::ID_SET_SIZER, "Set &Sizer...", "", "" ), 
	wxCommandInfo( -1, "SEPARATOR" ), 

	wxCommandInfo( WidgetView::ID_INSERT_WIDGET, "&Insert Widget...", "", "" ), 
	wxCommandInfo( WidgetView::ID_GRID_SETTINGS, "&Grid Settings...", "", "Ctrl+G" ), 
	wxCommandInfo( WidgetView::ID_RELOAD_WIDGET_LOOKS, "&Reload widget looks", "", "" ), 

#if USE_RESOURCE_PREVIEW
	
	wxCommandInfo( WidgetView::ID_PREVIEW, "&Preview...", "", "Ctrl+P" ), 

#endif

	wxCommandInfo( -1, "SEPARATOR" ), 
	wxCommandInfo( WidgetView::ID_SHOW_SIZERS, "&Highlight all sizers", "", "", TRUE, FALSE ), 

	wxCommandInfo( 0, "" ) // terminator
};

static wxCommandInfo _gItemCommands[] = {

	wxCommandInfo( WidgetView::ID_ESTABLISH_AS_LINK,  "&Establish as Link", "", "" ), 
	wxCommandInfo( WidgetView::ID_INSERT_LINKED_ITEM, "Insert Link to Item...", "", "" ), 
	wxCommandInfo( WidgetView::ID_SELECT_PARENT,      "Select Parent", "", "Ctrl+PRIOR" ), 
	wxCommandInfo( -1, "SEPARATOR" ), 

	wxCommandInfo( WidgetView::ID_MOVE_FORWARD, "Move &forward", "", "Ctrl+RIGHT" ), 
	wxCommandInfo( WidgetView::ID_MOVE_BACKWARD, "Move &backward", "", "Ctrl+LEFT" ), 
	wxCommandInfo( -1, "SEPARATOR" ), 

	wxCommandInfo( WidgetView::ID_INC_BORDER, "&Increase border", "", "Ctrl+ADD" ), 
	wxCommandInfo( WidgetView::ID_DEC_BORDER, "&Decrease border", "", "Ctrl+SUBTRACT" ), 
	wxCommandInfo( -1, "SEPARATOR" ), 

	wxCommandInfo( WidgetView::ID_ALIGN_LEFT, "Align &left (top)", "", "Ctrl+1" ), 
	wxCommandInfo( WidgetView::ID_ALIGN_RIGHT, "Align &center)", "", "Ctrl+2" ), 
	wxCommandInfo( WidgetView::ID_ALIGN_CENTER, "Align &right (bottom)", "", "Ctrl+3" ), 
	wxCommandInfo( WidgetView::ID_EXPAND, "&Expand", "", "Ctrl+4" ), 

	wxCommandInfo( 0, "" ) // terminator
};

wxDMClipboard* WidgetView::mpClipboard = NULL;

IMPLEMENT_DYNAMIC_CLASS( WidgetView, wxPMView )

BEGIN_EVENT_TABLE( WidgetView, wxPMView )

	EVT_COMMAND_RANGE( XGenericEditorHandler::FILE_OPEN,
					   XGenericEditorHandler::LAYOUT_FLIP,
					   wxEVT_COMMAND_MENU_SELECTED,
					   WidgetView::OnEditorCommandsRange )

	EVT_COMMAND_RANGE( XGenericEditorHandler::FILE_OPEN,
					   XGenericEditorHandler::LAYOUT_FLIP,
					   wxEVT_UPDATE_UI,
					   WidgetView::OnEditorCommandsRange )

	EVT_COMMAND_RANGE( ID_INSERT_WIDGET_IDS_START,
					   ID_INSERT_WIDGET_IDS_END,
					   wxEVT_COMMAND_MENU_SELECTED,
					   WidgetView::OnInsertWidgetCommandsRange )

	EVT_MENU( ID_SET_UI_TYPE, WidgetView::OnSetUIType )
	EVT_MENU( ID_SET_UNITS, WidgetView::OnSetUnits )
	EVT_MENU( ID_SET_SIZER, WidgetView::OnSetSizer )
	EVT_MENU( ID_GRID_SETTINGS, WidgetView::OnGridSettings )
	EVT_MENU( ID_INSERT_WIDGET, WidgetView::OnInsertWidget )
	EVT_MENU( ID_PREVIEW, WidgetView::OnPreview )
	EVT_MENU( ID_RELOAD_WIDGET_LOOKS, WidgetView::OnReloadWigetLooks )
	EVT_MENU( ID_SHOW_SIZERS, WidgetView::OnShowSizers )

	EVT_MENU( ID_INSERT_LINKED_ITEM, WidgetView::OnInsertLinkedItem )
	EVT_MENU( ID_ESTABLISH_AS_LINK,  WidgetView::OnEstablishAsLink  )

	EVT_MENU( ID_SELECT_PARENT, WidgetView::OnItemSelectParent )
	EVT_MENU( ID_MOVE_FORWARD, WidgetView::OnItemMoveForward )
	EVT_MENU( ID_MOVE_BACKWARD, WidgetView::OnItemMoveBackward )
	EVT_MENU( ID_INC_BORDER, WidgetView::OnItemIncBorder )
	EVT_MENU( ID_DEC_BORDER, WidgetView::OnItemDecBorder )
	EVT_MENU( ID_ALIGN_LEFT, WidgetView::OnItemAlignLeft )
	EVT_MENU( ID_ALIGN_RIGHT, WidgetView::OnItemAlignCenter )
	EVT_MENU( ID_ALIGN_CENTER, WidgetView::OnItemAlignRight )
	EVT_MENU( ID_EXPAND, WidgetView::OnItemExpand )

	EVT_KEY_DOWN( WidgetView::OnKeyDown )

END_EVENT_TABLE()

WidgetView::WidgetView()

	: mpScene( NULL ),
	  mpEditorHandler( NULL ),
	  mpXCmdMgr( NULL ),
	  mpRootNode( NULL ),
	  mUseDialogUnits( FALSE ),
	  mSizersShown( FALSE ),
	  mSnapSpacing( 5,5 ),
	  mGridSpacing( 10,10 ),
	  mGridType( GRID_NONE ),
	  mpWidgetMenus( NULL ),
	  mpMenuChains( NULL ),
	  mpPropPane( NULL )
{
	mpEditorHandler = new XGenericEditorHandler();
}

WidgetView::~WidgetView()
{
	delete mpEditorHandler;

//	delete mpMenuChains;  // glt

	if ( mpScene ) 
	{
		delete mpScene;
		delete mpXCmdMgr;
		GetViewFrame()->PopEventHandler();
	}
}

XCollection& WidgetView::GetScene()
{
	return *mpScene;
}

void WidgetView::SetDialogsUnits( bool on )
{
	mUseDialogUnits = on;
}

bool WidgetView::UseDialogUnits()
{
	return mUseDialogUnits;
}

WidgetGlyph& WidgetView::GetGlyphForNode( wxDMNode& node )
{
	WidgetGlyph* pGlyph = (WidgetGlyph*)mGlyphHash.Get( (long)&node );

	wxASSERT( pGlyph ); // DBG::

	return *pGlyph;
}

WidgetGlyph* WidgetView::FindGlyphForNode( wxDMNode& node )
{
	return (WidgetGlyph*)mGlyphHash.Get( (long)&node );
}

WidgetNode& WidgetView::GetRootNode()
{
	return *mpRootNode;
}

void WidgetView::RelayoutScene()
{
	mpRootNode->DoLayoutChildren( mpScene->GetCanvas() );
}

ResTemplateManager& WidgetView::GetTemplateManager()
{
	WidgetDocument* pDoc = (WidgetDocument*)GetDocument();

	return pDoc->GetTemplateManager();
}

WidgetDocument& WidgetView::GetWidgetDoc()
{
	return *(WidgetDocument*)GetDocument();
}

void WidgetView::SetGridType( int type )
{
	mGridType = type;
}

void WidgetView::GetTopLevelWidgetOrigin( int* x, int* y )
{
	*x = mpRootNode->mBounds.x;
	*y = mpRootNode->mBounds.y;
}

double WidgetView::GetVerticalPixelsPerUnitRatio()
{
	return 1.0;
}

double WidgetView::GetHorizontalPixelsPerUnitRatio()
{
	return 1.0;
}

void WidgetView::PixelRectToUnitRect( XRect& pixelRect, XRect& unitRect )
{
	int ox = 0, oy = 0;
	GetTopLevelWidgetOrigin( &ox, &oy );

	int relX = pixelRect.x - ox;
	int relY = pixelRect.y - oy;

	unitRect.x = PixelsToUnits( pixelRect.x - ox );
	unitRect.y = PixelsToUnits( pixelRect.y - oy );
	unitRect.width  = PixelsToUnits( pixelRect.width  );
	unitRect.height = PixelsToUnits( pixelRect.height );
}

void WidgetView::UnitRectToPixelRect( XRect& unitRect, XRect& pixelRect )
{
	int ox = 0, oy = 0;
	GetTopLevelWidgetOrigin( &ox, &oy );

	pixelRect.x = UnitsToPixels( unitRect.x ) + ox;
	pixelRect.y = UnitsToPixels( unitRect.y ) + oy;

	pixelRect.width  = UnitsToPixels( unitRect.width );
	pixelRect.height = UnitsToPixels( unitRect.height );
}

int WidgetView::AdjustToSnappingX( int units )
{
	return ( units / mSnapSpacing.x ) * mSnapSpacing.x;
}

int WidgetView::AdjustToSnappingY( int units )
{
	return ( units / mSnapSpacing.x ) * mSnapSpacing.y;
}

int WidgetView::GetGridType()
{
	return mGridType;
}

wxSize& WidgetView::GetGridSpacinig() { return mGridSpacing; }

wxSize& WidgetView::GetSnapSpacing() { return mSnapSpacing; }

void WidgetView::AdjustRectToGrid( XRect& rect, WidgetGlyph& forGlyph )
{
	XRect lbounds = !forGlyph.GetParentGlyph() ? forGlyph.GetBounds()
											   : forGlyph.GetParentGlyph()->GetLimitingBounds();

	rect.x = lbounds.x + AdjustToSnappingX( rect.x - lbounds.x );
	rect.y = lbounds.y + AdjustToSnappingY( rect.y - lbounds.y );
	rect.width  = AdjustToSnappingX( rect.width );
	rect.height = AdjustToSnappingY( rect.height );

	/*
	// OLD STUFF::
	int ox = 0, oy = 0;
	GetTopLevelWidgetOrigin( &ox, &oy );

	int relX = rect.x - ox;
	int relY = rect.y - oy;

	rect.x = ox + UnitsToPixels( AdjustToSnapping( PixelsToUnits( relX ) ) );
	rect.y = oy + UnitsToPixels( AdjustToSnapping( PixelsToUnits( relY ) ) );

	rect.width  = UnitsToPixels( AdjustToSnapping( PixelsToUnits( rect.width  ) ) );
	rect.height = UnitsToPixels( AdjustToSnapping( PixelsToUnits( rect.height ) ) );
	*/
}

int WidgetView::PixelsToUnits( int pixels )
{
	return pixels;
}

int WidgetView::UnitsToPixels( int units )
{
	return units;
}

void WidgetView::OnActivateView(bool activate, wxView *activeView, 
									wxView *deactiveView)
{
	if ( activate && mpRootNode != NULL )
	{
		if ( !mpPropPane )
		{
			wxPMView::OnActivateView( activate, activeView, deactiveView );

			wxPMService* pSvc = wxPMBootSvc().FindServiceByClass( 
					CLASSINFO( wxDMNodePropertyPanel ) );

			if ( !pSvc )
                return;

			mpPropPane = (wxDMNodePropertyPanel*)pSvc->GetWxObject();

			GetRootNode().FindCmdProcessor()->AddListener( mpPropPane->GetGroupEditor() );

			// auto-select the root-node

			DMNodeListT sel;
			sel.Add( mpRootNode );
			mpRootNode->SetSelection( sel );
			mpRootNode->SetFocused( TRUE );

			wxDMNodePropertyGroupEditor* pEditor = mpPropPane->GetGroupEditor();
		}

		//pEditor->Update( &GetRootNode() );
	}

	wxPMView::OnActivateView( activate, activeView, deactiveView );
}

void WidgetView::OnUpdate( wxView* sender, wxObject* hint )
{
	mpRootNode = (WidgetNode*)((WidgetDocument*)GetDocument())->GetRootNode();

	if ( !mpRootNode )
        return;

	mpXCmdMgr->SetCommandProcessor( mpRootNode->FindCmdProcessor() );

	RecreateScene();

	GetRootNode().FindCmdProcessor()->AddListener( this );
}

void WidgetView::RemoveGlyphsForNode( WidgetNode& node )
{
	WidgetGlyph* pGlyph = FindGlyphForNode( node );

	if ( pGlyph )
	{
		mGlyphHash.Delete( (long)&node );

		// FIXME:: why not found???

		if ( mpScene->GetContents().Index( pGlyph ) != wxNOT_FOUND )
		{
			mpScene->Remove( pGlyph, FALSE );
		}
		else
			wxLogMessage( "DBG:: mpScene->Remove( pGlyph, FALSE ); failed!!" );
	}

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

		RemoveGlyphsForNode( *((WidgetNode*)node.GetChild(i)) );
}

void WidgetView::CreateGlyphsForNode( WidgetNode& node )
{
	XObjListT& lst = mpScene->GetContents();

	CreateGlyphsRecursive( lst, node );
}

void WidgetView::RecreateScene()
{
	mpScene->SetFocus( NULL, FALSE );
	mpScene->ResetSelection();

	mGlyphHash.Clear();

	XObjListT& lst = mpScene->GetContents();

	XObject* pBg = mpScene->GetBackgroundObj();

	mpScene->Detach( pBg );

	WX_CLEAR_ARRAY( lst );

	mpScene->Insert( pBg );

	CreateGlyphsRecursive( lst, *mpRootNode );

	InvalidateLooks();

	ReorderAllGlyphs();

	RelayoutScene();

	mpScene->RefreshNow();
}

bool WidgetView::OnCreate( wxDocument* doc, long flags )
{
	if ( !wxPMView::OnCreate( doc, flags ) )
        return FALSE;

	wxConfig *config = new wxConfig( PROGRAM_NAME );
	
	if ( config && config->Read( "GridType", &mGridType ) )
	{
		config->Read( "GridSnappingX", &mSnapSpacing.x );
		config->Read( "GridSnappingY", &mSnapSpacing.y );
		config->Read( "GridSpacingX",  &mGridSpacing.x );
		config->Read( "GridSpacingY",  &mGridSpacing.y );

		if ( mGridType > GRID_LAST_TYPE )

			mGridType = 0;
	}

	if ( config )

		delete config;

	wxASSERT( doc->IsKindOf( CLASSINFO(WidgetDocument) ) ); // DBG::

	mpScene = new MyXCollection( this );

	mpScene->SetCanvasOrigin( XPoint(30,30) );

	XCellBackground* pBg = 
		new XCellBackground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOW ) );

	mpScene->Insert( pBg );

	XSelectionEditor* pEditor = new XSelectionEditor();
	mpScene->SetEditor( pEditor );

	mpScene->MarkBackgroundObj( *pBg );

	mpXCmdMgr = new MyXCommandMgr( NULL, mpScene, this );
	
	XwxWindowHook* pHook = new XwxWindowHook( GetViewFrame(), mpScene );

	mpEditorHandler->SetLayoutUtility( new XLayoutUtility() );
	mpEditorHandler->SetCurrentScene( *mpScene );
	mpEditorHandler->SetCurrentCmdManager( *mpXCmdMgr );

	return TRUE;
}

wxPMMergableMenuChain* WidgetView::GetMergableMenuChain()
{
	if ( !mpMenuChains )

		RecreateMenuChains();

	return mpMenuChains;
}

void WidgetView::OnEditorCommandsRange( wxCommandEvent& event )
{
	mpEditorHandler->ProcessEvent( event );
}

void WidgetView::OnInsertWidgetCommandsRange( wxCommandEvent& event )
{
	size_t widgetNo = event.m_id - ID_INSERT_WIDGET_IDS_START;


	WidgetInsertionEditor* pEditor = new WidgetInsertionEditor( this, widgetNo );

	/*
	XPoint pos; WidgetNode* pTarget = NULL;

	GetWidgetDoc().FindInsertionPoint( pos, pTarget );
	GetWidgetDoc().InsertWidgetByCode( widgetNo, pTarget, pos );

	mpScene->RefreshNow();
	*/
}

void WidgetView::OnSetUIType( wxCommandEvent& event )
{
	wxString choices[6] = { "WINDOWS", "GTK", "PM", "MOTIF", "QT", "MAC" };

	wxSingleChoiceDialog dlg( wxPMBootSvc().GetMainWindow(), "Types:", 
							  "Set UI-Type", 6, choices );

	wxString type = GetWidgetDoc().GetUIType();

    size_t i;
	for( i = 0; i != 6; ++i )
    {
		if ( choices[i] == type ) 
		{
			dlg.SetSelection( i ); break;
		}
    }

	if ( dlg.ShowModal() == wxID_OK )
	{
		GetWidgetDoc().SetUIType( dlg.GetStringSelection() );

		RecreateScene();
	}
}

void WidgetView::OnSetUnits( wxCommandEvent& event )
{
	wxString choices[2] = { "Dialog Units", "Pixels" };

	wxSingleChoiceDialog dlg( wxPMBootSvc().GetMainWindow(), "Unit types:", 
							  "Set Unit", 2, choices );

	if ( dlg.ShowModal() == wxID_OK )
	{
		mUseDialogUnits = dlg.GetStringSelection() == "Dialog Units";

		RecreateScene();
	}
}

void WidgetView::OnSetSizer( wxCommandEvent& event )
{
	wxMessageBox( "FOR NOW:: use menu \"Insert | wxBoxSizer\"" );
}

void WidgetView::OnGridSettings( wxCommandEvent& event )
{
	GridSettingsDialog dlg( wxPMBootSvc().GetMainWindow(), 
							mGridType, mGridSpacing, mSnapSpacing );

	if ( dlg.ShowModal() == wxID_OK )
	{
		mGridType = dlg.GetGridType();
		mGridSpacing = dlg.GetGridSpacinig();
		mSnapSpacing = dlg.GetSnapSpacing();

		wxConfig *config = new wxConfig( PROGRAM_NAME );
		
		if ( config  )
		{
			config->Write( "GridType",  (long)mGridType );
			config->Write( "GridSnappingX", (long)mSnapSpacing.x );
			config->Write( "GridSnappingY", (long)mSnapSpacing.y );
			config->Write( "GridSpacingX",  (long)mGridSpacing.x );
			config->Write( "GridSpacingY",  (long)mGridSpacing.y );

			delete config;
		}

		RefreshScene();
	}
}

void WidgetView::OnInsertWidget( wxCommandEvent& event )
{
	wxMessageBox( "OnInsertWidget" );
}

static wxString _gLastPreviewFrameId = "_asdfasd7_";

class MyPreviewDialog : public wxDialog
{
public:
	MyPreviewDialog( wxWindow* parent, wxWindowID id, const wxString& title, 
					 const wxPoint& pos = wxDefaultPosition, 
					 const wxSize& size = wxDefaultSize, 
					 long style = wxDEFAULT_DIALOG_STYLE, 
					 const wxString& name = "dialogBox" )
		: wxDialog( parent, id, title, pos, size, style, name )
	{}

	void OnSize( wxSizeEvent& event )
	{
		if ( GetChildren().GetCount() == 1 )
		{
			int w = 0, h = 0;
			GetClientSize( &w, &h );

			wxWindow* wnd = (wxWindow*)GetChildren().First()->GetData();

			wnd->SetSize(w,h);
			wnd->Layout();
			this->Refresh();
		}
	}

	DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE( MyPreviewDialog, wxDialog )

	EVT_SIZE( MyPreviewDialog::OnSize )

END_EVENT_TABLE()

void WidgetView::OnPreview( wxCommandEvent& event )
{
#if USE_RESOURCE_PREVIEW

	//wxString fname = "tempres777.xrc";
	//GetWidgetDoc().SaveResToTempFile( fname );

	WidgetDocument::LockUndoBuffer( TRUE );

	wxPMBootSvc().GetDocManager()->SaveDocument( GetWidgetDoc().GetParentDocument() );

	WidgetDocument::LockUndoBuffer( FALSE );

    wxTheXmlResource->InitAllHandlers();

	wxTheXmlResource->UnloadAll();
    wxTheXmlResource->Load( GetWidgetDoc().GetParentDocument()->GetFileInfo()->GetPath() );
	wxTheXmlResource->UpdateResources();
	wxTheXmlResource->UpdateResources();
	wxTheXmlResource->UpdateResources();

	WidgetNode* pRoot = &GetRootNode();

	wxString id = pRoot->GetNodeAttrValue( "name" );

	wxString wTag = pRoot->GetTemplate().mTag;
	wxString wClass = pRoot->GetNodeAttrValue( "subclass" );

	if ( !wClass.Length() )

		wClass = pRoot->GetTemplate().mBasedOnClass;

	wxTheXmlResource->SetPreviewMode( TRUE );

	if ( wTag == "frame" )
	{
		//if ( ::wxFindWindowByName( _gLastPreviewFrameId ) )

		//	::wxFindWindowByName( _gLastPreviewFrameId )->Destroy();

		//wxFrame* pFrame = new wxFrame();
		
		wxTheXmlResource->LoadWindow( id, TRUE );

		_gLastPreviewFrameId = id;

		//pFrame->Center( wxBOTH );
		//pFrame->Show(TRUE);
	}
	else
	if ( wTag == "dialog" )
	{
		wxWindow* wnd = wxTheXmlResource->LoadWindow( wxPMBootSvc().GetMainWindow(), id );

		if ( wnd )
		{
			wnd->Layout();
			wnd->Center(wxBOTH);

			((wxDialog*)wnd)->ShowModal();
		}
	}
	else
	{
		wxSize sz( StrToInt( pRoot->GetNodeAttrValue( "width" ) ),
				   StrToInt( pRoot->GetNodeAttrValue( "height" ) ) );

		wxString subTitle = "'";

		if ( wClass.Length() )
			subTitle += " (subclass " + wClass + ")";

		MyPreviewDialog dlg( wxPMBootSvc().GetMainWindow(), -1, 
							 "Preview of '" + id + subTitle, 
							 wxDefaultPosition, sz,
							 wxSYSTEM_MENU | wxRESIZE_BORDER | wxRESIZE_BOX );

		dlg.SetClientSize( sz.x, sz.y );
		wxTheXmlResource->LoadWindow( &dlg, id, FALSE );
		dlg.SetClientSize( sz.x+1, sz.y+1 );

		dlg.Layout();
		dlg.Center( wxBOTH );
		dlg.ShowModal();

		//wxMessageBox( "Cannot preview resource of class " + pRoot->GetTemplate().mWidgetClass );
	}

	wxTheXmlResource->SetPreviewMode( FALSE );

#endif // USE_RESOURCE_PREVIEW
}

void WidgetView::OnReloadWigetLooks( wxCommandEvent& event )
{
	wxMessageBox( "OnReloadWigetLooks" );
}

void WidgetView::OnShowSizers( wxCommandEvent& event )
{
	wxMenuBar* pBar = wxPMBootSvc().GetMainWindow()->GetMenuBar() ;

	// m_callbackUserData holds the orignal item-id (not affected
	// by PMF's transaltions

	if ( pBar->IsChecked( event.m_commandInt ) )
	
		mSizersShown = TRUE;
	else
		mSizersShown = FALSE;

	mpScene->InvalidateRect( GetGlyphForNode( *mpRootNode ).GetBounds() );

	mpScene->RefreshNow();
}

bool WidgetView::SizersShown()
{
	return mSizersShown;
}

WidgetNode* WidgetView::FindFocus()
{
	return (WidgetNode*)GetWidgetDoc().GetRootNode()->FindFocus();
}

// any-item related operations

void WidgetView::OnInsertLinkedItem( wxCommandEvent& event )
{
	ResDocument& doc = *((ResDocument*)GetWidgetDoc().GetParentDocument());

	wxString itemId = doc.SelectItem( "Insert item to link" );

	if ( itemId.Len() )
	
		WidgetInsertionEditor* pEditor = new WidgetInsertionEditor( this, itemId );
}

void WidgetView::OnEstablishAsLink( wxCommandEvent& event )
{
	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();

	if ( !pNode ) 
		return;

	pGlyph->OnStartEdit();

	Record* pRec = new Record();

	pNode->SetIsLinked( TRUE );

	GetWidgetDoc().StoreNodesRecursively( pNode, *pRec, GetTemplateManager() );

	pNode->SetIsLinked( TRUE );

	ResDocument& doc = *((ResDocument*)GetWidgetDoc().GetParentDocument());

	doc.GetRootRecord()->AddField( pRec );

	pNode->FindCmdProcessor()->SetChangeSender( this );

	pGlyph->OnFinishEdit();

	doc.Modify( TRUE );

	wxPMBootSvc().SetStatusText( "Item " + pNode->GetNodeAttrValue("name") +
								 " was established as link" );
}

// sizer-item related operations

void WidgetView::OnItemSelectParent( wxCommandEvent& event )
{
	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	if ( !pGlyph )
		return;

	wxDMNode* pParent = pGlyph->GetNode().GetParent();

	if ( pParent )
	{
		DMNodeListT lst; lst.Add( pParent );

		pGlyph->GetNode().SetSelection( lst );
		pParent->SetFocused(TRUE);
	}
}

void WidgetView::OnItemMoveForward( wxCommandEvent& event )
{
	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();
	if ( !pNode || !pNode->HasExtraProperties() )
		return;

	if ( pNode->GetOrderNo() != pNode->GetParent()->GetChildCount() - 1 )
	{
		pNode->NotifyWillChange( pNode );
		pNode->SetOrderNo( pNode->GetOrderNo() + 1 );

		RelayoutScene();
		pGlyph->GetEditor().NotifyObjectChanged();
		pGlyph->RefreshNow();
	}
}

void WidgetView::OnItemMoveBackward( wxCommandEvent& event )
{
	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();
	if ( !pNode || !pNode->HasExtraProperties() )
		return;

	if ( pNode->GetOrderNo() != 0 )
	{
		pNode->NotifyWillChange( pNode );
		pNode->SetOrderNo( pNode->GetOrderNo() - 1 );

		RelayoutScene();
		pGlyph->GetEditor().NotifyObjectChanged();
		pGlyph->RefreshNow();
	}
}

void WidgetView::OnItemIncBorder( wxCommandEvent& event )
{
	// FIXME:: cut&paste's...............

	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();
	if ( !pNode || !pNode->HasExtraProperties() )
		return;

	pNode->NotifyWillChange( pNode );
	int b = StrToInt( pNode->GetNodeExtraAttrValue( "border" ) );
	pNode->SetNodeExtraAttrValue( "border", IntToStr( b + 5 ) );
	RelayoutScene();
	pGlyph->GetEditor().NotifyObjectChanged();
	pGlyph->RefreshNow();
}

void WidgetView::OnItemDecBorder( wxCommandEvent& event )
{
	// FIXME:: cut&paste's...............

	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();
	if ( !pNode || !pNode->HasExtraProperties() )
        return;

	pNode->NotifyWillChange( pNode );
	int b = StrToInt( pNode->GetNodeExtraAttrValue( "border" ) );
	b -= 5;
    if ( b < 0 )
        b = 0;
	pNode->SetNodeExtraAttrValue( "border", IntToStr( b ) );
	RelayoutScene();
	pGlyph->GetEditor().NotifyObjectChanged();
	pGlyph->RefreshNow();
}

void WidgetView::OnItemAlignLeft( wxCommandEvent& event )
{
	// FIXME:: cut&past's...............

	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();
	if ( !pNode || !pNode->HasExtraProperties() )
		return;

	pNode->NotifyWillChange( pNode );
	pNode->SetNodeExtraAttrValue( "isExpandable", "false" );
	pNode->SetNodeExtraAttrValue( "align", "wxALIGN_LEFT" );
	RelayoutScene();
	pGlyph->GetEditor().NotifyObjectChanged();
	pGlyph->RefreshNow();
}

void WidgetView::OnItemAlignCenter( wxCommandEvent& event )
{
	// FIXME:: cut&paste's...............

	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();
	if ( !pNode || !pNode->HasExtraProperties() )
		return;

	pNode->NotifyWillChange( pNode );
	pNode->SetNodeExtraAttrValue( "isExpandable", "false" );
	pNode->SetNodeExtraAttrValue( "align", "wxALIGN_CENTER" );
	RelayoutScene();
	pGlyph->GetEditor().NotifyObjectChanged();
	pGlyph->RefreshNow();
}

void WidgetView::OnItemAlignRight( wxCommandEvent& event )
{
	// FIXME:: cut&paste's...............

	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();
	if ( !pNode || !pNode->HasExtraProperties() )
		return;

	pNode->NotifyWillChange( pNode );
	pNode->SetNodeExtraAttrValue( "isExpandable", "false" );
	pNode->SetNodeExtraAttrValue( "align", "wxALIGN_RIGHT" );
	RelayoutScene();
	pGlyph->GetEditor().NotifyObjectChanged();
	pGlyph->RefreshNow();
}

void WidgetView::OnItemExpand( wxCommandEvent& event )
{
	// FIXME:: cut&paste's...............

	WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetFocus();

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	WidgetNode* pNode = &pGlyph->GetNode();
	if ( !pNode || !pNode->HasExtraProperties() )
		return;

	pNode->NotifyWillChange( pNode );
	pNode->SetNodeExtraAttrValue( "isExpandable", "true" );
	RelayoutScene();
	pGlyph->GetEditor().NotifyObjectChanged();
	pGlyph->RefreshNow();
}

void WidgetView::OnKeyDown( wxKeyEvent& event )
{
	int c = event.GetKeyCode();

	if ( c == WXK_DELETE )
	{
		mpRootNode->GetCommandProcessor()->Delete();
	}
	else
	if ( !event.AltDown() &&
		 !event.ControlDown() &&
		 !event.MetaDown() &&
		 !event.ShiftDown() &&
		 ( c <= 'z' ) &&
		 c != WXK_DELETE &&
		 ( mpScene && mpScene->HasSingleSelection() )
	   )
	{
		WidgetGlyph* pGlyph = (WidgetGlyph*)mpScene->GetSelection()[0];

		wxASSERT( pGlyph ); // DBG::
		if (!pGlyph)
			return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

		WidgetNode* pNode = &pGlyph->GetNode();

		wxString labelProp = pNode->mpTempl->mTextPropName;

		size_t labelIdx =
			pNode->GetAttrGroupInfo( WidgetNode::WIDGET_PROPERTIES_GROUP )->
				GetAttrIndex( labelProp );

		wxString name = pNode->GetNodeAttrValue( "name" );
        wxString tmp = "";  
		wxString label = labelIdx != (size_t)(-1) ? pNode->GetNodeAttrValue( labelProp ) : tmp;

		wxString subclass = pNode->GetNodeAttrValue( "subclass" );
		wxString dlgTitle = pNode->IsLinked() ? "-> '" : "'";
		dlgTitle += pNode->GetNodeAttrValue( "name" ) + "' of " + pNode->mpTempl->mWidgetClass;

		if ( subclass.Length() )

			dlgTitle += " (Subclass='" + subclass + "')";

		LabelEditDlg dlg( wxPMBootSvc().GetMainWindow(), dlgTitle, labelProp, label,
						  "name", name, labelIdx != (size_t)(-1), TRUE );		

		if ( dlg.ShowModal() == wxID_OK &&
			 ( name != dlg.GetItemName() ||
			   label != dlg.GetItemLabel() )
		   )
		{
			pNode->OnStartEdit();

			GetWidgetDoc().MarkUnusedId( name );

			if ( labelIdx != (size_t)(-1) )
			{
				pNode->SetNodeAttrValue( labelProp, dlg.GetItemLabel() );
			}

			pNode->SetNodeAttrValue( "name", dlg.GetItemName() );

			GetWidgetDoc().MarkUsedId( dlg.GetItemName() );

			pNode->FindCmdProcessor()->SetChangeSender( this );

			pNode->OnFinishEdit();
		}
	}
	else
		event.Skip();
}

/*** notifications ***/

void WidgetView::OnNodeRemoved( wxDMNode& node )
{
    size_t i;
	for( i = 0; i != node.GetChildCount(); ++i )
    {
		OnNodeRemoved( *node.GetChild(i) );
    }

	GetWidgetDoc().MarkUnusedId( ((WidgetNode*)&node)->GetNodeAttrValue( "name" ) );

	WidgetGlyph* pGlyph = FindGlyphForNode( node );

	if ( pGlyph )
	{
		mGlyphHash.Delete( (long)&node );
		mpScene->Remove( pGlyph, FALSE );
	}

	WidgetNode* pParent = (WidgetNode*)node.GetParent();

	if ( pParent && pParent->IsKindOf( CLASSINFO( SizerNodeBase ) ) )

		RelayoutScene();
}

void WidgetView::OnNodeAdded( wxDMNode& node )
{
	GetWidgetDoc().MarkUsedId( ((WidgetNode*)&node)->GetNodeAttrValue( "name" ) );

	if ( node.GetParent() )
	{
		WidgetGlyph* pGlyph = FindGlyphForNode( *node.GetParent() );

		if ( pGlyph )
		{
			pGlyph = CreateGlyphForNode( *(WidgetNode*)&node );

			mpScene->Insert( pGlyph, FALSE );
		}
	}

    size_t i;
	for( i = 0; i != node.GetChildCount(); ++i )
    {
		OnNodeAdded( *node.GetChild(i) );
    }

	WidgetNode* pParent = (WidgetNode*)node.GetParent();

	if ( pParent && pParent->IsKindOf( CLASSINFO( SizerNodeBase ) ) )

		RelayoutScene();
}

void WidgetView::OnNodeChanged( wxDMNode& node )
{
	// if the modification came from external party
	// (e.g. property editor) then reflect changes to
	// node as well as it's to view

	wxObject* pSender = GetWidgetDoc().GetCmdProcessor().GetChangeSender();

	if ( pSender == NULL || !pSender->IsKindOf( CLASSINFO( XObject ) ) )
	{
		WidgetNode& wnode = *((WidgetNode*)&node);

		wnode.ExtractBoundsInfo();

		wnode.SetBounds( wnode.GetBounds(), mpScene->GetCanvas() );

		WidgetGlyph& glyph = GetGlyphForNode( node );

		glyph.InvalidateLook();

		mpScene->InvalidateRect( glyph.GetInclusiveBounds() );

		AssignPropertiesToGlyph( glyph, *(WidgetNode*)&node );

		WidgetNode* pParent = (WidgetNode*)node.GetParent();

        if (pParent)  // glt
		    pParent->OnChildChanged( &node );

		// FIMXE:: dirty
		if ( pParent && pParent->IsKindOf( CLASSINFO( SizerNodeBase ) ) )

			RelayoutScene();

		mpScene->InvalidateRect( glyph.GetInclusiveBounds() );

		glyph.GetEditor().NotifyObjectChanged();

		mpScene->RefreshNow();
	}
}

void WidgetView::OnNodeOrderChanged( wxDMNode& node )
{
	ReorderAllGlyphs();
}

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

void WidgetView::OnNodeReplaced( wxDMNode& node, wxDMNode& withNode )
{
	WidgetGlyph* pGlyph = FindGlyphForNode( *(WidgetNode*)&node );

	// FIXME:: find out more about this 'if'

	if ( pGlyph )
	{
		mpScene->Remove( pGlyph, FALSE );

		mGlyphHash.Delete( (long)&node );

		pGlyph = CreateGlyphForNode( *(WidgetNode*)&withNode );

		mpScene->Insert( pGlyph, FALSE );
	}

	if ( &node == mpRootNode )
	{
		mpRootNode = (WidgetNode*)&withNode;
		GetWidgetDoc().SetRootNode( mpRootNode );
	}

	((WidgetNode*)&withNode)->InvalidateLooksRecursive();

	if ( pGlyph )

		ReorderAllGlyphs();

	RelayoutScene();
}

void WidgetView::OnFocusChanged( wxDMNode& node )
{
	mpScene->SetFocus( &GetGlyphForNode( node ) );
}

void WidgetView::OnFocusRemoved()
{
	mpScene->SetFocus( NULL );
}

void WidgetView::OnSelectionSet( DMNodeListT& nodes )
{
	XObjListT lst;

    size_t i;
	for( i = 0; i != nodes.Count(); ++i )
    {
		lst.Add( &GetGlyphForNode( *nodes[i] ) );
    }

	XObjListT cur = mpScene->GetSelection();

	if ( cur.Count() == lst.Count() )
	{
		size_t k = 0;
		for( ; k != lst.Count(); ++k )
        {
			if ( lst[k] != cur[k] )
				break;
        }

		if ( k == lst.Count() )

			return; // no changes to the selection, must be
					// a bounce-back notification, thus ignore it
	}

	mpScene->SetSelection( lst ); // also causes immediate refresh of selection
}

void WidgetView::OnNodeAddedToSelection( wxDMNode& node )
{
	// TBD:: ?
}

void WidgetView::OnNodeDeselected( wxDMNode& node )
{
	// TBD:: ?
}

void WidgetView::OnUpdateFinished()
{
	if ( mpScene->HasEditor() ) mpScene->GetEditor().NotifyObjectChanged();

	mpScene->RefreshNow();
}

/*** protected methods ***/

// helpers
XRect wxRectToXRect( const wxRect& rect )
{
	return XRect( rect.x, rect.y, rect.width, rect.height );
}

wxRect XRectToWxRect( const XRect& rect )
{
	return wxRect( rect.x, rect.y, rect.width, rect.height );
}

void WidgetView::AssignPropertiesToGlyph( WidgetGlyph& glyph, WidgetNode& fromNode )
{
	XRect bounds = 	fromNode.GetBounds();

	glyph.ToGlyphRect( bounds );

	glyph.SetBoundsSilent( bounds );
}

WidgetGlyph* WidgetView::CreateGlyphForNode( WidgetNode& node )
{
	wxClassInfo* pClass = 
		wxClassInfo::FindClass( (char*)(const char*)node.mpTempl->mPreviewerClass );

	WidgetGlyph* pGlyph = NULL;

	if ( pClass )
		pGlyph = (WidgetGlyph*)pClass->CreateObject();

	if ( !( pClass && pGlyph ) )
	{
		wxMessageBox( "Cannot create previewer object of class " + 
					  node.mpTempl->mPreviewerClass );

		pGlyph = new WidgetGlyph();
	}

	pGlyph->SetNode( &node );

	pGlyph->SetParent( mpScene );

	mGlyphHash.Put( (long)&node, pGlyph );

	if ( node.HasParent() )
	{
		WidgetGlyph* pParent = FindGlyphForNode( *node.GetParent() );

		pGlyph->SetParent( pParent );
	}

	AssignPropertiesToGlyph( *pGlyph, node );

	if ( pGlyph->GetBounds().width == 0 )
	{
		int o=0;
		++o;
	}

	XRectEditor* pEditor = new XRectEditor();
	pEditor->SetAutoRise( FALSE );

	if ( &node == mpRootNode || 
		 ( node.mpTempl->mCanHaveChildren &&
		   node.mpTempl->mWidgetClass != "wxNotebook" ) // FIXME:: hack for notebook
	   )
		pEditor->EnableMove( FALSE );

	pGlyph->SetEditor( pEditor );

	return pGlyph;
}

void WidgetView::AddGlyphRefsRecursive( XObjListT& toLst, wxDMNode& node )
{
	WidgetGlyph* pGlyph = FindGlyphForNode( node );

	if ( pGlyph )
	{
		WidgetGlyph* pParentGlyph = pGlyph->GetParentGlyph();

		mpScene->InvalidateRect( pGlyph->GetInclusiveBounds() );

		toLst.Add( pGlyph );
	}
    
    size_t i;
	for( i = 0; i != node.GetChildCount(); ++i )
    {
		AddGlyphRefsRecursive( toLst, *node.GetChild(i) );
    }
}

void WidgetView::CreateGlyphsRecursive( XObjListT& toLst, wxDMNode& node )
{
	WidgetGlyph* pParentGlyph = NULL;
	
	if ( node.GetParent() )
		
		pParentGlyph = FindGlyphForNode( *node.GetParent() );

	if ( pParentGlyph && !pParentGlyph->IsVisibleChild( node.GetOrderNo() ) )

		return;

	WidgetGlyph* pGlyph = CreateGlyphForNode( *(WidgetNode*)&node );

	wxASSERT( pGlyph ); // DBG::
	if (!pGlyph)
		return;  // glt - added this to avoid a crash - though I do not know if this is the right behavior

	pGlyph->GetNode().RecalcPreferredSize( mpScene->GetCanvas() );

	mpScene->Insert( pGlyph, FALSE );

    size_t i;
	for( i = 0; i != node.GetChildCount(); ++i )
    {
		CreateGlyphsRecursive( toLst, *node.GetChild( i ) );
    }
}

void WidgetView::ReorderAllGlyphs()
{
	XObjListT& lst = mpScene->GetContents();

	XObject* pBg = mpScene->GetBackgroundObj();

	lst.Clear();

	lst.Add( pBg );

	AddGlyphRefsRecursive( lst, *mpRootNode );
}

void WidgetView::RefreshScene()
{
	XObjListT& lst = mpScene->GetContents();

	XObject* pBg = mpScene->GetBackgroundObj();

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
    {
		if ( lst[i] != pBg )
			mpScene->InvalidateRect( lst[i]->GetInclusiveBounds() );
    }
	mpScene->RefreshNow();
}

void WidgetView::InvalidateLooks()
{
	XObject* pBg = mpScene->GetBackgroundObj();

	XObjListT& lst = mpScene->GetContents();

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{	
		XObject* pObj = lst[i];

		if ( pObj != pBg )

			((WidgetGlyph*)pObj)->InvalidateLook();
	}
}

void WidgetView::RecreateMenuChains()
{
	if ( mpWidgetMenus )
		delete [] mpWidgetMenus;
	if ( mpMenuChains )
		delete [] mpMenuChains;

	wxPMMergableMenuChain* pEditorChains = mpEditorHandler->GetMergableMenuChain();
	wxPMMergableMenuChain* pChain = pEditorChains;

	size_t sz = 0;
	while( pChain->m_pCommands )
	{
		++sz;
		++pChain;
	}

	// prepare items for the "Insert" menu

	WidgetResTemplateListT& lst = GetTemplateManager().GetTemplates();

	mpWidgetMenus = new wxCommandInfo[ lst.Count() + 1 ];

	int id = ID_INSERT_WIDGET_IDS_START;

	size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		wxCommandInfo& cmd = mpWidgetMenus[i];
		cmd.SetTitle( lst[i]->mWidgetClass );
		cmd.SetId( id++ );
		cmd.SetHelpString( "Insert " + lst[i]->mWidgetClass );

		if ( lst[i]->mToolBarBitmap.Ok() )
		{
			wxBitmap bmp = lst[i]->mToolBarBitmap;

			wxMask* pMask = new wxMask();
			pMask->Create( bmp, *wxRED );
			bmp.SetMask( pMask );

			cmd.SetBitmap( bmp );
		}

		//cmd.SetBitmap( wxPMBmpMgr().FindBitmap( "filetypes/16x16_wrs_type" ) );
	}

	mpWidgetMenus[ lst.Count() ].SetId(0); // terminator
	mpWidgetMenus[ lst.Count() ].SetTitle("");

	// put all together into chanes

	mpMenuChains = new wxPMMergableMenuChain[ sz + 4 ];

	for( i = 0; i != sz; ++i )

		mpMenuChains[i] = pEditorChains[i];

	// sorry, i'll put them back to "&Edit" when i get toolbars to work
	mpMenuChains[sz+0].m_PredecessorPath = "/&Canvas/";
	mpMenuChains[sz+0].m_pCommands = _gCanvasCommands;

	mpMenuChains[sz+1].m_PredecessorPath = "/&Insert/";
	mpMenuChains[sz+1].m_pCommands = mpWidgetMenus;

	mpMenuChains[sz+2].m_PredecessorPath = "/I&tem/";
	mpMenuChains[sz+2].m_pCommands = _gItemCommands;

	mpMenuChains[sz+3].m_pCommands = NULL; // terminator

}

/***** Implementation for class WidgetEqSolver *****/

WidgetEqSolver::WidgetEqSolver()

	: mpGroup( NULL ),
	  mpGroupData( NULL ),
	  mCurVarIdx( 0 )
{}

void WidgetEqSolver::Init( wxDMAttrGroupInfo* pGroup, 
						   void* pGroupData,
						   const wxString& uiType,
						   WidgetResTemplate* pTempl )
{
	mpGroup = pGroup;
	mpGroupData = pGroupData;
	mUIType = uiType;
	mpTempl = pTempl;
}

EqSolverVar* WidgetEqSolver::FindVar( const wxString& name )
{
	if ( name == "widgetclass" )

		return GetVarForValue( mpTempl->mWidgetClass );
	else
	if ( name == "UI_TYPE" )

		return GetVarForValue( mUIType );
	else
	{
		wxDMAttrInfo* pAttr = mpGroup->GetAttrByName( name );

		if ( !pAttr )

			return EqSolver::FindVar( name );

		return GetVarForValue( pAttr->GetStringValue( mpGroupData ) );
	}
}

/*** protected methods ***/

EqSolverVar* WidgetEqSolver::GetVarForValue( const wxString& value )
{
	++mCurVarIdx;

	if ( mCurVarIdx == 5 ) mCurVarIdx = 0;

	mVarsRibbon[ mCurVarIdx ].SetString( value );

	return &mVarsRibbon[ mCurVarIdx ];
}

/***** Implementation for class WidgetNode *****/

IMPLEMENT_DYNAMIC_CLASS( WidgetNode, wxDMCompositeNode )

WidgetNode::WidgetNode()

	: mpTempl( NULL ),
	  mpView( NULL ),
	  mIsLinked( FALSE )
{}

WidgetNode::~WidgetNode()
{}

size_t WidgetNode::GetGroupCount()
{
	return HasExtraProperties() ? 2 : 1;
}

wxDMAttrGroupInfo* WidgetNode::GetAttrGroupInfo( size_t index )
{
	return mAttribGroups[index].GetGroupInfo();
}

void* WidgetNode::GetAttrGroupData( size_t index )
{
	return mAttribGroups[index].GetData();
}

void* WidgetNode::GetAttrGroupData( wxDMAttrGroupInfo* pForGroup )
{
    size_t i;
	for( i = 0; i != MAX_PROPERTY_GROUPS; ++i )
    {
		if ( mAttribGroups[i].GetGroupInfo() == pForGroup )
			return &mAttribGroups[i];
    }

	return NULL;
}

wxString WidgetNode::GetDisplayName()
{
	return GetNodeAttrValue( "name" );
}

void WidgetNode::SetView( WidgetView* pView )
{
	mpView = pView;
}

const wxString& WidgetNode::GetWidgetClass()
{
	return mpTempl->mWidgetClass;
}

XRect& WidgetNode::GetBounds()
{
	return mBounds;
}

void WidgetNode::SetBounds( const XRect& rect, wxDC& dc )
{
	mBounds = rect;

	WidgetGlyph* pGlyph = mpView->FindGlyphForNode( *this );

	//mpView->AdjustRectToGrid( mBounds );

	StoreBoundsInfo();

	if ( pGlyph )
	{
		XRect bounds = mBounds;

		pGlyph->ToGlyphRect( bounds );

		pGlyph->SetBoundsSilent( bounds );
	}

	DoLayoutChildren( dc );
}

wxColour& WidgetNode::GetBgColor()
{
	if ( mBgColor.Ok() ) 
		return mBgColor;

	if ( mpParent && mpTempl->mInheritsBg && IsDefaultValue("bg") )
		return ((WidgetNode*)GetParent())->GetBgColor();

	CreateBgColor();
	return mBgColor;
}

wxColour& WidgetNode::GetFgColor()
{
	if ( mFgColor.Ok() ) 
		return mFgColor;

	if ( mpParent && mpTempl->mInheritsFg && IsDefaultValue("fg") )
		return ((WidgetNode*)GetParent())->GetFgColor();

	CreateFgColor();
	return mFgColor;
}

wxFont& WidgetNode::GetFont()
{
	if ( mFont.GetRefData() != NULL ) 
		return mFont;

	if ( mpParent && mpTempl->mInheritsFont && IsDefaultValue("font") )
		return ((WidgetNode*)GetParent())->GetFont();

	CreateFont();
	return mFont;
}

bool WidgetNode::IsDefaultValue( const wxString& prop )
{
	ResGroupData& grpData = mAttribGroups[WIDGET_PROPERTIES_GROUP];

	ResFontAttrInfo* attr =	(ResFontAttrInfo*)grpData.mpGroupInfo->GetAttrByName( prop );

	if ( !attr )
		return TRUE;

	return attr->IsDefaultValue( &grpData );
}

void WidgetNode::CreateFont()
{
	ResGroupData& grpData = mAttribGroups[WIDGET_PROPERTIES_GROUP];

	ResFontAttrInfo* attr =	(ResFontAttrInfo*)grpData.mpGroupInfo->GetAttrByName( "font" );

	if ( !attr )
		return; // TBD:: what to do?

	if ( attr->IsDefaultValue( grpData.GetData() ) )
	
		mFont = this->mpView->GetViewFrame()->GetFont();
	else
	{
		wxFont* pFnt = attr->CreateFontObject( grpData.GetData() );

		mFont = *pFnt;
		delete pFnt;
	}
}

void WidgetNode::CreateBgColor()
{
	mBgColor = GetColourPropByName( "bg", 
									//wxSystemSettings::GetSystemColour( wxSYS_COLOUR_3DFACE ) );
									wxColour() );
}

void WidgetNode::CreateFgColor()
{
	mFgColor = GetColourPropByName( "fg", 
									//wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOWTEXT ) );
									wxColour() );
}

wxColour WidgetNode::GetColourPropByName( const wxString& prop, const wxColour& defaultVal )
{
	ResGroupData& grpData = mAttribGroups[WIDGET_PROPERTIES_GROUP];

	ResColorAttrInfo* attr = (ResColorAttrInfo*)grpData.mpGroupInfo->GetAttrByName( prop );

	if ( !attr || attr->IsDefaultValue( grpData.GetData() ) )

		return defaultVal;

	return attr->GetColorValue( grpData.GetData() );
}

bool WidgetNode::HasExtraProperties()
{
	return mAttribGroups[EXTRA_PROPERTIES_GROUP].mAttrData.Count() != 0;
}

void WidgetNode::ExtractBoundsInfo()
{
	// FIXME:: this can be slow..

	void* pData = GetAttrGroupData( (unsigned int)WIDGET_PROPERTIES_GROUP );
	wxDMAttrGroupInfo* pGroup = mAttribGroups[WIDGET_PROPERTIES_GROUP].GetGroupInfo();

	mBounds.x = StrToInt( pGroup->GetAttrValueByName( "x", pData ) );
	mBounds.y = StrToInt( pGroup->GetAttrValueByName( "y", pData ) );
	mBounds.width  = StrToInt( pGroup->GetAttrValueByName( "width",  pData ) );
	mBounds.height = StrToInt( pGroup->GetAttrValueByName( "height", pData ) );

	if ( mBounds.x == -1 ) mBounds.x = 0;
	if ( mBounds.y == -1  ) mBounds.y = 0;
}

void WidgetNode::StoreBoundsInfo()
{
	// FIXME:: this can be slow..

	void* pData = GetAttrGroupData( (unsigned int)WIDGET_PROPERTIES_GROUP );
	wxDMAttrGroupInfo* pGroup = mAttribGroups[WIDGET_PROPERTIES_GROUP].GetGroupInfo();

	pGroup->SetAttrValueByName( "x", IntToStr( mBounds.x ), pData );
	pGroup->SetAttrValueByName( "y", IntToStr( mBounds.y ), pData );
	pGroup->SetAttrValueByName( "width",  IntToStr( mBounds.width  ), pData );
	pGroup->SetAttrValueByName( "height", IntToStr( mBounds.height ), pData );
}

wxString WidgetNode::GetNodeAttrValue( const wxString& name )
{
	ResGroupData& grpData = mAttribGroups[WIDGET_PROPERTIES_GROUP];

	return grpData.mpGroupInfo->GetAttrValueByName( name, grpData.GetData() );
}

wxString WidgetNode::GetNodeExtraAttrValue( const wxString& name )
{
	ResGroupData& grpData = mAttribGroups[EXTRA_PROPERTIES_GROUP];

	return grpData.mpGroupInfo->GetAttrValueByName( name, grpData.GetData() );
}

void WidgetNode::SetNodeAttrValue( const wxString& name, const wxString& value )
{
	ResGroupData& grpData = mAttribGroups[WIDGET_PROPERTIES_GROUP];

	grpData.mpGroupInfo->SetAttrValueByName( name, value, grpData.GetData() );
}

void WidgetNode::SetNodeExtraAttrValue( const wxString& name, const wxString& value )
{
	ResGroupData& grpData = mAttribGroups[EXTRA_PROPERTIES_GROUP];

	grpData.mpGroupInfo->SetAttrValueByName( name, value, grpData.GetData() );
}

void WidgetNode::OnChildInserted( wxDMNode* pNode )
{
	if ( mpTempl->mpExtraAttrGroup )

		AddExtraPropertiesToChild( (WidgetNode*)pNode );
}

void WidgetNode::OnChildRemoved( wxDMNode* pNode )
{
	WidgetNode* pWNode = (WidgetNode*)pNode;

	if ( pWNode->HasExtraProperties() )

		pWNode->mAttribGroups[ EXTRA_PROPERTIES_GROUP ].CleanUp();
}

XRect WidgetNode::GetLimitingBounds()
{
	WidgetGlyph* pGlyph = mpView->FindGlyphForNode( *this );

	if ( !pGlyph )
		return XRect();

	// FIXME:: dirty hack!!!

	XRect bounds = pGlyph->GetBounds();

	XRect lbounds = pGlyph->GetLimitingBounds();

	lbounds.x -= bounds.x;
	lbounds.y -= bounds.y;

	return lbounds;
}

void WidgetNode::DoLayoutChildren( wxDC& dc )
{
	// TBD:: comments...

	if ( mChildren.Count() == 1 &&
		 mChildren[0]->IsKindOf( CLASSINFO( SizerNodeBase ) ) )
	{
		XRect lbounds = GetLimitingBounds();
		lbounds.x = 0;
		lbounds.y = 0;

		WidgetNode* pChild = (WidgetNode*)mChildren[0];

		pChild->SetBounds( lbounds, dc );

		return;
	}

    size_t i;
	for( i = 0; i != mChildren.Count(); ++i )
	{
		WidgetNode& node = *(WidgetNode*)mChildren[i];

		XRect bounds = node.GetBounds();

		// causes relayouting of child-nodes
		node.SetBounds( bounds, dc );
	}
}

void WidgetNode::AddExtraPropertiesToChild( WidgetNode* pChild )
{
	if ( this->mpTempl->mpExtraAttrGroup )
	{
		if ( pChild->mAttribGroups[ EXTRA_PROPERTIES_GROUP ].mpGroupInfo 
			 != this->mpTempl->mpExtraAttrGroup )
		{
			pChild->mAttribGroups[ EXTRA_PROPERTIES_GROUP ].CleanUp();

			pChild->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ].
				SetGroupInfo( this->mpTempl->mpExtraAttrGroup );
		}

		// FIXME:: HACK:: something less specific

		if ( pChild->IsKindOf( CLASSINFO(SizerNodeBase) ) )
		{
			pChild->SetNodeExtraAttrValue( "hasFullBorder", "false" );
		}
	}
	else
	if ( pChild->HasExtraProperties() )

		pChild->mAttribGroups[ EXTRA_PROPERTIES_GROUP ].CleanUp();
}

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

	AssignData( pCopy, uniqueCopy, cloneChildren );

	return pCopy;
}

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

	pNode->mBgColor = mBgColor;
	pNode->mFgColor = mFgColor;
	pNode->mFont    = mFont;

	pNode->mBounds = mBounds;
	pNode->mpTempl = mpTempl;
	pNode->mpView = mpView;
	pNode->mIsLinked = mIsLinked;

    size_t i;
	for( i = 0; i != MAX_PROPERTY_GROUPS; ++i )
    {
		mAttribGroups[i].CopyDataTo( pNode->mAttribGroups[i] );
    }

	if ( uniqueCopy )
	{
		wxString newId = mpView->GetWidgetDoc().GenerateUniqueId( *pNode );

		pNode->SetNodeAttrValue( "name", newId );
	}

	wxDMCompositeNode::AssignData( pCopy, uniqueCopy, cloneChildren );
}

WidgetResTemplate& WidgetNode::GetTemplate()
{
	return *mpTempl;
}

const XDim WidgetNode::GetPreferredSize()
{
	return mPrefSize;
}

void WidgetNode::SetPreferredSize( const XDim& size )
{
	mPrefSize;
}

void WidgetNode::OnStartEdit()
{
	NotifyWillChange( this );
}

void WidgetNode::OnFinishEdit()
{
	InvalidateLooksRecursive();

	NOTIFY_ONE_ARG( OnNodeChanged, *this );
}

void WidgetNode::InvalidateLooksRecursive()
{
	WidgetGlyph* glyph = mpView->FindGlyphForNode(*this);

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

		((WidgetNode*)GetChild(i))->InvalidateLooksRecursive();

	if ( glyph )

		glyph->InvalidateLook();
}

bool WidgetNode::IsLinked()
{
	return mIsLinked;
}

void WidgetNode::SetIsLinked( bool isLinked )
{
	mIsLinked = isLinked;
}

bool WidgetNode::CanHaveChildren()
{
	return mpTempl->mCanHaveChildren;
}

void WidgetNode::RecalcPreferredSize( wxDC& dc )
{
	// FIXME:: dirty (should not glyph for that matter)

	WidgetGlyph* pGlyph = mpView->FindGlyphForNode( *this );

	if ( !pGlyph )
		return;

	wxUILookInfo* pInfo = pGlyph->GetLookInfo();

	if ( pInfo == NULL )
	{
		mPrefSize.width  = 20;
		mPrefSize.height = 20;

		return;
	}

	wxString text;
	int x = 0, y = 0, w = 0, h = 0;

	WidgetDocument& doc = mpView->GetWidgetDoc();
	WidgetEqSolver& solver = doc.GetEqSolver();

	solver.Init( GetAttrGroupInfo( WidgetNode::WIDGET_PROPERTIES_GROUP ),
				 GetAttrGroupData( (unsigned int)WidgetNode::WIDGET_PROPERTIES_GROUP ),
				 mpView->GetWidgetDoc().GetUIType(),
				 mpTempl );
	
	doc.GetRenderer().CalcItemParams( *pInfo, 
									  wxSize(10,10), // doesn't matter here
									  dc, text, x, y, w, h,
									  this->GetFont(), this->GetBgColor(), this->GetFgColor(),
									  &solver );

	mPrefSize.width  = w;
	mPrefSize.height = h;
}

/***** Implementation for class WidgetGlyph *****/

IMPLEMENT_DYNAMIC_CLASS( WidgetGlyph, XObject )

WidgetGlyph::WidgetGlyph()

	: mpLookInfo( NULL ),
	  mpBmp( NULL ),
	  mpNode( NULL )
{}

WidgetGlyph::~WidgetGlyph()
{
	if ( mpBmp )
        delete mpBmp;
}

void WidgetGlyph::SetNode( WidgetNode* pNode )
{
	mpNode = pNode;
}

void WidgetGlyph::SetLookInfo( wxUILookInfo* pInfo )
{
	mpLookInfo = pInfo;
}

wxUILookInfo* WidgetGlyph::GetLookInfo()
{
	if ( !mpLookInfo )

		mpLookInfo = mpNode->mpView->GetWidgetDoc().FindLookForNode( *mpNode );

	return mpLookInfo;
}

bool WidgetGlyph::Contains( const XPoint& point )
{
	if ( mpLookInfo && mpLookInfo->mIsOpaque == FALSE )
	{
		XRect r = mPixBounds;

		r.x      += mpLookInfo->mEdges[ LEFT_LOOK_EDGE ].mWidth;
		r.width  -= mpLookInfo->mEdges[ LEFT_LOOK_EDGE ].mWidth + 
					mpLookInfo->mEdges[ RIGHT_LOOK_EDGE ].mWidth;

		r.y      += mpLookInfo->mEdges[ TOP_LOOK_EDGE ].mWidth;
		r.height -= mpLookInfo->mEdges[ TOP_LOOK_EDGE ].mWidth + 
					mpLookInfo->mEdges[ BOTTOM_LOOK_EDGE ].mWidth;

		// contains only if the inner (transparent) area is not hit

		return !r.Contains( point );
	}
	else
		return XObject::Contains( point );
}

void WidgetGlyph::Draw( wxDC& canvas )
{
	if ( !mpBmp )

		RegenerateLookBitmap();

	if ( !mpBmp )
	{
		wxBrush br( *wxRED, wxSOLID );

		wxPen pen( *wxWHITE, 2, wxSOLID );
		canvas.SetPen( pen );

		canvas.SetBrush( br );

		canvas.DrawRectangle( mPixBounds.x, mPixBounds.y, mPixBounds.width, mPixBounds.height );

		canvas.DrawLine( mPixBounds.x, mPixBounds.y, 
						 mPixBounds.x + mPixBounds.width - 1, mPixBounds.y + mPixBounds.height - 1 );

		canvas.DrawLine( mPixBounds.x, mPixBounds.y + mPixBounds.height - 1, 
						 mPixBounds.x + mPixBounds.width - 1, mPixBounds.y );

		canvas.SetTextForeground( *wxBLUE );

		canvas.DrawText( mpNode->mpTempl->mWidgetClass, mPixBounds.x + 5, mPixBounds.y + 5 );
	}
	else
	
		mpNode->mpView->GetWidgetDoc().GetRenderer().
			DrawLook( canvas, *this->mpLookInfo, *mpBmp, mPixBounds.x, mPixBounds.y );

		//canvas.DrawBitmap( *mpBmp, mPixBounds.x, mPixBounds.y );

	DrawGrid( canvas );
}

void WidgetGlyph::DrawGrid( wxDC& dc )
{
	if ( !mpLookInfo )
        return;

	if ( !mpNode->mpTempl->mCanHaveChildren )
        return;

	int type = mpNode->mpView->GetGridType();

	if ( type == WidgetView::GRID_NONE )
        return;

	XRect inner;
	GetLimitingRectInUnits( inner );

	int stepx = mpNode->mpView->GetGridSpacinig().x;
	int stepy = mpNode->mpView->GetGridSpacinig().x;

	/*
	// OLD STUF::
	int fromx = ( (inner.x  ) / stepx ) * stepx;
	int fromy = ( ( inner.y  ) / stepy ) * stepy;
	int tillx = ( ( inner.x + inner.width  ) / stepx ) * stepx;
	int tilly = ( ( inner.y + inner.height ) / stepy ) * stepy;
	if ( fromx < inner.x ) fromx += stepx;
	if ( fromy < inner.y ) fromy += stepy;
	if ( tillx >= inner.x + inner.width ) tillx -= stepx;
	if ( tilly >= inner.y + inner.height) tilly -= stepy;
	*/

	// much simpler now
	int fromx = inner.x;
	int fromy = inner.y;
	int tillx = inner.x + inner.width;
	int tilly = inner.y + inner.height;

	int ox = 0, oy = 0;

	//mpNode->mpView->GetTopLevelWidgetOrigin( &ox, &oy );

	double pixPerUnitV = mpNode->mpView->GetVerticalPixelsPerUnitRatio(), 
		   pixPerUnitH = mpNode->mpView->GetHorizontalPixelsPerUnitRatio();

	if ( type == WidgetView::GRID_LINES )
	{
		int left   = inner.x * (int)pixPerUnitH + ox;
		int right  = ( inner.x + inner.width ) * (int)pixPerUnitH + ox;
		int top    = inner.y * (int)pixPerUnitV + oy;
		int bottom = ( inner.y + inner.height ) * (int)pixPerUnitV + oy;

		dc.SetPen( *wxGREY_PEN );

		dc.DrawLine( left, top, right, top );
		dc.DrawLine( right, top, right, bottom );
		dc.DrawLine( right, bottom, left, bottom );
		dc.DrawLine( left, bottom, left, top );

        int y;
		for( y = fromy; y <= tilly; y += stepy )
		{
			int pixely = oy + y * (int)pixPerUnitV;

			dc.DrawLine( left, pixely, right, pixely );
		}

        int x;
		for( x = fromx; x <= tillx; x += stepx )
		{
			int pixelx = ox + x * (int)pixPerUnitH;

			dc.DrawLine( pixelx, top, pixelx, bottom );
		}
	}
	else
	{
		if ( type == WidgetView::GRID_DOTS_3D )
			
			dc.SetPen( *wxBLACK_PEN );
		else
			dc.SetPen( *wxGREY_PEN );

		{
            int y;
			for( y = fromy; y <= tilly; y += stepy )
			{
				int pixely = oy + y * (int)pixPerUnitV;

                int x;
				for( x = fromx; x <= tillx; x += stepx )
				{
					int pixelx = ox + x * (int)pixPerUnitH;

					dc.DrawPoint( pixelx, pixely );
				}
			}
		}

		if ( type == WidgetView::GRID_DOTS_3D )
		{
			dc.SetPen( *wxWHITE_PEN );

            int y;
			for( y = fromy; y <= tilly; y += stepy )
			{
				int pixely = oy + y * (int)pixPerUnitV + 1;

                int x;
				for( x = fromx; x <= tillx; x += stepx )
				{
					int pixelx = ox + x * (int)pixPerUnitH + 1;

					dc.DrawPoint( pixelx, pixely );
				}
			}
		}
	}
}

void WidgetGlyph::SetBoundsSilent( const XRect& bounds )
{
	// contrary to SetBounds(..) this method does not
	// set bounds to the glyph's node

	if ( mpEditor )

		InvalidateRect( mpEditor->GetInclusiveBounds() );

	int oldWidth = mPixBounds.width, oldHeight = mPixBounds.height;

	mPixBounds = bounds;

	if ( mpBmp && 
		 ( oldWidth  != mPixBounds.width ||
		   oldHeight != mPixBounds.height ) ) 
	{
		delete mpBmp;
		mpBmp = NULL; // invalidate current look-image
	}

	InvalidateRect( GetBounds() );
}

void WidgetGlyph::SetBounds( const XRect& bounds )
{
	int oldWidth = mPixBounds.width, oldHeight = mPixBounds.height;

	mPixBounds = bounds;

	mpNode->mpView->AdjustRectToGrid( mPixBounds, *this );

	if ( mpBmp && 
		 ( oldWidth  != mPixBounds.width ||
		   oldHeight != mPixBounds.height ) ) 
	{
		delete mpBmp;
		mpBmp = NULL; // invalidate current look-image
	}

	// FIXME::

	XRect nodeBounds = mPixBounds;
	ToNodeRect( nodeBounds );

	mpNode->SetBounds( nodeBounds, GetCanvas() );

	// apply layouting of children which was done by setting bounds to 
	// non-visual node

	// FIXME:: BS!

#if 0
    size_t i;
	for( i = 0; i != mpNode->GetChildCount(); ++i )
	{
		WidgetNode& child = *(WidgetNode*)mpNode->GetChild(i);

		XRect newBounds = child.GetBounds();

		WidgetGlyph* pGlyph = mpNode->mpView->FindGlyphForNode( child );

		if ( pGlyph )
		{
			pGlyph->ToGlyphRect( newBounds );

			InvalidateRect( pGlyph->GetBounds() );
			pGlyph->SetBounds( newBounds );
		}

		InvalidateRect( newBounds );
	}
#endif
}

XRect WidgetGlyph::GetBounds()
{
	return mPixBounds;
}

XRect WidgetGlyph::GetLimitingBounds()
{
	if ( !mpLookInfo )

		// FIXME:: make sure this is not excessive

		mpLookInfo = mpNode->mpView->GetWidgetDoc().FindLookForNode( *mpNode );

	if ( mpLookInfo )
	{
		// insets subsctracted in units

		XRect r;
		GetLimitingRectInUnits( r );

		XRect lbounds;
		mpNode->mpView->UnitRectToPixelRect( r, lbounds );

		return lbounds;
	}

	return mPixBounds;
}

bool WidgetGlyph::OnStartEdit()
{
	mpNode->OnStartEdit();

	return true;
}

bool WidgetGlyph::OnFinishEdit()
{
	WidgetNode* pParent = (WidgetNode*)mpNode->GetParent();

	if ( pParent )
	{
		WidgetNode* pTarget = NULL; int zOrder = 0;

		mpNode->mpView->FindInsertionTarget( XPoint( mPixBounds.x, mPixBounds.y ), pTarget, 
											 zOrder, mpNode );

		if ( pTarget 
			 &&
			 ( !(pTarget == pParent && zOrder == -1) || // either we are changing item's order
			    pTarget != pParent						// or reparenting
			  ) 
			 && 
			   // guard against insertion into one of own children
			   !mpNode->Contains( pTarget ) 
		   )

		{
			WidgetGlyph* pTargetGlyph = mpNode->mpView->FindGlyphForNode( *pTarget );
			XRect newNodeBounds = mPixBounds;
			pTargetGlyph->ToNodeRect( newNodeBounds, FALSE );

			// user has dragged item out of it's parent container meaning
			// to change item's parent or he/she intends to change the order 
			// of children within the parent

			SetEditor( NULL );

			InvalidateRect( GetParent().GetInclusiveBounds() );

			// FIXME:: kludge..
			size_t order = mpNode->GetOrderNo();
			mpNode->GetParent()->GetChildren().Remove( mpNode );

			// FIXME:: kludge..
			mpNode->GetParent()->GetChildren().Insert( mpNode, order );

			WidgetNode* pCopy = (WidgetNode*)mpNode->Clone( FALSE );

			GetWindow()->ReleaseMouse();
			ResetMouseFocus( this );

			WidgetGlyph& targetGlyph = pTarget->mpView->GetGlyphForNode( *pTarget );

			// translate new bounds into target-item's local coordinates

			pCopy->mBounds = newNodeBounds;

			if ( pTarget == pParent && zOrder > (int)mpNode->GetOrderNo() )

				--zOrder; // we're removing ourselves, the index should slide left

			XObject& parentObj = GetParent();

			pTarget->FindCmdProcessor()->StartBatch();

			mpNode->NotifyWillRemove( mpNode );
			mpNode->Remove();

			// add it now

			pTarget->AddChild( pCopy, zOrder );

			pCopy->NotifyAdded( pCopy );

			pTarget->FindCmdProcessor()->FinishBatch();

			pTarget->mpView->RelayoutScene();

			parentObj.RefreshNow();

			return FALSE;
		}
	}

	mpNode->FindCmdProcessor()->SetChangeSender( this );

	mpNode->OnFinishEdit();

	if ( pParent && pParent->IsKindOf( CLASSINFO( SizerNodeBase ) ) )
	{	
		mpNode->mpView->RelayoutScene();

		if ( HasEditor() )
			GetEditor().NotifyObjectChanged();
	}

	return TRUE;
}

WidgetGlyph* WidgetGlyph::GetParentGlyph()
{
	WidgetNode* pParent = (WidgetNode*)mpNode->GetParent();

	if ( pParent == NULL )
        return NULL;

	WidgetGlyph& parentGlyph = mpNode->mpView->GetGlyphForNode( *pParent );

	return &parentGlyph;
}

void WidgetGlyph::ToGlyphRect( XRect& rect )
{
	// rect is given in node's coordinates

	WidgetGlyph* pParent = GetParentGlyph();

	if ( !pParent )
        return;

	XRect lbounds = pParent->GetLimitingBounds();

	// convert them to canvas-coordinates of this glyph

	rect.x += lbounds.x;
	rect.y += lbounds.y;
}

void WidgetGlyph::ToNodeRect( XRect& rect, bool useParent )
{
	// rect is given in glyph's coordinates

	WidgetGlyph* pParent = this;
	
	if ( useParent )
	{
		pParent = GetParentGlyph();

		if ( !pParent ) 
			return;
	}

	XRect lbounds = pParent->GetLimitingBounds();

	rect.x -= lbounds.x;
	rect.y -= lbounds.y;
}

WidgetNode& WidgetGlyph::GetNode()
{
	return *mpNode;
}

XObject* WidgetGlyph::GetSurroundingObject()
{
	if ( mpNode->GetParent() &&
		 mpNode->GetParent()->IsKindOf( CLASSINFO(WidgetNode) ) )
	{
		if ( !mpNode->GetParent()->IsKindOf( CLASSINFO(SizerNodeBase) ) )

			return &mpNode->mpView->GetGlyphForNode( *(WidgetNode*)mpNode->GetParent() );
		else
			return NULL;
	}
	else
		return NULL;
}

void WidgetGlyph::InvalidateLook()
{
	mpLookInfo = NULL;

	if ( mpBmp )
	{
		delete mpBmp;
		mpBmp = NULL; // invalidate current look-image
	}

	if ( mpNode->mFont.GetRefData() != NULL )

		mpNode->mFont = wxFont();

	if ( mpNode->mFgColor.Ok() )

		mpNode->mFgColor = wxColour();

	if ( mpNode->mBgColor.Ok() )

		mpNode->mBgColor = wxColour();
}

bool WidgetGlyph::IsVisibleChild( size_t childNo )
{
	return TRUE;
}

/*** protected methods ***/

void WidgetGlyph::RegenerateLookBitmap()
{
	if ( !mpLookInfo ) 

		mpLookInfo = mpNode->mpView->GetWidgetDoc().FindLookForNode( *mpNode );

	if ( !mpLookInfo )
        return;

	if ( mpBmp )
        delete mpBmp;

	wxSize sz( mPixBounds.width, mPixBounds.height );

	mpBmp = new wxBitmap();

	WidgetEqSolver& solver = mpNode->mpView->GetWidgetDoc().GetEqSolver();

	// FIXME:: eliminate these cut&pastes

	solver.Init( mpNode->GetAttrGroupInfo( WidgetNode::WIDGET_PROPERTIES_GROUP ),
				 mpNode->GetAttrGroupData( (unsigned int)WidgetNode::WIDGET_PROPERTIES_GROUP ),
				 mpNode->mpView->GetWidgetDoc().GetUIType(),
				 &mpNode->GetTemplate() );

	int align = wxALIGN_LEFT;

	if ( mpNode->mpTempl->mAlignPropName.Length() )
	{
		wxString s = mpNode->GetNodeAttrValue( mpNode->mpTempl->mAlignPropName );

		if ( s == "wxALIGN_CENTER" )
			
			align = wxALIGN_CENTER;
		else
		if ( s== "wxALIGN_RIGHT" )

			align = wxALIGN_RIGHT;
	}

	bool ok = 
		mpNode->mpView->GetWidgetDoc().GetRenderer().
			RenderItemLook( *mpLookInfo, sz, wxPMBootSvc().GetMainWindow(),
							*mpBmp, GetCanvas(), 
							mpNode->GetFont(), mpNode->GetBgColor(), mpNode->GetFgColor(),
							align, &solver );
}

void WidgetGlyph::GetLimitingRectInUnits( XRect& r )
{
	mpNode->mpView->PixelRectToUnitRect( mPixBounds, r );

	if ( mpLookInfo )
	{
		r.x += mpLookInfo->mEdges[LEFT_LOOK_EDGE].mInset;
		r.y += mpLookInfo->mEdges[TOP_LOOK_EDGE].mInset;
		r.width -=  mpLookInfo->mEdges[LEFT_LOOK_EDGE].mInset +
				    mpLookInfo->mEdges[RIGHT_LOOK_EDGE].mInset;
		r.height -= mpLookInfo->mEdges[TOP_LOOK_EDGE].mInset +
				    mpLookInfo->mEdges[BOTTOM_LOOK_EDGE].mInset;
	}
}

/***** Implementation for class MyXCommandMgr *****/

MyXCommandMgr::MyXCommandMgr( wxDMCommandProcessor* pDMCmdProcessor, 
							  XCollection* pScene,
							  WidgetView* pView )

	: XCommandManager( pScene ),
	  mpMgr( pDMCmdProcessor ),
	  mpView( pView )
{}

void MyXCommandMgr::NotifyWillChange( XObject* pObj )
{
	mpMgr->NotifyWillChange( &((WidgetGlyph*)pObj)->GetNode() );
}

void MyXCommandMgr::NotifyWillRemove( XObject* pObj )
{
	mpMgr->NotifyAdded( &((WidgetGlyph*)pObj)->GetNode() );
}

void MyXCommandMgr::NotifyAdded( XObject* pObj )
{
	mpMgr->NotifyAdded( &((WidgetGlyph*)pObj)->GetNode() );
}

void MyXCommandMgr::StartBatch()
{
	mpMgr->StartBatch();
}

void MyXCommandMgr::FinishBatch()
{
	mpMgr->FinishBatch();
}

bool MyXCommandMgr::CanUndo()
{
	return mpMgr->CanUndo();
}

bool MyXCommandMgr::CanRedo()
{
	return mpMgr->CanRedo();
}

bool MyXCommandMgr::CanPaste()
{
	return mpMgr->CanPaste();
}

bool MyXCommandMgr::CanDelete()
{
	return mpMgr->CanDelete();
}

void MyXCommandMgr::Undo()
{
	mpMgr->Undo();
}

void MyXCommandMgr::Redo()
{
	mpMgr->Redo();
}

void MyXCommandMgr::Delete()
{
	mpMgr->Delete();
}

void MyXCommandMgr::Copy( XCollection* pScene )
{
	mpMgr->Copy();
}

void MyXCommandMgr::Paste( XCollection* pScene )
{
	mpMgr->Paste();
}

void MyXCommandMgr::SetCommandProcessor( wxDMCommandProcessor* pProc )
{
	mpMgr = pProc;
}

/***** Implementation for class MyXCollection *****/

MyXCollection::MyXCollection( WidgetView* pView )

	: mpView( pView ),
	  mReqsLocked( FALSE )
{}

void MyXCollection::NotifyWillChange( XObject* pObj )
{
	XCollection::NotifyWillChange( pObj );
}

void MyXCollection::NotifyWillRemove( XObject* pObj )
{
	XCollection::NotifyWillRemove( pObj );
}

void MyXCollection::NotifyAdded( XObject* pObj )
{
	XCollection::NotifyAdded( pObj );
}

void MyXCollection::SetFocus( XObject* pFocus, bool refreshNow )
{
	if ( mReqsLocked )
        return;

	mReqsLocked = TRUE;

	XCollection::SetFocus( pFocus, refreshNow );

	if ( pFocus )

		((WidgetGlyph*)pFocus)->GetNode().SetFocused( TRUE );

	mReqsLocked = FALSE;
}

void MyXCollection::SetSelection( XObjListT& newSelection, bool refreshNow )
{
	if ( mReqsLocked )
        return;

	mReqsLocked = TRUE;

		DoSetSelection( newSelection );

		XCollection::SetSelection( newSelection, refreshNow );

	mReqsLocked = FALSE;
}

void MyXCollection::ResetSelection()
{
	if ( mReqsLocked )
        return;

	mReqsLocked = TRUE;

		XCollection::ResetSelection();

		XObjListT emptyLst;
		DoSetSelection( emptyLst );

	mReqsLocked = FALSE;
}

void MyXCollection::SelectAll()
{
	if ( mReqsLocked )
        return;

	mReqsLocked = TRUE;

		XCollection::SelectAll();

		DoSetSelection( GetSelection() );

	mReqsLocked = FALSE;
}

void MyXCollection::DeleteSelection()
{
	XCollection::DeleteSelection();
}

void MyXCollection::BringSelectionToFront()
{
}

void MyXCollection::SendSelectionBack()
{
}

/*** protected methods ***/

void MyXCollection::DoSetSelection( XObjListT& lst )
{
	DMNodeListT nodeLst;

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		WidgetGlyph* pGlyph = (WidgetGlyph*)lst[i];

		wxASSERT( pGlyph ); // DBG::
		if (pGlyph)
		{
			// glt - added the IF to avoid a crash - though I do not know if this is the right behavior
			nodeLst.Add( &pGlyph->GetNode() );
		}
	}

	mpView->GetRootNode().SetSelection( nodeLst );
}

/***** Implementation for class wxMyDMPasteHandler *****/

void wxMyDMPasteHandler::HandleNodePasting( wxDMNode* pNode, wxDMNode* pToNode, int depth )
{
	WidgetNode* pWNode = (WidgetNode*)pNode;
	WidgetNode* pParent = (WidgetNode*)pToNode;

	int ofs = 0;

	if ( depth == 0 ) 
		
		ofs += 10;

	pWNode->mBounds.x += ofs;
	pWNode->mBounds.y += ofs;

	// do not allow child get out of it's parent's bounds

	if ( pWNode->mBounds.x + pWNode->mBounds.width >
		 pParent->mBounds.width )

		 pWNode->mBounds.x = pParent->mBounds.width - pWNode->mBounds.width;

	if ( pWNode->mBounds.y + pWNode->mBounds.height >
		 pParent->mBounds.height )

		 pWNode->mBounds.y = pParent->mBounds.height - pWNode->mBounds.height;

	pWNode->StoreBoundsInfo();

	// FIXME:: use dynamic cast
	((WidgetNode*)pWNode)->mpView = ((WidgetNode*)pToNode)->mpView;
}

/***** Implementation for class WidgetInsertionEditor *****/

WidgetInsertionEditor::WidgetInsertionEditor( WidgetView* pView, const wxString& itemId )

	: mWigetCode( -1 ),
	  mpView( pView )
{
	mItemId = itemId;

	mpPrevEditor = pView->GetScene().UnlinkEditor();

	pView->GetScene().SetEditor( this );

	wxCursor curs( wxCURSOR_HAND );
	pView->GetViewFrame()->SetCursor( curs );
}

WidgetInsertionEditor::WidgetInsertionEditor( WidgetView* pView, int widgetCode )

	: mWigetCode( widgetCode ),
	  mpView( pView )
{
	mpPrevEditor = pView->GetScene().UnlinkEditor();

	pView->GetScene().SetEditor( this );

	wxCursor curs( wxCURSOR_HAND );
	pView->GetViewFrame()->SetCursor( curs );
}

WidgetInsertionEditor::~WidgetInsertionEditor()
{
}

void WidgetView::FindInsertionTarget( const XPoint& pos, 
									  WidgetNode*& pTarget, int& orderNo,
									  WidgetNode* pNodeToExclude )
{
	XObjListT lst = mpScene->GetContents();

    int i;
	for( i = lst.Count() - 1; i >= 0; --i )
	{
		XObject* pObj = lst[i];

		if ( pObj->GetBounds().Contains( pos ) && 
			 pObj->IsKindOf( CLASSINFO( WidgetGlyph ) ) )
		{
			WidgetNode& node = ((WidgetGlyph*)pObj)->GetNode();

			if ( &node == pNodeToExclude ) continue;

			if ( node.mpTempl->mCanHaveChildren == TRUE )
			{
				pTarget = &node;
				orderNo = -1;

				return;
			}
			else
			if ( node.GetParent() && node.GetParent()->IsKindOf( CLASSINFO( SizerNodeBase ) ) )
			{
				// otherwise we hit non-container node, find out
				// at which side of it ("left" or "right") we should 
				// insert the dragged item

				XRect bounds = pObj->GetBounds();

				orderNo = node.GetOrderNo();

				if ( pos.x >= bounds.x + bounds.width  / 2 &&
					 pos.x >= bounds.y + bounds.height / 2 )

					 orderNo++; // insert after (on the "right");

				pTarget = (WidgetNode*)node.GetParent();

				return;
			}
			// otherwise it was the node which cannot contain
			// children as well as is not surrounded by sizer-parent
		}
	}

	pTarget = NULL; // insertion target not found
}

bool WidgetInsertionEditor::OnMouseDown( const XPoint& pos )
{
	DetachFromScene();

	WidgetNode* pTarget = NULL; int zOrder = -1;

	mpView->FindInsertionTarget( pos, pTarget, zOrder, NULL );

	if ( pTarget )
	{
		XRect parentBounds = mpView->GetGlyphForNode( *pTarget ).GetBounds();

		WidgetNode* pInserted = NULL;

		if ( mWigetCode == -1 ) // no-fresh-widget means we're linking to external object
		{
			ResDocument& doc = *((ResDocument*)mpView->GetWidgetDoc().GetParentDocument());

			Record* pRec = doc.FindTopLevelItem( mItemId );

			mpView->GetWidgetDoc().LoadNodesRecursively( *pRec, NULL, pTarget, 
														 mpView->GetTemplateManager(),
														 TRUE, NULL );

			for( size_t i = 0; i != pTarget->GetChildCount(); ++i )
			{
				WidgetNode* pChild = (WidgetNode*)pTarget->GetChild(i);

				if ( pChild->GetNodeAttrValue( "name" ) == mItemId )
				{
					pInserted = pChild;

					XRect bounds = pInserted->mBounds;

					bounds.x = pos.x - parentBounds.x;
					bounds.y = pos.y - parentBounds.y;

					// FIXME:: refreshing things twice!!!

					//pInserted->SetBounds( bounds, mpView->GetScene().GetCanvas() );

					break;
				}
			}

			if ( pTarget->mpTempl->mpExtraAttrGroup )
			{
				// FIXME:: duplicated code fragment

				WidgetNode* pNode = (WidgetNode*)pTarget->GetChild( pTarget->GetChildCount() - 1 );

				pNode->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ].
					SetGroupInfo( pTarget->mpTempl->mpExtraAttrGroup );

				mpView->GetTemplateManager().
					InitGroupData( pTarget->mpTempl->mExtraPropSet, 
								   pNode->mAttribGroups[ WidgetNode::EXTRA_PROPERTIES_GROUP ] );
			}


			pInserted->NotifyAdded( pInserted );
		}
		else
			// otherwise embed newly created widget "by value"

			// FIXME:: move this "pos.x - .. " conversion code to a separete method

			pInserted = mpView->GetWidgetDoc().
				InsertWidgetByCode( mWigetCode, pTarget, XPoint( pos.x - parentBounds.x, 
																 pos.y - parentBounds.y ),
									zOrder );

		if ( pInserted && mpView->FindGlyphForNode( *pInserted ) )
		
			mpView->GetScene().SelectObject( mpView->GetGlyphForNode( *pInserted ), FALSE );

		mpView->GetScene().RefreshNow();
	}

	delete this; // destroy ourselves!

	return TRUE;
}

bool WidgetInsertionEditor::OnRightClick( const XPoint& pos )
{
	CancelCreation();

	return TRUE;
}

bool WidgetInsertionEditor::OnMouseMove( const XPoint& pos )
{
	return TRUE; // "eat" movement events
}

void WidgetInsertionEditor::CancelCreation()
{
	DetachFromScene();

	delete this; // destroy ourselves!
}

void WidgetInsertionEditor::DetachFromScene()
{
	XCollection* pScene = (XCollection*)&GetParent();

	pScene->UnlinkEditor();

	pScene->SetEditor( mpPrevEditor );

	wxCursor curs( wxCURSOR_ARROW  );
	mpView->GetViewFrame()->SetCursor( curs );
}

/***** Implementation for class GridSettingsDialog *****/

BEGIN_EVENT_TABLE( GridSettingsDialog, wxDialog )

	EVT_BUTTON( wxID_OK, GridSettingsDialog::OnOk )
	EVT_LISTBOX_DCLICK( ID_GRIT_TYPES, GridSettingsDialog::OnDblClick )

END_EVENT_TABLE()

GridSettingsDialog::GridSettingsDialog( wxWindow* pParent, 
										int gridType,
										const wxSize& gridSpacing,
										const wxSize& snapSpacing )

	: wxDialog( pParent,-1, "Grid Settings", wxDefaultPosition, wxSize( 200, 250 ),
				wxSYSTEM_MENU | wxCAPTION | 
				wxTAB_TRAVERSAL | wxTHICK_FRAME | wxRESIZE_BORDER ),
	  mGridType( gridType ),
	  mGridSpacing( gridSpacing ),
	  mSnapSpacing( snapSpacing )
{
	// create contols first
	wxString types[] = { "None", "Lines", "Dots", "Dots-3D" };
	wxString spacings[] = { "1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20" };

	mpTypes = new wxListBox( this, ID_GRIT_TYPES, wxDefaultPosition, wxDefaultSize, 4, types );
	mpGridSpacing = new wxChoice( this, -1, wxDefaultPosition, wxDefaultSize, 20, spacings );
	mpSnapSpacing = new wxChoice( this, -1, wxDefaultPosition, wxDefaultSize, 20, spacings );

	mpTypes->SetSelection( mGridType );

	if ( mGridSpacing.x >= 1 && mGridSpacing.x <= 20 )

		mpGridSpacing->SetSelection( mGridSpacing.x - 1 );
	else
		mpGridSpacing->SetSelection( 0 );

	if ( mSnapSpacing.x >= 1 && mSnapSpacing.x <= 20 )

		mpSnapSpacing->SetSelection( mSnapSpacing.x - 1 );
	else
		mpSnapSpacing->SetSelection( 0 );

	Container& cont = *new Container( this );
	cont.setInsets( Insets( 5,4,5,4 ) );
	cont.setLayout( new GridBagLayout() );

	Container* btnPanel = new Container();
	btnPanel->setInsets( Insets( 15,5,5,5 ) );
	btnPanel->setLayout( new FlowLayout() );

	wxButton* okBtn = new wxButton( this, wxID_OK, "&Ok", wxDefaultPosition );

	btnPanel->add( okBtn );
	btnPanel->add( new wxButton( this, wxID_CANCEL, "C&ancel", wxDefaultPosition ) );

	GridBagConstraints c;
	c.weightx = 1.0; c.weighty = 1.0;
	c.anchor = GridBagConstraints::WEST;
	c.insets.top = 5;
	c.insets.left = 5;

	c.weightx = 0.0; c.weighty = 1.0;
	cont.add( new wxStaticText( this,-1, "Grid type:" ), &c );
	c.fill = GridBagConstraints::BOTH;
	c.weightx = 1.0; c.gridwidth = GridBagConstraints::REMAINDER;
	cont.add( mpTypes, &c );

	c.weightx = 0.0; c.weighty = 0.0; c.gridwidth = 1;
	cont.add( new wxStaticText( this,-1, "Grid Spacing:" ), &c );
	c.weightx = 1.0; c.gridwidth = GridBagConstraints::REMAINDER;
	cont.add( mpGridSpacing, &c );

	c.weightx = 0.0; c.gridwidth = 1;
	cont.add( new wxStaticText( this,-1, "Snap Spacing:" ), &c );
	c.weightx = 1.0; c.gridwidth = GridBagConstraints::REMAINDER;
	cont.add( mpSnapSpacing, &c );

	c.weightx = 0.0;
	cont.add( btnPanel, &c );

	okBtn->SetDefault();
	mpTypes->SetFocus();
	cont.doLayout();

	Center( wxBOTH );
}
int GridSettingsDialog::GetGridType()
{
	return mGridType;
}

wxSize& GridSettingsDialog::GetGridSpacinig()
{
	return mGridSpacing;
}

wxSize& GridSettingsDialog::GetSnapSpacing()
{
	return mSnapSpacing;
}

void GridSettingsDialog::OnOk( wxCommandEvent& event )
{
	mGridSpacing.x = StrToInt( mpGridSpacing->GetStringSelection() );
	mGridSpacing.y = mGridSpacing.x;

	mSnapSpacing.x = StrToInt( mpSnapSpacing->GetStringSelection() );
	mSnapSpacing.y = mSnapSpacing.x;

	mGridType = mpTypes->GetSelection();

	EndModal( wxID_OK );
}

void GridSettingsDialog::OnDblClick( wxCommandEvent& event )
{
	OnOk( event );
}

/***** Implementation for class SizerNodeBase *****/

IMPLEMENT_CLASS( SizerNodeBase, WidgetNode )

// FIXME:: this belongs to sizerglyphs.cpp

void SizerNodeBase::GetLayoutProps( WidgetNode& node, 
								    int& option, int& flag, int& border )
{
	option = 0; flag = 0; 
	
	border = StrToInt( node.GetNodeExtraAttrValue( "border" ) );

	option = node.GetNodeExtraAttrValue( "isStrechable" ) == "1" ? 1 : 0;

	if ( node.GetNodeExtraAttrValue( "isExpandable" ) == "1" )

		flag |= wxGROW;

	if ( node.GetNodeExtraAttrValue( "hasFullBorder" ) == "1" )

		flag |= wxALL;

	if ( node.GetNodeExtraAttrValue( "hasLeftBorder" ) == "1" )

		flag |= wxLEFT;

	if ( node.GetNodeExtraAttrValue( "hasRightBorder" ) == "1" )

		flag |= wxRIGHT;

	if ( node.GetNodeExtraAttrValue( "hasTopBorder" ) == "1" )

		flag |= wxTOP;

	if ( node.GetNodeExtraAttrValue( "hasBottomBorder" ) == "1" )

		flag |= wxBOTTOM;

	wxString align = node.GetNodeExtraAttrValue( "align" );

	if ( align == "wxALIGN_CENTER" )

		flag |= wxALIGN_CENTER;
	else
	if ( align == "wxALIGN_LEFT" )

		flag |= wxALIGN_LEFT;
	else
	if ( align == "wxALIGN_RIGHT" )

		flag |= wxALIGN_RIGHT;
	else
	if ( align == "wxALIGN_BOTTOM" )

		flag |= wxALIGN_BOTTOM;
	else
	if ( align == "wxALIGN_TOP" )

		flag |= wxALIGN_TOP;
}

/***** Implementation for class MultiFaceNodeBase *****/

IMPLEMENT_CLASS( MultiFaceNodeBase, WidgetNode )

MultiFaceNodeBase::MultiFaceNodeBase()

	: WidgetNode()
{}

void MultiFaceNodeBase::DoLayoutChildren( wxDC& dc )
{
	// TBD:: comments...

	if ( mpView->FindGlyphForNode( *this ) == NULL )

		return;

	XRect lbounds = GetLimitingBounds();
	lbounds.x = 0;
	lbounds.y = 0;

	for( size_t i = 0; i != mChildren.Count(); ++i )
	{
		WidgetNode& node = *(WidgetNode*)mChildren[i];

		if ( lbounds.width == 0 )
		{
			int o = 0;
			GetLimitingBounds();
			++o;
		}

		node.SetBounds( lbounds, dc );
	}
}

void MultiFaceNodeBase::OnChildRemoved( wxDMNode* pNode )
{
	WidgetNode::OnChildRemoved( pNode );

	DoUpdateGlyphForNode( pNode );

	if ( GetChildCount() )
	{
		MultiFaceGlyphBase* pGlyph = (MultiFaceGlyphBase*)mpView->FindGlyphForNode( *(WidgetNode*)this );

		if ( pGlyph )
		
			pGlyph->SetVisibleFace( 0 );
	}
}

void MultiFaceNodeBase::OnChildInserted( wxDMNode* pNode )
{
	WidgetNode::OnChildInserted( pNode );

	DoUpdateGlyphForNode( pNode );

	MultiFaceGlyphBase* pGlyph = (MultiFaceGlyphBase*)mpView->FindGlyphForNode( *(WidgetNode*)this );

	if ( pGlyph )
	{
		XRect lbounds = this->GetLimitingBounds();
		lbounds.x = lbounds.y = 0;

		((WidgetNode*)pNode)->SetBounds( lbounds, pGlyph->GetCanvas() );

		pGlyph->SetVisibleFace( GetChildCount() - 1 );
	}
}

void MultiFaceNodeBase::OnChildChanged( wxDMNode* pNode )
{
	DoUpdateGlyphForNode( pNode );
}

/*** protected methods ***/

void MultiFaceNodeBase::DoUpdateGlyphForNode( wxDMNode* pChild )
{
	WidgetGlyph* pGlyph = mpView->FindGlyphForNode( *(WidgetNode*)this );

	if ( pGlyph ) 
	{	
		// FIXME:: use wxDynamicCast

		((MultiFaceGlyphBase*)pGlyph)->RecalcFaceGeometry( pGlyph->GetCanvas() );

		pGlyph->InvalidateRect( pGlyph->GetInclusiveBounds() );
	}
}

/***** Implementation for class MultiFaceGlyphBase *****/

IMPLEMENT_CLASS( MultiFaceGlyphBase, WidgetGlyph )

MultiFaceGlyphBase::MultiFaceGlyphBase()

	: WidgetGlyph(),
	  mVisibleFace( 0 )
{}

void MultiFaceGlyphBase::RecalcFaceGeometry( wxDC& dc )
{
	// nothing by default
}

bool MultiFaceGlyphBase::IsVisibleChild( size_t childNo )
{
	return mVisibleFace == childNo;
}

void MultiFaceGlyphBase::SetVisibleFace( int faceIndex )
{
	mVisibleFace = faceIndex;

	InvalidateRect( GetInclusiveBounds() );

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

		mpNode->mpView->RemoveGlyphsForNode( *(WidgetNode*)mpNode->GetChild(i) );

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

		mpNode->mpView->CreateGlyphsForNode( *(WidgetNode*)mpNode->GetChild(i) );

	RefreshNow();
}

size_t MultiFaceGlyphBase::GetVisibleFace()
{
	return mVisibleFace;
}
