source-engine/vgui2/dme_controls/dmepanel.cpp

657 lines
19 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "dme_controls/dmepanel.h"
#include "tier1/KeyValues.h"
#include "dme_controls/dmecontrols.h"
#include "vgui_controls/combobox.h"
#include "datamodel/dmelement.h"
#include "dme_controls/dmecontrols_utils.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace vgui;
//-----------------------------------------------------------------------------
// All DmePanels used by the system must be listed here to link them in
//-----------------------------------------------------------------------------
USING_DMEPANEL_FACTORY( CDmeElementPanel, DmElement );
USING_DMEPANEL_FACTORY( CDmeSourceSkinPanel, DmeSourceSkin );
USING_DMEPANEL_FACTORY( CAssetBuilder, DmeMakefile );
USING_DMEPANEL_FACTORY( CDmeSourceDCCFilePanel, DmeSourceDCCFile );
USING_DMEPANEL_FACTORY( CDmeDagRenderPanel, DmeDag );
USING_DMEPANEL_FACTORY( CDmeDagRenderPanel, DmeSourceAnimation );
USING_DMEPANEL_FACTORY( CDmeDagRenderPanel, DmeSourceSkin );
USING_DMEPANEL_FACTORY( CDmeDagRenderPanel, DmeDCCMakefile );
USING_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeDag );
USING_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeSourceAnimation );
USING_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeSourceSkin );
USING_DMEPANEL_FACTORY( CDmeDagEditPanel, DmeDCCMakefile );
USING_DMEPANEL_FACTORY( CDmeMDLPanel, DmeMDLMakefile );
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CDmePanel::CDmePanel( vgui::Panel *pParent, const char *pPanelName, bool bComboBoxVisible ) :
BaseClass( pParent, pPanelName )
{
m_pEditorNames = new vgui::ComboBox( this, "EditorDisplayNames", 6, false );
if ( bComboBoxVisible )
{
m_pEditorNames->AddActionSignalTarget( this );
}
else
{
m_pEditorNames->SetVisible( false );
}
m_pDmeEditorPanel = NULL;
m_hElement = NULL;
SetDropEnabled( true );
}
CDmePanel::~CDmePanel()
{
DeleteCachedPanels();
}
//-----------------------------------------------------------------------------
// Scheme
//-----------------------------------------------------------------------------
void CDmePanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
m_pEditorNames->SetFont( pScheme->GetFont( "DefaultVerySmall" ) );
}
//-----------------------------------------------------------------------------
// Layout
//-----------------------------------------------------------------------------
void CDmePanel::PerformLayout()
{
BaseClass::PerformLayout();
int w, h;
GetSize( w, h );
if ( m_pEditorNames->IsVisible() )
{
m_pEditorNames->SetBounds( 1, 1, w-2, 20 );
if ( m_pDmeEditorPanel )
{
m_pDmeEditorPanel->SetBounds( 0, 24, w, h-24 );
}
}
else
{
if ( m_pDmeEditorPanel )
{
m_pDmeEditorPanel->SetBounds( 0, 0, w, h );
}
}
}
//-----------------------------------------------------------------------------
// Drag/drop
//-----------------------------------------------------------------------------
bool CDmePanel::IsDroppable( CUtlVector< KeyValues * >& msglist )
{
if ( msglist.Count() != 1 )
return false;
KeyValues *data = msglist[ 0 ];
CDmElement *ptr = GetElementKeyValue<CDmElement>( data, "dmeelement" );
if ( !ptr )
return false;
if ( ptr == m_hElement.Get() )
return false;
return true;
}
void CDmePanel::OnPanelDropped( CUtlVector< KeyValues * >& msglist )
{
if ( msglist.Count() != 1 )
return;
KeyValues *data = msglist[ 0 ];
CDmElement *ptr = GetElementKeyValue<CDmElement>( data, "dmeelement" );
if ( !ptr )
return;
// Already browsing
if ( ptr == m_hElement.Get() )
return;
SetDmeElement( ptr );
}
//-----------------------------------------------------------------------------
// Sets the default editor type
//-----------------------------------------------------------------------------
void CDmePanel::SetDefaultEditorType( const char *pEditorType )
{
m_DefaultEditorType = pEditorType;
}
//-----------------------------------------------------------------------------
// Populate editor name combo box
//-----------------------------------------------------------------------------
void CDmePanel::PopulateEditorNames( const char *pPanelName )
{
m_pEditorNames->RemoveAll();
m_pEditorNames->SetText( "" );
if ( !m_pEditorNames->IsVisible() )
{
SetEditor( pPanelName );
return;
}
if ( !m_hElement.Get() )
{
OnTextChanged();
return;
}
const char *pPreferredEditor = NULL;
if ( m_LastUsedEditorType.Defined( m_hElement->GetTypeString() ) )
{
pPreferredEditor = m_LastUsedEditorType[ m_hElement->GetTypeString() ].Get();
}
else
{
pPreferredEditor = m_DefaultEditorType;
}
int nBestInheritanceDepth = -1;
int nActiveItemID = -1;
bool bFoundPanelName = false;
DmeFactoryHandle_t h = DmePanelFirstFactory( m_hElement.Get() );
for ( ; h != DMEFACTORY_HANDLE_INVALID; h = DmePanelNextFactory( h, m_hElement.Get() ) )
{
const char *pDisplayName = DmePanelFactoryDisplayName( h );
const char *pEditorName = DmePanelFactoryName( h );
KeyValues *pKeyValues = new KeyValues( "entry", "editorName", pEditorName );
int nItemID = m_pEditorNames->AddItem( pDisplayName, pKeyValues );
if ( pPanelName && !Q_stricmp( pPanelName, pEditorName ) )
{
nBestInheritanceDepth = 0;
nActiveItemID = nItemID;
bFoundPanelName = true;
continue;
}
if ( pPreferredEditor && !bFoundPanelName && !Q_stricmp( pPreferredEditor, pEditorName ) )
{
nBestInheritanceDepth = 0;
nActiveItemID = nItemID;
continue;
}
// Don't select this as the default if it's not a default factory
if ( !DmePanelFactoryIsDefault(h) )
continue;
// Choose this factory if it's more derived than the previous best
const char *pElementType = DmePanelFactoryElementType( h );
int nInheritanceDepth = m_hElement->GetInheritanceDepth( pElementType );
Assert( nInheritanceDepth >= 0 );
if ( nBestInheritanceDepth >= 0 && ( nInheritanceDepth >= nBestInheritanceDepth ) )
continue;
nBestInheritanceDepth = nInheritanceDepth;
nActiveItemID = nItemID;
}
if ( m_pEditorNames->GetItemCount() == 0 )
{
// ItemCount == 0;
m_pEditorNames->SetText( "" );
m_CurrentEditorName = NULL;
OnTextChanged();
return;
}
if ( nActiveItemID >= 0 )
{
m_pEditorNames->ActivateItem( nActiveItemID );
}
else
{
m_pEditorNames->ActivateItemByRow( 0 );
}
}
//-----------------------------------------------------------------------------
// Called when the dme element was changed
//-----------------------------------------------------------------------------
void CDmePanel::OnDmeElementChanged()
{
PostActionSignal( new KeyValues( "DmeElementChanged" ) );
}
//-----------------------------------------------------------------------------
// Context menu support
//-----------------------------------------------------------------------------
void CDmePanel::OnOpenContextMenu( KeyValues *params )
{
// Forward the context menu message to the DME panel
KeyValues *pMsg = params->MakeCopy();
if ( m_pDmeEditorPanel )
{
PostMessage( m_pDmeEditorPanel->GetVPanel(), pMsg );
}
}
//-----------------------------------------------------------------------------
// Copy/paste support
//-----------------------------------------------------------------------------
void CDmePanel::PostMessageToDmePanel( const char *pMessage )
{
if ( m_pDmeEditorPanel )
{
PostMessage( m_pDmeEditorPanel->GetVPanel(), new KeyValues( pMessage ) );
}
}
void CDmePanel::OnCut()
{
PostMessageToDmePanel( "OnCut" );
}
void CDmePanel::OnCopy()
{
PostMessageToDmePanel( "OnCopy" );
}
void CDmePanel::OnPaste()
{
PostMessageToDmePanel( "OnPaste" );
}
void CDmePanel::OnPasteInsert()
{
PostMessageToDmePanel( "OnPasteInsert" );
}
void CDmePanel::OnPasteReference()
{
PostMessageToDmePanel( "OnPasteReference" );
}
void CDmePanel::OnEditDelete()
{
PostMessageToDmePanel( "OnEditDelete" );
}
//-----------------------------------------------------------------------------
// Called when a child of the dme panel switches the thing it's looking at
//-----------------------------------------------------------------------------
void CDmePanel::OnViewedElementChanged( KeyValues *kv )
{
// This is kind of tricky. It's called by the element properties tree
// when doing the back/forward searching. Just calling the normal SetDmeElement
// doesn't work because it reorders the history. What we want is to
// populate the combo box without causing the OnTextChanged message to get sent.
// FIXME: Perhaps it would be better to extract the back/forward/search
// out of the element properties tree and put it into the dme panel?
CDmElement *pElement = GetElementKeyValue<CDmElement>( kv, "dmeelement" );
if ( pElement == m_hElement )
return;
// If the current editor isn't supported by this new element, then just reset. Too bad.
bool bFound = false;
if ( m_CurrentEditorName.Length() && pElement )
{
DmeFactoryHandle_t h = DmePanelFirstFactory( pElement );
for ( ; h != DMEFACTORY_HANDLE_INVALID; h = DmePanelNextFactory( h, pElement ) )
{
const char *pEditorName = DmePanelFactoryName( h );
if ( !Q_stricmp( m_CurrentEditorName, pEditorName ) )
{
bFound = true;
break;
}
}
}
if ( !bFound )
{
SetDmeElement( pElement );
return;
}
// Remove obsolete items
int nCount = m_pEditorNames->GetItemCount();
while ( --nCount >= 0 )
{
int nItemID = m_pEditorNames->GetItemIDFromRow( nCount );
KeyValues *kv = m_pEditorNames->GetItemUserData( nItemID );
if ( Q_stricmp( m_CurrentEditorName, kv->GetString( "editorName" ) ) )
{
m_pEditorNames->DeleteItem( nItemID );
}
}
// Just want to populate the combo box with new items
DmeFactoryHandle_t h = DmePanelFirstFactory( pElement );
for ( ; h != DMEFACTORY_HANDLE_INVALID; h = DmePanelNextFactory( h, pElement ) )
{
const char *pEditorName = DmePanelFactoryName( h );
if ( Q_stricmp( pEditorName, m_CurrentEditorName ) )
{
const char *pDisplayName = DmePanelFactoryDisplayName( h );
KeyValues *pKeyValues = new KeyValues( "entry", "editorName", pEditorName );
m_pEditorNames->AddItem( pDisplayName, pKeyValues );
}
}
m_hElement = pElement;
}
//-----------------------------------------------------------------------------
// Delete cached panels
//-----------------------------------------------------------------------------
void CDmePanel::DeleteCachedPanels()
{
int nCount = m_EditorPanelCache.GetNumStrings();
for ( int i = 0; i < nCount; ++i )
{
int nEditorCount = m_EditorPanelCache[ i ].Count();
for ( int j = 0; j < nEditorCount; ++j )
{
m_EditorPanelCache[ i ][ j ].m_pEditorPanel->MarkForDeletion();
}
}
m_EditorPanelCache.Clear();
}
//-----------------------------------------------------------------------------
// Refreshes the current panel owing to external change
// Values only means no topological change
//-----------------------------------------------------------------------------
void CDmePanel::Refresh( bool bValuesOnly )
{
if ( m_pDmeEditorPanel )
{
KeyValues *pKeyValues = new KeyValues( "ElementChangedExternally", "valuesOnly", bValuesOnly );
PostMessage( m_pDmeEditorPanel, pKeyValues );
}
}
//-----------------------------------------------------------------------------
// Deactivates the current editor
//-----------------------------------------------------------------------------
void CDmePanel::DeactivateCurrentEditor()
{
if ( m_pDmeEditorPanel )
{
m_pDmeEditorPanel->SetParent( (vgui::Panel*)NULL );
m_pDmeEditorPanel = NULL;
m_CurrentEditorName = NULL;
}
}
//-----------------------------------------------------------------------------
// Switch to a new editor
//-----------------------------------------------------------------------------
void CDmePanel::SetEditor( const char *pEditorName )
{
if ( pEditorName && !Q_stricmp( m_CurrentEditorName, pEditorName ) )
return;
DeactivateCurrentEditor();
if ( !m_hElement.Get() || !pEditorName )
return;
if ( m_EditorPanelCache.Defined( pEditorName ) )
{
CUtlVector< EditorPanelMap_t > &entries = m_EditorPanelCache[ pEditorName ];
int nCount = entries.Count();
for ( int i = 0; i < nCount; ++i )
{
EditorPanelMap_t &entry = entries[i];
if ( !m_hElement->IsA( entry.m_pFactory->m_pElementType ) )
continue;
m_pDmeEditorPanel = entry.m_pEditorPanel;
m_pDmeEditorPanel->SetParent( this );
entry.m_pFactory->SetDmeElement( m_pDmeEditorPanel, m_hElement );
break;
}
}
if ( !m_pDmeEditorPanel )
{
EditorPanelMap_t entry;
if ( CreateDmePanel( this, "DmePanelEditor", m_hElement, pEditorName, &entry ) )
{
m_EditorPanelCache[ pEditorName ].AddToTail( entry );
m_pDmeEditorPanel = entry.m_pEditorPanel;
}
}
if ( m_pDmeEditorPanel )
{
// Store the last selected type of editor
m_LastUsedEditorType[ m_hElement->GetTypeString() ] = pEditorName;
m_CurrentEditorName = pEditorName;
m_pDmeEditorPanel->AddActionSignalTarget( this );
}
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Called when a new element in the combo box has been selected
//-----------------------------------------------------------------------------
void CDmePanel::OnTextChanged()
{
KeyValues *kv = m_pEditorNames->GetActiveItemUserData();
const char *pEditorName = kv ? kv->GetString( "editorName", NULL ) : NULL;
SetEditor( pEditorName );
}
//-----------------------------------------------------------------------------
// Setting a new element
//-----------------------------------------------------------------------------
void CDmePanel::SetDmeElement( CDmElement *pDmeElement, bool bForce, const char *pPanelName )
{
if ( ( m_hElement == pDmeElement ) && !bForce )
{
if ( !pPanelName || !Q_stricmp( pPanelName, m_CurrentEditorName.Get() ) )
return;
}
m_hElement = pDmeElement;
m_CurrentEditorName = NULL;
// Populate the editor type list
PopulateEditorNames( pPanelName );
}
//-----------------------------------------------------------------------------
// Statics for the panel factory
//-----------------------------------------------------------------------------
CBaseDmePanelFactory* CBaseDmePanelFactory::s_pFirstDmePanelFactory;
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CBaseDmePanelFactory::CBaseDmePanelFactory( const char *pElementType, const char *pEditorName,
const char *pEditorDisplayName, bool bIsDefault, bool bIsOverride )
{
// Prior to linking this in, look to see if this has been overridden
CBaseDmePanelFactory *pPrevFactory = NULL;
for( CBaseDmePanelFactory* pFactory = s_pFirstDmePanelFactory; pFactory;
pPrevFactory = pFactory, pFactory = pFactory->m_pNext )
{
if ( !Q_stricmp( pFactory->m_pElementType, pElementType ) &&
!Q_stricmp( pFactory->m_pEditorDisplayName, pEditorDisplayName ) )
{
// Collision found! If this is not an override, then we've been overridden
if ( !bIsOverride )
{
AssertMsg( pFactory->m_bIsOverride, ( "Two DmePanel factories have the same name (\"%s\") + type (\"%s\")!\n", pElementType, pEditorName ) );
return;
}
// If this *is* an override, replace the previous version
AssertMsg( !pFactory->m_bIsOverride, ( "Two DmePanel factories have the same name (\"%s\") + type (\"%s\")!\n", pElementType, pEditorName ) );
if ( pPrevFactory )
{
pPrevFactory->m_pNext = pFactory->m_pNext;
}
else
{
s_pFirstDmePanelFactory = pFactory->m_pNext;
}
break;
}
}
m_pNext = s_pFirstDmePanelFactory;
s_pFirstDmePanelFactory = this;
m_pElementType = pElementType;
m_pEditorName = pEditorName;
m_pEditorDisplayName = pEditorDisplayName;
m_bIsDefault = bIsDefault;
m_bIsOverride = bIsOverride;
}
//-----------------------------------------------------------------------------
// Dme Panel factory iteration methods
//-----------------------------------------------------------------------------
DmeFactoryHandle_t DmePanelFirstFactory( CDmElement *pElement )
{
CBaseDmePanelFactory *pFactory = CBaseDmePanelFactory::s_pFirstDmePanelFactory;
for ( ; pFactory; pFactory = pFactory->m_pNext )
{
if ( !pElement || pElement->IsA( pFactory->m_pElementType ) )
return (DmeFactoryHandle_t)pFactory;
}
return DMEFACTORY_HANDLE_INVALID;
}
DmeFactoryHandle_t DmePanelNextFactory( DmeFactoryHandle_t h, CDmElement *pElement )
{
CBaseDmePanelFactory *pFactory = (CBaseDmePanelFactory*)h;
if ( !pFactory )
return DMEFACTORY_HANDLE_INVALID;
for ( pFactory = pFactory->m_pNext; pFactory; pFactory = pFactory->m_pNext )
{
if ( !pElement || pElement->IsA( pFactory->m_pElementType ) )
return (DmeFactoryHandle_t)pFactory;
}
return DMEFACTORY_HANDLE_INVALID;
}
//-----------------------------------------------------------------------------
// Dme Panel factory info methods
//-----------------------------------------------------------------------------
const char *DmePanelFactoryName( DmeFactoryHandle_t h )
{
CBaseDmePanelFactory *pFactory = (CBaseDmePanelFactory*)h;
return pFactory ? pFactory->m_pEditorName : NULL;
}
const char *DmePanelFactoryDisplayName( DmeFactoryHandle_t h )
{
CBaseDmePanelFactory *pFactory = (CBaseDmePanelFactory*)h;
return pFactory ? pFactory->m_pEditorDisplayName : NULL;
}
const char *DmePanelFactoryElementType( DmeFactoryHandle_t h )
{
CBaseDmePanelFactory *pFactory = (CBaseDmePanelFactory*)h;
return pFactory ? pFactory->m_pElementType : NULL;
}
bool DmePanelFactoryIsDefault( DmeFactoryHandle_t h )
{
CBaseDmePanelFactory *pFactory = (CBaseDmePanelFactory*)h;
return pFactory ? pFactory->m_bIsDefault : false;
}
//-----------------------------------------------------------------------------
// Dme Panel factory methods
//-----------------------------------------------------------------------------
bool CDmePanel::CreateDmePanel( vgui::Panel *pParent, const char *pPanelName, CDmElement *pElement, const char *pEditorName, EditorPanelMap_t *pMap )
{
int nBestInheritanceDepth = -1;
CBaseDmePanelFactory *pBestFactory = NULL;
CBaseDmePanelFactory *pFactory = CBaseDmePanelFactory::s_pFirstDmePanelFactory;
for ( ; pFactory; pFactory = pFactory->m_pNext )
{
if ( !pElement->IsA( pFactory->m_pElementType ) )
continue;
if ( pEditorName )
{
if ( !Q_stricmp( pEditorName, pFactory->m_pEditorName ) )
{
pBestFactory = pFactory;
break;
}
continue;
}
// No editor name specified? Only use default factories
if ( !pFactory->m_bIsDefault )
continue;
// Choose this factory if it's more derived than the previous best
int nInheritanceDepth = pElement->GetInheritanceDepth( pFactory->m_pElementType );
Assert( nInheritanceDepth >= 0 );
if ( nBestInheritanceDepth >= 0 && ( nInheritanceDepth > nBestInheritanceDepth ) )
continue;
nBestInheritanceDepth = nInheritanceDepth;
pBestFactory = pFactory;
}
if ( pBestFactory )
{
pMap->m_pFactory = pBestFactory;
pMap->m_pEditorPanel = pBestFactory->CreateDmePanel( pParent, pPanelName, pElement );
return true;
}
return false;
}