//====== Copyright © 1996-2005, 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( 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( 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( 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; }