/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: menuedit.cpp,v 1.9 2001/04/21 11:46:10 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

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

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

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

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

#include "wx/log.h"

#include "menuedit.h"
#include "res_doc.h"
#include "dmbrowser.h"
#include "myutils.h"

#include "propedit.h"

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

#include "wx/pmf/pm_undef.h"

//#define LOG(x) wxLogMessage(x);
#define LOG(x) ;

/***** Implementation for class wxME_Node *****/

IMPLEMENT_DYNAMIC_CLASS( wxME_Node, wxDMCompositeNode )

wxDMAttrGroupInfo* wxME_Node::mpsMENodeAttribGroup = NULL;

wxME_Node::wxME_Node( const wxString& title, const wxString& hotKey, int flags )

	: mTitle( title ),
	  mHotKey( hotKey ),
	  mMenuFlags( flags ),
	  mpView( NULL )
{}

size_t wxME_Node::GetGroupCount() 
{
	return 1;
}

wxDMAttrGroupInfo* wxME_Node::GetAttrGroupInfo( size_t index )
{
	if ( mpsMENodeAttribGroup == NULL )
	{
		mpsMENodeAttribGroup = new wxDMAttrGroupInfo();

		mpsMENodeAttribGroup->SetName( "Properties" );

		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_TITLE, "title", "Title" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_NAME, "name", "Name (id)" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_HOTKEY, "hotkey", "Hot key" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_HELP, "help", "Help string" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_PLATFORM, "platform", "Platform" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_IS_SEPARATOR, "separator", "Is separator" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_IS_DISABLED, "disabled", "Disabled" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_IS_CHECKABLE, "checkable", "Is Checkable" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_IS_CHECKED, "checked", "Is Checked" ) );
		mpsMENodeAttribGroup->Add( new wxME_AttrInfo( wxME_AttrInfo::ATTR_IS_BOLD, "bold", "Is Bold" ) );
	}

	return mpsMENodeAttribGroup;
}

void* wxME_Node::GetAttrGroupData( size_t index )
{
	return (void*)this;
}

void* wxME_Node::GetAttrGroupData( wxDMAttrGroupInfo* pForGroup )
{
	return (void*)this; // here we always have one group
}

wxString wxME_Node::GetDisplayName()
{
	return mName;
}

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

	AssignData( pCopy, uniqueCopy, cloneChildren );

	return pCopy;
}

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

	wxME_Node& copy = *(wxME_Node*)pCopy;

	copy.mName = mName;
	copy.mTitle = mTitle;
	copy.mHelp = mHelp;
	copy.mHotKey = mHotKey;
	copy.mPlatform = mPlatform;
	copy.mMenuFlags = mMenuFlags;
	copy.mpView = mpView;

	if ( uniqueCopy )

		copy.mName = mpView->GenerateNewId();
}

/***** Implementation for class wxME_NodePainter *****/

wxME_NodeRenderer::wxME_NodeRenderer( wxWindow* pWnd )

	: mBgBrush( wxColour(192,192,192), wxSOLID ),
	  mpDc( NULL )
{
	mNormalFont = pWnd->GetFont();
	mBoldFont = pWnd->GetFont();
	mBoldFont.SetWeight( wxBOLD );

	mNormalFontUL = pWnd->GetFont();
	mBoldFontUL = pWnd->GetFont();
	mNormalFontUL.SetUnderlined( TRUE );
	mBoldFontUL.SetUnderlined( TRUE );
}

wxME_NodeRenderer::~wxME_NodeRenderer()
{}

wxSize wxME_NodeRenderer::GetItemColumnSize( wxME_Node& item, int columnNo )
{
	if ( item.mMenuFlags & wxME_SEPARATOR )

		return wxSize(0, GetSeparatorHeight() );

	if ( columnNo == wxME_CHECK_COLUMN )

		return wxSize( 8,1 );
	else
	if ( columnNo == wxME_IMAGE_COLUMN )

		return wxSize(0,0); // FOR NOW::
	else
	if ( columnNo == wxME_NAME_COLUMN ||
		 columnNo == wxME_HOTKEY_COLUMN )
	{
		mpDc->SetFont( GetItemFont( item ) );
		long w = 0, h = 0;

		wxString txt = columnNo == wxME_NAME_COLUMN 
					   ? item.mTitle : item.mHotKey;

		mpDc->GetTextExtent( StripControlChars( txt ), &w, &h );

		return wxSize( w, h );
	}
	else
	if ( columnNo == wxME_LAST_COLUMN )
	{
		return wxSize( 16,0 );
	}

	return wxSize(0,0); // never reached
}

wxSize wxME_NodeRenderer::GetBarPadding( bool isVertical )
{
	return wxSize(4,4);
}

wxSize wxME_NodeRenderer::GetItemPadding( wxME_Node& item, int visFlags, bool isVertical )
{
	if ( isVertical )
	{
		if ( visFlags & wxME_VIS_TARGETED )

			return wxSize( 10,10 );
		else
		if ( visFlags & wxME_VIS_EDITED )

			return wxSize(4+10,8+10);
		else
			return wxSize(4,8);
	}
	else
	{
		if ( visFlags & wxME_VIS_TARGETED )

			return wxSize( 10,10 );
		else
		if ( visFlags & wxME_VIS_EDITED )

			return wxSize(12+10,8+10);
		else
			return wxSize(12,8);
	}
}

int wxME_NodeRenderer::GetInterColumnGap( bool isVertical )
{
	return 5;
}

int wxME_NodeRenderer::GetInterItemGap( bool isVertical )
{
	return 0;
}

void wxME_NodeRenderer::DrawBarBorder( wxDC& dc, const wxRect& bounds, bool isVertical )
{
	const wxRect& r = bounds;

	dc.SetBrush( mBgBrush );
	dc.SetPen( *wxTRANSPARENT_PEN );
	dc.DrawRectangle( r.x, r.y, r.width+1, r.height+1 );

	dc.SetPen( *wxWHITE_PEN );
	dc.DrawLine( r.x+1, r.y+1, r.x + r.width-2, r.y+1  );
	dc.DrawLine( r.x+1, r.y+1, r.x+1, r.y + r.height-2 );

	dc.SetPen( *wxBLACK_PEN );
	dc.DrawLine( r.x, r.y + r.height-1, r.x + r.width, r.y + r.height-1 );
	dc.DrawLine( r.x + r.width-1, r.y, r.x + r.width-1, r.y + r.height-1  );

	dc.SetPen( *wxGREY_PEN );
	dc.DrawLine( r.x+1, r.y + r.height-2, r.x + r.width-1, r.y + r.height-2 );
	dc.DrawLine( r.x + r.width-2, r.y+1, r.x + r.width-2, r.y + r.height-2 );
}

void wxME_NodeRenderer::DrawItemColum( wxDC& dc, wxME_Node& item, 
									   const wxRect& bounds, int columnNo, 
									   int visFlags, bool isVertical )
{
	if ( ( columnNo == wxME_NAME_COLUMN ||
		   columnNo == wxME_HOTKEY_COLUMN )  )
	{
		if ( item.mMenuFlags & wxME_SEPARATOR ) return;

		dc.SetBackgroundMode( wxTRANSPARENT );

		wxString txt = columnNo == wxME_NAME_COLUMN 
					   ? item.mTitle : item.mHotKey;

		bool underLine = FALSE;
		size_t i = 0;
		wxString subStr = "";
		int curX = bounds.x;

		if ( visFlags & wxME_VIS_SELECTED )	dc.SetTextForeground( *wxWHITE );
									   else dc.SetTextForeground( *wxBLACK );

		// interchangably draw normal and underlined fragments

		while( GetNextSubstring( txt, subStr, i, underLine ) )
		{
			if ( underLine ) 
			{
				if ( item.mMenuFlags & wxME_BOLD ) dc.SetFont( mBoldFontUL );
										  else dc.SetFont( mNormalFontUL );
			}
			else
			{
				if ( item.mMenuFlags & wxME_BOLD ) dc.SetFont( mBoldFont );
										  else dc.SetFont( mNormalFont );
			}

			dc.DrawText( subStr, curX, bounds.y );

			long w = 0, h = 0;
			mpDc->GetTextExtent( subStr, &w, &h );

			curX += w;
		}

	}
	else
	if ( columnNo == wxME_CHECK_COLUMN && item.mMenuFlags & wxME_CHECKED )
	{
		long w = 0, h = 0;
		mpDc->SetFont( mNormalFont );
		mpDc->GetTextExtent( "X", &w, &h );

		int x = bounds.x + 2;
		int y = bounds.y + (h - 7)/2 + 3;

		if ( visFlags & wxME_VIS_SELECTED )	dc.SetPen( *wxWHITE_PEN );
									   else dc.SetPen( *wxBLACK_PEN );

		// look of M$ with precision...

        int n;
		for( n = 0; n != 3; ++n )
		{
			dc.DrawLine( x,y+n, x+2,y+2+n );
			dc.DrawLine( x+2,y+n+2,x+7,y+n-3 );
		}
	}
	else
	if ( columnNo == wxME_LAST_COLUMN && item.HasChildren() && isVertical )
	{
		long w = 0, h = 0;
		mpDc->SetFont( mNormalFont );
		mpDc->GetTextExtent( "X", &w, &h );

		// draw "expansion-triangle"

		int x = bounds.x + 10;
		int y = bounds.y + (h - 7)/2;

		if ( visFlags & wxME_VIS_SELECTED )	dc.SetPen( *wxWHITE_PEN );
									   else dc.SetPen( *wxBLACK_PEN );

		dc.DrawLine( x + 0, y + 0, x + 0, y + 7 );
		dc.DrawLine( x + 1, y + 1, x + 1, y + 6 );
		dc.DrawLine( x + 2, y + 2, x + 2, y + 5 );
		dc.DrawLine( x + 3, y + 3, x + 3, y + 4 ); 
	}
	else
	if ( columnNo == wxME_IMAGE_COLUMN )
	{
		// TBD::
	}
}

bool wxME_NodeRenderer::GetNextSubstring( const wxString& text, wxString& subStr, 
									      size_t& i, bool& underline )
{
	size_t len = text.Length();

	if ( i >= len ) return FALSE;

	if ( text[i] == '&' )
	{
		subStr = ""; ++i;

		if ( i < len )
		{
			subStr = text[i];
			++i;
			underline = TRUE;

			return TRUE;
		}

		return FALSE;
	}
	else
	{
		size_t start = i;

		while( i < len && text[i] != '&' ) ++i;

		underline = FALSE;
		subStr = wxString( text, start, i - start );

		return TRUE;
	}
}

#define _A  0xAA
#define _B  0x00
#define _C  0x55
#define _D  0x00

// FOR NOW:: static

static const unsigned char _cCheckerImg[16] = { _A,_B,_C,_D,
											    _A,_B,_C,_D,
											    _A,_B,_C,_D,
											    _A,_B,_C,_D
											  };

void wxME_NodeRenderer::DrawItemContent( wxDC& dc, wxME_Node& item, 
										 const wxRect& bounds, 
										 int visFlags, bool isVertical )
{
	if ( visFlags & wxME_VIS_SELECTED )
	{
		wxBrush br( wxColour(0,0,128), wxSOLID  );
		dc.SetBrush( br );
		dc.SetPen( *wxTRANSPARENT_PEN );

		dc.DrawRectangle( bounds.x, bounds.y, bounds.width, bounds.height );
	}

	if ( visFlags & wxME_VIS_BLANK )
	{
		// TBD::
		wxPen pen( *wxBLACK, 1, wxDOT );		   
		dc.SetPen( pen );

		wxRect r = ExcludePadding( bounds, GetItemPadding( item, visFlags, isVertical ) );

		dc.DrawLine( r.x, r.y, r.x+r.width, r.y );
		dc.DrawLine( r.x+r.width, r.y, r.x+r.width, r.y+r.height );
		dc.DrawLine( r.x+r.width, r.y+r.height, r.x, r.y+r.height );
		dc.DrawLine( r.x, r.y+r.height, r.x, r.y );
	}

	if ( item.mMenuFlags & wxME_SEPARATOR )
	{
		int y = bounds.y + bounds.height/2;

		dc.SetPen( *wxGREY_PEN );
		dc.DrawLine( bounds.x, y, bounds.x + bounds.width, y );

		dc.SetPen( *wxWHITE_PEN ); ++y;
		dc.DrawLine( bounds.x, y, bounds.x + bounds.width, y );
	}

	if ( visFlags & wxME_VIS_EDITED )
	{
		// this is ripped from wxFrameLayout contrib ("bardragpl.cpp:855")

		wxBitmap checker( (const char*)_cCheckerImg, 8,8 );
		wxBrush checkerBrush( checker );

		dc.SetBackgroundMode( wxSOLID  );
		dc.SetTextForeground( *wxBLACK );
		dc.SetPen( *wxTRANSPARENT_PEN );
		dc.SetBrush( checkerBrush );

		// FIXME:: this is real magic... without it the checker-outline gets
		//		   white when the above item is highlighted
		dc.DrawText( "", 0, 0 );

		int border = 4;
		int half = border / 2;

		wxRect r = bounds;
		r.x += half;
		r.y += half;
		r.width  -= half*2;
		r.height -= half*2;

		dc.DrawRectangle( r.x - half, r.y - half,
								r.width + 2*half, border );

		dc.DrawRectangle( r.x - half, r.y + r.height - half,
								r.width + 2*half, border );

		dc.DrawRectangle( r.x - half, r.y + half - 1,
								border, r.height - 2*half + 2);

		dc.DrawRectangle( bounds.x + r.width - half + 1,
								r.y + half - 1,
								border, r.height - 2*half + 2);

		dc.SetBrush( wxNullBrush );

	}
}

wxRect wxME_NodeRenderer::ExcludePadding( const wxRect& fromRect, const wxSize& pad )
{
	wxRect r = fromRect;

	r.x += pad.x/2;
	r.y += pad.y/2;
	r.width  -= pad.x;
	r.height -= pad.y;

	return r;
}


void wxME_NodeRenderer::PostDrawContent( wxDC& dc, wxME_Node& item, 
									     const wxRect& bounds, int visFlags )
{
	if ( item.mMenuFlags & wxME_DISABLED )
	{
		// TBD:: 
	}
}

void wxME_NodeRenderer::SetDc( wxDC* pDc )
{
	mpDc = pDc;
}

wxString wxME_NodeRenderer::StripControlChars( const wxString& txt )
{
	wxString result;

    size_t i;
	for( i = 0; i != txt.Length(); ++i )

		if ( txt[i] != '\t' && txt[i] != '&' )

			result += txt[i];

	return result;
}

wxFont& wxME_NodeRenderer::GetItemFont( wxME_Node& item )
{
	if ( item.mMenuFlags & wxME_BOLD ) return mBoldFont;
							  else return mNormalFont;
}

int wxME_NodeRenderer::GetSeparatorHeight()
{
	return 10;
}

/***** Implementation for class wxME_VisItemData *****/

wxME_VisItemData::wxME_VisItemData()
{
}

/***** Implementation for class wxME_VisItemContaienr *****/

wxME_VisItemContainer::wxME_VisItemContainer()
	: mpRenderer( NULL ),
	  mpView( NULL ),
	  mpHiglightedItem( NULL )
{}

wxME_VisItemContainer::~wxME_VisItemContainer()
{
	WX_CLEAR_ARRAY( mVisItems );
}

bool wxME_VisItemContainer::OnMouseDown( const XPoint& pos )
{
	if ( mpHiglightedItem && 
		 !( mpHiglightedItem->mVisFlags & wxME_VIS_EDITED ) )
	{
		mpView->SetEditedNodeName( mpHiglightedItem->mpItem->mName );

		mpView->RecreateVisualItems();
	}

	return TRUE;
}

bool wxME_VisItemContainer::OnMouseUp( const XPoint& pos )
{
	return TRUE;
}

bool wxME_VisItemContainer::OnMouseMove( const XPoint& pos )
{
	bool hadHighlight = mpHiglightedItem != NULL;

	wxME_VisItemData* pHitted = HitTestItem( pos );
	bool refresh = FALSE;

	if ( pHitted && pHitted != mpHiglightedItem )
	{
		pHitted->mVisFlags |= wxME_VIS_SELECTED;
		refresh = TRUE;
	}

	if ( mpHiglightedItem && mpHiglightedItem != pHitted )
	{
		mpHiglightedItem->mVisFlags = mpHiglightedItem->mVisFlags & ~wxME_VIS_SELECTED;
		mpHiglightedItem->mVisFlags = mpHiglightedItem->mVisFlags & ~wxME_VIS_OPENED;
		refresh = TRUE;
	}

	mpHiglightedItem = pHitted;

	if ( refresh )
	{
		InvalidateRect( this->GetBounds() );
		this->RefreshNow();
	}

	if ( !hadHighlight && mpHiglightedItem )
	{
		this->SetMouseFocus( this );
	}
	if ( hadHighlight && !mpHiglightedItem )
	{
		this->ResetMouseFocus( this );
	}

	if ( mpHiglightedItem && refresh &&
		 mpHiglightedItem->mpItem->HasChildren() &&
		 !(mpHiglightedItem->mVisFlags & wxME_VIS_OPENED)
	   )
	{
		mpHiglightedItem->mVisFlags |= wxME_VIS_OPENED;

		this->ResetMouseFocus( this );
		mpView->OpenItem( mpHiglightedItem->mpItem, this );
	}

	return TRUE;
}

bool wxME_VisItemContainer::OnKeyDown( const wxKeyEvent& keyInfo )
{
	return TRUE;
}

bool wxME_VisItemContainer::OnDblClick( const XPoint& pos )
{
	return TRUE;
}

bool wxME_VisItemContainer::OnRightClick( const XPoint& pos )
{
	return TRUE;
}

void wxME_VisItemContainer::SetRenderer( wxME_NodeRenderer* pRenderer )
{
	mpRenderer = pRenderer;
}

void wxME_VisItemContainer::SetView( MenuEditorView* pView )
{
	mpView = pView;
}

void wxME_VisItemContainer::SetHighlight( wxME_VisItemData* pData )
{
	mpHiglightedItem = pData;
}

void wxME_VisItemContainer::AddVisItem( wxME_VisItemData* pData )
{
	mVisItems.Add( pData );
}

void wxME_VisItemContainer::LayoutVisItems( wxDC& dc )
{
	wxFAIL_MSG( "LayoutVisItems() should be overridden in the derived classes" );
}

void wxME_VisItemContainer::Draw( wxDC& dc )
{
	mpRenderer->SetDc( &dc );

	mpRenderer->DrawBarBorder( dc, mBounds, IsVertical() );

    size_t i;
	for( i = 0; i != mVisItems.Count(); ++i )
	{
		wxME_VisItemData& data = *mVisItems[i];

		mpRenderer->DrawItemContent( dc, *data.mpItem, 
									 data.mBounds, data.mVisFlags, IsVertical() );

		wxSize pad = mpRenderer->GetItemPadding( *data.mpItem, data.mVisFlags, IsVertical() );

		int from = 0, till = wxME_LAST_COLUMN;

		if ( !IsVertical() )
		{
			from = wxME_NAME_COLUMN;
			till = wxME_NAME_COLUMN;
		}

        size_t col;
		for( col = from; col <= (unsigned)till; ++col )
		{
			wxRect r = data.mBounds;
			r.x += pad.x/2;
			r.y += pad.y/2;

			r.x += data.mColX[col];

			mpRenderer->DrawItemColum( dc, *data.mpItem, r, 
									   col, data.mVisFlags,
									   IsVertical() );
		}

		mpRenderer->PostDrawContent( dc, *data.mpItem, data.mBounds, data.mVisFlags );
	}
}

wxME_VisItemData* wxME_VisItemContainer::HitTestItem( const XPoint& pos )
{
    size_t i;
	for( i = 0; i != mVisItems.Count(); ++i )
	{
		wxRect& r = mVisItems[i]->mBounds;

		if ( pos.x >= r.x && pos.x < r.x + r.width &&
		     pos.y >= r.y && pos.y < r.y + r.height )
            return mVisItems[i];
	}

	return NULL;
}

XRect wxME_VisItemContainer::GetBounds()
{
	return XRect( mBounds.x, mBounds.y, mBounds.width, mBounds.height );
}

void wxME_VisItemContainer::SetBounds( const XRect& bounds )
{
	mBounds.x = bounds.x;
	mBounds.y = bounds.y;
	mBounds.width  = bounds.width;
	mBounds.height = bounds.height;
}

void wxME_VisItemContainer::Layout( wxDC& dc )
{
	mpRenderer->SetDc( &dc );

	if ( IsVertical() )
	{
		int colSizes[] = { 0,0,0,0,0,0,0 };

		// first caclculate dimensions of all columns within each item

		size_t i;
		for( i = 0; i != mVisItems.Count(); ++i )
		{
			wxME_VisItemData& data = *mVisItems[i];
			wxME_Node&		  item = *data.mpItem;

			int maxHeight = 0;
			wxSize pad = mpRenderer->GetItemPadding( item, data.mVisFlags, IsVertical() );

            size_t col;
			for( col = 0; col <= wxME_LAST_COLUMN; ++col )
			{
				wxSize sz = mpRenderer->GetItemColumnSize( item, col );

				if ( colSizes[col] < sz.x ) colSizes[col] = sz.x;
					
				if ( sz.y > maxHeight ) maxHeight = sz.y;
			}

			data.mBounds.height = maxHeight + pad.y;
		}

		// afterwards, position items with columns aligned uniformly
		
		for( i = 0; i != mVisItems.Count(); ++i )
		{
			wxME_VisItemData& data = *mVisItems[i];
			wxME_Node&		  item = *data.mpItem;

			wxSize pad = mpRenderer->GetItemPadding( item, data.mVisFlags, IsVertical() );

			int x = 0;
            size_t col;
			for( col = 0; col <= wxME_LAST_COLUMN; ++col )
			{
				if ( colSizes[col] == 0 ) continue;

				if ( col != 0 )

					x += mpRenderer->GetInterColumnGap( IsVertical() );

				data.mColX[col] = x;

				x += colSizes[col];
			}

			data.mBounds.width  = x + pad.x;
		}
	}
	else
		// otherwise for horizontal contaienr
	{
        size_t i;
		for( i = 0; i != mVisItems.Count(); ++i )
		{
			wxME_VisItemData& data = *mVisItems[i];
			wxME_Node&		  item = *data.mpItem;

			int maxHeight = 0, x = 0;
			wxSize pad = mpRenderer->GetItemPadding( item, data.mVisFlags, IsVertical() );

            size_t col;
			for( col = wxME_NAME_COLUMN; col <= wxME_NAME_COLUMN; ++col )
			{
				if ( col != wxME_NAME_COLUMN )

					x += mpRenderer->GetInterColumnGap( IsVertical() );

				data.mColX[col] = x;

				wxSize sz = mpRenderer->GetItemColumnSize( item, col );

				x += sz.x;
					
				if ( sz.y > maxHeight ) 
					
					maxHeight = sz.y;
			}

			data.mBounds.width  = x + pad.x;
			data.mBounds.height = maxHeight + pad.y;
		}
	}

	LayoutVisItems( dc ); // let derived classes finalize their specific layouting 
						  // of items in this container
}

bool wxME_VisItemContainer::IsVertical()
{
	return TRUE; // just a default
}

/***** Implementation for class wxME_VertMenu *****/

wxME_VertMenu::wxME_VertMenu()
{
}

void wxME_VertMenu::LayoutVisItems( wxDC& dc )
{
	wxSize pad = mpRenderer->GetBarPadding( IsVertical() );
	
	int x = mBounds.x + pad.x/2;
	int y = mBounds.y + pad.y/2;
	int maxWidth = 0;

    size_t i;
	for( i = 0; i != mVisItems.Count(); ++i )
	{
		wxME_VisItemData& data = *mVisItems[i];

		if ( i != 0 ) 
			y += mpRenderer->GetInterItemGap( IsVertical() );

		data.mBounds.x = x;
		data.mBounds.y = y;

		if ( data.mBounds.width > maxWidth ) 
			maxWidth = data.mBounds.width;

		y += data.mBounds.height;
	}

	maxWidth += pad.x;
	y += pad.y/2;

	// adjust the extent of item container after relayouting of it's content

	mBounds.width  = maxWidth;
	mBounds.height = y - mBounds.y;
}

bool wxME_VertMenu::IsVertical()
{
	return TRUE;
}

/***** Implementation for class wxME_HorizMenu *****/

wxME_HorizMenu::wxME_HorizMenu()
{
}

void wxME_HorizMenu::LayoutVisItems( wxDC& dc )
{
	wxSize pad = mpRenderer->GetBarPadding( IsVertical() );
	
	int x = mBounds.x + pad.x/2;
	int y = mBounds.y + pad.y/2;
	int maxHeight = 0;

    size_t i;
	for( i = 0; i != mVisItems.Count(); ++i )
	{
		wxME_VisItemData& data = *mVisItems[i];

		if ( i != 0 ) 
			x += mpRenderer->GetInterItemGap( IsVertical() );

		data.mBounds.x = x;
		data.mBounds.y = y;

		if ( data.mBounds.height > maxHeight ) 
			maxHeight = data.mBounds.height;

		x += data.mBounds.width;
	}

	maxHeight += pad.x;
	y += pad.y/2;

	// adjust the extent of item container after relayouting of it's content

	mBounds.width  = x - mBounds.x;
	mBounds.height = maxHeight;
}

bool wxME_HorizMenu::IsVertical()
{
	return FALSE;
}

/***** Implementation for class MenuEditorDoc *****/

IMPLEMENT_DYNAMIC_CLASS( MenuEditorDoc, wxPMDocument )

MenuEditorDoc::MenuEditorDoc()

	: mpRootNode( NULL ),
	  mpRecord( NULL )
{}

MenuEditorDoc::~MenuEditorDoc()
{
	if ( mpRootNode ) 
        delete mpRootNode;
}

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

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

	StoreNodesRecursively( *mpRecord, mpRootNode );

	if ( IsModified() )
		GetParentDocument()->Modify( TRUE );

	return TRUE;
}

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

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

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

	mpRecord = doc.GetCurrentRecord();

	if ( mpRootNode )
        delete mpRootNode;

	bool ok = LoadNodesRecursively( *mpRecord, NULL );

	UpdateAllViews();

	return ok;
}

void MenuEditorDoc::SetRootNode( wxME_Node* pRoot )
{
	mpRootNode = pRoot;
}

wxME_Node* MenuEditorDoc::GetRootNode()
{
	return mpRootNode;
}

bool MenuEditorDoc::LoadNodesRecursively( Record& rec, wxME_Node* pParent )
{
	wxME_Node* pNode = new wxME_Node();

	if ( rec.FindField( "name" ) )
		pNode->mName = rec[ "name" ];

	pNode->mTitle = rec.GetStringValue();

	if ( rec.FindField( "title" ) )
		pNode->mTitle = rec[ "title" ];

	if ( rec.FindField( "checked" ) &&
		 ( rec[ "checked" ] == "1" ) )
        pNode->mMenuFlags |= wxME_CHECKED;

	if ( rec.FindField( "enabled" ) &&
		 ( rec[ "enabled" ] == "0" ) )
        pNode->mMenuFlags |= wxME_DISABLED;

	if ( rec.FindField( "platform" ) )
		pNode->mPlatform = rec[ "platform" ];

	if ( rec.GetName() == "separator" )
		pNode->mMenuFlags |= wxME_SEPARATOR;

	if ( rec.GetName() == "bold" )
		pNode->mMenuFlags |= wxME_BOLD;

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

		mpDMCmdProcessor = new wxDMCommandProcessor();

		mpDMCmdProcessor->SetClipboard( 
			&((ResDocument*)GetParentDocument())->GetClipboard() );

		pNode->SetCommandProcessor( mpDMCmdProcessor );

		mpDMCmdProcessor->SetRoot( pNode );
	}

    size_t k;
	for( k = 0; k != pNode->mTitle.Length(); ++k )

		if ( pNode->mTitle[k] == '$' )

			pNode->mTitle[k] = '&';

    size_t i;
	for( i = 0; i != rec.GetFieldCount(); ++i )
	{
		FieldBase& fld = rec.GetFieldByIndex(i);

		if ( fld.GetType() == FT_RECORD )

			LoadNodesRecursively( rec.GetRecordByIndex(i), pNode );
	}

	return TRUE;
}

bool MenuEditorDoc::StoreNodesRecursively( Record& rec, wxME_Node* pNode )
{
	rec.RemoveAllFields();

	wxME_Node* pParent = (wxME_Node*)pNode->GetParent();

	wxString outTitle = pNode->mTitle;

    size_t k;
	for( k = 0; k != outTitle.Length(); ++k )

		if ( outTitle[k] == '&' )

			outTitle[k] = '$';

	if ( pNode->mMenuFlags & wxME_SEPARATOR )

		rec.SetName( "separator" );
	else
	if ( pParent == NULL )

		rec.SetName( "menubar" );
	else
	if ( pNode->HasChildren() )
	{
		rec.SetName( "menu" );
		rec.AddStringField( "title", outTitle );
	}
	else
	{
		rec.SetName( "item" );
		rec.SetStringValue( outTitle );
	}

	rec.AddStringField( "name", pNode->mName );

	if ( pNode->mPlatform != "" )

		rec.AddStringField( "platform", pNode->mPlatform );

	if ( pNode->mMenuFlags & wxME_DISABLED )

		rec.AddStringField( "enabled", "0" );

	if ( pNode->mMenuFlags & wxME_CHECKABLE )

		rec.AddStringField( "checkable", "1" );

	if ( pNode->mMenuFlags & wxME_CHECKED )

		rec.AddStringField( "checked", "1" );

	if ( pNode->mMenuFlags & wxME_BOLD )

		rec.AddStringField( "bold", "1" );

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

		StoreNodesRecursively( *pChildRec, (wxME_Node*)pNode->GetChild(i) );
	}

	return TRUE;
}

/***** Implementation for class MenuEditorView *****/

static wxCommandInfo _gMenuEditorEditCommands[] = {

	wxCommandInfo( MenuEditorView::ID_UNDO, "&Undo", "", "Ctrl+Z|Alt+BACK" ), 
	wxCommandInfo( MenuEditorView::ID_REDO, "&Redo", "", "Ctrl+A|Ctrl+BACK" ), 
	wxCommandInfo( -1, "SEPARATOR" ),

	wxCommandInfo( MenuEditorView::ID_COPY, "&Copy", "", "Ctrl+C|Ctrl+INSERT" ), 
	wxCommandInfo( MenuEditorView::ID_PASTE, "&Paste", "", "Ctrl+V|Shift+INSERT" ), 
	wxCommandInfo( MenuEditorView::ID_CUT, "Cut", "", "Ctrl+X" ), 
	wxCommandInfo( MenuEditorView::ID_REMOVE, "&Delete", "", "DELETE|D" ), 

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

static wxCommandInfo _gMenuEditorItemCommands[] = {

	wxCommandInfo( MenuEditorView::ID_INSERT, "&Insert", "", "INSERT|I" ), 
	wxCommandInfo( MenuEditorView::ID_INSERT_SUBITEM, "Insert &menu", "", "M" ), 
	wxCommandInfo( MenuEditorView::ID_INSERT_AFTER, "Insert after", "", "Alt+INSERT|Ctrl+I" ), 
	wxCommandInfo( MenuEditorView::ID_PREPEND, "&Prepend", "", "P" ), 
	wxCommandInfo( MenuEditorView::ID_APPEND, "&Append", "", "A" ), 
	wxCommandInfo( MenuEditorView::ID_INSERT_SEPARATOR, "Append Separator", "", "R" ), 
	wxCommandInfo( -1, "SEPARATOR" ), 

	wxCommandInfo( MenuEditorView::ID_MOVE_UP, "Move &Up", "", "Ctrl+UP" ), 
	wxCommandInfo( MenuEditorView::ID_MOVE_DOWN, "Move Do&wn", "", "Ctrl+DOWN" ), 
	wxCommandInfo( MenuEditorView::ID_MOVE_TOP, "Move &Top", "", "HOME" ), 
	wxCommandInfo( MenuEditorView::ID_MOVE_BOTTOM, "Move &Bottom", "", "END" ), 
	wxCommandInfo( -1, "SEPARATOR" ), 

	wxCommandInfo( MenuEditorView::ID_POPUP_VIEW, "Popup-View", "", "" ), 
	wxCommandInfo( MenuEditorView::ID_BAR_VIEW, "Bar-View", "", "" ), 

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

static wxPMMergableMenuChain _gMenuEditorChains[] = {

	wxPMMergableMenuChain( "/&Edit/", _gMenuEditorEditCommands ),

	wxPMMergableMenuChain( "/&Item/", _gMenuEditorItemCommands ),

	wxPMMergableMenuChain( "", NULL ) // terminator
};


IMPLEMENT_DYNAMIC_CLASS( MenuEditorView, wxPMView )

BEGIN_EVENT_TABLE( MenuEditorView, wxPMView )

	EVT_MENU( ID_INSERT, MenuEditorView::OnInsert )
	EVT_MENU( ID_INSERT_SUBITEM, MenuEditorView::OnInsertSubItem )
	EVT_MENU( ID_INSERT_AFTER, MenuEditorView::OnInsertAfter )
	EVT_MENU( ID_INSERT_SEPARATOR, MenuEditorView::OnInsertSeparator )
	EVT_MENU( ID_PREPEND, MenuEditorView::OnPrepend )
	EVT_MENU( ID_APPEND, MenuEditorView::OnAppend )
	EVT_MENU( ID_MOVE_UP, MenuEditorView::OnMoveUp )
	EVT_MENU( ID_MOVE_DOWN, MenuEditorView::OnMoveDown )
	EVT_MENU( ID_MOVE_TOP, MenuEditorView::OnMoveTop )
	EVT_MENU( ID_MOVE_BOTTOM, MenuEditorView::OnMoveBottom )

	EVT_MENU( ID_UNDO, MenuEditorView::OnUndo )
	EVT_MENU( ID_REDO, MenuEditorView::OnRedo )
	EVT_UPDATE_UI( ID_UNDO, MenuEditorView::OnUndoUI )
	EVT_UPDATE_UI( ID_REDO, MenuEditorView::OnRedoUI )
	EVT_MENU( ID_COPY, MenuEditorView::OnCopy )
	EVT_MENU( ID_CUT, MenuEditorView::OnCut )
	EVT_MENU( ID_PASTE, MenuEditorView::OnPaste )
	EVT_MENU( ID_REMOVE, MenuEditorView::OnRemove )

	EVT_MENU( ID_POPUP_VIEW, MenuEditorView::OnPopupView )
	EVT_MENU( ID_BAR_VIEW,   MenuEditorView::OnBarView )

END_EVENT_TABLE()

MenuEditorView::MenuEditorView()

	: mIsPopupView( FALSE ),
	  mpRenderer( NULL ),
	  mpScene( NULL ),
	  mpPropPane( NULL ),
	  mIdsHash( wxKEY_STRING ),
	  mIdCounter( 1 )
{}

MenuEditorView::~MenuEditorView()
{
	GetViewFrame()->PopEventHandler(); // remove instance of XwxWindowHook()

	if ( mpScene ) delete mpScene;

	if ( mpRenderer ) delete mpRenderer;
}

void MenuEditorView::OnUpdate( wxView* sender, wxObject* hint )
{
	GetRootNode()->FindCmdProcessor()->AddListener( this );

	mOpenedNodeNames.Clear();

	if ( GetRootNode() )

		mOpenedNodeNames.Add( GetRootNode()->mName );

	RehashItemIds();

	RecreateVisualItems();
}

void MenuEditorView::RehashItemIds()
{
	mIdsHash.Clear();

	RehashItemIdsRecursive( GetRootNode() );
}

void MenuEditorView::RehashItemIdsRecursive( wxME_Node* pNode )
{
	if ( pNode == NULL ) return;
	
	// "automagically" repair duplicated ids

	if ( pNode->mName == "" || mIdsHash.Get( pNode->mName ) != NULL )
	
		pNode->mName = GenerateNewId();

	mIdsHash.Put( pNode->mName, (wxObject*)1 );

	pNode->mpView = this;

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

		RehashItemIdsRecursive( (wxME_Node*)pNode->GetChild(i) );
}

void MenuEditorView::OpenNode( wxME_Node* pNode )
{
	mEditedNodeName = pNode->mName;

	mOpenedNodeNames.Clear();

	while( pNode )
	{
		mOpenedNodeNames.Insert( pNode->mName, 0 );

		pNode = (wxME_Node*)pNode->GetParent();
	}
}

void MenuEditorView::OnActivateView(bool activate, wxView *activeView, 
									wxView *deactiveView)
{
	if ( activate )
	{
		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() );
		}

		wxDMNodePropertyGroupEditor* pEditor = mpPropPane->GetGroupEditor();

		pEditor->Update( GetRootNode() );
	}

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

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

	wxASSERT( doc->IsKindOf( CLASSINFO(MenuEditorDoc) ) );

	mpScene = new XCollection();

	XCellBackground* pBg = new XCellBackground( *wxWHITE );
	XSelectionEditor* pEditor = new XSelectionEditor();

	mpScene->Insert( pBg );
	mpScene->MarkBackgroundObj( *pBg );
	mpScene->SetEditor( pEditor );

	XwxWindowHook* pHook = new XwxWindowHook( GetViewFrame(), mpScene );

	mpRenderer = new wxME_NodeRenderer( GetViewFrame() );

#if defined(__WXMSW__) || defined(__WINDOWS__) 
	wxFont font;
    font.SetFaceName("MS Sans Serif");
    font.SetPointSize( 9 );
    font.RealizeResource();
	GetViewFrame()->SetFont( font );
#endif

	if ( mpRenderer == NULL )

		mpRenderer = new wxME_NodeRenderer( GetViewFrame() ); // set default

	return TRUE;
}

wxPMMergableMenuChain* MenuEditorView::GetMergableMenuChain()
{
	return _gMenuEditorChains;
}

void MenuEditorView::OnNodeRemoved( wxDMNode& node )
{
	mIdsHash.Delete( ((wxME_Node*)&node)->mName );

	RecreateVisualItems();
}

void MenuEditorView::OnNodeAdded( wxDMNode& node )
{
	mIdsHash.Put( ((wxME_Node*)&node)->mName, (wxObject*)1 );

	OpenNode( (wxME_Node*)&node );

	RecreateVisualItems();
}

void MenuEditorView::OnNodeChanged( wxDMNode& node )
{
	mIdsHash.Put( ((wxME_Node*)&node)->mName, (wxObject*)1 );

	OpenNode( (wxME_Node*)&node );

	RecreateVisualItems();
}

void MenuEditorView::OnNodeOrderChanged( wxDMNode& node )
{
	RecreateVisualItems();
}

void MenuEditorView::OnNodeReplaced( wxDMNode& node, wxDMNode& withNode )
{
	RecreateVisualItems();
}

void MenuEditorView::OnFocusChanged( wxDMNode& node )
{
	/*
	wxME_Node& n = *(wxME_Node*)&node;

	mEditedNodeName = n.mName;

	RecreateVisualItems();
	*/
}

void MenuEditorView::OnFocusRemoved()
{
	mEditedNodeName = "";

	RecreateVisualItems();
}

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

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

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

void MenuEditorView::OnUpdateFinished()
{
}

void MenuEditorView::SetRootNode( wxME_Node* pRoot )
{
	((MenuEditorDoc*)GetDocument())->SetRootNode( pRoot );

}

wxME_Node* MenuEditorView::GetRootNode()
{
	return ((MenuEditorDoc*)GetDocument())->GetRootNode();
}

/*** event handlers ***/

void MenuEditorView::OnInsert( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	size_t order = pNode->GetOrderNo();

	pNode->FindCmdProcessor()->StartBatch();

	pNode = (wxME_Node*)pNode->GetParent();

	wxME_Node* pChild = CreateNewNode();

	pNode->AddChild( pChild );

	pChild->NotifyAdded( pChild );

	pChild->NotifyWillChange( pChild );

	pChild->SetOrderNo( order );

	pChild->FindCmdProcessor()->FinishBatch();
}

void MenuEditorView::OnInsertSubItem( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	wxME_Node* pChild = CreateNewNode();

	pNode->AddChild( pChild );

	pChild->NotifyAdded( pChild );
}

void MenuEditorView::OnInsertAfter( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	size_t order = pNode->GetOrderNo();

	pNode->FindCmdProcessor()->StartBatch();

	pNode = (wxME_Node*)pNode->GetParent();

	wxME_Node* pChild = CreateNewNode();

	pNode->AddChild( pChild );

	pChild->NotifyAdded( pChild );

	pChild->NotifyWillChange( pChild );

	pChild->SetOrderNo( order + 1 );

	pChild->FindCmdProcessor()->FinishBatch();
}

void MenuEditorView::OnInsertSeparator( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	pNode = (wxME_Node*)pNode->GetParent();

	wxME_Node* pChild = CreateNewNode();
	pChild->mMenuFlags |= wxME_SEPARATOR;

	pNode->AddChild( pChild );

	pChild->NotifyAdded( pChild );
}

void MenuEditorView::OnPrepend( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	pNode->FindCmdProcessor()->StartBatch();

	pNode = (wxME_Node*)pNode->GetParent();

	wxME_Node* pChild = CreateNewNode();

	pNode->AddChild( pChild );

	pChild->NotifyAdded( pChild );

	pChild->NotifyWillChange( pChild );

	pChild->SetOrderNo( 0 );

	pChild->FindCmdProcessor()->FinishBatch();
}

void MenuEditorView::OnAppend( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	pNode = (wxME_Node*)pNode->GetParent();

	wxME_Node* pChild = CreateNewNode();
	pNode->AddChild( pChild );

	pChild->NotifyAdded( pChild );
}

void MenuEditorView::OnMoveUp( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	size_t order = pNode->GetOrderNo();

	if ( order != 0 )
	{
		pNode->NotifyWillChange( pNode );

		pNode->SetOrderNo( order-1 );
	}
}

void MenuEditorView::OnMoveDown( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	size_t order = pNode->GetOrderNo();

	if ( order + 1 < pNode->GetParent()->GetChildCount() )
	{
		pNode->NotifyWillChange( pNode );

		pNode->SetOrderNo( order+1 );
	}
}

void MenuEditorView::OnMoveTop( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	size_t order = pNode->GetOrderNo();

	if ( order != 0 )
	{
		pNode->NotifyWillChange( pNode );

		pNode->SetOrderNo( 0 );
	}
}

void MenuEditorView::OnMoveBottom( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	size_t order = pNode->GetOrderNo();

	if ( order != pNode->GetParent()->GetChildCount()-1 )
	{
		pNode->NotifyWillChange( pNode );

		pNode->SetOrderNo( pNode->GetParent()->GetChildCount()-1 );
	}
}

void MenuEditorView::OnUndo( wxCommandEvent& event )
{
	if ( !GetRootNode()->FindCmdProcessor()->CanUndo() )

		return;

	GetRootNode()->FindCmdProcessor()->Undo();
}

void MenuEditorView::OnRedo( wxCommandEvent& event )
{
	if ( !GetRootNode()->FindCmdProcessor()->CanRedo() )

		return;

	GetRootNode()->FindCmdProcessor()->Redo();
}

void MenuEditorView::OnUndoUI( wxUpdateUIEvent& event )
{
	event.Enable( GetRootNode()->FindCmdProcessor()->CanUndo() );
}

void MenuEditorView::OnRedoUI( wxUpdateUIEvent& event )
{
	event.Enable( GetRootNode()->FindCmdProcessor()->CanRedo() );
}

void MenuEditorView::OnCopy( wxCommandEvent& event )
{
	GetRootNode()->FindCmdProcessor()->Copy();
}

void MenuEditorView::OnPaste( wxCommandEvent& event )
{
	GetRootNode()->FindCmdProcessor()->Paste();
}

void MenuEditorView::OnCut( wxCommandEvent& event )
{
	GetRootNode()->FindCmdProcessor()->Copy();
	GetRootNode()->FindCmdProcessor()->Delete();
}

void MenuEditorView::OnRemove( wxCommandEvent& event )
{
	wxME_Node* pNode = (wxME_Node*)GetRootNode()->GetFocus();

	if ( !pNode || ( pNode && pNode->GetParent() == NULL ) ) return;

	size_t orderNo = pNode->GetOrderNo();
	wxDMNode* pParent = pNode->GetParent();

	if ( orderNo > 0 )

		mEditedNodeName = ((wxME_Node*)pParent->GetChild( orderNo - 1 ))->mName;
	else
	if ( orderNo + 1 < pParent->GetChildCount() )

		mEditedNodeName = ((wxME_Node*)pParent->GetChild( orderNo + 1 ))->mName;

	pNode->FindCmdProcessor()->Delete();
}

void MenuEditorView::OnPopupView( wxCommandEvent& event )
{
	mIsPopupView = TRUE;

	RecreateVisualItems();
}

void MenuEditorView::OnBarView( wxCommandEvent& event )
{
	mIsPopupView = FALSE;

	RecreateVisualItems();
}

/*** protected methods ***/

void MenuEditorView::RecreateVisualItems()
{
	wxClientDC dc( GetViewFrame() );

	XObjListT lst = mpScene->GetContents();

	// erase everything except the background

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	
		if ( !mpScene->IsBackgroundObj( *lst[i] ) )

			mpScene->Remove( lst[i], FALSE );

	wxPoint nextPos = wxPoint(10,10);

	wxME_Node* pParentNode = NULL;

	wxME_VisItemContainer* pParentCont = NULL;

    size_t n;
	for( n = 0; n != (unsigned)mOpenedNodeNames.GetCount(); ++n )
	{
		wxString name = mOpenedNodeNames[n];

		wxME_Node* pNode = NULL;

		if ( pParentNode == NULL )
		
			pNode = GetRootNode();
		else
		{
            size_t i;
			for( i = 0; i != pParentNode->GetChildCount(); ++i )
			{
				wxME_Node* pChild = (wxME_Node*)pParentNode->GetChild(i);

				if ( pChild->mName == name )
				{
					pNode = pChild;

					break;
				}
			}
		}

		if ( !pNode ) break;

		if ( pParentCont )
		{
            size_t i;
			for( i = 0; i != pParentCont->mVisItems.Count(); ++i )
			{
				wxME_VisItemData* pVisItem = pParentCont->mVisItems[i];

				if ( pVisItem->mpItem == pNode )
				{
					pVisItem->mVisFlags |= wxME_VIS_OPENED;

					if ( !pParentCont->IsVertical() )
					{
						nextPos.x = pVisItem->mBounds.x;
						nextPos.y = pVisItem->mBounds.y + pVisItem->mBounds.height - 0;
					}
					else
					{
						nextPos.x = pVisItem->mBounds.x + pVisItem->mBounds.width - 5;
						nextPos.y = pVisItem->mBounds.y;
					}

					break;
				}
			}
		}

		pParentNode = pNode;

		if ( pNode->HasChildren() )
		{
			wxME_VisItemContainer* pCont = ( mIsPopupView || pNode != GetRootNode() )
										   ? (wxME_VisItemContainer*)new wxME_VertMenu()
										   : (wxME_VisItemContainer*)new wxME_HorizMenu();

			pParentCont = pCont;

			pCont->SetRenderer( mpRenderer );
			pCont->SetView( this );

			DMNodeListT& children = pNode->GetChildren();

            size_t i;
			for( i = 0; i != children.Count(); ++i )
			{
				wxME_Node& child = *(wxME_Node*)children[i];

				wxME_VisItemData* pData = new wxME_VisItemData();

				pData->mpItem = &child;
				pData->mVisFlags = 0;

				if ( child.mName == mEditedNodeName )
				{
					pData->mVisFlags |= wxME_VIS_EDITED;

					pData->mpItem->SetFocused( TRUE );

					DMNodeListT lst;
					lst.Add( pData->mpItem );

					GetRootNode()->SetSelection( lst );
				}

				pCont->AddVisItem( pData );
			}

			mpScene->Insert( pCont, FALSE );

			pCont->SetBounds( XRect( nextPos.x, nextPos.y, 50,50 ) );

			pCont->Layout( dc );
			pCont->InvalidateRect( pCont->GetBounds() );
		}
		else
			break;
	}

	mpScene->RefreshNow();
}

void MenuEditorView::SetPopupView( bool on )
{
	mIsPopupView = on;
}

bool MenuEditorView::IsPopupView()
{
	return mIsPopupView;
}

void MenuEditorView::SetEditedNodeName( const wxString& name )
{
	mEditedNodeName = name;
}

const wxString& MenuEditorView::GetEditedNodeName()
{
	return mEditedNodeName;
}

StrListT& MenuEditorView::GetOpenedItems()
{
	return mOpenedNodeNames;
}

void MenuEditorView::CreateSampleMenuTree()
{
	SetRootNode( new wxME_Node("top") );

	AddSimpleItemsRecursively( GetRootNode(), 5 );

	mOpenedNodeNames.Clear();

	mOpenedNodeNames.Add( GetRootNode()->mName );

	RecreateVisualItems();
}

void MenuEditorView::AddSimpleItemsRecursively( wxME_Node* pParent, int depth )
{
	if ( depth == 0 )
        return;

    size_t i;
	for( i = 0; i != 10; ++i )
	{
		wxString name = "It&em-" + wxString( (char)('A' + i + depth*4) );

		wxME_Node* pChild = new wxME_Node( name, "Ctrl-X", i % 2 ? wxME_CHECKED : 0 );

		pParent->AddChild( pChild );

		if ( i % 2 )

			AddSimpleItemsRecursively( pChild, depth - 1 );
	}
}

void MenuEditorView::OpenItem( wxME_Node* pItem, wxME_VisItemContainer* pCont )
{
	mOpenedNodeNames.Clear();

	do
	{
		if ( pItem->HasChildren() && pItem != GetRootNode() )

			mOpenedNodeNames.Insert( pItem->mName, 0 );

		pItem = (wxME_Node*)pItem->GetParent();

	} while( pItem != GetRootNode() );

	mOpenedNodeNames.Insert( pItem->mName, 0 );

	RecreateVisualItems();
}

void MenuEditorView::SetRenderer( wxME_NodeRenderer* pRenderer )
{
	mpRenderer = pRenderer;
	// TBD:: children
}

wxME_NodeRenderer* MenuEditorView::GetRenderer()
{
	return mpRenderer;
}

wxString MenuEditorView::GenerateNewId()
{
	mIdCounter = 1;

	for(;;)
	{
		wxString id = "item" + IntToStr( mIdCounter++ );

		if ( mIdsHash.Get( id ) == NULL )
		
			return id;
	}
}

wxME_Node* MenuEditorView::CreateNewNode()
{
	wxME_Node* pNode = new wxME_Node();

	pNode->mName = GenerateNewId();
	pNode->mTitle = pNode->mName;
	pNode->mpView = this;

	return pNode;
}

/***** Implementation for class wxME_AttrInfo *****/

wxME_AttrInfo::wxME_AttrInfo( int attrId, const wxString& name, const wxString& displayName )

	: mId( attrId )
{
	mName = name;
	mDisplayName = displayName;
}

wxString wxME_AttrInfo::GetStringValue( void* pGroupData )
{
	wxME_Node& n = *(wxME_Node*)pGroupData;

	switch(mId)
	{
		case ATTR_NAME : return n.mName;
		case ATTR_TITLE : return n.mTitle;
		case ATTR_HOTKEY : return n.mHotKey;
		case ATTR_HELP : return n.mHelp;
		case ATTR_PLATFORM : return n.mPlatform;
		case ATTR_IS_SEPARATOR : return FlagToStr( n, wxME_SEPARATOR );
		case ATTR_IS_DISABLED : return FlagToStr( n, wxME_DISABLED );
		case ATTR_IS_CHECKABLE : return FlagToStr( n, wxME_CHECKABLE );
		case ATTR_IS_CHECKED : return FlagToStr( n, wxME_CHECKED );
		case ATTR_IS_BOLD : return FlagToStr( n, wxME_BOLD );
		default: return "";
	}

//	return "";  // never reached
}

void wxME_AttrInfo::SetStringValue( const wxString& value, void* pGroupData )
{
	wxME_Node& n = *(wxME_Node*)pGroupData;

	switch(mId)
	{
		case ATTR_NAME :  n.mName = value; break;
		case ATTR_TITLE :  n.mTitle = value; break;
		case ATTR_HOTKEY :  n.mHotKey = value; break;
		case ATTR_HELP :  n.mHelp = value; break;
		case ATTR_PLATFORM :  n.mPlatform = value; break;
		case ATTR_IS_SEPARATOR :  StrToFlag( n, wxME_SEPARATOR, value ); break;
		case ATTR_IS_DISABLED :  StrToFlag( n, wxME_DISABLED, value ); break;
		case ATTR_IS_CHECKABLE :  StrToFlag( n, wxME_CHECKABLE, value ); break;
		case ATTR_IS_CHECKED :  StrToFlag( n, wxME_CHECKED, value ); break;
		case ATTR_IS_BOLD :  StrToFlag( n, wxME_BOLD, value ); break;
		default: break;
	}
}

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

bool wxME_AttrInfo::HasRestrictedValues( void* pGroupData )
{
	return mId >= ATTR_PLATFORM ;
}

bool wxME_AttrInfo::SetRestrictedValues( wxStringList& list )
{
	return TRUE;
}

void wxME_AttrInfo::GetRestrictedValues( wxStringList& toLst, void* pGroupData )
{
	if ( mId == ATTR_PLATFORM )
	{
		toLst.Add( "" );
		toLst.Add( "unix" );
		toLst.Add( "win" );
		toLst.Add( "os2" );
		toLst.Add( "mac" );
	}
	else
	if ( mId >= ATTR_IS_SEPARATOR )
	{
		toLst.Add( "true" );
		toLst.Add( "false" );
	}
}

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

void wxME_AttrInfo::CopyValue( void* pDestData, void* pSrcData )
{
	wxME_Node& from = *(wxME_Node*)pSrcData;
	wxME_Node& to = *(wxME_Node*)pDestData;

	switch(mId)
	{
		case ATTR_NAME : to.mName = from.mName; break;
		case ATTR_TITLE : to.mTitle = from.mTitle; break;
		case ATTR_HOTKEY : to.mHotKey = from.mHotKey; break;
		case ATTR_HELP : to.mHelp = from.mHelp; break;
		case ATTR_PLATFORM : to.mPlatform = from.mPlatform; break;

		// FOR NOW:: quick but not accurate...
		case ATTR_IS_SEPARATOR : to.mMenuFlags = from.mMenuFlags; break;
		case ATTR_IS_DISABLED : to.mMenuFlags = from.mMenuFlags; break;
		case ATTR_IS_CHECKABLE : to.mMenuFlags = from.mMenuFlags; break;
		case ATTR_IS_CHECKED : to.mMenuFlags = from.mMenuFlags; break;
		case ATTR_IS_BOLD : to.mMenuFlags = from.mMenuFlags; break;
		default: break;
	}
}

bool wxME_AttrInfo::ShownAsBooleanField()
{
	return mId >= ATTR_IS_SEPARATOR;
}

/*** protected methods ***/

wxString wxME_AttrInfo::FlagToStr( wxME_Node& node, int flag )
{
	if ( node.mMenuFlags & flag ) return "true";
						 else return "false";
}

void wxME_AttrInfo::StrToFlag( wxME_Node& node, int flag, const wxString& boolStr )
{
	if ( boolStr == "true" ) node.mMenuFlags |= flag;
					    else node.mMenuFlags &= ~flag;
}
