text: //============ Copyright (c) Valve Corporation, All rights reserved. ============
  //
  // Base class for panels used to edit nodes in layout system definitions.
  //
  //===============================================================================
  
  #include "vgui_controls/Panel.h"
  #include "vgui_controls/Label.h"
  #include "vgui_controls/Button.h"
  #include "vgui_controls/TextEntry.h"
  #include "KeyValues.h"
  #include "layout_system_editor/layout_system_kv_editor.h"
  #include "layout_system_editor/rule_instance_node_panel.h"
  #include "layout_system_editor/state_node_panel.h"
  #include "node_panel.h"
  
  using namespace vgui;
  
  CNodePanel::CNodePanel( Panel *pParent, const char *pName ) : 
  BaseClass( pParent, pName ),
  m_pNodeKV( NULL ),
  m_pEditor( NULL ),
  m_bShowNameEditBox( false ),
  m_bShowDeleteButton( false )
  { 
  	m_pLabel = new Label( this, "Label", "" );
  	m_pLabel->SetMouseInputEnabled( false );
  	m_pNameEditBox = new TextEntry( this, "TextEntry" );
  	m_NodeLabel[0] = '\0';
  	m_NodeName[0] = '\0';
  
  	m_pRootSizer = new CBoxSizer( ESLD_VERTICAL );
  	
  	m_pLabelSizer = new CBoxSizer( ESLD_HORIZONTAL );
  	m_pLabelSizer->AddPanel( m_pLabel, SizerAddArgs_t().Padding( 2 ) );
  	m_pLabelSizer->AddPanel( m_pNameEditBox, SizerAddArgs_t().Padding( 2 ).MinSize( 200, 20 ) );
  	m_pRootSizer->AddSizer( m_pLabelSizer, SizerAddArgs_t().Padding( 5 ) );
  
  	m_pHeadingSizer = new CBoxSizer( ESLD_HORIZONTAL );
  	m_pLabelSizer->AddSizer( m_pHeadingSizer, SizerAddArgs_t().Padding( 0 ).Expand( 1.0f ) );
  
  	m_pDeleteSelfButton = new Button( this, "DeleteSelf", "X", this, "DeleteSelf" );
  	m_pLabelSizer->AddPanel( m_pDeleteSelfButton, SizerAddArgs_t().Padding( 2 ) );
  
  	m_pChildSizer = new CBoxSizer( ESLD_VERTICAL );
  	m_pChildIndentSizer = new CBoxSizer( ESLD_HORIZONTAL );
  	m_pChildIndentSizer->AddSpacer( SizerAddArgs_t().MinSize( 0, 0 ).Padding( 0 ) );
  	m_pChildIndentSizer->AddSizer( m_pChildSizer, SizerAddArgs_t().Padding( 0 ).Expand( 1.0f ) );
  	m_pRootSizer->AddSizer( m_pChildIndentSizer, SizerAddArgs_t().Padding( 0 ).Expand( 1.0f ) );
  	
  	SetSizer( m_pRootSizer );
  
  	// We want a transparent background for the label.
  	m_pLabel->SetPaintBackgroundEnabled( false );
  }
  
  void CNodePanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  {
  	BaseClass::ApplySchemeSettings( pScheme );
  	SetBorder( pScheme->GetBorder( "RaisedBorder" ) );
  }
  
  void CNodePanel::OnCommand( const char *pCommand )
  {
  	if ( Q_stricmp( pCommand, "DeleteSelf" ) == 0 )
  	{
  		DeleteSelf();
  		return;
  	}
  
  	BaseClass::OnCommand( pCommand );
  }
  
  void CNodePanel::OnAddRule( KeyValues *pKV )
  {
  	int nIndex = pKV->GetInt( "index", -1 );
  	Assert( nIndex != -1 );
  	InsertChildData( nIndex, new KeyValues( "rule_instance", NULL, "<null>" ) );
  }
  
  void CNodePanel::OnAddState( KeyValues *pKV )
  {
  	int nIndex = pKV->GetInt( "index", -1 );
  	Assert( nIndex != -1 );
  	InsertChildData( nIndex, new KeyValues( "state", "name", "MyNewState" ) );
  }
  
  void CNodePanel::OnTextChanged( vgui::Panel *pPanel )
  {
  	Assert( pPanel == m_pNameEditBox );
  	const int nMaxTextLength = 2048;
  	char buf[nMaxTextLength];
  	m_pNameEditBox->GetText( buf, nMaxTextLength );
  	buf[nMaxTextLength - 1] = '\0';
  	SetNodeName( buf );
  }
  
  void CNodePanel::SetData( KeyValues *pNodeKV )
  {
  	if ( m_pNodeKV != NULL && m_pNodeKV != pNodeKV )
  	{
  		CNodePanel *pParentNodePanel = dynamic_cast< CNodePanel * >( GetParent() );
  		if ( pParentNodePanel != NULL )
  		{
  			Assert( pParentNodePanel->m_pNodeKV->ContainsSubKey( m_pNodeKV ) );
  
  			if ( pNodeKV != NULL )
  			{
  				// Must replace parent's sub key with ours
  				pParentNodePanel->m_pNodeKV->SwapSubKey( m_pNodeKV, pNodeKV );
  			}
  			else
  			{
  				// Just remove the parent's sub key
  				pParentNodePanel->m_pNodeKV->RemoveSubKey( m_pNodeKV );
  			}
  			
  		}
  		m_pNodeKV->deleteThis();
  	}
  
  	m_pNodeKV = pNodeKV;
  	
  	RecreateControls();
  }
  
  void CNodePanel::RecreateControls()
  {
  	ClearHeading();
  	ClearChildren();
  
  	CreatePanelContents();
  
  	UpdateState();
  }
  
  void CNodePanel::UpdateState()
  {
  	if ( m_NodeLabel[0] != '\0' )
  	{
  		m_pLabel->SetText( m_NodeLabel );
  		m_pLabel->SetVisible( true );
  	}
  	else
  	{
  		m_pLabel->SetText( "" );
  		m_pLabel->SetVisible( false );
  	}
  
  	if ( m_bShowNameEditBox )
  	{
  		m_pNameEditBox->SetText( m_NodeName );
  	}
  	m_pNameEditBox->SetVisible( m_bShowNameEditBox );
  	m_pDeleteSelfButton->SetVisible( m_bShowDeleteButton );
  
  	// Show or hide the "add" buttons on the CNewElementPanel objects
  	for ( int i = 0; i < m_pChildSizer->GetElementCount(); ++ i )
  	{
  		if ( m_pChildSizer->GetElementType( i ) == ESET_PANEL )
  		{
  			CNewElementPanel *pNewElementPanel = dynamic_cast< CNewElementPanel * >( m_pChildSizer->GetPanel( i ) );
  			if ( pNewElementPanel != NULL )
  			{
  				pNewElementPanel->SetVisible( GetEditor()->ShouldShowAddButtons() );
  			}
  		}
  	}
  
  	// Update state on all children
  	for ( int i = 0; i < m_ChildPanels.Count(); ++ i )
  	{
  		m_ChildPanels[i]->UpdateState();
  	}
  }
  
  void CNodePanel::InvalidateEditorLayout()
  {
  	if ( m_pEditor != NULL )
  	{
  		m_pEditor->InvalidateLayout();
  	}
  }
  
  void CNodePanel::ClearHeading()
  {
  	m_pHeadingSizer->RemoveAllMembers( true );
  	InvalidateEditorLayout();
  }
  
  void CNodePanel::AddHeadingElement( Panel *pPanel, float flExpandFactor, int nPadding )
  {
  	m_pHeadingSizer->AddPanel( pPanel, SizerAddArgs_t().Padding( nPadding ).Expand( flExpandFactor ) );
  	InvalidateEditorLayout();
  }
  
  void CNodePanel::AddHeadingSpacer()
  {
  	m_pHeadingSizer->AddSpacer( SizerAddArgs_t().Padding( 0 ).Expand( 1.0f ) );
  	InvalidateEditorLayout();
  }
  
  void CNodePanel::SetChildIndent( int nIndent )
  {
  	m_pChildIndentSizer->SetElementArgs( 0, SizerAddArgs_t().MinSize( nIndent, 0 ).Padding( 0 ) );
  	InvalidateEditorLayout();
  }
  
  void CNodePanel::ClearChildren()
  {
  	m_ChildPanels.RemoveAll();
  	m_pChildSizer->RemoveAllMembers( true );
  	InvalidateEditorLayout();
  }
  
  void CNodePanel::AddChild( CNodePanel *pPanel, int nPadding )
  {
  	m_ChildPanels.AddToTail( pPanel );
  	AddChild( ( Panel * )pPanel, nPadding );
  }
  
  void CNodePanel::AddChild( Panel *pPanel, int nPadding )
  {
  	m_pChildSizer->AddPanel( pPanel, SizerAddArgs_t().Padding( nPadding ) );
  	InvalidateEditorLayout();
  }
  
  void CNodePanel::InsertChildData( int nIndex, KeyValues *pKeyValues )
  {
  	KeyValues *pNodeKV = GetData();
  	pNodeKV->InsertSubKey( nIndex, pKeyValues );
  	RecreateControls();
  }
  
  // Register new panel types here.  There probably won't be that many kinds,
  // so there's no reason to have a general purpose factory function.
  CNodePanel *CNodePanel::CreateChild( KeyValues *pKey )
  {
  	CNodePanel *pNodePanel = NULL;
  	const char *pPanelType = pKey->GetName();
  
  	if ( Q_stricmp( pPanelType, "state" ) == 0 )
  	{
  		pNodePanel = new CStateNodePanel( this, "state_panel" );
  	}
  	else if ( Q_stricmp( pPanelType, "rule_instance" ) == 0 )
  	{
  		pNodePanel = new CRuleInstanceNodePanel( this, "rule_instance_panel" );
  	}
  
  	if ( pNodePanel == NULL )
  	{
  		Log_Warning( LOG_TilegenLayoutSystem, "Unrecognized node type (cannot create panel): %s.\n", pPanelType );
  		return NULL;
  	}
  
  	pNodePanel->SetEditor( m_pEditor );
  	pNodePanel->SetData( pKey );
  	
  	return pNodePanel;
  }
  
  void CNodePanel::DeleteSelf()
  {
  	CNodePanel *pParent = dynamic_cast< CNodePanel * >( GetParent() );
  	Assert( pParent != NULL );
  
  	KeyValues *pParentKV = pParent->GetData();
  	Assert( pParentKV->ContainsSubKey( m_pNodeKV ) );
  	pParentKV->RemoveSubKey( m_pNodeKV );
  	m_pNodeKV->deleteThis();
  	m_pNodeKV = NULL;
  
  	// Refresh parent's panel contents.
  	pParent->RecreateControls();
  }
  
  void CNodePanel::AddNewElementPanel( int nIndex, bool bAllowRules, bool bAllowStates )
  {
  	CNewElementPanel *pNewElementPanel = new CNewElementPanel( this, "NewElement", nIndex );
  	if ( bAllowRules )
  	{
  		pNewElementPanel->AddButton( "Add Rule", "AddRule" );
  	}
  	if ( bAllowStates )
  	{
  		pNewElementPanel->AddButton( "Add State", "AddState" );
  	}
  	AddChild( pNewElementPanel, 5 );
  }
  
  void CNodePanel::SetNodeName( const char *pNodeName )
  {
  	KeyValues *pNodeKV = GetData();
  	if ( pNodeKV != NULL )
  	{
  		pNodeKV->SetString( "name", pNodeName );
  	}
  }
  
  CNewElementPanel::CNewElementPanel( Panel *pParent, const char *pName, int nIndex ) :
  BaseClass( pParent, pName ),
  m_nIndex( nIndex )
  {
  	m_pHorizontalSizer = new CBoxSizer( ESLD_HORIZONTAL );
  	SetSizer( m_pHorizontalSizer );
  }
  
  void CNewElementPanel::AddButton( const char *pButtonText, const char *pActionName )
  {
  	Button *pButton = new Button( this, pActionName, pButtonText, GetParent(), pActionName );
  
  	KeyValues *pMessageKV = new KeyValues( pActionName );
  	KeyValues *pIndexKV = new KeyValues( "index" );
  	pIndexKV->SetInt( NULL, m_nIndex );
  	pMessageKV->AddSubKey( pIndexKV );
  	pButton->SetCommand( pMessageKV );
  
  	m_pHorizontalSizer->AddPanel( pButton, SizerAddArgs_t().MinSize( 50, 20 ).Padding( 0 ) );
  	InvalidateLayout();
  }